Skip to content

Commit 2385967

Browse files
authored
Add --swc shorthand for the built-in swc transpiler; graduate the swc transpiler out of experimental status (#1536)
* Add --swc shorthand for the built-in swc transpiler; graduate the swc transpiler out of experimental status * lint-fix * fix * fix tests * Improve swc / transpiler tests to prove that third-party transpiler was invoked * lintfix
1 parent 3e85ca1 commit 2385967

File tree

15 files changed

+138
-34
lines changed

15 files changed

+138
-34
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"./esm.mjs": "./esm.mjs",
2424
"./esm/transpile-only": "./esm/transpile-only.mjs",
2525
"./esm/transpile-only.mjs": "./esm/transpile-only.mjs",
26+
"./transpilers/swc": "./transpilers/swc.js",
2627
"./transpilers/swc-experimental": "./transpilers/swc-experimental.js",
2728
"./node10/tsconfig.json": "./node10/tsconfig.json",
2829
"./node12/tsconfig.json": "./node12/tsconfig.json",

src/bin.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function main(
5555
'--ignore': [String],
5656
'--transpile-only': Boolean,
5757
'--transpiler': String,
58+
'--swc': Boolean,
5859
'--type-check': Boolean,
5960
'--compiler-host': Boolean,
6061
'--pretty': Boolean,
@@ -116,6 +117,7 @@ export function main(
116117
'--transpile-only': transpileOnly,
117118
'--type-check': typeCheck,
118119
'--transpiler': transpiler,
120+
'--swc': swc,
119121
'--compiler-host': compilerHost,
120122
'--pretty': pretty,
121123
'--skip-project': skipProject,
@@ -145,6 +147,7 @@ export function main(
145147
--show-config Print resolved configuration and exit
146148
147149
-T, --transpile-only Use TypeScript's faster \`transpileModule\` or a third-party transpiler
150+
--swc Use the swc transpiler
148151
-H, --compiler-host Use TypeScript's compiler host API
149152
-I, --ignore [pattern] Override the path patterns to skip compilation
150153
-P, --project [path] Path to TypeScript JSON project file
@@ -256,6 +259,7 @@ export function main(
256259
experimentalReplAwait: noExperimentalReplAwait ? false : undefined,
257260
typeCheck,
258261
transpiler,
262+
swc,
259263
compilerHost,
260264
ignore,
261265
preferTsExts,

src/configuration.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ function filterRecognizedTsConfigTsNodeOptions(
276276
scopeDir,
277277
moduleTypes,
278278
experimentalReplAwait,
279+
swc,
279280
...unrecognized
280281
} = jsonObject as TsConfigOptions;
281282
const filteredTsConfigOptions = {
@@ -298,6 +299,7 @@ function filterRecognizedTsConfigTsNodeOptions(
298299
scope,
299300
scopeDir,
300301
moduleTypes,
302+
swc,
301303
};
302304
// Use the typechecker to make sure this implementation has the correct set of properties
303305
const catchExtraneousProps: keyof TsConfigOptions = (null as any) as keyof typeof filteredTsConfigOptions;

src/index.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ export interface CreateOptions {
256256
* Specify a custom transpiler for use with transpileOnly
257257
*/
258258
transpiler?: string | [string, object];
259+
/**
260+
* Transpile with swc instead of the TypeScript compiler, and skip typechecking.
261+
*
262+
* Equivalent to setting both `transpileOnly: true` and `transpiler: 'ts-node/transpilers/swc'`
263+
*/
264+
swc?: boolean;
259265
/**
260266
* Paths which should not be compiled.
261267
*
@@ -608,11 +614,33 @@ export function create(rawOptions: CreateOptions = {}): Service {
608614
({ compiler, ts } = loadCompiler(options.compiler, configFilePath));
609615
}
610616

617+
// swc implies two other options
618+
// typeCheck option was implemented specifically to allow overriding tsconfig transpileOnly from the command-line
619+
// So we should allow using typeCheck to override swc
620+
if (options.swc && !options.typeCheck) {
621+
if (options.transpileOnly === false) {
622+
throw new Error(
623+
"Cannot enable 'swc' option with 'transpileOnly: false'. 'swc' implies 'transpileOnly'."
624+
);
625+
}
626+
if (options.transpiler) {
627+
throw new Error(
628+
"Cannot specify both 'swc' and 'transpiler' options. 'swc' uses the built-in swc transpiler."
629+
);
630+
}
631+
}
632+
611633
const readFile = options.readFile || ts.sys.readFile;
612634
const fileExists = options.fileExists || ts.sys.fileExists;
613635
// typeCheck can override transpileOnly, useful for CLI flag to override config file
614636
const transpileOnly =
615-
options.transpileOnly === true && options.typeCheck !== true;
637+
(options.transpileOnly === true || options.swc === true) &&
638+
options.typeCheck !== true;
639+
const transpiler = options.transpiler
640+
? options.transpiler
641+
: options.swc
642+
? require.resolve('./transpilers/swc.js')
643+
: undefined;
616644
const transformers = options.transformers || undefined;
617645
const diagnosticFilters: Array<DiagnosticFilter> = [
618646
{
@@ -668,17 +696,15 @@ export function create(rawOptions: CreateOptions = {}): Service {
668696
);
669697
}
670698
let customTranspiler: Transpiler | undefined = undefined;
671-
if (options.transpiler) {
699+
if (transpiler) {
672700
if (!transpileOnly)
673701
throw new Error(
674702
'Custom transpiler can only be used when transpileOnly is enabled.'
675703
);
676704
const transpilerName =
677-
typeof options.transpiler === 'string'
678-
? options.transpiler
679-
: options.transpiler[0];
705+
typeof transpiler === 'string' ? transpiler : transpiler[0];
680706
const transpilerOptions =
681-
typeof options.transpiler === 'string' ? {} : options.transpiler[1] ?? {};
707+
typeof transpiler === 'string' ? {} : transpiler[1] ?? {};
682708
// TODO mimic fixed resolution logic from loadCompiler main
683709
// TODO refactor into a more generic "resolve dep relative to project" helper
684710
const transpilerPath = require.resolve(transpilerName, {

src/test/index.spec.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ test.suite('ts-node', (test) => {
7070
testsDirRequire.resolve('ts-node/esm/transpile-only');
7171
testsDirRequire.resolve('ts-node/esm/transpile-only.mjs');
7272

73+
testsDirRequire.resolve('ts-node/transpilers/swc');
7374
testsDirRequire.resolve('ts-node/transpilers/swc-experimental');
7475

7576
testsDirRequire.resolve('ts-node/node10/tsconfig.json');
@@ -304,21 +305,31 @@ test.suite('ts-node', (test) => {
304305
expect(err.message).toMatch('error TS1003: Identifier expected');
305306
});
306307

307-
test('should support third-party transpilers via --transpiler', async () => {
308-
const { err, stdout } = await exec(
309-
`${CMD_TS_NODE_WITHOUT_PROJECT_FLAG} --transpiler ts-node/transpilers/swc-experimental transpile-only-swc`
310-
);
311-
expect(err).toBe(null);
312-
expect(stdout).toMatch('Hello World!');
313-
});
314-
315-
test('should support third-party transpilers via tsconfig', async () => {
316-
const { err, stdout } = await exec(
317-
`${CMD_TS_NODE_WITHOUT_PROJECT_FLAG} transpile-only-swc-via-tsconfig`
318-
);
319-
expect(err).toBe(null);
320-
expect(stdout).toMatch('Hello World!');
321-
});
308+
for (const flavor of [
309+
'--transpiler ts-node/transpilers/swc transpile-only-swc',
310+
'--transpiler ts-node/transpilers/swc-experimental transpile-only-swc',
311+
'--swc transpile-only-swc',
312+
'transpile-only-swc-via-tsconfig',
313+
'transpile-only-swc-shorthand-via-tsconfig',
314+
]) {
315+
test(`should support swc and third-party transpilers: ${flavor}`, async () => {
316+
const { err, stdout } = await exec(
317+
`${CMD_TS_NODE_WITHOUT_PROJECT_FLAG} ${flavor}`,
318+
{
319+
env: {
320+
...process.env,
321+
NODE_OPTIONS: `${
322+
process.env.NODE_OPTIONS || ''
323+
} --require ${require.resolve('../../tests/spy-swc-transpiler')}`,
324+
},
325+
}
326+
);
327+
expect(err).toBe(null);
328+
expect(stdout).toMatch(
329+
'Hello World! swc transpiler invocation count: 1\n'
330+
);
331+
});
332+
}
322333

323334
if (semver.gte(process.version, '12.16.0')) {
324335
test('swc transpiler supports native ESM emit', async () => {

tests/spy-swc-transpiler.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Spy on the swc transpiler so that tests can prove it was used rather than
2+
// TypeScript's `transpileModule`.
3+
const swcTranspiler = require('ts-node/transpilers/swc');
4+
5+
global.swcTranspilerCalls = 0;
6+
7+
const wrappedCreate = swcTranspiler.create;
8+
swcTranspiler.create = function (...args) {
9+
const transpiler = wrappedCreate(...args);
10+
const wrappedTranspile = transpiler.transpile;
11+
transpiler.transpile = function (...args) {
12+
global.swcTranspilerCalls++;
13+
return wrappedTranspile.call(this, ...args);
14+
};
15+
return transpiler;
16+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Test for #1343
2+
const Decorator = function () {};
3+
@Decorator
4+
class World {}
5+
6+
// intentional type errors to check transpile-only ESM loader skips type checking
7+
parseInt(1101, 2);
8+
const x: number = `Hello ${World.name}! swc transpiler invocation count: ${global.swcTranspilerCalls}`;
9+
console.log(x);
10+
11+
// test module type emit
12+
import { readFileSync } from 'fs';
13+
readFileSync;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"ts-node": {
3+
"swc": true
4+
},
5+
"compilerOptions": {
6+
"target": "ES2018",
7+
"module": "CommonJS",
8+
"allowJs": true,
9+
"jsx": "react",
10+
"experimentalDecorators": true
11+
}
12+
}

tests/transpile-only-swc-via-tsconfig/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class World {}
55

66
// intentional type errors to check transpile-only ESM loader skips type checking
77
parseInt(1101, 2);
8-
const x: number = `Hello ${World.name}!`;
8+
const x: number = `Hello ${World.name}! swc transpiler invocation count: ${global.swcTranspilerCalls}`;
99
console.log(x);
1010

1111
// test module type emit

tests/transpile-only-swc-via-tsconfig/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"ts-node": {
33
"transpileOnly": true,
4-
"transpiler": "ts-node/transpilers/swc-experimental"
4+
"transpiler": "ts-node/transpilers/swc"
55
},
66
"compilerOptions": {
77
"target": "ES2018",

0 commit comments

Comments
 (0)