diff --git a/doc/api/cli.md b/doc/api/cli.md index 552d00ef07a404..321d2b9ec130ea 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1042,7 +1042,7 @@ Use this flag to enable [ShadowRealm][] support. added: v22.6.0 --> -> Stability: 1.0 - Early development +> Stability: 1.1 - Active development Enable experimental type-stripping for TypeScript files. For more information, see the [TypeScript type-stripping][] documentation. @@ -1096,7 +1096,7 @@ Enable module mocking in the test runner. added: v22.7.0 --> -> Stability: 1.0 - Early development +> Stability: 1.1 - Active development Enables the transformation of TypeScript-only syntax into JavaScript code. Implies `--experimental-strip-types` and `--enable-source-maps`. diff --git a/doc/api/module.md b/doc/api/module.md index 7a1928da5b05a2..d0c3f82d71dae2 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -352,6 +352,105 @@ changes: Register a module that exports [hooks][] that customize Node.js module resolution and loading behavior. See [Customization hooks][]. +## `module.stripTypeScriptTypes(code[, options])` + + + +> Stability: 1.1 - Active development + +* `code` {string} The code to strip type annotations from. +* `options` {Object} + * `mode` {string} **Default:** `'strip'`. Possible values are: + * `'strip'` Only strip type annotations without performing the transformation of TypeScript features. + * `'transform'` Strip type annotations and transform TypeScript features to JavaScript. + * `sourceMap` {boolean} **Default:** `false`. Only when `mode` is `'transform'`, if `true`, a source map + will be generated for the transformed code. + * `sourceUrl` {string} Specifies the source url used in the source map. +* Returns: {string} The code with type annotations stripped. + `module.stripTypeScriptTypes()` removes type annotations from TypeScript code. It + can be used to strip type annotations from TypeScript code before running it + with `vm.runInContext()` or `vm.compileFunction()`. + By default, it will throw an error if the code contains TypeScript features + that require transformation such as `Enums`, + see [type-stripping][] for more information. + When mode is `'transform'`, it also transforms TypeScript features to JavaScript, + see [transform TypeScript features][] for more information. + When mode is `'strip'`, source maps are not generated, because locations are preserved. + If `sourceMap` is provided, when mode is `'strip'`, an error will be thrown. + +_WARNING_: The output of this function should not be considered stable across Node.js versions, +due to changes in the TypeScript parser. + +```mjs +import { stripTypeScriptTypes } from 'node:module'; +const code = 'const a: number = 1;'; +const strippedCode = stripTypeScriptTypes(code); +console.log(strippedCode); +// Prints: const a = 1; +``` + +```cjs +const { stripTypeScriptTypes } = require('node:module'); +const code = 'const a: number = 1;'; +const strippedCode = stripTypeScriptTypes(code); +console.log(strippedCode); +// Prints: const a = 1; +``` + +If `sourceUrl` is provided, it will be used appended as a comment at the end of the output: + +```mjs +import { stripTypeScriptTypes } from 'node:module'; +const code = 'const a: number = 1;'; +const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' }); +console.log(strippedCode); +// Prints: const a = 1\n\n//# sourceURL=source.ts; +``` + +```cjs +const { stripTypeScriptTypes } = require('node:module'); +const code = 'const a: number = 1;'; +const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' }); +console.log(strippedCode); +// Prints: const a = 1\n\n//# sourceURL=source.ts; +``` + +When `mode` is `'transform'`, the code is transformed to JavaScript: + +```mjs +import { stripTypeScriptTypes } from 'node:module'; +const code = ` + namespace MathUtil { + export const add = (a: number, b: number) => a + b; + }`; +const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true }); +console.log(strippedCode); +// Prints: +// var MathUtil; +// (function(MathUtil) { +// MathUtil.add = (a, b)=>a + b; +// })(MathUtil || (MathUtil = {})); +// # sourceMappingURL=data:application/json;base64, ... +``` + +```cjs +const { stripTypeScriptTypes } = require('node:module'); +const code = ` + namespace MathUtil { + export const add = (a: number, b: number) => a + b; + }`; +const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true }); +console.log(strippedCode); +// Prints: +// var MathUtil; +// (function(MathUtil) { +// MathUtil.add = (a, b)=>a + b; +// })(MathUtil || (MathUtil = {})); +// # sourceMappingURL=data:application/json;base64, ... +``` + ### `module.syncBuiltinESMExports()` -> Stability: 1.0 - Early development +> Stability: 1.1 - Active development * {boolean|string} diff --git a/doc/api/typescript.md b/doc/api/typescript.md index 4415d2ea5e34aa..d2680670a5f316 100644 --- a/doc/api/typescript.md +++ b/doc/api/typescript.md @@ -7,7 +7,7 @@ changes: description: Added `--experimental-transform-types` flag. --> -> Stability: 1.0 - Early development +> Stability: 1.1 - Active development ## Enabling @@ -50,7 +50,7 @@ To use TypeScript with full support for all TypeScript features, including added: v22.6.0 --> -> Stability: 1.0 - Early development +> Stability: 1.1 - Active development The flag [`--experimental-strip-types`][] enables Node.js to run TypeScript files. By default Node.js will execute only files that contain no diff --git a/lib/internal/main/eval_string.js b/lib/internal/main/eval_string.js index 2ca016c4960e85..a3be08551425b1 100644 --- a/lib/internal/main/eval_string.js +++ b/lib/internal/main/eval_string.js @@ -14,8 +14,8 @@ const { markBootstrapComplete, } = require('internal/process/pre_execution'); const { evalModuleEntryPoint, evalScript } = require('internal/process/execution'); -const { addBuiltinLibsToObject, stripTypeScriptTypes } = require('internal/modules/helpers'); - +const { addBuiltinLibsToObject } = require('internal/modules/helpers'); +const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); const { getOptionValue } = require('internal/options'); prepareMainThreadExecution(); @@ -24,7 +24,7 @@ markBootstrapComplete(); const code = getOptionValue('--eval'); const source = getOptionValue('--experimental-strip-types') ? - stripTypeScriptTypes(code) : + stripTypeScriptModuleTypes(code) : code; const print = getOptionValue('--print'); diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index a1f95d0efaca27..559d431acf1e77 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -125,10 +125,10 @@ const { pathToFileURL, fileURLToPath, isURL } = require('internal/url'); const { pendingDeprecate, emitExperimentalWarning, - isUnderNodeModules, kEmptyObject, setOwnProperty, getLazy, + isUnderNodeModules, isWindows, } = require('internal/util'); const { @@ -153,8 +153,8 @@ const { setHasStartedUserCJSExecution, stripBOM, toRealPath, - stripTypeScriptTypes, } = require('internal/modules/helpers'); +const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); const packageJsonReader = require('internal/modules/package_json_reader'); const { getOptionValue, getEmbedderOptions } = require('internal/options'); const shouldReportRequiredModules = getLazy(() => process.env.WATCH_REPORT_DEPENDENCIES); @@ -170,7 +170,6 @@ const { ERR_REQUIRE_CYCLE_MODULE, ERR_REQUIRE_ESM, ERR_UNKNOWN_BUILTIN_MODULE, - ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, }, setArrowMessage, } = require('internal/errors'); @@ -1348,10 +1347,7 @@ let emittedRequireModuleWarning = false; function loadESMFromCJS(mod, filename) { let source = getMaybeCachedSource(mod, filename); if (getOptionValue('--experimental-strip-types') && path.extname(filename) === '.mts') { - if (isUnderNodeModules(filename)) { - throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); - } - source = stripTypeScriptTypes(source, filename); + source = stripTypeScriptModuleTypes(source, filename); } const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader(); const isMain = mod[kIsMainSymbol]; @@ -1587,11 +1583,8 @@ function getMaybeCachedSource(mod, filename) { } function loadCTS(module, filename) { - if (isUnderNodeModules(filename)) { - throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); - } const source = getMaybeCachedSource(module, filename); - const code = stripTypeScriptTypes(source, filename); + const code = stripTypeScriptModuleTypes(source, filename); module._compile(code, filename, 'commonjs'); } @@ -1601,12 +1594,9 @@ function loadCTS(module, filename) { * @param {string} filename The file path of the module */ function loadTS(module, filename) { - if (isUnderNodeModules(filename)) { - throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); - } // If already analyzed the source, then it will be cached. const source = getMaybeCachedSource(module, filename); - const content = stripTypeScriptTypes(source, filename); + const content = stripTypeScriptModuleTypes(source, filename); let format; const pkg = packageJsonReader.getNearestParentPackageJSON(filename); // Function require shouldn't be used in ES modules. @@ -1626,7 +1616,7 @@ function loadTS(module, filename) { if (Module._cache[parentPath]) { let parentSource; try { - parentSource = stripTypeScriptTypes(fs.readFileSync(parentPath, 'utf8'), parentPath); + parentSource = stripTypeScriptModuleTypes(fs.readFileSync(parentPath, 'utf8'), parentPath); } catch { // Continue regardless of error. } diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js index 26d0bace6cdd39..9519f947b8dfdc 100644 --- a/lib/internal/modules/esm/get_format.js +++ b/lib/internal/modules/esm/get_format.js @@ -164,9 +164,10 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE // Since experimental-strip-types depends on detect-module, we always return null // if source is undefined. if (!source) { return null; } - const { stripTypeScriptTypes, stringify } = require('internal/modules/helpers'); + const { stringify } = require('internal/modules/helpers'); + const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); const stringifiedSource = stringify(source); - const parsedSource = stripTypeScriptTypes(stringifiedSource, fileURLToPath(url)); + const parsedSource = stripTypeScriptModuleTypes(stringifiedSource, fileURLToPath(url)); const detectedFormat = detectModuleFormat(parsedSource, url); const format = `${detectedFormat}-typescript`; if (format === 'module-typescript' && foundPackageJson) { diff --git a/lib/internal/modules/esm/load.js b/lib/internal/modules/esm/load.js index d56dae3f001b1c..5ba13096b98047 100644 --- a/lib/internal/modules/esm/load.js +++ b/lib/internal/modules/esm/load.js @@ -4,7 +4,6 @@ const { RegExpPrototypeExec, } = primordials; const { - isUnderNodeModules, kEmptyObject, } = require('internal/util'); @@ -23,7 +22,6 @@ const { ERR_INVALID_URL, ERR_UNKNOWN_MODULE_FORMAT, ERR_UNSUPPORTED_ESM_URL_SCHEME, - ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, } = require('internal/errors').codes; const { @@ -131,12 +129,6 @@ async function defaultLoad(url, context = kEmptyObject) { validateAttributes(url, format, importAttributes); - if (getOptionValue('--experimental-strip-types') && - (format === 'module-typescript' || format === 'commonjs-typescript') && - isUnderNodeModules(url)) { - throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(url); - } - return { __proto__: null, format, diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index bbe2380b0ed6d7..491dc3f450733a 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -30,10 +30,10 @@ const { assertBufferSource, loadBuiltinModule, stringify, - stripTypeScriptTypes, stripBOM, urlToFilename, } = require('internal/modules/helpers'); +const { stripTypeScriptModuleTypes } = require('internal/modules/typescript'); const { kIsCachedByESMLoader, Module: CJSModule, @@ -244,7 +244,7 @@ translators.set('require-commonjs', (url, source, isMain) => { translators.set('require-commonjs-typescript', (url, source, isMain) => { emitExperimentalWarning('Type Stripping'); assert(cjsParse); - const code = stripTypeScriptTypes(stringify(source), url); + const code = stripTypeScriptModuleTypes(stringify(source), url); return createCJSModuleWrap(url, code); }); @@ -459,7 +459,7 @@ translators.set('wasm', async function(url, source) { translators.set('commonjs-typescript', function(url, source) { emitExperimentalWarning('Type Stripping'); assertBufferSource(source, true, 'load'); - const code = stripTypeScriptTypes(stringify(source), url); + const code = stripTypeScriptModuleTypes(stringify(source), url); debug(`Translating TypeScript ${url}`); return FunctionPrototypeCall(translators.get('commonjs'), this, url, code, false); }); @@ -468,7 +468,7 @@ translators.set('commonjs-typescript', function(url, source) { translators.set('module-typescript', function(url, source) { emitExperimentalWarning('Type Stripping'); assertBufferSource(source, true, 'load'); - const code = stripTypeScriptTypes(stringify(source), url); + const code = stripTypeScriptModuleTypes(stringify(source), url); debug(`Translating TypeScript ${url}`); return FunctionPrototypeCall(translators.get('module'), this, url, code, false); }); diff --git a/lib/internal/modules/helpers.js b/lib/internal/modules/helpers.js index 3153ecec198392..4445c0dc3fae73 100644 --- a/lib/internal/modules/helpers.js +++ b/lib/internal/modules/helpers.js @@ -15,7 +15,6 @@ const { const { ERR_INVALID_ARG_TYPE, ERR_INVALID_RETURN_PROPERTY_VALUE, - ERR_INVALID_TYPESCRIPT_SYNTAX, } = require('internal/errors').codes; const { BuiltinModule } = require('internal/bootstrap/realm'); @@ -26,9 +25,8 @@ const path = require('path'); const { pathToFileURL, fileURLToPath } = require('internal/url'); const assert = require('internal/assert'); -const { Buffer } = require('buffer'); const { getOptionValue } = require('internal/options'); -const { assertTypeScript, setOwnProperty, getLazy } = require('internal/util'); +const { setOwnProperty, getLazy } = require('internal/util'); const { inspect } = require('internal/util/inspect'); const lazyTmpdir = getLazy(() => require('os').tmpdir()); @@ -313,72 +311,6 @@ function getBuiltinModule(id) { return normalizedId ? require(normalizedId) : undefined; } -/** - * The TypeScript parsing mode, either 'strip-only' or 'transform'. - * @type {string} - */ -const getTypeScriptParsingMode = getLazy(() => - (getOptionValue('--experimental-transform-types') ? 'transform' : 'strip-only'), -); - -/** - * Load the TypeScript parser. - * and returns an object with a `code` property. - * @returns {Function} The TypeScript parser function. - */ -const loadTypeScriptParser = getLazy(() => { - assertTypeScript(); - const amaro = require('internal/deps/amaro/dist/index'); - return amaro.transformSync; -}); - -/** - * - * @param {string} source the source code - * @param {object} options the options to pass to the parser - * @returns {TransformOutput} an object with a `code` property. - */ -function parseTypeScript(source, options) { - const parse = loadTypeScriptParser(); - try { - return parse(source, options); - } catch (error) { - throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error); - } -} - -/** - * @typedef {object} TransformOutput - * @property {string} code The compiled code. - * @property {string} [map] The source maps (optional). - * - * Performs type-stripping to TypeScript source code. - * @param {string} source TypeScript code to parse. - * @param {string} filename The filename of the source code. - * @returns {TransformOutput} The stripped TypeScript code. - */ -function stripTypeScriptTypes(source, filename) { - assert(typeof source === 'string'); - const options = { - __proto__: null, - mode: getTypeScriptParsingMode(), - sourceMap: getOptionValue('--enable-source-maps'), - filename, - }; - const { code, map } = parseTypeScript(source, options); - if (map) { - // TODO(@marco-ippolito) When Buffer.transcode supports utf8 to - // base64 transformation, we should change this line. - const base64SourceMap = Buffer.from(map).toString('base64'); - return `${code}\n\n//# sourceMappingURL=data:application/json;base64,${base64SourceMap}`; - } - // Source map is not necessary in strip-only mode. However, to map the source - // file in debuggers to the original TypeScript source, add a sourceURL magic - // comment to hint that it is a generated source. - return `${code}\n\n//# sourceURL=${filename}`; -} - - /** * Enable on-disk compiled cache for all user modules being complied in the current Node.js instance * after this method is called. @@ -481,7 +413,6 @@ module.exports = { loadBuiltinModule, makeRequireFunction, normalizeReferrerURL, - stripTypeScriptTypes, stringify, stripBOM, toRealPath, diff --git a/lib/internal/modules/typescript.js b/lib/internal/modules/typescript.js new file mode 100644 index 00000000000000..d1b58e86c72ee7 --- /dev/null +++ b/lib/internal/modules/typescript.js @@ -0,0 +1,146 @@ +'use strict'; + +const { + validateBoolean, + validateOneOf, + validateObject, + validateString, +} = require('internal/validators'); +const { assertTypeScript, + emitExperimentalWarning, + getLazy, + isUnderNodeModules, + kEmptyObject } = require('internal/util'); +const { + ERR_INVALID_TYPESCRIPT_SYNTAX, + ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, +} = require('internal/errors').codes; +const { getOptionValue } = require('internal/options'); +const assert = require('internal/assert'); + +/** + * The TypeScript parsing mode, either 'strip-only' or 'transform'. + * @type {string} + */ +const getTypeScriptParsingMode = getLazy(() => + (getOptionValue('--experimental-transform-types') ? 'transform' : 'strip-only'), +); + +/** + * Load the TypeScript parser. + * and returns an object with a `code` property. + * @returns {Function} The TypeScript parser function. + */ +const loadTypeScriptParser = getLazy(() => { + assertTypeScript(); + const amaro = require('internal/deps/amaro/dist/index'); + return amaro.transformSync; +}); + +/** + * + * @param {string} source the source code + * @param {object} options the options to pass to the parser + * @returns {TransformOutput} an object with a `code` property. + */ +function parseTypeScript(source, options) { + const parse = loadTypeScriptParser(); + try { + return parse(source, options); + } catch (error) { + throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error); + } +} + +/** + * Performs type-stripping to TypeScript source code. + * @param {string} code TypeScript code to parse. + * @param {TransformOptions} options The configuration for type stripping. + * @returns {string} The stripped TypeScript code. + */ +function stripTypeScriptTypes(code, options = kEmptyObject) { + emitExperimentalWarning('stripTypeScriptTypes'); + validateString(code, 'code'); + validateObject(options, 'options'); + + const { + sourceMap = false, + sourceUrl = '', + } = options; + let { mode = 'strip' } = options; + validateOneOf(mode, 'options.mode', ['strip', 'transform']); + validateBoolean(sourceMap, 'options.sourceMap'); + validateString(sourceUrl, 'options.sourceUrl'); + if (mode === 'strip') { + validateOneOf(sourceMap, 'options.sourceMap', [false, undefined]); + // Rename mode from 'strip' to 'strip-only'. + // The reason is to match `process.features.typescript` which returns `strip`, + // but the parser expects `strip-only`. + mode = 'strip-only'; + } + + return processTypeScriptCode(code, { + mode, + sourceMap, + filename: sourceUrl, + }); +} + +/** + * Processes TypeScript code by stripping types or transforming. + * Handles source maps if needed. + * @param {string} code TypeScript code to process. + * @param {object} options The configuration object. + * @returns {string} The processed code. + */ +function processTypeScriptCode(code, options) { + const { code: transformedCode, map } = parseTypeScript(code, options); + + if (map) { + return addSourceMap(transformedCode, map); + } + + if (options.filename) { + return `${transformedCode}\n\n//# sourceURL=${options.filename}`; + } + + return transformedCode; +} + +/** + * Performs type-stripping to TypeScript source code internally. + * It is used by internal loaders. + * @param {string} source TypeScript code to parse. + * @param {string} filename The filename of the source code. + * @returns {TransformOutput} The stripped TypeScript code. + */ +function stripTypeScriptModuleTypes(source, filename) { + assert(typeof source === 'string'); + if (isUnderNodeModules(filename)) { + throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); + } + const options = { + mode: getTypeScriptParsingMode(), + sourceMap: getOptionValue('--enable-source-maps'), + filename, + }; + return processTypeScriptCode(source, options); +} + +/** + * + * @param {string} code The compiled code. + * @param {string} sourceMap The source map. + * @returns {string} The code with the source map attached. + */ +function addSourceMap(code, sourceMap) { + // TODO(@marco-ippolito) When Buffer.transcode supports utf8 to + // base64 transformation, we should change this line. + const base64SourceMap = internalBinding('buffer').btoa(sourceMap); + return `${code}\n\n//# sourceMappingURL=data:application/json;base64,${base64SourceMap}`; +} + +module.exports = { + stripTypeScriptModuleTypes, + stripTypeScriptTypes, +}; diff --git a/lib/module.js b/lib/module.js index 4ab0e6b5979a53..92c26fb548c02f 100644 --- a/lib/module.js +++ b/lib/module.js @@ -13,6 +13,7 @@ const { const { findPackageJSON, } = require('internal/modules/package_json_reader'); +const { stripTypeScriptTypes } = require('internal/modules/typescript'); Module.findSourceMap = findSourceMap; Module.register = register; @@ -21,6 +22,6 @@ Module.constants = constants; Module.enableCompileCache = enableCompileCache; Module.findPackageJSON = findPackageJSON; Module.flushCompileCache = flushCompileCache; - +Module.stripTypeScriptTypes = stripTypeScriptTypes; Module.getCompileCacheDir = getCompileCacheDir; module.exports = Module; diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 39be9f3cf6a1b2..12adfaa7f5c5e1 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -104,6 +104,7 @@ expected.beforePreExec = new Set([ 'NativeModule diagnostics_channel', 'Internal Binding wasm_web_api', 'NativeModule internal/events/abort_listener', + 'NativeModule internal/modules/typescript', ]); expected.atRunTime = new Set([ diff --git a/test/parallel/test-module-strip-types.js b/test/parallel/test-module-strip-types.js new file mode 100644 index 00000000000000..6e729a55936804 --- /dev/null +++ b/test/parallel/test-module-strip-types.js @@ -0,0 +1,92 @@ +'use strict'; + +const common = require('../common'); +if (!process.config.variables.node_use_amaro) common.skip('Requires Amaro'); +const assert = require('assert'); +const vm = require('node:vm'); +const { stripTypeScriptTypes } = require('node:module'); +const { test } = require('node:test'); + +common.expectWarning( + 'ExperimentalWarning', + 'stripTypeScriptTypes is an experimental feature and might change at any time', +); + +test('stripTypeScriptTypes', () => { + const source = 'const x: number = 1;'; + const result = stripTypeScriptTypes(source); + assert.strictEqual(result, 'const x = 1;'); +}); + +test('stripTypeScriptTypes explicit', () => { + const source = 'const x: number = 1;'; + const result = stripTypeScriptTypes(source, { mode: 'strip' }); + assert.strictEqual(result, 'const x = 1;'); +}); + +test('stripTypeScriptTypes code is not a string', () => { + assert.throws(() => stripTypeScriptTypes({}), + { code: 'ERR_INVALID_ARG_TYPE' }); +}); + +test('stripTypeScriptTypes invalid mode', () => { + const source = 'const x: number = 1;'; + assert.throws(() => stripTypeScriptTypes(source, { mode: 'invalid' }), { code: 'ERR_INVALID_ARG_VALUE' }); +}); + +test('stripTypeScriptTypes sourceMap throws when mode is strip', () => { + const source = 'const x: number = 1;'; + assert.throws(() => stripTypeScriptTypes(source, + { mode: 'strip', sourceMap: true }), + { code: 'ERR_INVALID_ARG_VALUE' }); +}); + +test('stripTypeScriptTypes sourceUrl throws when mode is strip', () => { + const source = 'const x: number = 1;'; + const result = stripTypeScriptTypes(source, { mode: 'strip', sourceUrl: 'foo.ts' }); + assert.strictEqual(result, 'const x = 1;\n\n//# sourceURL=foo.ts'); +}); + +test('stripTypeScriptTypes source map when mode is transform', () => { + const source = ` + namespace MathUtil { + export const add = (a: number, b: number) => a + b; + }`; + const result = stripTypeScriptTypes(source, { mode: 'transform', sourceMap: true }); + const script = new vm.Script(result); + const sourceMap = + { + version: 3, + sources: [ + '', + ], + sourcesContent: [ + '\n namespace MathUtil {\n export const add = (a: number, b: number) => a + b;\n }', + ], + names: [], + mappings: ';UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA' + }; + assert(script.sourceMapURL, `sourceMappingURL=data:application/json;base64,${JSON.stringify(sourceMap)}`); +}); + +test('stripTypeScriptTypes source map when mode is transform and sourceUrl', () => { + const source = ` + namespace MathUtil { + export const add = (a: number, b: number) => a + b; + }`; + const result = stripTypeScriptTypes(source, { mode: 'transform', sourceMap: true, sourceUrl: 'test.ts' }); + const script = new vm.Script(result); + const sourceMap = + { + version: 3, + sources: [ + 'test.ts', + ], + sourcesContent: [ + '\n namespace MathUtil {\n export const add = (a: number, b: number) => a + b;\n }', + ], + names: [], + mappings: ';UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA' + }; + assert(script.sourceMapURL, `sourceMappingURL=data:application/json;base64,${JSON.stringify(sourceMap)}`); +});