From 26e3a51ca3ee10723484fdd4c1c9570e84a05bb0 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Mon, 18 Oct 2021 16:23:08 +0400 Subject: [PATCH 1/4] add Target ES2021 mapping in transpilers/swc I was getting error: `jsc.target should be es5 or upper to use getter / setter` and after investigation I've isolated issue and here is the "fix" --- src/transpilers/swc.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transpilers/swc.ts b/src/transpilers/swc.ts index 9b7361a08..3c2398252 100644 --- a/src/transpilers/swc.ts +++ b/src/transpilers/swc.ts @@ -128,6 +128,7 @@ targetMapping.set(/* ts.ScriptTarget.ES2017 */ 4, 'es2017'); targetMapping.set(/* ts.ScriptTarget.ES2018 */ 5, 'es2018'); targetMapping.set(/* ts.ScriptTarget.ES2019 */ 6, 'es2019'); targetMapping.set(/* ts.ScriptTarget.ES2020 */ 7, 'es2019'); +targetMapping.set(/* ts.ScriptTarget.ES2021 */ 8, 'es2019'); targetMapping.set(/* ts.ScriptTarget.ESNext */ 99, 'es2019'); const ModuleKind = { From 906cdd547143f4279b7af26114751f7a7f87d3da Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 21 Oct 2021 23:46:34 -0400 Subject: [PATCH 2/4] fix and add tests --- src/test/transpilers.spec.ts | 46 ++++++++++++++++++++++++++++++++++++ src/transpilers/swc.ts | 28 ++++++++++++++++++---- 2 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/test/transpilers.spec.ts diff --git a/src/test/transpilers.spec.ts b/src/test/transpilers.spec.ts new file mode 100644 index 000000000..eacd35a48 --- /dev/null +++ b/src/test/transpilers.spec.ts @@ -0,0 +1,46 @@ +// third-party transpiler and swc transpiler tests +// TODO: at the time of writing, other transpiler tests have not been moved into this file. +// Should consolidate them here. + +import { context } from './testlib'; +import { + contextTsNodeUnderTest, + testsDirRequire, +} from './helpers'; +import * as expect from 'expect'; + +const test = context(contextTsNodeUnderTest); + +test.suite('swc', (test) => { + test('verify that TS->SWC target mappings suppport all possible values from both TS and SWC', async (t) => { + const swcTranspiler = testsDirRequire('ts-node/transpilers/swc-experimental') as typeof import('../transpilers/swc'); + + // Detect when mapping is missing any ts.ScriptTargets + const ts = testsDirRequire('typescript') as typeof import('typescript'); + for(const key of Object.keys(ts.ScriptTarget)) { + if(/^\d+$/.test(key)) continue; + if(key === 'JSON') continue; + expect(swcTranspiler.targetMapping.has(ts.ScriptTarget[key as any] as any)).toBe(true); + } + + // Detect when mapping is missing any swc targets + // Assuming that tests/package.json declares @swc/core: latest + const swc = testsDirRequire('@swc/core'); + let msg: string | undefined = undefined; + try { + swc.transformSync('', {jsc: {target: 'invalid'}}); + } catch(e) { + msg = (e as Error).message; + } + expect(msg).toBeDefined(); + // Error looks like: + // unknown variant `invalid`, expected one of `es3`, `es5`, `es2015`, `es2016`, `es2017`, `es2018`, `es2019`, `es2020`, `es2021` at line 1 column 28 + const match = msg!.match(/unknown variant.*, expected one of (.*) at line/); + expect(match).toBeDefined(); + const targets = match![1].split(', ').map((v: string) => v.slice(1, -1)); + + for(const target of targets) { + expect([...swcTranspiler.targetMapping.values()]).toContain(target); + } + }); +}); diff --git a/src/transpilers/swc.ts b/src/transpilers/swc.ts index 3c2398252..19e3ab351 100644 --- a/src/transpilers/swc.ts +++ b/src/transpilers/swc.ts @@ -56,7 +56,17 @@ export function create(createOptions: SwcTranspilerOptions): Transpiler { const nonTsxOptions = createSwcOptions(false); const tsxOptions = createSwcOptions(true); function createSwcOptions(isTsx: boolean): swcTypes.Options { - const swcTarget = targetMapping.get(target!) ?? 'es3'; + let swcTarget = targetMapping.get(target!) ?? 'es3'; + // Downgrade to lower target if swc does not support the selected target. + // Perhaps project has an older version of swc. + let swcTargetIndex = swcTargets.indexOf(swcTarget); + for(; swcTargetIndex >= 0; swcTargetIndex--) { + try { + swcInstance.transformSync('', {jsc: {target: swcTargets[swcTargetIndex]}}); + break; + } catch(e) {} + } + swcTarget = swcTargets[swcTargetIndex]; const keepClassNames = target! >= /* ts.ScriptTarget.ES2016 */ 3; const moduleType = module === ModuleKind.CommonJS @@ -119,7 +129,8 @@ export function create(createOptions: SwcTranspilerOptions): Transpiler { }; } -const targetMapping = new Map(); +/** @internal */ +export const targetMapping = new Map(); targetMapping.set(/* ts.ScriptTarget.ES3 */ 0, 'es3'); targetMapping.set(/* ts.ScriptTarget.ES5 */ 1, 'es5'); targetMapping.set(/* ts.ScriptTarget.ES2015 */ 2, 'es2015'); @@ -127,9 +138,16 @@ targetMapping.set(/* ts.ScriptTarget.ES2016 */ 3, 'es2016'); targetMapping.set(/* ts.ScriptTarget.ES2017 */ 4, 'es2017'); targetMapping.set(/* ts.ScriptTarget.ES2018 */ 5, 'es2018'); targetMapping.set(/* ts.ScriptTarget.ES2019 */ 6, 'es2019'); -targetMapping.set(/* ts.ScriptTarget.ES2020 */ 7, 'es2019'); -targetMapping.set(/* ts.ScriptTarget.ES2021 */ 8, 'es2019'); -targetMapping.set(/* ts.ScriptTarget.ESNext */ 99, 'es2019'); +targetMapping.set(/* ts.ScriptTarget.ES2020 */ 7, 'es2020'); +targetMapping.set(/* ts.ScriptTarget.ES2021 */ 8, 'es2021'); +targetMapping.set(/* ts.ScriptTarget.ESNext */ 99, 'es2021'); + +type SwcTarget = typeof swcTargets[number]; +/** + * @internal + * We use this list to downgrade to a prior target when we probe swc to detect if it supports a particular target + */ +const swcTargets = ['es3', 'es5', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021'] as const; const ModuleKind = { None: 0, From 7c5c1a0c31d6a7b2cbd686a6ad584de9bb1aa230 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 21 Oct 2021 23:52:56 -0400 Subject: [PATCH 3/4] fix --- src/transpilers/swc.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transpilers/swc.ts b/src/transpilers/swc.ts index 19e3ab351..03533d8f9 100644 --- a/src/transpilers/swc.ts +++ b/src/transpilers/swc.ts @@ -59,6 +59,7 @@ export function create(createOptions: SwcTranspilerOptions): Transpiler { let swcTarget = targetMapping.get(target!) ?? 'es3'; // Downgrade to lower target if swc does not support the selected target. // Perhaps project has an older version of swc. + // TODO cache the results of this; slightly faster let swcTargetIndex = swcTargets.indexOf(swcTarget); for(; swcTargetIndex >= 0; swcTargetIndex--) { try { From 96ef4ceeab7c824b41966d37e0665cc58793b23c Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Thu, 21 Oct 2021 23:54:44 -0400 Subject: [PATCH 4/4] lintfix --- src/test/transpilers.spec.ts | 25 +++++++++++++------------ src/transpilers/swc.ts | 20 ++++++++++++++++---- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/test/transpilers.spec.ts b/src/test/transpilers.spec.ts index eacd35a48..f1d30172a 100644 --- a/src/test/transpilers.spec.ts +++ b/src/test/transpilers.spec.ts @@ -3,24 +3,25 @@ // Should consolidate them here. import { context } from './testlib'; -import { - contextTsNodeUnderTest, - testsDirRequire, -} from './helpers'; +import { contextTsNodeUnderTest, testsDirRequire } from './helpers'; import * as expect from 'expect'; const test = context(contextTsNodeUnderTest); test.suite('swc', (test) => { test('verify that TS->SWC target mappings suppport all possible values from both TS and SWC', async (t) => { - const swcTranspiler = testsDirRequire('ts-node/transpilers/swc-experimental') as typeof import('../transpilers/swc'); + const swcTranspiler = testsDirRequire( + 'ts-node/transpilers/swc-experimental' + ) as typeof import('../transpilers/swc'); // Detect when mapping is missing any ts.ScriptTargets const ts = testsDirRequire('typescript') as typeof import('typescript'); - for(const key of Object.keys(ts.ScriptTarget)) { - if(/^\d+$/.test(key)) continue; - if(key === 'JSON') continue; - expect(swcTranspiler.targetMapping.has(ts.ScriptTarget[key as any] as any)).toBe(true); + for (const key of Object.keys(ts.ScriptTarget)) { + if (/^\d+$/.test(key)) continue; + if (key === 'JSON') continue; + expect( + swcTranspiler.targetMapping.has(ts.ScriptTarget[key as any] as any) + ).toBe(true); } // Detect when mapping is missing any swc targets @@ -28,8 +29,8 @@ test.suite('swc', (test) => { const swc = testsDirRequire('@swc/core'); let msg: string | undefined = undefined; try { - swc.transformSync('', {jsc: {target: 'invalid'}}); - } catch(e) { + swc.transformSync('', { jsc: { target: 'invalid' } }); + } catch (e) { msg = (e as Error).message; } expect(msg).toBeDefined(); @@ -39,7 +40,7 @@ test.suite('swc', (test) => { expect(match).toBeDefined(); const targets = match![1].split(', ').map((v: string) => v.slice(1, -1)); - for(const target of targets) { + for (const target of targets) { expect([...swcTranspiler.targetMapping.values()]).toContain(target); } }); diff --git a/src/transpilers/swc.ts b/src/transpilers/swc.ts index 03533d8f9..6955bc90e 100644 --- a/src/transpilers/swc.ts +++ b/src/transpilers/swc.ts @@ -61,11 +61,13 @@ export function create(createOptions: SwcTranspilerOptions): Transpiler { // Perhaps project has an older version of swc. // TODO cache the results of this; slightly faster let swcTargetIndex = swcTargets.indexOf(swcTarget); - for(; swcTargetIndex >= 0; swcTargetIndex--) { + for (; swcTargetIndex >= 0; swcTargetIndex--) { try { - swcInstance.transformSync('', {jsc: {target: swcTargets[swcTargetIndex]}}); + swcInstance.transformSync('', { + jsc: { target: swcTargets[swcTargetIndex] }, + }); break; - } catch(e) {} + } catch (e) {} } swcTarget = swcTargets[swcTargetIndex]; const keepClassNames = target! >= /* ts.ScriptTarget.ES2016 */ 3; @@ -148,7 +150,17 @@ type SwcTarget = typeof swcTargets[number]; * @internal * We use this list to downgrade to a prior target when we probe swc to detect if it supports a particular target */ -const swcTargets = ['es3', 'es5', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021'] as const; +const swcTargets = [ + 'es3', + 'es5', + 'es2015', + 'es2016', + 'es2017', + 'es2018', + 'es2019', + 'es2020', + 'es2021', +] as const; const ModuleKind = { None: 0,