Skip to content

Commit 67ecb10

Browse files
module: fix discrepancy between .ts and .js
PR-URL: #54461 Backport-PR-URL: #54566 Fixes: #54457 Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com>
1 parent 8a3482e commit 67ecb10

File tree

6 files changed

+83
-56
lines changed

6 files changed

+83
-56
lines changed

lib/internal/modules/esm/get_format.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,14 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
161161
default: { // The user did not pass `--experimental-default-type`.
162162
// `source` is undefined when this is called from `defaultResolve`;
163163
// but this gets called again from `defaultLoad`/`defaultLoadSync`.
164-
let parsedSource;
165-
if (source) {
166-
const { stripTypeScriptTypes } = require('internal/modules/helpers');
167-
parsedSource = stripTypeScriptTypes(source, url);
168-
}
164+
// Since experimental-strip-types depends on detect-module, we always return null
165+
// if source is undefined.
166+
if (!source) { return null; }
167+
const { stripTypeScriptTypes, stringify } = require('internal/modules/helpers');
168+
const stringifiedSource = stringify(source);
169+
const parsedSource = stripTypeScriptTypes(stringifiedSource, fileURLToPath(url));
169170
const detectedFormat = detectModuleFormat(parsedSource, url);
170-
// When source is undefined, default to module-typescript.
171-
const format = detectedFormat ? `${detectedFormat}-typescript` : 'module-typescript';
171+
const format = `${detectedFormat}-typescript`;
172172
if (format === 'module-typescript' && foundPackageJson) {
173173
// This module has a .js extension, a package.json with no `type` field, and ESM syntax.
174174
// Warn about the missing `type` field so that the user can avoid the performance penalty of detection.

lib/internal/modules/esm/translators.js

+2-49
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,6 @@ const {
1818
globalThis: { WebAssembly },
1919
} = primordials;
2020

21-
/** @type {import('internal/util/types')} */
22-
let _TYPES = null;
23-
/**
24-
* Lazily loads and returns the internal/util/types module.
25-
*/
26-
function lazyTypes() {
27-
if (_TYPES !== null) { return _TYPES; }
28-
return _TYPES = require('internal/util/types');
29-
}
30-
3121
const {
3222
compileFunctionForCJSLoader,
3323
} = internalBinding('contextify');
@@ -37,7 +27,9 @@ const assert = require('internal/assert');
3727
const { readFileSync } = require('fs');
3828
const { dirname, extname, isAbsolute } = require('path');
3929
const {
30+
assertBufferSource,
4031
loadBuiltinModule,
32+
stringify,
4133
stripTypeScriptTypes,
4234
stripBOM,
4335
urlToFilename,
@@ -57,7 +49,6 @@ let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
5749
const { emitExperimentalWarning, kEmptyObject, setOwnProperty, isWindows } = require('internal/util');
5850
const {
5951
ERR_UNKNOWN_BUILTIN_MODULE,
60-
ERR_INVALID_RETURN_PROPERTY_VALUE,
6152
} = require('internal/errors').codes;
6253
const { maybeCacheSourceMap } = require('internal/source_map/source_map_cache');
6354
const moduleWrap = internalBinding('module_wrap');
@@ -107,44 +98,6 @@ function initCJSParseSync() {
10798
const translators = new SafeMap();
10899
exports.translators = translators;
109100

110-
let DECODER = null;
111-
/**
112-
* Asserts that the given body is a buffer source (either a string, array buffer, or typed array).
113-
* Throws an error if the body is not a buffer source.
114-
* @param {string | ArrayBufferView | ArrayBuffer} body - The body to check.
115-
* @param {boolean} allowString - Whether or not to allow a string as a valid buffer source.
116-
* @param {string} hookName - The name of the hook being called.
117-
* @throws {ERR_INVALID_RETURN_PROPERTY_VALUE} If the body is not a buffer source.
118-
*/
119-
function assertBufferSource(body, allowString, hookName) {
120-
if (allowString && typeof body === 'string') {
121-
return;
122-
}
123-
const { isArrayBufferView, isAnyArrayBuffer } = lazyTypes();
124-
if (isArrayBufferView(body) || isAnyArrayBuffer(body)) {
125-
return;
126-
}
127-
throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
128-
`${allowString ? 'string, ' : ''}array buffer, or typed array`,
129-
hookName,
130-
'source',
131-
body,
132-
);
133-
}
134-
135-
/**
136-
* Converts a buffer or buffer-like object to a string.
137-
* @param {string | ArrayBuffer | ArrayBufferView} body - The buffer or buffer-like object to convert to a string.
138-
* @returns {string} The resulting string.
139-
*/
140-
function stringify(body) {
141-
if (typeof body === 'string') { return body; }
142-
assertBufferSource(body, false, 'load');
143-
const { TextDecoder } = require('internal/encoding');
144-
DECODER = DECODER === null ? new TextDecoder() : DECODER;
145-
return DECODER.decode(body);
146-
}
147-
148101
/**
149102
* Converts a URL to a file path if the URL protocol is 'file:'.
150103
* @param {string} url - The URL to convert.

lib/internal/modules/helpers.js

+52
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const {
1616
} = primordials;
1717
const {
1818
ERR_INVALID_ARG_TYPE,
19+
ERR_INVALID_RETURN_PROPERTY_VALUE,
1920
} = require('internal/errors').codes;
2021
const { BuiltinModule } = require('internal/bootstrap/realm');
2122

@@ -429,10 +430,60 @@ function getCompileCacheDir() {
429430
return _getCompileCacheDir() || undefined;
430431
}
431432

433+
/** @type {import('internal/util/types')} */
434+
let _TYPES = null;
435+
/**
436+
* Lazily loads and returns the internal/util/types module.
437+
*/
438+
function lazyTypes() {
439+
if (_TYPES !== null) { return _TYPES; }
440+
return _TYPES = require('internal/util/types');
441+
}
442+
443+
/**
444+
* Asserts that the given body is a buffer source (either a string, array buffer, or typed array).
445+
* Throws an error if the body is not a buffer source.
446+
* @param {string | ArrayBufferView | ArrayBuffer} body - The body to check.
447+
* @param {boolean} allowString - Whether or not to allow a string as a valid buffer source.
448+
* @param {string} hookName - The name of the hook being called.
449+
* @throws {ERR_INVALID_RETURN_PROPERTY_VALUE} If the body is not a buffer source.
450+
*/
451+
function assertBufferSource(body, allowString, hookName) {
452+
if (allowString && typeof body === 'string') {
453+
return;
454+
}
455+
const { isArrayBufferView, isAnyArrayBuffer } = lazyTypes();
456+
if (isArrayBufferView(body) || isAnyArrayBuffer(body)) {
457+
return;
458+
}
459+
throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
460+
`${allowString ? 'string, ' : ''}array buffer, or typed array`,
461+
hookName,
462+
'source',
463+
body,
464+
);
465+
}
466+
467+
let DECODER = null;
468+
469+
/**
470+
* Converts a buffer or buffer-like object to a string.
471+
* @param {string | ArrayBuffer | ArrayBufferView} body - The buffer or buffer-like object to convert to a string.
472+
* @returns {string} The resulting string.
473+
*/
474+
function stringify(body) {
475+
if (typeof body === 'string') { return body; }
476+
assertBufferSource(body, false, 'load');
477+
const { TextDecoder } = require('internal/encoding');
478+
DECODER = DECODER === null ? new TextDecoder() : DECODER;
479+
return DECODER.decode(body);
480+
}
481+
432482
module.exports = {
433483
addBuiltinLibsToObject,
434484
constants,
435485
enableCompileCache,
486+
assertBufferSource,
436487
getBuiltinModule,
437488
getCjsConditions,
438489
getCompileCacheDir,
@@ -442,6 +493,7 @@ module.exports = {
442493
makeRequireFunction,
443494
normalizeReferrerURL,
444495
stripTypeScriptTypes,
496+
stringify,
445497
stripBOM,
446498
toRealPath,
447499
hasStartedUserCJSExecution() {

test/es-module/test-typescript.mjs

+11
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,14 @@ test('execute a TypeScript file with CommonJS syntax requiring .mts with require
325325
match(result.stdout, /Hello, TypeScript!/);
326326
strictEqual(result.code, 0);
327327
});
328+
329+
test('execute a JavaScript file importing a cjs TypeScript file', async () => {
330+
const result = await spawnPromisified(process.execPath, [
331+
'--experimental-strip-types',
332+
'--no-warnings',
333+
fixtures.path('typescript/ts/issue-54457.mjs'),
334+
]);
335+
strictEqual(result.stderr, '');
336+
match(result.stdout, /Hello, TypeScript!/);
337+
strictEqual(result.code, 0);
338+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// https://github.com/nodejs/node/issues/54457
2+
const { text } = await import('./test-require.ts');
3+
4+
console.log(text);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const util = require('node:util');
2+
3+
const text = 'Hello, TypeScript!';
4+
5+
module.exports = {
6+
text
7+
};

0 commit comments

Comments
 (0)