diff --git a/apps/oxlint/.gitignore b/apps/oxlint/.gitignore index 3db66a0047980..7d6c1bcaaad85 100644 --- a/apps/oxlint/.gitignore +++ b/apps/oxlint/.gitignore @@ -1,3 +1,4 @@ /node_modules/ /dist/ +/debug/ *.node diff --git a/apps/oxlint/package.json b/apps/oxlint/package.json index 78db6a130730f..2c6a191332398 100644 --- a/apps/oxlint/package.json +++ b/apps/oxlint/package.json @@ -47,6 +47,6 @@ ] }, "imports": { - "#oxlint": "./dist/index.js" + "#oxlint": "./debug/index.js" } } diff --git a/apps/oxlint/scripts/build.ts b/apps/oxlint/scripts/build.ts index 7c356f92ac956..6b0182fc66437 100755 --- a/apps/oxlint/scripts/build.ts +++ b/apps/oxlint/scripts/build.ts @@ -3,7 +3,8 @@ import { copyFileSync, readdirSync, readFileSync, writeFileSync } from 'node:fs' import { join } from 'node:path'; const oxlintDirPath = join(import.meta.dirname, '..'), - distDirPath = join(oxlintDirPath, 'dist'); + distDirPath = join(oxlintDirPath, 'dist'), + debugDirPath = join(oxlintDirPath, 'debug'); // Modify `bindings.js` to use correct package names console.log('Modifying bindings.js...'); @@ -26,6 +27,7 @@ const srcDirPath = join(oxlintDirPath, 'src-js'); for (const filename of readdirSync(srcDirPath)) { if (!filename.endsWith('.node')) continue; copyFileSync(join(srcDirPath, filename), join(distDirPath, filename)); + copyFileSync(join(srcDirPath, filename), join(debugDirPath, filename)); } console.log('Build complete!'); diff --git a/apps/oxlint/src-js/globals.d.ts b/apps/oxlint/src-js/globals.d.ts new file mode 100644 index 0000000000000..ee7e100c44946 --- /dev/null +++ b/apps/oxlint/src-js/globals.d.ts @@ -0,0 +1,2 @@ +// Global constant defined at build time by TSDown +declare const DEBUG: boolean; diff --git a/apps/oxlint/src-js/plugins/utils.ts b/apps/oxlint/src-js/plugins/utils.ts index c5467bed9ce0e..e496510acab5f 100644 --- a/apps/oxlint/src-js/plugins/utils.ts +++ b/apps/oxlint/src-js/plugins/utils.ts @@ -45,13 +45,20 @@ export function assertIs(value: unknown): asserts value is T {} /** * Assert a value is not `null` or `undefined`. * - * Has no runtime effect - only for guiding the type-checker. - * Minification removes this function and all calls to it, so it has zero runtime cost. + * In release builds, is a no-op. Only does runtime checks in debug builds. + * Minification removes this function and all calls to it in release builds, so it has zero runtime cost. * * @param value - Value */ // oxlint-disable-next-line no-unused-vars -export function assertIsNonNull(value: T | null | undefined): asserts value is T {} +export function assertIsNonNull(value: T | null | undefined): asserts value is T { + if (!DEBUG) return; + + if (value === null || value === undefined) { + // oxlint-disable-next-line typescript/restrict-template-expressions + throw new Error(`Expected non-null value, got ${value}`); + } +} /** * Utility type to make specified properties of a type nullable. diff --git a/apps/oxlint/test/e2e.test.ts b/apps/oxlint/test/e2e.test.ts index a74a20cd5d01f..56c976c0896c2 100644 --- a/apps/oxlint/test/e2e.test.ts +++ b/apps/oxlint/test/e2e.test.ts @@ -4,7 +4,7 @@ import { PACKAGE_ROOT_PATH, getFixtures, testFixtureWithCommand } from './utils. import type { Fixture } from './utils.ts'; -const CLI_PATH = pathJoin(PACKAGE_ROOT_PATH, 'dist/cli.js'); +const CLI_PATH = pathJoin(PACKAGE_ROOT_PATH, 'debug/cli.js'); // Use current NodeJS executable, rather than `node`, to avoid problems with a Node version manager // installed on system resulting in using wrong NodeJS version diff --git a/apps/oxlint/tsdown.config.ts b/apps/oxlint/tsdown.config.ts index 03d95794d3cf9..9ceaf9e292b42 100644 --- a/apps/oxlint/tsdown.config.ts +++ b/apps/oxlint/tsdown.config.ts @@ -21,10 +21,11 @@ const commonConfig: UserConfig = { mangle: false, codegen: { removeWhitespace: false }, }, + define: { DEBUG: 'false' }, }; // Only generate `.d.ts` file for main export, not for CLI -export default defineConfig([ +const configs = defineConfig([ { entry: 'src-js/cli.ts', ...commonConfig, @@ -37,3 +38,13 @@ export default defineConfig([ attw: true, }, ]); + +// Create separate debug build with debug assertions enabled +const debugConfigs = configs.map((config) => ({ + ...config, + outDir: 'debug', + define: { DEBUG: 'true' }, +})); +configs.push(...debugConfigs); + +export default configs; diff --git a/apps/oxlint/vitest.config.ts b/apps/oxlint/vitest.config.ts index 9fef8e402cca0..b51b6d82f4ac8 100644 --- a/apps/oxlint/vitest.config.ts +++ b/apps/oxlint/vitest.config.ts @@ -4,4 +4,7 @@ export default defineConfig({ test: { exclude: [...configDefaults.exclude, 'fixtures/**'], }, + define: { + DEBUG: 'true', + }, });