From bbd864865f56649b9f6ab840de25bd8726379f1e Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 21 Jan 2026 21:40:18 +0100 Subject: [PATCH 1/3] feat: use jiti for plugin loading --- nx.json | 5 ++--- package-lock.json | 24 ++++++++++++++++++++++++ package.json | 1 + 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/nx.json b/nx.json index 7e7265dfe..806a8a509 100644 --- a/nx.json +++ b/nx.json @@ -127,15 +127,14 @@ "executor": "nx:run-commands", "dependsOn": ["code-pushup-*"], "options": { - "command": "node packages/cli/src/index.ts", + "command": "npx jiti-tsc packages/cli/src/index.ts", "args": [ "--config={projectRoot}/code-pushup.config.ts", "--cache.read", "--persist.outputDir=.code-pushup/{projectName}" ], "env": { - "NODE_OPTIONS": "--import tsx", - "TSX_TSCONFIG_PATH": "tsconfig.base.json" + "JITI_TS_CONFIG_PATH": "tsconfig.base.json" } } }, diff --git a/package-lock.json b/package-lock.json index b9c0986ff..9bc14cc43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "@nx/react": "22.3.3", "@nx/vite": "22.3.3", "@nx/workspace": "22.3.3", + "@push-based/jiti-tsc": "^0.0.2", "@push-based/nx-verdaccio": "0.0.7", "@swc-node/register": "1.9.2", "@swc/cli": "0.6.0", @@ -6316,6 +6317,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@push-based/jiti-tsc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@push-based/jiti-tsc/-/jiti-tsc-0.0.2.tgz", + "integrity": "sha512-9K8IYbZePUzPHDkA6tuOukFcJT6xu6bbFtWaws0CtVsmQaeqyRrKuSYFMl73OIee7oOXUOZS2QMIZp3ky6sh/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^3.3.2", + "jiti": "^2.4.2", + "ora": "^9.0.0", + "tslib": "^2.8.1", + "typescript": "5.7.3" + }, + "bin": { + "jiti-tsc": "src/bin/bin.js" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "zod": "^4.0.0" + } + }, "node_modules/@push-based/nx-verdaccio": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/@push-based/nx-verdaccio/-/nx-verdaccio-0.0.7.tgz", diff --git a/package.json b/package.json index a7b55cf4f..ba5389b73 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@nx/react": "22.3.3", "@nx/vite": "22.3.3", "@nx/workspace": "22.3.3", + "@push-based/jiti-tsc": "^0.0.2", "@push-based/nx-verdaccio": "0.0.7", "@swc-node/register": "1.9.2", "@swc/cli": "0.6.0", From cc442d1fb053bffd7f7d3a38441efe399e05a5ca Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 22 Jan 2026 22:56:40 +0100 Subject: [PATCH 2/3] refactor: move loadTargetConfig into utils --- .../src/lib/runner/ts-runner.ts | 8 +++-- .../plugin-typescript/src/lib/runner/utils.ts | 30 ------------------- packages/utils/package.json | 3 +- packages/utils/src/index.ts | 1 + .../src/lib/load-ts-config.int.test.ts} | 2 +- packages/utils/src/lib/load-ts-config.ts | 29 ++++++++++++++++++ 6 files changed, 39 insertions(+), 34 deletions(-) rename packages/{plugin-typescript/src/lib/runner/utils.int.test.ts => utils/src/lib/load-ts-config.int.test.ts} (97%) create mode 100644 packages/utils/src/lib/load-ts-config.ts diff --git a/packages/plugin-typescript/src/lib/runner/ts-runner.ts b/packages/plugin-typescript/src/lib/runner/ts-runner.ts index 6103b6e1e..d1541f126 100644 --- a/packages/plugin-typescript/src/lib/runner/ts-runner.ts +++ b/packages/plugin-typescript/src/lib/runner/ts-runner.ts @@ -3,8 +3,12 @@ import { createProgram, getPreEmitDiagnostics, } from 'typescript'; -import { logger, pluralizeToken, stringifyError } from '@code-pushup/utils'; -import { loadTargetConfig } from './utils.js'; +import { + loadTargetConfig, + logger, + pluralizeToken, + stringifyError, +} from '@code-pushup/utils'; export type DiagnosticsOptions = { tsconfig: string; diff --git a/packages/plugin-typescript/src/lib/runner/utils.ts b/packages/plugin-typescript/src/lib/runner/utils.ts index 23c3ee67b..48c68560c 100644 --- a/packages/plugin-typescript/src/lib/runner/utils.ts +++ b/packages/plugin-typescript/src/lib/runner/utils.ts @@ -3,9 +3,6 @@ import { type Diagnostic, DiagnosticCategory, flattenDiagnosticMessageText, - parseJsonConfigFileContent, - readConfigFile, - sys, } from 'typescript'; import type { Issue } from '@code-pushup/models'; import { truncateIssueMessage } from '@code-pushup/utils'; @@ -88,30 +85,3 @@ export function getIssueFromDiagnostic(diag: Diagnostic) { }, } satisfies Issue; } - -export function loadTargetConfig(tsConfigPath: string) { - const resolvedConfigPath = path.resolve(tsConfigPath); - const { config, error } = readConfigFile(resolvedConfigPath, sys.readFile); - - if (error) { - throw new Error( - `Error reading TypeScript config file at ${tsConfigPath}:\n${error.messageText}`, - ); - } - - const parsedConfig = parseJsonConfigFileContent( - config, - sys, - path.dirname(resolvedConfigPath), - {}, - resolvedConfigPath, - ); - - if (parsedConfig.fileNames.length === 0) { - throw new Error( - 'No files matched by the TypeScript configuration. Check your "include", "exclude" or "files" settings.', - ); - } - - return parsedConfig; -} diff --git a/packages/utils/package.json b/packages/utils/package.json index aed4bca83..990c0fd69 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -37,7 +37,8 @@ "simple-git": "^3.20.0", "string-width": "^8.1.0", "wrap-ansi": "^9.0.2", - "zod": "^4.2.1" + "zod": "^4.2.1", + "typescript": "5.7.3" }, "files": [ "src", diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index f019b8055..143df490f 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -32,6 +32,7 @@ export { type ProcessObserver, type ProcessResult, } from './lib/execute-process.js'; +export { loadTargetConfig } from './lib/load-ts-config.js'; export { crawlFileSystem, createReportPath, diff --git a/packages/plugin-typescript/src/lib/runner/utils.int.test.ts b/packages/utils/src/lib/load-ts-config.int.test.ts similarity index 97% rename from packages/plugin-typescript/src/lib/runner/utils.int.test.ts rename to packages/utils/src/lib/load-ts-config.int.test.ts index c202d59be..c8f326e58 100644 --- a/packages/plugin-typescript/src/lib/runner/utils.int.test.ts +++ b/packages/utils/src/lib/load-ts-config.int.test.ts @@ -1,7 +1,7 @@ import * as tsModule from 'typescript'; import { describe, expect, vi } from 'vitest'; import { osAgnosticPath } from '@code-pushup/test-utils'; -import { loadTargetConfig } from './utils.js'; +import { loadTargetConfig } from './load-ts-config.js'; describe('loadTargetConfig', () => { const readConfigFileSpy = vi.spyOn(tsModule, 'readConfigFile'); diff --git a/packages/utils/src/lib/load-ts-config.ts b/packages/utils/src/lib/load-ts-config.ts new file mode 100644 index 000000000..010732ebf --- /dev/null +++ b/packages/utils/src/lib/load-ts-config.ts @@ -0,0 +1,29 @@ +import path from 'node:path'; +import { parseJsonConfigFileContent, readConfigFile, sys } from 'typescript'; + +export function loadTargetConfig(tsConfigPath: string) { + const resolvedConfigPath = path.resolve(tsConfigPath); + const { config, error } = readConfigFile(resolvedConfigPath, sys.readFile); + + if (error) { + throw new Error( + `Error reading TypeScript config file at ${tsConfigPath}:\n${error.messageText}`, + ); + } + + const parsedConfig = parseJsonConfigFileContent( + config, + sys, + path.dirname(resolvedConfigPath), + {}, + resolvedConfigPath, + ); + + if (parsedConfig.fileNames.length === 0) { + throw new Error( + 'No files matched by the TypeScript configuration. Check your "include", "exclude" or "files" settings.', + ); + } + + return parsedConfig; +} From c9cb8957630e9381c508c46c8a5f2af115745899 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 22 Jan 2026 23:06:58 +0100 Subject: [PATCH 3/3] refactor: move importModule into extra file --- packages/utils/src/index.ts | 2 +- packages/utils/src/lib/file-system.ts | 19 ------------------ ....int.test.ts => import-module.int.test.ts} | 2 +- packages/utils/src/lib/import-module.ts | 20 +++++++++++++++++++ 4 files changed, 22 insertions(+), 21 deletions(-) rename packages/utils/src/lib/{file-system.int.test.ts => import-module.int.test.ts} (97%) create mode 100644 packages/utils/src/lib/import-module.ts diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 143df490f..a06e834b5 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -42,7 +42,6 @@ export { filePathToCliArg, findLineNumberInText, findNearestFile, - importModule, pluginWorkDir, projectToFilename, readJsonFile, @@ -179,3 +178,4 @@ export type { Prettify, WithRequired, } from './lib/types.js'; +export * from './lib/import-module.js'; diff --git a/packages/utils/src/lib/file-system.ts b/packages/utils/src/lib/file-system.ts index 5956dbbff..cbe72f547 100644 --- a/packages/utils/src/lib/file-system.ts +++ b/packages/utils/src/lib/file-system.ts @@ -1,9 +1,7 @@ -import { type Options, bundleRequire } from 'bundle-require'; import { mkdir, readFile, readdir, rm, stat } from 'node:fs/promises'; import path from 'node:path'; import type { Format, PersistConfig } from '@code-pushup/models'; import { logger } from './logger.js'; -import { settlePromise } from './promises.js'; export async function readTextFile(filePath: string): Promise { const buffer = await readFile(filePath); @@ -52,23 +50,6 @@ export async function removeDirectoryIfExists(dir: string) { } } -export async function importModule(options: Options): Promise { - const resolvedStats = await settlePromise(stat(options.filepath)); - if (resolvedStats.status === 'rejected') { - throw new Error(`File '${options.filepath}' does not exist`); - } - if (!resolvedStats.value.isFile()) { - throw new Error(`Expected '${options.filepath}' to be a file`); - } - - const { mod } = await bundleRequire(options); - - if (typeof mod === 'object' && 'default' in mod) { - return mod.default as T; - } - return mod as T; -} - export function createReportPath({ outputDir, filename, diff --git a/packages/utils/src/lib/file-system.int.test.ts b/packages/utils/src/lib/import-module.int.test.ts similarity index 97% rename from packages/utils/src/lib/file-system.int.test.ts rename to packages/utils/src/lib/import-module.int.test.ts index b355e1bb1..e347b423d 100644 --- a/packages/utils/src/lib/file-system.int.test.ts +++ b/packages/utils/src/lib/import-module.int.test.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import { describe, expect, it } from 'vitest'; -import { importModule } from './file-system.js'; +import { importModule } from './import-module.js'; describe('importModule', () => { const mockDir = path.join( diff --git a/packages/utils/src/lib/import-module.ts b/packages/utils/src/lib/import-module.ts new file mode 100644 index 000000000..eaefb187b --- /dev/null +++ b/packages/utils/src/lib/import-module.ts @@ -0,0 +1,20 @@ +import { type Options, bundleRequire } from 'bundle-require'; +import { stat } from 'node:fs/promises'; +import { settlePromise } from './promises.js'; + +export async function importModule(options: Options): Promise { + const resolvedStats = await settlePromise(stat(options.filepath)); + if (resolvedStats.status === 'rejected') { + throw new Error(`File '${options.filepath}' does not exist`); + } + if (!resolvedStats.value.isFile()) { + throw new Error(`Expected '${options.filepath}' to be a file`); + } + + const { mod } = await bundleRequire(options); + + if (typeof mod === 'object' && 'default' in mod) { + return mod.default as T; + } + return mod as T; +}