Skip to content

Commit d80a7c5

Browse files
committed
modules: use buffer.toString base64
`btoa` only supports latin-1 charset and produces invalid source mapping urls.
1 parent 0d00511 commit d80a7c5

File tree

3 files changed

+48
-27
lines changed

3 files changed

+48
-27
lines changed

lib/eslint.config_partial.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ const noRestrictedSyntax = [
1313
selector: "CallExpression[callee.object.name='assert']:not([callee.property.name='ok']):not([callee.property.name='fail']):not([callee.property.name='ifError'])",
1414
message: 'Only use simple assertions',
1515
},
16+
{
17+
selector: "CallExpression[callee.property.name='btoa'], CallExpression[callee.name='btoa']",
18+
message: "`btoa` supports only latin-1 charset, use Buffer.from(str).toString('base64') instead",
19+
},
1620
{
1721
selector: 'NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError|NodeAggregateError)$/])',
1822
message: "Use an error exported by 'internal/errors' instead.",

lib/internal/modules/typescript.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const {
1717
} = require('internal/errors').codes;
1818
const { getOptionValue } = require('internal/options');
1919
const assert = require('internal/assert');
20+
const { Buffer } = require('buffer');
2021

2122
/**
2223
* The TypeScript parsing mode, either 'strip-only' or 'transform'.
@@ -136,7 +137,10 @@ function stripTypeScriptModuleTypes(source, filename) {
136137
function addSourceMap(code, sourceMap) {
137138
// TODO(@marco-ippolito) When Buffer.transcode supports utf8 to
138139
// base64 transformation, we should change this line.
139-
const base64SourceMap = internalBinding('buffer').btoa(sourceMap);
140+
// The base64 encoding should be https://datatracker.ietf.org/doc/html/rfc4648#section-4,
141+
// not base64url https://datatracker.ietf.org/doc/html/rfc4648#section-5. See data url
142+
// spec https://tools.ietf.org/html/rfc2397#section-2.
143+
const base64SourceMap = Buffer.from(sourceMap).toString('base64');
140144
return `${code}\n\n//# sourceMappingURL=data:application/json;base64,${base64SourceMap}`;
141145
}
142146

test/parallel/test-module-strip-types.js

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ common.expectWarning(
1212
'stripTypeScriptTypes is an experimental feature and might change at any time',
1313
);
1414

15+
const sourceToBeTransformed = `
16+
namespace MathUtil {
17+
export const add = (a: number, b: number) => a + b;
18+
}`;
19+
const sourceToBeTransformedMapping = 'UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA';
20+
1521
test('stripTypeScriptTypes', () => {
1622
const source = 'const x: number = 1;';
1723
const result = stripTypeScriptTypes(source);
@@ -48,45 +54,52 @@ test('stripTypeScriptTypes sourceUrl throws when mode is strip', () => {
4854
});
4955

5056
test('stripTypeScriptTypes source map when mode is transform', () => {
51-
const source = `
52-
namespace MathUtil {
53-
export const add = (a: number, b: number) => a + b;
54-
}`;
55-
const result = stripTypeScriptTypes(source, { mode: 'transform', sourceMap: true });
57+
const result = stripTypeScriptTypes(sourceToBeTransformed, { mode: 'transform', sourceMap: true });
5658
const script = new vm.Script(result);
5759
const sourceMap =
5860
{
5961
version: 3,
60-
sources: [
61-
'<anon>',
62-
],
63-
sourcesContent: [
64-
'\n namespace MathUtil {\n export const add = (a: number, b: number) => a + b;\n }',
65-
],
62+
sources: [''],
6663
names: [],
67-
mappings: ';UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA'
64+
mappings: sourceToBeTransformedMapping,
6865
};
69-
assert(script.sourceMapURL, `sourceMappingURL=data:application/json;base64,${JSON.stringify(sourceMap)}`);
66+
const inlinedSourceMap = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
67+
assert.strictEqual(script.sourceMapURL, `data:application/json;base64,${inlinedSourceMap}`);
7068
});
7169

7270
test('stripTypeScriptTypes source map when mode is transform and sourceUrl', () => {
73-
const source = `
74-
namespace MathUtil {
75-
export const add = (a: number, b: number) => a + b;
76-
}`;
77-
const result = stripTypeScriptTypes(source, { mode: 'transform', sourceMap: true, sourceUrl: 'test.ts' });
71+
const result = stripTypeScriptTypes(sourceToBeTransformed, {
72+
mode: 'transform',
73+
sourceMap: true,
74+
sourceUrl: 'test.ts'
75+
});
76+
const script = new vm.Script(result);
77+
const sourceMap =
78+
{
79+
version: 3,
80+
sources: ['test.ts'],
81+
names: [],
82+
mappings: sourceToBeTransformedMapping,
83+
};
84+
const inlinedSourceMap = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
85+
assert.strictEqual(script.sourceMapURL, `data:application/json;base64,${inlinedSourceMap}`);
86+
});
87+
88+
test('stripTypeScriptTypes source map when mode is transform and sourceUrl with non-latin-1 chars', () => {
89+
const sourceUrl = 'dir%20with $unusual"chars?\'åß∂ƒ©∆¬…`.cts';
90+
const result = stripTypeScriptTypes(sourceToBeTransformed, {
91+
mode: 'transform',
92+
sourceMap: true,
93+
sourceUrl,
94+
});
7895
const script = new vm.Script(result);
7996
const sourceMap =
8097
{
8198
version: 3,
82-
sources: [
83-
'test.ts',
84-
],
85-
sourcesContent: [
86-
'\n namespace MathUtil {\n export const add = (a: number, b: number) => a + b;\n }',
87-
],
99+
sources: [sourceUrl],
88100
names: [],
89-
mappings: ';UACY;aACK,MAAM,CAAC,GAAW,IAAc,IAAI;AACnD,GAFU,aAAA'
101+
mappings: sourceToBeTransformedMapping,
90102
};
91-
assert(script.sourceMapURL, `sourceMappingURL=data:application/json;base64,${JSON.stringify(sourceMap)}`);
103+
const inlinedSourceMap = Buffer.from(JSON.stringify(sourceMap)).toString('base64');
104+
assert.strictEqual(script.sourceMapURL, `data:application/json;base64,${inlinedSourceMap}`);
92105
});

0 commit comments

Comments
 (0)