@animus-ui/vite-plugin 0.1.6 → 0.1.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"build-start.d.ts","sourceRoot":"","sources":["../src/build-start.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,aAAa,EAClB,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAC/D,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,MAAM,GAC3D,OAAO,CAAC,IAAI,CAAC,CAuRf"}
1
+ {"version":3,"file":"build-start.d.ts","sourceRoot":"","sources":["../src/build-start.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,aAAa,EAClB,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EAC/D,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,MAAM,GAC3D,OAAO,CAAC,IAAI,CAAC,CA2Rf"}
package/dist/context.d.ts CHANGED
@@ -44,34 +44,6 @@ export declare function buildFileEntriesFromCache(cache: Map<string, {
44
44
  * behavioral test doubles — carry no memo and generate here instead.
45
45
  */
46
46
  export declare function systemPropsModuleSource(ctx: PluginContext): string;
47
- /**
48
- * Run project analysis and report whether the served system-props module
49
- * CHANGED.
50
- *
51
- * The module is imported by every module that renders a system prop, so
52
- * re-delivering it pushes an update through all of them; every analysis
53
- * republishes its inputs whether or not they moved, so a new analysis is not
54
- * itself an admissible trigger (openspec: vite-extraction-plugin, "System prop
55
- * map HMR invalidation").
56
- *
57
- * The comparison is over the GENERATED MODULE, not over the prop map alone.
58
- * The map is one of four inputs, and they move independently: widening a
59
- * component's `.system({ ... })` opt-in adds a `dynamicPropConfig` entry while
60
- * minting no new utility class, so a map-only comparison reports "unchanged"
61
- * and the client is left with a config missing the new prop — permanently,
62
- * since Vite keeps serving the module's cached transform result across full
63
- * page reloads. Comparing the artifact itself needs no argument about which
64
- * inputs are volatile this month.
65
- *
66
- * A free function rather than a method: the comparison must run for real
67
- * against any context the caller holds, including the behavioral test doubles
68
- * that stand in for the engine.
69
- */
70
- export declare function runAnalysisTrackingSystemProps(ctx: PluginContext, fileEntries: Array<{
71
- path: string;
72
- source: string;
73
- hash?: string;
74
- }>): boolean;
75
47
  /**
76
48
  * Drop a deleted (or renamed-away) file from the dev file cache so its
77
49
  * last-known source stops riding along as a ghost entry on every later
@@ -115,12 +87,22 @@ export declare class PluginContext {
115
87
  layerDeclaration: string;
116
88
  transformOutputHashes: Map<string, string>;
117
89
  recordTransformOutput(relativePath: string, code: string): void;
90
+ /**
91
+ * The consumer system's OWN keyframes collections, captured at load time
92
+ * before any external merge touches `system.keyframesJson`. Every
93
+ * `applyExternalKeyframes` merge starts from this baseline, so repeated
94
+ * merges (a --watch rebuild's loadSystem + rediscovery, a geological
95
+ * reset) never compound prior external state — a removed include's
96
+ * keyframes disappear with it.
97
+ */
98
+ private consumerKeyframesJson;
118
99
  /**
119
100
  * Merge `Keyframes` collections from discovered external package entries
120
101
  * into the system's collections (keyframes-only carve-out — the consumer
121
102
  * system stays the singular config authority). Runs after buildStart
122
103
  * discovery AND after every geological-reset system reload, since a reload
123
- * rebuilds `this.system` from the consumer entry alone.
104
+ * rebuilds `this.system` from the consumer entry alone. Merges from the
105
+ * consumer-only baseline, never from the previously merged value.
124
106
  */
125
107
  applyExternalKeyframes(): void;
126
108
  /** Discovery-time keyframes diagnostics awaiting the next analysis's
@@ -135,7 +117,9 @@ export declare class PluginContext {
135
117
  hash: string;
136
118
  source: string;
137
119
  }>;
120
+ rawExtensionFallbacks: Set<string>;
138
121
  readonly hotUpdateEvents: HotUpdateEvents;
122
+ private pendingReloadTimer;
139
123
  packageMap: Record<string, string>;
140
124
  base: string;
141
125
  assetUrlBySpecifier: Map<string, string>;
@@ -145,6 +129,7 @@ export declare class PluginContext {
145
129
  externalDirOwners: Record<string, string>;
146
130
  externalFileOwners: Record<string, string>;
147
131
  externalSourceEntries: Map<string, string>;
132
+ externalKeyframesScanEntries: Map<string, string>;
148
133
  externalPackageOutcomes: ExternalPackageOutcome[];
149
134
  devServer: any;
150
135
  resolvedSystemPath: string | null;
@@ -174,13 +159,18 @@ export declare class PluginContext {
174
159
  loadSystem(): void;
175
160
  /**
176
161
  * Run project analysis via the shared `runProjectAnalysis` and update
177
- * all manifest-derived state.
162
+ * all manifest-derived state. Returns whether the analysis PUBLISHED —
163
+ * `false` means the previous manifest is still current, and callers that
164
+ * advanced the file cache for this attempt must roll that entry back or
165
+ * the content-hash gate will suppress the same-content retry forever
166
+ * (openspec: dev-transform-coherence, "Failed analyses do not suppress
167
+ * equal-content retries"). Strict mode still throws.
178
168
  */
179
169
  runAnalysis(fileEntries: Array<{
180
170
  path: string;
181
171
  source: string;
182
172
  hash?: string;
183
- }>): void;
173
+ }>): boolean;
184
174
  /** Base-prefixed dev URL for an absolute file (Vite mounts /@fs under base). */
185
175
  devFsUrl(absPath: string): string;
186
176
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
package/dist/hmr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,MAAM,CAAC;AAEd;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,aAAa,EAClB,WAAW,EAAE,cAAc,EAC3B,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,gBAAgB,GACzD,OAAO,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAmEzC"}
1
+ {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,MAAM,CAAC;AAEd;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,aAAa,EAClB,WAAW,EAAE,cAAc,EAC3B,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,gBAAgB,GACzD,OAAO,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAqEzC"}
@@ -10,9 +10,10 @@
10
10
  * modules in its own graph. `staleDefinitionFiles` are rootDir-relative
11
11
  * definition files whose component replacement changed (the changed file
12
12
  * itself is never listed). `systemPropsChanged` reports whether the served
13
- * system-props module moved (see `runAnalysisTrackingSystemProps` in
14
- * context.ts) — only the owning dispatch holds the before/after values, so
15
- * it travels with the decision rather than being re-derived per environment.
13
+ * system-props module moved (see the transaction-spanning compare in
14
+ * hmr.ts `analyzeChangedFile`) — only the owning dispatch holds the
15
+ * before/after values, so it travels with the decision rather than being
16
+ * re-derived per environment.
16
17
  * `presentationOnly` reports that the changed file's transform output is
17
18
  * byte-identical before and after the edit (style values are not part of
18
19
  * the emitted replacement — class names hash `filename::binding`): every
@@ -1 +1 @@
1
- {"version":3,"file":"hot-update-events.d.ts","sourceRoot":"","sources":["../src/hot-update-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,YAAY;IAFzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAE9D,YAA6B,YAAY,SAAK,EAAI;IAElD,0EAA0E;IAC1E,KAAK,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAUvE;IAED,wEAAwE;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAErE;IAED,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAEzD;CACF"}
1
+ {"version":3,"file":"hot-update-events.d.ts","sourceRoot":"","sources":["../src/hot-update-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,eAAe;IAGd,OAAO,CAAC,QAAQ,CAAC,YAAY;IAFzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAE9D,YAA6B,YAAY,SAAK,EAAI;IAElD,0EAA0E;IAC1E,KAAK,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAUvE;IAED,wEAAwE;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAErE;IAED,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAEzD;CACF"}
package/dist/index.cjs CHANGED
@@ -117,6 +117,7 @@ async function runBuildStart(ctx, resolveSpecifier, emitAsset) {
117
117
  const localFileCount = fileEntries.length;
118
118
  const packageSpecifiers = (0, _animus_ui_extract_pipeline.extractSystemFilePackages)(ctx.resolvedSystemPath);
119
119
  ctx.externalSourceEntries.clear();
120
+ ctx.externalKeyframesScanEntries.clear();
120
121
  const collected = await (0, _animus_ui_extract_pipeline.collectExternalPackageSources)({
121
122
  specifiers: packageSpecifiers,
122
123
  resolveSpecifier,
@@ -134,10 +135,11 @@ async function runBuildStart(ctx, resolveSpecifier, emitAsset) {
134
135
  });
135
136
  ctx.packageMap = collected.packageMap;
136
137
  ctx.externalPackageOutcomes = collected.outcomes;
137
- ctx.externalDirOwners = collected.dirOwners;
138
+ ctx.externalDirOwners = (0, _animus_ui_extract_pipeline.firstOwners)(collected.dirOwnerSets);
138
139
  ctx.externalFileOwners = collected.fileOwners;
139
140
  ctx.enforceIncludeResolution();
140
141
  for (const [specifier, srcEntry] of collected.sourceEntries) ctx.externalSourceEntries.set(specifier, srcEntry);
142
+ for (const [specifier, scanEntry] of collected.keyframesScanEntries) ctx.externalKeyframesScanEntries.set(specifier, scanEntry);
141
143
  for (const entry of collected.entries) {
142
144
  const hash = !ctx.isProd ? (0, _animus_ui_extract_pipeline.contentHash)(entry.source) : void 0;
143
145
  fileEntries.push({
@@ -396,34 +398,6 @@ function systemPropsModuleSource(ctx) {
396
398
  return ctx.systemPropsModuleMemo ??= generateSystemPropsModule(ctx);
397
399
  }
398
400
  /**
399
- * Run project analysis and report whether the served system-props module
400
- * CHANGED.
401
- *
402
- * The module is imported by every module that renders a system prop, so
403
- * re-delivering it pushes an update through all of them; every analysis
404
- * republishes its inputs whether or not they moved, so a new analysis is not
405
- * itself an admissible trigger (openspec: vite-extraction-plugin, "System prop
406
- * map HMR invalidation").
407
- *
408
- * The comparison is over the GENERATED MODULE, not over the prop map alone.
409
- * The map is one of four inputs, and they move independently: widening a
410
- * component's `.system({ ... })` opt-in adds a `dynamicPropConfig` entry while
411
- * minting no new utility class, so a map-only comparison reports "unchanged"
412
- * and the client is left with a config missing the new prop — permanently,
413
- * since Vite keeps serving the module's cached transform result across full
414
- * page reloads. Comparing the artifact itself needs no argument about which
415
- * inputs are volatile this month.
416
- *
417
- * A free function rather than a method: the comparison must run for real
418
- * against any context the caller holds, including the behavioral test doubles
419
- * that stand in for the engine.
420
- */
421
- function runAnalysisTrackingSystemProps(ctx, fileEntries) {
422
- const before = systemPropsModuleSource(ctx);
423
- ctx.runAnalysis(fileEntries);
424
- return systemPropsModuleSource(ctx) !== before;
425
- }
426
- /**
427
401
  * Drop a deleted (or renamed-away) file from the dev file cache so its
428
402
  * last-known source stops riding along as a ghost entry on every later
429
403
  * re-analysis. Both key forms are tried: the plain rootDir-relative path, and
@@ -469,15 +443,29 @@ var PluginContext = class {
469
443
  this.transformOutputHashes.set(relativePath, (0, _animus_ui_extract_pipeline.contentHash)(code));
470
444
  }
471
445
  /**
446
+ * The consumer system's OWN keyframes collections, captured at load time
447
+ * before any external merge touches `system.keyframesJson`. Every
448
+ * `applyExternalKeyframes` merge starts from this baseline, so repeated
449
+ * merges (a --watch rebuild's loadSystem + rediscovery, a geological
450
+ * reset) never compound prior external state — a removed include's
451
+ * keyframes disappear with it.
452
+ */
453
+ consumerKeyframesJson = null;
454
+ /**
472
455
  * Merge `Keyframes` collections from discovered external package entries
473
456
  * into the system's collections (keyframes-only carve-out — the consumer
474
457
  * system stays the singular config authority). Runs after buildStart
475
458
  * discovery AND after every geological-reset system reload, since a reload
476
- * rebuilds `this.system` from the consumer entry alone.
459
+ * rebuilds `this.system` from the consumer entry alone. Merges from the
460
+ * consumer-only baseline, never from the previously merged value.
477
461
  */
478
462
  applyExternalKeyframes() {
479
- if (this.externalSourceEntries.size === 0) return;
480
- const merge = (0, _animus_ui_extract_pipeline.mergeExternalKeyframes)((entry, root) => this.engineApi().scanKeyframesExports(entry, root), this.system.keyframesJson, this.externalSourceEntries.values(), this.rootDir);
463
+ if (this.externalKeyframesScanEntries.size === 0) {
464
+ this.system.keyframesJson = this.consumerKeyframesJson;
465
+ this.externalKeyframesDiagnostics = [];
466
+ return;
467
+ }
468
+ const merge = (0, _animus_ui_extract_pipeline.mergeExternalKeyframes)((entry, root) => this.engineApi().scanKeyframesExports(entry, root), this.consumerKeyframesJson, this.externalKeyframesScanEntries.values(), this.rootDir);
481
469
  this.system.keyframesJson = merge.keyframesJson;
482
470
  this.externalKeyframesDiagnostics = merge.diagnostics;
483
471
  }
@@ -490,7 +478,9 @@ var PluginContext = class {
490
478
  storedTransformsSource = "{}";
491
479
  systemPropsModuleMemo = null;
492
480
  fileCache = /* @__PURE__ */ new Map();
481
+ rawExtensionFallbacks = /* @__PURE__ */ new Set();
493
482
  hotUpdateEvents = new HotUpdateEvents();
483
+ pendingReloadTimer = null;
494
484
  packageMap = {};
495
485
  base = "/";
496
486
  assetUrlBySpecifier = /* @__PURE__ */ new Map();
@@ -500,6 +490,7 @@ var PluginContext = class {
500
490
  externalDirOwners = {};
501
491
  externalFileOwners = {};
502
492
  externalSourceEntries = /* @__PURE__ */ new Map();
493
+ externalKeyframesScanEntries = /* @__PURE__ */ new Map();
503
494
  externalPackageOutcomes = [];
504
495
  devServer;
505
496
  resolvedSystemPath = null;
@@ -589,6 +580,7 @@ var PluginContext = class {
589
580
  this.systemDependencyKeys = keys;
590
581
  this.systemDependencyPaths = deps;
591
582
  this.registerSystemWatchPaths();
583
+ this.consumerKeyframesJson = this.system.keyframesJson;
592
584
  this.applyExternalKeyframes();
593
585
  } catch (e) {
594
586
  if (this.options.strict) throw new Error(`[animus-extract] Failed to load system from ${this.resolvedSystemPath}: ${e}`, { cause: e });
@@ -598,7 +590,12 @@ var PluginContext = class {
598
590
  }
599
591
  /**
600
592
  * Run project analysis via the shared `runProjectAnalysis` and update
601
- * all manifest-derived state.
593
+ * all manifest-derived state. Returns whether the analysis PUBLISHED —
594
+ * `false` means the previous manifest is still current, and callers that
595
+ * advanced the file cache for this attempt must roll that entry back or
596
+ * the content-hash gate will suppress the same-content retry forever
597
+ * (openspec: dev-transform-coherence, "Failed analyses do not suppress
598
+ * equal-content retries"). Strict mode still throws.
602
599
  */
603
600
  runAnalysis(fileEntries) {
604
601
  try {
@@ -629,11 +626,12 @@ var PluginContext = class {
629
626
  } catch (e) {
630
627
  if (this.options.strict) throw new Error(`[animus-extract] analyzeProject failed: ${e}`, { cause: e });
631
628
  console.warn("[animus-extract] analyzeProject failed:", e);
632
- return;
629
+ return false;
633
630
  }
634
631
  this.systemPropsModuleMemo = generateSystemPropsModule(this);
635
632
  this.applyAssetSubstitutions();
636
633
  this.enforceExternalTokenContracts();
634
+ return true;
637
635
  }
638
636
  /** Base-prefixed dev URL for an absolute file (Vite mounts /@fs under base). */
639
637
  devFsUrl(absPath) {
@@ -781,7 +779,9 @@ var PluginContext = class {
781
779
  const mod = server.moduleGraph.getModuleById(moduleId);
782
780
  if (mod) server.moduleGraph.invalidateModule(mod);
783
781
  }
784
- setTimeout(() => {
782
+ if (this.pendingReloadTimer) return;
783
+ this.pendingReloadTimer = setTimeout(() => {
784
+ this.pendingReloadTimer = null;
785
785
  this.devServer?.hot?.send({ type: "full-reload" });
786
786
  }, 100);
787
787
  }
@@ -849,6 +849,212 @@ var PluginContext = class {
849
849
  }
850
850
  };
851
851
  //#endregion
852
+ //#region src/module-invalidation.ts
853
+ /**
854
+ * Evict every module node the dev server holds for each file — every
855
+ * environment graph (client, ssr, and any custom environment), by-file node
856
+ * sets (covers query-suffixed variants), plus the absolute-path id lookup.
857
+ * Falls back to the mixed compat graph for hosts without environment
858
+ * graphs. Returns the number of nodes invalidated.
859
+ *
860
+ * Completeness here is load-bearing: the file-plan diff is the ONLY
861
+ * invalidation candidate source, so a node shape this enumeration misses
862
+ * stays stale for the life of the server (openspec: dev-transform-coherence,
863
+ * "Client and SSR nodes are both evicted").
864
+ */
865
+ function invalidateFileModules(ctx, relPaths) {
866
+ const server = ctx.devServer;
867
+ if (!server || relPaths.length === 0) return 0;
868
+ const graphs = server.environments ? Object.values(server.environments).map((env) => env?.moduleGraph).filter(Boolean) : [server.moduleGraph].filter(Boolean);
869
+ let total = 0;
870
+ for (const relPath of relPaths) {
871
+ const absPath = (0, path.resolve)(ctx.rootDir, relPath);
872
+ let fileCount = 0;
873
+ for (const graph of graphs) {
874
+ const nodes = /* @__PURE__ */ new Set();
875
+ const byFile = graph.getModulesByFile?.(absPath);
876
+ if (byFile) for (const mod of byFile) nodes.add(mod);
877
+ const byId = graph.getModuleById?.(absPath);
878
+ if (byId) nodes.add(byId);
879
+ for (const mod of nodes) {
880
+ graph.invalidateModule(mod);
881
+ fileCount++;
882
+ }
883
+ }
884
+ if (fileCount > 0) ctx.log(`HMR invalidate: ${relPath} (plan changed, ${fileCount} nodes)`);
885
+ total += fileCount;
886
+ }
887
+ return total;
888
+ }
889
+ //#endregion
890
+ //#region src/rediscovery.ts
891
+ /** The current manifest's unresolved-parent drops, parsed from diagnostics
892
+ * (shared Rust-mirror matcher — the regex lives in manifest-diagnostics). */
893
+ function unresolvedParentDrops(ctx) {
894
+ const drops = [];
895
+ for (const d of ctx.storedManifest?.diagnostics ?? []) {
896
+ const parent = (0, _animus_ui_extract_pipeline.unresolvedParentName)(d);
897
+ if (parent !== null) drops.push({
898
+ file: String(d.file ?? ""),
899
+ component: String(d.component ?? ""),
900
+ parent
901
+ });
902
+ }
903
+ return drops;
904
+ }
905
+ const EMPTY_DROP_FILES = /* @__PURE__ */ new Set();
906
+ const dropFilesByManifest = /* @__PURE__ */ new WeakMap();
907
+ /** Files carrying an unresolved-parent drop in the CURRENT manifest —
908
+ * derived once per manifest publication, then a set lookup. */
909
+ function unresolvedDropFiles(ctx) {
910
+ const manifest = ctx.storedManifest;
911
+ if (!manifest) return EMPTY_DROP_FILES;
912
+ let files = dropFilesByManifest.get(manifest);
913
+ if (!files) {
914
+ const derived = /* @__PURE__ */ new Set();
915
+ for (const d of manifest.diagnostics ?? []) if ((0, _animus_ui_extract_pipeline.isUnresolvedParentDrop)(d)) derived.add(String(d.file ?? ""));
916
+ files = derived;
917
+ dropFilesByManifest.set(manifest, files);
918
+ }
919
+ return files;
920
+ }
921
+ /**
922
+ * Reconcile the discoverable on-disk source universe before an
923
+ * unresolved-parent result is acted on (openspec: dev-transform-coherence,
924
+ * "Source-universe reconciliation precedes unresolved-parent fallbacks").
925
+ *
926
+ * Drop-triggered: runs only when the current manifest reports `chain
927
+ * dropped: could not resolve parent component`. One discovery walk (the
928
+ * same walk and policy the server start uses) folds every eligible on-disk
929
+ * file the cache does not know, then one re-analysis resolves the whole
930
+ * extension graph — dependency depth never requires iteration depth, so the
931
+ * loop re-enters only when a re-analysis leaves NEW drops and the previous
932
+ * walk actually folded something (a changed discovery domain). The
933
+ * iteration cap is containment, not semantics.
934
+ *
935
+ * Synchronous by design — it runs inside the sync `transform` hook. `.mdx`
936
+ * sources need async preprocessing and are not folded here (they ingest on
937
+ * their first watcher edit).
938
+ *
939
+ * Returns whether any re-analysis ran (callers diff plans across the WHOLE
940
+ * transaction, so a fold-and-reanalyze is invisible to them beyond the
941
+ * final manifest).
942
+ */
943
+ function stabilizeSourceUniverse(ctx) {
944
+ if (ctx.isProd) return false;
945
+ let reanalyzed = false;
946
+ for (let iteration = 0; iteration < 3; iteration++) {
947
+ if (unresolvedDropFiles(ctx).size === 0) {
948
+ warnedVerdicts.get(ctx)?.clear();
949
+ return reanalyzed;
950
+ }
951
+ const drops = unresolvedParentDrops(ctx);
952
+ const dropKey = drops.map((d) => `${d.file}\0${d.component}\0${d.parent}`).sort().join("\n");
953
+ const memo = barrenWalkMemos.get(ctx);
954
+ if (memo && memo.dropKey === dropKey && memo.cacheSize === ctx.fileCache.size) return reanalyzed;
955
+ const folded = foldUndiscoveredFiles(ctx);
956
+ if (folded === 0) {
957
+ barrenWalkMemos.set(ctx, {
958
+ dropKey,
959
+ cacheSize: ctx.fileCache.size
960
+ });
961
+ warnInadmissibleParents(ctx, drops);
962
+ return reanalyzed;
963
+ }
964
+ barrenWalkMemos.delete(ctx);
965
+ ctx.log(`rediscovery: folded ${folded} on-disk file(s) after unresolved-parent drop`);
966
+ reanalyzed = true;
967
+ if (ctx.runAnalysis(buildFileEntriesFromCache(ctx.fileCache)) === false) return reanalyzed;
968
+ }
969
+ if (unresolvedDropFiles(ctx).size > 0) ctx.warn("source-universe rediscovery did not stabilize after bounded retries");
970
+ return reanalyzed;
971
+ }
972
+ /**
973
+ * One discovery walk over the project root with the server-start policy;
974
+ * folds every eligible file `fileCache` does not hold. Returns the fold
975
+ * count.
976
+ */
977
+ function foldUndiscoveredFiles(ctx) {
978
+ const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
979
+ const filePaths = (0, _animus_ui_extract_pipeline.discoverFiles)(ctx.rootDir, ctx.rootDir, excludePatterns, ctx.extensionsSet);
980
+ let folded = 0;
981
+ for (const filePath of filePaths) {
982
+ if ((0, path.extname)(filePath) === ".mdx") continue;
983
+ const relPath = (0, path.relative)(ctx.rootDir, filePath);
984
+ if (ctx.fileCache.has(relPath)) continue;
985
+ let source;
986
+ try {
987
+ source = (0, fs.readFileSync)(filePath, "utf-8");
988
+ } catch {
989
+ continue;
990
+ }
991
+ ctx.fileCache.set(relPath, {
992
+ hash: (0, _animus_ui_extract_pipeline.contentHash)(source),
993
+ source
994
+ });
995
+ folded++;
996
+ }
997
+ return folded;
998
+ }
999
+ /** Barren-walk memo per context: the drop tuple-set and cache size at the
1000
+ * last walk that folded nothing (see stabilizeSourceUniverse). */
1001
+ const barrenWalkMemos = /* @__PURE__ */ new WeakMap();
1002
+ /** Per-context (file, parent, condition) verdicts already warned — each
1003
+ * condition warns once; cleared when the drops disappear. */
1004
+ const warnedVerdicts = /* @__PURE__ */ new WeakMap();
1005
+ /** The narrow probe set is deliberate — NOT ctx.extensionsSet (conscious
1006
+ * no-behavior-change choice: the teaching probe resolves what it always did). */
1007
+ const PARENT_PROBE_EXTENSIONS = new Set([
1008
+ ".tsx",
1009
+ ".ts",
1010
+ ".jsx",
1011
+ ".js"
1012
+ ]);
1013
+ /**
1014
+ * The teaching half for parents that RESOLVE on disk but cannot be admitted
1015
+ * (excluded by configuration, unsupported source type, outside the root).
1016
+ * Resolution is a best-effort scan of the consumer's own import statements
1017
+ * for the parent binding — enough for the relative-specifier case a
1018
+ * developer actually hits; a parent this scan cannot resolve keeps the
1019
+ * engine's own diagnostic.
1020
+ */
1021
+ function warnInadmissibleParents(ctx, drops) {
1022
+ const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
1023
+ let warned = warnedVerdicts.get(ctx);
1024
+ if (!warned) {
1025
+ warned = /* @__PURE__ */ new Set();
1026
+ warnedVerdicts.set(ctx, warned);
1027
+ }
1028
+ const active = new Set(drops.map((d) => `${d.file}\0${d.parent}`));
1029
+ for (const key of [...warned]) if (!active.has(key.slice(0, key.lastIndexOf("\0")))) warned.delete(key);
1030
+ for (const drop of drops) {
1031
+ const consumer = ctx.fileCache.get(drop.file);
1032
+ if (!consumer) continue;
1033
+ const specifier = importSpecifierFor(consumer.source, drop.parent);
1034
+ if (!specifier || !specifier.startsWith(".")) continue;
1035
+ const resolved = (0, _animus_ui_extract_pipeline.resolveAbsolutePathSpecifier)((0, path.resolve)(ctx.rootDir, (0, path.dirname)(drop.file), specifier), PARENT_PROBE_EXTENSIONS);
1036
+ if (!resolved) continue;
1037
+ const relPath = (0, path.relative)(ctx.rootDir, resolved);
1038
+ const excludedBy = excludePatterns.find((pattern) => resolved.includes(pattern) || relPath.includes(pattern));
1039
+ const reason = excludedBy ? `which is excluded by pattern '${excludedBy}'. Include that file in the extraction universe or remove this extension.` : !(0, _animus_ui_extract_pipeline.isPathWithinRoot)(ctx.rootDir, resolved) ? "outside the extraction root. Declare its package in the system includes or move it under the project root." : !ctx.extensionsSet.has((0, path.extname)(resolved)) ? `whose extension is not in the configured source extensions.` : null;
1040
+ if (reason === null) continue;
1041
+ const verdictKey = `${drop.file}\0${drop.parent}\0${reason}`;
1042
+ if (warned.has(verdictKey)) continue;
1043
+ warned.add(verdictKey);
1044
+ ctx.warn(`cannot extract '${drop.component}': parent '${drop.parent}' resolves to ${relPath}, ${reason}`);
1045
+ }
1046
+ }
1047
+ /** The module specifier a named import binds `binding` from, if scannable. */
1048
+ function importSpecifierFor(source, binding) {
1049
+ const importRe = /import\s+([^;]+?)\s+from\s+['"]([^'"]+)['"]/g;
1050
+ let match;
1051
+ while ((match = importRe.exec(source)) !== null) {
1052
+ const clause = match[1];
1053
+ if (new RegExp(`[{,\\s]${binding}[,\\s}]|^${binding}$`).test(clause)) return match[2];
1054
+ }
1055
+ return null;
1056
+ }
1057
+ //#endregion
852
1058
  //#region src/transform.ts
853
1059
  /**
854
1060
  * The dev-mode bridge prepend, shared with the hot-update gate so both
@@ -863,6 +1069,27 @@ function applyDevBridgeImport(code) {
863
1069
  return prologue + `import '${VIRTUAL_BRIDGE_ID}';\n` + code.slice(prologue.length);
864
1070
  }
865
1071
  /**
1072
+ * Live raw-fallback files reachable from `relPath`'s components through
1073
+ * extension provenance — the transitive closure, so a grandparent's serve is
1074
+ * withheld exactly like a direct parent's.
1075
+ */
1076
+ function rawFallbackDescendants(ctx, relPath) {
1077
+ const conflicted = /* @__PURE__ */ new Set();
1078
+ const queue = [...ctx.storedManifest?.files?.[relPath] ?? []];
1079
+ const seen = new Set(queue);
1080
+ while (queue.length > 0) {
1081
+ const id = queue.shift();
1082
+ for (const childId of ctx.reverseProvenance[id] ?? []) {
1083
+ if (seen.has(childId)) continue;
1084
+ seen.add(childId);
1085
+ queue.push(childId);
1086
+ const childFile = ctx.storedManifest?.components?.[childId]?.file;
1087
+ if (childFile && childFile !== relPath && ctx.rawExtensionFallbacks.has(childFile)) conflicted.add(childFile);
1088
+ }
1089
+ }
1090
+ return [...conflicted].sort();
1091
+ }
1092
+ /**
866
1093
  * transform: replace builder chains with `createComponent()` calls using
867
1094
  * the pre-built manifest; detect files created after buildStart and fold
868
1095
  * them into the analysis.
@@ -875,17 +1102,17 @@ function applyDevBridgeImport(code) {
875
1102
  function transformSource(ctx, code, id) {
876
1103
  if (!ctx.storedManifest) return null;
877
1104
  if (id.startsWith("\0") || id.startsWith("virtual:animus/")) return null;
878
- const isExternalPkg = ctx.externalPackageDirs.some((dir) => id.startsWith(dir + path.sep) || id === dir);
1105
+ const isExternalPkg = ctx.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, id));
879
1106
  const relativePath = (0, path.relative)(ctx.rootDir, id);
880
1107
  if (!isExternalPkg) {
881
1108
  if (!/\.[jt]sx?$/.test(id)) return null;
882
1109
  if (id.includes("node_modules")) return null;
883
- if (relativePath.startsWith("..") || (0, path.isAbsolute)(relativePath)) return null;
1110
+ if (!(0, _animus_ui_extract_pipeline.isPathWithinRoot)(ctx.rootDir, id)) return null;
884
1111
  }
885
1112
  if (!ctx.storedManifest.files?.[relativePath]?.length) {
886
1113
  if (!ctx.isProd && !ctx.fileCache.has(relativePath)) {
887
1114
  if (isExternalPkg) {
888
- const owner = Object.entries(ctx.externalDirOwners).find(([dir]) => id === dir || id.startsWith(dir + path.sep));
1115
+ const owner = Object.entries(ctx.externalDirOwners).find(([dir]) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, id));
889
1116
  if (owner) ctx.externalFileOwners[relativePath] = owner[1];
890
1117
  }
891
1118
  const hash = (0, _animus_ui_extract_pipeline.contentHash)(code);
@@ -893,13 +1120,36 @@ function transformSource(ctx, code, id) {
893
1120
  hash,
894
1121
  source: code
895
1122
  });
1123
+ const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
896
1124
  const fileEntries = buildFileEntriesFromCache(ctx.fileCache, relativePath);
897
- ctx.runAnalysis(fileEntries);
898
- const compCount = ctx.storedManifest.files?.[relativePath]?.length ?? 0;
899
- ctx.info(`New file detected: ${relativePath} — ${compCount ? `${compCount} components extracted` : "no components"}`);
1125
+ let analysisOk = false;
1126
+ try {
1127
+ analysisOk = ctx.runAnalysis(fileEntries) !== false;
1128
+ } finally {
1129
+ if (!analysisOk) ctx.fileCache.delete(relativePath);
1130
+ }
1131
+ if (analysisOk) {
1132
+ stabilizeSourceUniverse(ctx);
1133
+ invalidateFileModules(ctx, (0, _animus_ui_extract_pipeline.diffFilePlans)(prevPlans, (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest), { exclude: relativePath }));
1134
+ const compCount = ctx.storedManifest.files?.[relativePath]?.length ?? 0;
1135
+ ctx.info(`New file detected: ${relativePath} — ${compCount ? `${compCount} components extracted` : "no components"}`);
1136
+ ctx.invalidateExtractedModules();
1137
+ }
1138
+ }
1139
+ if (!ctx.storedManifest.files?.[relativePath]?.length) {
1140
+ if (!ctx.isProd) if (unresolvedDropFiles(ctx).has(relativePath)) ctx.rawExtensionFallbacks.add(relativePath);
1141
+ else ctx.rawExtensionFallbacks.delete(relativePath);
1142
+ return null;
1143
+ }
1144
+ }
1145
+ if (!ctx.isProd && ctx.rawExtensionFallbacks.size > 0) {
1146
+ const conflicted = rawFallbackDescendants(ctx, relativePath);
1147
+ if (conflicted.length > 0) {
1148
+ invalidateFileModules(ctx, conflicted);
900
1149
  ctx.invalidateExtractedModules();
1150
+ for (const file of conflicted) ctx.rawExtensionFallbacks.delete(file);
1151
+ throw new Error(`ANIMUS_COMPOSITION_RECOVERING: '${relativePath}' extracted while extension consumer(s) ${conflicted.join(", ")} are still serving the runtime fallback — recovering via reload; this resolves on the next request.`);
901
1152
  }
902
- if (!ctx.storedManifest.files?.[relativePath]?.length) return null;
903
1153
  }
904
1154
  try {
905
1155
  const { transformFile } = ctx.engineApi();
@@ -913,6 +1163,7 @@ function transformSource(ctx, code, id) {
913
1163
  if (!ctx.isProd) {
914
1164
  outputCode = applyDevBridgeImport(result.code);
915
1165
  ctx.recordTransformOutput(relativePath, outputCode);
1166
+ ctx.rawExtensionFallbacks.delete(relativePath);
916
1167
  }
917
1168
  return {
918
1169
  code: outputCode,
@@ -957,7 +1208,6 @@ async function handleHotUpdate(ctx, environment, { type, file, timestamp, module
957
1208
  }
958
1209
  return [];
959
1210
  }
960
- if (type === "create") return;
961
1211
  if (type === "delete") {
962
1212
  if (ownsEvent) pruneDeletedFile(ctx, absFile);
963
1213
  return;
@@ -1028,7 +1278,8 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
1028
1278
  const ext = (0, path.extname)(file);
1029
1279
  if (!ctx.extensionsSet.has(ext)) return { kind: "ignored" };
1030
1280
  const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
1031
- if (!ctx.externalPackageDirs.some((dir) => file.startsWith(dir + path.sep) || file === dir) && excludePatterns.some((pattern) => file.includes(pattern) || (0, path.relative)(ctx.rootDir, file).includes(pattern))) return { kind: "ignored" };
1281
+ const isExternalPkg = ctx.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, file));
1282
+ if (!isExternalPkg && excludePatterns.some((pattern) => file.includes(pattern) || (0, path.relative)(ctx.rootDir, file).includes(pattern))) return { kind: "ignored" };
1032
1283
  const relPath = (0, path.relative)(ctx.rootDir, absFile);
1033
1284
  let source;
1034
1285
  try {
@@ -1056,13 +1307,20 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
1056
1307
  ctx.log(`HMR skip: ${scannerRelPath} (unchanged)`);
1057
1308
  return { kind: "unchanged" };
1058
1309
  }
1310
+ if (isExternalPkg && !ctx.externalFileOwners[scannerRelPath]) {
1311
+ const owner = Object.entries(ctx.externalDirOwners).find(([dir]) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, absFile));
1312
+ if (owner) ctx.externalFileOwners[scannerRelPath] = owner[1];
1313
+ }
1059
1314
  ctx.fileCache.set(scannerRelPath, {
1060
1315
  hash,
1061
1316
  source
1062
1317
  });
1318
+ const restoreEntry = () => {
1319
+ if (cached) ctx.fileCache.set(scannerRelPath, cached);
1320
+ else ctx.fileCache.delete(scannerRelPath);
1321
+ };
1063
1322
  const hmrStart = performance.now();
1064
- const prevReplacements = /* @__PURE__ */ new Map();
1065
- if (ctx.storedManifest?.components) for (const [id, desc] of Object.entries(ctx.storedManifest.components)) prevReplacements.set(id, desc.replacement ?? "");
1323
+ const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
1066
1324
  const directComponentIds = ctx.storedManifest?.files?.[scannerRelPath] ?? [];
1067
1325
  const invalidatedIds = new Set(directComponentIds);
1068
1326
  const queue = [...directComponentIds];
@@ -1078,17 +1336,23 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
1078
1336
  }
1079
1337
  if (ctx.verbose && invalidatedIds.size > directComponentIds.length) ctx.log(`HMR: ${directComponentIds.length} direct + ${invalidatedIds.size - directComponentIds.length} transitive components invalidated`);
1080
1338
  const analysisStart = performance.now();
1081
- const systemPropsChanged = runAnalysisTrackingSystemProps(ctx, buildFileEntriesFromCache(ctx.fileCache, relPath));
1082
- const analysisMs = Math.round(performance.now() - analysisStart);
1083
- const staleDefinitionFiles = [];
1084
- if (ctx.storedManifest?.components) {
1085
- const staleFiles = /* @__PURE__ */ new Set();
1086
- for (const [id, desc] of Object.entries(ctx.storedManifest.components)) if ((desc.replacement ?? "") !== (prevReplacements.get(id) ?? "")) staleFiles.add(desc.file);
1087
- for (const defFile of staleFiles) {
1088
- if ((0, path.resolve)(ctx.rootDir, defFile) === absFile) continue;
1089
- staleDefinitionFiles.push(defFile);
1090
- }
1339
+ const fileEntries = buildFileEntriesFromCache(ctx.fileCache, relPath);
1340
+ const systemPropsBefore = systemPropsModuleSource(ctx);
1341
+ let analysisOk;
1342
+ try {
1343
+ analysisOk = ctx.runAnalysis(fileEntries) !== false;
1344
+ } catch (e) {
1345
+ restoreEntry();
1346
+ throw e;
1091
1347
  }
1348
+ if (!analysisOk) {
1349
+ restoreEntry();
1350
+ return { kind: "ignored" };
1351
+ }
1352
+ stabilizeSourceUniverse(ctx);
1353
+ const systemPropsChanged = systemPropsModuleSource(ctx) !== systemPropsBefore;
1354
+ const analysisMs = Math.round(performance.now() - analysisStart);
1355
+ const staleDefinitionFiles = (0, _animus_ui_extract_pipeline.diffFilePlans)(prevPlans, (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest)).filter((defFile) => (0, path.resolve)(ctx.rootDir, defFile) !== absFile);
1092
1356
  const presentationOnly = isPresentationOnlyEdit(ctx, scannerRelPath, source);
1093
1357
  const hmrMs = Math.round(performance.now() - hmrStart);
1094
1358
  ctx.log(`HMR update: ${relPath} — analysis ${analysisMs}ms, total ${hmrMs}ms${presentationOnly ? " (presentation-only)" : ""}`);
@@ -1136,8 +1400,10 @@ function isPresentationOnlyEdit(ctx, scannerRelPath, source) {
1136
1400
  */
1137
1401
  function pruneDeletedFile(ctx, absFile) {
1138
1402
  if (!pruneFileCache(ctx.fileCache, ctx.rootDir, absFile)) return;
1139
- ctx.runAnalysis(buildFileEntriesFromCache(ctx.fileCache));
1403
+ const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
1404
+ const ok = ctx.runAnalysis(buildFileEntriesFromCache(ctx.fileCache)) !== false;
1140
1405
  ctx.log(`Deleted file pruned: ${(0, path.relative)(ctx.rootDir, absFile)}`);
1406
+ if (ok) invalidateFileModules(ctx, (0, _animus_ui_extract_pipeline.diffFilePlans)(prevPlans, (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest)));
1141
1407
  ctx.invalidateExtractedModules();
1142
1408
  }
1143
1409
  /**
@@ -0,0 +1,15 @@
1
+ import type { PluginContext } from './context';
2
+ /**
3
+ * Evict every module node the dev server holds for each file — every
4
+ * environment graph (client, ssr, and any custom environment), by-file node
5
+ * sets (covers query-suffixed variants), plus the absolute-path id lookup.
6
+ * Falls back to the mixed compat graph for hosts without environment
7
+ * graphs. Returns the number of nodes invalidated.
8
+ *
9
+ * Completeness here is load-bearing: the file-plan diff is the ONLY
10
+ * invalidation candidate source, so a node shape this enumeration misses
11
+ * stays stale for the life of the server (openspec: dev-transform-coherence,
12
+ * "Client and SSR nodes are both evicted").
13
+ */
14
+ export declare function invalidateFileModules(ctx: PluginContext, relPaths: string[]): number;
15
+ //# sourceMappingURL=module-invalidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-invalidation.d.ts","sourceRoot":"","sources":["../src/module-invalidation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,EAClB,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,CAmCR"}
@@ -0,0 +1,40 @@
1
+ import type { PluginContext } from './context';
2
+ interface UnresolvedParentDrop {
3
+ /** rootDir-relative consumer file the diagnostic names. */
4
+ file: string;
5
+ /** The dropped child component binding. */
6
+ component: string;
7
+ /** The parent binding as written in the consumer. */
8
+ parent: string;
9
+ }
10
+ /** The current manifest's unresolved-parent drops, parsed from diagnostics
11
+ * (shared Rust-mirror matcher — the regex lives in manifest-diagnostics). */
12
+ export declare function unresolvedParentDrops(ctx: PluginContext): UnresolvedParentDrop[];
13
+ /** Files carrying an unresolved-parent drop in the CURRENT manifest —
14
+ * derived once per manifest publication, then a set lookup. */
15
+ export declare function unresolvedDropFiles(ctx: PluginContext): ReadonlySet<string>;
16
+ /**
17
+ * Reconcile the discoverable on-disk source universe before an
18
+ * unresolved-parent result is acted on (openspec: dev-transform-coherence,
19
+ * "Source-universe reconciliation precedes unresolved-parent fallbacks").
20
+ *
21
+ * Drop-triggered: runs only when the current manifest reports `chain
22
+ * dropped: could not resolve parent component`. One discovery walk (the
23
+ * same walk and policy the server start uses) folds every eligible on-disk
24
+ * file the cache does not know, then one re-analysis resolves the whole
25
+ * extension graph — dependency depth never requires iteration depth, so the
26
+ * loop re-enters only when a re-analysis leaves NEW drops and the previous
27
+ * walk actually folded something (a changed discovery domain). The
28
+ * iteration cap is containment, not semantics.
29
+ *
30
+ * Synchronous by design — it runs inside the sync `transform` hook. `.mdx`
31
+ * sources need async preprocessing and are not folded here (they ingest on
32
+ * their first watcher edit).
33
+ *
34
+ * Returns whether any re-analysis ran (callers diff plans across the WHOLE
35
+ * transaction, so a fold-and-reanalyze is invisible to them beyond the
36
+ * final manifest).
37
+ */
38
+ export declare function stabilizeSourceUniverse(ctx: PluginContext): boolean;
39
+ export {};
40
+ //# sourceMappingURL=rediscovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rediscovery.d.ts","sourceRoot":"","sources":["../src/rediscovery.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,UAAU,oBAAoB;IAC5B,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;8EAC8E;AAC9E,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,GACjB,oBAAoB,EAAE,CAcxB;AASD;gEACgE;AAChE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CAc3E;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CA6DnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CAqHpC"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzD;AAiCD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CA4LpC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animus-ui/vite-plugin",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Animus static CSS extraction Vite plugin",
5
5
  "keywords": [
6
6
  "animus",
@@ -39,7 +39,7 @@
39
39
  "compile": "tsc --noEmit"
40
40
  },
41
41
  "dependencies": {
42
- "@animus-ui/extract": "0.1.6",
42
+ "@animus-ui/extract": "0.1.8",
43
43
  "browserslist": "^4.24.0",
44
44
  "lightningcss": "^1.29.0"
45
45
  },