Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 69dd1e1

Browse filesBrowse files
marco-ippolitoRafaelGSS
authored andcommitted
module: add module.stripTypeScriptTypes
PR-URL: #55282 Fixes: #54300 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com>
1 parent 0ea74f3 commit 69dd1e1
Copy full SHA for 69dd1e1

File tree

Expand file treeCollapse file tree

10 files changed

+358
-88
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

10 files changed

+358
-88
lines changed
Open diff view settings
Collapse file

‎doc/api/module.md‎

Copy file name to clipboardExpand all lines: doc/api/module.md
+101Lines changed: 101 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,105 @@ changes:
270270
Register a module that exports [hooks][] that customize Node.js module
271271
resolution and loading behavior. See [Customization hooks][].
272272
273+
## `module.stripTypeScriptTypes(code[, options])`
274+
275+
<!-- YAML
276+
added: REPLACEME
277+
-->
278+
279+
> Stability: 1.0 - Early development
280+
281+
* `code` {string} The code to strip type annotations from.
282+
* `options` {Object}
283+
* `mode` {string} **Default:** `'strip'`. Possible values are:
284+
* `'strip'` Only strip type annotations without performing the transformation of TypeScript features.
285+
* `'transform'` Strip type annotations and transform TypeScript features to JavaScript.
286+
* `sourceMap` {boolean} **Default:** `false`. Only when `mode` is `'transform'`, if `true`, a source map
287+
will be generated for the transformed code.
288+
* `sourceUrl` {string} Specifies the source url used in the source map.
289+
* Returns: {string} The code with type annotations stripped.
290+
`module.stripTypeScriptTypes()` removes type annotations from TypeScript code. It
291+
can be used to strip type annotations from TypeScript code before running it
292+
with `vm.runInContext()` or `vm.compileFunction()`.
293+
By default, it will throw an error if the code contains TypeScript features
294+
that require transformation such as `Enums`,
295+
see [type-stripping][] for more information.
296+
When mode is `'transform'`, it also transforms TypeScript features to JavaScript,
297+
see [transform TypeScript features][] for more information.
298+
When mode is `'strip'`, source maps are not generated, because locations are preserved.
299+
If `sourceMap` is provided, when mode is `'strip'`, an error will be thrown.
300+
301+
_WARNING_: The output of this function should not be considered stable across Node.js versions,
302+
due to changes in the TypeScript parser.
303+
304+
```mjs
305+
import { stripTypeScriptTypes } from 'node:module';
306+
const code = 'const a: number = 1;';
307+
const strippedCode = stripTypeScriptTypes(code);
308+
console.log(strippedCode);
309+
// Prints: const a = 1;
310+
```
311+
312+
```cjs
313+
const { stripTypeScriptTypes } = require('node:module');
314+
const code = 'const a: number = 1;';
315+
const strippedCode = stripTypeScriptTypes(code);
316+
console.log(strippedCode);
317+
// Prints: const a = 1;
318+
```
319+
320+
If `sourceUrl` is provided, it will be used appended as a comment at the end of the output:
321+
322+
```mjs
323+
import { stripTypeScriptTypes } from 'node:module';
324+
const code = 'const a: number = 1;';
325+
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
326+
console.log(strippedCode);
327+
// Prints: const a = 1\n\n//# sourceURL=source.ts;
328+
```
329+
330+
```cjs
331+
const { stripTypeScriptTypes } = require('node:module');
332+
const code = 'const a: number = 1;';
333+
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
334+
console.log(strippedCode);
335+
// Prints: const a = 1\n\n//# sourceURL=source.ts;
336+
```
337+
338+
When `mode` is `'transform'`, the code is transformed to JavaScript:
339+
340+
```mjs
341+
import { stripTypeScriptTypes } from 'node:module';
342+
const code = `
343+
namespace MathUtil {
344+
export const add = (a: number, b: number) => a + b;
345+
}`;
346+
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true });
347+
console.log(strippedCode);
348+
// Prints:
349+
// var MathUtil;
350+
// (function(MathUtil) {
351+
// MathUtil.add = (a, b)=>a + b;
352+
// })(MathUtil || (MathUtil = {}));
353+
// # sourceMappingURL=data:application/json;base64, ...
354+
```
355+
356+
```cjs
357+
const { stripTypeScriptTypes } = require('node:module');
358+
const code = `
359+
namespace MathUtil {
360+
export const add = (a: number, b: number) => a + b;
361+
}`;
362+
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true });
363+
console.log(strippedCode);
364+
// Prints:
365+
// var MathUtil;
366+
// (function(MathUtil) {
367+
// MathUtil.add = (a, b)=>a + b;
368+
// })(MathUtil || (MathUtil = {}));
369+
// # sourceMappingURL=data:application/json;base64, ...
370+
```
371+
273372
### `module.syncBuiltinESMExports()`
274373
275374
<!-- YAML
@@ -1251,3 +1350,5 @@ returned object contains the following keys:
12511350
[realm]: https://tc39.es/ecma262/#realm
12521351
[source map include directives]: https://sourcemaps.info/spec.html#h.lmz475t4mvbx
12531352
[transferable objects]: worker_threads.md#portpostmessagevalue-transferlist
1353+
[transform TypeScript features]: typescript.md#typescript-features
1354+
[type-stripping]: typescript.md#type-stripping
Collapse file

‎lib/internal/main/eval_string.js‎

Copy file name to clipboardExpand all lines: lib/internal/main/eval_string.js
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ const {
1414
markBootstrapComplete,
1515
} = require('internal/process/pre_execution');
1616
const { evalModuleEntryPoint, evalScript } = require('internal/process/execution');
17-
const { addBuiltinLibsToObject, stripTypeScriptTypes } = require('internal/modules/helpers');
18-
17+
const { addBuiltinLibsToObject } = require('internal/modules/helpers');
18+
const { stripTypeScriptModuleTypes } = require('internal/modules/typescript');
1919
const { getOptionValue } = require('internal/options');
2020

2121
prepareMainThreadExecution();
@@ -24,7 +24,7 @@ markBootstrapComplete();
2424

2525
const code = getOptionValue('--eval');
2626
const source = getOptionValue('--experimental-strip-types') ?
27-
stripTypeScriptTypes(code) :
27+
stripTypeScriptModuleTypes(code) :
2828
code;
2929

3030
const print = getOptionValue('--print');
Collapse file

‎lib/internal/modules/cjs/loader.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ const {
151151
setHasStartedUserCJSExecution,
152152
stripBOM,
153153
toRealPath,
154-
stripTypeScriptTypes,
155154
} = require('internal/modules/helpers');
155+
const { stripTypeScriptModuleTypes } = require('internal/modules/typescript');
156156
const packageJsonReader = require('internal/modules/package_json_reader');
157157
const { getOptionValue, getEmbedderOptions } = require('internal/options');
158158
const shouldReportRequiredModules = getLazy(() => process.env.WATCH_REPORT_DEPENDENCIES);
@@ -1357,7 +1357,7 @@ let hasPausedEntry = false;
13571357
function loadESMFromCJS(mod, filename) {
13581358
let source = getMaybeCachedSource(mod, filename);
13591359
if (getOptionValue('--experimental-strip-types') && path.extname(filename) === '.mts') {
1360-
source = stripTypeScriptTypes(source, filename);
1360+
source = stripTypeScriptModuleTypes(source, filename);
13611361
}
13621362
const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader();
13631363
const isMain = mod[kIsMainSymbol];
@@ -1599,7 +1599,7 @@ function getMaybeCachedSource(mod, filename) {
15991599

16001600
function loadCTS(module, filename) {
16011601
const source = getMaybeCachedSource(module, filename);
1602-
const code = stripTypeScriptTypes(source, filename);
1602+
const code = stripTypeScriptModuleTypes(source, filename);
16031603
module._compile(code, filename, 'commonjs');
16041604
}
16051605

@@ -1611,7 +1611,7 @@ function loadCTS(module, filename) {
16111611
function loadTS(module, filename) {
16121612
// If already analyzed the source, then it will be cached.
16131613
const source = getMaybeCachedSource(module, filename);
1614-
const content = stripTypeScriptTypes(source, filename);
1614+
const content = stripTypeScriptModuleTypes(source, filename);
16151615
let format;
16161616
const pkg = packageJsonReader.getNearestParentPackageJSON(filename);
16171617
// Function require shouldn't be used in ES modules.
@@ -1631,7 +1631,7 @@ function loadTS(module, filename) {
16311631
if (Module._cache[parentPath]) {
16321632
let parentSource;
16331633
try {
1634-
parentSource = stripTypeScriptTypes(fs.readFileSync(parentPath, 'utf8'), parentPath);
1634+
parentSource = stripTypeScriptModuleTypes(fs.readFileSync(parentPath, 'utf8'), parentPath);
16351635
} catch {
16361636
// Continue regardless of error.
16371637
}
Collapse file

‎lib/internal/modules/esm/get_format.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/esm/get_format.js
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,10 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE
164164
// Since experimental-strip-types depends on detect-module, we always return null
165165
// if source is undefined.
166166
if (!source) { return null; }
167-
const { stripTypeScriptTypes, stringify } = require('internal/modules/helpers');
167+
const { stringify } = require('internal/modules/helpers');
168+
const { stripTypeScriptModuleTypes } = require('internal/modules/typescript');
168169
const stringifiedSource = stringify(source);
169-
const parsedSource = stripTypeScriptTypes(stringifiedSource, fileURLToPath(url));
170+
const parsedSource = stripTypeScriptModuleTypes(stringifiedSource, fileURLToPath(url));
170171
const detectedFormat = detectModuleFormat(parsedSource, url);
171172
const format = `${detectedFormat}-typescript`;
172173
if (format === 'module-typescript' && foundPackageJson) {
Collapse file

‎lib/internal/modules/esm/translators.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/esm/translators.js
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ const {
3131
assertBufferSource,
3232
loadBuiltinModule,
3333
stringify,
34-
stripTypeScriptTypes,
3534
stripBOM,
3635
urlToFilename,
3736
} = require('internal/modules/helpers');
37+
const { stripTypeScriptModuleTypes } = require('internal/modules/typescript');
3838
const {
3939
kIsCachedByESMLoader,
4040
Module: CJSModule,
@@ -248,7 +248,7 @@ translators.set('require-commonjs', (url, source, isMain) => {
248248
translators.set('require-commonjs-typescript', (url, source, isMain) => {
249249
emitExperimentalWarning('Type Stripping');
250250
assert(cjsParse);
251-
const code = stripTypeScriptTypes(stringify(source), url);
251+
const code = stripTypeScriptModuleTypes(stringify(source), url);
252252
return createCJSModuleWrap(url, code);
253253
});
254254

@@ -463,7 +463,7 @@ translators.set('wasm', async function(url, source) {
463463
translators.set('commonjs-typescript', function(url, source) {
464464
emitExperimentalWarning('Type Stripping');
465465
assertBufferSource(source, true, 'load');
466-
const code = stripTypeScriptTypes(stringify(source), url);
466+
const code = stripTypeScriptModuleTypes(stringify(source), url);
467467
debug(`Translating TypeScript ${url}`);
468468
return FunctionPrototypeCall(translators.get('commonjs'), this, url, code, false);
469469
});
@@ -472,7 +472,7 @@ translators.set('commonjs-typescript', function(url, source) {
472472
translators.set('module-typescript', function(url, source) {
473473
emitExperimentalWarning('Type Stripping');
474474
assertBufferSource(source, true, 'load');
475-
const code = stripTypeScriptTypes(stringify(source), url);
475+
const code = stripTypeScriptModuleTypes(stringify(source), url);
476476
debug(`Translating TypeScript ${url}`);
477477
return FunctionPrototypeCall(translators.get('module'), this, url, code, false);
478478
});
Collapse file

‎lib/internal/modules/helpers.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/helpers.js
+1-73Lines changed: 1 addition & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ const {
1515
const {
1616
ERR_INVALID_ARG_TYPE,
1717
ERR_INVALID_RETURN_PROPERTY_VALUE,
18-
ERR_INVALID_TYPESCRIPT_SYNTAX,
19-
ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING,
2018
} = require('internal/errors').codes;
2119
const { BuiltinModule } = require('internal/bootstrap/realm');
2220

@@ -27,9 +25,8 @@ const path = require('path');
2725
const { pathToFileURL, fileURLToPath } = require('internal/url');
2826
const assert = require('internal/assert');
2927

30-
const { Buffer } = require('buffer');
3128
const { getOptionValue } = require('internal/options');
32-
const { assertTypeScript, setOwnProperty, getLazy, isUnderNodeModules } = require('internal/util');
29+
const { setOwnProperty, getLazy } = require('internal/util');
3330
const { inspect } = require('internal/util/inspect');
3431

3532
const lazyTmpdir = getLazy(() => require('os').tmpdir());
@@ -314,74 +311,6 @@ function getBuiltinModule(id) {
314311
return normalizedId ? require(normalizedId) : undefined;
315312
}
316313

317-
/**
318-
* The TypeScript parsing mode, either 'strip-only' or 'transform'.
319-
* @type {string}
320-
*/
321-
const getTypeScriptParsingMode = getLazy(() =>
322-
(getOptionValue('--experimental-transform-types') ? 'transform' : 'strip-only'),
323-
);
324-
325-
/**
326-
* Load the TypeScript parser.
327-
* and returns an object with a `code` property.
328-
* @returns {Function} The TypeScript parser function.
329-
*/
330-
const loadTypeScriptParser = getLazy(() => {
331-
assertTypeScript();
332-
const amaro = require('internal/deps/amaro/dist/index');
333-
return amaro.transformSync;
334-
});
335-
336-
/**
337-
*
338-
* @param {string} source the source code
339-
* @param {object} options the options to pass to the parser
340-
* @returns {TransformOutput} an object with a `code` property.
341-
*/
342-
function parseTypeScript(source, options) {
343-
const parse = loadTypeScriptParser();
344-
try {
345-
return parse(source, options);
346-
} catch (error) {
347-
throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error);
348-
}
349-
}
350-
351-
/**
352-
* @typedef {object} TransformOutput
353-
* @property {string} code The compiled code.
354-
* @property {string} [map] The source maps (optional).
355-
*
356-
* Performs type-stripping to TypeScript source code.
357-
* @param {string} source TypeScript code to parse.
358-
* @param {string} filename The filename of the source code.
359-
* @returns {TransformOutput} The stripped TypeScript code.
360-
*/
361-
function stripTypeScriptTypes(source, filename) {
362-
if (isUnderNodeModules(filename)) {
363-
throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename);
364-
}
365-
assert(typeof source === 'string');
366-
const options = {
367-
__proto__: null,
368-
mode: getTypeScriptParsingMode(),
369-
sourceMap: getOptionValue('--enable-source-maps'),
370-
filename,
371-
};
372-
const { code, map } = parseTypeScript(source, options);
373-
if (map) {
374-
// TODO(@marco-ippolito) When Buffer.transcode supports utf8 to
375-
// base64 transformation, we should change this line.
376-
const base64SourceMap = Buffer.from(map).toString('base64');
377-
return `${code}\n\n//# sourceMappingURL=data:application/json;base64,${base64SourceMap}`;
378-
}
379-
// Source map is not necessary in strip-only mode. However, to map the source
380-
// file in debuggers to the original TypeScript source, add a sourceURL magic
381-
// comment to hint that it is a generated source.
382-
return `${code}\n\n//# sourceURL=${filename}`;
383-
}
384-
385314
/** @type {import('internal/util/types')} */
386315
let _TYPES = null;
387316
/**
@@ -485,7 +414,6 @@ module.exports = {
485414
loadBuiltinModule,
486415
makeRequireFunction,
487416
normalizeReferrerURL,
488-
stripTypeScriptTypes,
489417
stringify,
490418
stripBOM,
491419
toRealPath,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.