Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
bbd8648
feat: use jiti for plugin loading
BioPhoton Jan 21, 2026
b2a0154
Merge branch 'refs/heads/main' into feat/jiti-module-loading
BioPhoton Jan 22, 2026
cc442d1
refactor: move loadTargetConfig into utils
BioPhoton Jan 22, 2026
c9cb895
refactor: move importModule into extra file
BioPhoton Jan 22, 2026
aee50d9
refactor: use jiti to load module
BioPhoton Jan 23, 2026
4c75af5
refactor: fix lint
BioPhoton Jan 23, 2026
2f53690
refactor: fix lint
BioPhoton Jan 23, 2026
3428c50
refactor: fix int-test
BioPhoton Jan 23, 2026
e477cbc
refactor: fix unit-test
BioPhoton Jan 23, 2026
81fd996
refactor: fix unit-test
BioPhoton Jan 23, 2026
23ff44d
refactor: fix build
BioPhoton Jan 23, 2026
8447ae2
refactor: fix int-test
BioPhoton Jan 23, 2026
5301aa7
refactor: fix lint
BioPhoton Jan 23, 2026
bf7dd0a
refactor: fix jiti context
BioPhoton Jan 23, 2026
a5319d8
refactor: fix jiti context
BioPhoton Jan 23, 2026
5a67328
refactor: fix int test
BioPhoton Jan 23, 2026
11c493b
refactor: fix e2e test
BioPhoton Jan 23, 2026
1b679b8
refactor: move mocks
BioPhoton Jan 23, 2026
933daf1
refactor: adjust path resolution
BioPhoton Jan 23, 2026
1a51ca9
refactor: fix axe
BioPhoton Jan 23, 2026
51106e9
refactor: fix eslint
BioPhoton Jan 23, 2026
7c3341b
refactor: fix eslint
BioPhoton Jan 23, 2026
03ff1f2
refactor: fix axe
BioPhoton Jan 23, 2026
481b536
refactor: fix import order
BioPhoton Jan 23, 2026
d5125c3
refactor: fix lint
BioPhoton Jan 23, 2026
b7109fb
refactor: add safe axeCore import helper
BioPhoton Jan 23, 2026
ba61c0a
refactor: wip
BioPhoton Jan 23, 2026
1cbe69a
refactor: revert changes
BioPhoton Jan 31, 2026
ce7e644
Merge branch 'main' into feat/jiti-module-loading
BioPhoton Jan 31, 2026
515577c
refactor: add axe-core polyfill
BioPhoton Jan 31, 2026
f0b9c6b
refactor: wip
BioPhoton Jan 31, 2026
85fbcbf
refactor: fix mocking in tests
BioPhoton Jan 31, 2026
c74b5e1
refactor: disable jit cache in tests
BioPhoton Jan 31, 2026
5dbcebe
refactor: fix importModule usage
BioPhoton Jan 31, 2026
a41c71d
refactor: jiti cache test handling
BioPhoton Feb 1, 2026
c42051d
refactor: wip
BioPhoton Feb 1, 2026
44d8e35
refactor: wip
BioPhoton Feb 1, 2026
e951629
refactor: wip
BioPhoton Feb 1, 2026
06f55a1
refactor: wip
BioPhoton Feb 1, 2026
a8e38cb
refactor: wip
BioPhoton Feb 1, 2026
9ea45db
refactor: wip
BioPhoton Feb 1, 2026
9e799f4
refactor: wip
BioPhoton Feb 1, 2026
5da4812
refactor: wip
BioPhoton Feb 1, 2026
196b26d
refactor: add issue repro
BioPhoton Feb 1, 2026
6a90e80
refactor: wip
BioPhoton Feb 1, 2026
98c1439
refactor: wip
BioPhoton Feb 1, 2026
2266a0b
refactor: wip
BioPhoton Feb 1, 2026
8f99f41
refactor: wip
BioPhoton Feb 2, 2026
203cb69
refactor: wip
BioPhoton Feb 2, 2026
d27bc8c
refactor: wip
BioPhoton Feb 2, 2026
560b1e7
refactor: wip
BioPhoton Feb 2, 2026
36aa855
refactor: wip
BioPhoton Feb 2, 2026
d953e35
refactor: wip
BioPhoton Feb 2, 2026
ec537fe
refactor: wip
BioPhoton Feb 2, 2026
2f72c82
refactor: wip
BioPhoton Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
308 changes: 258 additions & 50 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,13 @@
"@types/benchmark": "^2.1.5",
"@types/debug": "^4.1.12",
"@types/eslint": "^8.44.2",
"@types/jsdom": "^27.0.0",
"@types/node": "^22.13.4",
"@types/react": "18.3.1",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react": "^5.0.0",
"@vitest/coverage-v8": "1.3.1",
"@vitest/eslint-plugin": "^1.1.38",
"@vitest/eslint-plugin": "^1.6.6",
"@vitest/ui": "1.3.1",
"benchmark": "^2.1.4",
"chrome-launcher": "^1.1.2",
Expand All @@ -102,7 +103,7 @@
"husky": "^8.0.0",
"inquirer": "^9.3.7",
"jest-extended": "^6.0.0",
"jiti": "2.4.2",
"jiti": "^2.4.2",
"jsdom": "~24.0.0",
"jsonc-eslint-parser": "^2.4.0",
"knip": "^5.33.3",
Expand Down
43 changes: 43 additions & 0 deletions packages/cli/mocks/configs/code-pushup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export default {
upload: {
organization: 'code-pushup',
project: 'cli-js',
apiKey: 'e2e-api-key',
server: 'https://e2e.com/api',
},
categories: [
{
slug: 'category-1',
title: 'Category 1',
refs: [
{
type: 'audit',
plugin: 'node',
slug: 'node-version',
weight: 1,
},
],
},
],
plugins: [
{
audits: [
{
slug: 'node-version',
title: 'Node version',
description: 'prints node version to file',
docsUrl: 'https://nodejs.org/',
},
],
runner: {
command: 'node',
args: ['-v'],
outputFile: 'output.json',
},
groups: [],
slug: 'node',
title: 'Node.js',
icon: 'javascript',
},
],
};
43 changes: 43 additions & 0 deletions packages/cli/mocks/configs/code-pushup.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export default {
upload: {
organization: 'code-pushup',
project: 'cli-mjs',
apiKey: 'e2e-api-key',
server: 'https://e2e.com/api',
},
categories: [
{
slug: 'category-1',
title: 'Category 1',
refs: [
{
type: 'audit',
plugin: 'node',
slug: 'node-version',
weight: 1,
},
],
},
],
plugins: [
{
audits: [
{
slug: 'node-version',
title: 'Node version',
description: 'prints node version to file',
docsUrl: 'https://nodejs.org/',
},
],
runner: {
command: 'node',
args: ['-v'],
outputFile: 'output.json',
},
groups: [],
slug: 'node',
title: 'Node.js',
icon: 'javascript',
},
],
};
43 changes: 43 additions & 0 deletions packages/cli/mocks/configs/code-pushup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
export default {
upload: {
organization: 'code-pushup',
project: 'cli-ts',
apiKey: 'e2e-api-key',
server: 'https://e2e.com/api',
},
categories: [
{
slug: 'category-1',
title: 'Category 1',
refs: [
{
type: 'audit',
plugin: 'node',
slug: 'node-version',
weight: 1,
},
],
},
],
plugins: [
{
audits: [
{
slug: 'node-version',
title: 'Node version',
description: 'prints node version to file',
docsUrl: 'https://nodejs.org/',
},
],
runner: {
command: 'node',
args: ['-v'],
outputFile: 'output.json',
},
groups: [],
slug: 'node',
title: 'Node.js',
icon: 'javascript',
},
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// the point is to test runtime import which requires tsconfig for path aliases
import customPlugin from '@example/custom-plugin';

const config = {
plugins: [customPlugin],
};

export default config;
24 changes: 24 additions & 0 deletions packages/cli/mocks/configs/custom-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const customPluginConfig = {
slug: 'good-feels',
title: 'Good feels',
icon: 'javascript',
audits: [
{
slug: 'always-perfect',
title: 'Always perfect',
},
],
runner: () => [
{
slug: 'always-perfect',
score: 1,
value: 100,
displayValue: '✅ Perfect! 👌',
},
],
};

export function customPlugin() {
return customPluginConfig;
}
export default customPluginConfig;
11 changes: 11 additions & 0 deletions packages/cli/mocks/configs/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"baseUrl": ".",
"allowImportingTsExtensions": true,
"moduleResolution": "node",
"paths": {
"@example/custom-plugin": ["./custom-plugin.ts"]
}
},
"include": ["*.ts"]
}
35 changes: 35 additions & 0 deletions packages/cli/mocks/core-config-middleware.int-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { coreConfigMiddleware } from '../src/lib/implementation/core-config.middleware.js';

// Suppress all console output except our JSON
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
const originalStderrWrite = process.stderr.write.bind(process.stderr);

process.stdout.write = () => true;
process.stderr.write = () => true;

const CLI_DEFAULTS = {
plugins: [],
onlyPlugins: [],
skipPlugins: [],
};

const configPath = process.argv[2];
if (!configPath) {
process.stderr.write = originalStderrWrite;
process.stderr.write('Error: Config path argument is required\n');
process.exit(1);
}

const tsconfigPath = process.argv[3];

const result = await coreConfigMiddleware({
config: configPath, // relative path from cwd
...(tsconfigPath ? { tsconfig: tsconfigPath } : {}),
// If no tsconfig provided, should fallback to ./tsconfig.json
...CLI_DEFAULTS,
});

// Restore stdout and stderr, then write only JSON
process.stdout.write = originalStdoutWrite;
process.stderr.write = originalStderrWrite;
process.stdout.write(JSON.stringify({ success: true, config: result.config }));
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { executeProcess } from '@code-pushup/utils';
import { coreConfigMiddleware } from './core-config.middleware.js';

const configDirPath = path.join(
Expand All @@ -8,15 +9,31 @@ const configDirPath = path.join(
'..',
'..',
'..',
'..',
'testing',
'test-fixtures',
'src',
'lib',
'fixtures',
'cli',
'mocks',
'configs',
);

const helperPath = path.join(
fileURLToPath(path.dirname(import.meta.url)),
'..',
'..',
'..',
'..',
'cli',
'mocks',
'core-config-middleware.int-helper.ts',
);
const runMiddlewareInCwd = async (configPath: string, tsconfigPath?: string) =>
await executeProcess({
command: 'npx',
args: [
'tsx',
helperPath,
configPath,
...(tsconfigPath ? [tsconfigPath] : []),
],
cwd: configDirPath,
});
describe('coreConfigMiddleware', () => {
const CLI_DEFAULTS = {
plugins: [],
Expand All @@ -43,16 +60,17 @@ describe('coreConfigMiddleware', () => {
});

it('should load config which relies on provided --tsconfig', async () => {
await expect(
coreConfigMiddleware({
config: path.join(
configDirPath,
'code-pushup.needs-tsconfig.config.ts',
),
tsconfig: path.join(configDirPath, 'tsconfig.json'),
...CLI_DEFAULTS,
}),
).resolves.toBeTruthy();
const { stdout, code } = await runMiddlewareInCwd(
'code-pushup.needs-tsconfig.config.ts',
path.join(configDirPath, 'tsconfig.json'),
);

expect(code).toBe(0);
const output = JSON.parse(stdout);
expect(output).toStrictEqual({
success: true,
config: expect.any(String),
});
});

it('should throw if --tsconfig is missing but needed to resolve import', async () => {
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/lib/implementation/read-rc-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export async function readRcByPath(
const result = await importModule({
filepath: filePath,
tsconfig,
format: 'esm',
});
return { result, message: `Imported config from ${formattedTarget}` };
},
Expand Down
27 changes: 14 additions & 13 deletions packages/core/src/lib/implementation/read-rc-file.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,33 @@ import { CONFIG_FILE_NAME, type CoreConfig } from '@code-pushup/models';
import { MEMFS_VOLUME } from '@code-pushup/test-utils';
import { autoloadRc } from './read-rc-file.js';

// mock bundleRequire inside importEsmModule used for fetching config
vi.mock('bundle-require', async () => {
// mock importModule from @code-pushup/utils used for fetching config
vi.mock('@code-pushup/utils', async () => {
const utils =
await vi.importActual<typeof import('@code-pushup/utils')>(
'@code-pushup/utils',
);
const { CORE_CONFIG_MOCK }: Record<string, CoreConfig> =
await vi.importActual('@code-pushup/test-fixtures');

return {
bundleRequire: vi
...utils,
importModule: vi
.fn()
.mockImplementation((options: { filepath: string }) => {
.mockImplementation(async (options: { filepath: string }) => {
const extension = options.filepath.split('.').at(-1);
return {
mod: {
default: {
...CORE_CONFIG_MOCK,
upload: {
...CORE_CONFIG_MOCK?.upload,
project: extension, // returns loaded file extension to check format precedence
},
},
...CORE_CONFIG_MOCK,
upload: {
...CORE_CONFIG_MOCK?.upload,
project: extension, // returns loaded file extension to check format precedence
},
};
}),
};
});

// Note: memfs files are only listed to satisfy a system check, value is used from bundle-require mock
// Note: memfs files are only listed to satisfy a system check, value is used from importModule mock
describe('autoloadRc', () => {
it('prioritise a .ts configuration file', async () => {
vol.fromJSON(
Expand Down
Loading
Loading