stonyx 0.2.3-alpha.60 → 0.2.3-alpha.61
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/modules.js +42 -12
- package/dist/util/duplicate-core.js +2 -1
- package/package.json +1 -1
package/dist/modules.js
CHANGED
|
@@ -16,10 +16,29 @@ const modulePromises = {};
|
|
|
16
16
|
// and this is the only question it needs answered: "is this name in the
|
|
17
17
|
// manifest?" is exactly what `modulePromises` stopped being able to answer.
|
|
18
18
|
//
|
|
19
|
-
// Reassigned per `loadModules` call rather than accumulated,
|
|
20
|
-
// manifest
|
|
21
|
-
// load
|
|
22
|
-
//
|
|
19
|
+
// Reassigned per `loadModules` call rather than accumulated, because only the
|
|
20
|
+
// current manifest can answer for the current boot — a name declared by an
|
|
21
|
+
// earlier load must not answer for this one.
|
|
22
|
+
//
|
|
23
|
+
// It is NOT symmetric with `modulePromises`, and the asymmetry is worth
|
|
24
|
+
// stating because the obvious reading of the line above is that it is.
|
|
25
|
+
// `modulePromises` has no reset: no `delete`, no reassignment, no clear, at
|
|
26
|
+
// any commit on `dev`. It accumulates the union of every `loadModules` call in
|
|
27
|
+
// the process while this set is replaced per call. Measured over three loads
|
|
28
|
+
// in one process at c87826b: load A declares and installs
|
|
29
|
+
// `@stonyx/probe-life`; load B declares it with the package absent, so
|
|
30
|
+
// discovery warns and `continue`s and registers nothing — and
|
|
31
|
+
// `waitForModule('probe-life')` still RESOLVES, off load A's stale entry.
|
|
32
|
+
// That is the "reports success for a module never loaded, silently" outcome
|
|
33
|
+
// the registration loop below calls worse than the hang; T23 forecloses that
|
|
34
|
+
// remedy WITHIN one call and no test sees across calls.
|
|
35
|
+
//
|
|
36
|
+
// Inherited, not introduced here — `modulePromises` was never reset at base
|
|
37
|
+
// 5693744 either — and deliberately not closed here, because giving it a
|
|
38
|
+
// per-call lifetime has a blast radius of its own and is filed separately.
|
|
39
|
+
// Production reachability is nil: one `loadModules` call site, guarded by the
|
|
40
|
+
// `Stonyx.instance` early return. The suite is held safe by the
|
|
41
|
+
// unique-module-name-per-test rule in this file's test header, not by this.
|
|
23
42
|
let declaredModuleNames = new Set();
|
|
24
43
|
// Configure module-specific logging
|
|
25
44
|
function configureLog(chronicle, module, config) {
|
|
@@ -37,8 +56,9 @@ function initializeModule(moduleName, moduleClass, modules, initPromises) {
|
|
|
37
56
|
// which is never created. Third of the three never-resolved paths named in
|
|
38
57
|
// #120, and the one that scoping registration to `discovered` does NOT close,
|
|
39
58
|
// because such a module IS discovered — measured, not reasoned: with only
|
|
40
|
-
// the registration fix applied the suite read 180/1
|
|
41
|
-
// failure.
|
|
59
|
+
// the registration fix applied the suite read 180 pass / 1 fail at f575a3c
|
|
60
|
+
// with T24 the sole failure. The SHA is the point; see the aggregate
|
|
61
|
+
// convention noted at the `declaredDependencies` union below. Resolving here is readiness BY DEFINITION — there is no `init()`,
|
|
42
62
|
// so there is nothing to wait for — and that is what separates it from
|
|
43
63
|
// resolving on a discovery `continue`, where there is no module to be ready
|
|
44
64
|
// at all.
|
|
@@ -226,13 +246,18 @@ export default async function loadModules(config, rootPath, chronicle) {
|
|
|
226
246
|
// change: hang becomes throw — and the throw names the right cause, because
|
|
227
247
|
// `modulePromises` is no longer the app's declared list and `waitForModule`
|
|
228
248
|
// below branches on that difference rather than asserting the old one. T13
|
|
229
|
-
// pins
|
|
249
|
+
// pins the hang-becomes-throw change and the named cause; the PR body names
|
|
250
|
+
// them. ("Halves" is avoided here because T13 uses that word for its own
|
|
251
|
+
// input-domain split — the `devDependencies` half it covers and the
|
|
252
|
+
// `dependencies` half it hands to T23.)
|
|
230
253
|
//
|
|
231
254
|
// SCOPED DELIBERATELY, because the sentence above would otherwise read as a
|
|
232
255
|
// closure statement for the whole never-settling-promise family and it is
|
|
233
256
|
// not one. What it closes is names that FAIL discovery. A name that PASSES
|
|
234
|
-
// discovery is registered, and its
|
|
235
|
-
// `init()` has settled —
|
|
257
|
+
// discovery is registered, and where its class HAS an `init()` its promise
|
|
258
|
+
// is resolved only once that `init()` has settled — the qualifier matters,
|
|
259
|
+
// because two of the three resolve sites named above settle without any
|
|
260
|
+
// `init()` running. So two discovered modules whose `init()`s await
|
|
236
261
|
// each other through `waitForModule` are both registered, reach no resolve
|
|
237
262
|
// site, and leave `await Promise.all(initPromises)` at the end of this
|
|
238
263
|
// function pending forever. Measured on a cross-map mutual wait
|
|
@@ -242,6 +267,10 @@ export default async function loadModules(config, rootPath, chronicle) {
|
|
|
242
267
|
// discovered, so it failed fast for the wrong reason
|
|
243
268
|
// a57045e HUNG, 3001 ms
|
|
244
269
|
// f575a3c HUNG, 3003 ms — scoping registration does not touch it
|
|
270
|
+
// c87826b HUNG, 3002 ms — re-measured at the head this ships from,
|
|
271
|
+
// because the rows above stop before three commits that
|
|
272
|
+
// touch this file and a reader at head cannot tell from a
|
|
273
|
+
// truncated table whether the row still applies
|
|
245
274
|
// Rule 3 moves that shape from fail-fast to deadlock and nothing in this
|
|
246
275
|
// file closes it.
|
|
247
276
|
//
|
|
@@ -354,9 +383,10 @@ export async function waitForModule(moduleName) {
|
|
|
354
383
|
const modulePromise = modulePromises[fullName];
|
|
355
384
|
if (!modulePromise) {
|
|
356
385
|
throw new Error(declaredModuleNames.has(fullName)
|
|
357
|
-
? `Could wait for module: ${fullName}. It IS declared in this project's dependencies
|
|
358
|
-
'loader did not load it: either it is not installed under
|
|
359
|
-
'does not carry the "stonyx-module" keyword.
|
|
386
|
+
? `Could wait for module: ${fullName}. It IS declared in this project's dependencies or ` +
|
|
387
|
+
'devDependencies, but the loader did not load it: either it is not installed under ' +
|
|
388
|
+
'node_modules, or its package.json does not carry the "stonyx-module" keyword. ' +
|
|
389
|
+
'loadModules warned which one at load time.'
|
|
360
390
|
: `Could wait for module: ${fullName}. Module was not registered in project dependencies`);
|
|
361
391
|
}
|
|
362
392
|
await modulePromise.ready;
|
|
@@ -407,7 +407,8 @@ export function duplicateCoreMessage(foreign) {
|
|
|
407
407
|
'de-duplicated union of both maps. What it then reports is not those packages\' own copies but ' +
|
|
408
408
|
'the copy each of them would IMPORT: for every one it follows Node\'s ESM resolution walk up ' +
|
|
409
409
|
'from that module\'s own directory and takes the FIRST copy that walk finds, whoever owns it — ' +
|
|
410
|
-
'including this app\'s own node_modules/stonyx, which no module
|
|
410
|
+
'including this app\'s own node_modules/stonyx, which no module need declare. A copy no ' +
|
|
411
|
+
'module\'s ' +
|
|
411
412
|
'walk reaches first is not counted, and one further up a walk is hidden by a nearer one.',
|
|
412
413
|
].join('\n');
|
|
413
414
|
}
|