diff --git a/package.json b/package.json index 7cc9f4ab9..49510830c 100644 --- a/package.json +++ b/package.json @@ -116,8 +116,8 @@ "@cspotcode/ava-lib": "https://github.com/cspotcode/ava-lib#805aab17b2b89c388596b6dc2b4eece403c5fb87", "@cspotcode/expect-stream": "https://github.com/cspotcode/node-expect-stream#4e425ff1eef240003af8716291e80fbaf3e3ae8f", "@microsoft/api-extractor": "^7.19.4", - "@swc/core": "1.3.32", - "@swc/wasm": "1.3.32", + "@swc/core": "1.3.85", + "@swc/wasm": "1.3.85", "@types/diff": "^4.0.2", "@types/lodash": "^4.14.151", "@types/node": "13.13.5", @@ -145,8 +145,8 @@ "workaround-broken-npm-prepack-behavior": "https://github.com/cspotcode/workaround-broken-npm-prepack-behavior#1a7adbbb8a527784daf97edad6ba42d6e96611f6" }, "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", + "@swc/core": ">=1.3.85", + "@swc/wasm": ">=1.3.85", "@types/node": "*", "typescript": ">=4.4" }, diff --git a/src/index.ts b/src/index.ts index c7f320530..894c17dba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1158,7 +1158,7 @@ export function createFromPreloadedConfig(foundConfigResult: ReturnType { .create({ swc: true, skipProject: true, - compilerOptions: { - module: 'esnext', - ...compilerOptions, - }, + compilerOptions, }) .compile(input, 'input.tsx'); expect(code.replace(/\/\/# sourceMappingURL.*/, '').trim()).toBe(expectedOutput); @@ -93,12 +99,17 @@ test.suite('swc', (test) => { const div =
; `; - test(compileMacro, { jsx: 'react' }, input, `const div = /*#__PURE__*/ React.createElement("div", null);`); + test( + compileMacro, + { module: 'esnext', jsx: 'react' }, + input, + `const div = /*#__PURE__*/ React.createElement("div", null);` + ); test.suite('react 17 jsx factories', (test) => { test.if(tsSupportsReact17JsxFactories); test( compileMacro, - { jsx: 'react-jsx' }, + { module: 'esnext', jsx: 'react-jsx' }, input, outdent` import { jsx as _jsx } from "react/jsx-runtime"; @@ -107,7 +118,7 @@ test.suite('swc', (test) => { ); test( compileMacro, - { jsx: 'react-jsxdev' }, + { module: 'esnext', jsx: 'react-jsxdev' }, input, outdent` import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime"; @@ -139,4 +150,166 @@ test.suite('swc', (test) => { ` ); }); + + test.suite('useDefineForClassFields', (test) => { + const input = outdent` + class Foo { + bar = 1; + } + `; + const outputNative = outdent` + let Foo = class Foo { + bar = 1; + }; + `; + const outputCtorAssignment = outdent` + let Foo = class Foo { + constructor(){ + this.bar = 1; + } + }; + `; + const outputDefine = outdent` + function _define_property(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; + } + let Foo = class Foo { + constructor(){ + _define_property(this, "bar", 1); + } + }; + `; + test( + 'useDefineForClassFields unset, should default to true and emit native property assignment b/c `next` target', + compileMacro, + { module: 'esnext', target: 'ESNext' }, + input, + outputNative + ); + test( + 'useDefineForClassFields unset, should default to true and emit native property assignment b/c new target', + compileMacro, + { module: 'esnext', target: 'ES2022' }, + input, + outputNative + ); + test( + 'useDefineForClassFields unset, should default to false b/c old target', + compileMacro, + { module: 'esnext', target: 'ES2021' }, + input, + outputCtorAssignment + ); + test( + 'useDefineForClassFields=true, should emit native property assignment b/c new target', + compileMacro, + { + module: 'esnext', + useDefineForClassFields: true, + target: 'ES2022', + }, + input, + outputNative + ); + test( + 'useDefineForClassFields=true, should emit define b/c old target', + compileMacro, + { + module: 'esnext', + useDefineForClassFields: true, + target: 'ES2021', + }, + input, + outputDefine + ); + test( + 'useDefineForClassFields=false, new target, should still emit legacy property assignment in ctor', + compileMacro, + { + module: 'esnext', + useDefineForClassFields: false, + target: 'ES2022', + }, + input, + outputCtorAssignment + ); + test( + 'useDefineForClassFields=false, old target, should emit legacy property assignment in ctor', + compileMacro, + { + module: 'esnext', + useDefineForClassFields: false, + }, + input, + outputCtorAssignment + ); + }); + + test.suite('jsx and jsxImportSource', (test) => { + test( + 'jsx=react-jsx', + compileMacro, + { + module: 'esnext', + jsx: 'react-jsx', + }, + outdent` +
+ `, + outdent` + /*#__PURE__*/ import { jsx as _jsx } from "react/jsx-runtime"; + _jsx("div", {}); + ` + ); + test( + 'jsx=react-jsx w/custom jsxImportSource', + compileMacro, + { + module: 'esnext', + jsx: 'react-jsx', + jsxImportSource: 'foo', + }, + outdent` +
+ `, + outdent` + /*#__PURE__*/ import { jsx as _jsx } from "foo/jsx-runtime"; + _jsx("div", {}); + ` + ); + }); + + test.suite( + '#1996 regression: ts-node gracefully allows swc to not return a sourcemap for type-only files', + (test) => { + // https://github.com/TypeStrong/ts-node/issues/1996 + // @swc/core 1.3.51 returned `undefined` instead of sourcemap if the file was empty or only exported types. + // Newer swc versions do not do this. But our typedefs technically allow it. + const exec = createExec({ + cwd: join(TEST_DIR, '1996'), + }); + test('import empty file w/swc', async (t) => { + const r = await exec(`${CMD_TS_NODE_WITHOUT_PROJECT_FLAG} ./index.ts`); + expect(r.err).toBe(null); + expect(r.stdout).toMatch(/#1996 regression test./); + }); + test('use custom transpiler which never returns a sourcemap', async (t) => { + const r = await exec( + `${CMD_TS_NODE_WITHOUT_PROJECT_FLAG} --project tsconfig.custom-transpiler.json ./empty.ts` + ); + expect(r.err).toBe(null); + expect(r.stdout).toMatch(/#1996 regression test with custom transpiler./); + }); + } + ); }); diff --git a/src/transpilers/swc.ts b/src/transpilers/swc.ts index 16868cad2..ea41d534c 100644 --- a/src/transpilers/swc.ts +++ b/src/transpilers/swc.ts @@ -3,8 +3,9 @@ import type * as swcWasm from '@swc/wasm'; import type * as swcTypes from '@swc/core'; import type { CreateTranspilerOptions, Transpiler } from './types'; import type { NodeModuleEmitKind } from '..'; +import { getUseDefineForClassFields } from '../ts-internals'; -type SwcInstance = typeof swcWasm; +type SwcInstance = typeof swcTypes; export interface SwcTranspilerOptions extends CreateTranspilerOptions { /** * swc compiler to use for compilation @@ -28,7 +29,7 @@ export function create(createOptions: SwcTranspilerOptions): Transpiler { let swcDepName: string = 'swc'; if (typeof swc === 'string') { swcDepName = swc; - swcInstance = require(transpilerConfigLocalResolveHelper(swc, true)) as typeof swcWasm; + swcInstance = require(transpilerConfigLocalResolveHelper(swc, true)) as SwcInstance; } else if (swc == null) { let swcResolved; try { @@ -44,9 +45,9 @@ export function create(createOptions: SwcTranspilerOptions): Transpiler { ); } } - swcInstance = require(swcResolved) as typeof swcWasm; + swcInstance = require(swcResolved) as SwcInstance; } else { - swcInstance = swc; + swcInstance = swc as any as SwcInstance; } // Prepare SWC options derived from typescript compiler options @@ -142,6 +143,7 @@ export function createSwcOptions( strict, alwaysStrict, noImplicitUseStrict, + jsxImportSource, } = compilerOptions; let swcTarget = targetMapping.get(target!) ?? 'es3'; @@ -194,6 +196,8 @@ export function createSwcOptions( jsx === JsxEmit.ReactJSX || jsx === JsxEmit.ReactJSXDev ? 'automatic' : undefined; const jsxDevelopment: swcTypes.ReactConfig['development'] = jsx === JsxEmit.ReactJSXDev ? true : undefined; + const useDefineForClassFields = getUseDefineForClassFields(compilerOptions); + const nonTsxOptions = createVariant(false); const tsxOptions = createVariant(true); return { nonTsxOptions, tsxOptions }; @@ -204,11 +208,15 @@ export function createSwcOptions( // isModule: true, module: moduleType ? { - noInterop: !esModuleInterop, type: moduleType, - strictMode, - // For NodeNext and Node12, emit as CJS but do not transform dynamic imports - ignoreDynamic: nodeModuleEmitKind === 'nodecjs', + ...(moduleType === 'amd' || moduleType === 'commonjs' || moduleType === 'umd' + ? { + noInterop: !esModuleInterop, + strictMode, + // For NodeNext and Node12, emit as CJS but do not transform dynamic imports + ignoreDynamic: nodeModuleEmitKind === 'nodecjs', + } + : {}), } : undefined, swcrc: false, @@ -232,12 +240,15 @@ export function createSwcOptions( pragma: jsxFactory!, pragmaFrag: jsxFragmentFactory!, runtime: jsxRuntime, + importSource: jsxImportSource, }, + useDefineForClassFields, }, keepClassNames, experimental: { - keepImportAssertions: true, - }, + keepImportAttributes: true, + emitAssertForImportAttributes: true, + } as swcTypes.JscConfig['experimental'], }, }; diff --git a/src/ts-internals.ts b/src/ts-internals.ts index 9c37f43e1..81fb3070c 100644 --- a/src/ts-internals.ts +++ b/src/ts-internals.ts @@ -329,3 +329,28 @@ function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: st function isImplicitGlob(lastPathComponent: string): boolean { return !/[.*?]/.test(lastPathComponent); } + +const ts_ScriptTarget_ES5 = 1; +const ts_ScriptTarget_ES2022 = 9; +const ts_ScriptTarget_ESNext = 99; +const ts_ModuleKind_Node16 = 100; +const ts_ModuleKind_NodeNext = 199; +// https://github.com/microsoft/TypeScript/blob/fc418a2e611c88cf9afa0115ff73490b2397d311/src/compiler/utilities.ts#L8761 +export function getUseDefineForClassFields(compilerOptions: _ts.CompilerOptions): boolean { + return compilerOptions.useDefineForClassFields === undefined + ? getEmitScriptTarget(compilerOptions) >= ts_ScriptTarget_ES2022 + : compilerOptions.useDefineForClassFields; +} + +// https://github.com/microsoft/TypeScript/blob/fc418a2e611c88cf9afa0115ff73490b2397d311/src/compiler/utilities.ts#L8556 +export function getEmitScriptTarget(compilerOptions: { + module?: _ts.CompilerOptions['module']; + target?: _ts.CompilerOptions['target']; +}): _ts.ScriptTarget { + return ( + compilerOptions.target ?? + ((compilerOptions.module === ts_ModuleKind_Node16 && ts_ScriptTarget_ES2022) || + (compilerOptions.module === ts_ModuleKind_NodeNext && ts_ScriptTarget_ESNext) || + ts_ScriptTarget_ES5) + ); +} diff --git a/tests/1996/empty.ts b/tests/1996/empty.ts new file mode 100644 index 000000000..cb0ff5c3b --- /dev/null +++ b/tests/1996/empty.ts @@ -0,0 +1 @@ +export {}; diff --git a/tests/1996/index.ts b/tests/1996/index.ts new file mode 100644 index 000000000..b643f71c7 --- /dev/null +++ b/tests/1996/index.ts @@ -0,0 +1,3 @@ +import * as empty from './empty'; +empty; +console.log('#1996 regression test.'); diff --git a/tests/1996/transpiler.js b/tests/1996/transpiler.js new file mode 100644 index 000000000..ba0e34080 --- /dev/null +++ b/tests/1996/transpiler.js @@ -0,0 +1,13 @@ +// A custom transpiler that returns `undefined` instead of a sourcemap, which is +// allowed according to our typedefs. + +exports.create = function () { + return { + transpile(input, options) { + return { + outputText: 'console.log("#1996 regression test with custom transpiler.")', + sourceMapText: undefined, + }; + }, + }; +}; diff --git a/tests/1996/tsconfig.custom-transpiler.json b/tests/1996/tsconfig.custom-transpiler.json new file mode 100644 index 000000000..d10e9a7b0 --- /dev/null +++ b/tests/1996/tsconfig.custom-transpiler.json @@ -0,0 +1,6 @@ +{ + "ts-node": { + "transpileOnly": true, + "transpiler": "./transpiler.js" + } +} diff --git a/tests/1996/tsconfig.json b/tests/1996/tsconfig.json new file mode 100644 index 000000000..3f9c57e6f --- /dev/null +++ b/tests/1996/tsconfig.json @@ -0,0 +1,5 @@ +{ + "ts-node": { + "swc": true + } +} diff --git a/yarn.lock b/yarn.lock index ccd9029ae..55633a4b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -511,90 +511,93 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-darwin-arm64@npm:1.3.32" +"@swc/core-darwin-arm64@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-darwin-arm64@npm:1.3.85" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-darwin-x64@npm:1.3.32" +"@swc/core-darwin-x64@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-darwin-x64@npm:1.3.85" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.32" +"@swc/core-linux-arm-gnueabihf@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.85" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.32" +"@swc/core-linux-arm64-gnu@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.85" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.32" +"@swc/core-linux-arm64-musl@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.85" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.32" +"@swc/core-linux-x64-gnu@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.85" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-linux-x64-musl@npm:1.3.32" +"@swc/core-linux-x64-musl@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-linux-x64-musl@npm:1.3.85" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.32" +"@swc/core-win32-arm64-msvc@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.85" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.32" +"@swc/core-win32-ia32-msvc@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.85" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.32" +"@swc/core-win32-x64-msvc@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.85" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/core@npm:1.3.32" +"@swc/core@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/core@npm:1.3.85" dependencies: - "@swc/core-darwin-arm64": 1.3.32 - "@swc/core-darwin-x64": 1.3.32 - "@swc/core-linux-arm-gnueabihf": 1.3.32 - "@swc/core-linux-arm64-gnu": 1.3.32 - "@swc/core-linux-arm64-musl": 1.3.32 - "@swc/core-linux-x64-gnu": 1.3.32 - "@swc/core-linux-x64-musl": 1.3.32 - "@swc/core-win32-arm64-msvc": 1.3.32 - "@swc/core-win32-ia32-msvc": 1.3.32 - "@swc/core-win32-x64-msvc": 1.3.32 + "@swc/core-darwin-arm64": 1.3.85 + "@swc/core-darwin-x64": 1.3.85 + "@swc/core-linux-arm-gnueabihf": 1.3.85 + "@swc/core-linux-arm64-gnu": 1.3.85 + "@swc/core-linux-arm64-musl": 1.3.85 + "@swc/core-linux-x64-gnu": 1.3.85 + "@swc/core-linux-x64-musl": 1.3.85 + "@swc/core-win32-arm64-msvc": 1.3.85 + "@swc/core-win32-ia32-msvc": 1.3.85 + "@swc/core-win32-x64-msvc": 1.3.85 + "@swc/types": ^0.1.4 + peerDependencies: + "@swc/helpers": ^0.5.0 dependenciesMeta: "@swc/core-darwin-arm64": optional: true @@ -616,14 +619,24 @@ __metadata: optional: true "@swc/core-win32-x64-msvc": optional: true - checksum: 61d3eeee89b2dec8d128f63051cb8808b74aea42afc444a0e6477a763b145b5a7c511eab14f5ab0a6e2fa486a99316b0428cb5e02cb11dd65003b760caf85c6e + peerDependenciesMeta: + "@swc/helpers": + optional: true + checksum: af9ec7d88fd9ad3dd876c8fea812b20ba734c2ed917c9f8281fd57c68ab57d5931ccb841f4467332b84c0cd522682737a770dfc8c3f9e0cc88cdce713971978c + languageName: node + linkType: hard + +"@swc/types@npm:^0.1.4": + version: 0.1.4 + resolution: "@swc/types@npm:0.1.4" + checksum: 9b09de7dca8e4b19bfb43f9e332c771855158cb761d26000807fe858447ecbc5342a6c257b26d9aa5497f7138fc58913693e2bee222e5042e0e8f57c2979ae66 languageName: node linkType: hard -"@swc/wasm@npm:1.3.32": - version: 1.3.32 - resolution: "@swc/wasm@npm:1.3.32" - checksum: 80df9b6983f4211e049592f210762dfbbd19899a24d37bf3dad7d8c2ede6fee70c7945cca3c508dae24e6b7f4a14b0ca6d645648fab01694ac4e93fda1ced379 +"@swc/wasm@npm:1.3.85": + version: 1.3.85 + resolution: "@swc/wasm@npm:1.3.85" + checksum: e4937fa9278eb11e7fd336937b3b5043a34a9236560e45df4d587566bafb6baedce68afbf34a984398bf33cffaf05b48a7662283fec2cfc7a93057845b68acb0 languageName: node linkType: hard @@ -3815,8 +3828,8 @@ __metadata: "@cspotcode/expect-stream": "https://github.com/cspotcode/node-expect-stream#4e425ff1eef240003af8716291e80fbaf3e3ae8f" "@cspotcode/source-map-support": ^0.8.0 "@microsoft/api-extractor": ^7.19.4 - "@swc/core": 1.3.32 - "@swc/wasm": 1.3.32 + "@swc/core": 1.3.85 + "@swc/wasm": 1.3.85 "@tsconfig/node14": "*" "@tsconfig/node16": "*" "@tsconfig/node18": "*" @@ -3853,8 +3866,8 @@ __metadata: v8-compile-cache-lib: ^3.0.1 workaround-broken-npm-prepack-behavior: "https://github.com/cspotcode/workaround-broken-npm-prepack-behavior#1a7adbbb8a527784daf97edad6ba42d6e96611f6" peerDependencies: - "@swc/core": ">=1.2.50" - "@swc/wasm": ">=1.2.50" + "@swc/core": ">=1.3.85" + "@swc/wasm": ">=1.3.85" "@types/node": "*" typescript: ">=4.4" peerDependenciesMeta: