Skip to content

Commit c20e841

Browse files
module: fix strip-types interaction with detect-module
PR-URL: #54164 Backport-PR-URL: #54211 Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Jake Yuesong Li <jake.yuesong@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent b731528 commit c20e841

10 files changed

+167
-12
lines changed

lib/internal/modules/esm/get_format.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
163163
const { tsParse } = require('internal/modules/helpers');
164164
const parsedSource = tsParse(source);
165165
const detectedFormat = detectModuleFormat(parsedSource, url);
166-
const format = detectedFormat ? `${detectedFormat}-typescript` : 'commonjs-typescript';
166+
// When source is undefined, default to module-typescript.
167+
const format = detectedFormat ? `${detectedFormat}-typescript` : 'module-typescript';
167168
if (format === 'module-typescript') {
168169
// This module has a .js extension, a package.json with no `type` field, and ESM syntax.
169170
// Warn about the missing `type` field so that the user can avoid the performance penalty of detection.

lib/internal/modules/helpers.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,15 @@ function lazyLoadTSParser() {
307307
return parseTS;
308308
}
309309

310+
/**
311+
* Performs type-stripping to TypeScript source code.
312+
* @param {string} source TypeScript code to parse.
313+
* @returns {string} JavaScript code.
314+
*/
310315
function tsParse(source) {
311-
if (!source || typeof source !== 'string') { return; }
316+
if (!source || typeof source !== 'string') { return ''; }
312317
const transformSync = lazyLoadTSParser();
313-
const { code } = transformSync(source);
318+
const { code } = transformSync(source, { __proto__: null, mode: 'strip-only' });
314319
return code;
315320
}
316321

lib/internal/modules/run_main.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function shouldUseESMLoader(mainPath) {
8484
if (getOptionValue('--experimental-strip-types')) {
8585
// This ensures that --experimental-default-type=commonjs and .mts files are treated as commonjs
8686
if (getOptionValue('--experimental-default-type') === 'commonjs') { return false; }
87-
if (mainPath && StringPrototypeEndsWith(mainPath, '.cts')) { return false; }
87+
if (!mainPath || StringPrototypeEndsWith(mainPath, '.cts')) { return false; }
8888
// This will likely change in the future to start with commonjs loader by default
8989
if (mainPath && StringPrototypeEndsWith(mainPath, '.mts')) { return true; }
9090
}

src/node_options.cc

+1
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,7 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
800800
"Experimental type-stripping for TypeScript files.",
801801
&EnvironmentOptions::experimental_strip_types,
802802
kAllowedInEnvvar);
803+
Implies("--experimental-strip-types", "--experimental-detect-module");
803804
AddOption("--interactive",
804805
"always enter the REPL even if stdin does not appear "
805806
"to be a terminal",

test/es-module/test-typescript-commonjs.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ test('execute a .cts file importing a .mts file export', async () => {
118118
strictEqual(result.code, 0);
119119
});
120120

121-
test('expect failure of a .cts file with default type module', async () => {
121+
test('execute a .cts file with default type module', async () => {
122122
const result = await spawnPromisified(process.execPath, [
123123
'--experimental-strip-types',
124124
'--experimental-default-type=module', // Keeps working with commonjs

test/es-module/test-typescript-eval.mjs

+28-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { test } from 'node:test';
44

55
test('eval TypeScript ESM syntax', async () => {
66
const result = await spawnPromisified(process.execPath, [
7-
'--input-type=module',
87
'--experimental-strip-types',
98
'--eval',
109
`import util from 'node:util'
@@ -16,9 +15,22 @@ test('eval TypeScript ESM syntax', async () => {
1615
strictEqual(result.code, 0);
1716
});
1817

18+
test('eval TypeScript ESM syntax with input-type module', async () => {
19+
const result = await spawnPromisified(process.execPath, [
20+
'--experimental-strip-types',
21+
'--input-type=module',
22+
'--eval',
23+
`import util from 'node:util'
24+
const text: string = 'Hello, TypeScript!'
25+
console.log(util.styleText('red', text));`]);
26+
27+
match(result.stderr, /Type Stripping is an experimental feature and might change at any time/);
28+
match(result.stdout, /Hello, TypeScript!/);
29+
strictEqual(result.code, 0);
30+
});
31+
1932
test('eval TypeScript CommonJS syntax', async () => {
2033
const result = await spawnPromisified(process.execPath, [
21-
'--input-type=commonjs',
2234
'--experimental-strip-types',
2335
'--eval',
2436
`const util = require('node:util');
@@ -30,6 +42,20 @@ test('eval TypeScript CommonJS syntax', async () => {
3042
strictEqual(result.code, 0);
3143
});
3244

45+
test('eval TypeScript CommonJS syntax with input-type commonjs', async () => {
46+
const result = await spawnPromisified(process.execPath, [
47+
'--experimental-strip-types',
48+
'--input-type=commonjs',
49+
'--eval',
50+
`const util = require('node:util');
51+
const text: string = 'Hello, TypeScript!'
52+
console.log(util.styleText('red', text));`,
53+
'--no-warnings']);
54+
match(result.stdout, /Hello, TypeScript!/);
55+
strictEqual(result.stderr, '');
56+
strictEqual(result.code, 0);
57+
});
58+
3359
test('eval TypeScript CommonJS syntax by default', async () => {
3460
const result = await spawnPromisified(process.execPath, [
3561
'--experimental-strip-types',

test/es-module/test-typescript-module.mjs

+37-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ test('execute an .mts file importing an .mts file', async () => {
2828
test('execute an .mts file importing a .ts file', async () => {
2929
const result = await spawnPromisified(process.execPath, [
3030
'--experimental-strip-types',
31-
'--experimental-default-type=module', // this should fail
3231
'--no-warnings',
3332
fixtures.path('typescript/mts/test-import-ts-file.mts'),
3433
]);
@@ -38,10 +37,22 @@ test('execute an .mts file importing a .ts file', async () => {
3837
strictEqual(result.code, 0);
3938
});
4039

41-
test('execute an .mts file importing a .cts file', async () => {
40+
test('execute an .mts file importing a .ts file with default-type module', async () => {
4241
const result = await spawnPromisified(process.execPath, [
4342
'--experimental-strip-types',
43+
'--experimental-default-type=module',
4444
'--no-warnings',
45+
fixtures.path('typescript/mts/test-import-ts-file.mts'),
46+
]);
47+
48+
strictEqual(result.stderr, '');
49+
match(result.stdout, /Hello, TypeScript!/);
50+
strictEqual(result.code, 0);
51+
});
52+
53+
test('execute an .mts file importing a .cts file', async () => {
54+
const result = await spawnPromisified(process.execPath, [
55+
'--experimental-strip-types',
4556
'--no-warnings',
4657
fixtures.path('typescript/mts/test-import-commonjs.mts'),
4758
]);
@@ -95,3 +106,27 @@ test('execute a .ts file from node_modules', async () => {
95106
strictEqual(result.stdout, '');
96107
strictEqual(result.code, 1);
97108
});
109+
110+
test('execute an empty .ts file', async () => {
111+
const result = await spawnPromisified(process.execPath, [
112+
'--experimental-strip-types',
113+
'--no-warnings',
114+
fixtures.path('typescript/ts/test-empty-file.ts'),
115+
]);
116+
117+
strictEqual(result.stderr, '');
118+
strictEqual(result.stdout, '');
119+
strictEqual(result.code, 0);
120+
});
121+
122+
test('execute .ts file importing a module', async () => {
123+
const result = await spawnPromisified(process.execPath, [
124+
'--experimental-strip-types',
125+
'--no-warnings',
126+
fixtures.path('typescript/ts/test-import-fs.ts'),
127+
]);
128+
129+
strictEqual(result.stderr, '');
130+
strictEqual(result.stdout, 'Hello, TypeScript!\n');
131+
strictEqual(result.code, 0);
132+
});

test/es-module/test-typescript.mjs

+87-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ test('execute a TypeScript file', async () => {
1515
});
1616

1717
test('execute a TypeScript file with imports', async () => {
18+
const result = await spawnPromisified(process.execPath, [
19+
'--experimental-strip-types',
20+
'--no-warnings',
21+
fixtures.path('typescript/ts/test-import-foo.ts'),
22+
]);
23+
24+
strictEqual(result.stderr, '');
25+
match(result.stdout, /Hello, TypeScript!/);
26+
strictEqual(result.code, 0);
27+
});
28+
29+
test('execute a TypeScript file with imports with default-type module', async () => {
1830
const result = await spawnPromisified(process.execPath, [
1931
'--experimental-strip-types',
2032
'--experimental-default-type=module',
@@ -28,6 +40,18 @@ test('execute a TypeScript file with imports', async () => {
2840
});
2941

3042
test('execute a TypeScript file with node_modules', async () => {
43+
const result = await spawnPromisified(process.execPath, [
44+
'--experimental-strip-types',
45+
'--no-warnings',
46+
fixtures.path('typescript/ts/test-typescript-node-modules.ts'),
47+
]);
48+
49+
strictEqual(result.stderr, '');
50+
match(result.stdout, /Hello, TypeScript!/);
51+
strictEqual(result.code, 0);
52+
});
53+
54+
test('execute a TypeScript file with node_modules with default-type module', async () => {
3155
const result = await spawnPromisified(process.execPath, [
3256
'--experimental-strip-types',
3357
'--experimental-default-type=module',
@@ -43,7 +67,6 @@ test('execute a TypeScript file with node_modules', async () => {
4367
test('expect error when executing a TypeScript file with imports with no extensions', async () => {
4468
const result = await spawnPromisified(process.execPath, [
4569
'--experimental-strip-types',
46-
'--experimental-default-type=module',
4770
fixtures.path('typescript/ts/test-import-no-extension.ts'),
4871
]);
4972

@@ -52,6 +75,19 @@ test('expect error when executing a TypeScript file with imports with no extensi
5275
strictEqual(result.code, 1);
5376
});
5477

78+
test('expect error when executing a TypeScript file with imports with no extensions with default-type module',
79+
async () => {
80+
const result = await spawnPromisified(process.execPath, [
81+
'--experimental-strip-types',
82+
'--experimental-default-type=module',
83+
fixtures.path('typescript/ts/test-import-no-extension.ts'),
84+
]);
85+
86+
match(result.stderr, /Error \[ERR_MODULE_NOT_FOUND\]:/);
87+
strictEqual(result.stdout, '');
88+
strictEqual(result.code, 1);
89+
});
90+
5591
test('expect error when executing a TypeScript file with enum', async () => {
5692
const result = await spawnPromisified(process.execPath, [
5793
'--experimental-strip-types',
@@ -99,6 +135,17 @@ test('execute a TypeScript file with type definition', async () => {
99135
});
100136

101137
test('execute a TypeScript file with type definition but no type keyword', async () => {
138+
const result = await spawnPromisified(process.execPath, [
139+
'--experimental-strip-types',
140+
fixtures.path('typescript/ts/test-import-no-type-keyword.ts'),
141+
]);
142+
143+
match(result.stderr, /does not provide an export named 'MyType'/);
144+
strictEqual(result.stdout, '');
145+
strictEqual(result.code, 1);
146+
});
147+
148+
test('execute a TypeScript file with type definition but no type keyword with default-type modue', async () => {
102149
const result = await spawnPromisified(process.execPath, [
103150
'--experimental-strip-types',
104151
'--experimental-default-type=module',
@@ -122,6 +169,18 @@ test('execute a TypeScript file with CommonJS syntax', async () => {
122169
});
123170

124171
test('execute a TypeScript file with ES module syntax', async () => {
172+
const result = await spawnPromisified(process.execPath, [
173+
'--experimental-strip-types',
174+
'--no-warnings',
175+
fixtures.path('typescript/ts/test-module-typescript.ts'),
176+
]);
177+
178+
strictEqual(result.stderr, '');
179+
match(result.stdout, /Hello, TypeScript!/);
180+
strictEqual(result.code, 0);
181+
});
182+
183+
test('execute a TypeScript file with ES module syntax with default-type module', async () => {
125184
const result = await spawnPromisified(process.execPath, [
126185
'--experimental-strip-types',
127186
'--experimental-default-type=module',
@@ -159,7 +218,6 @@ test('expect stack trace of a TypeScript file to be correct', async () => {
159218

160219
test('execute CommonJS TypeScript file from node_modules with require-module', async () => {
161220
const result = await spawnPromisified(process.execPath, [
162-
'--experimental-default-type=module',
163221
'--experimental-strip-types',
164222
fixtures.path('typescript/ts/test-import-ts-node-modules.ts'),
165223
]);
@@ -169,6 +227,19 @@ test('execute CommonJS TypeScript file from node_modules with require-module', a
169227
strictEqual(result.code, 1);
170228
});
171229

230+
test('execute CommonJS TypeScript file from node_modules with require-module and default-type module',
231+
async () => {
232+
const result = await spawnPromisified(process.execPath, [
233+
'--experimental-strip-types',
234+
'--experimental-default-type=module',
235+
fixtures.path('typescript/ts/test-import-ts-node-modules.ts'),
236+
]);
237+
238+
match(result.stderr, /ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING/);
239+
strictEqual(result.stdout, '');
240+
strictEqual(result.code, 1);
241+
});
242+
172243
test('execute a TypeScript file with CommonJS syntax but default type module', async () => {
173244
const result = await spawnPromisified(process.execPath, [
174245
'--experimental-strip-types',
@@ -218,7 +289,6 @@ test('execute a TypeScript file with CommonJS syntax requiring .mts with require
218289
test('execute a TypeScript file with CommonJS syntax requiring .mts with require-module', async () => {
219290
const result = await spawnPromisified(process.execPath, [
220291
'--experimental-strip-types',
221-
'--experimental-default-type=commonjs',
222292
'--no-warnings',
223293
fixtures.path('typescript/ts/test-require-cts.ts'),
224294
]);
@@ -227,3 +297,17 @@ test('execute a TypeScript file with CommonJS syntax requiring .mts with require
227297
match(result.stdout, /Hello, TypeScript!/);
228298
strictEqual(result.code, 0);
229299
});
300+
301+
test('execute a TypeScript file with CommonJS syntax requiring .mts with require-module with default-type commonjs',
302+
async () => {
303+
const result = await spawnPromisified(process.execPath, [
304+
'--experimental-strip-types',
305+
'--experimental-default-type=commonjs',
306+
'--no-warnings',
307+
fixtures.path('typescript/ts/test-require-cts.ts'),
308+
]);
309+
310+
strictEqual(result.stderr, '');
311+
match(result.stdout, /Hello, TypeScript!/);
312+
strictEqual(result.code, 0);
313+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import fs from 'fs';
2+
console.log('Hello, TypeScript!');

0 commit comments

Comments
 (0)