@maka/meteor-sdk 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/isopackets/combined/isopacket-buildinfo.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/.npm-shrinkwrap.json +3 -3
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/.package-lock.json +4 -4
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/baseline-browser-mapping/dist/index.cjs +1 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/baseline-browser-mapping/dist/index.js +1 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/baseline-browser-mapping/package.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/boilerplate-generator/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/constraint-solver/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ddp-client/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime-client/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime-server/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/fetch/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/inter-process-messaging/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/logging/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/logic-solver/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/meteor/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/modules/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/modules-runtime/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/npm-mongo/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/package-version-parser/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/promise/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/react-fast-refresh/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/socket-stream-client/node_modules/.package-lock.json +1 -1
- package/package.json +1 -1
- package/src/project.js +40 -0
- package/tools/fs/optimistic.ts +106 -1
- package/tools/fs/safe-watcher.ts +40 -0
- package/tools/fs/watch.ts +39 -0
- package/tools/isobuild/meteor-npm.js +171 -39
- package/isopackets/combined/npm/node_modules/meteor/fetch/node_modules/node-fetch/.meteor-portable-2.json +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/meteor-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "Programmatic Node API over the Meteor build tool (bundle/run/create/etc.) -- for driving Meteor's build system from plain Node code instead of shelling out to a meteor CLI (there isn't one anymore; see README.md). \"dependencies\" is generated by scripts/write-tool-package-json.js from scripts/dev-bundle-tool-package.js at the monorepo root -- do not hand-edit that field, edit that file and re-run the script instead.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
package/src/project.js
CHANGED
|
@@ -65,6 +65,19 @@ async function loadProject(projectDir) {
|
|
|
65
65
|
projectDir: standardProjectDir,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
// Second-stage npm-deps prewarm (bootstrap.js fired the restore-only
|
|
69
|
+
// first stage): now that an app is known, its .meteor/versions names
|
|
70
|
+
// the exact core-package set the build will need, so cache MISSES can
|
|
71
|
+
// be npm-installed too -- in parallel (small pool; concurrent npm
|
|
72
|
+
// processes contend on npm's shared content cache) and BEFORE the
|
|
73
|
+
// build's serial per-package walk, which also keeps those installs'
|
|
74
|
+
// .npm churn from re-firing the app runner's file watcher mid-build.
|
|
75
|
+
// Un-awaited: every prewarm operation holds the same cross-process
|
|
76
|
+
// per-package lock updateDependencies() takes, so the build below
|
|
77
|
+
// simply waits on (or wins) each package's lock. Best-effort by
|
|
78
|
+
// design -- on any failure the build installs serially as before.
|
|
79
|
+
startAppNpmDepsPrewarm(standardProjectDir);
|
|
80
|
+
|
|
68
81
|
await runCaptured("preparing project", () =>
|
|
69
82
|
projectContext.prepareProjectForBuild()
|
|
70
83
|
);
|
|
@@ -72,4 +85,31 @@ async function loadProject(projectDir) {
|
|
|
72
85
|
return new Project(projectContext);
|
|
73
86
|
}
|
|
74
87
|
|
|
88
|
+
function startAppNpmDepsPrewarm(standardProjectDir) {
|
|
89
|
+
try {
|
|
90
|
+
const files = require("../tools/fs/files");
|
|
91
|
+
const versionsFile = files.pathJoin(
|
|
92
|
+
standardProjectDir, ".meteor", "versions");
|
|
93
|
+
// name@version lines; author:package names never match the bundled
|
|
94
|
+
// core-package directory names, so no special-casing needed. A
|
|
95
|
+
// missing versions file (brand-new app) just skips this stage.
|
|
96
|
+
const names = files.readFile(versionsFile, "utf8")
|
|
97
|
+
.split("\n")
|
|
98
|
+
.map((line) => line.trim().split("@")[0])
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
if (!names.length) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const meteorNpm = require("../tools/isobuild/meteor-npm.js");
|
|
104
|
+
Promise.resolve(
|
|
105
|
+
meteorNpm.prewarmNpmDepsCache(
|
|
106
|
+
path.resolve(__dirname, "..", "packages"),
|
|
107
|
+
{ onlyPackages: names, installOnMiss: true }
|
|
108
|
+
)
|
|
109
|
+
).catch(() => {});
|
|
110
|
+
} catch (e) {
|
|
111
|
+
// Best-effort: the build's own per-package install path still works.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
75
115
|
module.exports = { loadProject, Project };
|
package/tools/fs/optimistic.ts
CHANGED
|
@@ -85,9 +85,40 @@ function makeOptimistic<
|
|
|
85
85
|
// subscribing to file changes.
|
|
86
86
|
const optimisticFindAppDir = wrap(findAppDir);
|
|
87
87
|
|
|
88
|
+
// The running tool's own installation root (tools/fs/optimistic.ts ->
|
|
89
|
+
// two levels up). When the tool is installed as an npm package (e.g.
|
|
90
|
+
// @maka/meteor-sdk under a global node_modules), every file it owns
|
|
91
|
+
// lives beneath one or more node_modules segments, which made the
|
|
92
|
+
// nested-node_modules rule below refuse to watch ANY tool file. With no
|
|
93
|
+
// watcher, an optimistic result cached while a package's .npm directory
|
|
94
|
+
// was being atomically swapped (renameDirAlmostAtomically's brief gap
|
|
95
|
+
// during npm dependency updates) could never be invalidated -- observed
|
|
96
|
+
// as optimisticHashOrNull permanently returning null for an existing
|
|
97
|
+
// npm-shrinkwrap.json, convincing every isopack up-to-date check and
|
|
98
|
+
// the app runner's watchset that a build input was deleted: an eternal
|
|
99
|
+
// rebuild/restart loop ("flapping") after a fresh install. A git
|
|
100
|
+
// checkout has no node_modules prefix and always watched these files;
|
|
101
|
+
// ignoring node_modules segments inside the tool's own prefix restores
|
|
102
|
+
// exactly those semantics.
|
|
103
|
+
const toolRootParts = pathDirname(pathDirname(__dirname))
|
|
104
|
+
.split("\\").join(pathSep)
|
|
105
|
+
.split(pathSep);
|
|
106
|
+
|
|
107
|
+
function toolRootPrefixLength(parts: string[]): number {
|
|
108
|
+
if (parts.length <= toolRootParts.length) {
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
for (let i = 0; i < toolRootParts.length; i++) {
|
|
112
|
+
if (parts[i] !== toolRootParts[i]) {
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return toolRootParts.length;
|
|
117
|
+
}
|
|
118
|
+
|
|
88
119
|
const shouldWatch = wrap(Profile("shouldWatch", (path: string) => {
|
|
89
120
|
const parts = path.split(pathSep);
|
|
90
|
-
const nmi = parts.indexOf("node_modules");
|
|
121
|
+
const nmi = parts.indexOf("node_modules", toolRootPrefixLength(parts));
|
|
91
122
|
|
|
92
123
|
if (nmi < 0) {
|
|
93
124
|
// Watch everything not in a node_modules directory.
|
|
@@ -204,6 +235,36 @@ function dependOnParentDirectory(path: string) {
|
|
|
204
235
|
}
|
|
205
236
|
}
|
|
206
237
|
|
|
238
|
+
// For files belonging to the running tool itself, never let a negative
|
|
239
|
+
// (missing-file) result stick: these paths only ever go missing during
|
|
240
|
+
// meteor-npm.js's atomic .npm directory swaps, and every event- or
|
|
241
|
+
// dirty-based invalidation can be raced by a computation that STARTED
|
|
242
|
+
// before the swap and commits its cached null after them (the
|
|
243
|
+
// dirtyReplacedDirectory call included). A null that survives such a
|
|
244
|
+
// race convinced every rebuild that npm-shrinkwrap.json was deleted --
|
|
245
|
+
// the eternal restart loop. Self-dirtying on the next tick makes the
|
|
246
|
+
// null last at most one turn of the event loop; the recompute then sees
|
|
247
|
+
// the real post-swap file. Genuinely missing tool files are rare enough
|
|
248
|
+
// that losing negative-caching for them costs nothing measurable.
|
|
249
|
+
function scheduleEphemeralNull(
|
|
250
|
+
wrapper: { dirty: (...args: any[]) => any },
|
|
251
|
+
args: IArguments | any[],
|
|
252
|
+
) {
|
|
253
|
+
const path = args[0];
|
|
254
|
+
if (typeof path !== "string") {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (toolRootPrefixLength(path.split(pathSep)) === 0) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const argArray = Array.prototype.slice.call(args);
|
|
261
|
+
setImmediate(() => {
|
|
262
|
+
try {
|
|
263
|
+
wrapper.dirty(...argArray);
|
|
264
|
+
} catch (e) {}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
207
268
|
// Called by any optimistic function that receives a */node_modules/* path
|
|
208
269
|
// as its first argument, so that we can later bulk-invalidate the results
|
|
209
270
|
// of those calls if the contents of the node_modules directory change.
|
|
@@ -255,6 +316,7 @@ export const optimisticStatOrNull = makeCheapPathFunction(
|
|
|
255
316
|
const result = statOrNull(path);
|
|
256
317
|
if (result === null) {
|
|
257
318
|
dependOnParentDirectory(path);
|
|
319
|
+
scheduleEphemeralNull(optimisticStatOrNull, [path]);
|
|
258
320
|
}
|
|
259
321
|
return result;
|
|
260
322
|
},
|
|
@@ -268,6 +330,8 @@ export const optimisticLStatOrNull = makeCheapPathFunction(
|
|
|
268
330
|
} catch (e: any) {
|
|
269
331
|
if (e.code !== "ENOENT") throw e;
|
|
270
332
|
dependOnParentDirectory(path);
|
|
333
|
+
scheduleEphemeralNull(optimisticLStatOrNull, [path]);
|
|
334
|
+
scheduleEphemeralNull(optimisticLStat, [path]);
|
|
271
335
|
return null;
|
|
272
336
|
}
|
|
273
337
|
},
|
|
@@ -290,6 +354,10 @@ export const optimisticHashOrNull = makeOptimistic("hashOrNull", (
|
|
|
290
354
|
}
|
|
291
355
|
|
|
292
356
|
dependOnParentDirectory(path);
|
|
357
|
+
scheduleEphemeralNull(
|
|
358
|
+
optimisticHashOrNull, options === undefined ? [path] : [path, options]);
|
|
359
|
+
scheduleEphemeralNull(
|
|
360
|
+
optimisticReadFile, options === undefined ? [path] : [path, options]);
|
|
293
361
|
|
|
294
362
|
return null;
|
|
295
363
|
});
|
|
@@ -312,6 +380,12 @@ makeOptimistic("readJsonOrNull", (
|
|
|
312
380
|
} catch (e: any) {
|
|
313
381
|
if (e.code === "ENOENT") {
|
|
314
382
|
dependOnParentDirectory(path);
|
|
383
|
+
scheduleEphemeralNull(
|
|
384
|
+
optimisticReadJsonOrNull,
|
|
385
|
+
options === undefined ? [path] : [path, options]);
|
|
386
|
+
scheduleEphemeralNull(
|
|
387
|
+
optimisticReadFile,
|
|
388
|
+
options === undefined ? [path] : [path, options]);
|
|
315
389
|
return null;
|
|
316
390
|
}
|
|
317
391
|
throw e;
|
|
@@ -338,6 +412,37 @@ makeOptimistic("readJsonOrNull", (
|
|
|
338
412
|
}
|
|
339
413
|
});
|
|
340
414
|
|
|
415
|
+
// Deterministically forget cached results for a directory that was
|
|
416
|
+
// just replaced wholesale (meteor-npm.js's atomic .npm swap via
|
|
417
|
+
// renameDirAlmostAtomically). Watcher-event-driven invalidation cannot
|
|
418
|
+
// be relied on for such swaps: the events arrive only for the
|
|
419
|
+
// directory's own path (see safe-watcher's fireEntriesUnder), can race
|
|
420
|
+
// the read that cached the stale result, and in npm-installed tool
|
|
421
|
+
// trees per-file watches historically never existed at all (see
|
|
422
|
+
// toolRootPrefixLength above). A stale null cached here convinced
|
|
423
|
+
// every rebuild that npm-shrinkwrap.json was deleted -- an eternal
|
|
424
|
+
// rebuild/restart loop after fresh installs. An explicit dirty at the
|
|
425
|
+
// mutation site is the only race-free invalidation. Dirties the
|
|
426
|
+
// directory, its parent, and its current immediate children across
|
|
427
|
+
// every per-path optimistic function.
|
|
428
|
+
export function dirtyReplacedDirectory(dir: string) {
|
|
429
|
+
const paths = [dir, pathDirname(dir)];
|
|
430
|
+
try {
|
|
431
|
+
readdir(dir).forEach(name => paths.push(pathJoin(dir, name)));
|
|
432
|
+
} catch (e) {}
|
|
433
|
+
paths.forEach(path => {
|
|
434
|
+
optimisticStatOrNull.dirty(path);
|
|
435
|
+
optimisticLStat.dirty(path);
|
|
436
|
+
optimisticLStatOrNull.dirty(path);
|
|
437
|
+
optimisticReadFile.dirty(path);
|
|
438
|
+
optimisticReaddir.dirty(path);
|
|
439
|
+
optimisticHashOrNull.dirty(path);
|
|
440
|
+
optimisticReadJsonOrNull.dirty(path);
|
|
441
|
+
dependOnDirectory.dirty(path);
|
|
442
|
+
dependOnPath.dirty(path);
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
341
446
|
export const optimisticReadMeteorIgnore = wrap((dir: string) => {
|
|
342
447
|
const meteorIgnorePath = pathJoin(dir, ".meteorignore");
|
|
343
448
|
const meteorIgnoreStat = optimisticStatOrNull(meteorIgnorePath);
|
package/tools/fs/safe-watcher.ts
CHANGED
|
@@ -93,6 +93,25 @@ function deleteEntry(path: string): void {
|
|
|
93
93
|
entries.delete(path);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// A rename or removal of a DIRECTORY reaches us as a single event for
|
|
97
|
+
// the directory's own path; entries registered for files INSIDE it
|
|
98
|
+
// (e.g. the optimistic layer's per-file watches under a package's .npm
|
|
99
|
+
// directory, which meteor-npm.js swaps wholesale via
|
|
100
|
+
// renameDirAlmostAtomically) would otherwise never hear about it --
|
|
101
|
+
// findNearestEntry only walks UP. Observed failure: an
|
|
102
|
+
// optimisticHashOrNull result cached as null during the swap was never
|
|
103
|
+
// invalidated, convincing every rebuild a build input was deleted (an
|
|
104
|
+
// eternal rebuild/restart loop after fresh installs). Fire every
|
|
105
|
+
// registered descendant of the changed path too.
|
|
106
|
+
function fireEntriesUnder(changedPath: string, evtType: string): void {
|
|
107
|
+
const prefix = pathResolve(changedPath) + "/";
|
|
108
|
+
for (const [entryPath, entry] of entries) {
|
|
109
|
+
if (entry && entryPath.startsWith(prefix)) {
|
|
110
|
+
entry._fire(evtType);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
96
115
|
function findNearestEntry(startPath: string): Entry | null {
|
|
97
116
|
let currentPath = pathResolve(startPath);
|
|
98
117
|
|
|
@@ -353,6 +372,27 @@ async function ensureWatchRoot(dirPath: string): Promise<void> {
|
|
|
353
372
|
for (const event of events) {
|
|
354
373
|
const changedPath = toPosixPath(event.path);
|
|
355
374
|
const entry = findNearestEntry(changedPath);
|
|
375
|
+
// Deletes/creates can be directory renames whose contents
|
|
376
|
+
// never get their own events -- notify entries BELOW the
|
|
377
|
+
// path as well (see fireEntriesUnder).
|
|
378
|
+
if (event.type !== "update") {
|
|
379
|
+
fireEntriesUnder(
|
|
380
|
+
changedPath, event.type === "delete" ? "delete" : "change");
|
|
381
|
+
}
|
|
382
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
383
|
+
// Re-derive the matched registration so flap debugging can
|
|
384
|
+
// see WHICH registered path a deep event was attributed to.
|
|
385
|
+
let cursor = pathResolve(changedPath);
|
|
386
|
+
let matched = null;
|
|
387
|
+
while (true) {
|
|
388
|
+
if (getEntry(cursor)) { matched = cursor; break; }
|
|
389
|
+
const parent = pathDirname(cursor);
|
|
390
|
+
if (parent === cursor) break;
|
|
391
|
+
cursor = parent;
|
|
392
|
+
}
|
|
393
|
+
console.log("WATCH-FIRE", event.type, changedPath,
|
|
394
|
+
"->", matched || "(no entry)");
|
|
395
|
+
}
|
|
356
396
|
if (!entry) continue;
|
|
357
397
|
// In Meteor's safe-watcher API, both create/update trigger "change" events.
|
|
358
398
|
const evtType = event.type === "delete" ? "delete" : "change";
|
package/tools/fs/watch.ts
CHANGED
|
@@ -123,6 +123,14 @@ export class WatchSet {
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
// Nope, inconsistent.
|
|
126
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
127
|
+
// The file changed DURING the build (recorded twice with
|
|
128
|
+
// different hashes). Every Watcher built on this set now fires
|
|
129
|
+
// immediately -- flap debugging needs the culprit's name.
|
|
130
|
+
console.log("WATCHSET-CONFLICT", filePath,
|
|
131
|
+
"recorded", this.files[filePath], "now", hash, "at",
|
|
132
|
+
new Error("watchset-conflict").stack);
|
|
133
|
+
}
|
|
126
134
|
this.alwaysFire = true;
|
|
127
135
|
return;
|
|
128
136
|
}
|
|
@@ -448,6 +456,10 @@ export class Watcher {
|
|
|
448
456
|
|
|
449
457
|
// Were we given an inconsistent WatchSet? Fire now and be done with it.
|
|
450
458
|
if (this.watchSet.alwaysFire) {
|
|
459
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
460
|
+
console.log("WATCHER-ALWAYSFIRE: inconsistent watchSet -- " +
|
|
461
|
+
"restarting immediately (see WATCHSET-CONFLICT above)");
|
|
462
|
+
}
|
|
451
463
|
this.fire();
|
|
452
464
|
return;
|
|
453
465
|
}
|
|
@@ -481,12 +493,18 @@ export class Watcher {
|
|
|
481
493
|
return false;
|
|
482
494
|
}
|
|
483
495
|
// Nope, not what we expected.
|
|
496
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
497
|
+
console.log("WATCH-CHANGED-FILE (deleted/now-dir)", absPath);
|
|
498
|
+
}
|
|
484
499
|
this.fire();
|
|
485
500
|
return true;
|
|
486
501
|
}
|
|
487
502
|
|
|
488
503
|
// File exists! Is that what we expected?
|
|
489
504
|
if (oldHash === null) {
|
|
505
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
506
|
+
console.log("WATCH-CHANGED-FILE (appeared)", absPath);
|
|
507
|
+
}
|
|
490
508
|
this.fire();
|
|
491
509
|
return true;
|
|
492
510
|
}
|
|
@@ -496,6 +514,9 @@ export class Watcher {
|
|
|
496
514
|
return false;
|
|
497
515
|
}
|
|
498
516
|
|
|
517
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
518
|
+
console.log("WATCH-CHANGED-FILE", absPath, oldHash, "->", newHash);
|
|
519
|
+
}
|
|
499
520
|
this.fire();
|
|
500
521
|
return true;
|
|
501
522
|
}
|
|
@@ -513,6 +534,11 @@ export class Watcher {
|
|
|
513
534
|
|
|
514
535
|
// If the directory has changed (including being deleted or created).
|
|
515
536
|
if (! _.isEqual(info.contents, newContents)) {
|
|
537
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
538
|
+
console.log("WATCH-CHANGED-DIR", info.absPath,
|
|
539
|
+
"expected", JSON.stringify(info.contents),
|
|
540
|
+
"found", JSON.stringify(newContents));
|
|
541
|
+
}
|
|
516
542
|
this.fire();
|
|
517
543
|
return true;
|
|
518
544
|
}
|
|
@@ -678,6 +704,11 @@ export class Watcher {
|
|
|
678
704
|
let stat = files.statOrNull(absPath);
|
|
679
705
|
const mustNotExist = this.mustNotExist(absPath);
|
|
680
706
|
const mustBeAFile = this.mustBeAFile(absPath);
|
|
707
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
708
|
+
// Tag the path before any of the fires below so the adjacent
|
|
709
|
+
// WATCH-FIRE-STACK line can be attributed to a file.
|
|
710
|
+
(this as any)._debugLastStatPath = absPath;
|
|
711
|
+
}
|
|
681
712
|
|
|
682
713
|
if (stat && lastStat === undefined) {
|
|
683
714
|
// We have not checked for this file before, so our expectations are
|
|
@@ -787,6 +818,14 @@ export class Watcher {
|
|
|
787
818
|
|
|
788
819
|
private fire() {
|
|
789
820
|
if (this.stopped) return;
|
|
821
|
+
if (process.env.METEOR_WATCH_DEBUG) {
|
|
822
|
+
// The chokepoint every restart-triggering change funnels through;
|
|
823
|
+
// the stack identifies WHICH check fired when the per-site logs
|
|
824
|
+
// (WATCH-CHANGED-*, WATCHSET-CONFLICT) didn't catch it.
|
|
825
|
+
console.log("WATCH-FIRE-STACK",
|
|
826
|
+
"lastStatPath=" + ((this as any)._debugLastStatPath || "(none)"),
|
|
827
|
+
new Error("watcher-fired").stack);
|
|
828
|
+
}
|
|
790
829
|
this.stop();
|
|
791
830
|
this.onChange();
|
|
792
831
|
}
|