Skip to content

Commit a12d520

Browse files
GeoffreyBoothaduh95
authored andcommitted
Move setup code into fixture; avoid global; add JSON.stringify, __proto__
1 parent 3e846c4 commit a12d520

File tree

3 files changed

+73
-72
lines changed

3 files changed

+73
-72
lines changed
Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,6 @@
1-
// Flags: --no-warnings
2-
import '../common/index.mjs';
3-
import * as fixtures from '../common/fixtures.mjs';
1+
// Flags: --no-warnings --import ./test/fixtures/es-module-loaders/mock.mjs
42
import assert from 'node:assert/strict';
5-
import { register } from 'node:module';
6-
import { MessageChannel } from 'node:worker_threads';
7-
8-
const { port1, port2 } = new MessageChannel();
9-
10-
register(fixtures.fileURL('es-module-loaders/mock-loader.mjs'), {
11-
parentURL: import.meta.url,
12-
data: { port: port2 },
13-
transferList: [port2],
14-
});
15-
16-
/**
17-
* This is the Map that saves *all* the mocked URL -> replacement Module
18-
* mappings
19-
* @type {Map<string, {namespace, listeners}>}
20-
*/
21-
globalThis.mockedModules = new Map();
22-
let mockVersion = 0;
23-
24-
/**
25-
* @param {string} resolved an absolute URL HREF string
26-
* @param {object} replacementProperties an object to pick properties from
27-
* to act as a module namespace
28-
* @returns {object} a mutator object that can update the module namespace
29-
* since we can't do something like old Object.observe
30-
*/
31-
function mock(resolved, replacementProperties) {
32-
const exportNames = Object.keys(replacementProperties);
33-
const namespace = { __proto__: null };
34-
/**
35-
* @type {Array<(name: string)=>void>} functions to call whenever an
36-
* export name is updated
37-
*/
38-
const listeners = [];
39-
for (const name of exportNames) {
40-
let currentValueForPropertyName = replacementProperties[name];
41-
Object.defineProperty(namespace, name, {
42-
enumerable: true,
43-
get() {
44-
return currentValueForPropertyName;
45-
},
46-
set(v) {
47-
currentValueForPropertyName = v;
48-
for (const fn of listeners) {
49-
try {
50-
fn(name);
51-
} catch {
52-
/* noop */
53-
}
54-
}
55-
}
56-
});
57-
}
58-
globalThis.mockedModules.set(encodeURIComponent(resolved), {
59-
namespace,
60-
listeners
61-
});
62-
mockVersion++;
63-
// Inform the loader that the `resolved` URL should now use the specific
64-
// `mockVersion` and has export names of `exportNames`
65-
//
66-
// This allows the loader to generate a fake module for that version
67-
// and names the next time it resolves a specifier to equal `resolved`
68-
port1.postMessage({ mockVersion, resolved, exports: exportNames });
69-
return namespace;
70-
}
3+
import { mock } from '../fixtures/es-module-loaders/mock.mjs';
714

725
mock('node:events', {
736
EventEmitter: 'This is mocked!'
@@ -107,5 +40,3 @@ assert.deepStrictEqual(mockedV2, Object.defineProperty({
10740
enumerable: false,
10841
value: 'Module'
10942
}));
110-
111-
delete globalThis.mockedModules;

test/fixtures/es-module-loaders/mock-loader.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export async function load(url, context, defaultLoad) {
7575
return defaultLoad(url, context);
7676
}
7777

78+
const mainImportURL = new URL('./mock.mjs', import.meta.url);
7879
/**
7980
* Generate the source code for a mocked module.
8081
* @param {string} encodedTargetURL the module being mocked
@@ -85,12 +86,13 @@ function generateModule(encodedTargetURL) {
8586
decodeURIComponent(encodedTargetURL)
8687
);
8788
let body = [
89+
`import { mockedModules } from '${mainImportURL}';`,
8890
'export {};',
8991
'let mapping = {__proto__: null};'
9092
];
9193
for (const [i, name] of Object.entries(exports)) {
9294
let key = JSON.stringify(name);
93-
body.push(`import.meta.mock = globalThis.mockedModules.get('${encodedTargetURL}');`);
95+
body.push(`import.meta.mock = mockedModules.get(${JSON.stringify(encodedTargetURL)});`);
9496
body.push(`var _${i} = import.meta.mock.namespace[${key}];`);
9597
body.push(`Object.defineProperty(mapping, ${key}, { enumerable: true, set(v) {_${i} = v;}, get() {return _${i};} });`);
9698
body.push(`export {_${i} as ${name}};`);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as fixtures from '../../common/fixtures.mjs';
2+
import { register } from 'node:module';
3+
import { MessageChannel } from 'node:worker_threads';
4+
5+
6+
const { port1, port2 } = new MessageChannel();
7+
8+
register(fixtures.fileURL('es-module-loaders/mock-loader.mjs'), {
9+
data: { port: port2 },
10+
transferList: [port2],
11+
});
12+
13+
/**
14+
* This is the Map that saves *all* the mocked URL -> replacement Module
15+
* mappings
16+
* @type {Map<string, {namespace, listeners}>}
17+
*/
18+
export const mockedModules = new Map();
19+
let mockVersion = 0;
20+
21+
/**
22+
* @param {string} resolved an absolute URL HREF string
23+
* @param {object} replacementProperties an object to pick properties from
24+
* to act as a module namespace
25+
* @returns {object} a mutator object that can update the module namespace
26+
* since we can't do something like old Object.observe
27+
*/
28+
export function mock(resolved, replacementProperties) {
29+
const exportNames = Object.keys(replacementProperties);
30+
const namespace = { __proto__: null };
31+
/**
32+
* @type {Array<(name: string)=>void>} functions to call whenever an
33+
* export name is updated
34+
*/
35+
const listeners = [];
36+
for (const name of exportNames) {
37+
let currentValueForPropertyName = replacementProperties[name];
38+
Object.defineProperty(namespace, name, {
39+
__proto__: null,
40+
enumerable: true,
41+
get() {
42+
return currentValueForPropertyName;
43+
},
44+
set(v) {
45+
currentValueForPropertyName = v;
46+
for (const fn of listeners) {
47+
try {
48+
fn(name);
49+
} catch {
50+
/* noop */
51+
}
52+
}
53+
}
54+
});
55+
}
56+
mockedModules.set(encodeURIComponent(resolved), {
57+
namespace,
58+
listeners
59+
});
60+
mockVersion++;
61+
// Inform the loader that the `resolved` URL should now use the specific
62+
// `mockVersion` and has export names of `exportNames`
63+
//
64+
// This allows the loader to generate a fake module for that version
65+
// and names the next time it resolves a specifier to equal `resolved`
66+
port1.postMessage({ mockVersion, resolved, exports: exportNames });
67+
return namespace;
68+
}

0 commit comments

Comments
 (0)