Skip to content

Commit 13b13b8

Browse files
committed
test_runner: simplify mock.module normalization
- Unify internal export representation into a single `moduleExports` object, removing the split into `defaultExport`, `hasDefaultExport`, and `namedExports`. - DRY up `emitLegacyMockOptionWarning` with a lookup map instead of a switch statement with separate flag variables. - Use `ObjectDefineProperty` for `defaultExport` option to be consistent with descriptor copying in `namedExports`.
1 parent f956d87 commit 13b13b8

File tree

2 files changed

+36
-73
lines changed

2 files changed

+36
-73
lines changed

lib/internal/test_runner/mock/loader.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ function defaultExportSource(useESM, hasDefaultExport) {
113113
if (!hasDefaultExport) {
114114
return '';
115115
} else if (useESM) {
116-
return 'export default $__exports.defaultExport;';
116+
return 'export default $__exports.moduleExports.default;';
117117
}
118118

119-
return 'module.exports = $__exports.defaultExport;';
119+
return 'module.exports = $__exports.moduleExports.default;';
120120
}
121121

122122
function namedExportsSource(useESM, exportNames) {
@@ -134,9 +134,9 @@ if (module.exports === null || typeof module.exports !== 'object') {
134134
const name = exportNames[i];
135135

136136
if (useESM) {
137-
source += `export let ${name} = $__exports.namedExports[${JSONStringify(name)}];\n`;
137+
source += `export let ${name} = $__exports.moduleExports[${JSONStringify(name)}];\n`;
138138
} else {
139-
source += `module.exports[${JSONStringify(name)}] = $__exports.namedExports[${JSONStringify(name)}];\n`;
139+
source += `module.exports[${JSONStringify(name)}] = $__exports.moduleExports[${JSONStringify(name)}];\n`;
140140
}
141141
}
142142

lib/internal/test_runner/mock/mock.js

Lines changed: 32 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22
const {
3+
ArrayPrototypeFilter,
34
ArrayPrototypePush,
45
ArrayPrototypeSlice,
56
Error,
@@ -61,8 +62,12 @@ const kSupportedFormats = [
6162
'module',
6263
];
6364
let sharedModuleState;
64-
let warnedLegacyDefaultExport;
65-
let warnedLegacyNamedExports;
65+
const warnedLegacyOptions = { __proto__: null };
66+
const kLegacyOptionReplacements = {
67+
__proto__: null,
68+
defaultExport: 'options.exports.default',
69+
namedExports: 'options.exports',
70+
};
6671
const {
6772
hooks: mockHooks,
6873
mocks,
@@ -187,20 +192,16 @@ class MockModuleContext {
187192
baseURL,
188193
cache,
189194
caller,
190-
defaultExport,
191195
format,
192196
fullPath,
193-
hasDefaultExport,
194-
namedExports,
197+
moduleExports,
195198
sharedState,
196199
specifier,
197200
}) {
198201
const config = {
199202
__proto__: null,
200203
cache,
201-
defaultExport,
202-
hasDefaultExport,
203-
namedExports,
204+
moduleExports,
204205
caller,
205206
};
206207

@@ -232,8 +233,8 @@ class MockModuleContext {
232233
__proto__: null,
233234
url: baseURL,
234235
cache,
235-
exportNames: ObjectKeys(namedExports),
236-
hasDefaultExport,
236+
exportNames: ArrayPrototypeFilter(ObjectKeys(moduleExports), (k) => k !== 'default'),
237+
hasDefaultExport: 'default' in moduleExports,
237238
format,
238239
localVersion,
239240
active: true,
@@ -243,8 +244,7 @@ class MockModuleContext {
243244
delete Module._cache[fullPath];
244245
sharedState.mockExports.set(baseURL, {
245246
__proto__: null,
246-
defaultExport,
247-
namedExports,
247+
moduleExports,
248248
});
249249
}
250250

@@ -630,9 +630,7 @@ class MockTracker {
630630

631631
const {
632632
cache,
633-
defaultExport,
634-
hasDefaultExport,
635-
namedExports,
633+
moduleExports,
636634
} = normalizeModuleMockOptions(options);
637635

638636
const sharedState = setupSharedModuleState();
@@ -672,11 +670,9 @@ class MockTracker {
672670
baseURL: baseURL.href,
673671
cache,
674672
caller,
675-
defaultExport,
676673
format,
677674
fullPath,
678-
hasDefaultExport,
679-
namedExports,
675+
moduleExports,
680676
sharedState,
681677
specifier: mockSpecifier,
682678
});
@@ -819,9 +815,7 @@ function normalizeModuleMockOptions(options) {
819815
const { cache = false } = options;
820816
validateBoolean(cache, 'options.cache');
821817

822-
const moduleExports = {
823-
__proto__: null,
824-
};
818+
const moduleExports = { __proto__: null };
825819

826820
if ('exports' in options) {
827821
validateObject(options.exports, 'options.exports');
@@ -836,57 +830,27 @@ function normalizeModuleMockOptions(options) {
836830

837831
if ('defaultExport' in options) {
838832
emitLegacyMockOptionWarning('defaultExport');
839-
moduleExports.default = options.defaultExport;
840-
}
841-
842-
const namedExports = { __proto__: null };
843-
const exportNames = ObjectKeys(moduleExports);
844-
845-
for (let i = 0; i < exportNames.length; ++i) {
846-
const name = exportNames[i];
847-
848-
if (name === 'default') {
849-
continue;
850-
}
851-
852-
const descriptor = ObjectGetOwnPropertyDescriptor(moduleExports, name);
853-
ObjectDefineProperty(namedExports, name, descriptor);
833+
ObjectDefineProperty(moduleExports, 'default', {
834+
__proto__: null,
835+
...ObjectGetOwnPropertyDescriptor(options, 'defaultExport'),
836+
});
854837
}
855838

856839
return {
857840
__proto__: null,
858841
cache,
859-
defaultExport: moduleExports.default,
860-
hasDefaultExport: 'default' in moduleExports,
861-
namedExports,
842+
moduleExports,
862843
};
863844
}
864845

865846
function emitLegacyMockOptionWarning(option) {
866-
switch (option) {
867-
case 'defaultExport':
868-
if (warnedLegacyDefaultExport === true) {
869-
return;
870-
}
871-
warnedLegacyDefaultExport = true;
872-
process.emitWarning(
873-
'mock.module(): options.defaultExport is deprecated. ' +
874-
'Use options.exports.default instead.',
875-
'DeprecationWarning',
876-
);
877-
break;
878-
case 'namedExports':
879-
if (warnedLegacyNamedExports === true) {
880-
return;
881-
}
882-
warnedLegacyNamedExports = true;
883-
process.emitWarning(
884-
'mock.module(): options.namedExports is deprecated. ' +
885-
'Use options.exports instead.',
886-
'DeprecationWarning',
887-
);
888-
break;
889-
}
847+
if (warnedLegacyOptions[option]) return;
848+
warnedLegacyOptions[option] = true;
849+
process.emitWarning(
850+
`mock.module(): options.${option} is deprecated. ` +
851+
`Use ${kLegacyOptionReplacements[option]} instead.`,
852+
'DeprecationWarning',
853+
);
890854
}
891855

892856
function copyOwnProperties(from, to) {
@@ -938,9 +902,7 @@ function cjsMockModuleLoad(request, parent, isMain) {
938902
const {
939903
cache,
940904
caller,
941-
defaultExport,
942-
hasDefaultExport,
943-
namedExports,
905+
moduleExports,
944906
} = config;
945907

946908
if (cache && Module._cache[resolved]) {
@@ -949,9 +911,10 @@ function cjsMockModuleLoad(request, parent, isMain) {
949911
return Module._cache[resolved].exports;
950912
}
951913

914+
const hasDefaultExport = 'default' in moduleExports;
952915
// eslint-disable-next-line node-core/set-proto-to-null-in-object
953-
const modExports = hasDefaultExport ? defaultExport : {};
954-
const exportNames = ObjectKeys(namedExports);
916+
const modExports = hasDefaultExport ? moduleExports.default : {};
917+
const exportNames = ArrayPrototypeFilter(ObjectKeys(moduleExports), (k) => k !== 'default');
955918

956919
if ((typeof modExports !== 'object' || modExports === null) &&
957920
exportNames.length > 0) {
@@ -961,7 +924,7 @@ function cjsMockModuleLoad(request, parent, isMain) {
961924

962925
for (let i = 0; i < exportNames.length; ++i) {
963926
const name = exportNames[i];
964-
const descriptor = ObjectGetOwnPropertyDescriptor(namedExports, name);
927+
const descriptor = ObjectGetOwnPropertyDescriptor(moduleExports, name);
965928
ObjectDefineProperty(modExports, name, descriptor);
966929
}
967930

0 commit comments

Comments
 (0)