@wyw-in-js/transform 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/cache.js +47 -6
- package/esm/cache.js.map +1 -1
- package/esm/module.js +2 -2
- package/esm/module.js.map +1 -1
- package/esm/options/buildOptions.js +23 -2
- package/esm/options/buildOptions.js.map +1 -1
- package/esm/options/buildOptions.test.js +97 -0
- package/esm/options/buildOptions.test.js.map +1 -1
- package/esm/plugins/shaker.js +87 -9
- package/esm/plugins/shaker.js.map +1 -1
- package/esm/transform/Entrypoint.helpers.js +8 -1
- package/esm/transform/Entrypoint.helpers.js.map +1 -1
- package/esm/transform/Entrypoint.js +5 -3
- package/esm/transform/Entrypoint.js.map +1 -1
- package/esm/transform/EvaluatedEntrypoint.js.map +1 -1
- package/esm/transform/actions/BaseAction.js +1 -1
- package/esm/transform/actions/BaseAction.js.map +1 -1
- package/esm/transform/generators/processImports.js +53 -0
- package/esm/transform/generators/processImports.js.map +1 -1
- package/esm/transform/generators/resolveImports.js +2 -2
- package/esm/transform/generators/resolveImports.js.map +1 -1
- package/esm/utils/importOverrides.js +60 -0
- package/esm/utils/importOverrides.js.map +1 -1
- package/esm/vm/createVmContext.js +48 -16
- package/esm/vm/createVmContext.js.map +1 -1
- package/lib/cache.js +47 -7
- package/lib/cache.js.map +1 -1
- package/lib/module.js +2 -2
- package/lib/module.js.map +1 -1
- package/lib/options/buildOptions.js +23 -3
- package/lib/options/buildOptions.js.map +1 -1
- package/lib/options/buildOptions.test.js +97 -0
- package/lib/options/buildOptions.test.js.map +1 -1
- package/lib/plugins/shaker.js +88 -9
- package/lib/plugins/shaker.js.map +1 -1
- package/lib/transform/Entrypoint.helpers.js +8 -1
- package/lib/transform/Entrypoint.helpers.js.map +1 -1
- package/lib/transform/Entrypoint.js +5 -3
- package/lib/transform/Entrypoint.js.map +1 -1
- package/lib/transform/EvaluatedEntrypoint.js.map +1 -1
- package/lib/transform/actions/BaseAction.js +1 -1
- package/lib/transform/actions/BaseAction.js.map +1 -1
- package/lib/transform/generators/processImports.js +53 -0
- package/lib/transform/generators/processImports.js.map +1 -1
- package/lib/transform/generators/resolveImports.js +1 -1
- package/lib/transform/generators/resolveImports.js.map +1 -1
- package/lib/utils/importOverrides.js +62 -0
- package/lib/utils/importOverrides.js.map +1 -1
- package/lib/vm/createVmContext.js +48 -16
- package/lib/vm/createVmContext.js.map +1 -1
- package/package.json +5 -4
- package/types/cache.d.ts +2 -1
- package/types/cache.js +48 -6
- package/types/module.js +1 -1
- package/types/options/buildOptions.js +29 -2
- package/types/plugins/shaker.js +104 -9
- package/types/transform/Entrypoint.helpers.js +10 -1
- package/types/transform/Entrypoint.js +5 -3
- package/types/transform/EvaluatedEntrypoint.d.ts +2 -0
- package/types/transform/EvaluatedEntrypoint.js +1 -0
- package/types/transform/actions/BaseAction.js +1 -1
- package/types/transform/generators/processImports.js +70 -0
- package/types/transform/generators/resolveImports.js +1 -1
- package/types/utils/importOverrides.d.ts +2 -1
- package/types/utils/importOverrides.js +63 -0
- package/types/vm/createVmContext.js +61 -13
|
@@ -1,10 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.processImports = processImports;
|
|
4
|
+
const importOverrides_1 = require("../../utils/importOverrides");
|
|
5
|
+
const warnedSlowImportsByServices = new WeakMap();
|
|
6
|
+
function emitWarning(services, message) {
|
|
7
|
+
if (services.emitWarning) {
|
|
8
|
+
services.emitWarning(message);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.warn(message);
|
|
13
|
+
}
|
|
14
|
+
function getWarnedSlowImports(services) {
|
|
15
|
+
const cached = warnedSlowImportsByServices.get(services);
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
const created = new Set();
|
|
19
|
+
warnedSlowImportsByServices.set(services, created);
|
|
20
|
+
return created;
|
|
21
|
+
}
|
|
22
|
+
function isWarningEnabled(value) {
|
|
23
|
+
return Boolean(value) && value !== '0' && value !== 'false';
|
|
24
|
+
}
|
|
4
25
|
/**
|
|
5
26
|
* Creates new entrypoints and emits processEntrypoint for each resolved import
|
|
6
27
|
*/
|
|
7
28
|
function* processImports() {
|
|
29
|
+
const slowImportWarningsEnabled = isWarningEnabled(process.env.WYW_WARN_SLOW_IMPORTS);
|
|
30
|
+
const slowImportThresholdMs = (() => {
|
|
31
|
+
const raw = process.env.WYW_WARN_SLOW_IMPORTS_MS;
|
|
32
|
+
if (!raw)
|
|
33
|
+
return 50;
|
|
34
|
+
const parsed = Number(raw);
|
|
35
|
+
if (!Number.isFinite(parsed))
|
|
36
|
+
return 50;
|
|
37
|
+
return parsed;
|
|
38
|
+
})();
|
|
39
|
+
const warnedSlowImports = slowImportWarningsEnabled
|
|
40
|
+
? getWarnedSlowImports(this.services)
|
|
41
|
+
: null;
|
|
42
|
+
const { root } = this.services.options;
|
|
8
43
|
for (const dependency of this.data.resolved) {
|
|
9
44
|
const { resolved, only } = dependency;
|
|
10
45
|
if (!resolved) {
|
|
@@ -15,6 +50,41 @@ function* processImports() {
|
|
|
15
50
|
if (nextEntrypoint === 'loop' || nextEntrypoint.ignored) {
|
|
16
51
|
continue;
|
|
17
52
|
}
|
|
53
|
+
const startedAt = slowImportWarningsEnabled ? performance.now() : 0;
|
|
18
54
|
yield* this.getNext('processEntrypoint', nextEntrypoint, undefined, null);
|
|
55
|
+
if (slowImportWarningsEnabled &&
|
|
56
|
+
warnedSlowImports &&
|
|
57
|
+
slowImportThresholdMs > 0) {
|
|
58
|
+
const durationMs = performance.now() - startedAt;
|
|
59
|
+
if (durationMs >= slowImportThresholdMs) {
|
|
60
|
+
const { key: importKey } = (0, importOverrides_1.toImportKey)({
|
|
61
|
+
source: dependency.source,
|
|
62
|
+
resolved,
|
|
63
|
+
root,
|
|
64
|
+
});
|
|
65
|
+
const dedupeKey = `${this.entrypoint.name}::${importKey}`;
|
|
66
|
+
if (!warnedSlowImports.has(dedupeKey)) {
|
|
67
|
+
warnedSlowImports.add(dedupeKey);
|
|
68
|
+
const warning = [
|
|
69
|
+
`[wyw-in-js] Slow import during prepare stage`,
|
|
70
|
+
``,
|
|
71
|
+
`file: ${this.entrypoint.name}`,
|
|
72
|
+
`import: ${dependency.source}`,
|
|
73
|
+
`resolved: ${resolved}`,
|
|
74
|
+
`duration: ${durationMs.toFixed(1)}ms`,
|
|
75
|
+
``,
|
|
76
|
+
`tip: if this import is runtime-only or heavy, mock it during evaluation via importOverrides:`,
|
|
77
|
+
` importOverrides: {`,
|
|
78
|
+
` '${importKey}': { mock: './path/to/mock' },`,
|
|
79
|
+
` }`,
|
|
80
|
+
``,
|
|
81
|
+
`note: importOverrides affects only build-time evaluation (it does not change your bundler runtime behavior)`,
|
|
82
|
+
``,
|
|
83
|
+
`note: configure threshold with WYW_WARN_SLOW_IMPORTS_MS (current: ${slowImportThresholdMs}ms)`,
|
|
84
|
+
].join('\n');
|
|
85
|
+
emitWarning(this.services, warning);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
19
89
|
}
|
|
20
90
|
}
|
|
@@ -20,7 +20,7 @@ function applyImportOverrides(services, entrypoint, resolvedImports) {
|
|
|
20
20
|
resolved: dependency.resolved,
|
|
21
21
|
root,
|
|
22
22
|
});
|
|
23
|
-
const override = overrides
|
|
23
|
+
const override = (0, importOverrides_1.getImportOverride)(overrides, key);
|
|
24
24
|
if (!override) {
|
|
25
25
|
return dependency;
|
|
26
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ImportOverride } from '@wyw-in-js/shared';
|
|
1
|
+
import { type ImportOverride, type ImportOverrides } from '@wyw-in-js/shared';
|
|
2
2
|
export type ImportKeyKind = 'file' | 'package';
|
|
3
3
|
export type ImportKey = {
|
|
4
4
|
key: string;
|
|
@@ -17,3 +17,4 @@ export declare function resolveMockSpecifier({ importer, mock, root, stack, }: {
|
|
|
17
17
|
stack: string[];
|
|
18
18
|
}): string;
|
|
19
19
|
export declare function applyImportOverrideToOnly(only: string[], override: ImportOverride | undefined): string[];
|
|
20
|
+
export declare function getImportOverride(importOverrides: ImportOverrides | undefined, key: string): ImportOverride | undefined;
|
|
@@ -7,8 +7,10 @@ exports.toCanonicalFileKey = toCanonicalFileKey;
|
|
|
7
7
|
exports.toImportKey = toImportKey;
|
|
8
8
|
exports.resolveMockSpecifier = resolveMockSpecifier;
|
|
9
9
|
exports.applyImportOverrideToOnly = applyImportOverrideToOnly;
|
|
10
|
+
exports.getImportOverride = getImportOverride;
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
12
|
const shared_1 = require("@wyw-in-js/shared");
|
|
13
|
+
const minimatch_1 = require("minimatch");
|
|
12
14
|
function toCanonicalFileKey(resolved, root) {
|
|
13
15
|
const rootDir = root ? path_1.default.resolve(root) : process.cwd();
|
|
14
16
|
const normalizedResolved = path_1.default.resolve(resolved);
|
|
@@ -38,3 +40,64 @@ function applyImportOverrideToOnly(only, override) {
|
|
|
38
40
|
}
|
|
39
41
|
return only;
|
|
40
42
|
}
|
|
43
|
+
const compiledImportOverridesCache = new WeakMap();
|
|
44
|
+
const minimatchOptions = {
|
|
45
|
+
dot: true,
|
|
46
|
+
nocomment: true,
|
|
47
|
+
nonegate: true,
|
|
48
|
+
};
|
|
49
|
+
function getPatternSpecificity(pattern) {
|
|
50
|
+
let wildcardCount = 0;
|
|
51
|
+
let escaped = false;
|
|
52
|
+
for (const char of pattern) {
|
|
53
|
+
if (escaped) {
|
|
54
|
+
escaped = false;
|
|
55
|
+
}
|
|
56
|
+
else if (char === '\\') {
|
|
57
|
+
escaped = true;
|
|
58
|
+
}
|
|
59
|
+
else if (char === '*' || char === '?') {
|
|
60
|
+
wildcardCount += 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return pattern.length - wildcardCount * 10;
|
|
64
|
+
}
|
|
65
|
+
function compileImportOverrides(importOverrides) {
|
|
66
|
+
const matchers = Object.entries(importOverrides)
|
|
67
|
+
.map(([pattern, override]) => {
|
|
68
|
+
return {
|
|
69
|
+
matcher: new minimatch_1.Minimatch(pattern, minimatchOptions),
|
|
70
|
+
override,
|
|
71
|
+
pattern,
|
|
72
|
+
specificity: getPatternSpecificity(pattern),
|
|
73
|
+
};
|
|
74
|
+
})
|
|
75
|
+
.sort((a, b) => {
|
|
76
|
+
const bySpecificity = b.specificity - a.specificity;
|
|
77
|
+
if (bySpecificity !== 0)
|
|
78
|
+
return bySpecificity;
|
|
79
|
+
const byLength = b.pattern.length - a.pattern.length;
|
|
80
|
+
if (byLength !== 0)
|
|
81
|
+
return byLength;
|
|
82
|
+
return a.pattern.localeCompare(b.pattern);
|
|
83
|
+
});
|
|
84
|
+
return { matchers };
|
|
85
|
+
}
|
|
86
|
+
function getCompiledImportOverrides(importOverrides) {
|
|
87
|
+
const cached = compiledImportOverridesCache.get(importOverrides);
|
|
88
|
+
if (cached)
|
|
89
|
+
return cached;
|
|
90
|
+
const compiled = compileImportOverrides(importOverrides);
|
|
91
|
+
compiledImportOverridesCache.set(importOverrides, compiled);
|
|
92
|
+
return compiled;
|
|
93
|
+
}
|
|
94
|
+
function getImportOverride(importOverrides, key) {
|
|
95
|
+
if (!importOverrides) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const direct = importOverrides[key];
|
|
99
|
+
if (direct)
|
|
100
|
+
return direct;
|
|
101
|
+
const { matchers } = getCompiledImportOverrides(importOverrides);
|
|
102
|
+
return matchers.find(({ matcher }) => matcher.match(key))?.override;
|
|
103
|
+
}
|
|
@@ -39,7 +39,16 @@ const shared_1 = require("@wyw-in-js/shared");
|
|
|
39
39
|
const process = __importStar(require("./process"));
|
|
40
40
|
const NOOP = () => { };
|
|
41
41
|
const IMPORT_META_ENV = '__wyw_import_meta_env';
|
|
42
|
+
const HAPPY_DOM_REQUIRE_HOOK = '__wyw_requireHappyDom';
|
|
42
43
|
let importMetaEnvWarned = false;
|
|
44
|
+
let happyDomRequireEsmWarned = false;
|
|
45
|
+
let happyDomUnavailable = false;
|
|
46
|
+
function isErrRequireEsm(error) {
|
|
47
|
+
return (typeof error === 'object' &&
|
|
48
|
+
error !== null &&
|
|
49
|
+
'code' in error &&
|
|
50
|
+
error.code === 'ERR_REQUIRE_ESM');
|
|
51
|
+
}
|
|
43
52
|
function createImportMetaEnvProxy() {
|
|
44
53
|
const target = Object.create(null);
|
|
45
54
|
const warnOnce = () => {
|
|
@@ -84,14 +93,51 @@ function createImportMetaEnvProxy() {
|
|
|
84
93
|
},
|
|
85
94
|
});
|
|
86
95
|
}
|
|
96
|
+
function requireHappyDom() {
|
|
97
|
+
const hook = globalThis[HAPPY_DOM_REQUIRE_HOOK];
|
|
98
|
+
if (typeof hook === 'function') {
|
|
99
|
+
return hook();
|
|
100
|
+
}
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
102
|
+
return require('happy-dom');
|
|
103
|
+
}
|
|
87
104
|
function createWindow() {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
if (happyDomUnavailable)
|
|
106
|
+
return undefined;
|
|
107
|
+
try {
|
|
108
|
+
const { Window, GlobalWindow } = requireHappyDom();
|
|
109
|
+
const HappyWindow = GlobalWindow || Window;
|
|
110
|
+
const win = new HappyWindow();
|
|
111
|
+
// TODO: browser doesn't expose Buffer, but a lot of dependencies use it
|
|
112
|
+
win.Buffer = Buffer;
|
|
113
|
+
win.Uint8Array = Uint8Array;
|
|
114
|
+
return win;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (!isErrRequireEsm(error)) {
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
const hasCustomRequireHook = typeof globalThis[HAPPY_DOM_REQUIRE_HOOK] ===
|
|
121
|
+
'function';
|
|
122
|
+
if (!hasCustomRequireHook) {
|
|
123
|
+
happyDomUnavailable = true;
|
|
124
|
+
}
|
|
125
|
+
if (happyDomRequireEsmWarned)
|
|
126
|
+
return undefined;
|
|
127
|
+
happyDomRequireEsmWarned = true;
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.warn([
|
|
130
|
+
`[wyw-in-js] DOM emulation is enabled (features.happyDOM), but "happy-dom" could not be loaded in this build-time runtime.`,
|
|
131
|
+
`This usually happens because "happy-dom" is ESM-only and cannot be loaded via require() in a CJS build.`,
|
|
132
|
+
``,
|
|
133
|
+
`WyW will continue without DOM emulation (as if features.happyDOM:false).`,
|
|
134
|
+
``,
|
|
135
|
+
`To silence this warning: set features: { happyDOM: false }.`,
|
|
136
|
+
`To get real DOM emulation in Node 20+, WyW needs an ESM-only eval architecture (planned for v2.0.0),`,
|
|
137
|
+
`or a runtime that supports require(ESM) (Node 24+).`,
|
|
138
|
+
].join('\n'));
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
95
141
|
}
|
|
96
142
|
/**
|
|
97
143
|
* `happy-dom` already has required references, so we don't need to set them.
|
|
@@ -125,8 +171,16 @@ function createBaseContext(win, additionalContext) {
|
|
|
125
171
|
}
|
|
126
172
|
return baseContext;
|
|
127
173
|
}
|
|
174
|
+
function createNothing() {
|
|
175
|
+
return {
|
|
176
|
+
teardown: () => { },
|
|
177
|
+
window: undefined,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
128
180
|
function createHappyDOMWindow() {
|
|
129
181
|
const win = createWindow();
|
|
182
|
+
if (!win)
|
|
183
|
+
return createNothing();
|
|
130
184
|
return {
|
|
131
185
|
teardown: () => {
|
|
132
186
|
win.happyDOM.abort();
|
|
@@ -134,12 +188,6 @@ function createHappyDOMWindow() {
|
|
|
134
188
|
window: win,
|
|
135
189
|
};
|
|
136
190
|
}
|
|
137
|
-
function createNothing() {
|
|
138
|
-
return {
|
|
139
|
-
teardown: () => { },
|
|
140
|
-
window: undefined,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
191
|
function createVmContext(filename, features, additionalContext, overrideContext = (i) => i) {
|
|
144
192
|
const isHappyDOMEnabled = (0, shared_1.isFeatureEnabled)(features, 'happyDOM', filename);
|
|
145
193
|
const { teardown, window } = isHappyDOMEnabled
|