@jsenv/core 36.0.1 → 36.1.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.
@@ -13,10 +13,11 @@ import http from "node:http";
13
13
  import { Readable, Stream, Writable } from "node:stream";
14
14
  import { Http2ServerResponse } from "node:http2";
15
15
  import { lookup } from "node:dns";
16
- import { SOURCEMAP, generateSourcemapFileUrl, composeTwoSourcemaps, generateSourcemapDataUrl, createMagicSource, getOriginalPosition } from "@jsenv/sourcemap";
16
+ import { SOURCEMAP, generateSourcemapFileUrl, composeTwoSourcemaps, generateSourcemapDataUrl, createMagicSource } from "@jsenv/sourcemap";
17
17
  import { parseHtmlString, visitHtmlNodes, getHtmlNodeAttribute, analyzeScriptNode, stringifyHtmlAst, parseSrcSet, getHtmlNodeText, setHtmlNodeAttributes, getHtmlNodePosition, getHtmlNodeAttributePosition, removeHtmlNodeText, setHtmlNodeText, parseCssUrls, parseJsUrls, applyBabelPlugins, injectHtmlNodeAsEarlyAsPossible, createHtmlNode, findHtmlNode, removeHtmlNode, injectJsImport, analyzeLinkNode, injectHtmlNode, insertHtmlNodeAfter } from "@jsenv/ast";
18
18
  import { createRequire } from "node:module";
19
19
  import babelParser from "@babel/parser";
20
+ import { jsenvPluginSupervisor } from "@jsenv/plugin-supervisor";
20
21
 
21
22
  /*
22
23
  * data:[<mediatype>][;base64],<data>
@@ -7290,8 +7291,6 @@ const generateAccessControlHeaders = ({
7290
7291
  };
7291
7292
  };
7292
7293
 
7293
- new Map();
7294
-
7295
7294
  const lookupPackageDirectory = currentUrl => {
7296
7295
  if (currentUrl === "file:///") {
7297
7296
  return null;
@@ -7806,7 +7805,6 @@ const createUrlInfo = url => {
7806
7805
  originalUrl: undefined,
7807
7806
  filename: "",
7808
7807
  isEntryPoint: false,
7809
- shouldHandle: undefined,
7810
7808
  originalContent: undefined,
7811
7809
  content: undefined,
7812
7810
  sourcemap: null,
@@ -7972,7 +7970,7 @@ const createPluginController = kitchenContext => {
7972
7970
  if (info.timing) {
7973
7971
  info.timing[`${hook.name}-${hook.plugin.name.replace("jsenv:", "")}`] = performance$1.now() - startTimestamp;
7974
7972
  }
7975
- valueReturned = assertAndNormalizeReturnValue(hook.name, valueReturned);
7973
+ valueReturned = assertAndNormalizeReturnValue(hook.name, valueReturned, info);
7976
7974
  return valueReturned;
7977
7975
  };
7978
7976
  const callAsyncHook = async (hook, info, context) => {
@@ -7993,7 +7991,7 @@ const createPluginController = kitchenContext => {
7993
7991
  if (info.timing) {
7994
7992
  info.timing[`${hook.name}-${hook.plugin.name.replace("jsenv:", "")}`] = performance$1.now() - startTimestamp;
7995
7993
  }
7996
- valueReturned = assertAndNormalizeReturnValue(hook.name, valueReturned);
7994
+ valueReturned = assertAndNormalizeReturnValue(hook.name, valueReturned, info);
7997
7995
  return valueReturned;
7998
7996
  };
7999
7997
  const callHooks = (hookName, info, context, callback) => {
@@ -8085,7 +8083,7 @@ info = {}) => {
8085
8083
  }
8086
8084
  return hookValue;
8087
8085
  };
8088
- const assertAndNormalizeReturnValue = (hookName, returnValue) => {
8086
+ const assertAndNormalizeReturnValue = (hookName, returnValue, info) => {
8089
8087
  // all hooks are allowed to return null/undefined as a signal of "I don't do anything"
8090
8088
  if (returnValue === null || returnValue === undefined) {
8091
8089
  return returnValue;
@@ -8094,7 +8092,7 @@ const assertAndNormalizeReturnValue = (hookName, returnValue) => {
8094
8092
  if (!returnValueAssertion.appliesTo.includes(hookName)) {
8095
8093
  continue;
8096
8094
  }
8097
- const assertionResult = returnValueAssertion.assertion(returnValue);
8095
+ const assertionResult = returnValueAssertion.assertion(returnValue, info);
8098
8096
  if (assertionResult !== undefined) {
8099
8097
  // normalization
8100
8098
  returnValue = assertionResult;
@@ -8118,7 +8116,7 @@ const returnValueAssertions = [{
8118
8116
  }, {
8119
8117
  name: "content_assertion",
8120
8118
  appliesTo: ["fetchUrlContent", "transformUrlContent", "finalizeUrlContent", "optimizeUrlContent"],
8121
- assertion: valueReturned => {
8119
+ assertion: (valueReturned, urlInfo) => {
8122
8120
  if (typeof valueReturned === "string" || Buffer.isBuffer(valueReturned)) {
8123
8121
  return {
8124
8122
  content: valueReturned
@@ -8126,11 +8124,10 @@ const returnValueAssertions = [{
8126
8124
  }
8127
8125
  if (typeof valueReturned === "object") {
8128
8126
  const {
8129
- shouldHandle,
8130
8127
  content,
8131
8128
  body
8132
8129
  } = valueReturned;
8133
- if (shouldHandle === false) {
8130
+ if (urlInfo.url.startsWith("ignore:")) {
8134
8131
  return undefined;
8135
8132
  }
8136
8133
  if (typeof content !== "string" && !Buffer.isBuffer(content) && !body) {
@@ -9102,6 +9099,9 @@ const createKitchen = ({
9102
9099
  logLevel,
9103
9100
  rootDirectoryUrl,
9104
9101
  mainFilePath,
9102
+ ignore,
9103
+ ignoreProtocol = "remove",
9104
+ supportedProtocols = ["file:", "data:", "virtual:", "http:", "https:"],
9105
9105
  urlGraph,
9106
9106
  dev = false,
9107
9107
  build = false,
@@ -9147,6 +9147,35 @@ const createKitchen = ({
9147
9147
  plugins.forEach(pluginEntry => {
9148
9148
  pluginController.pushPlugin(pluginEntry);
9149
9149
  });
9150
+ const isIgnoredByProtocol = url => {
9151
+ const {
9152
+ protocol
9153
+ } = new URL(url);
9154
+ const protocolIsSupported = supportedProtocols.some(supportedProtocol => protocol === supportedProtocol);
9155
+ return !protocolIsSupported;
9156
+ };
9157
+ let isIgnoredByParam = () => false;
9158
+ if (ignore) {
9159
+ const associations = URL_META.resolveAssociations({
9160
+ ignore
9161
+ }, rootDirectoryUrl);
9162
+ const cache = new Map();
9163
+ isIgnoredByParam = url => {
9164
+ const fromCache = cache.get(url);
9165
+ if (fromCache) return fromCache;
9166
+ const {
9167
+ ignore
9168
+ } = URL_META.applyAssociations({
9169
+ url,
9170
+ associations
9171
+ });
9172
+ cache.set(url, ignore);
9173
+ return ignore;
9174
+ };
9175
+ }
9176
+ const isIgnored = url => {
9177
+ return isIgnoredByProtocol(url) || isIgnoredByParam(url);
9178
+ };
9150
9179
 
9151
9180
  /*
9152
9181
  * - "http_request"
@@ -9192,7 +9221,6 @@ const createKitchen = ({
9192
9221
  specifierColumn,
9193
9222
  baseUrl,
9194
9223
  isOriginalPosition,
9195
- shouldHandle,
9196
9224
  isEntryPoint = false,
9197
9225
  isResourceHint = false,
9198
9226
  isImplicit = false,
@@ -9241,7 +9269,6 @@ const createKitchen = ({
9241
9269
  specifierColumn,
9242
9270
  isOriginalPosition,
9243
9271
  baseUrl,
9244
- shouldHandle,
9245
9272
  isEntryPoint,
9246
9273
  isResourceHint,
9247
9274
  isImplicit,
@@ -9286,13 +9313,29 @@ const createKitchen = ({
9286
9313
  url = normalizeUrl(url);
9287
9314
  let referencedUrlObject;
9288
9315
  let searchParams;
9289
- const onReferenceUrlChange = referenceUrl => {
9316
+ const setReferenceUrl = referenceUrl => {
9317
+ // ignored urls are prefixed with "ignore:" so that reference are associated
9318
+ // to a dedicated urlInfo that is ignored.
9319
+ // this way it's only once a resource is referenced by reference that is not ignored
9320
+ // that the resource is cooked
9321
+ if (reference.specifier[0] === "#" &&
9322
+ // For Html, css and "#" refer to a resource in the page, reference must be preserved
9323
+ // However for js import specifiers they have a different meaning and we want
9324
+ // to resolve them (https://nodejs.org/api/packages.html#imports for instance)
9325
+ reference.type !== "js_import") {
9326
+ referenceUrl = `ignore:${referenceUrl}`;
9327
+ } else if (isIgnored(referenceUrl)) {
9328
+ referenceUrl = `ignore:${referenceUrl}`;
9329
+ }
9330
+ if (referenceUrl.startsWith("ignore:") && !reference.specifier.startsWith("ignore:")) {
9331
+ reference.specifier = `ignore:${reference.specifier}`;
9332
+ }
9290
9333
  referencedUrlObject = new URL(referenceUrl);
9291
9334
  searchParams = referencedUrlObject.searchParams;
9292
9335
  reference.url = referenceUrl;
9293
9336
  reference.searchParams = searchParams;
9294
9337
  };
9295
- onReferenceUrlChange(url);
9338
+ setReferenceUrl(url);
9296
9339
  if (reference.debug) {
9297
9340
  logger.debug(`url resolved by "${pluginController.getLastPluginUsed().name}"
9298
9341
  ${ANSI.color(reference.specifier, ANSI.GREY)} ->
@@ -9314,7 +9357,7 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
9314
9357
  ...reference
9315
9358
  };
9316
9359
  updateReference(prevReference, reference);
9317
- onReferenceUrlChange(normalizedReturnValue);
9360
+ setReferenceUrl(normalizedReturnValue);
9318
9361
  });
9319
9362
  reference.generatedUrl = reference.url;
9320
9363
  const urlInfo = urlGraph.reuseOrCreateUrlInfo(reference.url);
@@ -9333,8 +9376,16 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
9333
9376
  reference.generatedUrl = normalizeUrl(referencedUrlObject.href);
9334
9377
  });
9335
9378
  const returnValue = pluginController.callHooksUntil("formatReference", reference, referenceContext);
9336
- reference.generatedSpecifier = returnValue || reference.generatedUrl;
9337
- reference.generatedSpecifier = urlSpecifierEncoding.encode(reference);
9379
+ if (reference.url.startsWith("ignore:")) {
9380
+ if (ignoreProtocol === "remove") {
9381
+ reference.specifier = reference.specifier.slice("ignore:".length);
9382
+ }
9383
+ reference.generatedSpecifier = reference.specifier;
9384
+ reference.generatedSpecifier = urlSpecifierEncoding.encode(reference);
9385
+ } else {
9386
+ reference.generatedSpecifier = returnValue || reference.generatedUrl;
9387
+ reference.generatedSpecifier = urlSpecifierEncoding.encode(reference);
9388
+ }
9338
9389
  return [reference, urlInfo];
9339
9390
  } catch (error) {
9340
9391
  throw createResolveUrlError({
@@ -9504,7 +9555,7 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
9504
9555
  contextDuringFetch: context
9505
9556
  });
9506
9557
  };
9507
- if (urlInfo.shouldHandle) {
9558
+ if (!urlInfo.url.startsWith("ignore:")) {
9508
9559
  // references
9509
9560
  const references = [];
9510
9561
  context.referenceUtils = {
@@ -9834,11 +9885,6 @@ const traceFromUrlSite = urlSite => {
9834
9885
  };
9835
9886
  };
9836
9887
  const applyReferenceEffectsOnUrlInfo = (reference, urlInfo, context) => {
9837
- if (reference.shouldHandle) {
9838
- urlInfo.shouldHandle = true;
9839
- } else {
9840
- urlInfo.shouldHandle = false;
9841
- }
9842
9888
  urlInfo.originalUrl = urlInfo.originalUrl || reference.url;
9843
9889
  if (reference.isEntryPoint || isWebWorkerEntryPointReference(reference)) {
9844
9890
  urlInfo.isEntryPoint = true;
@@ -10070,15 +10116,19 @@ const createUrlGraphReport = urlGraph => {
10070
10116
  total: 0
10071
10117
  };
10072
10118
  urlGraph.urlInfoMap.forEach(urlInfo => {
10073
- if (urlInfo.url.startsWith("data:")) {
10074
- return;
10075
- }
10076
10119
  // ignore:
10077
- // - inline files: they are already taken into account in the file where they appear
10078
10120
  // - ignored files: we don't know their content
10079
- if (urlInfo.isInline || !urlInfo.shouldHandle) {
10121
+ // - inline files and data files: they are already taken into account in the file where they appear
10122
+ if (urlInfo.url.startsWith("ignore:")) {
10123
+ return;
10124
+ }
10125
+ if (urlInfo.isInline) {
10126
+ return;
10127
+ }
10128
+ if (urlInfo.url.startsWith("data:")) {
10080
10129
  return;
10081
10130
  }
10131
+
10082
10132
  // file loaded via import assertion are already inside the graph
10083
10133
  // their js module equivalent are ignored to avoid counting it twice
10084
10134
  // in the build graph the file targeted by import assertion will likely be gone
@@ -11106,17 +11156,12 @@ const parseAndTransformJsReferences = async (urlInfo, context, {
11106
11156
  };
11107
11157
 
11108
11158
  const jsenvPluginReferenceAnalysis = ({
11109
- include,
11110
- supportedProtocols = ["file:", "data:", "virtual:", "http:", "https:"],
11111
11159
  inlineContent = true,
11112
11160
  inlineConvertedScript = false,
11113
11161
  fetchInlineUrls = true,
11114
11162
  allowEscapeForVersioning = false
11115
11163
  }) => {
11116
- return [jsenvPluginReferenceAnalysisInclude({
11117
- include,
11118
- supportedProtocols
11119
- }), jsenvPluginDirectoryReferenceAnalysis(), jsenvPluginHtmlReferenceAnalysis({
11164
+ return [jsenvPluginDirectoryReferenceAnalysis(), jsenvPluginHtmlReferenceAnalysis({
11120
11165
  inlineContent,
11121
11166
  inlineConvertedScript
11122
11167
  }), jsenvPluginWebmanifestReferenceAnalysis(), jsenvPluginCssReferenceAnalysis(), jsenvPluginJsReferenceAnalysis({
@@ -11124,65 +11169,6 @@ const jsenvPluginReferenceAnalysis = ({
11124
11169
  allowEscapeForVersioning
11125
11170
  }), ...(inlineContent ? [jsenvPluginDataUrlsAnalysis()] : []), ...(inlineContent && fetchInlineUrls ? [jsenvPluginInlineContentFetcher()] : []), jsenvPluginReferenceExpectedTypes()];
11126
11171
  };
11127
- const jsenvPluginReferenceAnalysisInclude = ({
11128
- include,
11129
- supportedProtocols
11130
- }) => {
11131
- // eslint-disable-next-line no-unused-vars
11132
- let getIncludeInfo = url => undefined;
11133
- return {
11134
- name: "jsenv:reference_analysis_include",
11135
- appliesDuring: "*",
11136
- init: ({
11137
- rootDirectoryUrl
11138
- }) => {
11139
- if (include) {
11140
- const associations = URL_META.resolveAssociations({
11141
- include
11142
- }, rootDirectoryUrl);
11143
- getIncludeInfo = url => {
11144
- const {
11145
- include
11146
- } = URL_META.applyAssociations({
11147
- url,
11148
- associations
11149
- });
11150
- return include;
11151
- };
11152
- }
11153
- },
11154
- redirectReference: reference => {
11155
- if (reference.shouldHandle !== undefined) {
11156
- return;
11157
- }
11158
- if (reference.specifier[0] === "#" &&
11159
- // For Html, css and in general "#" refer to a resource in the page
11160
- // so that urls must be kept intact
11161
- // However for js import specifiers they have a different meaning and we want
11162
- // to resolve them (https://nodejs.org/api/packages.html#imports for instance)
11163
- reference.type !== "js_import") {
11164
- reference.shouldHandle = false;
11165
- return;
11166
- }
11167
- const includeInfo = getIncludeInfo(reference.url);
11168
- if (includeInfo === true) {
11169
- reference.shouldHandle = true;
11170
- return;
11171
- }
11172
- if (includeInfo === false) {
11173
- reference.shouldHandle = false;
11174
- return;
11175
- }
11176
- const {
11177
- protocol
11178
- } = new URL(reference.url);
11179
- const protocolIsSupported = supportedProtocols.some(supportedProtocol => protocol === supportedProtocol);
11180
- if (protocolIsSupported) {
11181
- reference.shouldHandle = true;
11182
- }
11183
- }
11184
- };
11185
- };
11186
11172
  const jsenvPluginInlineContentFetcher = () => {
11187
11173
  return {
11188
11174
  name: "jsenv:inline_content_fetcher",
@@ -17435,10 +17421,10 @@ const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
17435
17421
  runtimeCompat,
17436
17422
  preservesSymlink: true
17437
17423
  });
17438
- if (!resolvers.js_module) {
17424
+ if (resolvers.js_module === undefined) {
17439
17425
  resolvers.js_module = nodeEsmResolverDefault;
17440
17426
  }
17441
- if (!resolvers.js_classic) {
17427
+ if (resolvers.js_classic === undefined) {
17442
17428
  resolvers.js_classic = (reference, context) => {
17443
17429
  if (reference.subtype === "self_import_scripts_arg") {
17444
17430
  return nodeEsmResolverDefault(reference, context);
@@ -17467,41 +17453,25 @@ const jsenvPluginNodeEsmResolution = (resolutionConfig = {}) => {
17467
17453
  };
17468
17454
  };
17469
17455
 
17470
- const jsenvPluginWebResolution = (resolutionConfig = {}) => {
17471
- const resolvers = {};
17472
- const resolveUsingWebResolution = (reference, context) => {
17473
- if (reference.specifier === "/") {
17474
- const {
17475
- mainFilePath,
17476
- rootDirectoryUrl
17477
- } = context;
17478
- return String(new URL(mainFilePath, rootDirectoryUrl));
17479
- }
17480
- if (reference.specifier[0] === "/") {
17481
- return new URL(reference.specifier.slice(1), context.rootDirectoryUrl).href;
17482
- }
17483
- return new URL(reference.specifier,
17484
- // baseUrl happens second argument to new URL() is different from
17485
- // import.meta.url or document.currentScript.src
17486
- reference.baseUrl || reference.parentUrl).href;
17487
- };
17488
- Object.keys(resolutionConfig).forEach(urlType => {
17489
- const config = resolutionConfig[urlType];
17490
- if (config === true) {
17491
- resolvers[urlType] = resolveUsingWebResolution;
17492
- } else if (config === false) {
17493
- resolvers[urlType] = () => null;
17494
- } else {
17495
- throw new TypeError(`config must be true or false, got ${config} on "${urlType}"`);
17496
- }
17497
- });
17456
+ const jsenvPluginWebResolution = () => {
17498
17457
  return {
17499
17458
  name: "jsenv:web_resolution",
17500
17459
  appliesDuring: "*",
17501
17460
  resolveReference: (reference, context) => {
17502
- const urlType = urlTypeFromReference(reference, context);
17503
- const resolver = resolvers[urlType];
17504
- return resolver ? resolver(reference, context) : resolveUsingWebResolution(reference, context);
17461
+ if (reference.specifier === "/") {
17462
+ const {
17463
+ mainFilePath,
17464
+ rootDirectoryUrl
17465
+ } = context;
17466
+ return String(new URL(mainFilePath, rootDirectoryUrl));
17467
+ }
17468
+ if (reference.specifier[0] === "/") {
17469
+ return new URL(reference.specifier.slice(1), context.rootDirectoryUrl).href;
17470
+ }
17471
+ return new URL(reference.specifier,
17472
+ // baseUrl happens second argument to new URL() is different from
17473
+ // import.meta.url or document.currentScript.src
17474
+ reference.baseUrl || reference.parentUrl).href;
17505
17475
  }
17506
17476
  };
17507
17477
  };
@@ -17540,14 +17510,14 @@ const jsenvPluginVersionSearchParam = () => {
17540
17510
  };
17541
17511
  };
17542
17512
 
17543
- const jsenvPluginFileUrls = ({
17513
+ const jsenvPluginProtocolFile = ({
17544
17514
  magicExtensions = ["inherit", ".js"],
17545
17515
  magicDirectoryIndex = true,
17546
17516
  preserveSymlinks = false,
17547
17517
  directoryReferenceAllowed = false
17548
17518
  }) => {
17549
17519
  return [{
17550
- name: "jsenv:file_url_resolution",
17520
+ name: "jsenv:fs_redirection",
17551
17521
  appliesDuring: "*",
17552
17522
  redirectReference: reference => {
17553
17523
  // http, https, data, about, ...
@@ -17557,6 +17527,19 @@ const jsenvPluginFileUrls = ({
17557
17527
  if (reference.isInline) {
17558
17528
  return null;
17559
17529
  }
17530
+ // ignore root file url
17531
+ if (reference.url === "file:///" || reference.url === "file://") {
17532
+ reference.leadsToADirectory = true;
17533
+ return `ignore:file:///`;
17534
+ }
17535
+ // ignore "./" on new URL("./")
17536
+ if (reference.subtype === "new_url_first_arg" && reference.specifier === "./") {
17537
+ return `ignore:${reference.url}`;
17538
+ }
17539
+ // ignore all new URL second arg
17540
+ if (reference.subtype === "new_url_second_arg") {
17541
+ return `ignore:${reference.url}`;
17542
+ }
17560
17543
  const urlObject = new URL(reference.url);
17561
17544
  let stat;
17562
17545
  try {
@@ -17578,6 +17561,7 @@ const jsenvPluginFileUrls = ({
17578
17561
  const pathnameUsesTrailingSlash = pathname.endsWith("/");
17579
17562
  urlObject.search = "";
17580
17563
  urlObject.hash = "";
17564
+
17581
17565
  // force trailing slash on directories
17582
17566
  if (stat && stat.isDirectory() && !pathnameUsesTrailingSlash) {
17583
17567
  urlObject.pathname = `${pathname}/`;
@@ -17588,33 +17572,24 @@ const jsenvPluginFileUrls = ({
17588
17572
  urlObject.pathname = pathname.slice(0, -1);
17589
17573
  }
17590
17574
  let url = urlObject.href;
17591
- const shouldPreserve = stat && stat.isDirectory() && (
17592
- // ignore new URL second arg
17593
- reference.subtype === "new_url_second_arg" ||
17594
- // ignore root file url
17595
- reference.url === "file:///" || reference.subtype === "new_url_first_arg" && reference.specifier === "./");
17596
- if (shouldPreserve) {
17597
- reference.shouldHandle = false;
17598
- } else {
17599
- const shouldApplyDilesystemMagicResolution = reference.type === "js_import";
17600
- if (shouldApplyDilesystemMagicResolution) {
17601
- const filesystemResolution = applyFileSystemMagicResolution(url, {
17602
- fileStat: stat,
17603
- magicDirectoryIndex,
17604
- magicExtensions: getExtensionsToTry(magicExtensions, reference.parentUrl)
17605
- });
17606
- if (filesystemResolution.stat) {
17607
- stat = filesystemResolution.stat;
17608
- url = filesystemResolution.url;
17609
- }
17575
+ const shouldApplyDilesystemMagicResolution = reference.type === "js_import";
17576
+ if (shouldApplyDilesystemMagicResolution) {
17577
+ const filesystemResolution = applyFileSystemMagicResolution(url, {
17578
+ fileStat: stat,
17579
+ magicDirectoryIndex,
17580
+ magicExtensions: getExtensionsToTry(magicExtensions, reference.parentUrl)
17581
+ });
17582
+ if (filesystemResolution.stat) {
17583
+ stat = filesystemResolution.stat;
17584
+ url = filesystemResolution.url;
17610
17585
  }
17611
- if (stat && stat.isDirectory()) {
17612
- const directoryAllowed = reference.type === "filesystem" || typeof directoryReferenceAllowed === "function" && directoryReferenceAllowed(reference) || directoryReferenceAllowed;
17613
- if (!directoryAllowed) {
17614
- const error = new Error("Reference leads to a directory");
17615
- error.code = "DIRECTORY_REFERENCE_NOT_ALLOWED";
17616
- throw error;
17617
- }
17586
+ }
17587
+ if (stat && stat.isDirectory()) {
17588
+ const directoryAllowed = reference.type === "filesystem" || typeof directoryReferenceAllowed === "function" && directoryReferenceAllowed(reference) || directoryReferenceAllowed;
17589
+ if (!directoryAllowed) {
17590
+ const error = new Error("Reference leads to a directory");
17591
+ error.code = "DIRECTORY_REFERENCE_NOT_ALLOWED";
17592
+ throw error;
17618
17593
  }
17619
17594
  }
17620
17595
  reference.leadsToADirectory = stat && stat.isDirectory();
@@ -17626,7 +17601,7 @@ const jsenvPluginFileUrls = ({
17626
17601
  return null;
17627
17602
  }
17628
17603
  }, {
17629
- name: "jsenv:filesystem_resolution",
17604
+ name: "jsenv:fs_resolution",
17630
17605
  appliesDuring: "*",
17631
17606
  resolveReference: {
17632
17607
  filesystem: (reference, context) => {
@@ -17639,10 +17614,10 @@ const jsenvPluginFileUrls = ({
17639
17614
  }
17640
17615
  }
17641
17616
  }, {
17642
- name: "jsenv:@fs_resolution",
17643
- // during dev and test it's a browser running the code
17617
+ name: "jsenv:@fs",
17618
+ // during build it's fine to use "file://"" urls
17619
+ // but during dev it's a browser running the code
17644
17620
  // so absolute file urls needs to be relativized
17645
- // during build it's fine to use file:// urls
17646
17621
  appliesDuring: "dev",
17647
17622
  resolveReference: reference => {
17648
17623
  if (reference.specifier.startsWith("/@fs/")) {
@@ -17697,715 +17672,17 @@ const resolveSymlink = fileUrl => {
17697
17672
  return pathToFileURL(realpathSync(new URL(fileUrl))).href;
17698
17673
  };
17699
17674
 
17700
- const jsenvPluginHttpUrls = () => {
17675
+ const jsenvPluginProtocolHttp = () => {
17701
17676
  return {
17702
- name: "jsenv:http_urls",
17677
+ name: "jsenv:protocol_http",
17703
17678
  appliesDuring: "*",
17704
17679
  redirectReference: reference => {
17705
- if (reference.url.startsWith("http:") || reference.url.startsWith("https:")) {
17706
- reference.shouldHandle = false;
17707
- }
17708
17680
  // TODO: according to some pattern matching jsenv could be allowed
17709
17681
  // to fetch and transform http urls
17710
- }
17711
- };
17712
- };
17713
-
17714
- /*
17715
- * ```js
17716
- * console.log(42)
17717
- * ```
17718
- * becomes
17719
- * ```js
17720
- * window.__supervisor__.jsClassicStart('main.html@L10-L13.js')
17721
- * try {
17722
- * console.log(42)
17723
- * window.__supervisor__.jsClassicEnd('main.html@L10-L13.js')
17724
- * } catch(e) {
17725
- * window.__supervisor__.jsClassicError('main.html@L10-L13.js', e)
17726
- * }
17727
- * ```
17728
- *
17729
- * ```js
17730
- * import value from "./file.js"
17731
- * console.log(value)
17732
- * ```
17733
- * becomes
17734
- * ```js
17735
- * window.__supervisor__.jsModuleStart('main.html@L10-L13.js')
17736
- * try {
17737
- * const value = await import("./file.js")
17738
- * console.log(value)
17739
- * window.__supervisor__.jsModuleEnd('main.html@L10-L13.js')
17740
- * } catch(e) {
17741
- * window.__supervisor__.jsModuleError('main.html@L10-L13.js', e)
17742
- * }
17743
- * ```
17744
- *
17745
- * -> TO KEEP IN MIND:
17746
- * Static import can throw errors like
17747
- * The requested module '/js_module_export_not_found/foo.js' does not provide an export named 'answerr'
17748
- * While dynamic import will work just fine
17749
- * and create a variable named "undefined"
17750
- */
17751
-
17752
- const injectSupervisorIntoJs = async ({
17753
- webServer,
17754
- content,
17755
- url,
17756
- type,
17757
- inlineSrc
17758
- }) => {
17759
- const babelPluginJsSupervisor = type === "js_module" ? babelPluginJsModuleSupervisor : babelPluginJsClassicSupervisor;
17760
- const result = await applyBabelPlugins({
17761
- urlInfo: {
17762
- content,
17763
- originalUrl: url,
17764
- type
17765
- },
17766
- babelPlugins: [[babelPluginJsSupervisor, {
17767
- inlineSrc
17768
- }]]
17769
- });
17770
- let code = result.code;
17771
- let map = result.map;
17772
- const sourcemapDataUrl = generateSourcemapDataUrl(map);
17773
- code = SOURCEMAP.writeComment({
17774
- contentType: "text/javascript",
17775
- content: code,
17776
- specifier: sourcemapDataUrl
17777
- });
17778
- code = `${code}
17779
- //# sourceURL=${urlToRelativeUrl(url, webServer.rootDirectoryUrl)}`;
17780
- return code;
17781
- };
17782
- const babelPluginJsModuleSupervisor = babel => {
17783
- const t = babel.types;
17784
- return {
17785
- name: "js-module-supervisor",
17786
- visitor: {
17787
- Program: (programPath, state) => {
17788
- const {
17789
- inlineSrc
17790
- } = state.opts;
17791
- if (state.file.metadata.jsExecutionInstrumented) return;
17792
- state.file.metadata.jsExecutionInstrumented = true;
17793
- const urlNode = t.stringLiteral(inlineSrc);
17794
- const startCallNode = createSupervisionCall({
17795
- t,
17796
- urlNode,
17797
- methodName: "jsModuleStart"
17798
- });
17799
- const endCallNode = createSupervisionCall({
17800
- t,
17801
- urlNode,
17802
- methodName: "jsModuleEnd"
17803
- });
17804
- const errorCallNode = createSupervisionCall({
17805
- t,
17806
- urlNode,
17807
- methodName: "jsModuleError",
17808
- args: [t.identifier("e")]
17809
- });
17810
- const bodyPath = programPath.get("body");
17811
- const importNodes = [];
17812
- const topLevelNodes = [];
17813
- for (const topLevelNodePath of bodyPath) {
17814
- const topLevelNode = topLevelNodePath.node;
17815
- if (t.isImportDeclaration(topLevelNode)) {
17816
- importNodes.push(topLevelNode);
17817
- } else {
17818
- topLevelNodes.push(topLevelNode);
17819
- }
17820
- }
17821
-
17822
- // replace all import nodes with dynamic imports
17823
- const dynamicImports = [];
17824
- importNodes.forEach(importNode => {
17825
- const dynamicImportConversion = convertStaticImportIntoDynamicImport(importNode, t);
17826
- if (Array.isArray(dynamicImportConversion)) {
17827
- dynamicImports.push(...dynamicImportConversion);
17828
- } else {
17829
- dynamicImports.push(dynamicImportConversion);
17830
- }
17831
- });
17832
- const tryCatchNode = t.tryStatement(t.blockStatement([...dynamicImports, ...topLevelNodes, endCallNode]), t.catchClause(t.identifier("e"), t.blockStatement([errorCallNode])));
17833
- programPath.replaceWith(t.program([startCallNode, tryCatchNode]));
17834
- }
17835
- }
17836
- };
17837
- };
17838
- const convertStaticImportIntoDynamicImport = (staticImportNode, t) => {
17839
- const awaitExpression = t.awaitExpression(t.callExpression(t.import(), [t.stringLiteral(staticImportNode.source.value)]));
17840
-
17841
- // import "./file.js" -> await import("./file.js")
17842
- if (staticImportNode.specifiers.length === 0) {
17843
- return t.expressionStatement(awaitExpression);
17844
- }
17845
- if (staticImportNode.specifiers.length === 1) {
17846
- const [firstSpecifier] = staticImportNode.specifiers;
17847
- if (firstSpecifier.type === "ImportNamespaceSpecifier") {
17848
- return t.variableDeclaration("const", [t.variableDeclarator(t.identifier(firstSpecifier.local.name), awaitExpression)]);
17849
- }
17850
- }
17851
- if (staticImportNode.specifiers.length === 2) {
17852
- const [first, second] = staticImportNode.specifiers;
17853
- if (first.type === "ImportDefaultSpecifier" && second.type === "ImportNamespaceSpecifier") {
17854
- const namespaceDeclaration = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(second.local.name), awaitExpression)]);
17855
- const defaultDeclaration = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(first.local.name), t.memberExpression(t.identifier(second.local.name), t.identifier("default")))]);
17856
- return [namespaceDeclaration, defaultDeclaration];
17857
- }
17858
- }
17859
-
17860
- // import { name } from "./file.js" -> const { name } = await import("./file.js")
17861
- // import toto, { name } from "./file.js" -> const { name, default as toto } = await import("./file.js")
17862
- const objectPattern = t.objectPattern(staticImportNode.specifiers.map(specifier => {
17863
- if (specifier.type === "ImportDefaultSpecifier") {
17864
- return t.objectProperty(t.identifier("default"), t.identifier(specifier.local.name), false,
17865
- // computed
17866
- false // shorthand
17867
- );
17868
- }
17869
- // if (specifier.type === "ImportNamespaceSpecifier") {
17870
- // return t.restElement(t.identifier(specifier.local.name))
17871
- // }
17872
- const isRenamed = specifier.imported.name !== specifier.local.name;
17873
- if (isRenamed) {
17874
- return t.objectProperty(t.identifier(specifier.imported.name), t.identifier(specifier.local.name), false,
17875
- // computed
17876
- false // shorthand
17877
- );
17878
- }
17879
- // shorthand must be true
17880
- return t.objectProperty(t.identifier(specifier.local.name), t.identifier(specifier.local.name), false,
17881
- // computed
17882
- true // shorthand
17883
- );
17884
- }));
17885
-
17886
- const variableDeclarator = t.variableDeclarator(objectPattern, awaitExpression);
17887
- const variableDeclaration = t.variableDeclaration("const", [variableDeclarator]);
17888
- return variableDeclaration;
17889
- };
17890
- const babelPluginJsClassicSupervisor = babel => {
17891
- const t = babel.types;
17892
- return {
17893
- name: "js-classic-supervisor",
17894
- visitor: {
17895
- Program: (programPath, state) => {
17896
- const {
17897
- inlineSrc
17898
- } = state.opts;
17899
- if (state.file.metadata.jsExecutionInstrumented) return;
17900
- state.file.metadata.jsExecutionInstrumented = true;
17901
- const urlNode = t.stringLiteral(inlineSrc);
17902
- const startCallNode = createSupervisionCall({
17903
- t,
17904
- urlNode,
17905
- methodName: "jsClassicStart"
17906
- });
17907
- const endCallNode = createSupervisionCall({
17908
- t,
17909
- urlNode,
17910
- methodName: "jsClassicEnd"
17911
- });
17912
- const errorCallNode = createSupervisionCall({
17913
- t,
17914
- urlNode,
17915
- methodName: "jsClassicError",
17916
- args: [t.identifier("e")]
17917
- });
17918
- const topLevelNodes = programPath.node.body;
17919
- const tryCatchNode = t.tryStatement(t.blockStatement([...topLevelNodes, endCallNode]), t.catchClause(t.identifier("e"), t.blockStatement([errorCallNode])));
17920
- programPath.replaceWith(t.program([startCallNode, tryCatchNode]));
17921
- }
17922
- }
17923
- };
17924
- };
17925
- const createSupervisionCall = ({
17926
- t,
17927
- methodName,
17928
- urlNode,
17929
- args = []
17930
- }) => {
17931
- return t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier("window"), t.identifier("__supervisor__")), t.identifier(methodName)), [urlNode, ...args]), [], null);
17932
- };
17933
-
17934
- /*
17935
- * Jsenv needs to track js execution in order to:
17936
- * 1. report errors
17937
- * 2. wait for all js execution inside an HTML page before killing the browser
17938
- *
17939
- * A naive approach would rely on "load" events on window but:
17940
- * scenario | covered by window "load"
17941
- * ------------------------------------------- | -------------------------
17942
- * js referenced by <script src> | yes
17943
- * js inlined into <script> | yes
17944
- * js referenced by <script type="module" src> | partially (not for import and top level await)
17945
- * js inlined into <script type="module"> | not at all
17946
- * Same for "error" event on window who is not enough
17947
- *
17948
- * <script src="file.js">
17949
- * becomes
17950
- * <script>
17951
- * window.__supervisor__.superviseScript('file.js')
17952
- * </script>
17953
- *
17954
- * <script>
17955
- * console.log(42)
17956
- * </script>
17957
- * becomes
17958
- * <script inlined-from-src="main.html@L10-C5.js">
17959
- * window.__supervisor.__superviseScript("main.html@L10-C5.js")
17960
- * </script>
17961
- *
17962
- * <script type="module" src="module.js"></script>
17963
- * becomes
17964
- * <script type="module">
17965
- * window.__supervisor__.superviseScriptTypeModule('module.js')
17966
- * </script>
17967
- *
17968
- * <script type="module">
17969
- * console.log(42)
17970
- * </script>
17971
- * becomes
17972
- * <script type="module" inlined-from-src="main.html@L10-C5.js">
17973
- * window.__supervisor__.superviseScriptTypeModule('main.html@L10-C5.js')
17974
- * </script>
17975
- *
17976
- * Why Inline scripts are converted to files dynamically?
17977
- * -> No changes required on js source code, it's only the HTML that is modified
17978
- * - Also allow to catch syntax errors and export missing
17979
- */
17980
-
17981
- const supervisorFileUrl$1 = new URL("./js/supervisor.js", import.meta.url).href;
17982
- const injectSupervisorIntoHTML = async ({
17983
- content,
17984
- url
17985
- }, {
17986
- supervisorScriptSrc = supervisorFileUrl$1,
17987
- supervisorOptions,
17988
- webServer,
17989
- onInlineScript = () => {},
17990
- generateInlineScriptSrc = ({
17991
- inlineScriptUrl
17992
- }) => urlToRelativeUrl(inlineScriptUrl, webServer.rootDirectoryUrl),
17993
- inlineAsRemote
17994
- }) => {
17995
- const htmlAst = parseHtmlString(content);
17996
- const mutations = [];
17997
- const actions = [];
17998
- const scriptInfos = [];
17999
- // 1. Find inline and remote scripts
18000
- {
18001
- const handleInlineScript = (scriptNode, {
18002
- type,
18003
- extension,
18004
- textContent
18005
- }) => {
18006
- const {
18007
- line,
18008
- column,
18009
- lineEnd,
18010
- columnEnd,
18011
- isOriginal
18012
- } = getHtmlNodePosition(scriptNode, {
18013
- preferOriginal: true
18014
- });
18015
- const inlineScriptUrl = generateInlineContentUrl({
18016
- url,
18017
- extension: extension || ".js",
18018
- line,
18019
- column,
18020
- lineEnd,
18021
- columnEnd
18022
- });
18023
- const inlineScriptSrc = generateInlineScriptSrc({
18024
- type,
18025
- textContent,
18026
- inlineScriptUrl,
18027
- isOriginal,
18028
- line,
18029
- column
18030
- });
18031
- onInlineScript({
18032
- type,
18033
- textContent,
18034
- url: inlineScriptUrl,
18035
- isOriginal,
18036
- line,
18037
- column,
18038
- src: inlineScriptSrc
18039
- });
18040
- if (inlineAsRemote) {
18041
- // prefere la version src
18042
- scriptInfos.push({
18043
- type,
18044
- src: inlineScriptSrc
18045
- });
18046
- const remoteJsSupervised = generateCodeToSuperviseScriptWithSrc({
18047
- type,
18048
- src: inlineScriptSrc
18049
- });
18050
- mutations.push(() => {
18051
- setHtmlNodeText(scriptNode, remoteJsSupervised, {
18052
- indentation: "auto"
18053
- });
18054
- setHtmlNodeAttributes(scriptNode, {
18055
- "jsenv-cooked-by": "jsenv:supervisor",
18056
- "src": undefined,
18057
- "inlined-from-src": inlineScriptSrc
18058
- });
18059
- });
18060
- } else {
18061
- scriptInfos.push({
18062
- type,
18063
- src: inlineScriptSrc,
18064
- isInline: true
18065
- });
18066
- actions.push(async () => {
18067
- try {
18068
- const inlineJsSupervised = await injectSupervisorIntoJs({
18069
- webServer,
18070
- content: textContent,
18071
- url: inlineScriptUrl,
18072
- type,
18073
- inlineSrc: inlineScriptSrc
18074
- });
18075
- mutations.push(() => {
18076
- setHtmlNodeText(scriptNode, inlineJsSupervised, {
18077
- indentation: "auto"
18078
- });
18079
- setHtmlNodeAttributes(scriptNode, {
18080
- "jsenv-cooked-by": "jsenv:supervisor"
18081
- });
18082
- });
18083
- } catch (e) {
18084
- if (e.code === "PARSE_ERROR") {
18085
- // mutations.push(() => {
18086
- // setHtmlNodeAttributes(scriptNode, {
18087
- // "jsenv-cooked-by": "jsenv:supervisor",
18088
- // })
18089
- // })
18090
- // on touche a rien
18091
- return;
18092
- }
18093
- throw e;
18094
- }
18095
- });
18096
- }
18097
- };
18098
- const handleScriptWithSrc = (scriptNode, {
18099
- type,
18100
- src
18101
- }) => {
18102
- scriptInfos.push({
18103
- type,
18104
- src
18105
- });
18106
- const remoteJsSupervised = generateCodeToSuperviseScriptWithSrc({
18107
- type,
18108
- src
18109
- });
18110
- mutations.push(() => {
18111
- setHtmlNodeText(scriptNode, remoteJsSupervised, {
18112
- indentation: "auto"
18113
- });
18114
- setHtmlNodeAttributes(scriptNode, {
18115
- "jsenv-cooked-by": "jsenv:supervisor",
18116
- "src": undefined,
18117
- "inlined-from-src": src
18118
- });
18119
- });
18120
- };
18121
- visitHtmlNodes(htmlAst, {
18122
- script: scriptNode => {
18123
- const {
18124
- type,
18125
- extension
18126
- } = analyzeScriptNode(scriptNode);
18127
- if (type !== "js_classic" && type !== "js_module") {
18128
- return;
18129
- }
18130
- if (getHtmlNodeAttribute(scriptNode, "jsenv-injected-by")) {
18131
- return;
18132
- }
18133
- const noSupervisor = getHtmlNodeAttribute(scriptNode, "no-supervisor");
18134
- if (noSupervisor !== undefined) {
18135
- return;
18136
- }
18137
- const scriptNodeText = getHtmlNodeText(scriptNode);
18138
- if (scriptNodeText) {
18139
- handleInlineScript(scriptNode, {
18140
- type,
18141
- extension,
18142
- textContent: scriptNodeText
18143
- });
18144
- return;
18145
- }
18146
- const src = getHtmlNodeAttribute(scriptNode, "src");
18147
- if (src) {
18148
- const urlObject = new URL(src, "http://example.com");
18149
- if (urlObject.searchParams.has("inline")) {
18150
- return;
18151
- }
18152
- handleScriptWithSrc(scriptNode, {
18153
- type,
18154
- src
18155
- });
18156
- return;
18157
- }
18158
- }
18159
- });
18160
- }
18161
- // 2. Inject supervisor js file + setup call
18162
- {
18163
- const setupParamsSource = stringifyParams({
18164
- ...supervisorOptions,
18165
- serverIsJsenvDevServer: webServer.isJsenvDevServer,
18166
- rootDirectoryUrl: webServer.rootDirectoryUrl,
18167
- scriptInfos
18168
- }, " ");
18169
- injectHtmlNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
18170
- tagName: "script",
18171
- textContent: `window.__supervisor__.setup({${setupParamsSource}})`
18172
- }), "jsenv:supervisor");
18173
- injectHtmlNodeAsEarlyAsPossible(htmlAst, createHtmlNode({
18174
- tagName: "script",
18175
- src: supervisorScriptSrc
18176
- }), "jsenv:supervisor");
18177
- }
18178
- // 3. Perform actions (transforming inline script content) and html mutations
18179
- if (actions.length > 0) {
18180
- await Promise.all(actions.map(action => action()));
18181
- }
18182
- mutations.forEach(mutation => mutation());
18183
- const htmlModified = stringifyHtmlAst(htmlAst);
18184
- return {
18185
- content: htmlModified
18186
- };
18187
- };
18188
- const stringifyParams = (params, prefix = "") => {
18189
- const source = JSON.stringify(params, null, prefix);
18190
- if (prefix.length) {
18191
- // remove leading "{\n"
18192
- // remove leading prefix
18193
- // remove trailing "\n}"
18194
- return source.slice(2 + prefix.length, -2);
18195
- }
18196
- // remove leading "{"
18197
- // remove trailing "}"
18198
- return source.slice(1, -1);
18199
- };
18200
- const generateCodeToSuperviseScriptWithSrc = ({
18201
- type,
18202
- src
18203
- }) => {
18204
- const srcEncoded = JSON.stringify(src);
18205
- if (type === "js_module") {
18206
- return `window.__supervisor__.superviseScriptTypeModule(${srcEncoded}, (url) => import(url));`;
18207
- }
18208
- return `window.__supervisor__.superviseScript(${srcEncoded});`;
18209
- };
18210
-
18211
- /*
18212
- * This plugin provides a way for jsenv to know when js execution is done
18213
- */
18214
-
18215
- const supervisorFileUrl = new URL("./js/supervisor.js", import.meta.url).href;
18216
- const jsenvPluginSupervisor = ({
18217
- logs = false,
18218
- measurePerf = false,
18219
- errorOverlay = true,
18220
- openInEditor = true,
18221
- errorBaseUrl
18222
- }) => {
18223
- return {
18224
- name: "jsenv:supervisor",
18225
- appliesDuring: "dev",
18226
- serve: async (request, context) => {
18227
- if (request.pathname.startsWith("/__get_code_frame__/")) {
18228
- const {
18229
- pathname,
18230
- searchParams
18231
- } = new URL(request.url);
18232
- let urlWithLineAndColumn = pathname.slice("/__get_code_frame__/".length);
18233
- urlWithLineAndColumn = decodeURIComponent(urlWithLineAndColumn);
18234
- const match = urlWithLineAndColumn.match(/:([0-9]+):([0-9]+)$/);
18235
- if (!match) {
18236
- return {
18237
- status: 400,
18238
- body: "Missing line and column in url"
18239
- };
18240
- }
18241
- const file = urlWithLineAndColumn.slice(0, match.index);
18242
- let line = parseInt(match[1]);
18243
- let column = parseInt(match[2]);
18244
- const urlInfo = context.urlGraph.getUrlInfo(file);
18245
- if (!urlInfo) {
18246
- return {
18247
- status: 204,
18248
- headers: {
18249
- "cache-control": "no-store"
18250
- }
18251
- };
18252
- }
18253
- const remap = searchParams.has("remap");
18254
- if (remap) {
18255
- const sourcemap = urlInfo.sourcemap;
18256
- if (sourcemap) {
18257
- const original = getOriginalPosition({
18258
- sourcemap,
18259
- url: file,
18260
- line,
18261
- column
18262
- });
18263
- if (original.line !== null) {
18264
- line = original.line;
18265
- if (original.column !== null) {
18266
- column = original.column;
18267
- }
18268
- }
18269
- }
18270
- }
18271
- const codeFrame = stringifyUrlSite({
18272
- url: file,
18273
- line,
18274
- column,
18275
- content: urlInfo.originalContent
18276
- });
18277
- return {
18278
- status: 200,
18279
- headers: {
18280
- "cache-control": "no-store",
18281
- "content-type": "text/plain",
18282
- "content-length": Buffer.byteLength(codeFrame)
18283
- },
18284
- body: codeFrame
18285
- };
18286
- }
18287
- if (request.pathname.startsWith("/__get_error_cause__/")) {
18288
- let file = request.pathname.slice("/__get_error_cause__/".length);
18289
- file = decodeURIComponent(file);
18290
- if (!file) {
18291
- return {
18292
- status: 400,
18293
- body: "Missing file in url"
18294
- };
18295
- }
18296
- const getErrorCauseInfo = () => {
18297
- const urlInfo = context.urlGraph.getUrlInfo(file);
18298
- if (!urlInfo) {
18299
- return null;
18300
- }
18301
- const {
18302
- error
18303
- } = urlInfo;
18304
- if (error) {
18305
- return error;
18306
- }
18307
- // search in direct dependencies (404 or 500)
18308
- const {
18309
- dependencies
18310
- } = urlInfo;
18311
- for (const dependencyUrl of dependencies) {
18312
- const dependencyUrlInfo = context.urlGraph.getUrlInfo(dependencyUrl);
18313
- if (dependencyUrlInfo.error) {
18314
- return dependencyUrlInfo.error;
18315
- }
18316
- }
18317
- return null;
18318
- };
18319
- const causeInfo = getErrorCauseInfo();
18320
- const body = JSON.stringify(causeInfo ? {
18321
- code: causeInfo.code,
18322
- message: causeInfo.message,
18323
- reason: causeInfo.reason,
18324
- stack: errorBaseUrl ? `stack mocked for snapshot` : causeInfo.stack,
18325
- codeFrame: causeInfo.traceMessage
18326
- } : null, null, " ");
18327
- return {
18328
- status: 200,
18329
- headers: {
18330
- "cache-control": "no-store",
18331
- "content-type": "application/json",
18332
- "content-length": Buffer.byteLength(body)
18333
- },
18334
- body
18335
- };
18336
- }
18337
- if (request.pathname.startsWith("/__open_in_editor__/")) {
18338
- let file = request.pathname.slice("/__open_in_editor__/".length);
18339
- file = decodeURIComponent(file);
18340
- if (!file) {
18341
- return {
18342
- status: 400,
18343
- body: "Missing file in url"
18344
- };
18345
- }
18346
- const launch = requireFromJsenv("launch-editor");
18347
- launch(fileURLToPath(file), () => {
18348
- // ignore error for now
18349
- });
18350
- return {
18351
- status: 200,
18352
- headers: {
18353
- "cache-control": "no-store"
18354
- }
18355
- };
17682
+ if (reference.url.startsWith("http:") || reference.url.startsWith("https:")) {
17683
+ return `ignore:${reference.url}`;
18356
17684
  }
18357
17685
  return null;
18358
- },
18359
- transformUrlContent: {
18360
- html: ({
18361
- url,
18362
- content
18363
- }, context) => {
18364
- const [supervisorFileReference] = context.referenceUtils.inject({
18365
- type: "script",
18366
- expectedType: "js_classic",
18367
- specifier: supervisorFileUrl
18368
- });
18369
- return injectSupervisorIntoHTML({
18370
- content,
18371
- url
18372
- }, {
18373
- supervisorScriptSrc: supervisorFileReference.generatedSpecifier,
18374
- supervisorOptions: {
18375
- errorBaseUrl,
18376
- logs,
18377
- measurePerf,
18378
- errorOverlay,
18379
- openInEditor
18380
- },
18381
- webServer: {
18382
- rootDirectoryUrl: context.rootDirectoryUrl,
18383
- isJsenvDevServer: true
18384
- },
18385
- inlineAsRemote: true,
18386
- generateInlineScriptSrc: ({
18387
- type,
18388
- textContent,
18389
- inlineScriptUrl,
18390
- isOriginal,
18391
- line,
18392
- column
18393
- }) => {
18394
- const [inlineScriptReference] = context.referenceUtils.foundInline({
18395
- type: "script",
18396
- subtype: "inline",
18397
- expectedType: type,
18398
- isOriginalPosition: isOriginal,
18399
- specifierLine: line - 1,
18400
- specifierColumn: column,
18401
- specifier: inlineScriptUrl,
18402
- contentType: "text/javascript",
18403
- content: textContent
18404
- });
18405
- return inlineScriptReference.generatedSpecifier;
18406
- }
18407
- });
18408
- }
18409
17686
  }
18410
17687
  };
18411
17688
  };
@@ -20893,8 +20170,8 @@ const getCorePlugins = ({
20893
20170
  runtimeCompat,
20894
20171
  referenceAnalysis = {},
20895
20172
  nodeEsmResolution = {},
20896
- webResolution = {},
20897
- fileSystemMagicRedirection,
20173
+ magicExtensions,
20174
+ magicDirectoryIndex,
20898
20175
  directoryReferenceAllowed,
20899
20176
  supervisor,
20900
20177
  transpilation = true,
@@ -20912,9 +20189,6 @@ const getCorePlugins = ({
20912
20189
  if (supervisor === true) {
20913
20190
  supervisor = {};
20914
20191
  }
20915
- if (fileSystemMagicRedirection === true) {
20916
- fileSystemMagicRedirection = {};
20917
- }
20918
20192
  if (clientAutoreload === true) {
20919
20193
  clientAutoreload = {};
20920
20194
  }
@@ -20930,10 +20204,11 @@ const getCorePlugins = ({
20930
20204
  - reference inside a js module -> resolved by node esm
20931
20205
  - All the rest uses web standard url resolution
20932
20206
  */
20933
- jsenvPluginFileUrls({
20207
+ jsenvPluginProtocolFile({
20934
20208
  directoryReferenceAllowed,
20935
- ...fileSystemMagicRedirection
20936
- }), jsenvPluginHttpUrls(), ...(nodeEsmResolution ? [jsenvPluginNodeEsmResolution(nodeEsmResolution)] : []), jsenvPluginWebResolution(webResolution), jsenvPluginVersionSearchParam(), jsenvPluginCommonJsGlobals(), jsenvPluginImportMetaScenarios(), ...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []), jsenvPluginNodeRuntime({
20209
+ magicExtensions,
20210
+ magicDirectoryIndex
20211
+ }), jsenvPluginProtocolHttp(), ...(nodeEsmResolution ? [jsenvPluginNodeEsmResolution(nodeEsmResolution)] : []), jsenvPluginWebResolution(), jsenvPluginVersionSearchParam(), jsenvPluginCommonJsGlobals(), jsenvPluginImportMetaScenarios(), ...(scenarioPlaceholders ? [jsenvPluginGlobalScenarios()] : []), jsenvPluginNodeRuntime({
20937
20212
  runtimeCompat
20938
20213
  }), jsenvPluginImportMetaHot(), ...(clientAutoreload ? [jsenvPluginAutoreload({
20939
20214
  ...clientAutoreload,
@@ -21330,13 +20605,14 @@ const build = async ({
21330
20605
  buildDirectoryUrl,
21331
20606
  entryPoints = {},
21332
20607
  assetsDirectory = "",
20608
+ ignore,
21333
20609
  runtimeCompat = defaultRuntimeCompat,
21334
20610
  base = runtimeCompat.node ? "./" : "/",
21335
20611
  plugins = [],
21336
20612
  referenceAnalysis = {},
21337
20613
  nodeEsmResolution,
21338
- webResolution,
21339
- fileSystemMagicRedirection,
20614
+ magicExtensions,
20615
+ magicDirectoryIndex,
21340
20616
  directoryReferenceAllowed,
21341
20617
  scenarioPlaceholders,
21342
20618
  transpilation = {},
@@ -21467,18 +20743,16 @@ build ${entryPointKeys.length} entry points`);
21467
20743
  signal,
21468
20744
  logLevel,
21469
20745
  rootDirectoryUrl: sourceDirectoryUrl,
20746
+ ignore,
20747
+ // during first pass (craft) we keep "ignore:" when a reference is ignored
20748
+ // so that the second pass (shape) properly ignore those urls
20749
+ ignoreProtocol: "keep",
21470
20750
  urlGraph: rawGraph,
21471
20751
  build: true,
21472
20752
  runtimeCompat,
21473
20753
  ...contextSharedDuringBuild,
21474
20754
  plugins: [...plugins, {
21475
20755
  appliesDuring: "build",
21476
- formatReference: reference => {
21477
- if (!reference.shouldHandle) {
21478
- return `ignore:${reference.specifier}`;
21479
- }
21480
- return null;
21481
- },
21482
20756
  fetchUrlContent: (urlInfo, context) => {
21483
20757
  if (context.reference.original) {
21484
20758
  rawRedirections.set(context.reference.original.url, context.reference.url);
@@ -21490,8 +20764,8 @@ build ${entryPointKeys.length} entry points`);
21490
20764
  runtimeCompat,
21491
20765
  referenceAnalysis,
21492
20766
  nodeEsmResolution,
21493
- webResolution,
21494
- fileSystemMagicRedirection,
20767
+ magicExtensions,
20768
+ magicDirectoryIndex,
21495
20769
  directoryReferenceAllowed,
21496
20770
  transpilation: {
21497
20771
  ...transpilation,
@@ -21527,6 +20801,14 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
21527
20801
  const finalGraphKitchen = createKitchen({
21528
20802
  logLevel,
21529
20803
  rootDirectoryUrl: buildDirectoryUrl,
20804
+ // here most plugins are not there
20805
+ // - no external plugin
20806
+ // - no plugin putting reference.mustIgnore on https urls
20807
+ // At this stage it's only about redirecting urls to the build directory
20808
+ // consequently only a subset or urls are supported
20809
+ supportedProtocols: ["file:", "data:", "virtual:", "ignore:"],
20810
+ ignore,
20811
+ ignoreProtocol: versioning ? "keep" : "remove",
21530
20812
  urlGraph: finalGraph,
21531
20813
  build: true,
21532
20814
  runtimeCompat,
@@ -21685,9 +20967,6 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
21685
20967
  },
21686
20968
  formatReference: reference => {
21687
20969
  if (!reference.generatedUrl.startsWith("file:")) {
21688
- if (!versioning && reference.generatedUrl.startsWith("ignore:")) {
21689
- return reference.generatedUrl.slice("ignore:".length);
21690
- }
21691
20970
  return null;
21692
20971
  }
21693
20972
  if (reference.isResourceHint) {
@@ -22104,14 +21383,11 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22104
21383
  const contentVersionMap = new Map();
22105
21384
  const hashCallbacks = [];
22106
21385
  GRAPH.forEach(finalGraph, urlInfo => {
22107
- if (urlInfo.url.startsWith("data:")) {
22108
- return;
22109
- }
22110
21386
  if (urlInfo.type === "sourcemap") {
22111
21387
  return;
22112
21388
  }
22113
21389
  // ignore:
22114
- // - inline files:
21390
+ // - inline files and data files:
22115
21391
  // they are already taken into account in the file where they appear
22116
21392
  // - ignored files:
22117
21393
  // we don't know their content
@@ -22123,7 +21399,10 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22123
21399
  if (urlInfo.isInline) {
22124
21400
  return;
22125
21401
  }
22126
- if (!urlInfo.shouldHandle) {
21402
+ if (urlInfo.url.startsWith("data:")) {
21403
+ return;
21404
+ }
21405
+ if (urlInfo.url.startsWith("ignore:")) {
22127
21406
  return;
22128
21407
  }
22129
21408
  if (urlInfo.dependents.size === 0 && !urlInfo.isEntryPoint) {
@@ -22149,7 +21428,7 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22149
21428
  const dependencyContentVersion = contentVersionMap.get(reference.url);
22150
21429
  if (!dependencyContentVersion) {
22151
21430
  // no content generated for this dependency
22152
- // (inline, data:, sourcemap, shouldHandle is false, ...)
21431
+ // (inline, data:, ignore:, sourcemap, ...)
22153
21432
  return null;
22154
21433
  }
22155
21434
  if (preferWithoutVersioning(reference)) {
@@ -22208,6 +21487,8 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22208
21487
  const versioningKitchen = createKitchen({
22209
21488
  logLevel: logger.level,
22210
21489
  rootDirectoryUrl: buildDirectoryUrl,
21490
+ ignore,
21491
+ ignoreProtocol: "remove",
22211
21492
  urlGraph: finalGraph,
22212
21493
  build: true,
22213
21494
  runtimeCompat,
@@ -22247,13 +21528,13 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22247
21528
  return url;
22248
21529
  },
22249
21530
  formatReference: reference => {
22250
- if (!reference.shouldHandle) {
22251
- if (reference.generatedUrl.startsWith("ignore:")) {
22252
- return reference.generatedUrl.slice("ignore:".length);
22253
- }
21531
+ if (reference.url.startsWith("ignore:")) {
21532
+ return null;
21533
+ }
21534
+ if (reference.isInline) {
22254
21535
  return null;
22255
21536
  }
22256
- if (reference.isInline || reference.url.startsWith("data:")) {
21537
+ if (reference.url.startsWith("data:")) {
22257
21538
  return null;
22258
21539
  }
22259
21540
  if (reference.isResourceHint) {
@@ -22265,9 +21546,6 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22265
21546
  if (!canUseVersionedUrl(referencedUrlInfo)) {
22266
21547
  return reference.specifier;
22267
21548
  }
22268
- if (!referencedUrlInfo.shouldHandle) {
22269
- return null;
22270
- }
22271
21549
  const versionedUrl = versionedUrlMap.get(reference.url);
22272
21550
  if (!versionedUrl) {
22273
21551
  // happens for sourcemap
@@ -22375,9 +21653,6 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22375
21653
  }
22376
21654
  {
22377
21655
  GRAPH.forEach(finalGraph, urlInfo => {
22378
- if (!urlInfo.shouldHandle) {
22379
- return;
22380
- }
22381
21656
  if (!urlInfo.url.startsWith("file:")) {
22382
21657
  return;
22383
21658
  }
@@ -22524,10 +21799,10 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22524
21799
  if (serviceWorkerEntryUrlInfos.length > 0) {
22525
21800
  const serviceWorkerResources = {};
22526
21801
  GRAPH.forEach(finalGraph, urlInfo => {
22527
- if (urlInfo.isInline || !urlInfo.shouldHandle) {
21802
+ if (!urlInfo.url.startsWith("file:")) {
22528
21803
  return;
22529
21804
  }
22530
- if (!urlInfo.url.startsWith("file:")) {
21805
+ if (urlInfo.isInline) {
22531
21806
  return;
22532
21807
  }
22533
21808
  if (!canUseVersionedUrl(urlInfo)) {
@@ -22583,9 +21858,6 @@ ${ANSI.color(buildUrl, ANSI.MAGENTA)}
22583
21858
  return buildRelativeUrl;
22584
21859
  };
22585
21860
  GRAPH.forEach(finalGraph, urlInfo => {
22586
- if (!urlInfo.shouldHandle) {
22587
- return;
22588
- }
22589
21861
  if (!urlInfo.url.startsWith("file:")) {
22590
21862
  return;
22591
21863
  }
@@ -22850,13 +22122,14 @@ const createFileService = ({
22850
22122
  contextCache,
22851
22123
  sourceDirectoryUrl,
22852
22124
  sourceMainFilePath,
22125
+ ignore,
22853
22126
  sourceFilesConfig,
22854
22127
  runtimeCompat,
22855
22128
  plugins,
22856
22129
  referenceAnalysis,
22857
22130
  nodeEsmResolution,
22858
- webResolution,
22859
- fileSystemMagicRedirection,
22131
+ magicExtensions,
22132
+ magicDirectoryIndex,
22860
22133
  supervisor,
22861
22134
  transpilation,
22862
22135
  clientAutoreload,
@@ -22922,6 +22195,7 @@ const createFileService = ({
22922
22195
  logLevel,
22923
22196
  rootDirectoryUrl: sourceDirectoryUrl,
22924
22197
  mainFilePath: sourceMainFilePath,
22198
+ ignore,
22925
22199
  urlGraph,
22926
22200
  dev: true,
22927
22201
  runtimeCompat,
@@ -22932,8 +22206,8 @@ const createFileService = ({
22932
22206
  runtimeCompat,
22933
22207
  referenceAnalysis,
22934
22208
  nodeEsmResolution,
22935
- webResolution,
22936
- fileSystemMagicRedirection,
22209
+ magicExtensions,
22210
+ magicDirectoryIndex,
22937
22211
  supervisor,
22938
22212
  transpilation,
22939
22213
  clientAutoreload,
@@ -23247,6 +22521,7 @@ const inferParentFromRequest = (request, sourceDirectoryUrl) => {
23247
22521
  const startDevServer = async ({
23248
22522
  sourceDirectoryUrl,
23249
22523
  sourceMainFilePath = "./index.html",
22524
+ ignore,
23250
22525
  port = 3456,
23251
22526
  hostname,
23252
22527
  acceptAnyIp,
@@ -23271,9 +22546,9 @@ const startDevServer = async ({
23271
22546
  plugins = [],
23272
22547
  referenceAnalysis = {},
23273
22548
  nodeEsmResolution,
23274
- webResolution,
23275
22549
  supervisor = true,
23276
- fileSystemMagicRedirection,
22550
+ magicExtensions,
22551
+ magicDirectoryIndex,
23277
22552
  transpilation,
23278
22553
  cacheControl = true,
23279
22554
  ribbon = true,
@@ -23385,13 +22660,14 @@ const startDevServer = async ({
23385
22660
  contextCache,
23386
22661
  sourceDirectoryUrl,
23387
22662
  sourceMainFilePath,
22663
+ ignore,
23388
22664
  sourceFilesConfig,
23389
22665
  runtimeCompat,
23390
22666
  plugins,
23391
22667
  referenceAnalysis,
23392
22668
  nodeEsmResolution,
23393
- webResolution,
23394
- fileSystemMagicRedirection,
22669
+ magicExtensions,
22670
+ magicDirectoryIndex,
23395
22671
  supervisor,
23396
22672
  transpilation,
23397
22673
  clientAutoreload,