@jsenv/core 40.0.7 → 40.0.8
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 +10684 -0
- package/dist/jsenv_core.js +24 -14
- package/dist/jsenv_core_packages.js +16773 -7081
- package/dist/{js → start_build_server}/start_build_server.js +6 -7
- package/dist/{plugins.js → start_dev_server/start_dev_server.js} +953 -25
- package/package.json +9 -9
- package/src/build/build.js +12 -14
- package/src/build/build_specifier_manager.js +4 -0
- package/src/build/build_urls_generator.js +4 -0
- package/src/build/jsenv_plugin_subbuilds.js +4 -0
- package/src/kitchen/out_directory_url.js +1 -0
- package/src/plugins/protocol_http/jsenv_plugin_protocol_http.js +6 -0
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +6 -1
- package/dist/js/build.js +0 -2636
- package/dist/js/start_dev_server.js +0 -840
- package/dist/main.js +0 -117
package/dist/js/build.js
DELETED
|
@@ -1,2636 +0,0 @@
|
|
|
1
|
-
import { parseHtml, findHtmlNode, getHtmlNodeAttribute, getHtmlNodeText, stringifyHtmlAst, removeHtmlNode, setHtmlNodeText, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, visitHtmlNodes, setHtmlNodeAttributes, insertHtmlNodeAfter } from "@jsenv/ast";
|
|
2
|
-
import { distributePercentages, ANSI, humanizeFileSize, urlIsInsideOf, urlToRelativeUrl, urlToFilename, comparePathnames, UNICODE, escapeRegexpSpecialChars, createDetailedMessage, ensurePathnameTrailingSlash, CONTENT_TYPE, injectQueryParamIntoSpecifierWithoutEncoding, renderUrlOrRelativeUrlFilename, assertAndNormalizeDirectoryUrl, lookupPackageDirectory, Abort, raceProcessTeardownEvents, createLogger, jsenvPluginBundling, jsenvPluginMinification, ensureEmptyDirectory, jsenvPluginJsModuleFallback, clearDirectorySync, writeFileSync, createTaskLog } from "../jsenv_core_packages.js";
|
|
3
|
-
import { GRAPH_VISITOR, prependContent, isWebWorkerUrlInfo, getDefaultBase, defaultRuntimeCompat, logsDefault, watchSourceFiles, createKitchen, createPluginStore, getCorePlugins, createPluginController, jsenvPluginReferenceAnalysis, jsenvPluginDirectoryReferenceEffect, jsenvPluginInlining } from "../plugins.js";
|
|
4
|
-
import { generateSourcemapFileUrl, createMagicSource } from "@jsenv/sourcemap";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
-
import "node:path";
|
|
7
|
-
import "node:fs";
|
|
8
|
-
import "node:url";
|
|
9
|
-
import "node:module";
|
|
10
|
-
import "@jsenv/js-module-fallback";
|
|
11
|
-
import "node:process";
|
|
12
|
-
import "node:os";
|
|
13
|
-
import "node:tty";
|
|
14
|
-
import "string-width";
|
|
15
|
-
import "@jsenv/runtime-compat";
|
|
16
|
-
import "node:perf_hooks";
|
|
17
|
-
import "@jsenv/plugin-supervisor";
|
|
18
|
-
import "@jsenv/server";
|
|
19
|
-
import "../main.js";
|
|
20
|
-
|
|
21
|
-
const createUrlGraphSummary = (
|
|
22
|
-
urlGraph,
|
|
23
|
-
{ title = "graph summary" } = {},
|
|
24
|
-
) => {
|
|
25
|
-
const graphReport = createUrlGraphReport(urlGraph);
|
|
26
|
-
return `--- ${title} ---
|
|
27
|
-
${createRepartitionMessage(graphReport)}
|
|
28
|
-
--------------------`;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const createUrlGraphReport = (urlGraph) => {
|
|
32
|
-
const countGroups = {
|
|
33
|
-
sourcemaps: 0,
|
|
34
|
-
html: 0,
|
|
35
|
-
css: 0,
|
|
36
|
-
js: 0,
|
|
37
|
-
json: 0,
|
|
38
|
-
other: 0,
|
|
39
|
-
total: 0,
|
|
40
|
-
};
|
|
41
|
-
const sizeGroups = {
|
|
42
|
-
sourcemaps: 0,
|
|
43
|
-
html: 0,
|
|
44
|
-
css: 0,
|
|
45
|
-
js: 0,
|
|
46
|
-
json: 0,
|
|
47
|
-
other: 0,
|
|
48
|
-
total: 0,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
|
|
52
|
-
urlGraph.rootUrlInfo,
|
|
53
|
-
(urlInfo) => {
|
|
54
|
-
// ignore:
|
|
55
|
-
// - ignored files: we don't know their content
|
|
56
|
-
// - inline files and data files: they are already taken into account in the file where they appear
|
|
57
|
-
if (urlInfo.url.startsWith("ignore:")) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (urlInfo.isInline) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (urlInfo.url.startsWith("data:")) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// file loaded via import assertion are already inside the graph
|
|
68
|
-
// their js module equivalent are ignored to avoid counting it twice
|
|
69
|
-
// in the build graph the file targeted by import assertion will likely be gone
|
|
70
|
-
// and only the js module remain (likely bundled)
|
|
71
|
-
if (
|
|
72
|
-
urlInfo.searchParams.has("as_json_module") ||
|
|
73
|
-
urlInfo.searchParams.has("as_css_module") ||
|
|
74
|
-
urlInfo.searchParams.has("as_text_module")
|
|
75
|
-
) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const urlContentSize = Buffer.byteLength(urlInfo.content);
|
|
80
|
-
const category = determineCategory(urlInfo);
|
|
81
|
-
if (category === "sourcemap") {
|
|
82
|
-
countGroups.sourcemaps++;
|
|
83
|
-
sizeGroups.sourcemaps += urlContentSize;
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
countGroups.total++;
|
|
87
|
-
sizeGroups.total += urlContentSize;
|
|
88
|
-
if (category === "html") {
|
|
89
|
-
countGroups.html++;
|
|
90
|
-
sizeGroups.html += urlContentSize;
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (category === "css") {
|
|
94
|
-
countGroups.css++;
|
|
95
|
-
sizeGroups.css += urlContentSize;
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (category === "js") {
|
|
99
|
-
countGroups.js++;
|
|
100
|
-
sizeGroups.js += urlContentSize;
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (category === "json") {
|
|
104
|
-
countGroups.json++;
|
|
105
|
-
sizeGroups.json += urlContentSize;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
countGroups.other++;
|
|
109
|
-
sizeGroups.other += urlContentSize;
|
|
110
|
-
return;
|
|
111
|
-
},
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
const sizesToDistribute = {};
|
|
115
|
-
Object.keys(sizeGroups).forEach((groupName) => {
|
|
116
|
-
if (groupName !== "sourcemaps" && groupName !== "total") {
|
|
117
|
-
sizesToDistribute[groupName] = sizeGroups[groupName];
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
const percentageGroups = distributePercentages(sizesToDistribute);
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
// sourcemaps are special, there size are ignored
|
|
124
|
-
// so there is no "percentage" associated
|
|
125
|
-
sourcemaps: {
|
|
126
|
-
count: countGroups.sourcemaps,
|
|
127
|
-
size: sizeGroups.sourcemaps,
|
|
128
|
-
percentage: undefined,
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
html: {
|
|
132
|
-
count: countGroups.html,
|
|
133
|
-
size: sizeGroups.html,
|
|
134
|
-
percentage: percentageGroups.html,
|
|
135
|
-
},
|
|
136
|
-
css: {
|
|
137
|
-
count: countGroups.css,
|
|
138
|
-
size: sizeGroups.css,
|
|
139
|
-
percentage: percentageGroups.css,
|
|
140
|
-
},
|
|
141
|
-
js: {
|
|
142
|
-
count: countGroups.js,
|
|
143
|
-
size: sizeGroups.js,
|
|
144
|
-
percentage: percentageGroups.js,
|
|
145
|
-
},
|
|
146
|
-
json: {
|
|
147
|
-
count: countGroups.json,
|
|
148
|
-
size: sizeGroups.json,
|
|
149
|
-
percentage: percentageGroups.json,
|
|
150
|
-
},
|
|
151
|
-
other: {
|
|
152
|
-
count: countGroups.other,
|
|
153
|
-
size: sizeGroups.other,
|
|
154
|
-
percentage: percentageGroups.other,
|
|
155
|
-
},
|
|
156
|
-
total: {
|
|
157
|
-
count: countGroups.total,
|
|
158
|
-
size: sizeGroups.total,
|
|
159
|
-
percentage: 100,
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
const determineCategory = (urlInfo) => {
|
|
165
|
-
if (urlInfo.type === "sourcemap") {
|
|
166
|
-
return "sourcemap";
|
|
167
|
-
}
|
|
168
|
-
if (urlInfo.type === "html") {
|
|
169
|
-
return "html";
|
|
170
|
-
}
|
|
171
|
-
if (urlInfo.type === "css") {
|
|
172
|
-
return "css";
|
|
173
|
-
}
|
|
174
|
-
if (urlInfo.type === "js_module" || urlInfo.type === "js_classic") {
|
|
175
|
-
return "js";
|
|
176
|
-
}
|
|
177
|
-
if (urlInfo.type === "json") {
|
|
178
|
-
return "json";
|
|
179
|
-
}
|
|
180
|
-
return "other";
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const createRepartitionMessage = ({ html, css, js, json, other, total }) => {
|
|
184
|
-
const addPart = (name, { count, size, percentage }) => {
|
|
185
|
-
parts.push(
|
|
186
|
-
`${ANSI.color(`${name}:`, ANSI.GREY)} ${count} (${humanizeFileSize(
|
|
187
|
-
size,
|
|
188
|
-
)} / ${percentage} %)`,
|
|
189
|
-
);
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const parts = [];
|
|
193
|
-
// if (sourcemaps.count) {
|
|
194
|
-
// parts.push(
|
|
195
|
-
// `${ANSI.color(`sourcemaps:`, ANSI.GREY)} ${
|
|
196
|
-
// sourcemaps.count
|
|
197
|
-
// } (${humanizeFileSize(sourcemaps.size)})`,
|
|
198
|
-
// )
|
|
199
|
-
// }
|
|
200
|
-
if (html.count) {
|
|
201
|
-
addPart("html ", html);
|
|
202
|
-
}
|
|
203
|
-
if (css.count) {
|
|
204
|
-
addPart("css ", css);
|
|
205
|
-
}
|
|
206
|
-
if (js.count) {
|
|
207
|
-
addPart("js ", js);
|
|
208
|
-
}
|
|
209
|
-
if (json.count) {
|
|
210
|
-
addPart("json ", json);
|
|
211
|
-
}
|
|
212
|
-
if (other.count) {
|
|
213
|
-
addPart("other", other);
|
|
214
|
-
}
|
|
215
|
-
addPart("total", total);
|
|
216
|
-
return `- ${parts.join(`
|
|
217
|
-
- `)}`;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const createBuildUrlsGenerator = ({
|
|
221
|
-
logger,
|
|
222
|
-
sourceDirectoryUrl,
|
|
223
|
-
buildDirectoryUrl,
|
|
224
|
-
assetsDirectory,
|
|
225
|
-
}) => {
|
|
226
|
-
const cache = {};
|
|
227
|
-
const getUrlName = (url, urlInfo) => {
|
|
228
|
-
if (!urlInfo) {
|
|
229
|
-
return urlToFilename(url);
|
|
230
|
-
}
|
|
231
|
-
if (urlInfo.filenameHint) {
|
|
232
|
-
return urlInfo.filenameHint;
|
|
233
|
-
}
|
|
234
|
-
return urlToFilename(url);
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
const buildUrlCache = new Map();
|
|
238
|
-
|
|
239
|
-
const associateBuildUrl = (url, buildUrl) => {
|
|
240
|
-
buildUrlCache.set(url, buildUrl);
|
|
241
|
-
logger.debug(`associate a build url
|
|
242
|
-
${ANSI.color(url, ANSI.GREY)} ->
|
|
243
|
-
${ANSI.color(buildUrl, ANSI.MAGENTA)}
|
|
244
|
-
`);
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
const generate = (url, { urlInfo, ownerUrlInfo }) => {
|
|
248
|
-
const buildUrlFromCache = buildUrlCache.get(url);
|
|
249
|
-
if (buildUrlFromCache) {
|
|
250
|
-
return buildUrlFromCache;
|
|
251
|
-
}
|
|
252
|
-
if (urlIsInsideOf(url, buildDirectoryUrl)) {
|
|
253
|
-
buildUrlCache.set(url, url);
|
|
254
|
-
return url;
|
|
255
|
-
}
|
|
256
|
-
if (
|
|
257
|
-
urlInfo.type === "directory" ||
|
|
258
|
-
(urlInfo.type === undefined && urlInfo.typeHint === "directory")
|
|
259
|
-
) {
|
|
260
|
-
let directoryPath;
|
|
261
|
-
if (url === sourceDirectoryUrl) {
|
|
262
|
-
directoryPath = "";
|
|
263
|
-
} else if (urlInfo.filenameHint) {
|
|
264
|
-
directoryPath = urlInfo.filenameHint;
|
|
265
|
-
} else {
|
|
266
|
-
directoryPath = urlToRelativeUrl(url, sourceDirectoryUrl);
|
|
267
|
-
}
|
|
268
|
-
const { search } = new URL(url);
|
|
269
|
-
const buildUrl = `${buildDirectoryUrl}${directoryPath}${search}`;
|
|
270
|
-
associateBuildUrl(url, buildUrl);
|
|
271
|
-
return buildUrl;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const directoryPath = determineDirectoryPath({
|
|
275
|
-
sourceDirectoryUrl,
|
|
276
|
-
assetsDirectory,
|
|
277
|
-
urlInfo,
|
|
278
|
-
ownerUrlInfo,
|
|
279
|
-
});
|
|
280
|
-
let names = cache[directoryPath];
|
|
281
|
-
if (!names) {
|
|
282
|
-
names = [];
|
|
283
|
-
cache[directoryPath] = names;
|
|
284
|
-
}
|
|
285
|
-
const urlObject = new URL(url);
|
|
286
|
-
let { search, hash } = urlObject;
|
|
287
|
-
let name = getUrlName(url, urlInfo);
|
|
288
|
-
let [basename, extension] = splitFileExtension(name);
|
|
289
|
-
extension = extensionMappings[extension] || extension;
|
|
290
|
-
let nameCandidate = `${basename}${extension}`; // reconstruct name in case extension was normalized
|
|
291
|
-
let integer = 1;
|
|
292
|
-
while (true) {
|
|
293
|
-
if (!names.includes(nameCandidate)) {
|
|
294
|
-
names.push(nameCandidate);
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
integer++;
|
|
298
|
-
nameCandidate = `${basename}${integer}${extension}`;
|
|
299
|
-
}
|
|
300
|
-
const buildUrl = `${buildDirectoryUrl}${directoryPath}${nameCandidate}${search}${hash}`;
|
|
301
|
-
associateBuildUrl(url, buildUrl);
|
|
302
|
-
return buildUrl;
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
return {
|
|
306
|
-
generate,
|
|
307
|
-
};
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
// It's best to generate files with an extension representing what is inside the file
|
|
311
|
-
// and after build js files contains solely js (js or typescript is gone).
|
|
312
|
-
// This way a static file server is already configured to server the correct content-type
|
|
313
|
-
// (otherwise one would have to configure that ".jsx" is "text/javascript")
|
|
314
|
-
// To keep in mind: if you have "user.jsx" and "user.js" AND both file are not bundled
|
|
315
|
-
// you end up with "dist/js/user.js" and "dist/js/user2.js"
|
|
316
|
-
const extensionMappings = {
|
|
317
|
-
".jsx": ".js",
|
|
318
|
-
".ts": ".js",
|
|
319
|
-
".tsx": ".js",
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const splitFileExtension = (filename) => {
|
|
323
|
-
const dotLastIndex = filename.lastIndexOf(".");
|
|
324
|
-
if (dotLastIndex === -1) {
|
|
325
|
-
return [filename, ""];
|
|
326
|
-
}
|
|
327
|
-
return [filename.slice(0, dotLastIndex), filename.slice(dotLastIndex)];
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const determineDirectoryPath = ({
|
|
331
|
-
sourceDirectoryUrl,
|
|
332
|
-
assetsDirectory,
|
|
333
|
-
urlInfo,
|
|
334
|
-
ownerUrlInfo,
|
|
335
|
-
}) => {
|
|
336
|
-
if (urlInfo.dirnameHint) {
|
|
337
|
-
return urlInfo.dirnameHint;
|
|
338
|
-
}
|
|
339
|
-
if (urlInfo.type === "directory") {
|
|
340
|
-
return "";
|
|
341
|
-
}
|
|
342
|
-
if (urlInfo.isInline) {
|
|
343
|
-
const parentDirectoryPath = determineDirectoryPath({
|
|
344
|
-
sourceDirectoryUrl,
|
|
345
|
-
assetsDirectory,
|
|
346
|
-
urlInfo: ownerUrlInfo || urlInfo.firstReference.ownerUrlInfo,
|
|
347
|
-
});
|
|
348
|
-
return parentDirectoryPath;
|
|
349
|
-
}
|
|
350
|
-
if (urlInfo.isEntryPoint && !urlInfo.isDynamicEntryPoint) {
|
|
351
|
-
return "";
|
|
352
|
-
}
|
|
353
|
-
if (urlInfo.type === "importmap") {
|
|
354
|
-
return "";
|
|
355
|
-
}
|
|
356
|
-
if (urlInfo.type === "html") {
|
|
357
|
-
return `${assetsDirectory}html/`;
|
|
358
|
-
}
|
|
359
|
-
if (urlInfo.type === "css") {
|
|
360
|
-
return `${assetsDirectory}css/`;
|
|
361
|
-
}
|
|
362
|
-
if (urlInfo.type === "js_module" || urlInfo.type === "js_classic") {
|
|
363
|
-
return `${assetsDirectory}js/`;
|
|
364
|
-
}
|
|
365
|
-
if (urlInfo.type === "json") {
|
|
366
|
-
return `${assetsDirectory}json/`;
|
|
367
|
-
}
|
|
368
|
-
return `${assetsDirectory}other/`;
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
// https://bundlers.tooling.report/hashing/avoid-cascade/
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const injectGlobalMappings = async (urlInfo, mappings) => {
|
|
375
|
-
if (urlInfo.type === "html") {
|
|
376
|
-
const minification = Boolean(
|
|
377
|
-
urlInfo.context.getPluginMeta("willMinifyJsClassic"),
|
|
378
|
-
);
|
|
379
|
-
const content = generateClientCodeForMappings(mappings, {
|
|
380
|
-
globalName: "window",
|
|
381
|
-
minification,
|
|
382
|
-
});
|
|
383
|
-
await prependContent(urlInfo, { type: "js_classic", content });
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
if (urlInfo.type === "js_classic" || urlInfo.type === "js_module") {
|
|
387
|
-
const minification = Boolean(
|
|
388
|
-
urlInfo.context.getPluginMeta("willMinifyJsClassic"),
|
|
389
|
-
);
|
|
390
|
-
const content = generateClientCodeForMappings(mappings, {
|
|
391
|
-
globalName: isWebWorkerUrlInfo(urlInfo) ? "self" : "window",
|
|
392
|
-
minification,
|
|
393
|
-
});
|
|
394
|
-
await prependContent(urlInfo, { type: "js_classic", content });
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
const generateClientCodeForMappings = (
|
|
400
|
-
versionMappings,
|
|
401
|
-
{ globalName, minification },
|
|
402
|
-
) => {
|
|
403
|
-
if (minification) {
|
|
404
|
-
return `;(function(){var m = ${JSON.stringify(
|
|
405
|
-
versionMappings,
|
|
406
|
-
)}; ${globalName}.__v__ = function (s) { return m[s] || s }; })();`;
|
|
407
|
-
}
|
|
408
|
-
return `;(function() {
|
|
409
|
-
var __versionMappings__ = {
|
|
410
|
-
${stringifyParams(versionMappings, " ")}
|
|
411
|
-
};
|
|
412
|
-
${globalName}.__v__ = function (specifier) {
|
|
413
|
-
return __versionMappings__[specifier] || specifier
|
|
414
|
-
};
|
|
415
|
-
})();`;
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
const injectImportmapMappings = (urlInfo, getMappings) => {
|
|
419
|
-
const htmlAst = parseHtml({
|
|
420
|
-
html: urlInfo.content,
|
|
421
|
-
url: urlInfo.url,
|
|
422
|
-
storeOriginalPositions: false,
|
|
423
|
-
});
|
|
424
|
-
// jsenv_plugin_importmap.js is removing importmap during build
|
|
425
|
-
// it means at this point we know HTML has no importmap in it
|
|
426
|
-
// we can safely inject one
|
|
427
|
-
const importmapMinification = Boolean(
|
|
428
|
-
urlInfo.context.getPluginMeta("willMinifyJson"),
|
|
429
|
-
);
|
|
430
|
-
const importmapNode = findHtmlNode(htmlAst, (node) => {
|
|
431
|
-
return (
|
|
432
|
-
node.tagName === "script" &&
|
|
433
|
-
getHtmlNodeAttribute(node, "type") === "importmap"
|
|
434
|
-
);
|
|
435
|
-
});
|
|
436
|
-
const generateMappingText = (mappings) => {
|
|
437
|
-
if (importmapMinification) {
|
|
438
|
-
return JSON.stringify({ imports: mappings });
|
|
439
|
-
}
|
|
440
|
-
return JSON.stringify({ imports: mappings }, null, " ");
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
const mutate = (mutation) => {
|
|
444
|
-
mutation();
|
|
445
|
-
urlInfo.mutateContent({
|
|
446
|
-
content: stringifyHtmlAst(htmlAst),
|
|
447
|
-
});
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
if (importmapNode) {
|
|
451
|
-
// we want to remove some mappings, override others, add eventually add new
|
|
452
|
-
const currentMappings = JSON.parse(getHtmlNodeText(importmapNode));
|
|
453
|
-
const mappings = getMappings(currentMappings.imports);
|
|
454
|
-
if (!mappings || Object.keys(mappings).length === 0) {
|
|
455
|
-
mutate(() => {
|
|
456
|
-
removeHtmlNode(importmapNode);
|
|
457
|
-
});
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
mutate(() => {
|
|
461
|
-
setHtmlNodeText(importmapNode, generateMappingText(mappings), {
|
|
462
|
-
indentation: "auto",
|
|
463
|
-
});
|
|
464
|
-
});
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
const mappings = getMappings(null);
|
|
468
|
-
if (!mappings || Object.keys(mappings).length === 0) {
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
mutate(() => {
|
|
472
|
-
injectHtmlNodeAsEarlyAsPossible(
|
|
473
|
-
htmlAst,
|
|
474
|
-
createHtmlNode({
|
|
475
|
-
tagName: "script",
|
|
476
|
-
type: "importmap",
|
|
477
|
-
children: generateMappingText(getMappings(null)),
|
|
478
|
-
}),
|
|
479
|
-
"jsenv:versioning",
|
|
480
|
-
);
|
|
481
|
-
});
|
|
482
|
-
return;
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
const stringifyParams = (params, prefix = "") => {
|
|
486
|
-
const source = JSON.stringify(params, null, prefix);
|
|
487
|
-
if (prefix.length) {
|
|
488
|
-
// remove leading "{\n"
|
|
489
|
-
// remove leading prefix
|
|
490
|
-
// remove trailing "\n}"
|
|
491
|
-
return source.slice(2 + prefix.length, -2);
|
|
492
|
-
}
|
|
493
|
-
// remove leading "{"
|
|
494
|
-
// remove trailing "}"
|
|
495
|
-
return source.slice(1, -1);
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
const createBuildSpecifierManager = ({
|
|
499
|
-
rawKitchen,
|
|
500
|
-
finalKitchen,
|
|
501
|
-
logger,
|
|
502
|
-
sourceDirectoryUrl,
|
|
503
|
-
buildDirectoryUrl,
|
|
504
|
-
base,
|
|
505
|
-
assetsDirectory,
|
|
506
|
-
length = 8,
|
|
507
|
-
|
|
508
|
-
versioning,
|
|
509
|
-
versioningMethod,
|
|
510
|
-
versionLength,
|
|
511
|
-
canUseImportmap,
|
|
512
|
-
}) => {
|
|
513
|
-
const buildUrlsGenerator = createBuildUrlsGenerator({
|
|
514
|
-
logger,
|
|
515
|
-
sourceDirectoryUrl,
|
|
516
|
-
buildDirectoryUrl,
|
|
517
|
-
assetsDirectory,
|
|
518
|
-
});
|
|
519
|
-
const placeholderAPI = createPlaceholderAPI({
|
|
520
|
-
length,
|
|
521
|
-
});
|
|
522
|
-
const placeholderToReferenceMap = new Map();
|
|
523
|
-
const urlInfoToBuildUrlMap = new Map();
|
|
524
|
-
const buildUrlToUrlInfoMap = new Map();
|
|
525
|
-
const buildUrlToBuildSpecifierMap = new Map();
|
|
526
|
-
|
|
527
|
-
const generateReplacement = (reference) => {
|
|
528
|
-
let buildUrl;
|
|
529
|
-
if (reference.type === "sourcemap_comment") {
|
|
530
|
-
const parentBuildUrl = urlInfoToBuildUrlMap.get(reference.ownerUrlInfo);
|
|
531
|
-
buildUrl = generateSourcemapFileUrl(parentBuildUrl);
|
|
532
|
-
reference.generatedSpecifier = buildUrl;
|
|
533
|
-
} else {
|
|
534
|
-
const url = reference.generatedUrl;
|
|
535
|
-
let urlInfo;
|
|
536
|
-
const rawUrlInfo = rawKitchen.graph.getUrlInfo(reference.url);
|
|
537
|
-
if (rawUrlInfo) {
|
|
538
|
-
urlInfo = rawUrlInfo;
|
|
539
|
-
} else {
|
|
540
|
-
const buildUrlInfo = reference.urlInfo;
|
|
541
|
-
buildUrlInfo.type = reference.expectedType || "asset";
|
|
542
|
-
buildUrlInfo.subtype = reference.expectedSubtype;
|
|
543
|
-
urlInfo = buildUrlInfo;
|
|
544
|
-
}
|
|
545
|
-
buildUrl = buildUrlsGenerator.generate(url, {
|
|
546
|
-
urlInfo,
|
|
547
|
-
ownerUrlInfo: reference.ownerUrlInfo,
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
let buildSpecifier;
|
|
552
|
-
if (base === "./") {
|
|
553
|
-
const { ownerUrlInfo } = reference;
|
|
554
|
-
const parentBuildUrl = ownerUrlInfo.isRoot
|
|
555
|
-
? buildDirectoryUrl
|
|
556
|
-
: urlInfoToBuildUrlMap.get(
|
|
557
|
-
ownerUrlInfo.isInline
|
|
558
|
-
? ownerUrlInfo.findParentIfInline()
|
|
559
|
-
: ownerUrlInfo,
|
|
560
|
-
);
|
|
561
|
-
const urlRelativeToParent = urlToRelativeUrl(buildUrl, parentBuildUrl);
|
|
562
|
-
if (urlRelativeToParent[0] === ".") {
|
|
563
|
-
buildSpecifier = urlRelativeToParent;
|
|
564
|
-
} else {
|
|
565
|
-
// ensure "./" on relative url (otherwise it could be a "bare specifier")
|
|
566
|
-
buildSpecifier = `./${urlRelativeToParent}`;
|
|
567
|
-
}
|
|
568
|
-
} else {
|
|
569
|
-
const urlRelativeToBuildDirectory = urlToRelativeUrl(
|
|
570
|
-
buildUrl,
|
|
571
|
-
buildDirectoryUrl,
|
|
572
|
-
);
|
|
573
|
-
buildSpecifier = `${base}${urlRelativeToBuildDirectory}`;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
urlInfoToBuildUrlMap.set(reference.urlInfo, buildUrl);
|
|
577
|
-
buildUrlToUrlInfoMap.set(buildUrl, reference.urlInfo);
|
|
578
|
-
buildUrlToBuildSpecifierMap.set(buildUrl, buildSpecifier);
|
|
579
|
-
const buildGeneratedSpecifier = applyVersioningOnBuildSpecifier(
|
|
580
|
-
buildSpecifier,
|
|
581
|
-
reference,
|
|
582
|
-
);
|
|
583
|
-
return buildGeneratedSpecifier;
|
|
584
|
-
};
|
|
585
|
-
const internalRedirections = new Map();
|
|
586
|
-
const bundleInfoMap = new Map();
|
|
587
|
-
|
|
588
|
-
const applyBundling = async ({ bundler, urlInfosToBundle }) => {
|
|
589
|
-
const urlInfosBundled = await rawKitchen.pluginController.callAsyncHook(
|
|
590
|
-
{
|
|
591
|
-
plugin: bundler.plugin,
|
|
592
|
-
hookName: "bundle",
|
|
593
|
-
value: bundler.bundleFunction,
|
|
594
|
-
},
|
|
595
|
-
urlInfosToBundle,
|
|
596
|
-
);
|
|
597
|
-
for (const url of Object.keys(urlInfosBundled)) {
|
|
598
|
-
const urlInfoBundled = urlInfosBundled[url];
|
|
599
|
-
if (urlInfoBundled.sourceUrls) {
|
|
600
|
-
for (const sourceUrl of urlInfoBundled.sourceUrls) {
|
|
601
|
-
const sourceRawUrlInfo = rawKitchen.graph.getUrlInfo(sourceUrl);
|
|
602
|
-
if (sourceRawUrlInfo) {
|
|
603
|
-
sourceRawUrlInfo.data.bundled = true;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
bundleInfoMap.set(url, urlInfoBundled);
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
const jsenvPluginMoveToBuildDirectory = {
|
|
612
|
-
name: "jsenv:move_to_build_directory",
|
|
613
|
-
appliesDuring: "build",
|
|
614
|
-
// reference resolution is split in 2
|
|
615
|
-
// the redirection to build directory is done in a second phase (redirectReference)
|
|
616
|
-
// to let opportunity to others plugins (js_module_fallback)
|
|
617
|
-
// to mutate reference (inject ?js_module_fallback)
|
|
618
|
-
// before it gets redirected to build directory
|
|
619
|
-
resolveReference: (reference) => {
|
|
620
|
-
const { ownerUrlInfo } = reference;
|
|
621
|
-
if (ownerUrlInfo.remapReference && !reference.isInline) {
|
|
622
|
-
const newSpecifier = ownerUrlInfo.remapReference(reference);
|
|
623
|
-
reference.specifier = newSpecifier;
|
|
624
|
-
}
|
|
625
|
-
const referenceFromPlaceholder = placeholderToReferenceMap.get(
|
|
626
|
-
reference.specifier,
|
|
627
|
-
);
|
|
628
|
-
if (referenceFromPlaceholder) {
|
|
629
|
-
return referenceFromPlaceholder.url;
|
|
630
|
-
}
|
|
631
|
-
if (reference.type === "filesystem") {
|
|
632
|
-
const ownerRawUrl = ensurePathnameTrailingSlash(ownerUrlInfo.url);
|
|
633
|
-
const url = new URL(reference.specifier, ownerRawUrl).href;
|
|
634
|
-
return url;
|
|
635
|
-
}
|
|
636
|
-
if (reference.specifierPathname[0] === "/") {
|
|
637
|
-
const url = new URL(reference.specifier.slice(1), sourceDirectoryUrl)
|
|
638
|
-
.href;
|
|
639
|
-
return url;
|
|
640
|
-
}
|
|
641
|
-
if (reference.injected) {
|
|
642
|
-
// js_module_fallback
|
|
643
|
-
const url = new URL(
|
|
644
|
-
reference.specifier,
|
|
645
|
-
reference.baseUrl || ownerUrlInfo.url,
|
|
646
|
-
).href;
|
|
647
|
-
return url;
|
|
648
|
-
}
|
|
649
|
-
const parentUrl = reference.baseUrl || ownerUrlInfo.url;
|
|
650
|
-
const url = new URL(reference.specifier, parentUrl).href;
|
|
651
|
-
return url;
|
|
652
|
-
},
|
|
653
|
-
redirectReference: (reference) => {
|
|
654
|
-
let referenceBeforeInlining = reference;
|
|
655
|
-
if (
|
|
656
|
-
referenceBeforeInlining.isInline &&
|
|
657
|
-
referenceBeforeInlining.prev &&
|
|
658
|
-
!referenceBeforeInlining.prev.isInline
|
|
659
|
-
) {
|
|
660
|
-
referenceBeforeInlining = referenceBeforeInlining.prev;
|
|
661
|
-
}
|
|
662
|
-
const rawUrl = referenceBeforeInlining.url;
|
|
663
|
-
const rawUrlInfo = rawKitchen.graph.getUrlInfo(rawUrl);
|
|
664
|
-
if (rawUrlInfo) {
|
|
665
|
-
reference.filenameHint = rawUrlInfo.filenameHint;
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
if (referenceBeforeInlining.injected) {
|
|
669
|
-
return null;
|
|
670
|
-
}
|
|
671
|
-
if (
|
|
672
|
-
referenceBeforeInlining.isInline &&
|
|
673
|
-
referenceBeforeInlining.ownerUrlInfo.url ===
|
|
674
|
-
referenceBeforeInlining.ownerUrlInfo.originalUrl
|
|
675
|
-
) {
|
|
676
|
-
const rawUrlInfo = findRawUrlInfoWhenInline(
|
|
677
|
-
referenceBeforeInlining,
|
|
678
|
-
rawKitchen,
|
|
679
|
-
);
|
|
680
|
-
if (rawUrlInfo) {
|
|
681
|
-
reference.rawUrl = rawUrlInfo.url;
|
|
682
|
-
reference.filenameHint = rawUrlInfo.filenameHint;
|
|
683
|
-
return null;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
reference.filenameHint = referenceBeforeInlining.filenameHint;
|
|
687
|
-
return null;
|
|
688
|
-
},
|
|
689
|
-
transformReferenceSearchParams: () => {
|
|
690
|
-
// those search params are reflected into the build file name
|
|
691
|
-
// moreover it create cleaner output
|
|
692
|
-
// otherwise output is full of ?js_module_fallback search param
|
|
693
|
-
return {
|
|
694
|
-
js_module_fallback: undefined,
|
|
695
|
-
as_json_module: undefined,
|
|
696
|
-
as_css_module: undefined,
|
|
697
|
-
as_text_module: undefined,
|
|
698
|
-
as_js_module: undefined,
|
|
699
|
-
as_js_classic: undefined,
|
|
700
|
-
cjs_as_js_module: undefined,
|
|
701
|
-
js_classic: undefined, // TODO: add comment to explain who is using this
|
|
702
|
-
entry_point: undefined,
|
|
703
|
-
dynamic_import: undefined,
|
|
704
|
-
};
|
|
705
|
-
},
|
|
706
|
-
formatReference: (reference) => {
|
|
707
|
-
const generatedUrl = reference.generatedUrl;
|
|
708
|
-
if (!generatedUrl.startsWith("file:")) {
|
|
709
|
-
return null;
|
|
710
|
-
}
|
|
711
|
-
if (reference.isWeak && reference.expectedType !== "directory") {
|
|
712
|
-
return null;
|
|
713
|
-
}
|
|
714
|
-
if (reference.type === "sourcemap_comment") {
|
|
715
|
-
return null;
|
|
716
|
-
}
|
|
717
|
-
const placeholder = placeholderAPI.generate();
|
|
718
|
-
if (generatedUrl !== reference.url) {
|
|
719
|
-
internalRedirections.set(generatedUrl, reference.url);
|
|
720
|
-
}
|
|
721
|
-
placeholderToReferenceMap.set(placeholder, reference);
|
|
722
|
-
return placeholder;
|
|
723
|
-
},
|
|
724
|
-
fetchUrlContent: async (finalUrlInfo) => {
|
|
725
|
-
// not need because it will be inherit from rawUrlInfo
|
|
726
|
-
// if (urlIsInsideOf(finalUrlInfo.url, buildDirectoryUrl)) {
|
|
727
|
-
// finalUrlInfo.type = "asset";
|
|
728
|
-
// }
|
|
729
|
-
let { firstReference } = finalUrlInfo;
|
|
730
|
-
if (
|
|
731
|
-
firstReference.isInline &&
|
|
732
|
-
firstReference.prev &&
|
|
733
|
-
!firstReference.prev.isInline
|
|
734
|
-
) {
|
|
735
|
-
firstReference = firstReference.prev;
|
|
736
|
-
}
|
|
737
|
-
const rawUrl = firstReference.rawUrl || firstReference.url;
|
|
738
|
-
const rawUrlInfo = rawKitchen.graph.getUrlInfo(rawUrl);
|
|
739
|
-
const bundleInfo = bundleInfoMap.get(rawUrl);
|
|
740
|
-
if (bundleInfo) {
|
|
741
|
-
finalUrlInfo.remapReference = bundleInfo.remapReference;
|
|
742
|
-
return {
|
|
743
|
-
// url: bundleInfo.url,
|
|
744
|
-
originalUrl: bundleInfo.originalUrl,
|
|
745
|
-
type: bundleInfo.type,
|
|
746
|
-
content: bundleInfo.content,
|
|
747
|
-
contentType: bundleInfo.contentType,
|
|
748
|
-
sourcemap: bundleInfo.sourcemap,
|
|
749
|
-
data: bundleInfo.data,
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
if (rawUrlInfo) {
|
|
753
|
-
return rawUrlInfo;
|
|
754
|
-
}
|
|
755
|
-
// reference injected during "shape":
|
|
756
|
-
// - "js_module_fallback" using getWithoutSearchParam to obtain source
|
|
757
|
-
// url info that will be converted to systemjs/UMD
|
|
758
|
-
// - "js_module_fallback" injecting "s.js"
|
|
759
|
-
if (firstReference.injected) {
|
|
760
|
-
const reference = firstReference.original || firstReference;
|
|
761
|
-
const rawReference = rawKitchen.graph.rootUrlInfo.dependencies.inject({
|
|
762
|
-
type: reference.type,
|
|
763
|
-
expectedType: reference.expectedType,
|
|
764
|
-
specifier: reference.specifier,
|
|
765
|
-
specifierLine: reference.specifierLine,
|
|
766
|
-
specifierColumn: reference.specifierColumn,
|
|
767
|
-
specifierStart: reference.specifierStart,
|
|
768
|
-
specifierEnd: reference.specifierEnd,
|
|
769
|
-
isInline: reference.isInline,
|
|
770
|
-
filenameHint: reference.filenameHint,
|
|
771
|
-
content: reference.content,
|
|
772
|
-
contentType: reference.contentType,
|
|
773
|
-
});
|
|
774
|
-
const rawUrlInfo = rawReference.urlInfo;
|
|
775
|
-
await rawUrlInfo.cook();
|
|
776
|
-
return {
|
|
777
|
-
type: rawUrlInfo.type,
|
|
778
|
-
content: rawUrlInfo.content,
|
|
779
|
-
contentType: rawUrlInfo.contentType,
|
|
780
|
-
originalContent: rawUrlInfo.originalContent,
|
|
781
|
-
originalUrl: rawUrlInfo.originalUrl,
|
|
782
|
-
sourcemap: rawUrlInfo.sourcemap,
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
if (firstReference.isInline) {
|
|
786
|
-
if (
|
|
787
|
-
firstReference.ownerUrlInfo.url ===
|
|
788
|
-
firstReference.ownerUrlInfo.originalUrl
|
|
789
|
-
) {
|
|
790
|
-
if (rawUrlInfo) {
|
|
791
|
-
return rawUrlInfo;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
return {
|
|
795
|
-
originalContent: finalUrlInfo.originalContent,
|
|
796
|
-
content: firstReference.content,
|
|
797
|
-
contentType: firstReference.contentType,
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
throw new Error(createDetailedMessage(`${rawUrl} not found in graph`));
|
|
801
|
-
},
|
|
802
|
-
};
|
|
803
|
-
|
|
804
|
-
const buildSpecifierToBuildSpecifierVersionedMap = new Map();
|
|
805
|
-
|
|
806
|
-
const versionMap = new Map();
|
|
807
|
-
|
|
808
|
-
const referenceInSeparateContextSet = new Set();
|
|
809
|
-
const referenceVersioningInfoMap = new Map();
|
|
810
|
-
const _getReferenceVersioningInfo = (reference) => {
|
|
811
|
-
if (!shouldApplyVersioningOnReference(reference)) {
|
|
812
|
-
return {
|
|
813
|
-
type: "not_versioned",
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
|
-
const ownerUrlInfo = reference.ownerUrlInfo;
|
|
817
|
-
if (ownerUrlInfo.jsQuote) {
|
|
818
|
-
// here we use placeholder as specifier, so something like
|
|
819
|
-
// "/other/file.png" becomes "!~{0001}~" and finally "__v__("/other/file.png")"
|
|
820
|
-
// this is to support cases like CSS inlined in JS
|
|
821
|
-
// CSS minifier must see valid CSS specifiers like background-image: url("!~{0001}~");
|
|
822
|
-
// that is finally replaced by invalid css background-image: url("__v__("/other/file.png")")
|
|
823
|
-
return {
|
|
824
|
-
type: "global",
|
|
825
|
-
render: (buildSpecifier) => {
|
|
826
|
-
return placeholderAPI.markAsCode(
|
|
827
|
-
`${ownerUrlInfo.jsQuote}+__v__(${JSON.stringify(buildSpecifier)})+${
|
|
828
|
-
ownerUrlInfo.jsQuote
|
|
829
|
-
}`,
|
|
830
|
-
);
|
|
831
|
-
},
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
if (reference.type === "js_url") {
|
|
835
|
-
return {
|
|
836
|
-
type: "global",
|
|
837
|
-
render: (buildSpecifier) => {
|
|
838
|
-
return placeholderAPI.markAsCode(
|
|
839
|
-
`__v__(${JSON.stringify(buildSpecifier)})`,
|
|
840
|
-
);
|
|
841
|
-
},
|
|
842
|
-
};
|
|
843
|
-
}
|
|
844
|
-
if (reference.type === "js_import") {
|
|
845
|
-
if (reference.subtype === "import_dynamic") {
|
|
846
|
-
return {
|
|
847
|
-
type: "global",
|
|
848
|
-
render: (buildSpecifier) => {
|
|
849
|
-
return placeholderAPI.markAsCode(
|
|
850
|
-
`__v__(${JSON.stringify(buildSpecifier)})`,
|
|
851
|
-
);
|
|
852
|
-
},
|
|
853
|
-
};
|
|
854
|
-
}
|
|
855
|
-
if (reference.subtype === "import_meta_resolve") {
|
|
856
|
-
return {
|
|
857
|
-
type: "global",
|
|
858
|
-
render: (buildSpecifier) => {
|
|
859
|
-
return placeholderAPI.markAsCode(
|
|
860
|
-
`__v__(${JSON.stringify(buildSpecifier)})`,
|
|
861
|
-
);
|
|
862
|
-
},
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
if (canUseImportmap && !isInsideSeparateContext(reference)) {
|
|
866
|
-
return {
|
|
867
|
-
type: "importmap",
|
|
868
|
-
render: (buildSpecifier) => {
|
|
869
|
-
return buildSpecifier;
|
|
870
|
-
},
|
|
871
|
-
};
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
return {
|
|
875
|
-
type: "inline",
|
|
876
|
-
render: (buildSpecifier) => {
|
|
877
|
-
const buildSpecifierVersioned =
|
|
878
|
-
buildSpecifierToBuildSpecifierVersionedMap.get(buildSpecifier);
|
|
879
|
-
return buildSpecifierVersioned;
|
|
880
|
-
},
|
|
881
|
-
};
|
|
882
|
-
};
|
|
883
|
-
const getReferenceVersioningInfo = (reference) => {
|
|
884
|
-
const infoFromCache = referenceVersioningInfoMap.get(reference);
|
|
885
|
-
if (infoFromCache) {
|
|
886
|
-
return infoFromCache;
|
|
887
|
-
}
|
|
888
|
-
const info = _getReferenceVersioningInfo(reference);
|
|
889
|
-
referenceVersioningInfoMap.set(reference, info);
|
|
890
|
-
return info;
|
|
891
|
-
};
|
|
892
|
-
const isInsideSeparateContext = (reference) => {
|
|
893
|
-
if (referenceInSeparateContextSet.has(reference)) {
|
|
894
|
-
return true;
|
|
895
|
-
}
|
|
896
|
-
const referenceOwnerUrllInfo = reference.ownerUrlInfo;
|
|
897
|
-
let is = false;
|
|
898
|
-
if (isWebWorkerUrlInfo(referenceOwnerUrllInfo)) {
|
|
899
|
-
is = true;
|
|
900
|
-
} else {
|
|
901
|
-
GRAPH_VISITOR.findDependent(
|
|
902
|
-
referenceOwnerUrllInfo,
|
|
903
|
-
(dependentUrlInfo) => {
|
|
904
|
-
if (isWebWorkerUrlInfo(dependentUrlInfo)) {
|
|
905
|
-
is = true;
|
|
906
|
-
return true;
|
|
907
|
-
}
|
|
908
|
-
return false;
|
|
909
|
-
},
|
|
910
|
-
);
|
|
911
|
-
}
|
|
912
|
-
if (is) {
|
|
913
|
-
referenceInSeparateContextSet.add(reference);
|
|
914
|
-
return true;
|
|
915
|
-
}
|
|
916
|
-
return false;
|
|
917
|
-
};
|
|
918
|
-
const canUseVersionedUrl = (urlInfo) => {
|
|
919
|
-
if (urlInfo.isRoot) {
|
|
920
|
-
return false;
|
|
921
|
-
}
|
|
922
|
-
if (urlInfo.isEntryPoint) {
|
|
923
|
-
// if (urlInfo.subtype === "worker") {
|
|
924
|
-
// return true;
|
|
925
|
-
// }
|
|
926
|
-
return false;
|
|
927
|
-
}
|
|
928
|
-
return urlInfo.type !== "webmanifest";
|
|
929
|
-
};
|
|
930
|
-
const shouldApplyVersioningOnReference = (reference) => {
|
|
931
|
-
if (reference.isInline) {
|
|
932
|
-
return false;
|
|
933
|
-
}
|
|
934
|
-
if (reference.next && reference.next.isInline) {
|
|
935
|
-
return false;
|
|
936
|
-
}
|
|
937
|
-
if (reference.type === "sourcemap_comment") {
|
|
938
|
-
return false;
|
|
939
|
-
}
|
|
940
|
-
if (reference.expectedType === "directory") {
|
|
941
|
-
return true;
|
|
942
|
-
}
|
|
943
|
-
// specifier comes from "normalize" hook done a bit earlier in this file
|
|
944
|
-
// we want to get back their build url to access their infos
|
|
945
|
-
const referencedUrlInfo = reference.urlInfo;
|
|
946
|
-
if (!canUseVersionedUrl(referencedUrlInfo)) {
|
|
947
|
-
return false;
|
|
948
|
-
}
|
|
949
|
-
return true;
|
|
950
|
-
};
|
|
951
|
-
|
|
952
|
-
const prepareVersioning = () => {
|
|
953
|
-
const contentOnlyVersionMap = new Map();
|
|
954
|
-
const urlInfoToContainedPlaceholderSetMap = new Map();
|
|
955
|
-
const directoryUrlInfoSet = new Set();
|
|
956
|
-
{
|
|
957
|
-
GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
|
|
958
|
-
finalKitchen.graph.rootUrlInfo,
|
|
959
|
-
(urlInfo) => {
|
|
960
|
-
// ignore:
|
|
961
|
-
// - inline files and data files:
|
|
962
|
-
// they are already taken into account in the file where they appear
|
|
963
|
-
// - ignored files:
|
|
964
|
-
// we don't know their content
|
|
965
|
-
// - unused files without reference
|
|
966
|
-
// File updated such as style.css -> style.css.js or file.js->file.nomodule.js
|
|
967
|
-
// Are used at some point just to be discarded later because they need to be converted
|
|
968
|
-
// There is no need to version them and we could not because the file have been ignored
|
|
969
|
-
// so their content is unknown
|
|
970
|
-
if (urlInfo.type === "sourcemap") {
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
if (urlInfo.isInline) {
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
if (urlInfo.url.startsWith("data:")) {
|
|
977
|
-
// urlInfo became inline and is not referenced by something else
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
if (urlInfo.url.startsWith("ignore:")) {
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
let content = urlInfo.content;
|
|
984
|
-
if (urlInfo.type === "html") {
|
|
985
|
-
content = stringifyHtmlAst(
|
|
986
|
-
parseHtml({
|
|
987
|
-
html: urlInfo.content,
|
|
988
|
-
url: urlInfo.url,
|
|
989
|
-
storeOriginalPositions: false,
|
|
990
|
-
}),
|
|
991
|
-
{
|
|
992
|
-
cleanupJsenvAttributes: true,
|
|
993
|
-
cleanupPositionAttributes: true,
|
|
994
|
-
},
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
const containedPlaceholderSet = new Set();
|
|
998
|
-
if (mayUsePlaceholder(urlInfo)) {
|
|
999
|
-
const contentWithPredictibleVersionPlaceholders =
|
|
1000
|
-
placeholderAPI.replaceWithDefault(content, (placeholder) => {
|
|
1001
|
-
containedPlaceholderSet.add(placeholder);
|
|
1002
|
-
});
|
|
1003
|
-
content = contentWithPredictibleVersionPlaceholders;
|
|
1004
|
-
}
|
|
1005
|
-
urlInfoToContainedPlaceholderSetMap.set(
|
|
1006
|
-
urlInfo,
|
|
1007
|
-
containedPlaceholderSet,
|
|
1008
|
-
);
|
|
1009
|
-
const contentVersion = generateVersion([content], versionLength);
|
|
1010
|
-
contentOnlyVersionMap.set(urlInfo, contentVersion);
|
|
1011
|
-
},
|
|
1012
|
-
{
|
|
1013
|
-
directoryUrlInfoSet,
|
|
1014
|
-
},
|
|
1015
|
-
);
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
{
|
|
1019
|
-
const getSetOfUrlInfoInfluencingVersion = (urlInfo) => {
|
|
1020
|
-
const placeholderInfluencingVersionSet = new Set();
|
|
1021
|
-
const visitContainedPlaceholders = (urlInfo) => {
|
|
1022
|
-
const referencedContentVersion = contentOnlyVersionMap.get(urlInfo);
|
|
1023
|
-
if (!referencedContentVersion) {
|
|
1024
|
-
// ignored while traversing graph (not used anymore, inline, ...)
|
|
1025
|
-
return;
|
|
1026
|
-
}
|
|
1027
|
-
const containedPlaceholderSet =
|
|
1028
|
-
urlInfoToContainedPlaceholderSetMap.get(urlInfo);
|
|
1029
|
-
for (const containedPlaceholder of containedPlaceholderSet) {
|
|
1030
|
-
if (placeholderInfluencingVersionSet.has(containedPlaceholder)) {
|
|
1031
|
-
continue;
|
|
1032
|
-
}
|
|
1033
|
-
const reference =
|
|
1034
|
-
placeholderToReferenceMap.get(containedPlaceholder);
|
|
1035
|
-
const referenceVersioningInfo =
|
|
1036
|
-
getReferenceVersioningInfo(reference);
|
|
1037
|
-
if (
|
|
1038
|
-
referenceVersioningInfo.type === "global" ||
|
|
1039
|
-
referenceVersioningInfo.type === "importmap"
|
|
1040
|
-
) {
|
|
1041
|
-
// when versioning is dynamic no need to take into account
|
|
1042
|
-
continue;
|
|
1043
|
-
}
|
|
1044
|
-
placeholderInfluencingVersionSet.add(containedPlaceholder);
|
|
1045
|
-
const referencedUrlInfo = reference.urlInfo;
|
|
1046
|
-
visitContainedPlaceholders(referencedUrlInfo);
|
|
1047
|
-
}
|
|
1048
|
-
};
|
|
1049
|
-
visitContainedPlaceholders(urlInfo);
|
|
1050
|
-
|
|
1051
|
-
const setOfUrlInfluencingVersion = new Set();
|
|
1052
|
-
for (const placeholderInfluencingVersion of placeholderInfluencingVersionSet) {
|
|
1053
|
-
const reference = placeholderToReferenceMap.get(
|
|
1054
|
-
placeholderInfluencingVersion,
|
|
1055
|
-
);
|
|
1056
|
-
const referencedUrlInfo = reference.urlInfo;
|
|
1057
|
-
setOfUrlInfluencingVersion.add(referencedUrlInfo);
|
|
1058
|
-
}
|
|
1059
|
-
return setOfUrlInfluencingVersion;
|
|
1060
|
-
};
|
|
1061
|
-
|
|
1062
|
-
for (const [
|
|
1063
|
-
contentOnlyUrlInfo,
|
|
1064
|
-
contentOnlyVersion,
|
|
1065
|
-
] of contentOnlyVersionMap) {
|
|
1066
|
-
const setOfUrlInfoInfluencingVersion =
|
|
1067
|
-
getSetOfUrlInfoInfluencingVersion(contentOnlyUrlInfo);
|
|
1068
|
-
const versionPartSet = new Set();
|
|
1069
|
-
versionPartSet.add(contentOnlyVersion);
|
|
1070
|
-
for (const urlInfoInfluencingVersion of setOfUrlInfoInfluencingVersion) {
|
|
1071
|
-
const otherUrlInfoContentVersion = contentOnlyVersionMap.get(
|
|
1072
|
-
urlInfoInfluencingVersion,
|
|
1073
|
-
);
|
|
1074
|
-
if (!otherUrlInfoContentVersion) {
|
|
1075
|
-
throw new Error(
|
|
1076
|
-
`cannot find content version for ${urlInfoInfluencingVersion.url} (used by ${contentOnlyUrlInfo.url})`,
|
|
1077
|
-
);
|
|
1078
|
-
}
|
|
1079
|
-
versionPartSet.add(otherUrlInfoContentVersion);
|
|
1080
|
-
}
|
|
1081
|
-
const version = generateVersion(versionPartSet, versionLength);
|
|
1082
|
-
versionMap.set(contentOnlyUrlInfo, version);
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
{
|
|
1087
|
-
// we should grab all the files inside this directory
|
|
1088
|
-
// they will influence his versioning
|
|
1089
|
-
for (const directoryUrlInfo of directoryUrlInfoSet) {
|
|
1090
|
-
const directoryUrl = directoryUrlInfo.url;
|
|
1091
|
-
// const urlInfoInsideThisDirectorySet = new Set();
|
|
1092
|
-
const versionsInfluencingThisDirectorySet = new Set();
|
|
1093
|
-
for (const [url, urlInfo] of finalKitchen.graph.urlInfoMap) {
|
|
1094
|
-
if (!urlIsInsideOf(url, directoryUrl)) {
|
|
1095
|
-
continue;
|
|
1096
|
-
}
|
|
1097
|
-
// ideally we should exclude eventual directories as the are redundant
|
|
1098
|
-
// with the file they contains
|
|
1099
|
-
const version = versionMap.get(urlInfo);
|
|
1100
|
-
if (version !== undefined) {
|
|
1101
|
-
versionsInfluencingThisDirectorySet.add(version);
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
const contentVersion =
|
|
1105
|
-
versionsInfluencingThisDirectorySet.size === 0
|
|
1106
|
-
? "empty"
|
|
1107
|
-
: generateVersion(
|
|
1108
|
-
versionsInfluencingThisDirectorySet,
|
|
1109
|
-
versionLength,
|
|
1110
|
-
);
|
|
1111
|
-
versionMap.set(directoryUrlInfo, contentVersion);
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
|
|
1116
|
-
const importMappings = {};
|
|
1117
|
-
const globalMappings = {};
|
|
1118
|
-
|
|
1119
|
-
const applyVersioningOnBuildSpecifier = (buildSpecifier, reference) => {
|
|
1120
|
-
if (!versioning) {
|
|
1121
|
-
return buildSpecifier;
|
|
1122
|
-
}
|
|
1123
|
-
const referenceVersioningInfo = getReferenceVersioningInfo(reference);
|
|
1124
|
-
if (referenceVersioningInfo.type === "not_versioned") {
|
|
1125
|
-
return buildSpecifier;
|
|
1126
|
-
}
|
|
1127
|
-
const version = versionMap.get(reference.urlInfo);
|
|
1128
|
-
if (version === undefined) {
|
|
1129
|
-
return buildSpecifier;
|
|
1130
|
-
}
|
|
1131
|
-
const buildSpecifierVersioned = injectVersionIntoBuildSpecifier({
|
|
1132
|
-
buildSpecifier,
|
|
1133
|
-
versioningMethod,
|
|
1134
|
-
version,
|
|
1135
|
-
});
|
|
1136
|
-
buildSpecifierToBuildSpecifierVersionedMap.set(
|
|
1137
|
-
buildSpecifier,
|
|
1138
|
-
buildSpecifierVersioned,
|
|
1139
|
-
);
|
|
1140
|
-
return referenceVersioningInfo.render(buildSpecifier);
|
|
1141
|
-
};
|
|
1142
|
-
const finishVersioning = async () => {
|
|
1143
|
-
{
|
|
1144
|
-
for (const [reference, versioningInfo] of referenceVersioningInfoMap) {
|
|
1145
|
-
if (versioningInfo.type === "global") {
|
|
1146
|
-
const urlInfo = reference.urlInfo;
|
|
1147
|
-
const buildUrl = urlInfoToBuildUrlMap.get(urlInfo);
|
|
1148
|
-
const buildSpecifier = buildUrlToBuildSpecifierMap.get(buildUrl);
|
|
1149
|
-
const buildSpecifierVersioned =
|
|
1150
|
-
buildSpecifierToBuildSpecifierVersionedMap.get(buildSpecifier);
|
|
1151
|
-
globalMappings[buildSpecifier] = buildSpecifierVersioned;
|
|
1152
|
-
}
|
|
1153
|
-
if (versioningInfo.type === "importmap") {
|
|
1154
|
-
const urlInfo = reference.urlInfo;
|
|
1155
|
-
const buildUrl = urlInfoToBuildUrlMap.get(urlInfo);
|
|
1156
|
-
const buildSpecifier = buildUrlToBuildSpecifierMap.get(buildUrl);
|
|
1157
|
-
const buildSpecifierVersioned =
|
|
1158
|
-
buildSpecifierToBuildSpecifierVersionedMap.get(buildSpecifier);
|
|
1159
|
-
importMappings[buildSpecifier] = buildSpecifierVersioned;
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
};
|
|
1164
|
-
|
|
1165
|
-
const getBuildGeneratedSpecifier = (urlInfo) => {
|
|
1166
|
-
const buildUrl = urlInfoToBuildUrlMap.get(urlInfo);
|
|
1167
|
-
const buildSpecifier = buildUrlToBuildSpecifierMap.get(buildUrl);
|
|
1168
|
-
const buildGeneratedSpecifier =
|
|
1169
|
-
buildSpecifierToBuildSpecifierVersionedMap.get(buildSpecifier) ||
|
|
1170
|
-
buildSpecifier;
|
|
1171
|
-
return buildGeneratedSpecifier;
|
|
1172
|
-
};
|
|
1173
|
-
|
|
1174
|
-
return {
|
|
1175
|
-
jsenvPluginMoveToBuildDirectory,
|
|
1176
|
-
applyBundling,
|
|
1177
|
-
|
|
1178
|
-
remapPlaceholder: (specifier) => {
|
|
1179
|
-
const reference = placeholderToReferenceMap.get(specifier);
|
|
1180
|
-
if (reference) {
|
|
1181
|
-
return reference.specifier;
|
|
1182
|
-
}
|
|
1183
|
-
return specifier;
|
|
1184
|
-
},
|
|
1185
|
-
|
|
1186
|
-
replacePlaceholders: async () => {
|
|
1187
|
-
if (versioning) {
|
|
1188
|
-
prepareVersioning();
|
|
1189
|
-
}
|
|
1190
|
-
const urlInfoSet = new Set();
|
|
1191
|
-
GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
|
|
1192
|
-
finalKitchen.graph.rootUrlInfo,
|
|
1193
|
-
(urlInfo) => {
|
|
1194
|
-
urlInfoSet.add(urlInfo);
|
|
1195
|
-
if (urlInfo.isEntryPoint) {
|
|
1196
|
-
generateReplacement(urlInfo.firstReference);
|
|
1197
|
-
}
|
|
1198
|
-
if (urlInfo.type === "sourcemap") {
|
|
1199
|
-
const { referenceFromOthersSet } = urlInfo;
|
|
1200
|
-
let lastRef;
|
|
1201
|
-
for (const ref of referenceFromOthersSet) {
|
|
1202
|
-
lastRef = ref;
|
|
1203
|
-
}
|
|
1204
|
-
generateReplacement(lastRef);
|
|
1205
|
-
}
|
|
1206
|
-
if (urlInfo.isInline) {
|
|
1207
|
-
generateReplacement(urlInfo.firstReference);
|
|
1208
|
-
}
|
|
1209
|
-
if (urlInfo.firstReference.type === "side_effect_file") {
|
|
1210
|
-
// side effect stuff must be generated too
|
|
1211
|
-
generateReplacement(urlInfo.firstReference);
|
|
1212
|
-
}
|
|
1213
|
-
if (mayUsePlaceholder(urlInfo)) {
|
|
1214
|
-
const contentBeforeReplace = urlInfo.content;
|
|
1215
|
-
const { content, sourcemap } = placeholderAPI.replaceAll(
|
|
1216
|
-
contentBeforeReplace,
|
|
1217
|
-
(placeholder) => {
|
|
1218
|
-
const reference = placeholderToReferenceMap.get(placeholder);
|
|
1219
|
-
const value = generateReplacement(reference);
|
|
1220
|
-
return value;
|
|
1221
|
-
},
|
|
1222
|
-
);
|
|
1223
|
-
urlInfo.mutateContent({ content, sourcemap });
|
|
1224
|
-
}
|
|
1225
|
-
},
|
|
1226
|
-
);
|
|
1227
|
-
referenceInSeparateContextSet.clear();
|
|
1228
|
-
if (versioning) {
|
|
1229
|
-
await finishVersioning();
|
|
1230
|
-
}
|
|
1231
|
-
const actions = [];
|
|
1232
|
-
const visitors = [];
|
|
1233
|
-
if (Object.keys(globalMappings).length > 0) {
|
|
1234
|
-
visitors.push((urlInfo) => {
|
|
1235
|
-
if (urlInfo.isRoot) {
|
|
1236
|
-
return;
|
|
1237
|
-
}
|
|
1238
|
-
if (!urlInfo.isEntryPoint) {
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
actions.push(async () => {
|
|
1242
|
-
await injectGlobalMappings(urlInfo, globalMappings);
|
|
1243
|
-
});
|
|
1244
|
-
});
|
|
1245
|
-
}
|
|
1246
|
-
{
|
|
1247
|
-
visitors.push((urlInfo) => {
|
|
1248
|
-
if (urlInfo.isRoot) {
|
|
1249
|
-
return;
|
|
1250
|
-
}
|
|
1251
|
-
if (!urlInfo.isEntryPoint) {
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
if (urlInfo.type !== "html") {
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
actions.push(async () => {
|
|
1259
|
-
await injectImportmapMappings(urlInfo, (topLevelMappings) => {
|
|
1260
|
-
if (!topLevelMappings) {
|
|
1261
|
-
return importMappings;
|
|
1262
|
-
}
|
|
1263
|
-
const topLevelMappingsToKeep = {};
|
|
1264
|
-
for (const topLevelMappingKey of Object.keys(topLevelMappings)) {
|
|
1265
|
-
const topLevelMappingValue =
|
|
1266
|
-
topLevelMappings[topLevelMappingKey];
|
|
1267
|
-
const urlInfo = finalKitchen.graph.getUrlInfo(
|
|
1268
|
-
`ignore:${topLevelMappingKey}`,
|
|
1269
|
-
);
|
|
1270
|
-
if (urlInfo) {
|
|
1271
|
-
topLevelMappingsToKeep[topLevelMappingKey] =
|
|
1272
|
-
topLevelMappingValue;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
return {
|
|
1276
|
-
...topLevelMappingsToKeep,
|
|
1277
|
-
...importMappings,
|
|
1278
|
-
};
|
|
1279
|
-
});
|
|
1280
|
-
});
|
|
1281
|
-
});
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
if (visitors.length) {
|
|
1285
|
-
GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
|
|
1286
|
-
for (const visitor of visitors) {
|
|
1287
|
-
visitor(urlInfo);
|
|
1288
|
-
}
|
|
1289
|
-
});
|
|
1290
|
-
if (actions.length) {
|
|
1291
|
-
await Promise.all(actions.map((action) => action()));
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
for (const urlInfo of urlInfoSet) {
|
|
1296
|
-
urlInfo.kitchen.urlInfoTransformer.applySourcemapOnContent(
|
|
1297
|
-
urlInfo,
|
|
1298
|
-
(source) => {
|
|
1299
|
-
const buildUrl = urlInfoToBuildUrlMap.get(urlInfo);
|
|
1300
|
-
if (buildUrl) {
|
|
1301
|
-
return urlToRelativeUrl(source, buildUrl);
|
|
1302
|
-
}
|
|
1303
|
-
return source;
|
|
1304
|
-
},
|
|
1305
|
-
);
|
|
1306
|
-
}
|
|
1307
|
-
urlInfoSet.clear();
|
|
1308
|
-
},
|
|
1309
|
-
|
|
1310
|
-
prepareResyncResourceHints: ({ registerHtmlRefine }) => {
|
|
1311
|
-
const hintToInjectMap = new Map();
|
|
1312
|
-
registerHtmlRefine((htmlAst, { registerHtmlMutation }) => {
|
|
1313
|
-
visitHtmlNodes(htmlAst, {
|
|
1314
|
-
link: (node) => {
|
|
1315
|
-
const href = getHtmlNodeAttribute(node, "href");
|
|
1316
|
-
if (href === undefined || href.startsWith("data:")) {
|
|
1317
|
-
return;
|
|
1318
|
-
}
|
|
1319
|
-
const rel = getHtmlNodeAttribute(node, "rel");
|
|
1320
|
-
const isResourceHint = [
|
|
1321
|
-
"preconnect",
|
|
1322
|
-
"dns-prefetch",
|
|
1323
|
-
"prefetch",
|
|
1324
|
-
"preload",
|
|
1325
|
-
"modulepreload",
|
|
1326
|
-
].includes(rel);
|
|
1327
|
-
if (!isResourceHint) {
|
|
1328
|
-
return;
|
|
1329
|
-
}
|
|
1330
|
-
const rawUrl = href;
|
|
1331
|
-
const finalUrl = internalRedirections.get(rawUrl) || rawUrl;
|
|
1332
|
-
const urlInfo = finalKitchen.graph.getUrlInfo(finalUrl);
|
|
1333
|
-
if (!urlInfo) {
|
|
1334
|
-
logger.warn(
|
|
1335
|
-
`${UNICODE.WARNING} remove resource hint because cannot find "${href}" in the graph`,
|
|
1336
|
-
);
|
|
1337
|
-
registerHtmlMutation(() => {
|
|
1338
|
-
removeHtmlNode(node);
|
|
1339
|
-
});
|
|
1340
|
-
return;
|
|
1341
|
-
}
|
|
1342
|
-
if (!urlInfo.isUsed()) {
|
|
1343
|
-
const rawUrlInfo = rawKitchen.graph.getUrlInfo(rawUrl);
|
|
1344
|
-
if (rawUrlInfo && rawUrlInfo.data.bundled) {
|
|
1345
|
-
logger.warn(
|
|
1346
|
-
`${UNICODE.WARNING} remove resource hint on "${href}" because it was bundled`,
|
|
1347
|
-
);
|
|
1348
|
-
registerHtmlMutation(() => {
|
|
1349
|
-
removeHtmlNode(node);
|
|
1350
|
-
});
|
|
1351
|
-
return;
|
|
1352
|
-
}
|
|
1353
|
-
logger.warn(
|
|
1354
|
-
`${UNICODE.WARNING} remove resource hint on "${href}" because it is not used anymore`,
|
|
1355
|
-
);
|
|
1356
|
-
registerHtmlMutation(() => {
|
|
1357
|
-
removeHtmlNode(node);
|
|
1358
|
-
});
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
const buildGeneratedSpecifier = getBuildGeneratedSpecifier(urlInfo);
|
|
1362
|
-
registerHtmlMutation(() => {
|
|
1363
|
-
setHtmlNodeAttributes(node, {
|
|
1364
|
-
href: buildGeneratedSpecifier,
|
|
1365
|
-
...(urlInfo.type === "js_classic"
|
|
1366
|
-
? { crossorigin: undefined }
|
|
1367
|
-
: {}),
|
|
1368
|
-
});
|
|
1369
|
-
});
|
|
1370
|
-
for (const referenceToOther of urlInfo.referenceToOthersSet) {
|
|
1371
|
-
if (referenceToOther.isWeak) {
|
|
1372
|
-
continue;
|
|
1373
|
-
}
|
|
1374
|
-
const referencedUrlInfo = referenceToOther.urlInfo;
|
|
1375
|
-
if (referencedUrlInfo.data.generatedToShareCode) {
|
|
1376
|
-
hintToInjectMap.set(referencedUrlInfo, { node });
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
},
|
|
1380
|
-
});
|
|
1381
|
-
for (const [referencedUrlInfo, { node }] of hintToInjectMap) {
|
|
1382
|
-
const buildGeneratedSpecifier =
|
|
1383
|
-
getBuildGeneratedSpecifier(referencedUrlInfo);
|
|
1384
|
-
const found = findHtmlNode(htmlAst, (htmlNode) => {
|
|
1385
|
-
return (
|
|
1386
|
-
htmlNode.nodeName === "link" &&
|
|
1387
|
-
getHtmlNodeAttribute(htmlNode, "href") === buildGeneratedSpecifier
|
|
1388
|
-
);
|
|
1389
|
-
});
|
|
1390
|
-
if (found) {
|
|
1391
|
-
continue;
|
|
1392
|
-
}
|
|
1393
|
-
registerHtmlMutation(() => {
|
|
1394
|
-
const nodeToInsert = createHtmlNode({
|
|
1395
|
-
tagName: "link",
|
|
1396
|
-
rel: getHtmlNodeAttribute(node, "rel"),
|
|
1397
|
-
href: buildGeneratedSpecifier,
|
|
1398
|
-
as: getHtmlNodeAttribute(node, "as"),
|
|
1399
|
-
type: getHtmlNodeAttribute(node, "type"),
|
|
1400
|
-
crossorigin: getHtmlNodeAttribute(node, "crossorigin"),
|
|
1401
|
-
});
|
|
1402
|
-
insertHtmlNodeAfter(nodeToInsert, node);
|
|
1403
|
-
});
|
|
1404
|
-
}
|
|
1405
|
-
});
|
|
1406
|
-
},
|
|
1407
|
-
|
|
1408
|
-
prepareServiceWorkerUrlInjection: () => {
|
|
1409
|
-
const serviceWorkerEntryUrlInfos = GRAPH_VISITOR.filter(
|
|
1410
|
-
finalKitchen.graph,
|
|
1411
|
-
(finalUrlInfo) => {
|
|
1412
|
-
return (
|
|
1413
|
-
finalUrlInfo.subtype === "service_worker" &&
|
|
1414
|
-
finalUrlInfo.isEntryPoint &&
|
|
1415
|
-
finalUrlInfo.isUsed()
|
|
1416
|
-
);
|
|
1417
|
-
},
|
|
1418
|
-
);
|
|
1419
|
-
if (serviceWorkerEntryUrlInfos.length === 0) {
|
|
1420
|
-
return null;
|
|
1421
|
-
}
|
|
1422
|
-
return async () => {
|
|
1423
|
-
const allResourcesFromJsenvBuild = {};
|
|
1424
|
-
GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
|
|
1425
|
-
finalKitchen.graph.rootUrlInfo,
|
|
1426
|
-
(urlInfo) => {
|
|
1427
|
-
if (!urlInfo.url.startsWith("file:")) {
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
|
-
if (urlInfo.isInline) {
|
|
1431
|
-
return;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
const buildUrl = urlInfoToBuildUrlMap.get(urlInfo);
|
|
1435
|
-
const buildSpecifier = buildUrlToBuildSpecifierMap.get(buildUrl);
|
|
1436
|
-
if (canUseVersionedUrl(urlInfo)) {
|
|
1437
|
-
const buildSpecifierVersioned = versioning
|
|
1438
|
-
? buildSpecifierToBuildSpecifierVersionedMap.get(buildSpecifier)
|
|
1439
|
-
: null;
|
|
1440
|
-
allResourcesFromJsenvBuild[buildSpecifier] = {
|
|
1441
|
-
version: versionMap.get(urlInfo),
|
|
1442
|
-
versionedUrl: buildSpecifierVersioned,
|
|
1443
|
-
};
|
|
1444
|
-
} else {
|
|
1445
|
-
// when url is not versioned we compute a "version" for that url anyway
|
|
1446
|
-
// so that service worker source still changes and navigator
|
|
1447
|
-
// detect there is a change
|
|
1448
|
-
allResourcesFromJsenvBuild[buildSpecifier] = {
|
|
1449
|
-
version: versionMap.get(urlInfo),
|
|
1450
|
-
};
|
|
1451
|
-
}
|
|
1452
|
-
},
|
|
1453
|
-
);
|
|
1454
|
-
for (const serviceWorkerEntryUrlInfo of serviceWorkerEntryUrlInfos) {
|
|
1455
|
-
const resourcesFromJsenvBuild = {
|
|
1456
|
-
...allResourcesFromJsenvBuild,
|
|
1457
|
-
};
|
|
1458
|
-
const serviceWorkerBuildUrl = urlInfoToBuildUrlMap.get(
|
|
1459
|
-
serviceWorkerEntryUrlInfo,
|
|
1460
|
-
);
|
|
1461
|
-
const serviceWorkerBuildSpecifier = buildUrlToBuildSpecifierMap.get(
|
|
1462
|
-
serviceWorkerBuildUrl,
|
|
1463
|
-
);
|
|
1464
|
-
delete resourcesFromJsenvBuild[serviceWorkerBuildSpecifier];
|
|
1465
|
-
await prependContent(serviceWorkerEntryUrlInfo, {
|
|
1466
|
-
type: "js_classic",
|
|
1467
|
-
content: `self.resourcesFromJsenvBuild = ${JSON.stringify(
|
|
1468
|
-
resourcesFromJsenvBuild,
|
|
1469
|
-
null,
|
|
1470
|
-
" ",
|
|
1471
|
-
)};\n`,
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
};
|
|
1475
|
-
},
|
|
1476
|
-
|
|
1477
|
-
getBuildInfo: () => {
|
|
1478
|
-
const buildManifest = {};
|
|
1479
|
-
const buildContents = {};
|
|
1480
|
-
const buildInlineRelativeUrlSet = new Set();
|
|
1481
|
-
GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
|
|
1482
|
-
finalKitchen.graph.rootUrlInfo,
|
|
1483
|
-
(urlInfo) => {
|
|
1484
|
-
const buildUrl = urlInfoToBuildUrlMap.get(urlInfo);
|
|
1485
|
-
if (!buildUrl) {
|
|
1486
|
-
return;
|
|
1487
|
-
}
|
|
1488
|
-
if (
|
|
1489
|
-
urlInfo.type === "asset" &&
|
|
1490
|
-
urlIsInsideOf(urlInfo.url, buildDirectoryUrl)
|
|
1491
|
-
) {
|
|
1492
|
-
return;
|
|
1493
|
-
}
|
|
1494
|
-
const buildSpecifier = buildUrlToBuildSpecifierMap.get(buildUrl);
|
|
1495
|
-
const buildSpecifierVersioned = versioning
|
|
1496
|
-
? buildSpecifierToBuildSpecifierVersionedMap.get(buildSpecifier)
|
|
1497
|
-
: null;
|
|
1498
|
-
const buildRelativeUrl = urlToRelativeUrl(
|
|
1499
|
-
buildUrl,
|
|
1500
|
-
buildDirectoryUrl,
|
|
1501
|
-
);
|
|
1502
|
-
let contentKey;
|
|
1503
|
-
// if to guard for html where versioned build specifier is not generated
|
|
1504
|
-
if (buildSpecifierVersioned) {
|
|
1505
|
-
const buildUrlVersioned = asBuildUrlVersioned({
|
|
1506
|
-
buildSpecifierVersioned,
|
|
1507
|
-
buildDirectoryUrl,
|
|
1508
|
-
});
|
|
1509
|
-
const buildRelativeUrlVersioned = urlToRelativeUrl(
|
|
1510
|
-
buildUrlVersioned,
|
|
1511
|
-
buildDirectoryUrl,
|
|
1512
|
-
);
|
|
1513
|
-
buildManifest[buildRelativeUrl] = buildRelativeUrlVersioned;
|
|
1514
|
-
contentKey = buildRelativeUrlVersioned;
|
|
1515
|
-
} else {
|
|
1516
|
-
contentKey = buildRelativeUrl;
|
|
1517
|
-
}
|
|
1518
|
-
if (urlInfo.type !== "directory") {
|
|
1519
|
-
buildContents[contentKey] = urlInfo.content;
|
|
1520
|
-
}
|
|
1521
|
-
if (urlInfo.isInline) {
|
|
1522
|
-
buildInlineRelativeUrlSet.add(buildRelativeUrl);
|
|
1523
|
-
}
|
|
1524
|
-
},
|
|
1525
|
-
);
|
|
1526
|
-
const buildFileContents = {};
|
|
1527
|
-
const buildInlineContents = {};
|
|
1528
|
-
Object.keys(buildContents)
|
|
1529
|
-
.sort((a, b) => comparePathnames(a, b))
|
|
1530
|
-
.forEach((buildRelativeUrl) => {
|
|
1531
|
-
if (buildInlineRelativeUrlSet.has(buildRelativeUrl)) {
|
|
1532
|
-
buildInlineContents[buildRelativeUrl] =
|
|
1533
|
-
buildContents[buildRelativeUrl];
|
|
1534
|
-
} else {
|
|
1535
|
-
buildFileContents[buildRelativeUrl] =
|
|
1536
|
-
buildContents[buildRelativeUrl];
|
|
1537
|
-
}
|
|
1538
|
-
});
|
|
1539
|
-
|
|
1540
|
-
return { buildFileContents, buildInlineContents, buildManifest };
|
|
1541
|
-
},
|
|
1542
|
-
};
|
|
1543
|
-
};
|
|
1544
|
-
|
|
1545
|
-
const findRawUrlInfoWhenInline = (reference, rawKitchen) => {
|
|
1546
|
-
const rawUrlInfo = GRAPH_VISITOR.find(
|
|
1547
|
-
rawKitchen.graph,
|
|
1548
|
-
(rawUrlInfoCandidate) => {
|
|
1549
|
-
const { inlineUrlSite } = rawUrlInfoCandidate;
|
|
1550
|
-
if (!inlineUrlSite) {
|
|
1551
|
-
return false;
|
|
1552
|
-
}
|
|
1553
|
-
if (
|
|
1554
|
-
inlineUrlSite.url === reference.ownerUrlInfo.url &&
|
|
1555
|
-
inlineUrlSite.line === reference.specifierLine &&
|
|
1556
|
-
inlineUrlSite.column === reference.specifierColumn &&
|
|
1557
|
-
rawUrlInfoCandidate.contentType === reference.contentType
|
|
1558
|
-
) {
|
|
1559
|
-
return true;
|
|
1560
|
-
}
|
|
1561
|
-
if (rawUrlInfoCandidate.content === reference.content) {
|
|
1562
|
-
return true;
|
|
1563
|
-
}
|
|
1564
|
-
if (rawUrlInfoCandidate.originalContent === reference.content) {
|
|
1565
|
-
return true;
|
|
1566
|
-
}
|
|
1567
|
-
return false;
|
|
1568
|
-
},
|
|
1569
|
-
);
|
|
1570
|
-
return rawUrlInfo;
|
|
1571
|
-
};
|
|
1572
|
-
|
|
1573
|
-
// see https://github.com/rollup/rollup/blob/ce453507ab8457dd1ea3909d8dd7b117b2d14fab/src/utils/hashPlaceholders.ts#L1
|
|
1574
|
-
// see also "New hashing algorithm that "fixes (nearly) everything"
|
|
1575
|
-
// at https://github.com/rollup/rollup/pull/4543
|
|
1576
|
-
const placeholderLeft = "!~{";
|
|
1577
|
-
const placeholderRight = "}~";
|
|
1578
|
-
const placeholderOverhead = placeholderLeft.length + placeholderRight.length;
|
|
1579
|
-
|
|
1580
|
-
const createPlaceholderAPI = ({ length }) => {
|
|
1581
|
-
const chars =
|
|
1582
|
-
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
|
|
1583
|
-
const toBase64 = (value) => {
|
|
1584
|
-
let outString = "";
|
|
1585
|
-
do {
|
|
1586
|
-
const currentDigit = value % 64;
|
|
1587
|
-
value = (value / 64) | 0;
|
|
1588
|
-
outString = chars[currentDigit] + outString;
|
|
1589
|
-
} while (value !== 0);
|
|
1590
|
-
return outString;
|
|
1591
|
-
};
|
|
1592
|
-
|
|
1593
|
-
let nextIndex = 0;
|
|
1594
|
-
const generate = () => {
|
|
1595
|
-
nextIndex++;
|
|
1596
|
-
const id = toBase64(nextIndex);
|
|
1597
|
-
let placeholder = placeholderLeft;
|
|
1598
|
-
placeholder += id.padStart(length - placeholderOverhead, "0");
|
|
1599
|
-
placeholder += placeholderRight;
|
|
1600
|
-
return placeholder;
|
|
1601
|
-
};
|
|
1602
|
-
|
|
1603
|
-
const replaceFirst = (code, value) => {
|
|
1604
|
-
let replaced = false;
|
|
1605
|
-
return code.replace(PLACEHOLDER_REGEX, (match) => {
|
|
1606
|
-
if (replaced) return match;
|
|
1607
|
-
replaced = true;
|
|
1608
|
-
return value;
|
|
1609
|
-
});
|
|
1610
|
-
};
|
|
1611
|
-
|
|
1612
|
-
const extractFirst = (string) => {
|
|
1613
|
-
const match = string.match(PLACEHOLDER_REGEX);
|
|
1614
|
-
return match ? match[0] : null;
|
|
1615
|
-
};
|
|
1616
|
-
|
|
1617
|
-
const defaultPlaceholder = `${placeholderLeft}${"0".repeat(
|
|
1618
|
-
length - placeholderOverhead,
|
|
1619
|
-
)}${placeholderRight}`;
|
|
1620
|
-
const replaceWithDefault = (code, onPlaceholder) => {
|
|
1621
|
-
const transformedCode = code.replace(PLACEHOLDER_REGEX, (placeholder) => {
|
|
1622
|
-
onPlaceholder(placeholder);
|
|
1623
|
-
return defaultPlaceholder;
|
|
1624
|
-
});
|
|
1625
|
-
return transformedCode;
|
|
1626
|
-
};
|
|
1627
|
-
|
|
1628
|
-
const PLACEHOLDER_REGEX = new RegExp(
|
|
1629
|
-
`${escapeRegexpSpecialChars(placeholderLeft)}[0-9a-zA-Z_$]{1,${
|
|
1630
|
-
length - placeholderOverhead
|
|
1631
|
-
}}${escapeRegexpSpecialChars(placeholderRight)}`,
|
|
1632
|
-
"g",
|
|
1633
|
-
);
|
|
1634
|
-
|
|
1635
|
-
const markAsCode = (string) => {
|
|
1636
|
-
return {
|
|
1637
|
-
__isCode__: true,
|
|
1638
|
-
toString: () => string,
|
|
1639
|
-
value: string,
|
|
1640
|
-
};
|
|
1641
|
-
};
|
|
1642
|
-
|
|
1643
|
-
const replaceAll = (string, replacer) => {
|
|
1644
|
-
const magicSource = createMagicSource(string);
|
|
1645
|
-
|
|
1646
|
-
string.replace(PLACEHOLDER_REGEX, (placeholder, index) => {
|
|
1647
|
-
const replacement = replacer(placeholder, index);
|
|
1648
|
-
if (!replacement) {
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
let value;
|
|
1652
|
-
let isCode = false;
|
|
1653
|
-
if (replacement && replacement.__isCode__) {
|
|
1654
|
-
value = replacement.value;
|
|
1655
|
-
isCode = true;
|
|
1656
|
-
} else {
|
|
1657
|
-
value = replacement;
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
let start = index;
|
|
1661
|
-
let end = start + placeholder.length;
|
|
1662
|
-
if (
|
|
1663
|
-
isCode &&
|
|
1664
|
-
// when specifier is wrapper by quotes
|
|
1665
|
-
// we remove the quotes to transform the string
|
|
1666
|
-
// into code that will be executed
|
|
1667
|
-
isWrappedByQuote(string, start, end)
|
|
1668
|
-
) {
|
|
1669
|
-
start = start - 1;
|
|
1670
|
-
end = end + 1;
|
|
1671
|
-
}
|
|
1672
|
-
magicSource.replace({
|
|
1673
|
-
start,
|
|
1674
|
-
end,
|
|
1675
|
-
replacement: value,
|
|
1676
|
-
});
|
|
1677
|
-
});
|
|
1678
|
-
return magicSource.toContentAndSourcemap();
|
|
1679
|
-
};
|
|
1680
|
-
|
|
1681
|
-
return {
|
|
1682
|
-
generate,
|
|
1683
|
-
replaceFirst,
|
|
1684
|
-
replaceAll,
|
|
1685
|
-
extractFirst,
|
|
1686
|
-
markAsCode,
|
|
1687
|
-
replaceWithDefault,
|
|
1688
|
-
};
|
|
1689
|
-
};
|
|
1690
|
-
|
|
1691
|
-
const mayUsePlaceholder = (urlInfo) => {
|
|
1692
|
-
if (urlInfo.referenceToOthersSet.size === 0) {
|
|
1693
|
-
return false;
|
|
1694
|
-
}
|
|
1695
|
-
if (!CONTENT_TYPE.isTextual(urlInfo.contentType)) {
|
|
1696
|
-
return false;
|
|
1697
|
-
}
|
|
1698
|
-
return true;
|
|
1699
|
-
};
|
|
1700
|
-
|
|
1701
|
-
const isWrappedByQuote = (content, start, end) => {
|
|
1702
|
-
const previousChar = content[start - 1];
|
|
1703
|
-
const nextChar = content[end];
|
|
1704
|
-
if (previousChar === `'` && nextChar === `'`) {
|
|
1705
|
-
return true;
|
|
1706
|
-
}
|
|
1707
|
-
if (previousChar === `"` && nextChar === `"`) {
|
|
1708
|
-
return true;
|
|
1709
|
-
}
|
|
1710
|
-
if (previousChar === "`" && nextChar === "`") {
|
|
1711
|
-
return true;
|
|
1712
|
-
}
|
|
1713
|
-
return false;
|
|
1714
|
-
};
|
|
1715
|
-
|
|
1716
|
-
// https://github.com/rollup/rollup/blob/19e50af3099c2f627451a45a84e2fa90d20246d5/src/utils/FileEmitter.ts#L47
|
|
1717
|
-
// https://github.com/rollup/rollup/blob/5a5391971d695c808eed0c5d7d2c6ccb594fc689/src/Chunk.ts#L870
|
|
1718
|
-
const generateVersion = (parts, length) => {
|
|
1719
|
-
const hash = createHash("sha256");
|
|
1720
|
-
parts.forEach((part) => {
|
|
1721
|
-
hash.update(part);
|
|
1722
|
-
});
|
|
1723
|
-
return hash.digest("hex").slice(0, length);
|
|
1724
|
-
};
|
|
1725
|
-
|
|
1726
|
-
const injectVersionIntoBuildSpecifier = ({
|
|
1727
|
-
buildSpecifier,
|
|
1728
|
-
version,
|
|
1729
|
-
versioningMethod,
|
|
1730
|
-
}) => {
|
|
1731
|
-
if (versioningMethod === "search_param") {
|
|
1732
|
-
return injectQueryParamIntoSpecifierWithoutEncoding(
|
|
1733
|
-
buildSpecifier,
|
|
1734
|
-
"v",
|
|
1735
|
-
version,
|
|
1736
|
-
);
|
|
1737
|
-
}
|
|
1738
|
-
return renderUrlOrRelativeUrlFilename(
|
|
1739
|
-
buildSpecifier,
|
|
1740
|
-
({ basename, extension }) => {
|
|
1741
|
-
return `${basename}-${version}${extension}`;
|
|
1742
|
-
},
|
|
1743
|
-
);
|
|
1744
|
-
};
|
|
1745
|
-
|
|
1746
|
-
const asBuildUrlVersioned = ({
|
|
1747
|
-
buildSpecifierVersioned,
|
|
1748
|
-
buildDirectoryUrl,
|
|
1749
|
-
}) => {
|
|
1750
|
-
if (buildSpecifierVersioned[0] === "/") {
|
|
1751
|
-
return new URL(buildSpecifierVersioned.slice(1), buildDirectoryUrl).href;
|
|
1752
|
-
}
|
|
1753
|
-
const buildUrl = new URL(buildSpecifierVersioned, buildDirectoryUrl).href;
|
|
1754
|
-
if (buildUrl.startsWith(buildDirectoryUrl)) {
|
|
1755
|
-
return buildUrl;
|
|
1756
|
-
}
|
|
1757
|
-
// it's likely "base" parameter was set to an url origin like "https://cdn.example.com"
|
|
1758
|
-
// let's move url to build directory
|
|
1759
|
-
const { pathname, search, hash } = new URL(buildSpecifierVersioned);
|
|
1760
|
-
return `${buildDirectoryUrl}${pathname}${search}${hash}`;
|
|
1761
|
-
};
|
|
1762
|
-
|
|
1763
|
-
const ensureUnixLineBreaks = (stringOrBuffer) => {
|
|
1764
|
-
if (typeof stringOrBuffer === "string") {
|
|
1765
|
-
const stringWithLinuxBreaks = stringOrBuffer.replace(/\r\n/g, "\n");
|
|
1766
|
-
return stringWithLinuxBreaks;
|
|
1767
|
-
}
|
|
1768
|
-
return ensureUnixLineBreaksOnBuffer(stringOrBuffer);
|
|
1769
|
-
};
|
|
1770
|
-
|
|
1771
|
-
// https://github.com/nodejs/help/issues/1738#issuecomment-458460503
|
|
1772
|
-
const ensureUnixLineBreaksOnBuffer = (buffer) => {
|
|
1773
|
-
const int32Array = new Int32Array(buffer, 0, buffer.length);
|
|
1774
|
-
const int32ArrayWithLineBreaksNormalized = int32Array.filter(
|
|
1775
|
-
(element, index, typedArray) => {
|
|
1776
|
-
if (element === 0x0d) {
|
|
1777
|
-
if (typedArray[index + 1] === 0x0a) {
|
|
1778
|
-
// Windows -> Unix
|
|
1779
|
-
return false;
|
|
1780
|
-
}
|
|
1781
|
-
// Mac OS -> Unix
|
|
1782
|
-
typedArray[index] = 0x0a;
|
|
1783
|
-
}
|
|
1784
|
-
return true;
|
|
1785
|
-
},
|
|
1786
|
-
);
|
|
1787
|
-
return Buffer.from(int32ArrayWithLineBreaksNormalized);
|
|
1788
|
-
};
|
|
1789
|
-
|
|
1790
|
-
const jsenvPluginLineBreakNormalization = () => {
|
|
1791
|
-
return {
|
|
1792
|
-
name: "jsenv:line_break_normalizer",
|
|
1793
|
-
appliesDuring: "build",
|
|
1794
|
-
transformUrlContent: (urlInfo) => {
|
|
1795
|
-
if (CONTENT_TYPE.isTextual(urlInfo.contentType)) {
|
|
1796
|
-
return ensureUnixLineBreaks(urlInfo.content);
|
|
1797
|
-
}
|
|
1798
|
-
return null;
|
|
1799
|
-
},
|
|
1800
|
-
};
|
|
1801
|
-
};
|
|
1802
|
-
|
|
1803
|
-
const jsenvPluginSubbuilds = (
|
|
1804
|
-
subBuildParamsArray,
|
|
1805
|
-
{ parentBuildParams, onCustomBuildDirectory, buildStart },
|
|
1806
|
-
) => {
|
|
1807
|
-
if (subBuildParamsArray.length === 0) {
|
|
1808
|
-
return [];
|
|
1809
|
-
}
|
|
1810
|
-
return subBuildParamsArray.map((subBuildParams, index) => {
|
|
1811
|
-
const defaultChildBuildParams = {};
|
|
1812
|
-
const childBuildParams = {
|
|
1813
|
-
...parentBuildParams,
|
|
1814
|
-
logs: {
|
|
1815
|
-
level: "warn",
|
|
1816
|
-
disabled: true,
|
|
1817
|
-
},
|
|
1818
|
-
...defaultChildBuildParams,
|
|
1819
|
-
...subBuildParams,
|
|
1820
|
-
};
|
|
1821
|
-
const subBuildDirectoryUrl = subBuildParams.buildDirectoryUrl;
|
|
1822
|
-
if (subBuildDirectoryUrl) {
|
|
1823
|
-
const subBuildRelativeUrl = urlToRelativeUrl(
|
|
1824
|
-
subBuildDirectoryUrl,
|
|
1825
|
-
parentBuildParams.buildDirectoryUrl,
|
|
1826
|
-
);
|
|
1827
|
-
const subbuildRuntimeCompat =
|
|
1828
|
-
childBuildParams.runtimeCompat || defaultRuntimeCompat;
|
|
1829
|
-
const subbuildBase =
|
|
1830
|
-
subBuildParams.base || getDefaultBase(subbuildRuntimeCompat);
|
|
1831
|
-
childBuildParams.base = `${subbuildBase}${subBuildRelativeUrl}`;
|
|
1832
|
-
onCustomBuildDirectory(subBuildRelativeUrl);
|
|
1833
|
-
}
|
|
1834
|
-
const buildPromise = buildStart(childBuildParams, index);
|
|
1835
|
-
const entryPointBuildUrlMap = new Map();
|
|
1836
|
-
const entryPointSourceUrlSet = new Set();
|
|
1837
|
-
const entryPointBuildUrlSet = new Set();
|
|
1838
|
-
const childBuildEntryPoints = childBuildParams.entryPoints;
|
|
1839
|
-
for (const key of Object.keys(childBuildEntryPoints)) {
|
|
1840
|
-
const entryPointUrl = new URL(key, childBuildParams.sourceDirectoryUrl)
|
|
1841
|
-
.href;
|
|
1842
|
-
const entryPointBuildUrl = new URL(
|
|
1843
|
-
childBuildEntryPoints[key],
|
|
1844
|
-
childBuildParams.buildDirectoryUrl,
|
|
1845
|
-
).href;
|
|
1846
|
-
entryPointBuildUrlMap.set(entryPointUrl, entryPointBuildUrl);
|
|
1847
|
-
entryPointSourceUrlSet.add(entryPointUrl);
|
|
1848
|
-
entryPointBuildUrlSet.add(entryPointBuildUrl);
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
return {
|
|
1852
|
-
name: `jsenv:subbuild_${index}`,
|
|
1853
|
-
redirectReference: (reference) => {
|
|
1854
|
-
const entryPointBuildUrl = entryPointBuildUrlMap.get(reference.url);
|
|
1855
|
-
if (!entryPointBuildUrl) {
|
|
1856
|
-
return null;
|
|
1857
|
-
}
|
|
1858
|
-
return entryPointBuildUrl;
|
|
1859
|
-
},
|
|
1860
|
-
fetchUrlContent: async (urlInfo) => {
|
|
1861
|
-
if (!entryPointBuildUrlSet.has(urlInfo.url)) {
|
|
1862
|
-
return;
|
|
1863
|
-
}
|
|
1864
|
-
await buildPromise;
|
|
1865
|
-
urlInfo.typeHint = "asset"; // this ensure the rest of jsenv do not scan or modify the content of this file
|
|
1866
|
-
},
|
|
1867
|
-
};
|
|
1868
|
-
});
|
|
1869
|
-
};
|
|
1870
|
-
|
|
1871
|
-
/*
|
|
1872
|
-
* Build is split in 3 steps:
|
|
1873
|
-
* 1. craft
|
|
1874
|
-
* 2. shape
|
|
1875
|
-
* 3. refine
|
|
1876
|
-
*
|
|
1877
|
-
* craft: prepare all the materials
|
|
1878
|
-
* - resolve, fetch and transform all source files into "rawKitchen.graph"
|
|
1879
|
-
* shape: this step can drastically change url content and their relationships
|
|
1880
|
-
* - bundling
|
|
1881
|
-
* - optimizations (minification)
|
|
1882
|
-
* refine: perform minor changes on the url contents
|
|
1883
|
-
* - cleaning html
|
|
1884
|
-
* - url versioning
|
|
1885
|
-
* - ressource hints
|
|
1886
|
-
* - injecting urls into service workers
|
|
1887
|
-
*/
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
/**
|
|
1891
|
-
* Generate an optimized version of source files into a directory.
|
|
1892
|
-
*
|
|
1893
|
-
* @param {Object} params
|
|
1894
|
-
* @param {string|url} params.sourceDirectoryUrl
|
|
1895
|
-
* Directory containing source files
|
|
1896
|
-
* @param {string|url} params.buildDirectoryUrl
|
|
1897
|
-
* Directory where optimized files will be written
|
|
1898
|
-
* @param {object} params.entryPoints
|
|
1899
|
-
* Object where keys are paths to source files and values are their future name in the build directory.
|
|
1900
|
-
* Keys are relative to sourceDirectoryUrl
|
|
1901
|
-
* @param {object} params.runtimeCompat
|
|
1902
|
-
* Code generated will be compatible with these runtimes
|
|
1903
|
-
* @param {string} [params.assetsDirectory=""]
|
|
1904
|
-
* Directory where asset files will be written
|
|
1905
|
-
* @param {string|url} [params.base=""]
|
|
1906
|
-
* Urls in build file contents will be prefixed with this string
|
|
1907
|
-
* @param {boolean|object} [params.bundling=true]
|
|
1908
|
-
* Reduce number of files written in the build directory
|
|
1909
|
-
* @param {boolean|object} [params.minification=true]
|
|
1910
|
-
* Minify the content of files written into the build directory
|
|
1911
|
-
* @param {boolean} [params.versioning=true]
|
|
1912
|
-
* Use versioning on files written in the build directory
|
|
1913
|
-
* @param {('search_param'|'filename')} [params.versioningMethod="search_param"]
|
|
1914
|
-
* Controls how url are versioned in the build directory
|
|
1915
|
-
* @param {('none'|'inline'|'file'|'programmatic')} [params.sourcemaps="none"]
|
|
1916
|
-
* Generate sourcemaps in the build directory
|
|
1917
|
-
* @param {('error'|'copy'|'preserve')|function} [params.directoryReferenceEffect="error"]
|
|
1918
|
-
* What to do when a reference leads to a directory on the filesystem
|
|
1919
|
-
* @return {Promise<Object>} buildReturnValue
|
|
1920
|
-
* @return {Promise<Object>} buildReturnValue.buildInlineContents
|
|
1921
|
-
* Contains content that is inline into build files
|
|
1922
|
-
* @return {Promise<Object>} buildReturnValue.buildManifest
|
|
1923
|
-
* Map build file paths without versioning to versioned file paths
|
|
1924
|
-
*/
|
|
1925
|
-
const build = async ({
|
|
1926
|
-
signal = new AbortController().signal,
|
|
1927
|
-
handleSIGINT = true,
|
|
1928
|
-
logs = logsDefault,
|
|
1929
|
-
sourceDirectoryUrl,
|
|
1930
|
-
buildDirectoryUrl,
|
|
1931
|
-
entryPoints = {},
|
|
1932
|
-
assetsDirectory = "",
|
|
1933
|
-
runtimeCompat = defaultRuntimeCompat,
|
|
1934
|
-
base = getDefaultBase(runtimeCompat),
|
|
1935
|
-
ignore,
|
|
1936
|
-
|
|
1937
|
-
subbuilds = [],
|
|
1938
|
-
plugins = [],
|
|
1939
|
-
referenceAnalysis = {},
|
|
1940
|
-
nodeEsmResolution,
|
|
1941
|
-
magicExtensions,
|
|
1942
|
-
magicDirectoryIndex,
|
|
1943
|
-
directoryReferenceEffect,
|
|
1944
|
-
scenarioPlaceholders,
|
|
1945
|
-
injections,
|
|
1946
|
-
transpilation = {},
|
|
1947
|
-
bundling = true,
|
|
1948
|
-
minification = !runtimeCompat.node,
|
|
1949
|
-
versioning = !runtimeCompat.node,
|
|
1950
|
-
versioningMethod = "search_param", // "filename", "search_param"
|
|
1951
|
-
versioningViaImportmap = true,
|
|
1952
|
-
versionLength = 8,
|
|
1953
|
-
lineBreakNormalization = process.platform === "win32",
|
|
1954
|
-
|
|
1955
|
-
sourceFilesConfig = {},
|
|
1956
|
-
cooldownBetweenFileEvents,
|
|
1957
|
-
watch = false,
|
|
1958
|
-
http = false,
|
|
1959
|
-
|
|
1960
|
-
buildDirectoryCleanPatterns = {
|
|
1961
|
-
"**/*": true,
|
|
1962
|
-
},
|
|
1963
|
-
sourcemaps = "none",
|
|
1964
|
-
sourcemapsSourcesContent,
|
|
1965
|
-
writeOnFileSystem = true,
|
|
1966
|
-
outDirectoryUrl,
|
|
1967
|
-
assetManifest = versioningMethod === "filename",
|
|
1968
|
-
assetManifestFileRelativeUrl = "asset-manifest.json",
|
|
1969
|
-
returnBuildInlineContents,
|
|
1970
|
-
returnBuildManifest,
|
|
1971
|
-
...rest
|
|
1972
|
-
}) => {
|
|
1973
|
-
// param validation
|
|
1974
|
-
{
|
|
1975
|
-
const unexpectedParamNames = Object.keys(rest);
|
|
1976
|
-
if (unexpectedParamNames.length > 0) {
|
|
1977
|
-
throw new TypeError(
|
|
1978
|
-
`${unexpectedParamNames.join(",")}: there is no such param`,
|
|
1979
|
-
);
|
|
1980
|
-
}
|
|
1981
|
-
// logs
|
|
1982
|
-
{
|
|
1983
|
-
if (typeof logs !== "object") {
|
|
1984
|
-
throw new TypeError(`logs must be an object, got ${logs}`);
|
|
1985
|
-
}
|
|
1986
|
-
const unexpectedLogsKeys = Object.keys(logs).filter(
|
|
1987
|
-
(key) => !Object.hasOwn(logsDefault, key),
|
|
1988
|
-
);
|
|
1989
|
-
if (unexpectedLogsKeys.length > 0) {
|
|
1990
|
-
throw new TypeError(
|
|
1991
|
-
`${unexpectedLogsKeys.join(",")}: no such key on logs`,
|
|
1992
|
-
);
|
|
1993
|
-
}
|
|
1994
|
-
logs = { ...logsDefault, ...logs };
|
|
1995
|
-
}
|
|
1996
|
-
sourceDirectoryUrl = assertAndNormalizeDirectoryUrl(
|
|
1997
|
-
sourceDirectoryUrl,
|
|
1998
|
-
"sourceDirectoryUrl",
|
|
1999
|
-
);
|
|
2000
|
-
buildDirectoryUrl = assertAndNormalizeDirectoryUrl(
|
|
2001
|
-
buildDirectoryUrl,
|
|
2002
|
-
"buildDirectoryUrl",
|
|
2003
|
-
);
|
|
2004
|
-
if (outDirectoryUrl === undefined) {
|
|
2005
|
-
if (
|
|
2006
|
-
process.env.CAPTURING_SIDE_EFFECTS ||
|
|
2007
|
-
(false)
|
|
2008
|
-
) {
|
|
2009
|
-
outDirectoryUrl = new URL("../.jsenv_b/", sourceDirectoryUrl);
|
|
2010
|
-
} else {
|
|
2011
|
-
const packageDirectoryUrl = lookupPackageDirectory(sourceDirectoryUrl);
|
|
2012
|
-
if (packageDirectoryUrl) {
|
|
2013
|
-
outDirectoryUrl = `${packageDirectoryUrl}.jsenv/`;
|
|
2014
|
-
}
|
|
2015
|
-
}
|
|
2016
|
-
} else if (outDirectoryUrl !== null && outDirectoryUrl !== false) {
|
|
2017
|
-
outDirectoryUrl = assertAndNormalizeDirectoryUrl(
|
|
2018
|
-
outDirectoryUrl,
|
|
2019
|
-
"outDirectoryUrl",
|
|
2020
|
-
);
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
if (typeof entryPoints !== "object" || entryPoints === null) {
|
|
2024
|
-
throw new TypeError(`entryPoints must be an object, got ${entryPoints}`);
|
|
2025
|
-
}
|
|
2026
|
-
const keys = Object.keys(entryPoints);
|
|
2027
|
-
keys.forEach((key) => {
|
|
2028
|
-
if (!key.startsWith("./")) {
|
|
2029
|
-
throw new TypeError(
|
|
2030
|
-
`entryPoints keys must start with "./", found ${key}`,
|
|
2031
|
-
);
|
|
2032
|
-
}
|
|
2033
|
-
const value = entryPoints[key];
|
|
2034
|
-
if (typeof value !== "string") {
|
|
2035
|
-
throw new TypeError(
|
|
2036
|
-
`entryPoints values must be strings, found "${value}" on key "${key}"`,
|
|
2037
|
-
);
|
|
2038
|
-
}
|
|
2039
|
-
if (value.includes("/")) {
|
|
2040
|
-
throw new TypeError(
|
|
2041
|
-
`entryPoints values must be plain strings (no "/"), found "${value}" on key "${key}"`,
|
|
2042
|
-
);
|
|
2043
|
-
}
|
|
2044
|
-
});
|
|
2045
|
-
if (!["filename", "search_param"].includes(versioningMethod)) {
|
|
2046
|
-
throw new TypeError(
|
|
2047
|
-
`versioningMethod must be "filename" or "search_param", got ${versioning}`,
|
|
2048
|
-
);
|
|
2049
|
-
}
|
|
2050
|
-
if (bundling === true) {
|
|
2051
|
-
bundling = {};
|
|
2052
|
-
}
|
|
2053
|
-
if (minification === true) {
|
|
2054
|
-
minification = {};
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
if (assetsDirectory && assetsDirectory[assetsDirectory.length - 1] !== "/") {
|
|
2059
|
-
assetsDirectory = `${assetsDirectory}/`;
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
const operation = Abort.startOperation();
|
|
2063
|
-
operation.addAbortSignal(signal);
|
|
2064
|
-
if (handleSIGINT) {
|
|
2065
|
-
operation.addAbortSource((abort) => {
|
|
2066
|
-
return raceProcessTeardownEvents(
|
|
2067
|
-
{
|
|
2068
|
-
SIGINT: true,
|
|
2069
|
-
},
|
|
2070
|
-
abort,
|
|
2071
|
-
);
|
|
2072
|
-
});
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
const runBuild = async ({ signal, logLevel }) => {
|
|
2076
|
-
const logger = createLogger({ logLevel });
|
|
2077
|
-
const createBuildTask = (label) => {
|
|
2078
|
-
return createTaskLog(label, {
|
|
2079
|
-
disabled:
|
|
2080
|
-
logs.disabled || (!logger.levels.debug && !logger.levels.info),
|
|
2081
|
-
animated: logs.animation && !logger.levels.debug,
|
|
2082
|
-
});
|
|
2083
|
-
};
|
|
2084
|
-
|
|
2085
|
-
const buildOperation = Abort.startOperation();
|
|
2086
|
-
buildOperation.addAbortSignal(signal);
|
|
2087
|
-
const entryPointKeys = Object.keys(entryPoints);
|
|
2088
|
-
if (entryPointKeys.length === 1) {
|
|
2089
|
-
logger.info(`
|
|
2090
|
-
build "${entryPointKeys[0]}"`);
|
|
2091
|
-
} else {
|
|
2092
|
-
logger.info(`
|
|
2093
|
-
build ${entryPointKeys.length} entry points`);
|
|
2094
|
-
}
|
|
2095
|
-
let explicitJsModuleConversion = false;
|
|
2096
|
-
for (const entryPointKey of entryPointKeys) {
|
|
2097
|
-
if (entryPointKey.includes("?js_module_fallback")) {
|
|
2098
|
-
explicitJsModuleConversion = true;
|
|
2099
|
-
break;
|
|
2100
|
-
}
|
|
2101
|
-
if (entryPointKey.includes("?as_js_classic")) {
|
|
2102
|
-
explicitJsModuleConversion = true;
|
|
2103
|
-
break;
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
const entryUrls = [];
|
|
2107
|
-
const contextSharedDuringBuild = {
|
|
2108
|
-
buildStep: "craft",
|
|
2109
|
-
buildDirectoryUrl,
|
|
2110
|
-
assetsDirectory,
|
|
2111
|
-
versioning,
|
|
2112
|
-
versioningViaImportmap,
|
|
2113
|
-
};
|
|
2114
|
-
const rawKitchen = createKitchen({
|
|
2115
|
-
signal,
|
|
2116
|
-
logLevel: logs.level,
|
|
2117
|
-
rootDirectoryUrl: sourceDirectoryUrl,
|
|
2118
|
-
ignore,
|
|
2119
|
-
// during first pass (craft) we keep "ignore:" when a reference is ignored
|
|
2120
|
-
// so that the second pass (shape) properly ignore those urls
|
|
2121
|
-
ignoreProtocol: "keep",
|
|
2122
|
-
build: true,
|
|
2123
|
-
runtimeCompat,
|
|
2124
|
-
initialContext: contextSharedDuringBuild,
|
|
2125
|
-
sourcemaps,
|
|
2126
|
-
sourcemapsSourcesContent,
|
|
2127
|
-
outDirectoryUrl: outDirectoryUrl
|
|
2128
|
-
? new URL("craft/", outDirectoryUrl)
|
|
2129
|
-
: undefined,
|
|
2130
|
-
});
|
|
2131
|
-
|
|
2132
|
-
let subbuildResults = [];
|
|
2133
|
-
|
|
2134
|
-
const rawPluginStore = createPluginStore([
|
|
2135
|
-
...jsenvPluginSubbuilds(subbuilds, {
|
|
2136
|
-
parentBuildParams: {
|
|
2137
|
-
sourceDirectoryUrl,
|
|
2138
|
-
buildDirectoryUrl,
|
|
2139
|
-
runtimeCompat,
|
|
2140
|
-
bundling,
|
|
2141
|
-
minification,
|
|
2142
|
-
versioning,
|
|
2143
|
-
versioningMethod,
|
|
2144
|
-
},
|
|
2145
|
-
onCustomBuildDirectory: (subBuildRelativeUrl) => {
|
|
2146
|
-
buildDirectoryCleanPatterns = {
|
|
2147
|
-
...buildDirectoryCleanPatterns,
|
|
2148
|
-
[`${subBuildRelativeUrl}**/*`]: false,
|
|
2149
|
-
};
|
|
2150
|
-
},
|
|
2151
|
-
buildStart: async (params, index) => {
|
|
2152
|
-
const result = await build({
|
|
2153
|
-
...params,
|
|
2154
|
-
signal,
|
|
2155
|
-
handleSIGINT: false,
|
|
2156
|
-
});
|
|
2157
|
-
subbuildResults[index] = result;
|
|
2158
|
-
return result;
|
|
2159
|
-
},
|
|
2160
|
-
}),
|
|
2161
|
-
...plugins,
|
|
2162
|
-
...(bundling ? [jsenvPluginBundling(bundling)] : []),
|
|
2163
|
-
...(minification ? [jsenvPluginMinification(minification)] : []),
|
|
2164
|
-
...getCorePlugins({
|
|
2165
|
-
rootDirectoryUrl: sourceDirectoryUrl,
|
|
2166
|
-
runtimeCompat,
|
|
2167
|
-
referenceAnalysis,
|
|
2168
|
-
nodeEsmResolution,
|
|
2169
|
-
magicExtensions,
|
|
2170
|
-
magicDirectoryIndex,
|
|
2171
|
-
directoryReferenceEffect,
|
|
2172
|
-
injections,
|
|
2173
|
-
transpilation: {
|
|
2174
|
-
babelHelpersAsImport: !explicitJsModuleConversion,
|
|
2175
|
-
...transpilation,
|
|
2176
|
-
jsModuleFallback: false,
|
|
2177
|
-
},
|
|
2178
|
-
inlining: false,
|
|
2179
|
-
http,
|
|
2180
|
-
scenarioPlaceholders,
|
|
2181
|
-
}),
|
|
2182
|
-
]);
|
|
2183
|
-
const rawPluginController = createPluginController(
|
|
2184
|
-
rawPluginStore,
|
|
2185
|
-
rawKitchen,
|
|
2186
|
-
);
|
|
2187
|
-
rawKitchen.setPluginController(rawPluginController);
|
|
2188
|
-
|
|
2189
|
-
{
|
|
2190
|
-
const generateSourceGraph = createBuildTask("generate source graph");
|
|
2191
|
-
try {
|
|
2192
|
-
if (outDirectoryUrl) {
|
|
2193
|
-
await ensureEmptyDirectory(new URL(`craft/`, outDirectoryUrl));
|
|
2194
|
-
}
|
|
2195
|
-
const rawRootUrlInfo = rawKitchen.graph.rootUrlInfo;
|
|
2196
|
-
await rawRootUrlInfo.dependencies.startCollecting(() => {
|
|
2197
|
-
Object.keys(entryPoints).forEach((key) => {
|
|
2198
|
-
const entryReference = rawRootUrlInfo.dependencies.found({
|
|
2199
|
-
trace: { message: `"${key}" in entryPoints parameter` },
|
|
2200
|
-
isEntryPoint: true,
|
|
2201
|
-
type: "entry_point",
|
|
2202
|
-
specifier: key,
|
|
2203
|
-
filenameHint: entryPoints[key],
|
|
2204
|
-
});
|
|
2205
|
-
entryUrls.push(entryReference.url);
|
|
2206
|
-
});
|
|
2207
|
-
});
|
|
2208
|
-
await rawRootUrlInfo.cookDependencies({
|
|
2209
|
-
operation: buildOperation,
|
|
2210
|
-
});
|
|
2211
|
-
} catch (e) {
|
|
2212
|
-
generateSourceGraph.fail();
|
|
2213
|
-
throw e;
|
|
2214
|
-
}
|
|
2215
|
-
generateSourceGraph.done();
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
const finalKitchen = createKitchen({
|
|
2219
|
-
name: "shape",
|
|
2220
|
-
logLevel: logs.level,
|
|
2221
|
-
rootDirectoryUrl: sourceDirectoryUrl,
|
|
2222
|
-
// here most plugins are not there
|
|
2223
|
-
// - no external plugin
|
|
2224
|
-
// - no plugin putting reference.mustIgnore on https urls
|
|
2225
|
-
// At this stage it's only about redirecting urls to the build directory
|
|
2226
|
-
// consequently only a subset or urls are supported
|
|
2227
|
-
supportedProtocols: ["file:", "data:", "virtual:", "ignore:"],
|
|
2228
|
-
ignore,
|
|
2229
|
-
ignoreProtocol: "remove",
|
|
2230
|
-
build: true,
|
|
2231
|
-
runtimeCompat,
|
|
2232
|
-
initialContext: contextSharedDuringBuild,
|
|
2233
|
-
sourcemaps,
|
|
2234
|
-
sourcemapsComment: "relative",
|
|
2235
|
-
sourcemapsSourcesContent,
|
|
2236
|
-
outDirectoryUrl: outDirectoryUrl
|
|
2237
|
-
? new URL("shape/", outDirectoryUrl)
|
|
2238
|
-
: undefined,
|
|
2239
|
-
});
|
|
2240
|
-
const buildSpecifierManager = createBuildSpecifierManager({
|
|
2241
|
-
rawKitchen,
|
|
2242
|
-
finalKitchen,
|
|
2243
|
-
logger,
|
|
2244
|
-
sourceDirectoryUrl,
|
|
2245
|
-
buildDirectoryUrl,
|
|
2246
|
-
base,
|
|
2247
|
-
assetsDirectory,
|
|
2248
|
-
|
|
2249
|
-
versioning,
|
|
2250
|
-
versioningMethod,
|
|
2251
|
-
versionLength,
|
|
2252
|
-
canUseImportmap:
|
|
2253
|
-
versioningViaImportmap &&
|
|
2254
|
-
entryUrls.every((finalEntryUrl) => {
|
|
2255
|
-
const entryUrlInfo = rawKitchen.graph.getUrlInfo(finalEntryUrl);
|
|
2256
|
-
return entryUrlInfo.type === "html";
|
|
2257
|
-
}) &&
|
|
2258
|
-
rawKitchen.context.isSupportedOnCurrentClients("importmap"),
|
|
2259
|
-
});
|
|
2260
|
-
const finalPluginStore = createPluginStore([
|
|
2261
|
-
jsenvPluginReferenceAnalysis({
|
|
2262
|
-
...referenceAnalysis,
|
|
2263
|
-
fetchInlineUrls: false,
|
|
2264
|
-
// inlineContent: false,
|
|
2265
|
-
}),
|
|
2266
|
-
jsenvPluginDirectoryReferenceEffect(directoryReferenceEffect),
|
|
2267
|
-
...(lineBreakNormalization ? [jsenvPluginLineBreakNormalization()] : []),
|
|
2268
|
-
jsenvPluginJsModuleFallback({
|
|
2269
|
-
remapImportSpecifier: (specifier, parentUrl) => {
|
|
2270
|
-
return buildSpecifierManager.remapPlaceholder(specifier, parentUrl);
|
|
2271
|
-
},
|
|
2272
|
-
}),
|
|
2273
|
-
jsenvPluginInlining(),
|
|
2274
|
-
{
|
|
2275
|
-
name: "jsenv:optimize",
|
|
2276
|
-
appliesDuring: "build",
|
|
2277
|
-
transformUrlContent: async (urlInfo) => {
|
|
2278
|
-
await rawKitchen.pluginController.callAsyncHooks(
|
|
2279
|
-
"optimizeUrlContent",
|
|
2280
|
-
urlInfo,
|
|
2281
|
-
(optimizeReturnValue) => {
|
|
2282
|
-
urlInfo.mutateContent(optimizeReturnValue);
|
|
2283
|
-
},
|
|
2284
|
-
);
|
|
2285
|
-
},
|
|
2286
|
-
},
|
|
2287
|
-
buildSpecifierManager.jsenvPluginMoveToBuildDirectory,
|
|
2288
|
-
]);
|
|
2289
|
-
const finalPluginController = createPluginController(
|
|
2290
|
-
finalPluginStore,
|
|
2291
|
-
finalKitchen,
|
|
2292
|
-
{
|
|
2293
|
-
initialPuginsMeta: rawKitchen.pluginController.pluginsMeta,
|
|
2294
|
-
},
|
|
2295
|
-
);
|
|
2296
|
-
finalKitchen.setPluginController(finalPluginController);
|
|
2297
|
-
|
|
2298
|
-
const bundlers = {};
|
|
2299
|
-
{
|
|
2300
|
-
for (const plugin of rawKitchen.pluginController.activePlugins) {
|
|
2301
|
-
const bundle = plugin.bundle;
|
|
2302
|
-
if (!bundle) {
|
|
2303
|
-
continue;
|
|
2304
|
-
}
|
|
2305
|
-
if (typeof bundle !== "object") {
|
|
2306
|
-
throw new Error(
|
|
2307
|
-
`bundle must be an object, found "${bundle}" on plugin named "${plugin.name}"`,
|
|
2308
|
-
);
|
|
2309
|
-
}
|
|
2310
|
-
for (const type of Object.keys(bundle)) {
|
|
2311
|
-
const bundleFunction = bundle[type];
|
|
2312
|
-
if (!bundleFunction) {
|
|
2313
|
-
continue;
|
|
2314
|
-
}
|
|
2315
|
-
const bundlerForThatType = bundlers[type];
|
|
2316
|
-
if (bundlerForThatType) {
|
|
2317
|
-
// first plugin to define a bundle hook wins
|
|
2318
|
-
continue;
|
|
2319
|
-
}
|
|
2320
|
-
bundlers[type] = {
|
|
2321
|
-
plugin,
|
|
2322
|
-
bundleFunction: bundle[type],
|
|
2323
|
-
urlInfoMap: new Map(),
|
|
2324
|
-
};
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
const addToBundlerIfAny = (rawUrlInfo) => {
|
|
2328
|
-
const bundler = bundlers[rawUrlInfo.type];
|
|
2329
|
-
if (bundler) {
|
|
2330
|
-
bundler.urlInfoMap.set(rawUrlInfo.url, rawUrlInfo);
|
|
2331
|
-
}
|
|
2332
|
-
};
|
|
2333
|
-
// ignore unused urls thanks to "forEachUrlInfoStronglyReferenced"
|
|
2334
|
-
// it avoid bundling things that are not actually used
|
|
2335
|
-
// happens for:
|
|
2336
|
-
// - js import assertions
|
|
2337
|
-
// - conversion to js classic using ?as_js_classic or ?js_module_fallback
|
|
2338
|
-
GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
|
|
2339
|
-
rawKitchen.graph.rootUrlInfo,
|
|
2340
|
-
(rawUrlInfo) => {
|
|
2341
|
-
if (rawUrlInfo.isEntryPoint) {
|
|
2342
|
-
addToBundlerIfAny(rawUrlInfo);
|
|
2343
|
-
}
|
|
2344
|
-
if (rawUrlInfo.type === "html") {
|
|
2345
|
-
for (const referenceToOther of rawUrlInfo.referenceToOthersSet) {
|
|
2346
|
-
if (
|
|
2347
|
-
referenceToOther.isResourceHint &&
|
|
2348
|
-
referenceToOther.expectedType === "js_module"
|
|
2349
|
-
) {
|
|
2350
|
-
const referencedUrlInfo = referenceToOther.urlInfo;
|
|
2351
|
-
if (
|
|
2352
|
-
referencedUrlInfo &&
|
|
2353
|
-
// something else than the resource hint is using this url
|
|
2354
|
-
referencedUrlInfo.referenceFromOthersSet.size > 0
|
|
2355
|
-
) {
|
|
2356
|
-
addToBundlerIfAny(referencedUrlInfo);
|
|
2357
|
-
continue;
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
if (referenceToOther.isWeak) {
|
|
2361
|
-
continue;
|
|
2362
|
-
}
|
|
2363
|
-
const referencedUrlInfo = referenceToOther.urlInfo;
|
|
2364
|
-
if (referencedUrlInfo.isInline) {
|
|
2365
|
-
if (referencedUrlInfo.type === "js_module") {
|
|
2366
|
-
// bundle inline script type module deps
|
|
2367
|
-
referencedUrlInfo.referenceToOthersSet.forEach(
|
|
2368
|
-
(jsModuleReferenceToOther) => {
|
|
2369
|
-
if (jsModuleReferenceToOther.type === "js_import") {
|
|
2370
|
-
const inlineUrlInfo = jsModuleReferenceToOther.urlInfo;
|
|
2371
|
-
addToBundlerIfAny(inlineUrlInfo);
|
|
2372
|
-
}
|
|
2373
|
-
},
|
|
2374
|
-
);
|
|
2375
|
-
}
|
|
2376
|
-
// inline content cannot be bundled
|
|
2377
|
-
continue;
|
|
2378
|
-
}
|
|
2379
|
-
addToBundlerIfAny(referencedUrlInfo);
|
|
2380
|
-
}
|
|
2381
|
-
return;
|
|
2382
|
-
}
|
|
2383
|
-
// File referenced with new URL('./file.js', import.meta.url)
|
|
2384
|
-
// are entry points that should be bundled
|
|
2385
|
-
// For instance we will bundle service worker/workers detected like this
|
|
2386
|
-
if (rawUrlInfo.type === "js_module") {
|
|
2387
|
-
for (const referenceToOther of rawUrlInfo.referenceToOthersSet) {
|
|
2388
|
-
if (referenceToOther.type === "js_url") {
|
|
2389
|
-
const referencedUrlInfo = referenceToOther.urlInfo;
|
|
2390
|
-
let isAlreadyBundled = false;
|
|
2391
|
-
for (const referenceFromOther of referencedUrlInfo.referenceFromOthersSet) {
|
|
2392
|
-
if (referenceFromOther.url === referencedUrlInfo.url) {
|
|
2393
|
-
if (
|
|
2394
|
-
referenceFromOther.subtype === "import_dynamic" ||
|
|
2395
|
-
referenceFromOther.type === "script"
|
|
2396
|
-
) {
|
|
2397
|
-
isAlreadyBundled = true;
|
|
2398
|
-
break;
|
|
2399
|
-
}
|
|
2400
|
-
}
|
|
2401
|
-
}
|
|
2402
|
-
if (!isAlreadyBundled) {
|
|
2403
|
-
addToBundlerIfAny(referencedUrlInfo);
|
|
2404
|
-
}
|
|
2405
|
-
continue;
|
|
2406
|
-
}
|
|
2407
|
-
if (referenceToOther.type === "js_inline_content") ;
|
|
2408
|
-
}
|
|
2409
|
-
}
|
|
2410
|
-
},
|
|
2411
|
-
);
|
|
2412
|
-
for (const type of Object.keys(bundlers)) {
|
|
2413
|
-
const bundler = bundlers[type];
|
|
2414
|
-
const urlInfosToBundle = Array.from(bundler.urlInfoMap.values());
|
|
2415
|
-
if (urlInfosToBundle.length === 0) {
|
|
2416
|
-
continue;
|
|
2417
|
-
}
|
|
2418
|
-
const bundleTask = createBuildTask(`bundle "${type}"`);
|
|
2419
|
-
try {
|
|
2420
|
-
await buildSpecifierManager.applyBundling({
|
|
2421
|
-
bundler,
|
|
2422
|
-
urlInfosToBundle,
|
|
2423
|
-
});
|
|
2424
|
-
} catch (e) {
|
|
2425
|
-
bundleTask.fail();
|
|
2426
|
-
throw e;
|
|
2427
|
-
}
|
|
2428
|
-
bundleTask.done();
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
{
|
|
2433
|
-
finalKitchen.context.buildStep = "shape";
|
|
2434
|
-
const generateBuildGraph = createBuildTask("generate build graph");
|
|
2435
|
-
try {
|
|
2436
|
-
if (outDirectoryUrl) {
|
|
2437
|
-
await ensureEmptyDirectory(new URL(`shape/`, outDirectoryUrl));
|
|
2438
|
-
}
|
|
2439
|
-
const finalRootUrlInfo = finalKitchen.graph.rootUrlInfo;
|
|
2440
|
-
await finalRootUrlInfo.dependencies.startCollecting(() => {
|
|
2441
|
-
entryUrls.forEach((entryUrl) => {
|
|
2442
|
-
finalRootUrlInfo.dependencies.found({
|
|
2443
|
-
trace: { message: `entryPoint` },
|
|
2444
|
-
isEntryPoint: true,
|
|
2445
|
-
type: "entry_point",
|
|
2446
|
-
specifier: entryUrl,
|
|
2447
|
-
});
|
|
2448
|
-
});
|
|
2449
|
-
});
|
|
2450
|
-
await finalRootUrlInfo.cookDependencies({
|
|
2451
|
-
operation: buildOperation,
|
|
2452
|
-
});
|
|
2453
|
-
} catch (e) {
|
|
2454
|
-
generateBuildGraph.fail();
|
|
2455
|
-
throw e;
|
|
2456
|
-
}
|
|
2457
|
-
generateBuildGraph.done();
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
{
|
|
2461
|
-
finalKitchen.context.buildStep = "refine";
|
|
2462
|
-
|
|
2463
|
-
const htmlRefineSet = new Set();
|
|
2464
|
-
const registerHtmlRefine = (htmlRefine) => {
|
|
2465
|
-
htmlRefineSet.add(htmlRefine);
|
|
2466
|
-
};
|
|
2467
|
-
|
|
2468
|
-
{
|
|
2469
|
-
await buildSpecifierManager.replacePlaceholders();
|
|
2470
|
-
}
|
|
2471
|
-
|
|
2472
|
-
/*
|
|
2473
|
-
* Update <link rel="preload"> and friends after build (once we know everything)
|
|
2474
|
-
* - Used to remove resource hint targeting an url that is no longer used:
|
|
2475
|
-
* - because of bundlings
|
|
2476
|
-
* - because of import assertions transpilation (file is inlined into JS)
|
|
2477
|
-
*/
|
|
2478
|
-
{
|
|
2479
|
-
buildSpecifierManager.prepareResyncResourceHints({
|
|
2480
|
-
registerHtmlRefine,
|
|
2481
|
-
});
|
|
2482
|
-
}
|
|
2483
|
-
|
|
2484
|
-
{
|
|
2485
|
-
GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
|
|
2486
|
-
if (!urlInfo.url.startsWith("file:")) {
|
|
2487
|
-
return;
|
|
2488
|
-
}
|
|
2489
|
-
if (urlInfo.type !== "html") {
|
|
2490
|
-
return;
|
|
2491
|
-
}
|
|
2492
|
-
const htmlAst = parseHtml({
|
|
2493
|
-
html: urlInfo.content,
|
|
2494
|
-
url: urlInfo.url,
|
|
2495
|
-
storeOriginalPositions: false,
|
|
2496
|
-
});
|
|
2497
|
-
for (const htmlRefine of htmlRefineSet) {
|
|
2498
|
-
const htmlMutationCallbackSet = new Set();
|
|
2499
|
-
const registerHtmlMutation = (callback) => {
|
|
2500
|
-
htmlMutationCallbackSet.add(callback);
|
|
2501
|
-
};
|
|
2502
|
-
htmlRefine(htmlAst, { registerHtmlMutation });
|
|
2503
|
-
for (const htmlMutationCallback of htmlMutationCallbackSet) {
|
|
2504
|
-
htmlMutationCallback();
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
// cleanup jsenv attributes from html as a last step
|
|
2508
|
-
urlInfo.content = stringifyHtmlAst(htmlAst, {
|
|
2509
|
-
cleanupJsenvAttributes: true,
|
|
2510
|
-
cleanupPositionAttributes: true,
|
|
2511
|
-
});
|
|
2512
|
-
});
|
|
2513
|
-
}
|
|
2514
|
-
|
|
2515
|
-
{
|
|
2516
|
-
const inject = buildSpecifierManager.prepareServiceWorkerUrlInjection();
|
|
2517
|
-
if (inject) {
|
|
2518
|
-
const urlsInjectionInSw = createBuildTask(
|
|
2519
|
-
"inject urls in service worker",
|
|
2520
|
-
);
|
|
2521
|
-
await inject();
|
|
2522
|
-
urlsInjectionInSw.done();
|
|
2523
|
-
buildOperation.throwIfAborted();
|
|
2524
|
-
}
|
|
2525
|
-
}
|
|
2526
|
-
}
|
|
2527
|
-
const { buildFileContents, buildInlineContents, buildManifest } =
|
|
2528
|
-
buildSpecifierManager.getBuildInfo();
|
|
2529
|
-
if (writeOnFileSystem) {
|
|
2530
|
-
const writingFiles = createBuildTask("write files in build directory");
|
|
2531
|
-
clearDirectorySync(buildDirectoryUrl, buildDirectoryCleanPatterns);
|
|
2532
|
-
const buildRelativeUrls = Object.keys(buildFileContents);
|
|
2533
|
-
buildRelativeUrls.forEach((buildRelativeUrl) => {
|
|
2534
|
-
writeFileSync(
|
|
2535
|
-
new URL(buildRelativeUrl, buildDirectoryUrl),
|
|
2536
|
-
buildFileContents[buildRelativeUrl],
|
|
2537
|
-
);
|
|
2538
|
-
});
|
|
2539
|
-
if (versioning && assetManifest && Object.keys(buildManifest).length) {
|
|
2540
|
-
writeFileSync(
|
|
2541
|
-
new URL(assetManifestFileRelativeUrl, buildDirectoryUrl),
|
|
2542
|
-
JSON.stringify(buildManifest, null, " "),
|
|
2543
|
-
);
|
|
2544
|
-
}
|
|
2545
|
-
writingFiles.done();
|
|
2546
|
-
}
|
|
2547
|
-
logger.info(
|
|
2548
|
-
createUrlGraphSummary(finalKitchen.graph, {
|
|
2549
|
-
title: "build files",
|
|
2550
|
-
}),
|
|
2551
|
-
);
|
|
2552
|
-
return {
|
|
2553
|
-
...(returnBuildInlineContents ? { buildInlineContents } : {}),
|
|
2554
|
-
...(returnBuildManifest ? { buildManifest } : {}),
|
|
2555
|
-
...(subbuilds.length ? { subbuilds: subbuildResults } : {}),
|
|
2556
|
-
};
|
|
2557
|
-
};
|
|
2558
|
-
|
|
2559
|
-
if (!watch) {
|
|
2560
|
-
try {
|
|
2561
|
-
const result = await runBuild({
|
|
2562
|
-
signal: operation.signal,
|
|
2563
|
-
logLevel: logs.level,
|
|
2564
|
-
});
|
|
2565
|
-
return result;
|
|
2566
|
-
} finally {
|
|
2567
|
-
await operation.end();
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
let resolveFirstBuild;
|
|
2572
|
-
let rejectFirstBuild;
|
|
2573
|
-
const firstBuildPromise = new Promise((resolve, reject) => {
|
|
2574
|
-
resolveFirstBuild = resolve;
|
|
2575
|
-
rejectFirstBuild = reject;
|
|
2576
|
-
});
|
|
2577
|
-
let buildAbortController;
|
|
2578
|
-
let watchFilesTask;
|
|
2579
|
-
const startBuild = async () => {
|
|
2580
|
-
const buildTask = createTaskLog("build");
|
|
2581
|
-
buildAbortController = new AbortController();
|
|
2582
|
-
try {
|
|
2583
|
-
const result = await runBuild({
|
|
2584
|
-
signal: buildAbortController.signal,
|
|
2585
|
-
logLevel: "warn",
|
|
2586
|
-
});
|
|
2587
|
-
buildTask.done();
|
|
2588
|
-
resolveFirstBuild(result);
|
|
2589
|
-
watchFilesTask = createTaskLog("watch files");
|
|
2590
|
-
} catch (e) {
|
|
2591
|
-
if (Abort.isAbortError(e)) {
|
|
2592
|
-
buildTask.fail(`build aborted`);
|
|
2593
|
-
} else if (e.code === "PARSE_ERROR") {
|
|
2594
|
-
buildTask.fail();
|
|
2595
|
-
console.error(e.stack);
|
|
2596
|
-
watchFilesTask = createTaskLog("watch files");
|
|
2597
|
-
} else {
|
|
2598
|
-
buildTask.fail();
|
|
2599
|
-
rejectFirstBuild(e);
|
|
2600
|
-
throw e;
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
2603
|
-
};
|
|
2604
|
-
|
|
2605
|
-
startBuild();
|
|
2606
|
-
let startTimeout;
|
|
2607
|
-
const stopWatchingSourceFiles = watchSourceFiles(
|
|
2608
|
-
sourceDirectoryUrl,
|
|
2609
|
-
({ url, event }) => {
|
|
2610
|
-
if (watchFilesTask) {
|
|
2611
|
-
watchFilesTask.happen(
|
|
2612
|
-
`${url.slice(sourceDirectoryUrl.length)} ${event}`,
|
|
2613
|
-
);
|
|
2614
|
-
watchFilesTask = null;
|
|
2615
|
-
}
|
|
2616
|
-
buildAbortController.abort();
|
|
2617
|
-
// setTimeout is to ensure the abortController.abort() above
|
|
2618
|
-
// is properly taken into account so that logs about abort comes first
|
|
2619
|
-
// then logs about re-running the build happens
|
|
2620
|
-
clearTimeout(startTimeout);
|
|
2621
|
-
startTimeout = setTimeout(startBuild, 20);
|
|
2622
|
-
},
|
|
2623
|
-
{
|
|
2624
|
-
sourceFilesConfig,
|
|
2625
|
-
keepProcessAlive: true,
|
|
2626
|
-
cooldownBetweenFileEvents,
|
|
2627
|
-
},
|
|
2628
|
-
);
|
|
2629
|
-
operation.addAbortCallback(() => {
|
|
2630
|
-
stopWatchingSourceFiles();
|
|
2631
|
-
});
|
|
2632
|
-
await firstBuildPromise;
|
|
2633
|
-
return stopWatchingSourceFiles;
|
|
2634
|
-
};
|
|
2635
|
-
|
|
2636
|
-
export { build };
|