Skip to content

Commit

Permalink
fix(plugin-coverage): support non-cjs jest configs
Browse files Browse the repository at this point in the history
refactored importEsmModule->importModule to be format-agnostic by default

fix #726
  • Loading branch information
matejchalk committed Jul 1, 2024
1 parent f9718bd commit 3fc351c
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 35 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/lib/implementation/read-rc-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
SUPPORTED_CONFIG_FILE_FORMATS,
coreConfigSchema,
} from '@code-pushup/models';
import { fileExists, importEsmModule } from '@code-pushup/utils';
import { fileExists, importModule } from '@code-pushup/utils';

export class ConfigPathError extends Error {
constructor(configPath: string) {
Expand All @@ -25,7 +25,7 @@ export async function readRcByPath(
throw new ConfigPathError(filepath);
}

const cfg = await importEsmModule({ filepath, tsconfig });
const cfg = await importModule({ filepath, tsconfig, format: 'esm' });

return coreConfigSchema.parse(cfg);
}
Expand Down
7 changes: 3 additions & 4 deletions packages/plugin-coverage/src/lib/nx/coverage-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { JestExecutorOptions } from '@nx/jest/src/executors/jest/schema';
import type { VitestExecutorOptions } from '@nx/vite/executors';
import chalk from 'chalk';
import { isAbsolute, join } from 'node:path';
import { importEsmModule, ui } from '@code-pushup/utils';
import { importModule, ui } from '@code-pushup/utils';
import { CoverageResult } from '../config';

/**
Expand Down Expand Up @@ -128,7 +128,7 @@ export async function getCoveragePathForVitest(
);
}

const vitestConfig = await importEsmModule<VitestCoverageConfig>({
const vitestConfig = await importModule<VitestCoverageConfig>({
filepath: config,
format: 'esm',
});
Expand Down Expand Up @@ -165,9 +165,8 @@ export async function getCoveragePathForJest(
) {
const { jestConfig } = options;

const testConfig = await importEsmModule<JestCoverageConfig>({
const testConfig = await importModule<JestCoverageConfig>({
filepath: jestConfig,
format: 'cjs',
});
const { coverageDirectory, coverageReporters } = {
...testConfig,
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-lighthouse/src/lib/runner/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import experimentalConfig from 'lighthouse/core/config/experimental-config.js';
import perfConfig from 'lighthouse/core/config/perf-config.js';
import { Result } from 'lighthouse/types/lhr/audit-result';
import { AuditOutput, AuditOutputs } from '@code-pushup/models';
import { importEsmModule, readJsonFile, ui } from '@code-pushup/utils';
import { importModule, readJsonFile, ui } from '@code-pushup/utils';
import type { LighthouseOptions } from '../types';
import { logUnsupportedDetails, toAuditDetails } from './details/details';
import { LighthouseCliFlags } from './types';
Expand Down Expand Up @@ -126,7 +126,7 @@ export async function getConfig(
// Resolve the config file path relative to where cli was called.
return readJsonFile<Config>(filepath);
} else if (/\.(ts|js|mjs)$/.test(filepath)) {
return importEsmModule<Config>({ filepath });
return importModule<Config>({ filepath, format: 'esm' });
} else {
ui().logger.info(`Format of file ${filepath} not supported`);
}
Expand Down
1 change: 1 addition & 0 deletions packages/utils/mocks/fixtures/valid-cjs-module-export.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'valid-cjs-module-export';
2 changes: 1 addition & 1 deletion packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export {
fileExists,
filePathToCliArg,
findLineNumberInText,
importEsmModule,
importModule,
logMultipleFileResults,
pluginWorkDir,
readJsonFile,
Expand Down
30 changes: 20 additions & 10 deletions packages/utils/src/lib/file-system.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
import { join } from 'node:path';
import { describe, expect, it } from 'vitest';
import { importEsmModule } from './file-system';
import { importModule } from './file-system';

describe('importEsmModule', () => {
describe('importModule', () => {
const mockDir = join(process.cwd(), 'packages', 'utils', 'mocks', 'fixtures');

it('should load a valid ES module', async () => {
await expect(
importEsmModule({
importModule({
filepath: join(mockDir, 'valid-es-module-export.mjs'),
}),
).resolves.toBe('valid-es-module-export');
});

it('should throw if the file does not exist', async () => {
it('should load a valid CommonJS module', async () => {
await expect(
importEsmModule({
filepath: 'path/to/non-existent-export.mjs',
importModule({
filepath: join(mockDir, 'valid-cjs-module-export.cjs'),
}),
).rejects.toThrow('non-existent-export.mjs');
).resolves.toBe('valid-cjs-module-export');
});

it('should throw if the file does not have any default export', async () => {
it('should load an ES module without default export', async () => {
await expect(
importEsmModule({
importModule({
filepath: join(mockDir, 'no-default-export.mjs'),
}),
).rejects.toThrow('No default export found');
).resolves.toEqual(
expect.objectContaining({ exportedVar: 'exported-variable' }),
);
});

it('should throw if the file does not exist', async () => {
await expect(
importModule({
filepath: 'path/to/non-existent-export.mjs',
}),
).rejects.toThrow('non-existent-export.mjs');
});
});
21 changes: 5 additions & 16 deletions packages/utils/src/lib/file-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,13 @@ export function logMultipleFileResults(
);
}

export class NoExportError extends Error {
constructor(filepath: string) {
super(`No default export found in ${filepath}`);
}
}

export async function importEsmModule<T = unknown>(
options: Options,
): Promise<T> {
const { mod } = await bundleRequire<object>({
format: 'esm',
...options,
});
export async function importModule<T = unknown>(options: Options): Promise<T> {
const { mod } = await bundleRequire<object>(options);

if (!('default' in mod)) {
throw new NoExportError(options.filepath);
if (typeof mod === 'object' && 'default' in mod) {
return mod.default as T;
}
return mod.default as T;
return mod as T;
}

export function pluginWorkDir(slug: string): string {
Expand Down

0 comments on commit 3fc351c

Please sign in to comment.