@jsenv/core 39.3.5 → 39.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@ Jsenv is a suite of tools that can be used in projects involving JavaScript.
14
14
  It favors standards and simplicity.
15
15
  As a result it can be enjoyed by people without much experience in tooling or seeking for simple tools without hidden complexities.
16
16
 
17
- If you want to try jsenv on your machine, use [@jsenv/cli](./packages/related/cli/readme.md).
17
+ If you want to try jsenv on your machine, use [@jsenv/cli](./packages/related/cli/#jsenvcli).
18
18
 
19
19
  Link to [documentation](./md/users/users.md)
20
20
 
@@ -11562,6 +11562,7 @@ ${reason}`,
11562
11562
  ),
11563
11563
  );
11564
11564
  defineNonEnumerableProperties(resolveError, {
11565
+ isJsenvCookingError: true,
11565
11566
  name: "RESOLVE_URL_ERROR",
11566
11567
  code,
11567
11568
  reason,
@@ -11610,6 +11611,7 @@ ${reason}`,
11610
11611
  ),
11611
11612
  );
11612
11613
  defineNonEnumerableProperties(fetchError, {
11614
+ isJsenvCookingError: true,
11613
11615
  name: "FETCH_URL_CONTENT_ERROR",
11614
11616
  code,
11615
11617
  reason,
@@ -11670,6 +11672,9 @@ const createTransformUrlContentError = ({
11670
11672
  return error;
11671
11673
  }
11672
11674
  if (error.code === "PARSE_ERROR") {
11675
+ if (error.isJsenvCookingError) {
11676
+ return error;
11677
+ }
11673
11678
  const reference = urlInfo.firstReference;
11674
11679
  let trace = reference.trace;
11675
11680
  let line = error.line;
@@ -11717,13 +11722,16 @@ const createTransformUrlContentError = ({
11717
11722
  ${trace.message}
11718
11723
  ${error.message}`,
11719
11724
  {
11720
- "first reference": `${reference.trace.url}:${reference.trace.line}:${reference.trace.column}`,
11725
+ "first reference": reference.trace.url
11726
+ ? `${reference.trace.url}:${reference.trace.line}:${reference.trace.column}`
11727
+ : reference.trace.message,
11721
11728
  ...detailsFromFirstReference(reference),
11722
11729
  ...detailsFromPluginController(pluginController),
11723
11730
  },
11724
11731
  ),
11725
11732
  );
11726
11733
  defineNonEnumerableProperties(transformError, {
11734
+ isJsenvCookingError: true,
11727
11735
  name: "TRANSFORM_URL_CONTENT_ERROR",
11728
11736
  code: "PARSE_ERROR",
11729
11737
  reason: error.message,
@@ -11753,6 +11761,7 @@ ${reason}`,
11753
11761
  ),
11754
11762
  );
11755
11763
  defineNonEnumerableProperties(transformError, {
11764
+ isJsenvCookingError: true,
11756
11765
  cause: error,
11757
11766
  name: "TRANSFORM_URL_CONTENT_ERROR",
11758
11767
  code,
@@ -11788,6 +11797,7 @@ ${reference.trace.message}`,
11788
11797
  ),
11789
11798
  );
11790
11799
  defineNonEnumerableProperties(finalizeError, {
11800
+ isJsenvCookingError: true,
11791
11801
  ...(error && error instanceof Error ? { cause: error } : {}),
11792
11802
  name: "FINALIZE_URL_CONTENT_ERROR",
11793
11803
  reason: `"finalizeUrlContent" error on "${urlInfo.type}"`,
@@ -11849,9 +11859,9 @@ const detailsFromValueThrown = (valueThrownByPlugin) => {
11849
11859
  };
11850
11860
  };
11851
11861
 
11852
- const defineNonEnumerableProperties = (assertionError, properties) => {
11862
+ const defineNonEnumerableProperties = (object, properties) => {
11853
11863
  for (const key of Object.keys(properties)) {
11854
- Object.defineProperty(assertionError, key, {
11864
+ Object.defineProperty(object, key, {
11855
11865
  configurable: true,
11856
11866
  writable: true,
11857
11867
  value: properties[key],
@@ -14486,7 +14496,6 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
14486
14496
  urlInfo,
14487
14497
  error,
14488
14498
  });
14489
- urlInfo.error = transformError;
14490
14499
  throw transformError;
14491
14500
  }
14492
14501
  };
@@ -14518,9 +14527,6 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
14518
14527
 
14519
14528
  // urlInfo objects are reused, they must be "reset" before cooking them again
14520
14529
  if (urlInfo.error || urlInfo.content !== undefined) {
14521
- if (urlInfo.isInline) {
14522
- return;
14523
- }
14524
14530
  urlInfo.error = null;
14525
14531
  urlInfo.type = null;
14526
14532
  urlInfo.subtype = null;
@@ -14542,30 +14548,46 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
14542
14548
  });
14543
14549
  } catch (e) {
14544
14550
  urlInfo.error = e;
14545
- if (
14546
- urlInfo.isInline &&
14547
- e.code !== "DIRECTORY_REFERENCE_NOT_ALLOWED" &&
14548
- errorOnInlineContentCanSkipThrow(urlInfo)
14549
- ) {
14550
- // When something like <style> or <script> contains syntax error
14551
- // the HTML in itself it still valid
14552
- // keep the syntax error and continue with the HTML
14553
- if (e.code === "PARSE_ERROR") {
14554
- logger.error(`parse error on "${urlInfo.type}"
14551
+ if (urlInfo.isInline) {
14552
+ const parentUrlInfo = urlInfo.findParentIfInline();
14553
+ parentUrlInfo.error = e;
14554
+ }
14555
+ let errorWrapperMessage;
14556
+ if (e.code === "PARSE_ERROR") {
14557
+ errorWrapperMessage =
14558
+ e.name === "TRANSFORM_URL_CONTENT_ERROR"
14559
+ ? e.message
14560
+ : `parse error on "${urlInfo.type}"
14555
14561
  ${e.trace?.message}
14556
14562
  ${e.reason}
14557
14563
  --- declared in ---
14558
- ${urlInfo.firstReference.trace.message}`);
14559
- return;
14560
- }
14561
- logger.error(
14562
- `Error while cooking ${urlInfo.type}
14563
- ${urlInfo.firstReference.trace.message}
14564
- ${e.stack}`,
14565
- );
14564
+ ${urlInfo.firstReference.trace.message}`;
14565
+ } else if (e.isJsenvCookingError) {
14566
+ errorWrapperMessage = e.message;
14567
+ } else {
14568
+ errorWrapperMessage = `Error while cooking ${urlInfo.type}
14569
+ ${urlInfo.firstReference.trace.message}`;
14570
+ }
14571
+ // if we are cooking inline content during dev it's better not to throw
14572
+ // because the main url info (html) is still valid and can be returned to the browser
14573
+ if (
14574
+ urlInfo.isInline &&
14575
+ urlInfo.context.dev &&
14576
+ // but if we are explicitely requesting inline content file then we throw
14577
+ // to properly send 500 to the browser
14578
+ urlInfo.context.reference !== urlInfo.url
14579
+ ) {
14580
+ logger.error(errorWrapperMessage);
14566
14581
  return;
14567
14582
  }
14568
- throw e;
14583
+ if (e.isJsenvCookingError) {
14584
+ throw e;
14585
+ }
14586
+ const error = new Error(errorWrapperMessage, { cause: e });
14587
+ defineNonEnumerableProperties(error, {
14588
+ __INTERNAL_ERROR__: true,
14589
+ });
14590
+ throw error;
14569
14591
  }
14570
14592
  }
14571
14593
 
@@ -14655,23 +14677,6 @@ ${e.stack}`,
14655
14677
  return kitchen;
14656
14678
  };
14657
14679
 
14658
- // if we are cooking the inline content internally it's better not to throw
14659
- // because the main url info (html) is still valid
14660
- // but if we are explicitely requesting inline content during dev
14661
- // then we should throw
14662
- const errorOnInlineContentCanSkipThrow = (urlInfo) => {
14663
- if (urlInfo.context.build) {
14664
- return true;
14665
- }
14666
- if (
14667
- urlInfo.context.reference &&
14668
- urlInfo.context.reference.url === urlInfo.url
14669
- ) {
14670
- return false;
14671
- }
14672
- return true;
14673
- };
14674
-
14675
14680
  const debounceCook = (cook) => {
14676
14681
  const pendingDishes = new Map();
14677
14682
  return async (urlInfo, context) => {
@@ -15047,7 +15052,10 @@ const jsenvPluginDirectoryReferenceEffect = (
15047
15052
  }
15048
15053
  if (actionForDirectory === "error") {
15049
15054
  const error = new Error("Reference leads to a directory");
15050
- error.code = "DIRECTORY_REFERENCE_NOT_ALLOWED";
15055
+ defineNonEnumerableProperties(error, {
15056
+ isJsenvCookingError: true,
15057
+ code: "DIRECTORY_REFERENCE_NOT_ALLOWED",
15058
+ });
15051
15059
  throw error;
15052
15060
  }
15053
15061
  if (actionForDirectory === "preserve") {
@@ -16450,13 +16458,8 @@ const jsenvPluginHtmlReferenceAnalysis = ({
16450
16458
  });
16451
16459
 
16452
16460
  actions.push(async () => {
16453
- try {
16454
- await inlineReference.urlInfo.cook();
16455
- } catch (e) {
16456
- if (!e || e.code !== "PARSE_ERROR") {
16457
- throw e;
16458
- }
16459
- }
16461
+ const inlineUrlInfo = inlineReference.urlInfo;
16462
+ await inlineUrlInfo.cook();
16460
16463
  mutations.push(() => {
16461
16464
  if (hotAccept) {
16462
16465
  removeHtmlNodeText(node);
@@ -16464,7 +16467,7 @@ const jsenvPluginHtmlReferenceAnalysis = ({
16464
16467
  "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
16465
16468
  });
16466
16469
  } else {
16467
- setHtmlNodeText(node, inlineReference.urlInfo.content, {
16470
+ setHtmlNodeText(node, inlineUrlInfo.content, {
16468
16471
  indentation: false, // indentation would decrease stack trace precision
16469
16472
  });
16470
16473
  setHtmlNodeAttributes(node, {
@@ -16737,7 +16740,8 @@ const jsenvPluginHtmlReferenceAnalysis = ({
16737
16740
  }
16738
16741
  mutations.forEach((mutation) => mutation());
16739
16742
  mutations.length = 0;
16740
- return stringifyHtmlAst(htmlAst);
16743
+ const html = stringifyHtmlAst(htmlAst);
16744
+ return html;
16741
16745
  } catch (e) {
16742
16746
  importmapLoaded();
16743
16747
  throw e;
@@ -17104,8 +17108,12 @@ const jsenvPluginInlineContentFetcher = () => {
17104
17108
  // when updating the file, first reference is the previous version
17105
17109
  // - we cannot use urlInfo.lastReference because it can be the reference created by "http_request"
17106
17110
  let lastInlineReference;
17111
+ let originalContent = urlInfo.originalContent;
17107
17112
  for (const reference of urlInfo.referenceFromOthersSet) {
17108
17113
  if (reference.isInline) {
17114
+ if (originalContent === undefined) {
17115
+ originalContent = reference.content;
17116
+ }
17109
17117
  lastInlineReference = reference;
17110
17118
  }
17111
17119
  }
@@ -17120,8 +17128,17 @@ const jsenvPluginInlineContentFetcher = () => {
17120
17128
  }
17121
17129
  }
17122
17130
  return {
17123
- originalContent: urlInfo.originalContent,
17124
- content: lastInlineReference.content,
17131
+ originalContent,
17132
+ content:
17133
+ // we must favor original content to re-apply the same plugin logic
17134
+ // so that the same references are generated
17135
+ // without this we would try to resolve references like
17136
+ // "/node_modules/package/file.js" instead of "package/file.js"
17137
+ // meaning we would not create the implicit dependency to package.json
17138
+ // resulting in a reload of the browser (as implicit reference to package.json is gone)
17139
+ originalContent === undefined
17140
+ ? lastInlineReference.content
17141
+ : originalContent,
17125
17142
  contentType: lastInlineReference.contentType,
17126
17143
  };
17127
17144
  },
@@ -18274,47 +18291,46 @@ const createNodeEsmResolver = ({
18274
18291
  specifier: reference.specifier,
18275
18292
  preservesSymlink,
18276
18293
  });
18277
- if (ownerUrlInfo.context.dev) {
18278
- const dependsOnPackageJson =
18279
- type !== "relative_specifier" &&
18280
- type !== "absolute_specifier" &&
18281
- type !== "node_builtin_specifier";
18282
- if (dependsOnPackageJson) {
18283
- // this reference depends on package.json and node_modules
18284
- // to be resolved. Each file using this specifier
18285
- // must be invalidated when corresponding package.json changes
18286
- addRelationshipWithPackageJson({
18287
- reference,
18288
- packageJsonUrl: `${packageDirectoryUrl}package.json`,
18289
- field: type.startsWith("field:")
18290
- ? `#${type.slice("field:".length)}`
18291
- : "",
18292
- });
18293
- }
18294
+ if (ownerUrlInfo.context.build) {
18295
+ return url;
18294
18296
  }
18295
- if (ownerUrlInfo.context.dev) {
18296
- // without this check a file inside a project without package.json
18297
- // could be considered as a node module if there is a ancestor package.json
18298
- // but we want to version only node modules
18299
- if (url.includes("/node_modules/")) {
18300
- const packageDirectoryUrl = defaultLookupPackageScope(url);
18301
- if (
18302
- packageDirectoryUrl &&
18303
- packageDirectoryUrl !== ownerUrlInfo.context.rootDirectoryUrl
18304
- ) {
18305
- const packageVersion =
18306
- defaultReadPackageJson(packageDirectoryUrl).version;
18307
- // package version can be null, see https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
18308
- if (packageVersion) {
18309
- addRelationshipWithPackageJson({
18310
- reference,
18311
- packageJsonUrl: `${packageDirectoryUrl}package.json`,
18312
- field: "version",
18313
- hasVersioningEffect: true,
18314
- });
18315
- }
18316
- reference.version = packageVersion;
18297
+ const dependsOnPackageJson =
18298
+ type !== "relative_specifier" &&
18299
+ type !== "absolute_specifier" &&
18300
+ type !== "node_builtin_specifier";
18301
+ if (dependsOnPackageJson) {
18302
+ // this reference depends on package.json and node_modules
18303
+ // to be resolved. Each file using this specifier
18304
+ // must be invalidated when corresponding package.json changes
18305
+ addRelationshipWithPackageJson({
18306
+ reference,
18307
+ packageJsonUrl: `${packageDirectoryUrl}package.json`,
18308
+ field: type.startsWith("field:")
18309
+ ? `#${type.slice("field:".length)}`
18310
+ : "",
18311
+ });
18312
+ }
18313
+ // without this check a file inside a project without package.json
18314
+ // could be considered as a node module if there is a ancestor package.json
18315
+ // but we want to version only node modules
18316
+ if (url.includes("/node_modules/")) {
18317
+ const packageDirectoryUrl = defaultLookupPackageScope(url);
18318
+ if (
18319
+ packageDirectoryUrl &&
18320
+ packageDirectoryUrl !== ownerUrlInfo.context.rootDirectoryUrl
18321
+ ) {
18322
+ const packageVersion =
18323
+ defaultReadPackageJson(packageDirectoryUrl).version;
18324
+ // package version can be null, see https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
18325
+ if (packageVersion) {
18326
+ addRelationshipWithPackageJson({
18327
+ reference,
18328
+ packageJsonUrl: `${packageDirectoryUrl}package.json`,
18329
+ field: "version",
18330
+ hasVersioningEffect: true,
18331
+ });
18317
18332
  }
18333
+ reference.version = packageVersion;
18318
18334
  }
18319
18335
  }
18320
18336
  return url;
@@ -23241,7 +23257,6 @@ const startDevServer = async ({
23241
23257
  };
23242
23258
  }
23243
23259
  }
23244
-
23245
23260
  await urlInfo.cook({ request, reference });
23246
23261
  let { response } = urlInfo;
23247
23262
  if (response) {
@@ -23284,9 +23299,8 @@ const startDevServer = async ({
23284
23299
  },
23285
23300
  );
23286
23301
  return response;
23287
- } catch (e) {
23288
- urlInfo.error = e;
23289
- const originalError = e ? e.cause || e : e;
23302
+ } catch (error) {
23303
+ const originalError = error ? error.cause || error : error;
23290
23304
  if (originalError.asResponse) {
23291
23305
  return originalError.asResponse();
23292
23306
  }
@@ -23298,13 +23312,13 @@ const startDevServer = async ({
23298
23312
  if (urlInfo.content !== undefined) {
23299
23313
  kitchen.context.logger.error(`Error while handling ${request.url}:
23300
23314
  ${originalError.reasonCode || originalError.code}
23301
- ${e.trace?.message}`);
23315
+ ${error.trace?.message}`);
23302
23316
  return {
23303
23317
  url: reference.url,
23304
23318
  status: 200,
23305
23319
  // reason becomes the http response statusText, it must not contain invalid chars
23306
23320
  // https://github.com/nodejs/node/blob/0c27ca4bc9782d658afeaebcec85ec7b28f1cc35/lib/_http_common.js#L221
23307
- statusText: e.reason,
23321
+ statusText: error.reason,
23308
23322
  statusMessage: originalError.message,
23309
23323
  headers: {
23310
23324
  "content-type": urlInfo.contentType,
@@ -23317,7 +23331,7 @@ ${e.trace?.message}`);
23317
23331
  return {
23318
23332
  url: reference.url,
23319
23333
  status: 500,
23320
- statusText: e.reason,
23334
+ statusText: error.reason,
23321
23335
  statusMessage: originalError.message,
23322
23336
  headers: {
23323
23337
  "cache-control": "no-store",
@@ -23352,8 +23366,11 @@ ${e.trace?.message}`);
23352
23366
  return {
23353
23367
  url: reference.url,
23354
23368
  status: 500,
23355
- statusText: e.reason,
23356
- statusMessage: e.stack,
23369
+ statusText: error.reason,
23370
+ statusMessage: error.stack,
23371
+ headers: {
23372
+ "cache-control": "no-store",
23373
+ },
23357
23374
  };
23358
23375
  }
23359
23376
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "39.3.5",
3
+ "version": "39.3.7",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -448,7 +448,6 @@ export const startDevServer = async ({
448
448
  };
449
449
  }
450
450
  }
451
-
452
451
  await urlInfo.cook({ request, reference });
453
452
  let { response } = urlInfo;
454
453
  if (response) {
@@ -491,9 +490,8 @@ export const startDevServer = async ({
491
490
  },
492
491
  );
493
492
  return response;
494
- } catch (e) {
495
- urlInfo.error = e;
496
- const originalError = e ? e.cause || e : e;
493
+ } catch (error) {
494
+ const originalError = error ? error.cause || error : error;
497
495
  if (originalError.asResponse) {
498
496
  return originalError.asResponse();
499
497
  }
@@ -505,13 +503,13 @@ export const startDevServer = async ({
505
503
  if (urlInfo.content !== undefined) {
506
504
  kitchen.context.logger.error(`Error while handling ${request.url}:
507
505
  ${originalError.reasonCode || originalError.code}
508
- ${e.trace?.message}`);
506
+ ${error.trace?.message}`);
509
507
  return {
510
508
  url: reference.url,
511
509
  status: 200,
512
510
  // reason becomes the http response statusText, it must not contain invalid chars
513
511
  // https://github.com/nodejs/node/blob/0c27ca4bc9782d658afeaebcec85ec7b28f1cc35/lib/_http_common.js#L221
514
- statusText: e.reason,
512
+ statusText: error.reason,
515
513
  statusMessage: originalError.message,
516
514
  headers: {
517
515
  "content-type": urlInfo.contentType,
@@ -524,7 +522,7 @@ ${e.trace?.message}`);
524
522
  return {
525
523
  url: reference.url,
526
524
  status: 500,
527
- statusText: e.reason,
525
+ statusText: error.reason,
528
526
  statusMessage: originalError.message,
529
527
  headers: {
530
528
  "cache-control": "no-store",
@@ -559,8 +557,11 @@ ${e.trace?.message}`);
559
557
  return {
560
558
  url: reference.url,
561
559
  status: 500,
562
- statusText: e.reason,
563
- statusMessage: e.stack,
560
+ statusText: error.reason,
561
+ statusMessage: error.stack,
562
+ headers: {
563
+ "cache-control": "no-store",
564
+ },
564
565
  };
565
566
  }
566
567
  },
@@ -25,6 +25,7 @@ ${reason}`,
25
25
  ),
26
26
  );
27
27
  defineNonEnumerableProperties(resolveError, {
28
+ isJsenvCookingError: true,
28
29
  name: "RESOLVE_URL_ERROR",
29
30
  code,
30
31
  reason,
@@ -73,6 +74,7 @@ ${reason}`,
73
74
  ),
74
75
  );
75
76
  defineNonEnumerableProperties(fetchError, {
77
+ isJsenvCookingError: true,
76
78
  name: "FETCH_URL_CONTENT_ERROR",
77
79
  code,
78
80
  reason,
@@ -133,6 +135,9 @@ export const createTransformUrlContentError = ({
133
135
  return error;
134
136
  }
135
137
  if (error.code === "PARSE_ERROR") {
138
+ if (error.isJsenvCookingError) {
139
+ return error;
140
+ }
136
141
  const reference = urlInfo.firstReference;
137
142
  let trace = reference.trace;
138
143
  let line = error.line;
@@ -180,13 +185,16 @@ export const createTransformUrlContentError = ({
180
185
  ${trace.message}
181
186
  ${error.message}`,
182
187
  {
183
- "first reference": `${reference.trace.url}:${reference.trace.line}:${reference.trace.column}`,
188
+ "first reference": reference.trace.url
189
+ ? `${reference.trace.url}:${reference.trace.line}:${reference.trace.column}`
190
+ : reference.trace.message,
184
191
  ...detailsFromFirstReference(reference),
185
192
  ...detailsFromPluginController(pluginController),
186
193
  },
187
194
  ),
188
195
  );
189
196
  defineNonEnumerableProperties(transformError, {
197
+ isJsenvCookingError: true,
190
198
  name: "TRANSFORM_URL_CONTENT_ERROR",
191
199
  code: "PARSE_ERROR",
192
200
  reason: error.message,
@@ -216,6 +224,7 @@ ${reason}`,
216
224
  ),
217
225
  );
218
226
  defineNonEnumerableProperties(transformError, {
227
+ isJsenvCookingError: true,
219
228
  cause: error,
220
229
  name: "TRANSFORM_URL_CONTENT_ERROR",
221
230
  code,
@@ -251,6 +260,7 @@ ${reference.trace.message}`,
251
260
  ),
252
261
  );
253
262
  defineNonEnumerableProperties(finalizeError, {
263
+ isJsenvCookingError: true,
254
264
  ...(error && error instanceof Error ? { cause: error } : {}),
255
265
  name: "FINALIZE_URL_CONTENT_ERROR",
256
266
  reason: `"finalizeUrlContent" error on "${urlInfo.type}"`,
@@ -312,9 +322,9 @@ const detailsFromValueThrown = (valueThrownByPlugin) => {
312
322
  };
313
323
  };
314
324
 
315
- const defineNonEnumerableProperties = (assertionError, properties) => {
325
+ export const defineNonEnumerableProperties = (object, properties) => {
316
326
  for (const key of Object.keys(properties)) {
317
- Object.defineProperty(assertionError, key, {
327
+ Object.defineProperty(object, key, {
318
328
  configurable: true,
319
329
  writable: true,
320
330
  value: properties[key],
@@ -17,6 +17,7 @@ import {
17
17
  createFinalizeUrlContentError,
18
18
  createResolveUrlError,
19
19
  createTransformUrlContentError,
20
+ defineNonEnumerableProperties,
20
21
  } from "./errors.js";
21
22
  import { assertFetchedContentCompliance } from "./fetched_content_compliance.js";
22
23
  import { createUrlGraph } from "./url_graph/url_graph.js";
@@ -426,7 +427,6 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
426
427
  urlInfo,
427
428
  error,
428
429
  });
429
- urlInfo.error = transformError;
430
430
  throw transformError;
431
431
  }
432
432
  };
@@ -458,9 +458,6 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
458
458
 
459
459
  // urlInfo objects are reused, they must be "reset" before cooking them again
460
460
  if (urlInfo.error || urlInfo.content !== undefined) {
461
- if (urlInfo.isInline) {
462
- return;
463
- }
464
461
  urlInfo.error = null;
465
462
  urlInfo.type = null;
466
463
  urlInfo.subtype = null;
@@ -482,30 +479,46 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
482
479
  });
483
480
  } catch (e) {
484
481
  urlInfo.error = e;
485
- if (
486
- urlInfo.isInline &&
487
- e.code !== "DIRECTORY_REFERENCE_NOT_ALLOWED" &&
488
- errorOnInlineContentCanSkipThrow(urlInfo)
489
- ) {
490
- // When something like <style> or <script> contains syntax error
491
- // the HTML in itself it still valid
492
- // keep the syntax error and continue with the HTML
493
- if (e.code === "PARSE_ERROR") {
494
- logger.error(`parse error on "${urlInfo.type}"
482
+ if (urlInfo.isInline) {
483
+ const parentUrlInfo = urlInfo.findParentIfInline();
484
+ parentUrlInfo.error = e;
485
+ }
486
+ let errorWrapperMessage;
487
+ if (e.code === "PARSE_ERROR") {
488
+ errorWrapperMessage =
489
+ e.name === "TRANSFORM_URL_CONTENT_ERROR"
490
+ ? e.message
491
+ : `parse error on "${urlInfo.type}"
495
492
  ${e.trace?.message}
496
493
  ${e.reason}
497
494
  --- declared in ---
498
- ${urlInfo.firstReference.trace.message}`);
499
- return;
500
- }
501
- logger.error(
502
- `Error while cooking ${urlInfo.type}
503
- ${urlInfo.firstReference.trace.message}
504
- ${e.stack}`,
505
- );
495
+ ${urlInfo.firstReference.trace.message}`;
496
+ } else if (e.isJsenvCookingError) {
497
+ errorWrapperMessage = e.message;
498
+ } else {
499
+ errorWrapperMessage = `Error while cooking ${urlInfo.type}
500
+ ${urlInfo.firstReference.trace.message}`;
501
+ }
502
+ // if we are cooking inline content during dev it's better not to throw
503
+ // because the main url info (html) is still valid and can be returned to the browser
504
+ if (
505
+ urlInfo.isInline &&
506
+ urlInfo.context.dev &&
507
+ // but if we are explicitely requesting inline content file then we throw
508
+ // to properly send 500 to the browser
509
+ urlInfo.context.reference !== urlInfo.url
510
+ ) {
511
+ logger.error(errorWrapperMessage);
506
512
  return;
507
513
  }
508
- throw e;
514
+ if (e.isJsenvCookingError) {
515
+ throw e;
516
+ }
517
+ const error = new Error(errorWrapperMessage, { cause: e });
518
+ defineNonEnumerableProperties(error, {
519
+ __INTERNAL_ERROR__: true,
520
+ });
521
+ throw error;
509
522
  }
510
523
  }
511
524
 
@@ -595,23 +608,6 @@ ${e.stack}`,
595
608
  return kitchen;
596
609
  };
597
610
 
598
- // if we are cooking the inline content internally it's better not to throw
599
- // because the main url info (html) is still valid
600
- // but if we are explicitely requesting inline content during dev
601
- // then we should throw
602
- const errorOnInlineContentCanSkipThrow = (urlInfo) => {
603
- if (urlInfo.context.build) {
604
- return true;
605
- }
606
- if (
607
- urlInfo.context.reference &&
608
- urlInfo.context.reference.url === urlInfo.url
609
- ) {
610
- return false;
611
- }
612
- return true;
613
- };
614
-
615
611
  const debounceCook = (cook) => {
616
612
  const pendingDishes = new Map();
617
613
  return async (urlInfo, context) => {
@@ -1,4 +1,5 @@
1
1
  import { urlToFilename } from "@jsenv/urls";
2
+ import { defineNonEnumerableProperties } from "../../kitchen/errors.js";
2
3
 
3
4
  export const jsenvPluginDirectoryReferenceEffect = (
4
5
  directoryReferenceEffect = "error",
@@ -53,7 +54,10 @@ export const jsenvPluginDirectoryReferenceEffect = (
53
54
  }
54
55
  if (actionForDirectory === "error") {
55
56
  const error = new Error("Reference leads to a directory");
56
- error.code = "DIRECTORY_REFERENCE_NOT_ALLOWED";
57
+ defineNonEnumerableProperties(error, {
58
+ isJsenvCookingError: true,
59
+ code: "DIRECTORY_REFERENCE_NOT_ALLOWED",
60
+ });
57
61
  throw error;
58
62
  }
59
63
  if (actionForDirectory === "preserve") {
@@ -274,13 +274,8 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
274
274
  });
275
275
 
276
276
  actions.push(async () => {
277
- try {
278
- await inlineReference.urlInfo.cook();
279
- } catch (e) {
280
- if (!e || e.code !== "PARSE_ERROR") {
281
- throw e;
282
- }
283
- }
277
+ const inlineUrlInfo = inlineReference.urlInfo;
278
+ await inlineUrlInfo.cook();
284
279
  mutations.push(() => {
285
280
  if (hotAccept) {
286
281
  removeHtmlNodeText(node);
@@ -288,7 +283,7 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
288
283
  "jsenv-cooked-by": "jsenv:html_inline_content_analysis",
289
284
  });
290
285
  } else {
291
- setHtmlNodeText(node, inlineReference.urlInfo.content, {
286
+ setHtmlNodeText(node, inlineUrlInfo.content, {
292
287
  indentation: false, // indentation would decrease stack trace precision
293
288
  });
294
289
  setHtmlNodeAttributes(node, {
@@ -561,7 +556,8 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
561
556
  }
562
557
  mutations.forEach((mutation) => mutation());
563
558
  mutations.length = 0;
564
- return stringifyHtmlAst(htmlAst);
559
+ const html = stringifyHtmlAst(htmlAst);
560
+ return html;
565
561
  } catch (e) {
566
562
  importmapLoaded();
567
563
  throw e;
@@ -45,8 +45,12 @@ const jsenvPluginInlineContentFetcher = () => {
45
45
  // when updating the file, first reference is the previous version
46
46
  // - we cannot use urlInfo.lastReference because it can be the reference created by "http_request"
47
47
  let lastInlineReference;
48
+ let originalContent = urlInfo.originalContent;
48
49
  for (const reference of urlInfo.referenceFromOthersSet) {
49
50
  if (reference.isInline) {
51
+ if (originalContent === undefined) {
52
+ originalContent = reference.content;
53
+ }
50
54
  lastInlineReference = reference;
51
55
  }
52
56
  }
@@ -61,8 +65,17 @@ const jsenvPluginInlineContentFetcher = () => {
61
65
  }
62
66
  }
63
67
  return {
64
- originalContent: urlInfo.originalContent,
65
- content: lastInlineReference.content,
68
+ originalContent,
69
+ content:
70
+ // we must favor original content to re-apply the same plugin logic
71
+ // so that the same references are generated
72
+ // without this we would try to resolve references like
73
+ // "/node_modules/package/file.js" instead of "package/file.js"
74
+ // meaning we would not create the implicit dependency to package.json
75
+ // resulting in a reload of the browser (as implicit reference to package.json is gone)
76
+ originalContent === undefined
77
+ ? lastInlineReference.content
78
+ : originalContent,
66
79
  contentType: lastInlineReference.contentType,
67
80
  };
68
81
  },
@@ -53,47 +53,46 @@ export const createNodeEsmResolver = ({
53
53
  specifier: reference.specifier,
54
54
  preservesSymlink,
55
55
  });
56
- if (ownerUrlInfo.context.dev) {
57
- const dependsOnPackageJson =
58
- type !== "relative_specifier" &&
59
- type !== "absolute_specifier" &&
60
- type !== "node_builtin_specifier";
61
- if (dependsOnPackageJson) {
62
- // this reference depends on package.json and node_modules
63
- // to be resolved. Each file using this specifier
64
- // must be invalidated when corresponding package.json changes
65
- addRelationshipWithPackageJson({
66
- reference,
67
- packageJsonUrl: `${packageDirectoryUrl}package.json`,
68
- field: type.startsWith("field:")
69
- ? `#${type.slice("field:".length)}`
70
- : "",
71
- });
72
- }
56
+ if (ownerUrlInfo.context.build) {
57
+ return url;
58
+ }
59
+ const dependsOnPackageJson =
60
+ type !== "relative_specifier" &&
61
+ type !== "absolute_specifier" &&
62
+ type !== "node_builtin_specifier";
63
+ if (dependsOnPackageJson) {
64
+ // this reference depends on package.json and node_modules
65
+ // to be resolved. Each file using this specifier
66
+ // must be invalidated when corresponding package.json changes
67
+ addRelationshipWithPackageJson({
68
+ reference,
69
+ packageJsonUrl: `${packageDirectoryUrl}package.json`,
70
+ field: type.startsWith("field:")
71
+ ? `#${type.slice("field:".length)}`
72
+ : "",
73
+ });
73
74
  }
74
- if (ownerUrlInfo.context.dev) {
75
- // without this check a file inside a project without package.json
76
- // could be considered as a node module if there is a ancestor package.json
77
- // but we want to version only node modules
78
- if (url.includes("/node_modules/")) {
79
- const packageDirectoryUrl = defaultLookupPackageScope(url);
80
- if (
81
- packageDirectoryUrl &&
82
- packageDirectoryUrl !== ownerUrlInfo.context.rootDirectoryUrl
83
- ) {
84
- const packageVersion =
85
- defaultReadPackageJson(packageDirectoryUrl).version;
86
- // package version can be null, see https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
87
- if (packageVersion) {
88
- addRelationshipWithPackageJson({
89
- reference,
90
- packageJsonUrl: `${packageDirectoryUrl}package.json`,
91
- field: "version",
92
- hasVersioningEffect: true,
93
- });
94
- }
95
- reference.version = packageVersion;
75
+ // without this check a file inside a project without package.json
76
+ // could be considered as a node module if there is a ancestor package.json
77
+ // but we want to version only node modules
78
+ if (url.includes("/node_modules/")) {
79
+ const packageDirectoryUrl = defaultLookupPackageScope(url);
80
+ if (
81
+ packageDirectoryUrl &&
82
+ packageDirectoryUrl !== ownerUrlInfo.context.rootDirectoryUrl
83
+ ) {
84
+ const packageVersion =
85
+ defaultReadPackageJson(packageDirectoryUrl).version;
86
+ // package version can be null, see https://github.com/babel/babel/blob/2ce56e832c2dd7a7ed92c89028ba929f874c2f5c/packages/babel-runtime/helpers/esm/package.json#L2
87
+ if (packageVersion) {
88
+ addRelationshipWithPackageJson({
89
+ reference,
90
+ packageJsonUrl: `${packageDirectoryUrl}package.json`,
91
+ field: "version",
92
+ hasVersioningEffect: true,
93
+ });
96
94
  }
95
+ reference.version = packageVersion;
97
96
  }
98
97
  }
99
98
  return url;