@jsenv/core 41.3.1 → 41.4.1
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/build/build.js +248 -0
- package/dist/js/dependency_status.js +61 -0
- package/dist/start_dev_server/jsenv_core_packages.js +262 -10
- package/dist/start_dev_server/start_dev_server.js +457 -23
- package/package.json +6 -6
- package/src/dev/dev_server_plugins/dev_server_plugin_serve_source_files.js +42 -21
- package/src/dev/start_dev_server.js +28 -0
- package/src/helpers/watch_dependencies.js +110 -0
- package/src/kitchen/errors.js +43 -0
- package/src/kitchen/package_dependencies.js +160 -0
- package/src/plugins/autoreload/jsenv_plugin_autoreload.js +2 -0
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +10 -0
- package/src/plugins/dependency_status/client/dependency_status.js +59 -0
- package/src/plugins/dependency_status/jsenv_plugin_dependency_status.js +64 -0
- package/src/plugins/plugins.js +5 -0
package/dist/build/build.js
CHANGED
|
@@ -132,6 +132,136 @@ const watchSourceFiles = (
|
|
|
132
132
|
|
|
133
133
|
const jsenvCoreDirectoryUrl = new URL("../", import.meta.url);
|
|
134
134
|
|
|
135
|
+
/*
|
|
136
|
+
* Compares what a package.json declares with what is actually inside
|
|
137
|
+
* node_modules, so the dev server can tell a dependency apart when it is
|
|
138
|
+
* missing (never installed) or outdated (installed at an other version).
|
|
139
|
+
*
|
|
140
|
+
* Only the dependencies declared by a package are looked at, never the
|
|
141
|
+
* transitive ones: a declared dependency is what pulls the rest, so it is
|
|
142
|
+
* enough to know whether an install is needed or over.
|
|
143
|
+
*
|
|
144
|
+
* Only exact declared versions ("1.2.3") are compared: a range ("^1.2.3"), a
|
|
145
|
+
* file/workspace protocol or a tag cannot be checked without resolving what npm
|
|
146
|
+
* would pick, which is way beyond what is needed here.
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
const DEPENDENCY_FIELDS = [
|
|
151
|
+
"dependencies",
|
|
152
|
+
"devDependencies",
|
|
153
|
+
"optionalDependencies",
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const packageNameFromSpecifier = (specifier) => {
|
|
157
|
+
const parts = specifier.split("/");
|
|
158
|
+
if (specifier[0] === "@") {
|
|
159
|
+
return parts.slice(0, 2).join("/");
|
|
160
|
+
}
|
|
161
|
+
return parts[0];
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/*
|
|
165
|
+
* declaringDirectoryUrl is the package directory the importer belongs to, which
|
|
166
|
+
* is not always the project one: a file inside node_modules resolves its bare
|
|
167
|
+
* specifiers against the dependencies of the package containing it.
|
|
168
|
+
*/
|
|
169
|
+
const readDependencyStatus = (
|
|
170
|
+
packageDirectory,
|
|
171
|
+
packageName,
|
|
172
|
+
declaringDirectoryUrl = packageDirectory.url,
|
|
173
|
+
) => {
|
|
174
|
+
const packageJSON = readPackageJSON(packageDirectory, declaringDirectoryUrl);
|
|
175
|
+
if (!packageJSON) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const declaredVersion = readDeclaredVersion(packageJSON, packageName);
|
|
179
|
+
if (!declaredVersion) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return createStatus(packageDirectory, {
|
|
183
|
+
packageName,
|
|
184
|
+
declaredVersion,
|
|
185
|
+
declaringDirectoryUrl,
|
|
186
|
+
declaredBy: packageJSON.name,
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const createStatus = (
|
|
191
|
+
packageDirectory,
|
|
192
|
+
{ packageName, declaredVersion, declaringDirectoryUrl, declaredBy },
|
|
193
|
+
) => {
|
|
194
|
+
const status = {
|
|
195
|
+
packageName,
|
|
196
|
+
declaredVersion,
|
|
197
|
+
declaredBy,
|
|
198
|
+
installedVersion: null,
|
|
199
|
+
state: "missing",
|
|
200
|
+
};
|
|
201
|
+
const installedDirectoryUrl = findInstalledDirectoryUrl(
|
|
202
|
+
declaringDirectoryUrl,
|
|
203
|
+
packageName,
|
|
204
|
+
);
|
|
205
|
+
if (!installedDirectoryUrl) {
|
|
206
|
+
return status;
|
|
207
|
+
}
|
|
208
|
+
const installedPackageJSON = readPackageJSON(
|
|
209
|
+
packageDirectory,
|
|
210
|
+
installedDirectoryUrl,
|
|
211
|
+
);
|
|
212
|
+
status.installedVersion = installedPackageJSON
|
|
213
|
+
? installedPackageJSON.version
|
|
214
|
+
: null;
|
|
215
|
+
status.state =
|
|
216
|
+
isExactVersion(declaredVersion) &&
|
|
217
|
+
status.installedVersion !== declaredVersion
|
|
218
|
+
? "outdated"
|
|
219
|
+
: "installed";
|
|
220
|
+
return status;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const findInstalledDirectoryUrl = (declaringDirectoryUrl, packageName) => {
|
|
224
|
+
let directoryUrl = declaringDirectoryUrl;
|
|
225
|
+
while (directoryUrl) {
|
|
226
|
+
const candidateUrl = `${directoryUrl}node_modules/${packageName}/`;
|
|
227
|
+
if (existsSync(new URL(`${candidateUrl}package.json`))) {
|
|
228
|
+
return candidateUrl;
|
|
229
|
+
}
|
|
230
|
+
const parentUrl = new URL("../", directoryUrl).href;
|
|
231
|
+
if (parentUrl === directoryUrl) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
directoryUrl = parentUrl;
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const readDeclaredVersion = (packageJSON, packageName) => {
|
|
240
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
241
|
+
const dependencies = packageJSON[field];
|
|
242
|
+
if (dependencies && dependencies[packageName]) {
|
|
243
|
+
return dependencies[packageName];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// an install in progress can be caught halfway, with a package.json not written yet
|
|
250
|
+
const readPackageJSON = (packageDirectory, directoryUrl) => {
|
|
251
|
+
if (!directoryUrl) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
return packageDirectory.read(directoryUrl);
|
|
256
|
+
} catch {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const isExactVersion = (declaredVersion) => {
|
|
262
|
+
return /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/.test(declaredVersion);
|
|
263
|
+
};
|
|
264
|
+
|
|
135
265
|
const createResolveUrlError = ({
|
|
136
266
|
jsenvPluginsController,
|
|
137
267
|
reference,
|
|
@@ -171,6 +301,18 @@ ${reason}`,
|
|
|
171
301
|
});
|
|
172
302
|
}
|
|
173
303
|
if (error.code === "MODULE_NOT_FOUND") {
|
|
304
|
+
const notInstalledStatus = readNotInstalledStatus(reference);
|
|
305
|
+
if (notInstalledStatus) {
|
|
306
|
+
const { packageName, declaredVersion, declaredBy, isProjectDependency } =
|
|
307
|
+
notInstalledStatus;
|
|
308
|
+
return createFailedToResolveUrlError({
|
|
309
|
+
"reason": isProjectDependency
|
|
310
|
+
? `"${packageName}" is declared in package.json but not installed`
|
|
311
|
+
: `"${packageName}" is declared by "${declaredBy}" but not installed`,
|
|
312
|
+
"declared version": declaredVersion,
|
|
313
|
+
"suggestion": `run npm install, the page will reload once "${packageName}" is installed`,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
174
316
|
const bareSpecifierError = createFailedToResolveUrlError({
|
|
175
317
|
reason: `"${reference.specifier}" is a bare specifier but cannot be remapped to a package`,
|
|
176
318
|
});
|
|
@@ -425,6 +567,32 @@ const getErrorTrace = (error, reference) => {
|
|
|
425
567
|
};
|
|
426
568
|
};
|
|
427
569
|
|
|
570
|
+
// a bare specifier is resolved against the dependencies of the package
|
|
571
|
+
// containing the file that imports it, which is the project one for a source
|
|
572
|
+
// file but an other one for a file inside node_modules
|
|
573
|
+
const readNotInstalledStatus = (reference) => {
|
|
574
|
+
const { ownerUrlInfo } = reference;
|
|
575
|
+
const { packageDirectory } = ownerUrlInfo.context;
|
|
576
|
+
if (!packageDirectory) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
const declaringDirectoryUrl =
|
|
580
|
+
packageDirectory.find(ownerUrlInfo.url) || packageDirectory.url;
|
|
581
|
+
const packageName = packageNameFromSpecifier(reference.specifier);
|
|
582
|
+
const status = readDependencyStatus(
|
|
583
|
+
packageDirectory,
|
|
584
|
+
packageName,
|
|
585
|
+
declaringDirectoryUrl,
|
|
586
|
+
);
|
|
587
|
+
if (!status || status.state !== "missing") {
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
...status,
|
|
592
|
+
isProjectDependency: declaringDirectoryUrl === packageDirectory.url,
|
|
593
|
+
};
|
|
594
|
+
};
|
|
595
|
+
|
|
428
596
|
const detailsFromFirstReference = (reference) => {
|
|
429
597
|
const referenceInProject = getFirstReferenceInProject(reference);
|
|
430
598
|
if (
|
|
@@ -8288,6 +8456,7 @@ const jsenvPluginAutoreloadClient = () => {
|
|
|
8288
8456
|
const jsenvPluginAutoreloadServer = ({
|
|
8289
8457
|
clientFileChangeEventEmitter,
|
|
8290
8458
|
clientFileDereferencedEventEmitter,
|
|
8459
|
+
reloadRequestEventEmitter,
|
|
8291
8460
|
}) => {
|
|
8292
8461
|
return {
|
|
8293
8462
|
name: "jsenv:autoreload_server",
|
|
@@ -8616,6 +8785,15 @@ const jsenvPluginAutoreloadServer = ({
|
|
|
8616
8785
|
});
|
|
8617
8786
|
},
|
|
8618
8787
|
);
|
|
8788
|
+
// something outside the url graph wants the page back from scratch,
|
|
8789
|
+
// typically a dependency that just got installed into node_modules
|
|
8790
|
+
reloadRequestEventEmitter.on(({ cause, reason }) => {
|
|
8791
|
+
serverEventInfo.sendServerEvent({
|
|
8792
|
+
cause,
|
|
8793
|
+
type: "full",
|
|
8794
|
+
typeReason: reason,
|
|
8795
|
+
});
|
|
8796
|
+
});
|
|
8619
8797
|
},
|
|
8620
8798
|
},
|
|
8621
8799
|
serverRoutes: [
|
|
@@ -8726,6 +8904,7 @@ const jsenvPluginHotSearchParam = () => {
|
|
|
8726
8904
|
const jsenvPluginAutoreload = ({
|
|
8727
8905
|
clientFileChangeEventEmitter,
|
|
8728
8906
|
clientFileDereferencedEventEmitter,
|
|
8907
|
+
reloadRequestEventEmitter,
|
|
8729
8908
|
}) => {
|
|
8730
8909
|
return [
|
|
8731
8910
|
jsenvPluginHotSearchParam(),
|
|
@@ -8733,10 +8912,75 @@ const jsenvPluginAutoreload = ({
|
|
|
8733
8912
|
jsenvPluginAutoreloadServer({
|
|
8734
8913
|
clientFileChangeEventEmitter,
|
|
8735
8914
|
clientFileDereferencedEventEmitter,
|
|
8915
|
+
reloadRequestEventEmitter,
|
|
8736
8916
|
}),
|
|
8737
8917
|
];
|
|
8738
8918
|
};
|
|
8739
8919
|
|
|
8920
|
+
/*
|
|
8921
|
+
* Tells the browser about the dependencies declared in package.json that
|
|
8922
|
+
* node_modules does not match, so the page can say it is running with something
|
|
8923
|
+
* else than what the project asks for.
|
|
8924
|
+
*
|
|
8925
|
+
* The state is sent on page load (it is known before any client connects) and
|
|
8926
|
+
* again whenever it changes, so a page opened during an install is updated
|
|
8927
|
+
* without being reloaded.
|
|
8928
|
+
*/
|
|
8929
|
+
|
|
8930
|
+
|
|
8931
|
+
const clientFileUrl = import.meta.resolve("../js/dependency_status.js");
|
|
8932
|
+
|
|
8933
|
+
const jsenvPluginDependencyStatus = ({
|
|
8934
|
+
dependencyProblemEventEmitter,
|
|
8935
|
+
getDependencyProblems,
|
|
8936
|
+
}) => {
|
|
8937
|
+
return {
|
|
8938
|
+
name: "jsenv:dependency_status",
|
|
8939
|
+
appliesDuring: "dev",
|
|
8940
|
+
serverEvents: {
|
|
8941
|
+
dependency_status: (serverEventInfo) => {
|
|
8942
|
+
dependencyProblemEventEmitter.on((problems) => {
|
|
8943
|
+
// the state is baked into the html by the injection below, so a page
|
|
8944
|
+
// served from the graph as it is would come back with the previous
|
|
8945
|
+
// state, which is exactly what a reload triggered by an install does
|
|
8946
|
+
for (const urlInfo of serverEventInfo.kitchen.graph.urlInfoMap.values()) {
|
|
8947
|
+
if (urlInfo.type === "html" && urlInfo.content !== undefined) {
|
|
8948
|
+
urlInfo.onModified();
|
|
8949
|
+
}
|
|
8950
|
+
}
|
|
8951
|
+
serverEventInfo.sendServerEvent({ problems });
|
|
8952
|
+
});
|
|
8953
|
+
},
|
|
8954
|
+
},
|
|
8955
|
+
transformUrlContent: {
|
|
8956
|
+
html: (htmlUrlInfo) => {
|
|
8957
|
+
const htmlAst = parseHtml({
|
|
8958
|
+
html: htmlUrlInfo.content,
|
|
8959
|
+
url: htmlUrlInfo.url,
|
|
8960
|
+
});
|
|
8961
|
+
const clientReference = htmlUrlInfo.dependencies.inject({
|
|
8962
|
+
type: "script",
|
|
8963
|
+
subtype: "js_module",
|
|
8964
|
+
expectedType: "js_module",
|
|
8965
|
+
specifier: clientFileUrl,
|
|
8966
|
+
});
|
|
8967
|
+
injectJsenvScript(htmlAst, {
|
|
8968
|
+
type: "module",
|
|
8969
|
+
src: clientReference.generatedSpecifier,
|
|
8970
|
+
initCall: {
|
|
8971
|
+
callee: "initDependencyStatus",
|
|
8972
|
+
params: { problems: getDependencyProblems() },
|
|
8973
|
+
},
|
|
8974
|
+
pluginName: "jsenv:dependency_status",
|
|
8975
|
+
});
|
|
8976
|
+
return {
|
|
8977
|
+
content: stringifyHtmlAst(htmlAst),
|
|
8978
|
+
};
|
|
8979
|
+
},
|
|
8980
|
+
},
|
|
8981
|
+
};
|
|
8982
|
+
};
|
|
8983
|
+
|
|
8740
8984
|
const jsenvPluginCacheControl = ({
|
|
8741
8985
|
versionedUrls = true,
|
|
8742
8986
|
maxAge = SECONDS_IN_30_DAYS,
|
|
@@ -9273,6 +9517,7 @@ const getCorePlugins = ({
|
|
|
9273
9517
|
|
|
9274
9518
|
clientAutoreload,
|
|
9275
9519
|
clientAutoreloadOnServerRestart,
|
|
9520
|
+
dependencyStatus,
|
|
9276
9521
|
cacheControl,
|
|
9277
9522
|
scenarioPlaceholders = true,
|
|
9278
9523
|
ribbon = true,
|
|
@@ -9371,6 +9616,9 @@ const getCorePlugins = ({
|
|
|
9371
9616
|
...(clientAutoreload && clientAutoreload.enabled
|
|
9372
9617
|
? [jsenvPluginAutoreload(clientAutoreload)]
|
|
9373
9618
|
: []),
|
|
9619
|
+
...(dependencyStatus
|
|
9620
|
+
? [jsenvPluginDependencyStatus(dependencyStatus)]
|
|
9621
|
+
: []),
|
|
9374
9622
|
...(cacheControl ? [jsenvPluginCacheControl(cacheControl)] : []),
|
|
9375
9623
|
...(ribbon ? [jsenvPluginRibbon({ rootDirectoryUrl, ...ribbon })] : []),
|
|
9376
9624
|
...(dropToOpen ? [jsenvPluginDropToOpen()] : []),
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Displays the "some dependencies are outdated" warning overlay.
|
|
3
|
+
*
|
|
4
|
+
* A missing dependency is not handled here: it makes the import fail, so the
|
|
5
|
+
* error overlay already says it, with more precision (which import, where).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
let removeOverlay = () => {};
|
|
9
|
+
|
|
10
|
+
const initDependencyStatus = ({ problems }) => {
|
|
11
|
+
render(problems);
|
|
12
|
+
// without the server events channel the page only knows the state it was
|
|
13
|
+
// served with, which is still better than nothing
|
|
14
|
+
if (window.__server_events__) {
|
|
15
|
+
window.__server_events__.listenEvents({
|
|
16
|
+
dependency_status: (event) => {
|
|
17
|
+
render(event.data.problems);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const render = (problems) => {
|
|
24
|
+
const outdatedList = problems.filter(({ state }) => state === "outdated");
|
|
25
|
+
removeOverlay();
|
|
26
|
+
removeOverlay = () => {};
|
|
27
|
+
if (outdatedList.length === 0) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const supervisor = window.__supervisor__;
|
|
31
|
+
if (!supervisor || !supervisor.reportWarning) {
|
|
32
|
+
console.warn(summarize(outdatedList));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
removeOverlay = supervisor.reportWarning({
|
|
36
|
+
title:
|
|
37
|
+
outdatedList.length === 1
|
|
38
|
+
? "A dependency is outdated"
|
|
39
|
+
: `${outdatedList.length} dependencies are outdated`,
|
|
40
|
+
text: outdatedList.map(describe).join("\n\n"),
|
|
41
|
+
details: [
|
|
42
|
+
"The page is running with what is installed in node_modules, which is not what package.json asks for. It may work, but it is not the code the project expects.",
|
|
43
|
+
"Run npm install to fix it. If an install is already running, there is nothing to do but wait.",
|
|
44
|
+
"This page reloads on its own as soon as node_modules matches package.json.",
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const describe = ({ packageName, declaredVersion, installedVersion }) => {
|
|
50
|
+
return `${packageName}
|
|
51
|
+
package.json declares ${declaredVersion}
|
|
52
|
+
node_modules holds ${installedVersion || "an unknown version"}`;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const summarize = (outdatedList) => {
|
|
56
|
+
return `[jsenv] outdated dependencies, run npm install: ${outdatedList
|
|
57
|
+
.map(({ packageName }) => packageName)
|
|
58
|
+
.join(", ")}`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { initDependencyStatus };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSupportsColor, isUnicodeSupported, eastAsianWidth, clearTerminal, eraseLines } from "./jsenv_core_node_modules.js";
|
|
2
2
|
import { stripVTControlCharacters } from "node:util";
|
|
3
3
|
import { readFileSync, existsSync, chmodSync, statSync, lstatSync, readdirSync, openSync, closeSync, unlinkSync, rmdirSync, mkdirSync, writeFileSync as writeFileSync$1, watch, realpathSync } from "node:fs";
|
|
4
|
-
import { extname } from "node:path";
|
|
4
|
+
import { extname, basename, dirname } from "node:path";
|
|
5
5
|
import crypto, { createHash } from "node:crypto";
|
|
6
6
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
7
7
|
|
|
@@ -3382,6 +3382,21 @@ const writeFileSync = (destination, content = "", { force } = {}) => {
|
|
|
3382
3382
|
}
|
|
3383
3383
|
};
|
|
3384
3384
|
|
|
3385
|
+
const callOnceIdle = (callback, idleMs) => {
|
|
3386
|
+
let timeoutId;
|
|
3387
|
+
return (...args) => {
|
|
3388
|
+
if (timeoutId) {
|
|
3389
|
+
clearTimeout(timeoutId);
|
|
3390
|
+
}
|
|
3391
|
+
timeoutId = setTimeout(() => {
|
|
3392
|
+
callback(...args);
|
|
3393
|
+
}, idleMs);
|
|
3394
|
+
if (timeoutId.unref) {
|
|
3395
|
+
timeoutId.unref();
|
|
3396
|
+
}
|
|
3397
|
+
};
|
|
3398
|
+
};
|
|
3399
|
+
|
|
3385
3400
|
const callOnceIdlePerFile = (callback, idleMs) => {
|
|
3386
3401
|
const timeoutIdMap = new Map();
|
|
3387
3402
|
return (fileEvent) => {
|
|
@@ -3433,6 +3448,25 @@ const createWatcher = (sourcePath, options) => {
|
|
|
3433
3448
|
return watcher;
|
|
3434
3449
|
};
|
|
3435
3450
|
|
|
3451
|
+
const guardTooFastSecondCall = (
|
|
3452
|
+
callback,
|
|
3453
|
+
cooldownBetweenFileEvents = 40,
|
|
3454
|
+
) => {
|
|
3455
|
+
let previousCallMs;
|
|
3456
|
+
return (...args) => {
|
|
3457
|
+
const nowMs = Date.now();
|
|
3458
|
+
if (previousCallMs) {
|
|
3459
|
+
const msEllapsed = nowMs - previousCallMs;
|
|
3460
|
+
if (msEllapsed < cooldownBetweenFileEvents) {
|
|
3461
|
+
previousCallMs = null;
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
previousCallMs = nowMs;
|
|
3466
|
+
callback(...args);
|
|
3467
|
+
};
|
|
3468
|
+
};
|
|
3469
|
+
|
|
3436
3470
|
const guardTooFastSecondCallPerFile = (
|
|
3437
3471
|
callback,
|
|
3438
3472
|
cooldownBetweenFileEvents = 40,
|
|
@@ -3476,8 +3510,8 @@ callback: ${callback}`);
|
|
|
3476
3510
|
return { registerCleanupCallback, cleanup };
|
|
3477
3511
|
};
|
|
3478
3512
|
|
|
3479
|
-
const isLinux = process.platform === "linux";
|
|
3480
|
-
const fsWatchSupportsRecursive = !isLinux;
|
|
3513
|
+
const isLinux$1 = process.platform === "linux";
|
|
3514
|
+
const fsWatchSupportsRecursive = !isLinux$1;
|
|
3481
3515
|
|
|
3482
3516
|
const registerDirectoryLifecycle = (
|
|
3483
3517
|
source,
|
|
@@ -3502,15 +3536,15 @@ const registerDirectoryLifecycle = (
|
|
|
3502
3536
|
},
|
|
3503
3537
|
) => {
|
|
3504
3538
|
const sourceUrl = assertAndNormalizeDirectoryUrl(source);
|
|
3505
|
-
if (!undefinedOrFunction(added)) {
|
|
3539
|
+
if (!undefinedOrFunction$1(added)) {
|
|
3506
3540
|
throw new TypeError(`added must be a function or undefined, got ${added}`);
|
|
3507
3541
|
}
|
|
3508
|
-
if (!undefinedOrFunction(updated)) {
|
|
3542
|
+
if (!undefinedOrFunction$1(updated)) {
|
|
3509
3543
|
throw new TypeError(
|
|
3510
3544
|
`updated must be a function or undefined, got ${updated}`,
|
|
3511
3545
|
);
|
|
3512
3546
|
}
|
|
3513
|
-
if (!undefinedOrFunction(removed)) {
|
|
3547
|
+
if (!undefinedOrFunction$1(removed)) {
|
|
3514
3548
|
throw new TypeError(
|
|
3515
3549
|
`removed must be a function or undefined, got ${removed}`,
|
|
3516
3550
|
);
|
|
@@ -3805,7 +3839,7 @@ const registerDirectoryLifecycle = (
|
|
|
3805
3839
|
}
|
|
3806
3840
|
};
|
|
3807
3841
|
const handleEntryUpdated = (entryInfo) => {
|
|
3808
|
-
if (updated && entryInfo.patternValue && shouldCallUpdated(entryInfo)) {
|
|
3842
|
+
if (updated && entryInfo.patternValue && shouldCallUpdated$1(entryInfo)) {
|
|
3809
3843
|
infoMap.set(entryInfo.relativeUrl, entryInfo);
|
|
3810
3844
|
updated({
|
|
3811
3845
|
relativeUrl: entryInfo.relativeUrl,
|
|
@@ -3848,7 +3882,7 @@ ${relativeUrls.join("\n")}`,
|
|
|
3848
3882
|
return tracker.cleanup;
|
|
3849
3883
|
};
|
|
3850
3884
|
|
|
3851
|
-
const shouldCallUpdated = (entryInfo) => {
|
|
3885
|
+
const shouldCallUpdated$1 = (entryInfo) => {
|
|
3852
3886
|
const { stat, previousInfo } = entryInfo;
|
|
3853
3887
|
if (!stat.atimeMs) {
|
|
3854
3888
|
return true;
|
|
@@ -3862,7 +3896,7 @@ const shouldCallUpdated = (entryInfo) => {
|
|
|
3862
3896
|
return true;
|
|
3863
3897
|
};
|
|
3864
3898
|
|
|
3865
|
-
const undefinedOrFunction = (value) => {
|
|
3899
|
+
const undefinedOrFunction$1 = (value) => {
|
|
3866
3900
|
return typeof value === "undefined" || typeof value === "function";
|
|
3867
3901
|
};
|
|
3868
3902
|
|
|
@@ -3891,6 +3925,224 @@ const fileSystemPathToDirectoryRelativeUrlAndFilename = (path) => {
|
|
|
3891
3925
|
};
|
|
3892
3926
|
};
|
|
3893
3927
|
|
|
3928
|
+
const isMacos = process.platform === "darwin";
|
|
3929
|
+
const isLinux = process.platform === "linux";
|
|
3930
|
+
const isFreeBSD = process.platform === "freebsd";
|
|
3931
|
+
|
|
3932
|
+
const registerFileLifecycle = (
|
|
3933
|
+
source,
|
|
3934
|
+
{
|
|
3935
|
+
added,
|
|
3936
|
+
updated,
|
|
3937
|
+
removed,
|
|
3938
|
+
notifyExistent = false,
|
|
3939
|
+
keepProcessAlive = true,
|
|
3940
|
+
cooldownBetweenFileEvents = 0,
|
|
3941
|
+
idleMs = 50,
|
|
3942
|
+
},
|
|
3943
|
+
) => {
|
|
3944
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
3945
|
+
if (!undefinedOrFunction(added)) {
|
|
3946
|
+
throw new TypeError(`added must be a function or undefined, got ${added}`);
|
|
3947
|
+
}
|
|
3948
|
+
if (!undefinedOrFunction(updated)) {
|
|
3949
|
+
throw new TypeError(
|
|
3950
|
+
`updated must be a function or undefined, got ${updated}`,
|
|
3951
|
+
);
|
|
3952
|
+
}
|
|
3953
|
+
if (!undefinedOrFunction(removed)) {
|
|
3954
|
+
throw new TypeError(
|
|
3955
|
+
`removed must be a function or undefined, got ${removed}`,
|
|
3956
|
+
);
|
|
3957
|
+
}
|
|
3958
|
+
if (idleMs) {
|
|
3959
|
+
if (updated) {
|
|
3960
|
+
updated = callOnceIdle(updated, idleMs);
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
if (cooldownBetweenFileEvents) {
|
|
3964
|
+
if (added) {
|
|
3965
|
+
added = guardTooFastSecondCall(added, cooldownBetweenFileEvents);
|
|
3966
|
+
}
|
|
3967
|
+
if (updated) {
|
|
3968
|
+
updated = guardTooFastSecondCall(updated, cooldownBetweenFileEvents);
|
|
3969
|
+
}
|
|
3970
|
+
if (removed) {
|
|
3971
|
+
removed = guardTooFastSecondCall(removed, cooldownBetweenFileEvents);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3975
|
+
const tracker = trackResources();
|
|
3976
|
+
|
|
3977
|
+
const handleFileFound = ({ stat, existent }) => {
|
|
3978
|
+
const fileMutationStopWatching = watchFileMutation(sourceUrl, {
|
|
3979
|
+
updated,
|
|
3980
|
+
removed: () => {
|
|
3981
|
+
fileMutationStopTracking();
|
|
3982
|
+
watchFileAdded();
|
|
3983
|
+
if (removed) {
|
|
3984
|
+
removed();
|
|
3985
|
+
}
|
|
3986
|
+
},
|
|
3987
|
+
keepProcessAlive,
|
|
3988
|
+
stat,
|
|
3989
|
+
});
|
|
3990
|
+
const fileMutationStopTracking = tracker.registerCleanupCallback(
|
|
3991
|
+
fileMutationStopWatching,
|
|
3992
|
+
);
|
|
3993
|
+
|
|
3994
|
+
if (added) {
|
|
3995
|
+
if (existent) {
|
|
3996
|
+
if (notifyExistent) {
|
|
3997
|
+
added({ existent: true });
|
|
3998
|
+
}
|
|
3999
|
+
} else {
|
|
4000
|
+
added({});
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
};
|
|
4004
|
+
|
|
4005
|
+
const watchFileAdded = () => {
|
|
4006
|
+
const fileCreationStopWatching = watchFileCreation(
|
|
4007
|
+
sourceUrl,
|
|
4008
|
+
(stat) => {
|
|
4009
|
+
fileCreationgStopTracking();
|
|
4010
|
+
handleFileFound({ stat, existent: false });
|
|
4011
|
+
},
|
|
4012
|
+
keepProcessAlive,
|
|
4013
|
+
);
|
|
4014
|
+
const fileCreationgStopTracking = tracker.registerCleanupCallback(
|
|
4015
|
+
fileCreationStopWatching,
|
|
4016
|
+
);
|
|
4017
|
+
};
|
|
4018
|
+
|
|
4019
|
+
const { type, stat } = readFileInfo(sourceUrl);
|
|
4020
|
+
if (type === null) {
|
|
4021
|
+
if (added) {
|
|
4022
|
+
watchFileAdded();
|
|
4023
|
+
} else {
|
|
4024
|
+
throw new Error(
|
|
4025
|
+
`${urlToFileSystemPath(sourceUrl)} must lead to a file, found nothing`,
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
4028
|
+
} else if (type === "file") {
|
|
4029
|
+
handleFileFound({ stat, existent: true });
|
|
4030
|
+
} else {
|
|
4031
|
+
throw new Error(
|
|
4032
|
+
`${urlToFileSystemPath(
|
|
4033
|
+
sourceUrl,
|
|
4034
|
+
)} must lead to a file, type found instead`,
|
|
4035
|
+
);
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
return tracker.cleanup;
|
|
4039
|
+
};
|
|
4040
|
+
|
|
4041
|
+
const readFileInfo = (url) => {
|
|
4042
|
+
try {
|
|
4043
|
+
const stat = readEntryStatSync(new URL(url));
|
|
4044
|
+
return {
|
|
4045
|
+
type: statsToType(stat),
|
|
4046
|
+
stat,
|
|
4047
|
+
};
|
|
4048
|
+
} catch (e) {
|
|
4049
|
+
if (e.code === "ENOENT") {
|
|
4050
|
+
return {
|
|
4051
|
+
type: null,
|
|
4052
|
+
stat: null,
|
|
4053
|
+
};
|
|
4054
|
+
}
|
|
4055
|
+
throw e;
|
|
4056
|
+
}
|
|
4057
|
+
};
|
|
4058
|
+
|
|
4059
|
+
const undefinedOrFunction = (value) =>
|
|
4060
|
+
typeof value === "undefined" || typeof value === "function";
|
|
4061
|
+
|
|
4062
|
+
const watchFileCreation = (source, callback, keepProcessAlive) => {
|
|
4063
|
+
const sourcePath = urlToFileSystemPath(source);
|
|
4064
|
+
const sourceFilename = basename(sourcePath);
|
|
4065
|
+
const directoryPath = dirname(sourcePath);
|
|
4066
|
+
let directoryWatcher = createWatcher(directoryPath, {
|
|
4067
|
+
persistent: keepProcessAlive,
|
|
4068
|
+
});
|
|
4069
|
+
directoryWatcher.on("change", (eventType, filename) => {
|
|
4070
|
+
if (filename && filename !== sourceFilename) return;
|
|
4071
|
+
|
|
4072
|
+
const { type, stat } = readFileInfo(source);
|
|
4073
|
+
// ignore if something else with that name gets created
|
|
4074
|
+
// we are only interested into files
|
|
4075
|
+
if (type !== "file") {
|
|
4076
|
+
return;
|
|
4077
|
+
}
|
|
4078
|
+
directoryWatcher.close();
|
|
4079
|
+
directoryWatcher = undefined;
|
|
4080
|
+
callback(stat);
|
|
4081
|
+
});
|
|
4082
|
+
|
|
4083
|
+
return () => {
|
|
4084
|
+
if (directoryWatcher) {
|
|
4085
|
+
directoryWatcher.close();
|
|
4086
|
+
}
|
|
4087
|
+
};
|
|
4088
|
+
};
|
|
4089
|
+
|
|
4090
|
+
const watchFileMutation = (
|
|
4091
|
+
sourceUrl,
|
|
4092
|
+
{ updated, removed, keepProcessAlive, stat },
|
|
4093
|
+
) => {
|
|
4094
|
+
let prevStat = stat;
|
|
4095
|
+
let watcher;
|
|
4096
|
+
|
|
4097
|
+
const onChange = () => {
|
|
4098
|
+
const { type, stat } = readFileInfo(sourceUrl);
|
|
4099
|
+
|
|
4100
|
+
if (type === null) {
|
|
4101
|
+
stopWatching();
|
|
4102
|
+
if (removed) {
|
|
4103
|
+
removed();
|
|
4104
|
+
}
|
|
4105
|
+
} else if (type === "file") {
|
|
4106
|
+
if (updated && shouldCallUpdated(stat, prevStat)) {
|
|
4107
|
+
updated();
|
|
4108
|
+
}
|
|
4109
|
+
if ((isMacos || isLinux || isFreeBSD) && prevStat.ino !== stat.ino) {
|
|
4110
|
+
stopWatching();
|
|
4111
|
+
watch();
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
prevStat = stat;
|
|
4115
|
+
};
|
|
4116
|
+
|
|
4117
|
+
const watch = () => {
|
|
4118
|
+
watcher = createWatcher(urlToFileSystemPath(sourceUrl), {
|
|
4119
|
+
persistent: keepProcessAlive,
|
|
4120
|
+
});
|
|
4121
|
+
watcher.on("change", onChange);
|
|
4122
|
+
};
|
|
4123
|
+
const stopWatching = () => {
|
|
4124
|
+
if (watcher) {
|
|
4125
|
+
watcher.close();
|
|
4126
|
+
watcher = undefined;
|
|
4127
|
+
}
|
|
4128
|
+
};
|
|
4129
|
+
watch();
|
|
4130
|
+
return stopWatching;
|
|
4131
|
+
};
|
|
4132
|
+
|
|
4133
|
+
const shouldCallUpdated = (stat, prevStat) => {
|
|
4134
|
+
if (!stat.atimeMs) {
|
|
4135
|
+
return true;
|
|
4136
|
+
}
|
|
4137
|
+
if (stat.atimeMs <= stat.mtimeMs) {
|
|
4138
|
+
return true;
|
|
4139
|
+
}
|
|
4140
|
+
if (stat.mtimeMs !== prevStat.mtimeMs) {
|
|
4141
|
+
return true;
|
|
4142
|
+
}
|
|
4143
|
+
return false;
|
|
4144
|
+
};
|
|
4145
|
+
|
|
3894
4146
|
/*
|
|
3895
4147
|
* - Buffer documentation on Node.js
|
|
3896
4148
|
* https://nodejs.org/docs/latest-v13.x/api/buffer.html
|
|
@@ -6419,4 +6671,4 @@ const isResponseEligibleForIntegrityValidation = (response) => {
|
|
|
6419
6671
|
return ["basic", "cors", "default"].includes(response.type);
|
|
6420
6672
|
};
|
|
6421
6673
|
|
|
6422
|
-
export { ANSI, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, bufferToEtag, compareFileUrls, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createLogger, createTaskLog, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, formatError, generateContentFrame, getCallerPosition, getExtensionsToTry, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, normalizeImportMap, normalizeUrl, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, stringifyUrlSite, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };
|
|
6674
|
+
export { ANSI, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, bufferToEtag, compareFileUrls, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createLogger, createTaskLog, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, formatError, generateContentFrame, getCallerPosition, getExtensionsToTry, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, normalizeImportMap, normalizeUrl, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, registerFileLifecycle, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, stringifyUrlSite, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };
|