From a034fd6e211012c9051e8e23f6dcc56a1fb67ddf Mon Sep 17 00:00:00 2001 From: KCM Date: Mon, 27 May 2024 20:39:59 -0500 Subject: [PATCH] feat!: specifier, out options. (#3) BREAKING CHANGE: renamed option specifiers to specifier. --- README.md | 9 ++- eslint.config.js | 4 +- package-lock.json | 116 ++++++++++++++++++++---------------- package.json | 5 +- src/module.ts | 34 ++++++++--- src/types.ts | 4 +- test/fixtures/file.cjs | 5 +- test/fixtures/file.mjs | 2 +- test/fixtures/specifier.cjs | 3 + test/fixtures/specifier.mjs | 10 ++++ test/module.ts | 69 +++++++++++++++++++-- 11 files changed, 189 insertions(+), 72 deletions(-) create mode 100644 test/fixtures/specifier.cjs create mode 100644 test/fixtures/specifier.mjs diff --git a/README.md b/README.md index 05a9703..56de234 100644 --- a/README.md +++ b/README.md @@ -89,8 +89,13 @@ type ModuleOptions = { /* Whether import/export and require/exports should be transformed. */ modules?: boolean /* Whether to change specifier extensions to the assigned value. If omitted they are left alone. */ - specifiers?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts' - /* What filepath to write the transformed source to. If omitted the transformed source is returned. */ + specifier?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts' + /* What filepath to write the transformed source to. */ out?: string } ``` + +## Roadmap + +- Support option `modules`. +- Remove `@knighted/specifier` and avoid double parsing. diff --git a/eslint.config.js b/eslint.config.js index 6169c82..02a6f39 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -12,10 +12,12 @@ export default tseslint.config( }, }, { - files: ['test/**/*.cjs'], + files: ['test/fixtures/**/*'], rules: { 'no-undef': 'off', + 'n/no-missing-import': 'off', '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-expressions': 'off', }, }, ) diff --git a/package-lock.json b/package-lock.json index 4e708b6..86bdb31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "@knighted/module", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@knighted/module", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "license": "MIT", "dependencies": { "@babel/parser": "^7.24.6", "@babel/traverse": "^7.24.6", + "@knighted/specifier": "^2.0.0-rc.1", "magic-string": "^0.30.10" }, "devDependencies": { @@ -27,7 +28,7 @@ "prettier": "^3.2.5", "tsx": "^4.11.0", "typescript": "^5.4.5", - "typescript-eslint": "^8.0.0-alpha.16" + "typescript-eslint": "^8.0.0-alpha.18" }, "engines": { "node": ">=20.11.0" @@ -2565,6 +2566,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@knighted/specifier": { + "version": "2.0.0-rc.1", + "resolved": "https://registry.npmjs.org/@knighted/specifier/-/specifier-2.0.0-rc.1.tgz", + "integrity": "sha512-VnvW1yZtm2Kd9h7LxEcYXJfg26XE4kDms2FxrVozrdgXnwZKNYlDlhNVDHtHVDUeDRwGHshZy7pKl+G1INZUCQ==", + "dependencies": { + "@babel/parser": "^7.24.6", + "@babel/traverse": "^7.24.6", + "magic-string": "^0.30.10" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2641,16 +2655,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.16.tgz", - "integrity": "sha512-ZDVgR/z28jg3CPzQJqFIOQ/gshqf3NDw7zCu2jTeAYqtyXpCsAkAivvkeuuuXCypRl53cK16qDPlCguUCZW5Ow==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.18.tgz", + "integrity": "sha512-xZPpYaoO2HMBlI46kfvs9+Rq8BtsruP3cY2xG3ovpVDz4zDuENXhUOywgd5Xl3LAJNVAB+sj1lsATkxnAbEYaQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.0.0-alpha.16", - "@typescript-eslint/type-utils": "8.0.0-alpha.16", - "@typescript-eslint/utils": "8.0.0-alpha.16", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.16", + "@typescript-eslint/scope-manager": "8.0.0-alpha.18", + "@typescript-eslint/type-utils": "8.0.0-alpha.18", + "@typescript-eslint/utils": "8.0.0-alpha.18", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.18", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2674,15 +2688,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.16.tgz", - "integrity": "sha512-L8eX2ggDQqb986+P9FZVsl/4M0vPplvgVzPkFFtPtsP2rVRSFpzGidZGzNN73RBq2G5KnWo87sx2mUrJ+99OZQ==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.18.tgz", + "integrity": "sha512-E2ot032IE5Vu2PfH4pMNWxf/Ihp9h+baZHnL8iNN1kfno2IKL9BxZ/3lHSnNxQQSQ63Cb7J5lrzrKh2lfjTHcQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.0.0-alpha.16", - "@typescript-eslint/types": "8.0.0-alpha.16", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.16", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.16", + "@typescript-eslint/scope-manager": "8.0.0-alpha.18", + "@typescript-eslint/types": "8.0.0-alpha.18", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.18", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.18", "debug": "^4.3.4" }, "engines": { @@ -2702,13 +2716,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.16.tgz", - "integrity": "sha512-SsN6Kf+sBK62CgDkW4XHZYDqCDwOY2d1Q4aUAOTcohhw06HiXYbY5xQ23GqOV2BL9TaKL+HuyyP+LLZ1aIG8FQ==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.18.tgz", + "integrity": "sha512-uJAAJI4IyKtoRiWkMLeQapjBSbWg9Yj11e3wvsrW5F1V5IxfXZHyakCoXr4Js5oHwFOjHJKkmL/ltnCgUwCWKQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.16", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.16" + "@typescript-eslint/types": "8.0.0-alpha.18", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.18" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2719,13 +2733,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.16.tgz", - "integrity": "sha512-g5GJ0sB6WLu71fkPlMe9JV1o3p6AKAN0vUfg4XGyYPLSElRYdMMy4Nuq1Snq2Gqs1rceomHrogp5v/qH7Iq7ig==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.18.tgz", + "integrity": "sha512-BbrQWvBmnOKf/LuGvfJDAsRKJ6kjqjYKheKP5DysDQUTqxqcxMQINjFAf7bqdMtcS7ZmtDTVhjnIEqiVGnWw1A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.0.0-alpha.16", - "@typescript-eslint/utils": "8.0.0-alpha.16", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.18", + "@typescript-eslint/utils": "8.0.0-alpha.18", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2743,9 +2757,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.16.tgz", - "integrity": "sha512-06m3u1WIT49iYLK2GJWdT7Lmx54pX8imcW06AFnmgMXYDQsTZDdNXpHM6vwwL29LAWDv44j8g+eDPjJ4UNNiCA==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.18.tgz", + "integrity": "sha512-HCOpBQpmRio9njMBRLC7sSGo9A0z9RBcePI5SBr4FNCzfsWRyND5UixJiKLrB5KB4EzS8xD9zlykmZZwI5qwAw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2756,13 +2770,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.16.tgz", - "integrity": "sha512-q5FvwPYGHmDF4/J7ssWMBHKDRY/3ar1PNoKTMYh/1foSCJ2e/Hv/GTuc63h03xi12IRyTn8R/M/56vH6qd+rSQ==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.18.tgz", + "integrity": "sha512-0e9nI8+tff0hWKgMlo/fYEc+OzWD2CAeq5pmUIqQ4f+ErEGUp6o6G/8thB6PL1hrg9EwA08v6tiJu4Rsh3BhZw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.16", - "@typescript-eslint/visitor-keys": "8.0.0-alpha.16", + "@typescript-eslint/types": "8.0.0-alpha.18", + "@typescript-eslint/visitor-keys": "8.0.0-alpha.18", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2808,15 +2822,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.16.tgz", - "integrity": "sha512-u7mFyhJ4/jX7VaGieK+BC+PynvCH8fdr4Gie4RXO9bclvGAvMTzk62UZ65t90KN25M9/tvodxUoaZS4W4MQSNg==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.18.tgz", + "integrity": "sha512-jh49v88o8/wtYz+8mjBYguQFfB5v6X5keDCR3b6WMK186uSI6J2rtfUqi/X6InNaGJbm/abdWCgzH2wMzsHPlw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.0.0-alpha.16", - "@typescript-eslint/types": "8.0.0-alpha.16", - "@typescript-eslint/typescript-estree": "8.0.0-alpha.16" + "@typescript-eslint/scope-manager": "8.0.0-alpha.18", + "@typescript-eslint/types": "8.0.0-alpha.18", + "@typescript-eslint/typescript-estree": "8.0.0-alpha.18" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2830,12 +2844,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.16.tgz", - "integrity": "sha512-vSmfkS6FVBW1lhuf700XjcbQXtoXg3Aqbi+axsFYPNr/6oEkpLRonbKMxBzj4cGTnL/3sJl+gDVQSS7fVHWz3A==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.18.tgz", + "integrity": "sha512-tFb5ayafXLel1AEa1VdYvpT7WZNpHaF8jQNSXHESBqJdk/kXyJ3E8cTn11Hqt6Zj1XyNbE/Hn/5u0tQlFUJkvg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.0.0-alpha.16", + "@typescript-eslint/types": "8.0.0-alpha.18", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -5105,14 +5119,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.0.0-alpha.16", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.16.tgz", - "integrity": "sha512-hseQjFKLOZXuBjGgEoYWKD+EL1yd2nVvqL9TLq8RELE1ZGkha15WS98GfwpREZkak+CuTPNsRHHNxeXUesQ/DA==", + "version": "8.0.0-alpha.18", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.18.tgz", + "integrity": "sha512-2xpCZAL0ta9GkvlssfLAhlfgCii1tjlofdBadTH1M+h2mg4QnXuumdMq4WzxQ0l0WM0oOZ4uCJ+WhtKCvyIzfw==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.0.0-alpha.16", - "@typescript-eslint/parser": "8.0.0-alpha.16", - "@typescript-eslint/utils": "8.0.0-alpha.16" + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.18", + "@typescript-eslint/parser": "8.0.0-alpha.18", + "@typescript-eslint/utils": "8.0.0-alpha.18" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 00c8038..309d1aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@knighted/module", - "version": "1.0.0-alpha.2", + "version": "1.0.0-alpha.3", "description": "Converts module differences in source files between ES and CommonJS.", "type": "module", "main": "dist/module.js", @@ -70,11 +70,12 @@ "prettier": "^3.2.5", "tsx": "^4.11.0", "typescript": "^5.4.5", - "typescript-eslint": "^8.0.0-alpha.16" + "typescript-eslint": "^8.0.0-alpha.18" }, "dependencies": { "@babel/parser": "^7.24.6", "@babel/traverse": "^7.24.6", + "@knighted/specifier": "^2.0.0-rc.1", "magic-string": "^0.30.10" }, "prettier": { diff --git a/src/module.ts b/src/module.ts index b0f9c35..74b5d16 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,18 +1,17 @@ import { resolve } from 'node:path' -import { readFile } from 'node:fs/promises' +import { readFile, writeFile } from 'node:fs/promises' + +import { specifier } from '@knighted/specifier' import { parse } from './parse.js' import { format } from './format.js' import type { ModuleOptions } from './types.js' -/** - * Defaults to only transforming ES module globals to CommonJS. - */ const defaultOptions = { type: 'commonjs', out: undefined, modules: false, - specifiers: undefined, + specifier: undefined, } satisfies ModuleOptions const transform = async (filename: string, options: ModuleOptions = defaultOptions) => { @@ -20,9 +19,30 @@ const transform = async (filename: string, options: ModuleOptions = defaultOptio const file = resolve(filename) const code = (await readFile(file)).toString() const ast = parse(code) + let source = format(code, ast, opts).toString() + + if (options.specifier) { + const { code, error } = await specifier.updateSrc(source, ({ value }) => { + // Collapse any BinaryExpression or NewExpression to test for a relative specifier + const collapsed = value.replace(/['"`+)\s]|new String\(/g, '') + const relative = /^(?:\.|\.\.)\// + + if (relative.test(collapsed)) { + // $2 is for any closing quotation/parens around BE or NE + return value.replace(/(.+)\.(?:m|c)?(?:j|t)s([)'"`]*)?$/, `$1${options.specifier}$2`) + } + }) + + if (code && !error) { + source = code + } + } + + if (opts.out) { + await writeFile(resolve(opts.out), source) + } - // TODO: Support `out` option. - return format(code, ast, opts).toString() + return source } export { transform } diff --git a/src/types.ts b/src/types.ts index 15ec39d..babef9a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,8 +4,8 @@ export type ModuleOptions = { /* Whether import/export and require/exports should be transformed. */ modules?: boolean /* Whether to change specifier extensions to the assigned value. If omitted they are left alone. */ - specifiers?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts' - /* What filepath to write the transformed source to. If omitted the transformed source is returned. */ + specifier?: '.js' | '.mjs' | '.cjs' | '.ts' | '.mts' | '.cts' + /* What filepath to write the transformed source to. */ out?: string } diff --git a/test/fixtures/file.cjs b/test/fixtures/file.cjs index 1d79cd7..0f5240b 100644 --- a/test/fixtures/file.cjs +++ b/test/fixtures/file.cjs @@ -1,13 +1,13 @@ // CommonJS was like the Wild Wild West (require.extensions is deprecated so meh). // Too much implied behavior without requiring explicitness. -const { foo } = require('./test.js') +const { esmodule } = require('./file.mjs') __filename __dirname require.main require.cache -require.resolve('./test.js') +require.resolve('./file.mjs') module module.exports @@ -15,3 +15,4 @@ module.exports = foo exports exports.commonjs = true +exports.esmodule = esmodule diff --git a/test/fixtures/file.mjs b/test/fixtures/file.mjs index c6b79ad..f3067a3 100644 --- a/test/fixtures/file.mjs +++ b/test/fixtures/file.mjs @@ -4,7 +4,7 @@ import.meta import.meta.url import.meta.dirname import.meta.filename -import.meta.resolve('./test.js') +import.meta.resolve('./file.cjs') export const esmodule = true export { commonjs } diff --git a/test/fixtures/specifier.cjs b/test/fixtures/specifier.cjs new file mode 100644 index 0000000..f60dd99 --- /dev/null +++ b/test/fixtures/specifier.cjs @@ -0,0 +1,3 @@ +require('./file.cjs') +require.resolve('./file.cjs') +import('./file.cjs') diff --git a/test/fixtures/specifier.mjs b/test/fixtures/specifier.mjs new file mode 100644 index 0000000..1f0a801 --- /dev/null +++ b/test/fixtures/specifier.mjs @@ -0,0 +1,10 @@ +import foo from './file.mts' +import bar from './file.cts' +import baz from './file.ts' + +import(`./${foo}${bar}${baz}.cjs`) +import(new String('not-relative.js')) +import(new String('./file.mjs')) + +import.meta.resolve('./file.cjs') +import.meta.resolve('./file.js') diff --git a/test/module.ts b/test/module.ts index ca77d41..316f584 100644 --- a/test/module.ts +++ b/test/module.ts @@ -1,13 +1,30 @@ import { describe, it } from 'node:test' import assert from 'node:assert/strict' import { resolve, join } from 'node:path' +import { rm, stat } from 'node:fs/promises' +import type { Stats } from 'node:fs' import { transform } from '../src/module.js' const fixtures = resolve(import.meta.dirname, 'fixtures') +const isValidFilename = async (filename: string) => { + let stats: Stats + + try { + stats = await stat(filename) + } catch { + return false + } + + if (!stats.isFile()) { + return false + } + + return true +} describe('@knighted/module', () => { - it('transforms es module scope globals to commonjs module scope globals', async () => { + it('transforms es module globals to commonjs globals', async () => { const result = await transform(join(fixtures, 'file.mjs')) assert.equal(result.indexOf('import.meta.url'), -1) @@ -17,13 +34,13 @@ describe('@knighted/module', () => { assert.ok(result.indexOf('require("node:url").pathToFileURL(__filename).toString()') > -1) assert.ok(result.indexOf('__dirname') > -1) assert.ok(result.indexOf('__filename') > -1) - assert.ok(result.indexOf("require.resolve('./test.js')") > -1) + assert.ok(result.indexOf('require.resolve(') > -1) // Check `import.meta` transformation assert.equal(/import\.meta\s/.test(result), false) assert.ok(result.indexOf('require.main') > -1) }) - it('transforms commonjs module scope globals to es module scope globals', async () => { + it('transforms commonjs globals to es module globals', async () => { const result = await transform(join(fixtures, 'file.cjs'), { type: 'module' }) assert.equal(result.indexOf('__filename'), -1) @@ -33,10 +50,54 @@ describe('@knighted/module', () => { assert.ok(result.indexOf('import.meta.dirname') > -1) assert.ok(result.indexOf('import.meta.resolve') > -1) // Check `module`, `exports` and `require.cache` - assert.equal(!/module\s/.test(result), true) + assert.equal(!/\smodule\s/.test(result), true) assert.equal(!/\sexports\s/.test(result), true) assert.equal(result.indexOf('require.cache'), -1) assert.equal(/import\.meta\s/.test(result), true) assert.ok(result.indexOf('{}') > -1) }) + + it('updates specifiers when option enabled', async () => { + const result = await transform(join(fixtures, 'specifier.mjs'), { specifier: '.js' }) + const cjsResult = await transform(join(fixtures, 'specifier.cjs'), { type: 'module', specifier: '.mjs' }) + + assert.equal((result.match(/\.\/file\.js/g) ?? []).length, 6) + assert.equal((result.match(/require\.resolve\('\.\/file\.js'\)/g) ?? []).length, 2) + assert.equal((cjsResult.match(/\.\/file\.mjs/g) ?? []).length, 3) + assert.equal((cjsResult.match(/import\.meta\.resolve\('\.\/file\.mjs'\)/g) ?? []).length, 1) + }) + + it('writes transformed source to a file when option enabled', async t => { + const mjs = join(fixtures, 'transformed.mjs') + const cjs = join(fixtures, 'transformed.cjs') + + t.after(() => { + rm(mjs, { force: true }) + rm(cjs, { force: true }) + }) + + await transform(join(fixtures, 'file.mjs'), { type: 'commonjs', out: cjs }) + await transform(join(fixtures, 'file.cjs'), { type: 'module', out: mjs }) + + assert.equal(await isValidFilename(mjs), true) + assert.equal(await isValidFilename(cjs), true) + + // When option `modules` is complete + /* + // Check for runtime errors against Node.js + const { status: statusEsm } = spawnSync( + 'node', + [mjs], + { stdio: 'inherit' }, + ) + assert.equal(statusEsm, 0) + + const { status: statusCjs } = spawnSync( + 'node', + [cjs], + { stdio: 'inherit' }, + ) + assert.equal(statusCjs, 0) + */ + }) })