Skip to content

Commit 717cfa4

Browse files
legendecasaduh95
authored andcommitted
module: use buffer.toString base64
`btoa` only supports latin-1 charset and produces invalid source mapping urls. PR-URL: #56315 Refs: #56296 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent ab3e646 commit 717cfa4

File tree

3 files changed

+53
-29
lines changed

3 files changed

+53
-29
lines changed

lib/eslint.config_partial.mjs

+9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ 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+
// Forbids usages of `btoa` that are not caught by no-restricted-globals, like:
18+
// ```
19+
// const { btoa } = internalBinding('buffer');
20+
// btoa('...');
21+
// ```
22+
selector: "CallExpression[callee.property.name='btoa'], CallExpression[callee.name='btoa']",
23+
message: "`btoa` supports only latin-1 charset, use Buffer.from(str).toString('base64') instead",
24+
},
1625
{
1726
selector: 'NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError|NodeAggregateError)$/])',
1827
message: "Use an error exported by 'internal/errors' instead.",

lib/internal/modules/typescript.js

+5-3
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'.
@@ -134,9 +135,10 @@ function stripTypeScriptModuleTypes(source, filename) {
134135
* @returns {string} The code with the source map attached.
135136
*/
136137
function addSourceMap(code, sourceMap) {
137-
// TODO(@marco-ippolito) When Buffer.transcode supports utf8 to
138-
// base64 transformation, we should change this line.
139-
const base64SourceMap = internalBinding('buffer').btoa(sourceMap);
138+
// The base64 encoding should be https://datatracker.ietf.org/doc/html/rfc4648#section-4,
139+
// not base64url https://datatracker.ietf.org/doc/html/rfc4648#section-5. See data url
140+
// spec https://tools.ietf.org/html/rfc2397#section-2.
141+
const base64SourceMap = Buffer.from(sourceMap).toString('base64');
140142
return `${code}\n\n//# sourceMappingURL=data:application/json;base64,${base64SourceMap}`;
141143
}
142144

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

+39-26
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)