@jsenv/core 37.1.4 → 38.0.0

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 +3221 -2483
  3. package/package.json +16 -15
  4. package/src/build/build.js +246 -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":
@@ -117,10 +92,10 @@ export const build = async ({
117
92
  buildDirectoryUrl,
118
93
  entryPoints = {},
119
94
  assetsDirectory = "",
120
- ignore,
121
-
122
95
  runtimeCompat = defaultRuntimeCompat,
123
96
  base = runtimeCompat.node ? "./" : "/",
97
+ ignore,
98
+
124
99
  plugins = [],
125
100
  referenceAnalysis = {},
126
101
  nodeEsmResolution,
@@ -129,6 +104,8 @@ export const build = async ({
129
104
  directoryReferenceAllowed,
130
105
  scenarioPlaceholders,
131
106
  transpilation = {},
107
+ bundling = true,
108
+ minification = !runtimeCompat.node,
132
109
  versioning = !runtimeCompat.node,
133
110
  versioningMethod = "search_param", // "filename", "search_param"
134
111
  versioningViaImportmap = true,
@@ -205,6 +182,12 @@ export const build = async ({
205
182
  `versioningMethod must be "filename" or "search_param", got ${versioning}`,
206
183
  );
207
184
  }
185
+ if (bundling === true) {
186
+ bundling = {};
187
+ }
188
+ if (minification === true) {
189
+ minification = {};
190
+ }
208
191
  }
209
192
 
210
193
  const operation = Abort.startOperation();
@@ -231,26 +214,6 @@ export const build = async ({
231
214
  }
232
215
  }
233
216
 
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
217
  const runBuild = async ({ signal, logLevel }) => {
255
218
  const logger = createLogger({ logLevel });
256
219
  const createBuildTask = (label) => {
@@ -274,30 +237,13 @@ build ${entryPointKeys.length} entry points`);
274
237
  key.includes("?js_module_fallback"),
275
238
  );
276
239
  const rawRedirections = new Map();
277
- const bundleRedirections = new Map();
278
- const bundleInternalRedirections = new Map();
279
- const finalRedirections = new Map();
280
240
  const entryUrls = [];
281
241
  const contextSharedDuringBuild = {
242
+ buildStep: "craft",
282
243
  buildDirectoryUrl,
283
244
  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
- })(),
245
+ versioning,
246
+ versioningViaImportmap,
301
247
  };
302
248
  const rawKitchen = createKitchen({
303
249
  signal,
@@ -309,9 +255,11 @@ build ${entryPointKeys.length} entry points`);
309
255
  ignoreProtocol: "keep",
310
256
  build: true,
311
257
  runtimeCompat,
312
- baseContext: contextSharedDuringBuild,
258
+ initialContext: contextSharedDuringBuild,
313
259
  plugins: [
314
260
  ...plugins,
261
+ ...(bundling ? [jsenvPluginBundling(bundling)] : []),
262
+ ...(minification ? [jsenvPluginMinification(minification)] : []),
315
263
  {
316
264
  appliesDuring: "build",
317
265
  fetchUrlContent: (urlInfo) => {
@@ -334,9 +282,8 @@ build ${entryPointKeys.length} entry points`);
334
282
  transpilation: {
335
283
  babelHelpersAsImport: !explicitJsModuleFallback,
336
284
  ...transpilation,
337
- jsModuleFallbackOnJsClassic: false,
285
+ jsModuleFallback: false,
338
286
  },
339
-
340
287
  inlining: false,
341
288
  scenarioPlaceholders,
342
289
  }),
@@ -344,38 +291,14 @@ build ${entryPointKeys.length} entry points`);
344
291
  sourcemaps,
345
292
  sourcemapsSourcesContent,
346
293
  outDirectoryUrl: outDirectoryUrl
347
- ? new URL("prebuild/", outDirectoryUrl)
294
+ ? new URL("craft/", outDirectoryUrl)
348
295
  : undefined,
349
296
  });
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
297
  craft: {
375
298
  const generateSourceGraph = createBuildTask("generate source graph");
376
299
  try {
377
300
  if (outDirectoryUrl) {
378
- await ensureEmptyDirectory(new URL(`build/`, outDirectoryUrl));
301
+ await ensureEmptyDirectory(new URL(`craft/`, outDirectoryUrl));
379
302
  }
380
303
  const rawRootUrlInfo = rawKitchen.graph.rootUrlInfo;
381
304
  await rawRootUrlInfo.dependencies.startCollecting(() => {
@@ -385,7 +308,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
385
308
  isEntryPoint: true,
386
309
  type: "entry_point",
387
310
  specifier: key,
388
- filename: entryPoints[key],
311
+ filenameHint: entryPoints[key],
389
312
  });
390
313
  entryUrls.push(entryReference.url);
391
314
  });
@@ -400,634 +323,249 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
400
323
  generateSourceGraph.done();
401
324
  }
402
325
 
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
- },
326
+ const finalKitchen = createKitchen({
327
+ name: "shape",
328
+ logLevel,
329
+ rootDirectoryUrl: sourceDirectoryUrl,
330
+ // here most plugins are not there
331
+ // - no external plugin
332
+ // - no plugin putting reference.mustIgnore on https urls
333
+ // At this stage it's only about redirecting urls to the build directory
334
+ // consequently only a subset or urls are supported
335
+ supportedProtocols: ["file:", "data:", "virtual:", "ignore:"],
336
+ ignore,
337
+ ignoreProtocol: "remove",
338
+ build: true,
339
+ runtimeCompat,
340
+ initialContext: contextSharedDuringBuild,
341
+ initialPluginsMeta: rawKitchen.pluginController.pluginsMeta,
342
+ plugins: [
343
+ jsenvPluginReferenceAnalysis({
344
+ ...referenceAnalysis,
345
+ fetchInlineUrls: false,
346
+ // inlineContent: false,
347
+ }),
348
+ ...(lineBreakNormalization
349
+ ? [jsenvPluginLineBreakNormalization()]
350
+ : []),
351
+ jsenvPluginJsModuleFallback({
352
+ remapImportSpecifier: (specifier) => {
353
+ return buildSpecifierManager.remapPlaceholder(specifier);
743
354
  },
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
- },
355
+ }),
356
+ jsenvPluginInlining(),
357
+ {
358
+ name: "jsenv:optimize",
359
+ appliesDuring: "build",
360
+ transformUrlContent: async (urlInfo) => {
361
+ await rawKitchen.pluginController.callAsyncHooks(
362
+ "optimizeUrlContent",
363
+ urlInfo,
364
+ (optimizeReturnValue) => {
365
+ urlInfo.mutateContent(optimizeReturnValue);
366
+ },
367
+ );
756
368
  },
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
- });
369
+ },
370
+ ],
371
+ sourcemaps,
372
+ sourcemapsComment: "relative",
373
+ sourcemapsSourcesContent,
374
+ outDirectoryUrl: outDirectoryUrl
375
+ ? new URL("shape/", outDirectoryUrl)
376
+ : undefined,
377
+ });
780
378
 
781
- bundle: {
782
- rawKitchen.pluginController.plugins.forEach((plugin) => {
783
- const bundle = plugin.bundle;
784
- if (!bundle) {
379
+ const buildSpecifierManager = createBuildSpecifierManager({
380
+ rawKitchen,
381
+ finalKitchen,
382
+ logger,
383
+ sourceDirectoryUrl,
384
+ buildDirectoryUrl,
385
+ base,
386
+ assetsDirectory,
387
+
388
+ versioning,
389
+ versioningMethod,
390
+ versionLength,
391
+ canUseImportmap:
392
+ versioningViaImportmap &&
393
+ entryUrls.every((finalEntryUrl) => {
394
+ const entryUrlInfo = rawKitchen.graph.getUrlInfo(finalEntryUrl);
395
+ return entryUrlInfo.type === "html";
396
+ }) &&
397
+ rawKitchen.context.isSupportedOnCurrentClients("importmap"),
398
+ });
399
+ finalKitchen.pluginController.pushPlugin(
400
+ buildSpecifierManager.jsenvPluginMoveToBuildDirectory,
401
+ );
402
+
403
+ const bundlers = {};
404
+ bundle: {
405
+ rawKitchen.pluginController.plugins.forEach((plugin) => {
406
+ const bundle = plugin.bundle;
407
+ if (!bundle) {
408
+ return;
409
+ }
410
+ if (typeof bundle !== "object") {
411
+ throw new Error(
412
+ `bundle must be an object, found "${bundle}" on plugin named "${plugin.name}"`,
413
+ );
414
+ }
415
+ Object.keys(bundle).forEach((type) => {
416
+ const bundleFunction = bundle[type];
417
+ if (!bundleFunction) {
785
418
  return;
786
419
  }
787
- if (typeof bundle !== "object") {
788
- throw new Error(
789
- `bundle must be an object, found "${bundle}" on plugin named "${plugin.name}"`,
790
- );
420
+ const bundlerForThatType = bundlers[type];
421
+ if (bundlerForThatType) {
422
+ // first plugin to define a bundle hook wins
423
+ return;
791
424
  }
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
- });
425
+ bundlers[type] = {
426
+ plugin,
427
+ bundleFunction: bundle[type],
428
+ urlInfoMap: new Map(),
429
+ };
808
430
  });
809
- const addToBundlerIfAny = (rawUrlInfo) => {
810
- const bundler = bundlers[rawUrlInfo.type];
811
- if (bundler) {
812
- bundler.urlInfoMap.set(rawUrlInfo.url, rawUrlInfo);
431
+ });
432
+ const addToBundlerIfAny = (rawUrlInfo) => {
433
+ const bundler = bundlers[rawUrlInfo.type];
434
+ if (bundler) {
435
+ bundler.urlInfoMap.set(rawUrlInfo.url, rawUrlInfo);
436
+ }
437
+ };
438
+ // ignore unused urls thanks to "forEachUrlInfoStronglyReferenced"
439
+ // it avoid bundling things that are not actually used
440
+ // happens for:
441
+ // - js import assertions
442
+ // - conversion to js classic using ?as_js_classic or ?js_module_fallback
443
+ GRAPH_VISITOR.forEachUrlInfoStronglyReferenced(
444
+ rawKitchen.graph.rootUrlInfo,
445
+ (rawUrlInfo) => {
446
+ if (rawUrlInfo.isEntryPoint) {
447
+ addToBundlerIfAny(rawUrlInfo);
813
448
  }
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;
449
+ if (rawUrlInfo.type === "html") {
450
+ rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
451
+ if (referenceToOther.isWeak) {
452
+ return;
453
+ }
454
+ const referencedUrlInfo = referenceToOther.urlInfo;
455
+ if (referencedUrlInfo.isInline) {
456
+ if (referencedUrlInfo.type === "js_module") {
457
+ // bundle inline script type module deps
458
+ referencedUrlInfo.referenceToOthersSet.forEach(
459
+ (jsModuleReferenceToOther) => {
460
+ if (jsModuleReferenceToOther.type === "js_import") {
461
+ const inlineUrlInfo = jsModuleReferenceToOther.urlInfo;
462
+ addToBundlerIfAny(inlineUrlInfo);
463
+ }
464
+ },
465
+ );
830
466
  }
467
+ // inline content cannot be bundled
468
+ return;
469
+ }
470
+ addToBundlerIfAny(referencedUrlInfo);
471
+ });
472
+ rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
473
+ if (
474
+ referenceToOther.isResourceHint &&
475
+ referenceToOther.expectedType === "js_module"
476
+ ) {
831
477
  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
478
  if (
852
- referenceToOther.isResourceHint &&
853
- referenceToOther.expectedType === "js_module"
479
+ referencedUrlInfo &&
480
+ // something else than the resource hint is using this url
481
+ referencedUrlInfo.referenceFromOthersSet.size > 0
854
482
  ) {
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
483
  addToBundlerIfAny(referencedUrlInfo);
886
- return;
887
- }
888
- if (referenceToOther.type === "js_inline_content") {
889
- // we should bundle it too right?
890
484
  }
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) {
485
+ }
486
+ });
900
487
  return;
901
488
  }
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;
489
+ // File referenced with new URL('./file.js', import.meta.url)
490
+ // are entry points that should be bundled
491
+ // For instance we will bundle service worker/workers detected like this
492
+ if (rawUrlInfo.type === "js_module") {
493
+ rawUrlInfo.referenceToOthersSet.forEach((referenceToOther) => {
494
+ if (referenceToOther.type === "js_url") {
495
+ const referencedUrlInfo = referenceToOther.urlInfo;
496
+ for (const referenceFromOther of referencedUrlInfo.referenceFromOthersSet) {
497
+ if (referenceFromOther.url === referencedUrlInfo.url) {
498
+ if (
499
+ referenceFromOther.subtype === "import_dynamic" ||
500
+ referenceFromOther.type === "script"
501
+ ) {
502
+ // will already be bundled
503
+ return;
504
+ }
938
505
  }
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
506
  }
963
- } else {
964
- associateBuildUrlAndRawUrl(buildUrl, url, "bundle");
965
- }
966
- bundleUrlInfos[buildUrl] = bundleUrlInfo;
967
- if (buildUrl.includes("?")) {
968
- bundleUrlInfos[asUrlWithoutSearch(buildUrl)] = bundleUrlInfo;
507
+ addToBundlerIfAny(referencedUrlInfo);
508
+ return;
969
509
  }
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
- }
510
+ if (referenceToOther.type === "js_inline_content") {
511
+ // we should bundle it too right?
978
512
  }
979
513
  });
980
- } catch (e) {
981
- bundleTask.fail();
982
- throw e;
983
514
  }
984
- bundleTask.done();
985
- }, Promise.resolve());
986
- }
987
- reload_in_build_directory: {
988
- const generateBuildGraph = createBuildTask("generate build graph");
515
+ },
516
+ );
517
+ for (const type of Object.keys(bundlers)) {
518
+ const bundler = bundlers[type];
519
+ const urlInfosToBundle = Array.from(bundler.urlInfoMap.values());
520
+ if (urlInfosToBundle.length === 0) {
521
+ continue;
522
+ }
523
+ const bundleTask = createBuildTask(`bundle "${type}"`);
989
524
  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,
525
+ await buildSpecifierManager.applyBundling({
526
+ bundler,
527
+ urlInfosToBundle,
1007
528
  });
1008
529
  } catch (e) {
1009
- generateBuildGraph.fail();
530
+ bundleTask.fail();
1010
531
  throw e;
1011
532
  }
1012
- generateBuildGraph.done();
533
+ bundleTask.done();
1013
534
  }
1014
535
  }
1015
536
 
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;
537
+ shape: {
538
+ finalKitchen.context.buildStep = "shape";
539
+ const generateBuildGraph = createBuildTask("generate build graph");
540
+ try {
541
+ if (outDirectoryUrl) {
542
+ await ensureEmptyDirectory(new URL(`shape/`, outDirectoryUrl));
1029
543
  }
1030
- versioningTask.done();
544
+ const finalRootUrlInfo = finalKitchen.graph.rootUrlInfo;
545
+ await finalRootUrlInfo.dependencies.startCollecting(() => {
546
+ entryUrls.forEach((entryUrl) => {
547
+ finalRootUrlInfo.dependencies.found({
548
+ trace: { message: `entryPoint` },
549
+ isEntryPoint: true,
550
+ type: "entry_point",
551
+ specifier: entryUrl,
552
+ });
553
+ });
554
+ });
555
+ await finalRootUrlInfo.cookDependencies({
556
+ operation: buildOperation,
557
+ });
558
+ } catch (e) {
559
+ generateBuildGraph.fail();
560
+ throw e;
561
+ }
562
+ generateBuildGraph.done();
563
+ }
564
+
565
+ refine: {
566
+ finalKitchen.context.buildStep = "refine";
567
+ replace_placeholders: {
568
+ await buildSpecifierManager.replacePlaceholders();
1031
569
  }
1032
570
  cleanup_jsenv_attributes_from_html: {
1033
571
  GRAPH_VISITOR.forEach(finalKitchen.graph, (urlInfo) => {
@@ -1052,293 +590,28 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
1052
590
  * - because of import assertions transpilation (file is inlined into JS)
1053
591
  */
1054
592
  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) {
593
+ const resync = buildSpecifierManager.prepareResyncResourceHints();
594
+ if (resync) {
1198
595
  const resyncTask = createBuildTask("resync resource hints");
1199
- actions.map((resourceHintAction) => resourceHintAction());
596
+ resync();
1200
597
  buildOperation.throwIfAborted();
1201
598
  resyncTask.done();
1202
599
  }
1203
600
  }
1204
601
  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) {
602
+ const inject = buildSpecifierManager.prepareServiceWorkerUrlInjection();
603
+ if (inject) {
1216
604
  const urlsInjectionInSw = createBuildTask(
1217
605
  "inject urls in service worker",
1218
606
  );
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
- }
607
+ await inject();
1268
608
  urlsInjectionInSw.done();
609
+ buildOperation.throwIfAborted();
1269
610
  }
1270
- buildOperation.throwIfAborted();
1271
611
  }
1272
612
  }
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
-
613
+ const { buildFileContents, buildInlineContents, buildManifest } =
614
+ buildSpecifierManager.getBuildInfo();
1342
615
  if (writeOnFileSystem) {
1343
616
  const writingFiles = createBuildTask("write files in build directory");
1344
617
  if (directoryToClean) {
@@ -1439,58 +712,3 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
1439
712
  await firstBuildPromise;
1440
713
  return stopWatchingSourceFiles;
1441
714
  };
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
- };