@jsenv/core 41.4.17 → 41.4.19
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/dist/build/build.js +693 -509
- package/dist/build/jsenv_core_packages.js +3 -187
- package/dist/client/directory_listing/js/directory_listing.js +23 -2
- package/dist/start_dev_server/start_dev_server.js +813 -625
- package/package.json +8 -8
- package/src/build/build.js +66 -15
- package/src/build/build_content_report.js +24 -15
- package/src/build/build_specifier_manager.js +2 -2
- package/src/dev/dev_server_plugins/dev_server_plugin_omega_error_handler.js +4 -1
- package/src/dev/dev_server_plugins/dev_server_plugin_serve_source_files.js +116 -68
- package/src/dev/start_dev_server.js +24 -4
- package/src/helpers/watch_source_files.js +25 -20
- package/src/kitchen/kitchen.js +6 -0
- package/src/kitchen/prepend_content.js +4 -4
- package/src/kitchen/url_graph/references.js +30 -13
- package/src/kitchen/url_graph/url_graph.js +0 -1
- package/src/kitchen/url_graph/url_info_transformations.js +16 -6
- package/src/plugins/import_meta_css/jsenv_plugin_import_meta_css.js +10 -40
- package/src/plugins/import_meta_hot/import_meta_hot_analysis.js +70 -0
- package/src/plugins/import_meta_hot/jsenv_plugin_import_meta_hot.js +35 -20
- package/src/plugins/import_meta_scenarios/jsenv_plugin_import_meta_scenarios.js +22 -56
- package/src/plugins/modulepreload/jsenv_plugin_modulepreload.js +128 -0
- package/src/plugins/plugins.js +3 -0
- package/src/plugins/protocol_file/client/directory_listing.jsx +27 -1
- package/src/plugins/protocol_file/jsenv_plugin_directory_listing.js +20 -0
- package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +32 -15
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +11 -2
- package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +1 -2
- package/src/plugins/resolution_node_esm/node_esm_resolver.js +0 -5
- package/src/plugins/import_meta_hot/babel_plugin_metadata_import_meta_hot.js +0 -97
|
@@ -2,24 +2,27 @@ import {
|
|
|
2
2
|
lookupPackageDirectory,
|
|
3
3
|
registerDirectoryLifecycle,
|
|
4
4
|
} from "@jsenv/filesystem";
|
|
5
|
-
import { urlToRelativeUrl } from "@jsenv/urls";
|
|
5
|
+
import { urlIsOrIsInsideOf, urlToRelativeUrl } from "@jsenv/urls";
|
|
6
6
|
import { readFileSync } from "node:fs";
|
|
7
7
|
|
|
8
8
|
export const getDirectoryWatchPatterns = (
|
|
9
9
|
directoryUrl,
|
|
10
10
|
watchedDirectoryUrl,
|
|
11
|
-
{ sourceFilesConfig },
|
|
11
|
+
{ sourceFilesConfig, defaultPatterns = true },
|
|
12
12
|
) => {
|
|
13
13
|
const directoryUrlRelativeToWatchedDirectory = urlToRelativeUrl(
|
|
14
14
|
directoryUrl,
|
|
15
15
|
watchedDirectoryUrl,
|
|
16
16
|
);
|
|
17
|
-
const watchPatterns = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const watchPatterns = {};
|
|
18
|
+
if (defaultPatterns) {
|
|
19
|
+
Object.assign(watchPatterns, {
|
|
20
|
+
[`${directoryUrlRelativeToWatchedDirectory}**/*`]: true, // by default watch everything inside the source directory
|
|
21
|
+
[`${directoryUrlRelativeToWatchedDirectory}**/.*`]: false, // file starting with a dot -> do not watch
|
|
22
|
+
[`${directoryUrlRelativeToWatchedDirectory}**/.*/`]: false, // directory starting with a dot -> do not watch
|
|
23
|
+
[`${directoryUrlRelativeToWatchedDirectory}**/node_modules/`]: false, // node_modules directory -> do not watch
|
|
24
|
+
});
|
|
25
|
+
}
|
|
23
26
|
for (const key of Object.keys(sourceFilesConfig)) {
|
|
24
27
|
watchPatterns[`${directoryUrlRelativeToWatchedDirectory}${key}`] =
|
|
25
28
|
sourceFilesConfig[key];
|
|
@@ -40,11 +43,12 @@ export const watchSourceFiles = (
|
|
|
40
43
|
// And jsenv should not consider these as source files and watch them (to not hurt performances)
|
|
41
44
|
const watchPatterns = {};
|
|
42
45
|
let watchedDirectoryUrl = "";
|
|
43
|
-
const addDirectoryToWatch = (directoryUrl) => {
|
|
46
|
+
const addDirectoryToWatch = (directoryUrl, { defaultPatterns } = {}) => {
|
|
44
47
|
Object.assign(
|
|
45
48
|
watchPatterns,
|
|
46
49
|
getDirectoryWatchPatterns(directoryUrl, watchedDirectoryUrl, {
|
|
47
50
|
sourceFilesConfig,
|
|
51
|
+
defaultPatterns,
|
|
48
52
|
}),
|
|
49
53
|
);
|
|
50
54
|
};
|
|
@@ -76,7 +80,6 @@ export const watchSourceFiles = (
|
|
|
76
80
|
},
|
|
77
81
|
},
|
|
78
82
|
);
|
|
79
|
-
stopWatchingSourceFiles.watchPatterns = watchPatterns;
|
|
80
83
|
return stopWatchingSourceFiles;
|
|
81
84
|
};
|
|
82
85
|
|
|
@@ -96,16 +99,18 @@ export const watchSourceFiles = (
|
|
|
96
99
|
}
|
|
97
100
|
watchedDirectoryUrl = packageDirectoryUrl;
|
|
98
101
|
for (const workspace of workspaces) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
const workspaceDirectoryUrl = workspace.endsWith("*")
|
|
103
|
+
? new URL(workspace.slice(0, -1), packageDirectoryUrl)
|
|
104
|
+
: new URL(workspace, packageDirectoryUrl);
|
|
105
|
+
addDirectoryToWatch(workspaceDirectoryUrl, {
|
|
106
|
+
// The source directory patterns already cover a workspace inside it.
|
|
107
|
+
// Every pattern is tested against every file found while watching, so
|
|
108
|
+
// the same patterns rooted at each workspace only multiply that work.
|
|
109
|
+
defaultPatterns: !urlIsOrIsInsideOf(
|
|
110
|
+
workspaceDirectoryUrl,
|
|
111
|
+
sourceDirectoryUrl,
|
|
112
|
+
),
|
|
113
|
+
});
|
|
109
114
|
}
|
|
110
115
|
// we are updating the root directory
|
|
111
116
|
// we must make the patterns relative to source directory relative to the new root directory
|
package/src/kitchen/kitchen.js
CHANGED
|
@@ -118,6 +118,12 @@ export const createKitchen = ({
|
|
|
118
118
|
INJECTIONS,
|
|
119
119
|
getPluginMeta: null,
|
|
120
120
|
sourcemaps,
|
|
121
|
+
// a plugin producing a sourcemap can skip the work when it would be
|
|
122
|
+
// thrown away (see shouldHandleSourcemap in url_info_transformations.js)
|
|
123
|
+
sourcemapsEnabled:
|
|
124
|
+
sourcemaps === "inline" ||
|
|
125
|
+
sourcemaps === "file" ||
|
|
126
|
+
sourcemaps === "programmatic",
|
|
121
127
|
outDirectoryUrl,
|
|
122
128
|
},
|
|
123
129
|
resolve: (specifier, importer = rootDirectoryUrl) => {
|
|
@@ -67,10 +67,9 @@ const prependJsClassicInJsClassic = (jsUrlInfo, urlInfoToPrepend) => {
|
|
|
67
67
|
const magicSource = createMagicSource(jsUrlInfo.content);
|
|
68
68
|
magicSource.prepend(`${urlInfoToPrepend.content}\n\n`);
|
|
69
69
|
const magicResult = magicSource.toContentAndSourcemap();
|
|
70
|
-
const sourcemap =
|
|
71
|
-
jsUrlInfo.sourcemap,
|
|
72
|
-
|
|
73
|
-
);
|
|
70
|
+
const sourcemap = jsUrlInfo.context.sourcemapsEnabled
|
|
71
|
+
? composeTwoSourcemaps(jsUrlInfo.sourcemap, magicResult.sourcemap)
|
|
72
|
+
: null;
|
|
74
73
|
jsUrlInfo.mutateContent({
|
|
75
74
|
content: magicResult.content,
|
|
76
75
|
sourcemap,
|
|
@@ -88,6 +87,7 @@ const prependJsClassicInJsModule = async (jsUrlInfo, urlInfoToPrepend) => {
|
|
|
88
87
|
input: jsUrlInfo.content,
|
|
89
88
|
inputIsJsModule: true,
|
|
90
89
|
inputUrl: jsUrlInfo.originalUrl,
|
|
90
|
+
options: { sourceMaps: jsUrlInfo.context.sourcemapsEnabled },
|
|
91
91
|
});
|
|
92
92
|
jsUrlInfo.mutateContent({
|
|
93
93
|
content: code,
|
|
@@ -634,20 +634,37 @@ const checkForDependencyRemovalEffects = (reference) => {
|
|
|
634
634
|
};
|
|
635
635
|
|
|
636
636
|
const traceFromUrlSite = (urlSite) => {
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
: ""
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
637
|
+
const { url, line, column, content } = urlSite;
|
|
638
|
+
const trace = { url, line, column };
|
|
639
|
+
// A file references many urls and an error on one of them is rare: the code
|
|
640
|
+
// frame (and the message embedding it) is built the first time it is read,
|
|
641
|
+
// not for every reference found.
|
|
642
|
+
defineLazyProperty(trace, "codeFrame", () =>
|
|
643
|
+
content ? generateContentFrame({ content, line, column }) : "",
|
|
644
|
+
);
|
|
645
|
+
defineLazyProperty(trace, "message", () => stringifyUrlSite(urlSite));
|
|
646
|
+
return trace;
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
const defineLazyProperty = (object, property, compute) => {
|
|
650
|
+
const setValue = (value) => {
|
|
651
|
+
Object.defineProperty(object, property, {
|
|
652
|
+
enumerable: true,
|
|
653
|
+
configurable: true,
|
|
654
|
+
writable: true,
|
|
655
|
+
value,
|
|
656
|
+
});
|
|
650
657
|
};
|
|
658
|
+
Object.defineProperty(object, property, {
|
|
659
|
+
enumerable: true,
|
|
660
|
+
configurable: true,
|
|
661
|
+
get: () => {
|
|
662
|
+
const value = compute();
|
|
663
|
+
setValue(value);
|
|
664
|
+
return value;
|
|
665
|
+
},
|
|
666
|
+
set: setValue,
|
|
667
|
+
});
|
|
651
668
|
};
|
|
652
669
|
|
|
653
670
|
const adjustUrlSite = (urlInfo, { url, line, column }) => {
|
|
@@ -185,7 +185,6 @@ const createUrlInfo = (url, context) => {
|
|
|
185
185
|
dereferencedTimestamp: 0,
|
|
186
186
|
originalContentEtag: null,
|
|
187
187
|
contentEtag: null,
|
|
188
|
-
isWatched: false,
|
|
189
188
|
isValid: () => false,
|
|
190
189
|
data: {}, // plugins can put whatever they want here
|
|
191
190
|
referenceToOthersSet: new Set(),
|
|
@@ -199,7 +199,6 @@ export const createUrlInfoTransformer = ({
|
|
|
199
199
|
contentAst, // undefined most of the time
|
|
200
200
|
contentEtag, // in practice always undefined
|
|
201
201
|
contentLength,
|
|
202
|
-
sourcemap,
|
|
203
202
|
sourcemapIsWrong,
|
|
204
203
|
contentInjections,
|
|
205
204
|
} = transformations;
|
|
@@ -226,11 +225,14 @@ export const createUrlInfoTransformer = ({
|
|
|
226
225
|
contentLength,
|
|
227
226
|
});
|
|
228
227
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
228
|
+
// "sourcemap" is read last, and only when it will be used: a plugin can
|
|
229
|
+
// hand it back as a getter that generates the map on first read, so that
|
|
230
|
+
// nothing is generated for a kitchen that throws sourcemaps away
|
|
231
|
+
const sourcemap =
|
|
232
|
+
mayHaveSourcemap(urlInfo) && shouldHandleSourcemap(urlInfo)
|
|
233
|
+
? transformations.sourcemap
|
|
234
|
+
: null;
|
|
235
|
+
if (sourcemap) {
|
|
234
236
|
const sourcemapNormalized = normalizeSourcemap(urlInfo, sourcemap);
|
|
235
237
|
let currentSourcemap = urlInfo.sourcemap;
|
|
236
238
|
const finalSourcemap = composeTwoSourcemaps(
|
|
@@ -263,6 +265,14 @@ export const createUrlInfoTransformer = ({
|
|
|
263
265
|
writeInsideOutDirectory(urlInfo);
|
|
264
266
|
};
|
|
265
267
|
|
|
268
|
+
// Written synchronously on purpose, and measured: async is the tempting
|
|
269
|
+
// choice, but here it loses. A cold load cooks hundreds of files; their
|
|
270
|
+
// synchronous writes block the event loop ~160ms in total, while
|
|
271
|
+
// asynchronous writes queue in the threadpool behind tens of MB of content
|
|
272
|
+
// and sourcemaps, and a response that waits for its own write then waits
|
|
273
|
+
// ~36ms on average (18s summed over 500 responses). Not waiting is not an
|
|
274
|
+
// option either: the last write lands after the test that cooked it has
|
|
275
|
+
// ended, and the side-effect snapshots lose it.
|
|
266
276
|
const writeInsideOutDirectory = (urlInfo) => {
|
|
267
277
|
// writing result inside ".jsenv" directory (debug purposes)
|
|
268
278
|
if (!outDirectoryUrl) {
|
|
@@ -23,7 +23,11 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
applyBabelPlugins,
|
|
28
|
+
getImportMetaPropertyName,
|
|
29
|
+
visitJsAstUntil,
|
|
30
|
+
} from "@jsenv/ast";
|
|
27
31
|
|
|
28
32
|
export const jsenvPluginImportMetaCss = () => {
|
|
29
33
|
const importMetaCssDevClientFileUrl = import.meta
|
|
@@ -47,15 +51,10 @@ export const jsenvPluginImportMetaCss = () => {
|
|
|
47
51
|
if (!urlInfo.content.includes("import.meta.css")) {
|
|
48
52
|
return null;
|
|
49
53
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
input: urlInfo.content,
|
|
53
|
-
inputIsJsModule: true,
|
|
54
|
-
inputUrl: urlInfo.originalUrl,
|
|
55
|
-
outputUrl: urlInfo.generatedUrl,
|
|
54
|
+
const importMetaCssNode = visitJsAstUntil(urlInfo.contentAst, {
|
|
55
|
+
MemberExpression: (node) => getImportMetaPropertyName(node) === "css",
|
|
56
56
|
});
|
|
57
|
-
|
|
58
|
-
if (!usesImportMetaCss) {
|
|
57
|
+
if (!importMetaCssNode) {
|
|
59
58
|
return null;
|
|
60
59
|
}
|
|
61
60
|
if (urlInfo.context.build) {
|
|
@@ -76,6 +75,8 @@ export const jsenvPluginImportMetaCss = () => {
|
|
|
76
75
|
inputIsJsModule: true,
|
|
77
76
|
inputUrl: urlInfo.originalUrl,
|
|
78
77
|
outputUrl: urlInfo.generatedUrl,
|
|
78
|
+
// the map would be dropped: the content sent back carries none
|
|
79
|
+
options: { sourceMaps: false },
|
|
79
80
|
});
|
|
80
81
|
if (code === urlInfo.content) {
|
|
81
82
|
// all assignments were already in array form (pre-built file) — nothing to do
|
|
@@ -135,37 +136,6 @@ const babelPluginRewriteImportMetaCssAssignment = (
|
|
|
135
136
|
};
|
|
136
137
|
};
|
|
137
138
|
|
|
138
|
-
const babelPluginMetadataUsesImportMetaCss = () => {
|
|
139
|
-
return {
|
|
140
|
-
name: "metadata-uses-import-meta-css",
|
|
141
|
-
visitor: {
|
|
142
|
-
Program(programPath, state) {
|
|
143
|
-
let usesImportMetaCss = false;
|
|
144
|
-
programPath.traverse({
|
|
145
|
-
MemberExpression(path) {
|
|
146
|
-
const { node } = path;
|
|
147
|
-
const { object } = node;
|
|
148
|
-
if (object.type !== "MetaProperty") {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
const { property: objectProperty } = object;
|
|
152
|
-
if (objectProperty.name !== "meta") {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const { property } = node;
|
|
156
|
-
const { name } = property;
|
|
157
|
-
if (name === "css") {
|
|
158
|
-
usesImportMetaCss = true;
|
|
159
|
-
path.stop();
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
state.file.metadata.usesImportMetaCss = usesImportMetaCss;
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
};
|
|
168
|
-
|
|
169
139
|
const injectImportMetaCss = (
|
|
170
140
|
urlInfo,
|
|
171
141
|
{ content, importFrom, importName, importAs, hot },
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getImportMetaPropertyName, visitJsAst } from "@jsenv/ast";
|
|
2
|
+
|
|
3
|
+
export const analyzeImportMetaHot = (ast) => {
|
|
4
|
+
const importMetaHotNodes = [];
|
|
5
|
+
let hotDecline = false;
|
|
6
|
+
let hotAcceptSelf = false;
|
|
7
|
+
let hotAcceptSpecifiers = [];
|
|
8
|
+
visitJsAst(ast, {
|
|
9
|
+
MemberExpression: (node) => {
|
|
10
|
+
if (getImportMetaPropertyName(node) === "hot") {
|
|
11
|
+
importMetaHotNodes.push(node);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
CallExpression: (node) => {
|
|
15
|
+
const methodName = getImportMetaHotMethodName(node);
|
|
16
|
+
if (methodName === "accept") {
|
|
17
|
+
const args = node.arguments;
|
|
18
|
+
if (args.length === 0) {
|
|
19
|
+
hotAcceptSelf = true;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const [firstArg] = args;
|
|
23
|
+
if (isStringLiteral(firstArg)) {
|
|
24
|
+
hotAcceptSpecifiers = [firstArg.value];
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (firstArg.type === "ArrayExpression") {
|
|
28
|
+
hotAcceptSpecifiers = firstArg.elements.map((element) => {
|
|
29
|
+
if (!isStringLiteral(element)) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`all array elements must be strings in "import.meta.hot.accept(array)"`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return element.value;
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// accept first arg can be "anything" such as
|
|
39
|
+
// `const cb = () => {}; import.meta.hot.accept(cb)`
|
|
40
|
+
hotAcceptSelf = true;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (methodName === "decline") {
|
|
44
|
+
hotDecline = true;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
importMetaHotNodes,
|
|
50
|
+
hotDecline,
|
|
51
|
+
hotAcceptSelf,
|
|
52
|
+
hotAcceptSpecifiers,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// "import.meta.hot.<method>(...)"
|
|
57
|
+
const getImportMetaHotMethodName = (callNode) => {
|
|
58
|
+
const { callee } = callNode;
|
|
59
|
+
if (callee.type !== "MemberExpression" || callee.computed) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (getImportMetaPropertyName(callee.object) !== "hot") {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return callee.property.name;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const isStringLiteral = (node) => {
|
|
69
|
+
return node.type === "Literal" && typeof node.value === "string";
|
|
70
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseHtml } from "@jsenv/ast";
|
|
2
2
|
import { createMagicSource } from "@jsenv/sourcemap";
|
|
3
3
|
|
|
4
|
-
import { babelPluginMetadataImportMetaHot } from "./babel_plugin_metadata_import_meta_hot.js";
|
|
5
4
|
import { collectHotDataFromHtmlAst } from "./html_hot_dependencies.js";
|
|
5
|
+
import { analyzeImportMetaHot } from "./import_meta_hot_analysis.js";
|
|
6
6
|
|
|
7
7
|
export const jsenvPluginImportMetaHot = () => {
|
|
8
8
|
const importMetaHotClientFileUrl = import.meta
|
|
@@ -52,7 +52,7 @@ export const jsenvPluginImportMetaHot = () => {
|
|
|
52
52
|
cssUrlInfo.data.hotAcceptSelf = false;
|
|
53
53
|
cssUrlInfo.data.hotAcceptDependencies = [];
|
|
54
54
|
},
|
|
55
|
-
js_module:
|
|
55
|
+
js_module: (urlInfo) => {
|
|
56
56
|
// Do not scan node modules for import.meta.hot
|
|
57
57
|
// - unlikely to be there
|
|
58
58
|
// - we don't watch node modules (too expensive)
|
|
@@ -64,27 +64,22 @@ export const jsenvPluginImportMetaHot = () => {
|
|
|
64
64
|
if (!urlInfo.content.includes("import.meta.hot")) {
|
|
65
65
|
return null;
|
|
66
66
|
}
|
|
67
|
-
const { metadata } = await applyBabelPlugins({
|
|
68
|
-
babelPlugins: [babelPluginMetadataImportMetaHot],
|
|
69
|
-
input: urlInfo.content,
|
|
70
|
-
inputIsJsModule: true,
|
|
71
|
-
inputUrl: urlInfo.originalUrl,
|
|
72
|
-
outputUrl: urlInfo.generatedUrl,
|
|
73
|
-
});
|
|
74
67
|
const {
|
|
75
|
-
|
|
68
|
+
importMetaHotNodes,
|
|
76
69
|
hotDecline,
|
|
77
70
|
hotAcceptSelf,
|
|
78
|
-
|
|
79
|
-
} =
|
|
71
|
+
hotAcceptSpecifiers,
|
|
72
|
+
} = analyzeImportMetaHot(urlInfo.contentAst);
|
|
80
73
|
urlInfo.data.hotDecline = hotDecline;
|
|
81
74
|
urlInfo.data.hotAcceptSelf = hotAcceptSelf;
|
|
82
|
-
urlInfo.data.hotAcceptDependencies =
|
|
83
|
-
|
|
75
|
+
urlInfo.data.hotAcceptDependencies = hotAcceptSpecifiers.map(
|
|
76
|
+
(specifier) => resolveHotAcceptSpecifier(urlInfo, specifier),
|
|
77
|
+
);
|
|
78
|
+
if (importMetaHotNodes.length === 0) {
|
|
84
79
|
return null;
|
|
85
80
|
}
|
|
86
81
|
if (urlInfo.context.build) {
|
|
87
|
-
return removeImportMetaHots(urlInfo,
|
|
82
|
+
return removeImportMetaHots(urlInfo, importMetaHotNodes);
|
|
88
83
|
}
|
|
89
84
|
return injectImportMetaHot(urlInfo, importMetaHotClientFileUrl);
|
|
90
85
|
},
|
|
@@ -92,12 +87,32 @@ export const jsenvPluginImportMetaHot = () => {
|
|
|
92
87
|
};
|
|
93
88
|
};
|
|
94
89
|
|
|
95
|
-
|
|
90
|
+
// The specifier given to import.meta.hot.accept() is one the file imports,
|
|
91
|
+
// so its url is known from that import (autoreload compares urls). A
|
|
92
|
+
// specifier the file does not import accepts nothing: it is kept as a plain
|
|
93
|
+
// url, never resolved as a dependency (that could throw on a bare specifier).
|
|
94
|
+
const resolveHotAcceptSpecifier = (urlInfo, specifier) => {
|
|
95
|
+
for (const referenceToOther of urlInfo.referenceToOthersSet) {
|
|
96
|
+
if (
|
|
97
|
+
referenceToOther.type === "js_import" &&
|
|
98
|
+
referenceToOther.specifier === specifier
|
|
99
|
+
) {
|
|
100
|
+
return referenceToOther.url;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
return new URL(specifier, urlInfo.url).href;
|
|
105
|
+
} catch {
|
|
106
|
+
return specifier;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const removeImportMetaHots = (urlInfo, importMetaHotNodes) => {
|
|
96
111
|
const magicSource = createMagicSource(urlInfo.content);
|
|
97
|
-
|
|
112
|
+
importMetaHotNodes.forEach((node) => {
|
|
98
113
|
magicSource.replace({
|
|
99
|
-
start:
|
|
100
|
-
end:
|
|
114
|
+
start: node.start,
|
|
115
|
+
end: node.end,
|
|
101
116
|
replacement: "undefined",
|
|
102
117
|
});
|
|
103
118
|
});
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* TODO: ideally during dev we would keep import.meta.dev and ensure we set it to true rather than replacing it with true?
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { getImportMetaPropertyName, visitJsAst } from "@jsenv/ast";
|
|
14
14
|
import { createMagicSource } from "@jsenv/sourcemap";
|
|
15
15
|
|
|
16
16
|
export const jsenvPluginImportMetaScenarios = () => {
|
|
@@ -18,7 +18,7 @@ export const jsenvPluginImportMetaScenarios = () => {
|
|
|
18
18
|
name: "jsenv:import_meta_scenario",
|
|
19
19
|
appliesDuring: "*",
|
|
20
20
|
transformUrlContent: {
|
|
21
|
-
js_module:
|
|
21
|
+
js_module: (urlInfo) => {
|
|
22
22
|
// Do not scan node modules for import.meta.dev/import.meta.build
|
|
23
23
|
// - node modules won't have this in their code
|
|
24
24
|
// - ;or should use other an other technic as this one won't be available
|
|
@@ -34,40 +34,42 @@ export const jsenvPluginImportMetaScenarios = () => {
|
|
|
34
34
|
) {
|
|
35
35
|
return null;
|
|
36
36
|
}
|
|
37
|
-
const {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
const importMetaScenarioNodes = { dev: [], build: [] };
|
|
38
|
+
visitJsAst(urlInfo.contentAst, {
|
|
39
|
+
MemberExpression: (node) => {
|
|
40
|
+
const name = getImportMetaPropertyName(node);
|
|
41
|
+
if (name === "dev" || name === "build") {
|
|
42
|
+
importMetaScenarioNodes[name].push(node);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
43
45
|
});
|
|
44
|
-
const { dev
|
|
46
|
+
const { dev, build } = importMetaScenarioNodes;
|
|
45
47
|
const replacements = [];
|
|
46
|
-
const replace = (
|
|
47
|
-
replacements.push({
|
|
48
|
+
const replace = (node, value) => {
|
|
49
|
+
replacements.push({ node, value });
|
|
48
50
|
};
|
|
49
51
|
if (urlInfo.context.build) {
|
|
50
52
|
// during build ensure replacement for tree-shaking
|
|
51
|
-
dev.forEach((
|
|
52
|
-
replace(
|
|
53
|
+
dev.forEach((node) => {
|
|
54
|
+
replace(node, "undefined");
|
|
53
55
|
});
|
|
54
|
-
build.forEach((
|
|
55
|
-
replace(
|
|
56
|
+
build.forEach((node) => {
|
|
57
|
+
replace(node, "true");
|
|
56
58
|
});
|
|
57
59
|
} else {
|
|
58
60
|
// during dev we can let "import.meta.build" untouched
|
|
59
61
|
// it will be evaluated to undefined.
|
|
60
62
|
// Moreover it can be surprising to see some "undefined"
|
|
61
63
|
// when source file contains "import.meta.build"
|
|
62
|
-
dev.forEach((
|
|
63
|
-
replace(
|
|
64
|
+
dev.forEach((node) => {
|
|
65
|
+
replace(node, "true");
|
|
64
66
|
});
|
|
65
67
|
}
|
|
66
68
|
const magicSource = createMagicSource(urlInfo.content);
|
|
67
|
-
replacements.forEach(({
|
|
69
|
+
replacements.forEach(({ node, value }) => {
|
|
68
70
|
magicSource.replace({
|
|
69
|
-
start:
|
|
70
|
-
end:
|
|
71
|
+
start: node.start,
|
|
72
|
+
end: node.end,
|
|
71
73
|
replacement: value,
|
|
72
74
|
});
|
|
73
75
|
});
|
|
@@ -76,39 +78,3 @@ export const jsenvPluginImportMetaScenarios = () => {
|
|
|
76
78
|
},
|
|
77
79
|
};
|
|
78
80
|
};
|
|
79
|
-
|
|
80
|
-
const babelPluginMetadataImportMetaScenarios = () => {
|
|
81
|
-
return {
|
|
82
|
-
name: "metadata-import-meta-scenarios",
|
|
83
|
-
visitor: {
|
|
84
|
-
Program(programPath, state) {
|
|
85
|
-
const importMetas = {};
|
|
86
|
-
programPath.traverse({
|
|
87
|
-
MemberExpression(path) {
|
|
88
|
-
const { node } = path;
|
|
89
|
-
const { object } = node;
|
|
90
|
-
if (object.type !== "MetaProperty") {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const { property: objectProperty } = object;
|
|
94
|
-
if (objectProperty.name !== "meta") {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const { property } = node;
|
|
98
|
-
const { name } = property;
|
|
99
|
-
const importMetaPaths = importMetas[name];
|
|
100
|
-
if (importMetaPaths) {
|
|
101
|
-
importMetaPaths.push(path);
|
|
102
|
-
} else {
|
|
103
|
-
importMetas[name] = [path];
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
state.file.metadata.importMetaScenarios = {
|
|
108
|
-
dev: importMetas.dev,
|
|
109
|
-
build: importMetas.build,
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
};
|