Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/eslint.config_partial.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ const noRestrictedSyntax = [
selector: "CallExpression[callee.object.name='assert']:not([callee.property.name='ok']):not([callee.property.name='fail']):not([callee.property.name='ifError'])",
message: 'Only use simple assertions',
},
{
// Forbids usages of `btoa` that are not caught by no-restricted-globals, like:
// ```
// const { btoa } = internalBinding('buffer');
// btoa('...');
// ```
selector: "CallExpression[callee.property.name='btoa'], CallExpression[callee.name='btoa']",
message: "`btoa` supports only latin-1 charset, use Buffer.from(str).toString('base64') instead",
},
{
selector: 'NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError|NodeAggregateError)$/])',
message: "Use an error exported by 'internal/errors' instead.",
Expand Down
8 changes: 5 additions & 3 deletions lib/internal/modules/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const {
} = require('internal/errors').codes;
const { getOptionValue } = require('internal/options');
const assert = require('internal/assert');
const { Buffer } = require('buffer');

/**
* The TypeScript parsing mode, either 'strip-only' or 'transform'.
Expand Down Expand Up @@ -134,9 +135,10 @@ function stripTypeScriptModuleTypes(source, filename) {
* @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);
// The base64 encoding should be https://datatracker.ietf.org/doc/html/rfc4648#section-4,
// not base64url https://datatracker.ietf.org/doc/html/rfc4648#section-5. See data url
// spec https://tools.ietf.org/html/rfc2397#section-2.
const base64SourceMap = Buffer.from(sourceMap).toString('base64');
return `${code}\n\n//# sourceMappingURL=data:application/json;base64,${base64SourceMap}`;
}

Expand Down
65 changes: 39 additions & 26 deletions test/parallel/test-module-strip-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ common.expectWarning(
'stripTypeScriptTypes is an experimental feature and might change at any time',
);

const sourceToBeTransformed = `
namespace MathUtil {
export const add = (a: number, b: number) => a + b;
}`;
const sourceToBeTransformedMapping = 'UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA';

test('stripTypeScriptTypes', () => {
const source = 'const x: number = 1;';
const result = stripTypeScriptTypes(source);
Expand Down Expand Up @@ -48,45 +54,52 @@ test('stripTypeScriptTypes sourceUrl throws when mode is strip', () => {
});

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 result = stripTypeScriptTypes(sourceToBeTransformed, { mode: 'transform', sourceMap: true });
const script = new vm.Script(result);
const sourceMap =
{
version: 3,
sources: [
'<anon>',
],
sourcesContent: [
'\n namespace MathUtil {\n export const add = (a: number, b: number) => a + b;\n }',
],
sources: [''],
names: [],
mappings: ';UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA'
mappings: sourceToBeTransformedMapping,
};
assert(script.sourceMapURL, `sourceMappingURL=data:application/json;base64,${JSON.stringify(sourceMap)}`);
const inlinedSourceMap = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
assert.strictEqual(script.sourceMapURL, `data:application/json;base64,${inlinedSourceMap}`);
});

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 result = stripTypeScriptTypes(sourceToBeTransformed, {
mode: 'transform',
sourceMap: true,
sourceUrl: 'test.ts'
});
const script = new vm.Script(result);
const sourceMap =
{
version: 3,
sources: ['test.ts'],
names: [],
mappings: sourceToBeTransformedMapping,
};
const inlinedSourceMap = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
assert.strictEqual(script.sourceMapURL, `data:application/json;base64,${inlinedSourceMap}`);
});

test('stripTypeScriptTypes source map when mode is transform and sourceUrl with non-latin-1 chars', () => {
const sourceUrl = 'dir%20with $unusual"chars?\'åß∂ƒ©∆¬…`.cts';
const result = stripTypeScriptTypes(sourceToBeTransformed, {
mode: 'transform',
sourceMap: true,
sourceUrl,
});
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 }',
],
sources: [sourceUrl],
names: [],
mappings: ';UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA'
mappings: sourceToBeTransformedMapping,
};
assert(script.sourceMapURL, `sourceMappingURL=data:application/json;base64,${JSON.stringify(sourceMap)}`);
const inlinedSourceMap = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
assert.strictEqual(script.sourceMapURL, `data:application/json;base64,${inlinedSourceMap}`);
});
Loading