@jsenv/core 39.9.4 → 39.9.6

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.
@@ -52,7 +52,29 @@ const initAutoreload = ({ mainFilePath }) => {
52
52
  currentExecution: null,
53
53
  reload: () => {
54
54
  const someEffectIsFullReload = reloader.changes.value.some(
55
- (reloadMessage) => reloadMessage.type === "full",
55
+ (reloadMessage) => {
56
+ if (reloadMessage.type === "full") {
57
+ return true;
58
+ }
59
+ if (reloadMessage.type === "hot") {
60
+ for (const reloadInstruction of reloadMessage.hotInstructions) {
61
+ if (reloadInstruction.type === "html") {
62
+ const acceptedByUrl = new URL(
63
+ reloadInstruction.acceptedBy,
64
+ `${window.location.origin}/`,
65
+ ).href;
66
+ const isCurrentHtmlFile = compareTwoUrlPaths(
67
+ acceptedByUrl,
68
+ window.location.href,
69
+ );
70
+ if (isCurrentHtmlFile) {
71
+ return true;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return false;
77
+ },
56
78
  );
57
79
  if (someEffectIsFullReload) {
58
80
  dispatchBeforeFullReload();
@@ -136,30 +158,32 @@ This could be due to syntax errors or importing non-existent modules (see errors
136
158
  // - code was not executed (code splitting with dynamic import)
137
159
  // - import.meta.hot.accept() is not called (happens for HTML and CSS)
138
160
  if (type === "prune") {
139
- if (urlHotMeta) {
140
- dispatchBeforePrune();
141
- delete urlHotMetas[urlToFetch];
142
- if (urlHotMeta.disposeCallback) {
143
- console.log(
144
- `[jsenv] cleanup ${boundary} (no longer referenced by ${acceptedBy})`,
145
- );
146
- await urlHotMeta.disposeCallback();
147
- }
161
+ if (!urlHotMeta) {
162
+ // code not executed for this url, no need to prune
163
+ continue;
164
+ }
165
+ dispatchBeforePrune();
166
+ delete urlHotMetas[urlToFetch];
167
+ if (urlHotMeta.disposeCallback) {
168
+ console.log(
169
+ `[jsenv] cleanup ${boundary} (no longer referenced by ${acceptedBy})`,
170
+ );
171
+ await urlHotMeta.disposeCallback();
148
172
  }
149
173
  continue;
150
174
  }
151
- if (acceptedBy === boundary) {
152
- console.log(`[jsenv] hot reloading ${boundary} (${cause})`);
153
- } else {
154
- console.log(
155
- `[jsenv] hot reloading ${acceptedBy} usage in ${boundary} (${cause})`,
156
- );
157
- }
158
175
  if (type === "js_module") {
159
176
  if (!urlHotMeta) {
160
- // code was not executed, no need to re-execute it
177
+ // code not yet executed for this url, no need to re-execute it
161
178
  continue;
162
179
  }
180
+ if (acceptedBy === boundary) {
181
+ console.log(`[jsenv] hot reload ${boundary} (${cause})`);
182
+ } else {
183
+ console.log(
184
+ `[jsenv] hot reload ${acceptedBy} usage in ${boundary} (${cause})`,
185
+ );
186
+ }
163
187
  if (urlHotMeta.disposeCallback) {
164
188
  await urlHotMeta.disposeCallback();
165
189
  }
@@ -189,6 +213,13 @@ This could be due to syntax errors or importing non-existent modules (see errors
189
213
  // we are not in that HTML page
190
214
  continue;
191
215
  }
216
+ if (acceptedBy === boundary) {
217
+ console.log(`[jsenv] hot reload ${boundary} (${cause})`);
218
+ } else {
219
+ console.log(
220
+ `[jsenv] hot reload ${acceptedBy} usage in ${boundary} (${cause})`,
221
+ );
222
+ }
192
223
  const urlToReload = new URL(acceptedBy, `${window.location.origin}/`)
193
224
  .href;
194
225
  const domNodesUsingUrl = getDOMNodesUsingUrl(urlToReload);
@@ -218,7 +218,7 @@
218
218
  null,
219
219
  false,
220
220
  );
221
- for (var next = void 0; (next = iter.nextNode()); ) {
221
+ for (var next = undefined; (next = iter.nextNode()); ) {
222
222
  callback(getShadowRoot(next));
223
223
  }
224
224
  }
@@ -225,7 +225,7 @@ var runtime = (function (exports) {
225
225
  // AsyncIterator objects; they just return a Promise for the value of
226
226
  // the final result produced by the iterator.
227
227
  exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {
228
- if (PromiseImpl === void 0) PromiseImpl = Promise;
228
+ if (PromiseImpl === undefined) PromiseImpl = Promise;
229
229
 
230
230
  var iter = new AsyncIterator(
231
231
  wrap(innerFn, outerFn, self, tryLocsList),
package/dist/js/ws.js CHANGED
@@ -1738,8 +1738,8 @@ function requireBuffer () {
1738
1738
  byteOffset = 0;
1739
1739
  } else if (byteOffset > 0x7fffffff) {
1740
1740
  byteOffset = 0x7fffffff;
1741
- } else if (byteOffset < -0x80000000) {
1742
- byteOffset = -0x80000000;
1741
+ } else if (byteOffset < -2147483648) {
1742
+ byteOffset = -2147483648;
1743
1743
  }
1744
1744
  byteOffset = +byteOffset; // Coerce to Number.
1745
1745
  if (numberIsNaN(byteOffset)) {
@@ -2480,7 +2480,7 @@ function requireBuffer () {
2480
2480
  Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
2481
2481
  value = +value;
2482
2482
  offset = offset >>> 0;
2483
- if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80);
2483
+ if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -128);
2484
2484
  if (value < 0) value = 0xff + value + 1;
2485
2485
  this[offset] = (value & 0xff);
2486
2486
  return offset + 1
@@ -2489,7 +2489,7 @@ function requireBuffer () {
2489
2489
  Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
2490
2490
  value = +value;
2491
2491
  offset = offset >>> 0;
2492
- if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000);
2492
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -32768);
2493
2493
  this[offset] = (value & 0xff);
2494
2494
  this[offset + 1] = (value >>> 8);
2495
2495
  return offset + 2
@@ -2498,7 +2498,7 @@ function requireBuffer () {
2498
2498
  Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
2499
2499
  value = +value;
2500
2500
  offset = offset >>> 0;
2501
- if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000);
2501
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -32768);
2502
2502
  this[offset] = (value >>> 8);
2503
2503
  this[offset + 1] = (value & 0xff);
2504
2504
  return offset + 2
@@ -2507,7 +2507,7 @@ function requireBuffer () {
2507
2507
  Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
2508
2508
  value = +value;
2509
2509
  offset = offset >>> 0;
2510
- if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
2510
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -2147483648);
2511
2511
  this[offset] = (value & 0xff);
2512
2512
  this[offset + 1] = (value >>> 8);
2513
2513
  this[offset + 2] = (value >>> 16);
@@ -2518,7 +2518,7 @@ function requireBuffer () {
2518
2518
  Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
2519
2519
  value = +value;
2520
2520
  offset = offset >>> 0;
2521
- if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
2521
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -2147483648);
2522
2522
  if (value < 0) value = 0xffffffff + value + 1;
2523
2523
  this[offset] = (value >>> 24);
2524
2524
  this[offset + 1] = (value >>> 16);
@@ -1968,7 +1968,7 @@ const comparePathnames = (leftPathame, rightPathname) => {
1968
1968
 
1969
1969
  // longer comes first
1970
1970
  if (!leftPartExists) {
1971
- return +1;
1971
+ return 1;
1972
1972
  }
1973
1973
  if (!rightPartExists) {
1974
1974
  return -1;
@@ -1978,7 +1978,7 @@ const comparePathnames = (leftPathame, rightPathname) => {
1978
1978
  const rightPartIsLast = i === rightPartArray.length - 1;
1979
1979
  // folder comes first
1980
1980
  if (leftPartIsLast && !rightPartIsLast) {
1981
- return +1;
1981
+ return 1;
1982
1982
  }
1983
1983
  if (!leftPartIsLast && rightPartIsLast) {
1984
1984
  return -1;
@@ -1995,7 +1995,7 @@ const comparePathnames = (leftPathame, rightPathname) => {
1995
1995
  }
1996
1996
 
1997
1997
  if (leftLength < rightLength) {
1998
- return +1;
1998
+ return 1;
1999
1999
  }
2000
2000
  if (leftLength > rightLength) {
2001
2001
  return -1;
@@ -3900,6 +3900,7 @@ const writeDirectorySync = (
3900
3900
  .slice(0, -1),
3901
3901
  ),
3902
3902
  );
3903
+ destinationStats = null;
3903
3904
  } else {
3904
3905
  throw new Error(
3905
3906
  `cannot write directory at ${destinationPath} because there is a file at ${urlToFileSystemPath(
@@ -3919,10 +3920,14 @@ const writeDirectorySync = (
3919
3920
  }
3920
3921
  throw new Error(`directory already exists at ${destinationPath}`);
3921
3922
  }
3922
- const destinationType = statsToType(destinationStats);
3923
- throw new Error(
3924
- `cannot write directory at ${destinationPath} because there is a ${destinationType}`,
3925
- );
3923
+ if (force) {
3924
+ unlinkSync(destinationPath);
3925
+ } else {
3926
+ const destinationType = statsToType(destinationStats);
3927
+ throw new Error(
3928
+ `cannot write directory at ${destinationPath} because there is a ${destinationType}`,
3929
+ );
3930
+ }
3926
3931
  }
3927
3932
 
3928
3933
  try {
@@ -13056,7 +13061,7 @@ const createReference = ({
13056
13061
  isInline = false,
13057
13062
  content,
13058
13063
  contentType,
13059
- leadsToADirectory = false,
13064
+ fsStat = null,
13060
13065
  debug = false,
13061
13066
  original = null,
13062
13067
  prev = null,
@@ -13117,7 +13122,7 @@ const createReference = ({
13117
13122
  version,
13118
13123
  injected,
13119
13124
  timing: {},
13120
- leadsToADirectory,
13125
+ fsStat,
13121
13126
  debug,
13122
13127
  // for inline resources the reference contains the content
13123
13128
  isInline,
@@ -14038,7 +14043,7 @@ const createUrlInfo = (url, context) => {
14038
14043
  version: reference.version,
14039
14044
  content: reference.content,
14040
14045
  contentType: reference.contentType,
14041
- leadsToADirectory: reference.leadsToADirectory,
14046
+ fsStat: reference.fsStat,
14042
14047
  debug: reference.debug,
14043
14048
  importAttributes: reference.importAttributes,
14044
14049
  astInfo: reference.astInfo,
@@ -14498,11 +14503,15 @@ const createUrlInfoTransformer = ({
14498
14503
  if (!generatedUrl.startsWith("file:")) {
14499
14504
  return;
14500
14505
  }
14501
- if (
14502
- urlInfo.type === "directory" ||
14503
- // happens when type is "html" to list directory content for example
14504
- urlInfo.firstReference?.leadsToADirectory
14505
- ) {
14506
+ if (urlToPathname$1(generatedUrl).endsWith("/")) {
14507
+ // when users explicitely request a directory
14508
+ // we can't write the content returned by the server in ".jsenv" at that url
14509
+ // because it would try to write a directory
14510
+ // ideally we would decide a filename for this
14511
+ // for now we just don't write anything
14512
+ return;
14513
+ }
14514
+ if (urlInfo.type === "directory") {
14506
14515
  // no need to write the directory
14507
14516
  return;
14508
14517
  }
@@ -15585,7 +15594,6 @@ const jsenvPluginDirectoryReferenceEffect = (
15585
15594
  if (pathname[pathname.length - 1] !== "/") {
15586
15595
  return null;
15587
15596
  }
15588
- reference.leadsToADirectory = true;
15589
15597
  reference.expectedType = "directory";
15590
15598
  if (reference.ownerUrlInfo.type === "directory") {
15591
15599
  reference.dirnameHint = reference.ownerUrlInfo.filenameHint;
@@ -16905,11 +16913,7 @@ const jsenvPluginHtmlReferenceAnalysis = ({
16905
16913
  } else {
16906
16914
  position = getHtmlNodeAttributePosition(node, attributeName);
16907
16915
  }
16908
- const {
16909
- line,
16910
- column,
16911
- // originalLine, originalColumn
16912
- } = position;
16916
+ const { line, column, originalLine, originalColumn } = position;
16913
16917
  const debug =
16914
16918
  getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
16915
16919
 
@@ -16942,8 +16946,9 @@ const jsenvPluginHtmlReferenceAnalysis = ({
16942
16946
  subtype,
16943
16947
  expectedType,
16944
16948
  specifier: attributeValue,
16945
- specifierLine: line,
16946
- specifierColumn: column,
16949
+ specifierLine: originalLine === undefined ? line : originalLine,
16950
+ specifierColumn:
16951
+ originalColumn === undefined ? column : originalColumn,
16947
16952
  specifierStart: attributeValueStart,
16948
16953
  specifierEnd: attributeValueEnd,
16949
16954
  isResourceHint,
@@ -18885,11 +18890,6 @@ const createNodeEsmResolver = ({
18885
18890
  return reference.specifier;
18886
18891
  }
18887
18892
  const { ownerUrlInfo } = reference;
18888
- if (reference.specifier === "/") {
18889
- const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
18890
- const url = new URL(mainFilePath, rootDirectoryUrl);
18891
- return url;
18892
- }
18893
18893
  if (reference.specifier[0] === "/") {
18894
18894
  const url = new URL(
18895
18895
  reference.specifier.slice(1),
@@ -19092,11 +19092,6 @@ const jsenvPluginWebResolution = () => {
19092
19092
  appliesDuring: "*",
19093
19093
  resolveReference: (reference) => {
19094
19094
  const { ownerUrlInfo } = reference;
19095
- if (reference.specifier === "/") {
19096
- const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
19097
- const url = new URL(mainFilePath, rootDirectoryUrl);
19098
- return url;
19099
- }
19100
19095
  if (reference.specifier[0] === "/") {
19101
19096
  const url = new URL(
19102
19097
  reference.specifier.slice(1),
@@ -19107,7 +19102,9 @@ const jsenvPluginWebResolution = () => {
19107
19102
  // baseUrl happens second argument to new URL() is different from
19108
19103
  // import.meta.url or document.currentScript.src
19109
19104
  const parentUrl =
19110
- reference.baseUrl || ownerUrlInfo.originalUrl || ownerUrlInfo.url;
19105
+ reference.baseUrl || ownerUrlInfo.context.dev
19106
+ ? ownerUrlInfo.url
19107
+ : ownerUrlInfo.originalUrl || ownerUrlInfo.url;
19111
19108
  const url = new URL(reference.specifier, parentUrl);
19112
19109
  return url;
19113
19110
  },
@@ -19165,7 +19162,6 @@ const jsenvPluginFsRedirection = ({
19165
19162
  return null;
19166
19163
  }
19167
19164
  if (reference.url === "file:///" || reference.url === "file://") {
19168
- reference.leadsToADirectory = true;
19169
19165
  return `ignore:file:///`;
19170
19166
  }
19171
19167
  // ignore all new URL second arg
@@ -19180,27 +19176,19 @@ const jsenvPluginFsRedirection = ({
19180
19176
  // return `ignore:${reference.url}`;
19181
19177
  // }
19182
19178
  const urlObject = new URL(reference.url);
19183
- let stat;
19184
- try {
19185
- stat = readEntryStatSync(urlObject);
19186
- } catch (e) {
19187
- if (e.code === "ENOENT") {
19188
- stat = null;
19189
- } else {
19190
- throw e;
19191
- }
19192
- }
19179
+ let fsStat = readEntryStatSync(urlObject, { nullIfNotFound: true });
19180
+ reference.fsStat = fsStat;
19193
19181
  const { search, hash } = urlObject;
19194
19182
  urlObject.search = "";
19195
19183
  urlObject.hash = "";
19196
- applyStatEffectsOnUrlObject(urlObject, stat);
19184
+ applyFsStatEffectsOnUrlObject(urlObject, fsStat);
19197
19185
  const shouldApplyFilesystemMagicResolution =
19198
19186
  reference.type === "js_import";
19199
19187
  if (shouldApplyFilesystemMagicResolution) {
19200
19188
  const filesystemResolution = applyFileSystemMagicResolution(
19201
19189
  urlObject.href,
19202
19190
  {
19203
- fileStat: stat,
19191
+ fileStat: fsStat,
19204
19192
  magicDirectoryIndex,
19205
19193
  magicExtensions: getExtensionsToTry(
19206
19194
  magicExtensions,
@@ -19209,12 +19197,28 @@ const jsenvPluginFsRedirection = ({
19209
19197
  },
19210
19198
  );
19211
19199
  if (filesystemResolution.stat) {
19212
- stat = filesystemResolution.stat;
19200
+ fsStat = filesystemResolution.stat;
19201
+ reference.fsStat = fsStat;
19213
19202
  urlObject.href = filesystemResolution.url;
19214
- applyStatEffectsOnUrlObject(urlObject, stat);
19203
+ applyFsStatEffectsOnUrlObject(urlObject, fsStat);
19215
19204
  }
19216
19205
  }
19217
- if (!stat) {
19206
+ if (!fsStat) {
19207
+ // for SPA we want to serve the root HTML file only when:
19208
+ // 1. There is no corresponding file on the filesystem
19209
+ // 2. The url pathname does not have an extension
19210
+ // This point assume client is requesting a file when there is an extension
19211
+ // and it assumes all routes will not use extension
19212
+ // 3. The url pathname does not ends with "/"
19213
+ // In that case we assume client explicitely asks to load a directory
19214
+ if (
19215
+ !urlToExtension$1(urlObject) &&
19216
+ !urlToPathname$1(urlObject).endsWith("/")
19217
+ ) {
19218
+ const { mainFilePath, rootDirectoryUrl } =
19219
+ reference.ownerUrlInfo.context;
19220
+ return new URL(mainFilePath, rootDirectoryUrl);
19221
+ }
19218
19222
  return null;
19219
19223
  }
19220
19224
  const urlBeforeSymlinkResolution = urlObject.href;
@@ -19234,15 +19238,19 @@ const jsenvPluginFsRedirection = ({
19234
19238
  };
19235
19239
  };
19236
19240
 
19237
- const applyStatEffectsOnUrlObject = (urlObject, stat) => {
19241
+ const applyFsStatEffectsOnUrlObject = (urlObject, fsStat) => {
19242
+ if (!fsStat) {
19243
+ return;
19244
+ }
19238
19245
  const { pathname } = urlObject;
19239
19246
  const pathnameUsesTrailingSlash = pathname.endsWith("/");
19240
19247
  // force trailing slash on directories
19241
- if (stat && stat.isDirectory() && !pathnameUsesTrailingSlash) {
19242
- urlObject.pathname = `${pathname}/`;
19243
- }
19244
- // otherwise remove trailing slash if any
19245
- if (stat && !stat.isDirectory() && pathnameUsesTrailingSlash) {
19248
+ if (fsStat.isDirectory()) {
19249
+ if (!pathnameUsesTrailingSlash) {
19250
+ urlObject.pathname = `${pathname}/`;
19251
+ }
19252
+ } else if (pathnameUsesTrailingSlash) {
19253
+ // otherwise remove trailing slash if any
19246
19254
  // a warning here? (because it's strange to reference a file with a trailing slash)
19247
19255
  urlObject.pathname = pathname.slice(0, -1);
19248
19256
  }
@@ -19327,45 +19335,16 @@ const jsenvPluginProtocolFile = ({
19327
19335
  if (!urlInfo.url.startsWith("file:")) {
19328
19336
  return null;
19329
19337
  }
19330
- const { rootDirectoryUrl } = urlInfo.context;
19331
- const generateContent = () => {
19332
- const urlObject = new URL(urlInfo.url);
19333
- const { firstReference } = urlInfo;
19334
- if (firstReference.leadsToADirectory) {
19335
- const directoryContentArray = readdirSync(urlObject);
19336
- if (firstReference.type === "filesystem") {
19337
- const content = JSON.stringify(directoryContentArray, null, " ");
19338
- return {
19339
- type: "directory",
19340
- contentType: "application/json",
19341
- content,
19342
- };
19343
- }
19344
- const acceptsHtml = urlInfo.context.request
19345
- ? pickContentType(urlInfo.context.request, ["text/html"])
19346
- : false;
19347
- if (acceptsHtml) {
19348
- firstReference.expectedType = "html";
19349
- const directoryUrl = urlObject.href;
19350
- const directoryContentItems = generateDirectoryContentItems(
19351
- directoryUrl,
19352
- rootDirectoryUrl,
19353
- );
19354
- const html = generateHtmlForDirectory(directoryContentItems);
19355
- return {
19356
- type: "html",
19357
- contentType: "text/html",
19358
- content: html,
19359
- };
19360
- }
19361
- return {
19362
- type: "directory",
19363
- contentType: "application/json",
19364
- content: JSON.stringify(directoryContentArray, null, " "),
19365
- };
19366
- }
19367
- const contentType = CONTENT_TYPE.fromUrlExtension(urlInfo.url);
19368
- const fileBuffer = readFileSync(urlObject);
19338
+ const { firstReference } = urlInfo;
19339
+ let { fsStat } = firstReference;
19340
+ if (!fsStat) {
19341
+ fsStat = readEntryStatSync(urlInfo.url, { nullIfNotFound: true });
19342
+ }
19343
+ const isDirectory = fsStat?.isDirectory();
19344
+ const { rootDirectoryUrl, request } = urlInfo.context;
19345
+ const serveFile = (url) => {
19346
+ const contentType = CONTENT_TYPE.fromUrlExtension(url);
19347
+ const fileBuffer = readFileSync(new URL(url));
19369
19348
  const content = CONTENT_TYPE.isTextual(contentType)
19370
19349
  ? String(fileBuffer)
19371
19350
  : fileBuffer;
@@ -19376,14 +19355,8 @@ const jsenvPluginProtocolFile = ({
19376
19355
  };
19377
19356
  };
19378
19357
 
19379
- const request = urlInfo.context.request;
19380
- if (request && request.headers["sec-fetch-dest"] === "document") {
19381
- try {
19382
- return generateContent();
19383
- } catch (e) {
19384
- if (e.code !== "ENOENT") {
19385
- throw e;
19386
- }
19358
+ if (!fsStat) {
19359
+ if (request && request.headers["sec-fetch-dest"] === "document") {
19387
19360
  const directoryContentItems = generateDirectoryContentItems(
19388
19361
  urlInfo.url,
19389
19362
  rootDirectoryUrl,
@@ -19403,7 +19376,40 @@ const jsenvPluginProtocolFile = ({
19403
19376
  };
19404
19377
  }
19405
19378
  }
19406
- return generateContent();
19379
+ if (isDirectory) {
19380
+ const directoryContentArray = readdirSync(new URL(urlInfo.url));
19381
+ if (firstReference.type === "filesystem") {
19382
+ const content = JSON.stringify(directoryContentArray, null, " ");
19383
+ return {
19384
+ type: "directory",
19385
+ contentType: "application/json",
19386
+ content,
19387
+ };
19388
+ }
19389
+ const acceptsHtml = request
19390
+ ? pickContentType(request, ["text/html"])
19391
+ : false;
19392
+ if (acceptsHtml) {
19393
+ firstReference.expectedType = "html";
19394
+ const directoryUrl = urlInfo.url;
19395
+ const directoryContentItems = generateDirectoryContentItems(
19396
+ directoryUrl,
19397
+ rootDirectoryUrl,
19398
+ );
19399
+ const html = generateHtmlForDirectory(directoryContentItems);
19400
+ return {
19401
+ type: "html",
19402
+ contentType: "text/html",
19403
+ content: html,
19404
+ };
19405
+ }
19406
+ return {
19407
+ type: "directory",
19408
+ contentType: "application/json",
19409
+ content: JSON.stringify(directoryContentArray, null, " "),
19410
+ };
19411
+ }
19412
+ return serveFile(urlInfo.url);
19407
19413
  },
19408
19414
  },
19409
19415
  ];
@@ -21122,6 +21128,19 @@ const getCorePlugins = ({
21122
21128
  directoryListingUrlMocks,
21123
21129
  }),
21124
21130
 
21131
+ {
21132
+ name: "jsenv:resolve_root_as_main",
21133
+ appliesDuring: "*",
21134
+ resolveReference: (reference) => {
21135
+ const { ownerUrlInfo } = reference;
21136
+ if (reference.specifier === "/") {
21137
+ const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
21138
+ const url = new URL(mainFilePath, rootDirectoryUrl);
21139
+ return url;
21140
+ }
21141
+ return null;
21142
+ },
21143
+ },
21125
21144
  ...(nodeEsmResolution
21126
21145
  ? [jsenvPluginNodeEsmResolution(nodeEsmResolution)]
21127
21146
  : []),
@@ -22865,7 +22884,7 @@ const build = async ({
22865
22884
  if (outDirectoryUrl === undefined) {
22866
22885
  if (
22867
22886
  process.env.CAPTURING_SIDE_EFFECTS ||
22868
- (!true)
22887
+ (false)
22869
22888
  ) {
22870
22889
  outDirectoryUrl = new URL("../.jsenv_b/", sourceDirectoryUrl);
22871
22890
  } else {
@@ -23742,7 +23761,7 @@ const startDevServer = async ({
23742
23761
  if (outDirectoryUrl === undefined) {
23743
23762
  if (
23744
23763
  process.env.CAPTURING_SIDE_EFFECTS ||
23745
- (!true)
23764
+ (false)
23746
23765
  ) {
23747
23766
  outDirectoryUrl = new URL("../.jsenv/", sourceDirectoryUrl);
23748
23767
  } else {
@@ -24052,12 +24071,14 @@ const startDevServer = async ({
24052
24071
  parentUrl,
24053
24072
  );
24054
24073
  if (!reference) {
24055
- reference =
24056
- kitchen.graph.rootUrlInfo.dependencies.createResolveAndFinalize({
24057
- trace: { message: parentUrl },
24058
- type: "http_request",
24059
- specifier: request.resource,
24060
- });
24074
+ const rootUrlInfo = kitchen.graph.rootUrlInfo;
24075
+ rootUrlInfo.context.request = request;
24076
+ reference = rootUrlInfo.dependencies.createResolveAndFinalize({
24077
+ trace: { message: parentUrl },
24078
+ type: "http_request",
24079
+ specifier: request.resource,
24080
+ });
24081
+ rootUrlInfo.context.request = null;
24061
24082
  }
24062
24083
  const urlInfo = reference.urlInfo;
24063
24084
  const ifNoneMatch = request.headers["if-none-match"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "39.9.4",
3
+ "version": "39.9.6",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -34,7 +34,7 @@
34
34
  "/src/"
35
35
  ],
36
36
  "volta": {
37
- "node": "22.3.0"
37
+ "node": "22.13.1"
38
38
  },
39
39
  "workspaces": [
40
40
  "./packages/independent/*",
@@ -69,17 +69,17 @@
69
69
  "dependencies": {
70
70
  "@financial-times/polyfill-useragent-normaliser": "1.10.2",
71
71
  "@jsenv/abort": "4.3.0",
72
- "@jsenv/ast": "6.4.1",
73
- "@jsenv/filesystem": "4.13.0",
72
+ "@jsenv/ast": "6.4.3",
73
+ "@jsenv/filesystem": "4.13.1",
74
74
  "@jsenv/humanize": "1.2.8",
75
75
  "@jsenv/importmap": "1.2.1",
76
76
  "@jsenv/integrity": "0.0.2",
77
- "@jsenv/js-module-fallback": "1.3.54",
77
+ "@jsenv/js-module-fallback": "1.3.55",
78
78
  "@jsenv/node-esm-resolution": "1.0.6",
79
- "@jsenv/plugin-bundling": "2.7.21",
80
- "@jsenv/plugin-minification": "1.5.12",
81
- "@jsenv/plugin-supervisor": "1.6.1",
82
- "@jsenv/plugin-transpilation": "1.4.89",
79
+ "@jsenv/plugin-bundling": "2.7.23",
80
+ "@jsenv/plugin-minification": "1.5.13",
81
+ "@jsenv/plugin-supervisor": "1.6.2",
82
+ "@jsenv/plugin-transpilation": "1.4.91",
83
83
  "@jsenv/runtime-compat": "1.3.1",
84
84
  "@jsenv/server": "15.3.3",
85
85
  "@jsenv/sourcemap": "1.2.29",
@@ -91,7 +91,6 @@
91
91
  "devDependencies": {
92
92
  "@babel/plugin-syntax-import-attributes": "7.26.0",
93
93
  "@babel/plugin-syntax-optional-chaining-assign": "7.25.9",
94
- "@eslint/compat": "1.2.4",
95
94
  "@jsenv/assert": "workspace:*",
96
95
  "@jsenv/cli": "workspace:*",
97
96
  "@jsenv/core": "./",
@@ -108,7 +107,7 @@
108
107
  "@playwright/browser-firefox": "1.49.1",
109
108
  "@playwright/browser-webkit": "1.49.1",
110
109
  "babel-plugin-transform-async-to-promises": "0.8.18",
111
- "eslint": "9.17.0",
110
+ "eslint": "9.18.0",
112
111
  "open": "10.1.0",
113
112
  "playwright": "1.49.1",
114
113
  "prettier": "3.4.2",
@@ -422,12 +422,14 @@ export const startDevServer = async ({
422
422
  parentUrl,
423
423
  );
424
424
  if (!reference) {
425
- reference =
426
- kitchen.graph.rootUrlInfo.dependencies.createResolveAndFinalize({
427
- trace: { message: parentUrl },
428
- type: "http_request",
429
- specifier: request.resource,
430
- });
425
+ const rootUrlInfo = kitchen.graph.rootUrlInfo;
426
+ rootUrlInfo.context.request = request;
427
+ reference = rootUrlInfo.dependencies.createResolveAndFinalize({
428
+ trace: { message: parentUrl },
429
+ type: "http_request",
430
+ specifier: request.resource,
431
+ });
432
+ rootUrlInfo.context.request = null;
431
433
  }
432
434
  const urlInfo = reference.urlInfo;
433
435
  const ifNoneMatch = request.headers["if-none-match"];
@@ -291,7 +291,7 @@ const createReference = ({
291
291
  isInline = false,
292
292
  content,
293
293
  contentType,
294
- leadsToADirectory = false,
294
+ fsStat = null,
295
295
  debug = false,
296
296
  original = null,
297
297
  prev = null,
@@ -352,7 +352,7 @@ const createReference = ({
352
352
  version,
353
353
  injected,
354
354
  timing: {},
355
- leadsToADirectory,
355
+ fsStat,
356
356
  debug,
357
357
  // for inline resources the reference contains the content
358
358
  isInline,
@@ -336,7 +336,7 @@ const createUrlInfo = (url, context) => {
336
336
  version: reference.version,
337
337
  content: reference.content,
338
338
  contentType: reference.contentType,
339
- leadsToADirectory: reference.leadsToADirectory,
339
+ fsStat: reference.fsStat,
340
340
  debug: reference.debug,
341
341
  importAttributes: reference.importAttributes,
342
342
  astInfo: reference.astInfo,
@@ -4,7 +4,7 @@ import {
4
4
  generateSourcemapDataUrl,
5
5
  SOURCEMAP,
6
6
  } from "@jsenv/sourcemap";
7
- import { isFileSystemPath, urlToRelativeUrl } from "@jsenv/urls";
7
+ import { isFileSystemPath, urlToPathname, urlToRelativeUrl } from "@jsenv/urls";
8
8
  import { pathToFileURL } from "node:url";
9
9
  import {
10
10
  defineGettersOnPropertiesDerivedFromContent,
@@ -251,11 +251,15 @@ export const createUrlInfoTransformer = ({
251
251
  if (!generatedUrl.startsWith("file:")) {
252
252
  return;
253
253
  }
254
- if (
255
- urlInfo.type === "directory" ||
256
- // happens when type is "html" to list directory content for example
257
- urlInfo.firstReference?.leadsToADirectory
258
- ) {
254
+ if (urlToPathname(generatedUrl).endsWith("/")) {
255
+ // when users explicitely request a directory
256
+ // we can't write the content returned by the server in ".jsenv" at that url
257
+ // because it would try to write a directory
258
+ // ideally we would decide a filename for this
259
+ // for now we just don't write anything
260
+ return;
261
+ }
262
+ if (urlInfo.type === "directory") {
259
263
  // no need to write the directory
260
264
  return;
261
265
  }
@@ -65,7 +65,29 @@ export const initAutoreload = ({ mainFilePath }) => {
65
65
  currentExecution: null,
66
66
  reload: () => {
67
67
  const someEffectIsFullReload = reloader.changes.value.some(
68
- (reloadMessage) => reloadMessage.type === "full",
68
+ (reloadMessage) => {
69
+ if (reloadMessage.type === "full") {
70
+ return true;
71
+ }
72
+ if (reloadMessage.type === "hot") {
73
+ for (const reloadInstruction of reloadMessage.hotInstructions) {
74
+ if (reloadInstruction.type === "html") {
75
+ const acceptedByUrl = new URL(
76
+ reloadInstruction.acceptedBy,
77
+ `${window.location.origin}/`,
78
+ ).href;
79
+ const isCurrentHtmlFile = compareTwoUrlPaths(
80
+ acceptedByUrl,
81
+ window.location.href,
82
+ );
83
+ if (isCurrentHtmlFile) {
84
+ return true;
85
+ }
86
+ }
87
+ }
88
+ }
89
+ return false;
90
+ },
69
91
  );
70
92
  if (someEffectIsFullReload) {
71
93
  dispatchBeforeFullReload();
@@ -149,33 +171,35 @@ This could be due to syntax errors or importing non-existent modules (see errors
149
171
  // - code was not executed (code splitting with dynamic import)
150
172
  // - import.meta.hot.accept() is not called (happens for HTML and CSS)
151
173
  if (type === "prune") {
152
- if (urlHotMeta) {
153
- dispatchBeforePrune();
154
- delete urlHotMetas[urlToFetch];
155
- if (urlHotMeta.disposeCallback) {
156
- console.log(
157
- `[jsenv] cleanup ${boundary} (no longer referenced by ${acceptedBy})`,
158
- );
159
- if (debug) {
160
- console.log(`call dispose callback`);
161
- }
162
- await urlHotMeta.disposeCallback();
174
+ if (!urlHotMeta) {
175
+ // code not executed for this url, no need to prune
176
+ continue;
177
+ }
178
+ dispatchBeforePrune();
179
+ delete urlHotMetas[urlToFetch];
180
+ if (urlHotMeta.disposeCallback) {
181
+ console.log(
182
+ `[jsenv] cleanup ${boundary} (no longer referenced by ${acceptedBy})`,
183
+ );
184
+ if (debug) {
185
+ console.log(`call dispose callback`);
163
186
  }
187
+ await urlHotMeta.disposeCallback();
164
188
  }
165
189
  continue;
166
190
  }
167
- if (acceptedBy === boundary) {
168
- console.log(`[jsenv] hot reloading ${boundary} (${cause})`);
169
- } else {
170
- console.log(
171
- `[jsenv] hot reloading ${acceptedBy} usage in ${boundary} (${cause})`,
172
- );
173
- }
174
191
  if (type === "js_module") {
175
192
  if (!urlHotMeta) {
176
- // code was not executed, no need to re-execute it
193
+ // code not yet executed for this url, no need to re-execute it
177
194
  continue;
178
195
  }
196
+ if (acceptedBy === boundary) {
197
+ console.log(`[jsenv] hot reload ${boundary} (${cause})`);
198
+ } else {
199
+ console.log(
200
+ `[jsenv] hot reload ${acceptedBy} usage in ${boundary} (${cause})`,
201
+ );
202
+ }
179
203
  if (urlHotMeta.disposeCallback) {
180
204
  if (debug) {
181
205
  console.log(`call dispose callback`);
@@ -211,9 +235,21 @@ This could be due to syntax errors or importing non-existent modules (see errors
211
235
  !isRootHtmlFile &&
212
236
  !compareTwoUrlPaths(urlToFetch, window.location.href)
213
237
  ) {
238
+ if (debug) {
239
+ console.log(
240
+ `[jsenv] skip ${acceptedBy} hot reload because we are not in that html page`,
241
+ );
242
+ }
214
243
  // we are not in that HTML page
215
244
  continue;
216
245
  }
246
+ if (acceptedBy === boundary) {
247
+ console.log(`[jsenv] hot reload ${boundary} (${cause})`);
248
+ } else {
249
+ console.log(
250
+ `[jsenv] hot reload ${acceptedBy} usage in ${boundary} (${cause})`,
251
+ );
252
+ }
217
253
  const urlToReload = new URL(acceptedBy, `${window.location.origin}/`)
218
254
  .href;
219
255
  const domNodesUsingUrl = getDOMNodesUsingUrl(urlToReload);
@@ -22,7 +22,6 @@ export const jsenvPluginDirectoryReferenceEffect = (
22
22
  if (pathname[pathname.length - 1] !== "/") {
23
23
  return null;
24
24
  }
25
- reference.leadsToADirectory = true;
26
25
  reference.expectedType = "directory";
27
26
  if (reference.ownerUrlInfo.type === "directory") {
28
27
  reference.dirnameHint = reference.ownerUrlInfo.filenameHint;
@@ -81,6 +81,19 @@ export const getCorePlugins = ({
81
81
  directoryListingUrlMocks,
82
82
  }),
83
83
 
84
+ {
85
+ name: "jsenv:resolve_root_as_main",
86
+ appliesDuring: "*",
87
+ resolveReference: (reference) => {
88
+ const { ownerUrlInfo } = reference;
89
+ if (reference.specifier === "/") {
90
+ const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
91
+ const url = new URL(mainFilePath, rootDirectoryUrl);
92
+ return url;
93
+ }
94
+ return null;
95
+ },
96
+ },
84
97
  ...(nodeEsmResolution
85
98
  ? [jsenvPluginNodeEsmResolution(nodeEsmResolution)]
86
99
  : []),
@@ -3,6 +3,7 @@ import {
3
3
  applyFileSystemMagicResolution,
4
4
  getExtensionsToTry,
5
5
  } from "@jsenv/node-esm-resolution";
6
+ import { urlToExtension, urlToPathname } from "@jsenv/urls";
6
7
  import { realpathSync } from "node:fs";
7
8
  import { pathToFileURL } from "node:url";
8
9
 
@@ -23,7 +24,6 @@ export const jsenvPluginFsRedirection = ({
23
24
  return null;
24
25
  }
25
26
  if (reference.url === "file:///" || reference.url === "file://") {
26
- reference.leadsToADirectory = true;
27
27
  return `ignore:file:///`;
28
28
  }
29
29
  // ignore all new URL second arg
@@ -38,27 +38,19 @@ export const jsenvPluginFsRedirection = ({
38
38
  // return `ignore:${reference.url}`;
39
39
  // }
40
40
  const urlObject = new URL(reference.url);
41
- let stat;
42
- try {
43
- stat = readEntryStatSync(urlObject);
44
- } catch (e) {
45
- if (e.code === "ENOENT") {
46
- stat = null;
47
- } else {
48
- throw e;
49
- }
50
- }
41
+ let fsStat = readEntryStatSync(urlObject, { nullIfNotFound: true });
42
+ reference.fsStat = fsStat;
51
43
  const { search, hash } = urlObject;
52
44
  urlObject.search = "";
53
45
  urlObject.hash = "";
54
- applyStatEffectsOnUrlObject(urlObject, stat);
46
+ applyFsStatEffectsOnUrlObject(urlObject, fsStat);
55
47
  const shouldApplyFilesystemMagicResolution =
56
48
  reference.type === "js_import";
57
49
  if (shouldApplyFilesystemMagicResolution) {
58
50
  const filesystemResolution = applyFileSystemMagicResolution(
59
51
  urlObject.href,
60
52
  {
61
- fileStat: stat,
53
+ fileStat: fsStat,
62
54
  magicDirectoryIndex,
63
55
  magicExtensions: getExtensionsToTry(
64
56
  magicExtensions,
@@ -67,12 +59,28 @@ export const jsenvPluginFsRedirection = ({
67
59
  },
68
60
  );
69
61
  if (filesystemResolution.stat) {
70
- stat = filesystemResolution.stat;
62
+ fsStat = filesystemResolution.stat;
63
+ reference.fsStat = fsStat;
71
64
  urlObject.href = filesystemResolution.url;
72
- applyStatEffectsOnUrlObject(urlObject, stat);
65
+ applyFsStatEffectsOnUrlObject(urlObject, fsStat);
73
66
  }
74
67
  }
75
- if (!stat) {
68
+ if (!fsStat) {
69
+ // for SPA we want to serve the root HTML file only when:
70
+ // 1. There is no corresponding file on the filesystem
71
+ // 2. The url pathname does not have an extension
72
+ // This point assume client is requesting a file when there is an extension
73
+ // and it assumes all routes will not use extension
74
+ // 3. The url pathname does not ends with "/"
75
+ // In that case we assume client explicitely asks to load a directory
76
+ if (
77
+ !urlToExtension(urlObject) &&
78
+ !urlToPathname(urlObject).endsWith("/")
79
+ ) {
80
+ const { mainFilePath, rootDirectoryUrl } =
81
+ reference.ownerUrlInfo.context;
82
+ return new URL(mainFilePath, rootDirectoryUrl);
83
+ }
76
84
  return null;
77
85
  }
78
86
  const urlBeforeSymlinkResolution = urlObject.href;
@@ -92,15 +100,19 @@ export const jsenvPluginFsRedirection = ({
92
100
  };
93
101
  };
94
102
 
95
- const applyStatEffectsOnUrlObject = (urlObject, stat) => {
103
+ const applyFsStatEffectsOnUrlObject = (urlObject, fsStat) => {
104
+ if (!fsStat) {
105
+ return;
106
+ }
96
107
  const { pathname } = urlObject;
97
108
  const pathnameUsesTrailingSlash = pathname.endsWith("/");
98
109
  // force trailing slash on directories
99
- if (stat && stat.isDirectory() && !pathnameUsesTrailingSlash) {
100
- urlObject.pathname = `${pathname}/`;
101
- }
102
- // otherwise remove trailing slash if any
103
- if (stat && !stat.isDirectory() && pathnameUsesTrailingSlash) {
110
+ if (fsStat.isDirectory()) {
111
+ if (!pathnameUsesTrailingSlash) {
112
+ urlObject.pathname = `${pathname}/`;
113
+ }
114
+ } else if (pathnameUsesTrailingSlash) {
115
+ // otherwise remove trailing slash if any
104
116
  // a warning here? (because it's strange to reference a file with a trailing slash)
105
117
  urlObject.pathname = pathname.slice(0, -1);
106
118
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  assertAndNormalizeDirectoryUrl,
3
3
  comparePathnames,
4
+ readEntryStatSync,
4
5
  } from "@jsenv/filesystem";
5
6
  import { pickContentType } from "@jsenv/server";
6
7
  import {
@@ -84,45 +85,16 @@ export const jsenvPluginProtocolFile = ({
84
85
  if (!urlInfo.url.startsWith("file:")) {
85
86
  return null;
86
87
  }
87
- const { rootDirectoryUrl } = urlInfo.context;
88
- const generateContent = () => {
89
- const urlObject = new URL(urlInfo.url);
90
- const { firstReference } = urlInfo;
91
- if (firstReference.leadsToADirectory) {
92
- const directoryContentArray = readdirSync(urlObject);
93
- if (firstReference.type === "filesystem") {
94
- const content = JSON.stringify(directoryContentArray, null, " ");
95
- return {
96
- type: "directory",
97
- contentType: "application/json",
98
- content,
99
- };
100
- }
101
- const acceptsHtml = urlInfo.context.request
102
- ? pickContentType(urlInfo.context.request, ["text/html"])
103
- : false;
104
- if (acceptsHtml) {
105
- firstReference.expectedType = "html";
106
- const directoryUrl = urlObject.href;
107
- const directoryContentItems = generateDirectoryContentItems(
108
- directoryUrl,
109
- rootDirectoryUrl,
110
- );
111
- const html = generateHtmlForDirectory(directoryContentItems);
112
- return {
113
- type: "html",
114
- contentType: "text/html",
115
- content: html,
116
- };
117
- }
118
- return {
119
- type: "directory",
120
- contentType: "application/json",
121
- content: JSON.stringify(directoryContentArray, null, " "),
122
- };
123
- }
124
- const contentType = CONTENT_TYPE.fromUrlExtension(urlInfo.url);
125
- const fileBuffer = readFileSync(urlObject);
88
+ const { firstReference } = urlInfo;
89
+ let { fsStat } = firstReference;
90
+ if (!fsStat) {
91
+ fsStat = readEntryStatSync(urlInfo.url, { nullIfNotFound: true });
92
+ }
93
+ const isDirectory = fsStat?.isDirectory();
94
+ const { rootDirectoryUrl, request } = urlInfo.context;
95
+ const serveFile = (url) => {
96
+ const contentType = CONTENT_TYPE.fromUrlExtension(url);
97
+ const fileBuffer = readFileSync(new URL(url));
126
98
  const content = CONTENT_TYPE.isTextual(contentType)
127
99
  ? String(fileBuffer)
128
100
  : fileBuffer;
@@ -133,14 +105,8 @@ export const jsenvPluginProtocolFile = ({
133
105
  };
134
106
  };
135
107
 
136
- const request = urlInfo.context.request;
137
- if (request && request.headers["sec-fetch-dest"] === "document") {
138
- try {
139
- return generateContent();
140
- } catch (e) {
141
- if (e.code !== "ENOENT") {
142
- throw e;
143
- }
108
+ if (!fsStat) {
109
+ if (request && request.headers["sec-fetch-dest"] === "document") {
144
110
  const directoryContentItems = generateDirectoryContentItems(
145
111
  urlInfo.url,
146
112
  rootDirectoryUrl,
@@ -160,7 +126,40 @@ export const jsenvPluginProtocolFile = ({
160
126
  };
161
127
  }
162
128
  }
163
- return generateContent();
129
+ if (isDirectory) {
130
+ const directoryContentArray = readdirSync(new URL(urlInfo.url));
131
+ if (firstReference.type === "filesystem") {
132
+ const content = JSON.stringify(directoryContentArray, null, " ");
133
+ return {
134
+ type: "directory",
135
+ contentType: "application/json",
136
+ content,
137
+ };
138
+ }
139
+ const acceptsHtml = request
140
+ ? pickContentType(request, ["text/html"])
141
+ : false;
142
+ if (acceptsHtml) {
143
+ firstReference.expectedType = "html";
144
+ const directoryUrl = urlInfo.url;
145
+ const directoryContentItems = generateDirectoryContentItems(
146
+ directoryUrl,
147
+ rootDirectoryUrl,
148
+ );
149
+ const html = generateHtmlForDirectory(directoryContentItems);
150
+ return {
151
+ type: "html",
152
+ contentType: "text/html",
153
+ content: html,
154
+ };
155
+ }
156
+ return {
157
+ type: "directory",
158
+ contentType: "application/json",
159
+ content: JSON.stringify(directoryContentArray, null, " "),
160
+ };
161
+ }
162
+ return serveFile(urlInfo.url);
164
163
  },
165
164
  },
166
165
  ];
@@ -156,11 +156,7 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
156
156
  } else {
157
157
  position = getHtmlNodeAttributePosition(node, attributeName);
158
158
  }
159
- const {
160
- line,
161
- column,
162
- // originalLine, originalColumn
163
- } = position;
159
+ const { line, column, originalLine, originalColumn } = position;
164
160
  const debug =
165
161
  getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
166
162
 
@@ -193,8 +189,9 @@ export const jsenvPluginHtmlReferenceAnalysis = ({
193
189
  subtype,
194
190
  expectedType,
195
191
  specifier: attributeValue,
196
- specifierLine: line,
197
- specifierColumn: column,
192
+ specifierLine: originalLine === undefined ? line : originalLine,
193
+ specifierColumn:
194
+ originalColumn === undefined ? column : originalColumn,
198
195
  specifierStart: attributeValueStart,
199
196
  specifierEnd: attributeValueEnd,
200
197
  isResourceHint,
@@ -33,11 +33,6 @@ export const createNodeEsmResolver = ({
33
33
  return reference.specifier;
34
34
  }
35
35
  const { ownerUrlInfo } = reference;
36
- if (reference.specifier === "/") {
37
- const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
38
- const url = new URL(mainFilePath, rootDirectoryUrl);
39
- return url;
40
- }
41
36
  if (reference.specifier[0] === "/") {
42
37
  const url = new URL(
43
38
  reference.specifier.slice(1),
@@ -4,11 +4,6 @@ export const jsenvPluginWebResolution = () => {
4
4
  appliesDuring: "*",
5
5
  resolveReference: (reference) => {
6
6
  const { ownerUrlInfo } = reference;
7
- if (reference.specifier === "/") {
8
- const { mainFilePath, rootDirectoryUrl } = ownerUrlInfo.context;
9
- const url = new URL(mainFilePath, rootDirectoryUrl);
10
- return url;
11
- }
12
7
  if (reference.specifier[0] === "/") {
13
8
  const url = new URL(
14
9
  reference.specifier.slice(1),
@@ -19,7 +14,9 @@ export const jsenvPluginWebResolution = () => {
19
14
  // baseUrl happens second argument to new URL() is different from
20
15
  // import.meta.url or document.currentScript.src
21
16
  const parentUrl =
22
- reference.baseUrl || ownerUrlInfo.originalUrl || ownerUrlInfo.url;
17
+ reference.baseUrl || ownerUrlInfo.context.dev
18
+ ? ownerUrlInfo.url
19
+ : ownerUrlInfo.originalUrl || ownerUrlInfo.url;
23
20
  const url = new URL(reference.specifier, parentUrl);
24
21
  return url;
25
22
  },