Skip to content

Commit d6e4f2d

Browse files
committed
test: compute list of expected globals from ESLint config file
We don't want to rely on mutable globals for core modules. Instead of maintaining a separate list of known globals in our test files, parse the ESLint config to ensure all globals are restricted in the `lib/` directory.
1 parent df0e665 commit d6e4f2d

File tree

2 files changed

+47
-67
lines changed

2 files changed

+47
-67
lines changed

test/common/index.js

+8-67
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ const bits = ['arm64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x', 'x64']
3737
.includes(process.arch) ? 64 : 32;
3838
const hasIntl = !!process.config.variables.v8_enable_i18n_support;
3939

40-
const {
41-
atob,
42-
btoa
43-
} = require('buffer');
40+
const parseEslintConfigForGlobals = require('./parseEslintConfigForGlobals');
4441

4542
// Some tests assume a umask of 0o022 so set that up front. Tests that need a
4643
// different umask will set it themselves.
@@ -257,67 +254,10 @@ function platformTimeout(ms) {
257254
return ms; // ARMv8+
258255
}
259256

260-
let knownGlobals = [
261-
atob,
262-
btoa,
263-
clearImmediate,
264-
clearInterval,
265-
clearTimeout,
266-
global,
267-
setImmediate,
268-
setInterval,
269-
setTimeout,
270-
queueMicrotask,
271-
];
272-
273-
// TODO(@jasnell): This check can be temporary. AbortController is
274-
// not currently supported in either Node.js 12 or 10, making it
275-
// difficult to run tests comparatively on those versions. Once
276-
// all supported versions have AbortController as a global, this
277-
// check can be removed and AbortController can be added to the
278-
// knownGlobals list above.
279-
if (global.AbortController)
280-
knownGlobals.push(global.AbortController);
281-
282-
if (global.gc) {
283-
knownGlobals.push(global.gc);
284-
}
285-
286-
if (global.performance) {
287-
knownGlobals.push(global.performance);
288-
}
289-
if (global.PerformanceMark) {
290-
knownGlobals.push(global.PerformanceMark);
291-
}
292-
if (global.PerformanceMeasure) {
293-
knownGlobals.push(global.PerformanceMeasure);
294-
}
295-
296-
// TODO(@ethan-arrowood): Similar to previous checks, this can be temporary
297-
// until v16.x is EOL. Once all supported versions have structuredClone we
298-
// can add this to the list above instead.
299-
if (global.structuredClone) {
300-
knownGlobals.push(global.structuredClone);
301-
}
302-
303-
if (global.fetch) {
304-
knownGlobals.push(
305-
global.fetch,
306-
global.FormData,
307-
global.Request,
308-
global.Response,
309-
global.Headers,
310-
);
311-
}
312-
if (hasCrypto && global.crypto) {
313-
knownGlobals.push(global.crypto);
314-
knownGlobals.push(global.Crypto);
315-
knownGlobals.push(global.CryptoKey);
316-
knownGlobals.push(global.SubtleCrypto);
317-
}
257+
const knownGlobals = parseEslintConfigForGlobals();
318258

319259
function allowGlobals(...allowlist) {
320-
knownGlobals = knownGlobals.concat(allowlist);
260+
knownGlobals.push(...allowlist);
321261
}
322262

323263
if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
@@ -329,9 +269,10 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
329269
function leakedGlobals() {
330270
const leaked = [];
331271

332-
for (const val in global) {
333-
if (!knownGlobals.includes(global[val])) {
334-
leaked.push(val);
272+
const globals = Object.getOwnPropertyDescriptors(global);
273+
for (const val in globals) {
274+
if (!knownGlobals.includes(global[val]) && globals[val].configurable) {
275+
leaked.push(val.toString());
335276
}
336277
}
337278

@@ -341,7 +282,7 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
341282
process.on('exit', function() {
342283
const leaked = leakedGlobals();
343284
if (leaked.length > 0) {
344-
assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}`);
285+
assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}. Add it to lib/.eslint.yaml or call common.allowGlobals().`);
345286
}
346287
});
347288
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const readline = require('readline');
6+
7+
const searchLines = [
8+
' no-restricted-globals:',
9+
' node-core/prefer-primordials:',
10+
];
11+
12+
const restrictedGlobalLine = /^\s{4}- name:\s?([^#\s]+)/;
13+
const closingLine = /^\s{0,3}[^#\s]/;
14+
15+
module.exports = function parseEslintConfigForGlobals() {
16+
const eslintConfig = readline.createInterface({
17+
input: fs.createReadStream(path.join(__dirname, '..', '..', 'lib', '.eslintrc.yaml')),
18+
crlfDelay: Infinity,
19+
});
20+
21+
const globals = [process];
22+
23+
let isReadingGlobals = false;
24+
eslintConfig.on('line', (line) => {
25+
if (isReadingGlobals) {
26+
const match = restrictedGlobalLine.exec(line);
27+
if (match && match[1] in global) {
28+
globals.push(global[match[1]]);
29+
} else if (closingLine.test(line)) {
30+
isReadingGlobals = false;
31+
}
32+
} else if (line === searchLines[0]) {
33+
searchLines.shift();
34+
isReadingGlobals = true;
35+
}
36+
});
37+
38+
return globals;
39+
};

0 commit comments

Comments
 (0)