From 56b70da9d0f776fd70e58bd560006aeb54e66032 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Fri, 22 Oct 2021 06:23:23 +0200 Subject: [PATCH] add Target ES2021 mapping in transpilers/swc (#1521) * 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" * fix and add tests * fix * lintfix Co-authored-by: Andrew Bradley --- src/test/transpilers.spec.ts | 47 ++++++++++++++++++++++++++++++++++++ src/transpilers/swc.ts | 40 +++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 4 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..f1d30172a --- /dev/null +++ b/src/test/transpilers.spec.ts @@ -0,0 +1,47 @@ +// 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 9b7361a08..6955bc90e 100644 --- a/src/transpilers/swc.ts +++ b/src/transpilers/swc.ts @@ -56,7 +56,20 @@ 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. + // TODO cache the results of this; slightly faster + 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 +132,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,8 +141,26 @@ 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.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,