diff --git a/packages/knip/fixtures/cli-preprocessor/node_modules/@org/preprocessor/package.json b/packages/knip/fixtures/cli-preprocessor/node_modules/@org/preprocessor/package.json index c98c1fffa..3121a4739 100644 --- a/packages/knip/fixtures/cli-preprocessor/node_modules/@org/preprocessor/package.json +++ b/packages/knip/fixtures/cli-preprocessor/node_modules/@org/preprocessor/package.json @@ -1,4 +1,5 @@ { "name": "@org/preprocessor", - "type": "module" + "type": "module", + "main": "index.js" } diff --git a/packages/knip/fixtures/cli-reporter/node_modules/@org/reporter/package.json b/packages/knip/fixtures/cli-reporter/node_modules/@org/reporter/package.json index 870b26ae5..e2b1690ca 100644 --- a/packages/knip/fixtures/cli-reporter/node_modules/@org/reporter/package.json +++ b/packages/knip/fixtures/cli-reporter/node_modules/@org/reporter/package.json @@ -1,4 +1,5 @@ { "name": "@org/reporter", - "type": "module" + "type": "module", + "main": "index.js" } diff --git a/packages/knip/fixtures/load-cjs/index.js b/packages/knip/fixtures/load-cjs/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/knip/fixtures/load-cjs/package.json b/packages/knip/fixtures/load-cjs/package.json new file mode 100644 index 000000000..8dcebe3aa --- /dev/null +++ b/packages/knip/fixtures/load-cjs/package.json @@ -0,0 +1,4 @@ +{ + "name": "@fixtures/load-cjs", + "type": "commonjs" +} diff --git a/packages/knip/fixtures/load-esm-ts/index.ts b/packages/knip/fixtures/load-esm-ts/index.ts new file mode 100644 index 000000000..c3b625885 --- /dev/null +++ b/packages/knip/fixtures/load-esm-ts/index.ts @@ -0,0 +1,22 @@ +// SyntaxError: Cannot use 'import.meta' outside a module + +import.meta; +import.meta.resolve; +import.meta.url; + +// SyntaxError: Unexpected identifier 'Promise' + +Promise; + +// TypeError: Reflect.metadata is not a function + +function meow(target: unknown, _: unknown) {} + +@meow +export class Cat { + constructor() {} +} + +// SyntaxError: await is only valid in async functions and the top level bodies of modules + +await Promise.resolve('TLA'); diff --git a/packages/knip/fixtures/load-esm-ts/package.json b/packages/knip/fixtures/load-esm-ts/package.json new file mode 100644 index 000000000..60fe855d1 --- /dev/null +++ b/packages/knip/fixtures/load-esm-ts/package.json @@ -0,0 +1,4 @@ +{ + "name": "@fixtures/load-esm-ts", + "type": "module" +} diff --git a/packages/knip/fixtures/load-esm-ts/tsconfig.json b/packages/knip/fixtures/load-esm-ts/tsconfig.json new file mode 100644 index 000000000..9afd99df0 --- /dev/null +++ b/packages/knip/fixtures/load-esm-ts/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "esnext", + "experimentalDecorators": true, + "emitDecoratorMetadata": true + } +} diff --git a/packages/knip/fixtures/load-esm/index.js b/packages/knip/fixtures/load-esm/index.js new file mode 100644 index 000000000..0f775692e --- /dev/null +++ b/packages/knip/fixtures/load-esm/index.js @@ -0,0 +1,18 @@ +// The CJS build of Vite's Node API is deprecated. See https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details. + +// SyntaxError: Cannot use 'import.meta' outside a module + +import.meta; +import.meta.resolve; +import.meta.url; + +// SyntaxError: await is only valid in async functions and the top level bodies of modules + +// SyntaxError: missing ) after argument list + +// SyntaxError: Unexpected identifier 'Promise' +Promise; + +// Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in [...]/node_modules/estree-walker/package.json + +await Promise.resolve('TLA'); diff --git a/packages/knip/fixtures/load-esm/package.json b/packages/knip/fixtures/load-esm/package.json new file mode 100644 index 000000000..6d5fa0c50 --- /dev/null +++ b/packages/knip/fixtures/load-esm/package.json @@ -0,0 +1,4 @@ +{ + "name": "@fixtures/load-esm", + "type": "module" +} diff --git a/packages/knip/src/util/fs.ts b/packages/knip/src/util/fs.ts index 238ccda83..1c1e94e97 100644 --- a/packages/knip/src/util/fs.ts +++ b/packages/knip/src/util/fs.ts @@ -1,11 +1,11 @@ -import { readFileSync, statSync } from 'node:fs'; +import { statSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import yaml from 'js-yaml'; import { parse as parseTOML } from 'smol-toml'; import stripJsonComments from 'strip-json-comments'; import { timerify } from './Performance.js'; import { LoaderError } from './errors.js'; -import { dirname, join } from './path.js'; +import { join } from './path.js'; export const isDirectory = (filePath: string) => { try { @@ -64,18 +64,4 @@ export const parseYAML = (contents: string) => { return yaml.load(contents); }; -export function isTypeModule(path: string) { - while (path && path !== '.' && path !== '/') { - path = dirname(path); - try { - const pkg = readFileSync(join(path, 'package.json'), 'utf-8'); - try { - return JSON.parse(pkg).type === 'module'; - } catch {} - break; - } catch {} - } - return false; -} - export const _loadJSON = timerify(loadJSON); diff --git a/packages/knip/src/util/loader.ts b/packages/knip/src/util/loader.ts index 0578a88d9..fe607880b 100644 --- a/packages/knip/src/util/loader.ts +++ b/packages/knip/src/util/loader.ts @@ -1,8 +1,6 @@ -import { pathToFileURL } from 'node:url'; import { timerify } from './Performance.js'; import { LoaderError } from './errors.js'; import { loadFile, loadJSON, loadTOML, loadYAML, parseJSON, parseYAML } from './fs.js'; -import { isTypeModule } from './fs.js'; import { jiti } from './jiti.js'; import { extname, isInternal } from './path.js'; @@ -39,13 +37,7 @@ const load = async (filePath: string) => { return await loadTOML(filePath); } - if (ext === '.mjs' || (ext === '.js' && isTypeModule(filePath))) { - const fileUrl = pathToFileURL(filePath); - const imported = await import(fileUrl.href); - return imported.default ?? imported; - } - - return await jiti(filePath); + return await jiti.import(filePath); } catch (error) { throw new LoaderError(`Error loading ${filePath}`, { cause: error }); } diff --git a/packages/knip/test/util/load.test.ts b/packages/knip/test/util/load.test.ts new file mode 100644 index 000000000..217d8fc86 --- /dev/null +++ b/packages/knip/test/util/load.test.ts @@ -0,0 +1,19 @@ +import { test } from 'bun:test'; +import assert from 'node:assert/strict'; +import { _load as load } from '../../src/util/loader.js'; +import { join, resolve } from '../../src/util/path.js'; + +test('Should load modules (CommonJS)', async () => { + const cwd = resolve('fixtures/load-cjs'); + await assert.doesNotReject(load(join(cwd, 'index.js'))); +}); + +test('Should load modules (ESM)', async () => { + const cwd = resolve('fixtures/load-esm'); + await assert.doesNotReject(load(join(cwd, 'index.js'))); +}); + +test('Should load modules (ESM/TS)', async () => { + const cwd = resolve('fixtures/load-esm-ts'); + await assert.doesNotReject(load(join(cwd, 'index.ts'))); +});