Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions apps/oxlint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"esquery": "^1.6.0",
"execa": "^9.6.0",
"jiti": "^2.6.0",
"rolldown": "catalog:",
"tsdown": "catalog:",
"type-fest": "^5.2.0",
"typescript": "catalog:",
Expand Down
79 changes: 78 additions & 1 deletion apps/oxlint/tsdown.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { defineConfig, type UserConfig } from 'tsdown';
import { join } from 'node:path';
import { defineConfig } from 'tsdown';

import type { UserConfig } from 'tsdown';
import type { Plugin } from 'rolldown';

const ASSERTS_PATH = join(import.meta.dirname, 'src-js/utils/asserts.ts');

const replaceAssertsPlugin = createReplaceAssertsPlugin();
const plugins = [replaceAssertsPlugin];

const commonConfig: UserConfig = {
format: 'esm',
Expand All @@ -22,6 +31,11 @@ const commonConfig: UserConfig = {
codegen: { removeWhitespace: false },
},
define: { DEBUG: 'false' },
plugins,
inputOptions: {
// For `replaceAssertsPlugin`
experimental: { nativeMagicString: true },
},
};

// Only generate `.d.ts` file for main export, not for CLI
Expand All @@ -44,7 +58,70 @@ const debugConfigs = configs.map((config) => ({
...config,
outDir: 'debug',
define: { DEBUG: 'true' },
plugins: plugins.filter((plugin) => plugin !== replaceAssertsPlugin),
}));
configs.push(...debugConfigs);

export default configs;

/**
* Create a plugin to remove imports of `assert*` functions from `src-js/utils/asserts.ts`,
* and replace those imports with empty function declarations.
*
* ```ts
* // Original code
* import { assertIs, assertIsNonNull } from '../utils/asserts.ts';
*
* // After transform
* function assertIs() {}
* function assertIsNonNull() {}
* ```
*
* Minifier can already remove all calls to these functions as dead code, but only if the functions are defined
* in the same file as the call sites.
*
* Problem is that `asserts.ts` is imported by files which end up in all output chunks.
* So without this transform, TSDown creates a shared chunk for `asserts.ts`. Minifier works chunk-by-chunk,
* so can't see that these functions are no-ops, and doesn't remove the function calls.
*
* Inlining these functions in each file solves the problem, and minifier removes all trace of them.
*
* @returns Plugin
*/
function createReplaceAssertsPlugin(): Plugin {
return {
name: 'replace-asserts',
transform: {
// Only process TS files in `src-js` directory
filter: { id: /\/src-js\/.+\.ts$/ },

async handler(code, id, meta) {
const magicString = meta.magicString!;
const program = this.parse(code, { lang: 'ts' });

stmts: for (const stmt of program.body) {
if (stmt.type !== 'ImportDeclaration') continue;

// Check if import is from `utils/asserts.ts`.
// `endsWith` check is just a shortcut to avoid resolving the specifier to a full path for most imports.
const source = stmt.source.value;
if (!source.endsWith('/asserts.ts') && !source.endsWith('/asserts.js')) continue;
// oxlint-disable-next-line no-await-in-loop
const importedId = await this.resolve(source, id);
if (importedId === null || importedId.id !== ASSERTS_PATH) continue;

// Replace `import` statement with empty function declarations
let functionsCode = '';
for (const specifier of stmt.specifiers) {
// Skip this `import` statement if it's a default or namespace import - can't handle those
if (specifier.type !== 'ImportSpecifier') continue stmts;
functionsCode += `function ${specifier.local.name}() {}\n`;
}
magicString.overwrite(stmt.start, stmt.end, functionsCode);
}

return { code: magicString };
},
},
};
}
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ catalog:
'@napi-rs/wasm-runtime': 1.0.7
'@types/node': ^24.0.0
publint: 0.3.15
rolldown: 1.0.0-beta.51
tsdown: 0.16.6
typescript: 5.9.3
vitest: 4.0.10
Expand Down
Loading