Skip to content

Commit

Permalink
module: use buffer.toString base64
Browse files Browse the repository at this point in the history
`btoa` only supports latin-1 charset and produces invalid source
mapping urls.
  • Loading branch information
legendecas committed Dec 19, 2024
1 parent 0d00511 commit 7572a08
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 29 deletions.
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` 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}`);
});

0 comments on commit 7572a08

Please sign in to comment.