@jsenv/core 37.1.5 → 38.0.1

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.
Files changed (29) hide show
  1. package/dist/js/autoreload.js +2 -2
  2. package/dist/jsenv_core.js +3231 -2483
  3. package/package.json +14 -13
  4. package/src/build/build.js +250 -1028
  5. package/src/build/build_specifier_manager.js +1200 -0
  6. package/src/build/build_urls_generator.js +40 -18
  7. package/src/build/version_mappings_injection.js +14 -16
  8. package/src/dev/file_service.js +0 -10
  9. package/src/dev/start_dev_server.js +0 -2
  10. package/src/kitchen/kitchen.js +54 -37
  11. package/src/kitchen/url_graph/references.js +84 -93
  12. package/src/kitchen/url_graph/url_graph.js +16 -6
  13. package/src/kitchen/url_graph/url_info_transformations.js +124 -55
  14. package/src/plugins/autoreload/client/autoreload.js +6 -2
  15. package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +20 -16
  16. package/src/plugins/autoreload/jsenv_plugin_hot_search_param.js +1 -1
  17. package/src/plugins/cache_control/jsenv_plugin_cache_control.js +2 -2
  18. package/src/plugins/clean_html/jsenv_plugin_clean_html.js +16 -0
  19. package/src/plugins/importmap/jsenv_plugin_importmap.js +11 -23
  20. package/src/plugins/inlining/jsenv_plugin_inlining_as_data_url.js +16 -1
  21. package/src/plugins/inlining/jsenv_plugin_inlining_into_html.js +14 -24
  22. package/src/plugins/plugin_controller.js +37 -25
  23. package/src/plugins/plugins.js +2 -0
  24. package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +31 -16
  25. package/src/plugins/reference_analysis/directory/jsenv_plugin_directory_reference_analysis.js +12 -6
  26. package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +33 -54
  27. package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +2 -9
  28. package/src/plugins/reference_analysis/jsenv_plugin_reference_analysis.js +15 -8
  29. package/src/build/build_versions_manager.js +0 -492
@@ -16,38 +16,16 @@
16
16
  * - injecting urls into service workers
17
17
  */
18
18
 
19
- import {
20
- asUrlWithoutSearch,
21
- ensurePathnameTrailingSlash,
22
- urlIsInsideOf,
23
- urlToRelativeUrl,
24
- } from "@jsenv/urls";
25
19
  import {
26
20
  assertAndNormalizeDirectoryUrl,
27
21
  ensureEmptyDirectory,
28
22
  writeFileSync,
29
- comparePathnames,
30
23
  } from "@jsenv/filesystem";
31
24
  import { Abort, raceProcessTeardownEvents } from "@jsenv/abort";
32
- import {
33
- createLogger,
34
- createTaskLog,
35
- ANSI,
36
- createDetailedMessage,
37
- } from "@jsenv/log";
38
- import { generateSourcemapFileUrl } from "@jsenv/sourcemap";
39
- import {
40
- parseHtmlString,
41
- stringifyHtmlAst,
42
- visitHtmlNodes,
43
- getHtmlNodeAttribute,
44
- setHtmlNodeAttributes,
45
- removeHtmlNode,
46
- createHtmlNode,
47
- insertHtmlNodeAfter,
48
- findHtmlNode,
49
- } from "@jsenv/ast";
50
- import { RUNTIME_COMPAT } from "@jsenv/runtime-compat";
25
+ import { createLogger, createTaskLog } from "@jsenv/log";
26
+ import { parseHtmlString, stringifyHtmlAst } from "@jsenv/ast";
27
+ import { jsenvPluginBundling } from "@jsenv/plugin-bundling";
28
+ import { jsenvPluginMinification } from "@jsenv/plugin-minification";
51
29
  import { jsenvPluginJsModuleFallback } from "@jsenv/plugin-transpilation";
52
30
 
53
31
  import { lookupPackageDirectory } from "../helpers/lookup_package_directory.js";
@@ -55,15 +33,12 @@ import { watchSourceFiles } from "../helpers/watch_source_files.js";
55
33
  import { GRAPH_VISITOR } from "../kitchen/url_graph/url_graph_visitor.js";
56
34
  import { createKitchen } from "../kitchen/kitchen.js";
57
35
  import { createUrlGraphSummary } from "../kitchen/url_graph/url_graph_report.js";
58
- import { isWebWorkerEntryPointReference } from "../kitchen/web_workers.js";
59
- import { prependContent } from "../kitchen/prepend_content.js";
60
36
  import { getCorePlugins } from "../plugins/plugins.js";
61
37
  import { jsenvPluginReferenceAnalysis } from "../plugins/reference_analysis/jsenv_plugin_reference_analysis.js";
62
38
  import { jsenvPluginInlining } from "../plugins/inlining/jsenv_plugin_inlining.js";
63
39
  import { jsenvPluginLineBreakNormalization } from "./jsenv_plugin_line_break_normalization.js";
64
40
 
65
- import { createBuildUrlsGenerator } from "./build_urls_generator.js";
66
- import { createBuildVersionsManager } from "./build_versions_manager.js";
41
+ import { createBuildSpecifierManager } from "./build_specifier_manager.js";
67
42
 
68
43
  // default runtimeCompat corresponds to
69
44
  // "we can keep <script type="module"> intact":
@@ -95,6 +70,10 @@ export const defaultRuntimeCompat = {
95
70
  * Directory where asset files will be written
96
71
  * @param {string|url} [buildParameters.base=""]
97
72
  * Urls in build file contents will be prefixed with this string
73
+ * @param {boolean|object} [buildParameters.bundling=true]
74
+ * Reduce number of files written in the build directory
75
+ * @param {boolean|object} [buildParameters.minification=true]
76
+ * Minify the content of files generated into the build directory
98
77
  * @param {boolean} [buildParameters.versioning=true]
99
78
  * Controls if url in build file contents are versioned
100
79
  * @param {('search_param'|'filename')} [buildParameters.versioningMethod="search_param"]
@@ -117,10 +96,10 @@ export const build = async ({
117
96
  buildDirectoryUrl,
118
97
  entryPoints = {},
119
98
  assetsDirectory = "",
120
- ignore,
121
-
122
99
  runtimeCompat = defaultRuntimeCompat,
123
100
  base = runtimeCompat.node ? "./" : "/",
101
+ ignore,
102
+
124
103
  plugins = [],
125
104
  referenceAnalysis = {},
126
105
  nodeEsmResolution,
@@ -129,6 +108,8 @@ export const build = async ({
129
108
  directoryReferenceAllowed,
130
109
  scenarioPlaceholders,
131
110
  transpilation = {},
111
+ bundling = true,
112
+ minification = !runtimeCompat.node,
132
113
  versioning = !runtimeCompat.node,
133
114
  versioningMethod = "search_param", // "filename", "search_param"
134
115
  versioningViaImportmap = true,
@@ -205,6 +186,12 @@ export const build = async ({
205
186
  `versioningMethod must be "filename" or "search_param", got ${versioning}`,
206
187
  );
207
188
  }
189
+ if (bundling === true) {
190
+ bundling = {};
191
+ }
192
+ if (minification === true) {
193
+ minification = {};
194
+ }
208
195
  }
209
196
 
210
197
  const operation = Abort.startOperation();
@@ -231,26 +218,6 @@ export const build = async ({
231
218
  }
232
219
  }
233
220
 
234
- const asFormattedBuildSpecifier = (reference, generatedUrl) => {
235
- if (base === "./") {
236
- const parentUrl =
237
- reference.ownerUrlInfo.url === sourceDirectoryUrl
238
- ? buildDirectoryUrl
239
- : reference.ownerUrlInfo.url;
240
- const urlRelativeToParent = urlToRelativeUrl(generatedUrl, parentUrl);
241
- if (urlRelativeToParent[0] !== ".") {
242
- // ensure "./" on relative url (otherwise it could be a "bare specifier")
243
- return `./${urlRelativeToParent}`;
244
- }
245
- return urlRelativeToParent;
246
- }
247
- const urlRelativeToBuildDirectory = urlToRelativeUrl(
248
- generatedUrl,
249
- buildDirectoryUrl,
250
- );
251
- return `${base}${urlRelativeToBuildDirectory}`;
252
- };
253
-
254
221
  const runBuild = async ({ signal, logLevel }) => {
255
222
  const logger = createLogger({ logLevel });
256
223
  const createBuildTask = (label) => {
@@ -274,30 +241,13 @@ build ${entryPointKeys.length} entry points`);
274
241
  key.includes("?js_module_fallback"),
275
242
  );
276
243
  const rawRedirections = new Map();
277
- const bundleRedirections = new Map();
278
- const bundleInternalRedirections = new Map();
279
- const finalRedirections = new Map();
280
244
  const entryUrls = [];
281
245
  const contextSharedDuringBuild = {
246
+ buildStep: "craft",
282
247
  buildDirectoryUrl,
283
248
  assetsDirectory,
284
- systemJsTranspilation: (() => {
285
- const nodeRuntimeEnabled = Object.keys(runtimeCompat).includes("node");
286
- if (nodeRuntimeEnabled) return false;
287
- if (!RUNTIME_COMPAT.isSupported(runtimeCompat, "script_type_module"))
288
- return true;
289
- if (!RUNTIME_COMPAT.isSupported(runtimeCompat, "import_dynamic"))
290
- return true;
291
- if (!RUNTIME_COMPAT.isSupported(runtimeCompat, "import_meta"))
292
- return true;
293
- if (
294
- versioning &&
295
- versioningViaImportmap &&
296
- !RUNTIME_COMPAT.isSupported(runtimeCompat, "importmap")
297
- )
298
- return true;
299
- return false;
300
- })(),
249
+ versioning,
250
+ versioningViaImportmap,
301
251
  };
302
252
  const rawKitchen = createKitchen({
303
253
  signal,
@@ -309,9 +259,11 @@ build ${entryPointKeys.length} entry points`);
309
259
  ignoreProtocol: "keep",
310
260
  build: true,
311
261
  runtimeCompat,
312
- baseContext: contextSharedDuringBuild,
262
+ initialContext: contextSharedDuringBuild,
313
263
  plugins: [
314
264
  ...plugins,
265
+ ...(bundling ? [jsenvPluginBundling(bundling)] : []),
266
+ ...(minification ? [jsenvPluginMinification(minification)] : []),
315
267
  {
316
268
  appliesDuring: "build",
317
269
  fetchUrlContent: (urlInfo) => {
@@ -334,9 +286,8 @@ build ${entryPointKeys.length} entry points`);
334
286
  transpilation: {
335
287
  babelHelpersAsImport: !explicitJsModuleFallback,
336
288
  ...transpilation,
337
- jsModuleFallbackOnJsClassic: false,
289
+ jsModuleFallback: false,
338
290
  },
339
-
340
291
  inlining: false,
341
292
  scenarioPlaceholders,
342
293
  }),
@@ -344,38 +295,14 @@ build ${entryPointKeys.length} entry points`);
344
295
  sourcemaps,
345
296
  sourcemapsSourcesContent,
346
297
  outDirectoryUrl: outDirectoryUrl
347
- ? new URL("prebuild/", outDirectoryUrl)
298
+ ? new URL("craft/", outDirectoryUrl)
348
299
  : undefined,
349
300
  });
350
-
351
- const buildUrlsGenerator = createBuildUrlsGenerator({
352
- buildDirectoryUrl,
353
- assetsDirectory,
354
- });
355
- const buildDirectoryRedirections = new Map();
356
- const associateBuildUrlAndRawUrl = (buildUrl, rawUrl, reason) => {
357
- if (urlIsInsideOf(rawUrl, buildDirectoryUrl)) {
358
- throw new Error(`raw url must be inside rawGraph, got ${rawUrl}`);
359
- }
360
- if (buildDirectoryRedirections.get(buildUrl) !== rawUrl) {
361
- logger.debug(`build url generated (${reason})
362
- ${ANSI.color(rawUrl, ANSI.GREY)} ->
363
- ${ANSI.color(buildUrl, ANSI.MAGENTA)}
364
- `);
365
- buildDirectoryRedirections.set(buildUrl, rawUrl);
366
- }
367
- };
368
- const buildSpecifierMap = new Map();
369
- const bundleUrlInfos = {};
370
- const bundlers = {};
371
- let finalKitchen;
372
- let finalEntryUrls = [];
373
-
374
301
  craft: {
375
302
  const generateSourceGraph = createBuildTask("generate source graph");
376
303
  try {
377
304
  if (outDirectoryUrl) {
378
- await ensureEmptyDirectory(new URL(`build/`, outDirectoryUrl));
305
+ await ensureEmptyDirectory(new URL(`craft/`, outDirectoryUrl));
379
306
  }
380
307
  const rawRootUrlInfo = rawKitchen.graph.rootUrlInfo;
381
308
  await rawRootUrlInfo.dependencies.startCollecting(() => {
@@ -385,7 +312,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
385
312
  isEntryPoint: true,
386
313
  type: "entry_point",
387
314
  specifier: key,
388
- filename: entryPoints[key],
315
+ filenameHint: entryPoints[key],
389
316
  });
390
317
  entryUrls.push(entryReference.url);
391
318
  });
@@ -400,634 +327,249 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
400
327
  generateSourceGraph.done();
401
328
  }
402
329
 
403
- let buildVersionsManager;
404
-
405
- shape: {
406
- finalKitchen = createKitchen({
407
- name: "shape",
408
- logLevel,
409
- rootDirectoryUrl: buildDirectoryUrl,
410
- // here most plugins are not there
411
- // - no external plugin
412
- // - no plugin putting reference.mustIgnore on https urls
413
- // At this stage it's only about redirecting urls to the build directory
414
- // consequently only a subset or urls are supported
415
- supportedProtocols: ["file:", "data:", "virtual:", "ignore:"],
416
- ignore,
417
- ignoreProtocol: "remove",
418
- build: true,
419
- shape: true,
420
- runtimeCompat,
421
- baseContext: contextSharedDuringBuild,
422
- plugins: [
423
- jsenvPluginReferenceAnalysis({
424
- ...referenceAnalysis,
425
- fetchInlineUrls: false,
426
- }),
427
- ...(lineBreakNormalization
428
- ? [jsenvPluginLineBreakNormalization()]
429
- : []),
430
- jsenvPluginJsModuleFallback(),
431
- jsenvPluginInlining(),
432
- {
433
- name: "jsenv:build_shape",
434
- appliesDuring: "build",
435
- resolveReference: (reference) => {
436
- const getUrl = () => {
437
- if (reference.type === "filesystem") {
438
- const ownerRawUrl = buildDirectoryRedirections.get(
439
- reference.ownerUrlInfo.url,
440
- );
441
- const ownerUrl = ensurePathnameTrailingSlash(ownerRawUrl);
442
- return new URL(reference.specifier, ownerUrl).href;
443
- }
444
- if (reference.specifier[0] === "/") {
445
- return new URL(
446
- reference.specifier.slice(1),
447
- buildDirectoryUrl,
448
- ).href;
449
- }
450
- return new URL(
451
- reference.specifier,
452
- reference.baseUrl || reference.ownerUrlInfo.url,
453
- ).href;
454
- };
455
- let url = getUrl();
456
- // url = rawRedirections.get(url) || url
457
- url = bundleRedirections.get(url) || url;
458
- url = bundleInternalRedirections.get(url) || url;
459
- return url;
460
- },
461
- // redirecting references into the build directory
462
- redirectReference: (reference) => {
463
- if (!reference.url.startsWith("file:")) {
464
- return null;
465
- }
466
- // referenced by resource hint
467
- // -> keep it untouched, it will be handled by "resync_resource_hints"
468
- if (reference.isResourceHint) {
469
- return reference.original ? reference.original.url : null;
470
- }
471
- // already a build url
472
- const rawUrl = buildDirectoryRedirections.get(reference.url);
473
- if (rawUrl) {
474
- return reference.url;
475
- }
476
- if (reference.isInline) {
477
- const ownerFinalUrlInfo = finalKitchen.graph.getUrlInfo(
478
- reference.ownerUrlInfo.url,
479
- );
480
- const ownerRawUrl = ownerFinalUrlInfo.originalUrl;
481
- const rawUrlInfo = GRAPH_VISITOR.find(
482
- rawKitchen.graph,
483
- (rawUrlInfo) => {
484
- const { inlineUrlSite } = rawUrlInfo;
485
- // not inline
486
- if (!inlineUrlSite) return false;
487
- if (
488
- inlineUrlSite.url === ownerRawUrl &&
489
- inlineUrlSite.line === reference.specifierLine &&
490
- inlineUrlSite.column === reference.specifierColumn
491
- ) {
492
- return true;
493
- }
494
- if (rawUrlInfo.content === reference.content) {
495
- return true;
496
- }
497
- if (rawUrlInfo.originalContent === reference.content) {
498
- return true;
499
- }
500
- return false;
501
- },
502
- );
503
-
504
- if (!rawUrlInfo) {
505
- // generated during final graph
506
- // (happens for JSON.parse injected for import assertions for instance)
507
- // throw new Error(`cannot find raw url for "${reference.url}"`)
508
- return reference.url;
509
- }
510
- const buildUrl = buildUrlsGenerator.generate(reference.url, {
511
- urlInfo: rawUrlInfo,
512
- ownerUrlInfo: ownerFinalUrlInfo,
513
- });
514
- associateBuildUrlAndRawUrl(
515
- buildUrl,
516
- rawUrlInfo.url,
517
- "inline content",
518
- );
519
- return buildUrl;
520
- }
521
- // from "js_module_fallback":
522
- // - injecting "?js_module_fallback" for the first time
523
- // - injecting "?js_module_fallback" because the parentUrl has it
524
- if (reference.original) {
525
- const urlBeforeRedirect = reference.original.url;
526
- const urlAfterRedirect = reference.url;
527
- const isEntryPoint =
528
- reference.isEntryPoint ||
529
- isWebWorkerEntryPointReference(reference);
530
- // the url info do not exists yet (it will be created after this "redirectReference" hook)
531
- // And the content will be generated when url is cooked by url graph loader.
532
- // Here we just want to reserve an url for that file
533
- const urlInfo = {
534
- data: reference.data,
535
- isEntryPoint,
536
- type: reference.expectedType,
537
- subtype: reference.expectedSubtype,
538
- filename: reference.filename,
539
- };
540
- if (urlIsInsideOf(urlBeforeRedirect, buildDirectoryUrl)) {
541
- // the redirection happened on a build url, happens due to:
542
- // 1. bundling
543
- const buildUrl = buildUrlsGenerator.generate(
544
- urlAfterRedirect,
545
- {
546
- urlInfo,
547
- },
548
- );
549
- finalRedirections.set(urlBeforeRedirect, buildUrl);
550
- return buildUrl;
551
- }
552
- const rawUrl = urlAfterRedirect;
553
- const buildUrl = buildUrlsGenerator.generate(rawUrl, {
554
- urlInfo,
555
- });
556
- finalRedirections.set(urlBeforeRedirect, buildUrl);
557
- associateBuildUrlAndRawUrl(
558
- buildUrl,
559
- rawUrl,
560
- "redirected during postbuild",
561
- );
562
- return buildUrl;
563
- }
564
- // from "js_module_fallback":
565
- // - to inject "s.js"
566
- if (reference.injected) {
567
- const buildUrl = buildUrlsGenerator.generate(reference.url, {
568
- urlInfo: {
569
- data: {},
570
- type: "js_classic",
571
- },
572
- });
573
- associateBuildUrlAndRawUrl(
574
- buildUrl,
575
- reference.url,
576
- "injected during postbuild",
577
- );
578
- finalRedirections.set(buildUrl, buildUrl);
579
- return buildUrl;
580
- }
581
- const rawUrlInfo = rawKitchen.graph.getUrlInfo(reference.url);
582
- const ownerFinalUrlInfo = finalKitchen.graph.getUrlInfo(
583
- reference.ownerUrlInfo.url,
584
- );
585
- // files from root directory but not given to rollup nor postcss
586
- if (rawUrlInfo) {
587
- const referencedUrlObject = new URL(reference.url);
588
- referencedUrlObject.searchParams.delete("as_js_classic");
589
- referencedUrlObject.searchParams.delete("as_json_module");
590
- const buildUrl = buildUrlsGenerator.generate(
591
- referencedUrlObject.href,
592
- {
593
- urlInfo: rawUrlInfo,
594
- ownerUrlInfo: ownerFinalUrlInfo,
595
- },
596
- );
597
- associateBuildUrlAndRawUrl(
598
- buildUrl,
599
- rawUrlInfo.url,
600
- "raw file",
601
- );
602
- return buildUrl;
603
- }
604
- if (reference.type === "sourcemap_comment") {
605
- // inherit parent build url
606
- return generateSourcemapFileUrl(reference.ownerUrlInfo.url);
607
- }
608
- // files generated during the final graph:
609
- // - sourcemaps
610
- // const finalUrlInfo = finalGraph.getUrlInfo(url)
611
- const buildUrl = buildUrlsGenerator.generate(reference.url, {
612
- urlInfo: {
613
- data: {},
614
- type: "asset",
615
- },
616
- });
617
- return buildUrl;
618
- },
619
- formatReference: (reference) => {
620
- if (!reference.generatedUrl.startsWith("file:")) {
621
- return null;
622
- }
623
- if (reference.isWeak) {
624
- return null;
625
- }
626
- if (!urlIsInsideOf(reference.generatedUrl, buildDirectoryUrl)) {
627
- throw new Error(
628
- `urls should be inside build directory at this stage, found "${reference.url}"`,
629
- );
630
- }
631
- const generatedUrlObject = new URL(reference.generatedUrl);
632
- generatedUrlObject.searchParams.delete("js_classic");
633
- generatedUrlObject.searchParams.delete("js_module");
634
- generatedUrlObject.searchParams.delete("js_module_fallback");
635
- generatedUrlObject.searchParams.delete("as_js_classic");
636
- generatedUrlObject.searchParams.delete("as_js_module");
637
- generatedUrlObject.searchParams.delete("as_json_module");
638
- generatedUrlObject.searchParams.delete("as_css_module");
639
- generatedUrlObject.searchParams.delete("as_text_module");
640
- generatedUrlObject.searchParams.delete("dynamic_import");
641
- generatedUrlObject.hash = "";
642
- const buildUrl = generatedUrlObject.href;
643
- const buildSpecifier = asFormattedBuildSpecifier(
644
- reference,
645
- buildUrl,
646
- );
647
- buildSpecifierMap.set(buildSpecifier, reference.generatedUrl);
648
-
649
- if (!versioning || !shouldApplyVersioningOnReference(reference)) {
650
- return buildSpecifier;
651
- }
652
- const buildSpecifierWithVersionPlaceholder =
653
- buildVersionsManager.generateBuildSpecifierPlaceholder(
654
- reference,
655
- buildSpecifier,
656
- );
657
- return buildSpecifierWithVersionPlaceholder;
658
- },
659
- fetchUrlContent: async (finalUrlInfo) => {
660
- const fromBundleOrRawGraph = (url) => {
661
- const bundleUrlInfo = bundleUrlInfos[url];
662
- if (bundleUrlInfo) {
663
- return bundleUrlInfo;
664
- }
665
- const rawUrl = buildDirectoryRedirections.get(url) || url;
666
- const rawUrlInfo = rawKitchen.graph.getUrlInfo(rawUrl);
667
- if (!rawUrlInfo) {
668
- throw new Error(
669
- createDetailedMessage(`Cannot find url`, {
670
- url,
671
- "raw urls": Array.from(
672
- buildDirectoryRedirections.values(),
673
- ),
674
- "build urls": Array.from(
675
- buildDirectoryRedirections.keys(),
676
- ),
677
- }),
678
- );
679
- }
680
- // logger.debug(`fetching from raw graph ${url}`)
681
- if (rawUrlInfo.isInline) {
682
- // Inline content, such as <script> inside html, is transformed during the previous phase.
683
- // If we read the inline content it would be considered as the original content.
684
- // - It could be "fixed" by taking into account sourcemap and consider sourcemap sources
685
- // as the original content.
686
- // - But it would not work when sourcemap are not generated
687
- // - would be a bit slower
688
- // - So instead of reading the inline content directly, we search into raw graph
689
- // to get "originalContent" and "sourcemap"
690
- finalUrlInfo.type = rawUrlInfo.type;
691
- finalUrlInfo.subtype = rawUrlInfo.subtype;
692
- return rawUrlInfo;
693
- }
694
- return rawUrlInfo;
695
- };
696
- const { firstReference } = finalUrlInfo;
697
- // .original reference updated during "postbuild":
698
- // happens for "js_module_fallback"
699
- const reference = firstReference.original || firstReference;
700
- // reference injected during "postbuild":
701
- // - happens for "js_module_fallback" injecting "s.js"
702
- if (reference.injected) {
703
- const rawReference =
704
- rawKitchen.graph.rootUrlInfo.dependencies.inject({
705
- type: reference.type,
706
- expectedType: reference.expectedType,
707
- specifier: reference.specifier,
708
- specifierLine: reference.specifierLine,
709
- specifierColumn: reference.specifierColumn,
710
- specifierStart: reference.specifierStart,
711
- specifierEnd: reference.specifierEnd,
712
- });
713
- await rawReference.urlInfo.cook();
714
- return {
715
- type: rawReference.urlInfo.type,
716
- content: rawReference.urlInfo.content,
717
- contentType: rawReference.urlInfo.contentType,
718
- originalContent: rawReference.urlInfo.originalContent,
719
- originalUrl: rawReference.urlInfo.originalUrl,
720
- sourcemap: rawReference.urlInfo.sourcemap,
721
- };
722
- }
723
- if (reference.isInline) {
724
- const prevReference = firstReference.prev;
725
- if (prevReference) {
726
- if (!prevReference.isInline) {
727
- // the reference was inlined
728
- const urlBeforeRedirect =
729
- findKey(finalRedirections, prevReference.url) ||
730
- prevReference.url;
731
- return fromBundleOrRawGraph(urlBeforeRedirect);
732
- }
733
- if (buildDirectoryRedirections.has(prevReference.url)) {
734
- // the prev reference is transformed to fetch underlying resource
735
- // (getWithoutSearchParam)
736
- return fromBundleOrRawGraph(prevReference.url);
737
- }
738
- }
739
- return fromBundleOrRawGraph(firstReference.url);
740
- }
741
- return fromBundleOrRawGraph(reference.url);
742
- },
330
+ const finalKitchen = createKitchen({
331
+ name: "shape",
332
+ logLevel,
333
+ rootDirectoryUrl: sourceDirectoryUrl,
334
+ // here most plugins are not there
335
+ // - no external plugin
336
+ // - no plugin putting reference.mustIgnore on https urls
337
+ // At this stage it's only about redirecting urls to the build directory
338
+ // consequently only a subset or urls are supported
339
+ supportedProtocols: ["file:", "data:", "virtual:", "ignore:"],
340
+ ignore,
341
+ ignoreProtocol: "remove",
342
+ build: true,
343
+ runtimeCompat,
344
+ initialContext: contextSharedDuringBuild,
345
+ initialPluginsMeta: rawKitchen.pluginController.pluginsMeta,
346
+ plugins: [
347
+ jsenvPluginReferenceAnalysis({
348
+ ...referenceAnalysis,
349
+ fetchInlineUrls: false,
350
+ // inlineContent: false,
351
+ }),
352
+ ...(lineBreakNormalization
353
+ ? [jsenvPluginLineBreakNormalization()]
354
+ : []),
355
+ jsenvPluginJsModuleFallback({
356
+ remapImportSpecifier: (specifier) => {
357
+ return buildSpecifierManager.remapPlaceholder(specifier);
743
358
  },
744
- {
745
- name: "jsenv:optimize",
746
- appliesDuring: "build",
747
- transformUrlContent: async (urlInfo) => {
748
- await rawKitchen.pluginController.callAsyncHooks(
749
- "optimizeUrlContent",
750
- urlInfo,
751
- (optimizeReturnValue) => {
752
- urlInfo.mutateContent(optimizeReturnValue);
753
- },
754
- );
755
- },
359
+ }),
360
+ jsenvPluginInlining(),
361
+ {
362
+ name: "jsenv:optimize",
363
+ appliesDuring: "build",
364
+ transformUrlContent: async (urlInfo) => {
365
+ await rawKitchen.pluginController.callAsyncHooks(
366
+ "optimizeUrlContent",
367
+ urlInfo,
368
+ (optimizeReturnValue) => {
369
+ urlInfo.mutateContent(optimizeReturnValue);
370
+ },
371
+ );
756
372
  },
757
- ],
758
- sourcemaps,
759
- sourcemapsSourcesContent,
760
- sourcemapsSourcesRelative: true,
761
- outDirectoryUrl: outDirectoryUrl
762
- ? new URL("postbuild/", outDirectoryUrl)
763
- : undefined,
764
- });
765
- buildVersionsManager = createBuildVersionsManager({
766
- finalKitchen,
767
- versioningMethod,
768
- versionLength,
769
- canUseImportmap:
770
- versioningViaImportmap &&
771
- finalEntryUrls.every((finalEntryUrl) => {
772
- const finalEntryUrlInfo =
773
- finalKitchen.graph.getUrlInfo(finalEntryUrl);
774
- return finalEntryUrlInfo.type === "html";
775
- }) &&
776
- rawKitchen.context.isSupportedOnCurrentClients("importmap"),
777
- getBuildUrlFromBuildSpecifier: (buildSpecifier) =>
778
- buildSpecifierMap.get(buildSpecifier),
779
- });
373
+ },
374
+ ],
375
+ sourcemaps,
376
+ sourcemapsComment: "relative",
377
+ sourcemapsSourcesContent,
378
+ outDirectoryUrl: outDirectoryUrl
379
+ ? new URL("shape/", outDirectoryUrl)
380
+ : undefined,
381
+ });
780
382
 
781
- bundle: {
782
- rawKitchen.pluginController.plugins.forEach((plugin) => {
783
- const bundle = plugin.bundle;
784
- if (!bundle) {
383
+ const buildSpecifierManager = createBuildSpecifierManager({
384
+ rawKitchen,
385
+ finalKitchen,
386
+ logger,
387
+ sourceDirectoryUrl,
388
+ buildDirectoryUrl,
389
+ base,
390
+ assetsDirectory,
391
+
392
+ versioning,
393
+ versioningMethod,
394
+ versionLength,
395
+ canUseImportmap:
396
+ versioningViaImportmap &&
397
+ entryUrls.every((finalEntryUrl) => {
398
+ const entryUrlInfo = rawKitchen.graph.getUrlInfo(finalEntryUrl);
399
+ return entryUrlInfo.type === "html";
400
+ }) &&
401
+ rawKitchen.context.isSupportedOnCurrentClients("importmap"),
402
+ });
403
+ finalKitchen.pluginController.pushPlugin(
404
+ buildSpecifierManager.jsenvPluginMoveToBuildDirectory,
405
+ );
406
+
407
+ const bundlers = {};
408
+ bundle: {
409
+ rawKitchen.pluginController.plugins.forEach((plugin) => {
410
+ const bundle = plugin.bundle;
411
+ if (!bundle) {
412
+ return;
413
+ }
414
+ if (typeof bundle !== "object") {
415
+ throw new Error(
416
+ `bundle must be an object, found "${bundle}" on plugin named "${plugin.name}"`,
417
+ );
418
+ }
419
+ Object.keys(bundle).forEach((type) => {
420
+ const bundleFunction = bundle[type];
421
+ if (!bundleFunction) {
785
422
  return;
786
423
  }
787
- if (typeof bundle !== "object") {
788
- throw new Error(
789
- `bundle must be an object, found "${bundle}" on plugin named "${plugin.name}"`,
790
- );
424
+ const bundlerForThatType = bundlers[type];
425
+ if (bundlerForThatType) {
426
+ // first plugin to define a bundle hook wins
427
+ return;
791
428
  }
792
- Object.keys(bundle).forEach((type) => {
793
- const bundleFunction = bundle[type];
794
- if (!bundleFunction) {
795
- return;
796
- }
797
- const bundlerForThatType = bundlers[type];
798
- if (bundlerForThatType) {
799
- // first plugin to define a bundle hook wins
800
- return;
801
- }
802
- bundlers[type] = {
803
- plugin,
804
- bundleFunction: bundle[type],
805
- urlInfoMap: new Map(),
806
- };
807
- });
429
+ bundlers[type] = {
430
+ plugin,
431
+ bundleFunction: bundle[type],
432
+ urlInfoMap: new Map(),
433
+ };
808
434
  });
809
- const addToBundlerIfAny = (rawUrlInfo) => {
810
- const bundler = bundlers[rawUrlInfo.type];
811
- if (bundler) {
812
- bundler.urlInfoMap.set(rawUrlInfo.url, rawUrlInfo);
435
+ });
436
+ const addToBundlerIfAny = (rawUrlInfo) => {
437
+ const bundler = bundlers[rawUrlInfo.type];
438
+ if (bundler) {
439
+ bundler.urlInfoMap.set(rawUrlInfo.url, rawUrlInfo);
440
+ }
441
+ };
442
+ // ignore unused urls thanks to "forEachUrlInfoStronglyReferenced"
443
+ // it avoid bundling things that are not actually used
444
+ // happens for:
445
+ // - js import assertions
446
+ // - conversion to js classic using ?as_js_classic or ?js_module_fallback
447
+ GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
448
+ rawKitchen.graph.rootUrlInfo,
449
+ (rawUrlInfo) => {
450
+ if (rawUrlInfo.isEntryPoint) {
451
+ addToBundlerIfAny(rawUrlInfo);
813
452
  }
814
- };
815
- // ignore unused urls thanks to "forEachUrlInfoStronglyReferenced"
816
- // it avoid bundling things that are not actually used
817
- // happens for:
818
- // - js import assertions
819
- // - conversion to js classic using ?as_js_classic or ?js_module_fallback
820
- GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
821
- rawKitchen.graph.rootUrlInfo,
822
- (rawUrlInfo) => {
823
- if (rawUrlInfo.isEntryPoint) {
824
- addToBundlerIfAny(rawUrlInfo);
825
- }
826
- if (rawUrlInfo.type === "html") {
827
- rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
828
- if (referenceToOther.isWeak) {
829
- return;
453
+ if (rawUrlInfo.type === "html") {
454
+ rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
455
+ if (referenceToOther.isWeak) {
456
+ return;
457
+ }
458
+ const referencedUrlInfo = referenceToOther.urlInfo;
459
+ if (referencedUrlInfo.isInline) {
460
+ if (referencedUrlInfo.type === "js_module") {
461
+ // bundle inline script type module deps
462
+ referencedUrlInfo.referenceToOthersSet.forEach(
463
+ (jsModuleReferenceToOther) => {
464
+ if (jsModuleReferenceToOther.type === "js_import") {
465
+ const inlineUrlInfo = jsModuleReferenceToOther.urlInfo;
466
+ addToBundlerIfAny(inlineUrlInfo);
467
+ }
468
+ },
469
+ );
830
470
  }
471
+ // inline content cannot be bundled
472
+ return;
473
+ }
474
+ addToBundlerIfAny(referencedUrlInfo);
475
+ });
476
+ rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
477
+ if (
478
+ referenceToOther.isResourceHint &&
479
+ referenceToOther.expectedType === "js_module"
480
+ ) {
831
481
  const referencedUrlInfo = referenceToOther.urlInfo;
832
- if (referencedUrlInfo.isInline) {
833
- if (referencedUrlInfo.type === "js_module") {
834
- // bundle inline script type module deps
835
- referencedUrlInfo.referenceToOthersSet.forEach(
836
- (jsModuleReferenceToOther) => {
837
- if (jsModuleReferenceToOther.type === "js_import") {
838
- const inlineUrlInfo =
839
- jsModuleReferenceToOther.urlInfo;
840
- addToBundlerIfAny(inlineUrlInfo);
841
- }
842
- },
843
- );
844
- }
845
- // inline content cannot be bundled
846
- return;
847
- }
848
- addToBundlerIfAny(referencedUrlInfo);
849
- });
850
- rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
851
482
  if (
852
- referenceToOther.isResourceHint &&
853
- referenceToOther.expectedType === "js_module"
483
+ referencedUrlInfo &&
484
+ // something else than the resource hint is using this url
485
+ referencedUrlInfo.referenceFromOthersSet.size > 0
854
486
  ) {
855
- const referencedUrlInfo = referenceToOther.urlInfo;
856
- if (
857
- referencedUrlInfo &&
858
- // something else than the resource hint is using this url
859
- referencedUrlInfo.referenceFromOthersSet.size > 0
860
- ) {
861
- addToBundlerIfAny(referencedUrlInfo);
862
- }
863
- }
864
- });
865
- return;
866
- }
867
- // File referenced with new URL('./file.js', import.meta.url)
868
- // are entry points that should be bundled
869
- // For instance we will bundle service worker/workers detected like this
870
- if (rawUrlInfo.type === "js_module") {
871
- rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
872
- if (referenceToOther.type === "js_url") {
873
- const referencedUrlInfo = referenceToOther.urlInfo;
874
- for (const referenceFromOther of referencedUrlInfo.referenceFromOthersSet) {
875
- if (referenceFromOther.url === referencedUrlInfo.url) {
876
- if (
877
- referenceFromOther.subtype === "import_dynamic" ||
878
- referenceFromOther.type === "script"
879
- ) {
880
- // will already be bundled
881
- return;
882
- }
883
- }
884
- }
885
487
  addToBundlerIfAny(referencedUrlInfo);
886
- return;
887
- }
888
- if (referenceToOther.type === "js_inline_content") {
889
- // we should bundle it too right?
890
488
  }
891
- });
892
- }
893
- },
894
- );
895
- await Object.keys(bundlers).reduce(async (previous, type) => {
896
- await previous;
897
- const bundler = bundlers[type];
898
- const urlInfosToBundle = Array.from(bundler.urlInfoMap.values());
899
- if (urlInfosToBundle.length === 0) {
489
+ }
490
+ });
900
491
  return;
901
492
  }
902
- const bundleTask = createBuildTask(`bundle "${type}"`);
903
- try {
904
- const bundlerGeneratedUrlInfos =
905
- await rawKitchen.pluginController.callAsyncHook(
906
- {
907
- plugin: bundler.plugin,
908
- hookName: "bundle",
909
- value: bundler.bundleFunction,
910
- },
911
- urlInfosToBundle,
912
- );
913
- Object.keys(bundlerGeneratedUrlInfos).forEach((url) => {
914
- const rawUrlInfo = rawKitchen.graph.getUrlInfo(url);
915
- const bundlerGeneratedUrlInfo = bundlerGeneratedUrlInfos[url];
916
- const bundleUrlInfo = {
917
- type,
918
- subtype: rawUrlInfo ? rawUrlInfo.subtype : undefined,
919
- isEntryPoint: rawUrlInfo ? rawUrlInfo.isEntryPoint : undefined,
920
- filename: rawUrlInfo ? rawUrlInfo.filename : undefined,
921
- originalUrl: rawUrlInfo ? rawUrlInfo.originalUrl : undefined,
922
- originalContent: rawUrlInfo
923
- ? rawUrlInfo.originalContent
924
- : undefined,
925
- ...bundlerGeneratedUrlInfo,
926
- data: {
927
- ...(rawUrlInfo ? rawUrlInfo.data : {}),
928
- ...bundlerGeneratedUrlInfo.data,
929
- fromBundle: true,
930
- },
931
- };
932
- if (bundlerGeneratedUrlInfo.sourceUrls) {
933
- bundlerGeneratedUrlInfo.sourceUrls.forEach((sourceUrl) => {
934
- const sourceRawUrlInfo =
935
- rawKitchen.graph.getUrlInfo(sourceUrl);
936
- if (sourceRawUrlInfo) {
937
- sourceRawUrlInfo.data.bundled = true;
493
+ // File referenced with new URL('./file.js', import.meta.url)
494
+ // are entry points that should be bundled
495
+ // For instance we will bundle service worker/workers detected like this
496
+ if (rawUrlInfo.type === "js_module") {
497
+ rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
498
+ if (referenceToOther.type === "js_url") {
499
+ const referencedUrlInfo = referenceToOther.urlInfo;
500
+ for (const referenceFromOther of referencedUrlInfo.referenceFromOthersSet) {
501
+ if (referenceFromOther.url === referencedUrlInfo.url) {
502
+ if (
503
+ referenceFromOther.subtype === "import_dynamic" ||
504
+ referenceFromOther.type === "script"
505
+ ) {
506
+ // will already be bundled
507
+ return;
508
+ }
938
509
  }
939
- });
940
- }
941
- const buildUrl = buildUrlsGenerator.generate(url, {
942
- urlInfo: bundleUrlInfo,
943
- });
944
- bundleRedirections.set(url, buildUrl);
945
- if (urlIsInsideOf(url, buildDirectoryUrl)) {
946
- if (bundlerGeneratedUrlInfo.data.isDynamicEntry) {
947
- const rawUrlInfo = rawKitchen.graph.getUrlInfo(
948
- bundlerGeneratedUrlInfo.originalUrl,
949
- );
950
- rawUrlInfo.data.bundled = false;
951
- bundleRedirections.set(
952
- bundlerGeneratedUrlInfo.originalUrl,
953
- buildUrl,
954
- );
955
- associateBuildUrlAndRawUrl(
956
- buildUrl,
957
- bundlerGeneratedUrlInfo.originalUrl,
958
- "bundle",
959
- );
960
- } else {
961
- bundleUrlInfo.data.generatedToShareCode = true;
962
510
  }
963
- } else {
964
- associateBuildUrlAndRawUrl(buildUrl, url, "bundle");
965
- }
966
- bundleUrlInfos[buildUrl] = bundleUrlInfo;
967
- if (buildUrl.includes("?")) {
968
- bundleUrlInfos[asUrlWithoutSearch(buildUrl)] = bundleUrlInfo;
511
+ addToBundlerIfAny(referencedUrlInfo);
512
+ return;
969
513
  }
970
- if (bundlerGeneratedUrlInfo.data.bundleRelativeUrl) {
971
- const urlForBundler = new URL(
972
- bundlerGeneratedUrlInfo.data.bundleRelativeUrl,
973
- buildDirectoryUrl,
974
- ).href;
975
- if (urlForBundler !== buildUrl) {
976
- bundleInternalRedirections.set(urlForBundler, buildUrl);
977
- }
514
+ if (referenceToOther.type === "js_inline_content") {
515
+ // we should bundle it too right?
978
516
  }
979
517
  });
980
- } catch (e) {
981
- bundleTask.fail();
982
- throw e;
983
518
  }
984
- bundleTask.done();
985
- }, Promise.resolve());
986
- }
987
- reload_in_build_directory: {
988
- const generateBuildGraph = createBuildTask("generate build graph");
519
+ },
520
+ );
521
+ for (const type of Object.keys(bundlers)) {
522
+ const bundler = bundlers[type];
523
+ const urlInfosToBundle = Array.from(bundler.urlInfoMap.values());
524
+ if (urlInfosToBundle.length === 0) {
525
+ continue;
526
+ }
527
+ const bundleTask = createBuildTask(`bundle "${type}"`);
989
528
  try {
990
- if (outDirectoryUrl) {
991
- await ensureEmptyDirectory(new URL(`postbuild/`, outDirectoryUrl));
992
- }
993
- const finalRootUrlInfo = finalKitchen.graph.rootUrlInfo;
994
- await finalRootUrlInfo.dependencies.startCollecting(() => {
995
- entryUrls.forEach((entryUrl) => {
996
- const entryReference = finalRootUrlInfo.dependencies.found({
997
- trace: { message: `entryPoint` },
998
- isEntryPoint: true,
999
- type: "entry_point",
1000
- specifier: entryUrl,
1001
- });
1002
- finalEntryUrls.push(entryReference.url);
1003
- });
1004
- });
1005
- await finalRootUrlInfo.cookDependencies({
1006
- operation: buildOperation,
529
+ await buildSpecifierManager.applyBundling({
530
+ bundler,
531
+ urlInfosToBundle,
1007
532
  });
1008
533
  } catch (e) {
1009
- generateBuildGraph.fail();
534
+ bundleTask.fail();
1010
535
  throw e;
1011
536
  }
1012
- generateBuildGraph.done();
537
+ bundleTask.done();
1013
538
  }
1014
539
  }
1015
540
 
1016
- refine: {
1017
- apply_versioning: {
1018
- if (!versioning) {
1019
- break apply_versioning;
1020
- }
1021
- // see also "New hashing algorithm that "fixes (nearly) everything"
1022
- // at https://github.com/rollup/rollup/pull/4543
1023
- const versioningTask = createBuildTask("apply versioning");
1024
- try {
1025
- await buildVersionsManager.applyVersioning(finalKitchen);
1026
- } catch (e) {
1027
- versioningTask.fail();
1028
- throw e;
541
+ shape: {
542
+ finalKitchen.context.buildStep = "shape";
543
+ const generateBuildGraph = createBuildTask("generate build graph");
544
+ try {
545
+ if (outDirectoryUrl) {
546
+ await ensureEmptyDirectory(new URL(`shape/`, outDirectoryUrl));
1029
547
  }
1030
- versioningTask.done();
548
+ const finalRootUrlInfo = finalKitchen.graph.rootUrlInfo;
549
+ await finalRootUrlInfo.dependencies.startCollecting(() => {
550
+ entryUrls.forEach((entryUrl) => {
551
+ finalRootUrlInfo.dependencies.found({
552
+ trace: { message: `entryPoint` },
553
+ isEntryPoint: true,
554
+ type: "entry_point",
555
+ specifier: entryUrl,
556
+ });
557
+ });
558
+ });
559
+ await finalRootUrlInfo.cookDependencies({
560
+ operation: buildOperation,
561
+ });
562
+ } catch (e) {
563
+ generateBuildGraph.fail();
564
+ throw e;
565
+ }
566
+ generateBuildGraph.done();
567
+ }
568
+
569
+ refine: {
570
+ finalKitchen.context.buildStep = "refine";
571
+ replace_placeholders: {
572
+ await buildSpecifierManager.replacePlaceholders();
1031
573
  }
1032
574
  cleanup_jsenv_attributes_from_html: {
1033
575
  GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
@@ -1052,293 +594,28 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
1052
594
  * - because of import assertions transpilation (file is inlined into JS)
1053
595
  */
1054
596
  resync_resource_hints: {
1055
- const actions = [];
1056
- GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
1057
- if (urlInfo.type !== "html") {
1058
- return;
1059
- }
1060
- const htmlAst = parseHtmlString(urlInfo.content, {
1061
- storeOriginalPositions: false,
1062
- });
1063
- const mutations = [];
1064
- const hintsToInject = {};
1065
- visitHtmlNodes(htmlAst, {
1066
- link: (node) => {
1067
- const href = getHtmlNodeAttribute(node, "href");
1068
- if (href === undefined || href.startsWith("data:")) {
1069
- return;
1070
- }
1071
- const rel = getHtmlNodeAttribute(node, "rel");
1072
- const isResourceHint = [
1073
- "preconnect",
1074
- "dns-prefetch",
1075
- "prefetch",
1076
- "preload",
1077
- "modulepreload",
1078
- ].includes(rel);
1079
- if (!isResourceHint) {
1080
- return;
1081
- }
1082
- const onBuildUrl = (buildUrl) => {
1083
- const buildUrlInfo = buildUrl
1084
- ? finalKitchen.graph.getUrlInfo(buildUrl)
1085
- : null;
1086
- if (!buildUrlInfo) {
1087
- logger.warn(
1088
- `remove resource hint because cannot find "${href}" in the graph`,
1089
- );
1090
- mutations.push(() => {
1091
- removeHtmlNode(node);
1092
- });
1093
- return;
1094
- }
1095
- if (!buildUrlInfo.isUsed()) {
1096
- let rawUrl = buildDirectoryRedirections.get(buildUrl);
1097
- if (!rawUrl && rawKitchen.graph.getUrlInfo(buildUrl)) {
1098
- rawUrl = buildUrl;
1099
- }
1100
- if (rawUrl) {
1101
- const rawUrlInfo = rawKitchen.graph.getUrlInfo(rawUrl);
1102
- if (rawUrlInfo && rawUrlInfo.data.bundled) {
1103
- logger.warn(
1104
- `remove resource hint on "${rawUrl}" because it was bundled`,
1105
- );
1106
- mutations.push(() => {
1107
- removeHtmlNode(node);
1108
- });
1109
- return;
1110
- }
1111
- }
1112
- logger.warn(
1113
- `remove resource hint on "${href}" because it is not used anymore`,
1114
- );
1115
- mutations.push(() => {
1116
- removeHtmlNode(node);
1117
- });
1118
- return;
1119
- }
1120
- const buildUrlFormatted = buildUrlInfo.url;
1121
- const buildSpecifier = findKey(
1122
- buildSpecifierMap,
1123
- buildUrlFormatted,
1124
- );
1125
- const buildSpecifierVersioned =
1126
- buildVersionsManager.getBuildSpecifierVersioned(
1127
- buildSpecifier,
1128
- );
1129
- let specifier = buildSpecifierVersioned || buildSpecifier;
1130
- mutations.push(() => {
1131
- setHtmlNodeAttributes(node, {
1132
- href: specifier,
1133
- ...(buildUrlInfo.type === "js_classic"
1134
- ? { crossorigin: undefined }
1135
- : {}),
1136
- });
1137
- });
1138
- for (const referenceToOther of buildUrlInfo.referenceToOthersSet) {
1139
- if (referenceToOther.isWeak) {
1140
- continue;
1141
- }
1142
- const referencedUrlInfo = referenceToOther.urlInfo;
1143
- if (referencedUrlInfo.data.generatedToShareCode) {
1144
- hintsToInject[referencedUrlInfo.url] = node;
1145
- }
1146
- }
1147
- };
1148
- if (href.startsWith("file:")) {
1149
- let url = href;
1150
- url = rawRedirections.get(url) || url;
1151
- url = bundleRedirections.get(url) || url;
1152
- url = bundleInternalRedirections.get(url) || url;
1153
- url = finalRedirections.get(url) || url;
1154
- url = findKey(buildDirectoryRedirections, url) || url;
1155
- onBuildUrl(url);
1156
- } else {
1157
- onBuildUrl(null);
1158
- }
1159
- },
1160
- });
1161
- Object.keys(hintsToInject).forEach((urlToHint) => {
1162
- const hintNode = hintsToInject[urlToHint];
1163
- const urlFormatted = urlToHint;
1164
- const buildSpecifier = findKey(buildSpecifierMap, urlFormatted);
1165
- const found = findHtmlNode(htmlAst, (htmlNode) => {
1166
- return (
1167
- htmlNode.nodeName === "link" &&
1168
- getHtmlNodeAttribute(htmlNode, "href") === buildSpecifier
1169
- );
1170
- });
1171
- if (!found) {
1172
- const buildSpecifierVersioned =
1173
- buildVersionsManager.getBuildSpecifierVersioned(buildSpecifier);
1174
- const href = buildSpecifierVersioned || buildSpecifier;
1175
- mutations.push(() => {
1176
- const nodeToInsert = createHtmlNode({
1177
- tagName: "link",
1178
- href,
1179
- rel: getHtmlNodeAttribute(hintNode, "rel"),
1180
- as: getHtmlNodeAttribute(hintNode, "as"),
1181
- type: getHtmlNodeAttribute(hintNode, "type"),
1182
- crossorigin: getHtmlNodeAttribute(hintNode, "crossorigin"),
1183
- });
1184
- insertHtmlNodeAfter(nodeToInsert, hintNode);
1185
- });
1186
- }
1187
- });
1188
- if (mutations.length > 0) {
1189
- actions.push(() => {
1190
- mutations.forEach((mutation) => mutation());
1191
- urlInfo.mutateContent({
1192
- content: stringifyHtmlAst(htmlAst),
1193
- });
1194
- });
1195
- }
1196
- });
1197
- if (actions.length > 0) {
597
+ const resync = buildSpecifierManager.prepareResyncResourceHints();
598
+ if (resync) {
1198
599
  const resyncTask = createBuildTask("resync resource hints");
1199
- actions.map((resourceHintAction) => resourceHintAction());
600
+ resync();
1200
601
  buildOperation.throwIfAborted();
1201
602
  resyncTask.done();
1202
603
  }
1203
604
  }
1204
605
  inject_urls_in_service_workers: {
1205
- const serviceWorkerEntryUrlInfos = GRAPH_VISITOR.filter(
1206
- finalKitchen.graph,
1207
- (finalUrlInfo) => {
1208
- return (
1209
- finalUrlInfo.subtype === "service_worker" &&
1210
- finalUrlInfo.isEntryPoint &&
1211
- finalUrlInfo.isUsed()
1212
- );
1213
- },
1214
- );
1215
- if (serviceWorkerEntryUrlInfos.length > 0) {
606
+ const inject = buildSpecifierManager.prepareServiceWorkerUrlInjection();
607
+ if (inject) {
1216
608
  const urlsInjectionInSw = createBuildTask(
1217
609
  "inject urls in service worker",
1218
610
  );
1219
- const serviceWorkerResources = {};
1220
- GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
1221
- finalKitchen.graph.rootUrlInfo,
1222
- (urlInfo) => {
1223
- if (!urlInfo.url.startsWith("file:")) {
1224
- return;
1225
- }
1226
- if (urlInfo.isInline) {
1227
- return;
1228
- }
1229
- if (!canUseVersionedUrl(urlInfo)) {
1230
- // when url is not versioned we compute a "version" for that url anyway
1231
- // so that service worker source still changes and navigator
1232
- // detect there is a change
1233
- const buildSpecifier = findKey(buildSpecifierMap, urlInfo.url);
1234
- serviceWorkerResources[buildSpecifier] = {
1235
- version: buildVersionsManager.getVersion(urlInfo),
1236
- };
1237
- return;
1238
- }
1239
- const buildSpecifier = findKey(buildSpecifierMap, urlInfo.url);
1240
- const buildSpecifierVersioned =
1241
- buildVersionsManager.getBuildSpecifierVersioned(buildSpecifier);
1242
- serviceWorkerResources[buildSpecifier] = {
1243
- version: buildVersionsManager.getVersion(urlInfo),
1244
- versionedUrl: buildSpecifierVersioned,
1245
- };
1246
- },
1247
- );
1248
- for (const serviceWorkerEntryUrlInfo of serviceWorkerEntryUrlInfos) {
1249
- const serviceWorkerResourcesWithoutSwScriptItSelf = {
1250
- ...serviceWorkerResources,
1251
- };
1252
- const serviceWorkerBuildSpecifier = findKey(
1253
- buildSpecifierMap,
1254
- serviceWorkerEntryUrlInfo.url,
1255
- );
1256
- delete serviceWorkerResourcesWithoutSwScriptItSelf[
1257
- serviceWorkerBuildSpecifier
1258
- ];
1259
- await prependContent(serviceWorkerEntryUrlInfo, {
1260
- type: "js_classic",
1261
- content: `\nself.resourcesFromJsenvBuild = ${JSON.stringify(
1262
- serviceWorkerResourcesWithoutSwScriptItSelf,
1263
- null,
1264
- " ",
1265
- )};\n`,
1266
- });
1267
- }
611
+ await inject();
1268
612
  urlsInjectionInSw.done();
613
+ buildOperation.throwIfAborted();
1269
614
  }
1270
- buildOperation.throwIfAborted();
1271
615
  }
1272
616
  }
1273
-
1274
- const buildManifest = {};
1275
- const buildContents = {};
1276
- const buildInlineRelativeUrls = [];
1277
- const getBuildRelativeUrl = (url) => {
1278
- const urlObject = new URL(url);
1279
- urlObject.searchParams.delete("js_module_fallback");
1280
- urlObject.searchParams.delete("as_css_module");
1281
- urlObject.searchParams.delete("as_json_module");
1282
- urlObject.searchParams.delete("as_text_module");
1283
- url = urlObject.href;
1284
- const buildRelativeUrl = urlToRelativeUrl(url, buildDirectoryUrl);
1285
- return buildRelativeUrl;
1286
- };
1287
- GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
1288
- finalKitchen.graph.rootUrlInfo,
1289
- (urlInfo) => {
1290
- if (!urlInfo.url.startsWith("file:")) {
1291
- return;
1292
- }
1293
- if (urlInfo.type === "directory") {
1294
- return;
1295
- }
1296
- if (urlInfo.isInline) {
1297
- const buildRelativeUrl = getBuildRelativeUrl(urlInfo.url);
1298
- buildContents[buildRelativeUrl] = urlInfo.content;
1299
- buildInlineRelativeUrls.push(buildRelativeUrl);
1300
- } else {
1301
- const buildRelativeUrl = getBuildRelativeUrl(urlInfo.url);
1302
- if (
1303
- buildVersionsManager.getVersion(urlInfo) &&
1304
- canUseVersionedUrl(urlInfo)
1305
- ) {
1306
- const buildSpecifier = findKey(buildSpecifierMap, urlInfo.url);
1307
- const buildSpecifierVersioned =
1308
- buildVersionsManager.getBuildSpecifierVersioned(buildSpecifier);
1309
- const buildUrlVersioned = asBuildUrlVersioned({
1310
- buildSpecifierVersioned,
1311
- buildDirectoryUrl,
1312
- });
1313
- const buildRelativeUrlVersioned = urlToRelativeUrl(
1314
- buildUrlVersioned,
1315
- buildDirectoryUrl,
1316
- );
1317
- if (versioningMethod === "search_param") {
1318
- buildContents[buildRelativeUrl] = urlInfo.content;
1319
- } else {
1320
- buildContents[buildRelativeUrlVersioned] = urlInfo.content;
1321
- }
1322
- buildManifest[buildRelativeUrl] = buildRelativeUrlVersioned;
1323
- } else {
1324
- buildContents[buildRelativeUrl] = urlInfo.content;
1325
- }
1326
- }
1327
- },
1328
- );
1329
- const buildFileContents = {};
1330
- const buildInlineContents = {};
1331
- Object.keys(buildContents)
1332
- .sort((a, b) => comparePathnames(a, b))
1333
- .forEach((buildRelativeUrl) => {
1334
- if (buildInlineRelativeUrls.includes(buildRelativeUrl)) {
1335
- buildInlineContents[buildRelativeUrl] =
1336
- buildContents[buildRelativeUrl];
1337
- } else {
1338
- buildFileContents[buildRelativeUrl] = buildContents[buildRelativeUrl];
1339
- }
1340
- });
1341
-
617
+ const { buildFileContents, buildInlineContents, buildManifest } =
618
+ buildSpecifierManager.getBuildInfo();
1342
619
  if (writeOnFileSystem) {
1343
620
  const writingFiles = createBuildTask("write files in build directory");
1344
621
  if (directoryToClean) {
@@ -1439,58 +716,3 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
1439
716
  await firstBuildPromise;
1440
717
  return stopWatchingSourceFiles;
1441
718
  };
1442
-
1443
- const findKey = (map, value) => {
1444
- for (const [keyCandidate, valueCandidate] of map) {
1445
- if (valueCandidate === value) {
1446
- return keyCandidate;
1447
- }
1448
- }
1449
- return undefined;
1450
- };
1451
-
1452
- const shouldApplyVersioningOnReference = (reference) => {
1453
- if (reference.isInline) {
1454
- return false;
1455
- }
1456
- if (reference.next && reference.next.isInline) {
1457
- return false;
1458
- }
1459
- // specifier comes from "normalize" hook done a bit earlier in this file
1460
- // we want to get back their build url to access their infos
1461
- const referencedUrlInfo = reference.urlInfo;
1462
- if (!canUseVersionedUrl(referencedUrlInfo)) {
1463
- return false;
1464
- }
1465
- if (referencedUrlInfo.type === "sourcemap") {
1466
- return false;
1467
- }
1468
- return true;
1469
- };
1470
-
1471
- const canUseVersionedUrl = (urlInfo) => {
1472
- if (urlInfo.isRoot) {
1473
- return false;
1474
- }
1475
- if (urlInfo.isEntryPoint) {
1476
- return false;
1477
- }
1478
- return urlInfo.type !== "webmanifest";
1479
- };
1480
-
1481
- const asBuildUrlVersioned = ({
1482
- buildSpecifierVersioned,
1483
- buildDirectoryUrl,
1484
- }) => {
1485
- if (buildSpecifierVersioned[0] === "/") {
1486
- return new URL(buildSpecifierVersioned.slice(1), buildDirectoryUrl).href;
1487
- }
1488
- const buildUrl = new URL(buildSpecifierVersioned, buildDirectoryUrl).href;
1489
- if (buildUrl.startsWith(buildDirectoryUrl)) {
1490
- return buildUrl;
1491
- }
1492
- // it's likely "base" parameter was set to an url origin like "https://cdn.example.com"
1493
- // let's move url to build directory
1494
- const { pathname, search, hash } = new URL(buildSpecifierVersioned);
1495
- return `${buildDirectoryUrl}${pathname}${search}${hash}`;
1496
- };