@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.
- package/dist/js/autoreload.js +49 -18
- package/dist/js/new_stylesheet.js +1 -1
- package/dist/js/regenerator_runtime.js +1 -1
- package/dist/js/ws.js +7 -7
- package/dist/jsenv_core.js +133 -112
- package/package.json +10 -11
- package/src/dev/start_dev_server.js +8 -6
- package/src/kitchen/url_graph/references.js +2 -2
- package/src/kitchen/url_graph/url_graph.js +1 -1
- package/src/kitchen/url_graph/url_info_transformations.js +10 -6
- package/src/plugins/autoreload/client/autoreload.js +56 -20
- package/src/plugins/directory_reference_effect/jsenv_plugin_directory_reference_effect.js +0 -1
- package/src/plugins/plugins.js +13 -0
- package/src/plugins/protocol_file/jsenv_plugin_fs_redirection.js +34 -22
- package/src/plugins/protocol_file/jsenv_plugin_protocol_file.js +47 -48
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +4 -7
- package/src/plugins/resolution_node_esm/node_esm_resolver.js +0 -5
- package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +3 -6
package/dist/js/autoreload.js
CHANGED
|
@@ -52,7 +52,29 @@ const initAutoreload = ({ mainFilePath }) => {
|
|
|
52
52
|
currentExecution: null,
|
|
53
53
|
reload: () => {
|
|
54
54
|
const someEffectIsFullReload = reloader.changes.value.some(
|
|
55
|
-
(reloadMessage) =>
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
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);
|
|
@@ -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 ===
|
|
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 < -
|
|
1742
|
-
byteOffset = -
|
|
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, -
|
|
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, -
|
|
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, -
|
|
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, -
|
|
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, -
|
|
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);
|
package/dist/jsenv_core.js
CHANGED
|
@@ -1968,7 +1968,7 @@ const comparePathnames = (leftPathame, rightPathname) => {
|
|
|
1968
1968
|
|
|
1969
1969
|
// longer comes first
|
|
1970
1970
|
if (!leftPartExists) {
|
|
1971
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14503
|
-
//
|
|
14504
|
-
|
|
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:
|
|
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.
|
|
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
|
|
19184
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
19200
|
+
fsStat = filesystemResolution.stat;
|
|
19201
|
+
reference.fsStat = fsStat;
|
|
19213
19202
|
urlObject.href = filesystemResolution.url;
|
|
19214
|
-
|
|
19203
|
+
applyFsStatEffectsOnUrlObject(urlObject, fsStat);
|
|
19215
19204
|
}
|
|
19216
19205
|
}
|
|
19217
|
-
if (!
|
|
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
|
|
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 (
|
|
19242
|
-
|
|
19243
|
-
|
|
19244
|
-
|
|
19245
|
-
|
|
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 {
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
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
|
-
|
|
19380
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
-
(
|
|
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
|
-
|
|
24056
|
-
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
73
|
-
"@jsenv/filesystem": "4.13.
|
|
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.
|
|
77
|
+
"@jsenv/js-module-fallback": "1.3.55",
|
|
78
78
|
"@jsenv/node-esm-resolution": "1.0.6",
|
|
79
|
-
"@jsenv/plugin-bundling": "2.7.
|
|
80
|
-
"@jsenv/plugin-minification": "1.5.
|
|
81
|
-
"@jsenv/plugin-supervisor": "1.6.
|
|
82
|
-
"@jsenv/plugin-transpilation": "1.4.
|
|
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.
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
256
|
-
//
|
|
257
|
-
|
|
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) =>
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
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;
|
package/src/plugins/plugins.js
CHANGED
|
@@ -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
|
|
42
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
62
|
+
fsStat = filesystemResolution.stat;
|
|
63
|
+
reference.fsStat = fsStat;
|
|
71
64
|
urlObject.href = filesystemResolution.url;
|
|
72
|
-
|
|
65
|
+
applyFsStatEffectsOnUrlObject(urlObject, fsStat);
|
|
73
66
|
}
|
|
74
67
|
}
|
|
75
|
-
if (!
|
|
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
|
|
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 (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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 {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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
|
},
|