@animus-ui/vite-plugin 0.1.7 → 0.1.9
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-start.d.ts.map +1 -1
- package/dist/context.d.ts +21 -31
- package/dist/context.d.ts.map +1 -1
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hot-update-events.d.ts +4 -3
- package/dist/hot-update-events.d.ts.map +1 -1
- package/dist/index.cjs +353 -67
- package/dist/module-invalidation.d.ts +15 -0
- package/dist/module-invalidation.d.ts.map +1 -0
- package/dist/rediscovery.d.ts +40 -0
- package/dist/rediscovery.d.ts.map +1 -0
- package/dist/transform.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-start.d.ts","sourceRoot":"","sources":["../src/build-start.ts"],"names":[],"mappings":"
|
|
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
|
-
}>):
|
|
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
|
/**
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"
|
|
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":"
|
|
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,CA8EzC"}
|
|
@@ -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
|
|
14
|
-
*
|
|
15
|
-
* it travels with the decision rather than being
|
|
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
|
|
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.
|
|
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.
|
|
480
|
-
|
|
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,11 +590,17 @@ 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) {
|
|
601
|
+
let result;
|
|
604
602
|
try {
|
|
605
|
-
|
|
603
|
+
result = (0, _animus_ui_extract_pipeline.runProjectAnalysis)(this.engineApi, {
|
|
606
604
|
fileEntries,
|
|
607
605
|
packageMap: this.packageMap,
|
|
608
606
|
system: this.system,
|
|
@@ -618,22 +616,24 @@ var PluginContext = class {
|
|
|
618
616
|
strict: this.options.strict,
|
|
619
617
|
extraDiagnostics: this.externalKeyframesDiagnostics
|
|
620
618
|
});
|
|
621
|
-
this.storedManifest = result.manifest;
|
|
622
|
-
this.storedManifestJson = result.manifestJson;
|
|
623
|
-
this.storedSystemPropMapJson = JSON.stringify(result.manifest?.system_prop_map ?? {});
|
|
624
|
-
this.storedDynamicPropsJson = JSON.stringify(result.manifest?.dynamic_props ?? {});
|
|
625
|
-
this.reverseProvenance = result.manifest?.reverse_provenance ?? {};
|
|
626
|
-
this.storedSheets = result.manifest?.sheets ?? null;
|
|
627
|
-
this.globalCss = result.globalCss;
|
|
628
|
-
this.resolvedComponentCss = result.componentCss;
|
|
629
619
|
} catch (e) {
|
|
630
620
|
if (this.options.strict) throw new Error(`[animus-extract] analyzeProject failed: ${e}`, { cause: e });
|
|
631
621
|
console.warn("[animus-extract] analyzeProject failed:", e);
|
|
632
|
-
return;
|
|
622
|
+
return false;
|
|
633
623
|
}
|
|
624
|
+
(0, _animus_ui_extract_pipeline.assertNoErrorDiagnostics)(result.manifest?.diagnostics);
|
|
625
|
+
this.storedManifest = result.manifest;
|
|
626
|
+
this.storedManifestJson = result.manifestJson;
|
|
627
|
+
this.storedSystemPropMapJson = JSON.stringify(result.manifest?.system_prop_map ?? {});
|
|
628
|
+
this.storedDynamicPropsJson = JSON.stringify(result.manifest?.dynamic_props ?? {});
|
|
629
|
+
this.reverseProvenance = result.manifest?.reverse_provenance ?? {};
|
|
630
|
+
this.storedSheets = result.manifest?.sheets ?? null;
|
|
631
|
+
this.globalCss = result.globalCss;
|
|
632
|
+
this.resolvedComponentCss = result.componentCss;
|
|
634
633
|
this.systemPropsModuleMemo = generateSystemPropsModule(this);
|
|
635
634
|
this.applyAssetSubstitutions();
|
|
636
635
|
this.enforceExternalTokenContracts();
|
|
636
|
+
return true;
|
|
637
637
|
}
|
|
638
638
|
/** Base-prefixed dev URL for an absolute file (Vite mounts /@fs under base). */
|
|
639
639
|
devFsUrl(absPath) {
|
|
@@ -781,7 +781,9 @@ var PluginContext = class {
|
|
|
781
781
|
const mod = server.moduleGraph.getModuleById(moduleId);
|
|
782
782
|
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
783
783
|
}
|
|
784
|
-
|
|
784
|
+
if (this.pendingReloadTimer) return;
|
|
785
|
+
this.pendingReloadTimer = setTimeout(() => {
|
|
786
|
+
this.pendingReloadTimer = null;
|
|
785
787
|
this.devServer?.hot?.send({ type: "full-reload" });
|
|
786
788
|
}, 100);
|
|
787
789
|
}
|
|
@@ -849,6 +851,222 @@ var PluginContext = class {
|
|
|
849
851
|
}
|
|
850
852
|
};
|
|
851
853
|
//#endregion
|
|
854
|
+
//#region src/module-invalidation.ts
|
|
855
|
+
/**
|
|
856
|
+
* Evict every module node the dev server holds for each file — every
|
|
857
|
+
* environment graph (client, ssr, and any custom environment), by-file node
|
|
858
|
+
* sets (covers query-suffixed variants), plus the absolute-path id lookup.
|
|
859
|
+
* Falls back to the mixed compat graph for hosts without environment
|
|
860
|
+
* graphs. Returns the number of nodes invalidated.
|
|
861
|
+
*
|
|
862
|
+
* Completeness here is load-bearing: the file-plan diff is the ONLY
|
|
863
|
+
* invalidation candidate source, so a node shape this enumeration misses
|
|
864
|
+
* stays stale for the life of the server (openspec: dev-transform-coherence,
|
|
865
|
+
* "Client and SSR nodes are both evicted").
|
|
866
|
+
*/
|
|
867
|
+
function invalidateFileModules(ctx, relPaths) {
|
|
868
|
+
const server = ctx.devServer;
|
|
869
|
+
if (!server || relPaths.length === 0) return 0;
|
|
870
|
+
const graphs = server.environments ? Object.values(server.environments).map((env) => env?.moduleGraph).filter(Boolean) : [server.moduleGraph].filter(Boolean);
|
|
871
|
+
let total = 0;
|
|
872
|
+
for (const relPath of relPaths) {
|
|
873
|
+
const absPath = (0, path.resolve)(ctx.rootDir, relPath);
|
|
874
|
+
let fileCount = 0;
|
|
875
|
+
for (const graph of graphs) {
|
|
876
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
877
|
+
const byFile = graph.getModulesByFile?.(absPath);
|
|
878
|
+
if (byFile) for (const mod of byFile) nodes.add(mod);
|
|
879
|
+
const byId = graph.getModuleById?.(absPath);
|
|
880
|
+
if (byId) nodes.add(byId);
|
|
881
|
+
for (const mod of nodes) {
|
|
882
|
+
graph.invalidateModule(mod);
|
|
883
|
+
fileCount++;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (fileCount > 0) ctx.log(`HMR invalidate: ${relPath} (plan changed, ${fileCount} nodes)`);
|
|
887
|
+
total += fileCount;
|
|
888
|
+
}
|
|
889
|
+
return total;
|
|
890
|
+
}
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/rediscovery.ts
|
|
893
|
+
/** The current manifest's unresolved-parent drops, parsed from diagnostics
|
|
894
|
+
* (shared Rust-mirror matcher — the regex lives in manifest-diagnostics). */
|
|
895
|
+
function unresolvedParentDrops(ctx) {
|
|
896
|
+
const drops = [];
|
|
897
|
+
for (const d of ctx.storedManifest?.diagnostics ?? []) {
|
|
898
|
+
const parent = (0, _animus_ui_extract_pipeline.unresolvedParentName)(d);
|
|
899
|
+
if (parent !== null) drops.push({
|
|
900
|
+
file: String(d.file ?? ""),
|
|
901
|
+
component: String(d.component ?? ""),
|
|
902
|
+
parent
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
return drops;
|
|
906
|
+
}
|
|
907
|
+
const EMPTY_DROP_FILES = /* @__PURE__ */ new Set();
|
|
908
|
+
const dropFilesByManifest = /* @__PURE__ */ new WeakMap();
|
|
909
|
+
/** Files carrying an unresolved-parent drop in the CURRENT manifest —
|
|
910
|
+
* derived once per manifest publication, then a set lookup. */
|
|
911
|
+
function unresolvedDropFiles(ctx) {
|
|
912
|
+
const manifest = ctx.storedManifest;
|
|
913
|
+
if (!manifest) return EMPTY_DROP_FILES;
|
|
914
|
+
let files = dropFilesByManifest.get(manifest);
|
|
915
|
+
if (!files) {
|
|
916
|
+
const derived = /* @__PURE__ */ new Set();
|
|
917
|
+
for (const d of manifest.diagnostics ?? []) if ((0, _animus_ui_extract_pipeline.isUnresolvedParentDrop)(d)) derived.add(String(d.file ?? ""));
|
|
918
|
+
files = derived;
|
|
919
|
+
dropFilesByManifest.set(manifest, files);
|
|
920
|
+
}
|
|
921
|
+
return files;
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Reconcile the discoverable on-disk source universe before an
|
|
925
|
+
* unresolved-parent result is acted on (openspec: dev-transform-coherence,
|
|
926
|
+
* "Source-universe reconciliation precedes unresolved-parent fallbacks").
|
|
927
|
+
*
|
|
928
|
+
* Drop-triggered: runs only when the current manifest reports `chain
|
|
929
|
+
* dropped: could not resolve parent component`. One discovery walk (the
|
|
930
|
+
* same walk and policy the server start uses) folds every eligible on-disk
|
|
931
|
+
* file the cache does not know, then one re-analysis resolves the whole
|
|
932
|
+
* extension graph — dependency depth never requires iteration depth, so the
|
|
933
|
+
* loop re-enters only when a re-analysis leaves NEW drops and the previous
|
|
934
|
+
* walk actually folded something (a changed discovery domain). The
|
|
935
|
+
* iteration cap is containment, not semantics.
|
|
936
|
+
*
|
|
937
|
+
* Synchronous by design — it runs inside the sync `transform` hook. `.mdx`
|
|
938
|
+
* sources need async preprocessing and are not folded here (they ingest on
|
|
939
|
+
* their first watcher edit).
|
|
940
|
+
*
|
|
941
|
+
* Returns whether any re-analysis ran (callers diff plans across the WHOLE
|
|
942
|
+
* transaction, so a fold-and-reanalyze is invisible to them beyond the
|
|
943
|
+
* final manifest).
|
|
944
|
+
*/
|
|
945
|
+
function stabilizeSourceUniverse(ctx) {
|
|
946
|
+
if (ctx.isProd) return false;
|
|
947
|
+
let reanalyzed = false;
|
|
948
|
+
for (let iteration = 0; iteration < 3; iteration++) {
|
|
949
|
+
if (unresolvedDropFiles(ctx).size === 0) {
|
|
950
|
+
warnedVerdicts.get(ctx)?.clear();
|
|
951
|
+
return reanalyzed;
|
|
952
|
+
}
|
|
953
|
+
const drops = unresolvedParentDrops(ctx);
|
|
954
|
+
const dropKey = drops.map((d) => `${d.file}\0${d.component}\0${d.parent}`).sort().join("\n");
|
|
955
|
+
const memo = barrenWalkMemos.get(ctx);
|
|
956
|
+
if (memo && memo.dropKey === dropKey && memo.cacheSize === ctx.fileCache.size) return reanalyzed;
|
|
957
|
+
const folded = foldUndiscoveredFiles(ctx);
|
|
958
|
+
if (folded.length === 0) {
|
|
959
|
+
barrenWalkMemos.set(ctx, {
|
|
960
|
+
dropKey,
|
|
961
|
+
cacheSize: ctx.fileCache.size
|
|
962
|
+
});
|
|
963
|
+
warnInadmissibleParents(ctx, drops);
|
|
964
|
+
return reanalyzed;
|
|
965
|
+
}
|
|
966
|
+
barrenWalkMemos.delete(ctx);
|
|
967
|
+
ctx.log(`rediscovery: folded ${folded.length} on-disk file(s) after unresolved-parent drop`);
|
|
968
|
+
reanalyzed = true;
|
|
969
|
+
let published = false;
|
|
970
|
+
try {
|
|
971
|
+
published = ctx.runAnalysis(buildFileEntriesFromCache(ctx.fileCache)) !== false;
|
|
972
|
+
} finally {
|
|
973
|
+
if (!published) for (const key of folded) ctx.fileCache.delete(key);
|
|
974
|
+
}
|
|
975
|
+
if (!published) return reanalyzed;
|
|
976
|
+
}
|
|
977
|
+
if (unresolvedDropFiles(ctx).size > 0) ctx.warn("source-universe rediscovery did not stabilize after bounded retries");
|
|
978
|
+
return reanalyzed;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* One discovery walk over the project root with the server-start policy;
|
|
982
|
+
* folds every eligible file `fileCache` does not hold. Returns the fold
|
|
983
|
+
* count.
|
|
984
|
+
*/
|
|
985
|
+
/** Returns the cache keys this fold ADDED, so a failed analysis can roll
|
|
986
|
+
* them back — `runAnalysis` requires callers that advanced the file cache to
|
|
987
|
+
* restore it, or the content-hash gate suppresses the equal-content retry
|
|
988
|
+
* forever. */
|
|
989
|
+
function foldUndiscoveredFiles(ctx) {
|
|
990
|
+
const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
|
|
991
|
+
const filePaths = (0, _animus_ui_extract_pipeline.discoverFiles)(ctx.rootDir, ctx.rootDir, excludePatterns, ctx.extensionsSet);
|
|
992
|
+
const folded = [];
|
|
993
|
+
for (const filePath of filePaths) {
|
|
994
|
+
if ((0, path.extname)(filePath) === ".mdx") continue;
|
|
995
|
+
const relPath = (0, path.relative)(ctx.rootDir, filePath);
|
|
996
|
+
if (ctx.fileCache.has(relPath)) continue;
|
|
997
|
+
let source;
|
|
998
|
+
try {
|
|
999
|
+
source = (0, fs.readFileSync)(filePath, "utf-8");
|
|
1000
|
+
} catch {
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
ctx.fileCache.set(relPath, {
|
|
1004
|
+
hash: (0, _animus_ui_extract_pipeline.contentHash)(source),
|
|
1005
|
+
source
|
|
1006
|
+
});
|
|
1007
|
+
folded.push(relPath);
|
|
1008
|
+
}
|
|
1009
|
+
return folded;
|
|
1010
|
+
}
|
|
1011
|
+
/** Barren-walk memo per context: the drop tuple-set and cache size at the
|
|
1012
|
+
* last walk that folded nothing (see stabilizeSourceUniverse). */
|
|
1013
|
+
const barrenWalkMemos = /* @__PURE__ */ new WeakMap();
|
|
1014
|
+
/** Per-context (file, parent, condition) verdicts already warned — each
|
|
1015
|
+
* condition warns once; cleared when the drops disappear. */
|
|
1016
|
+
const warnedVerdicts = /* @__PURE__ */ new WeakMap();
|
|
1017
|
+
/** The narrow probe set is deliberate — NOT ctx.extensionsSet (conscious
|
|
1018
|
+
* no-behavior-change choice: the teaching probe resolves what it always did). */
|
|
1019
|
+
const PARENT_PROBE_EXTENSIONS = new Set([
|
|
1020
|
+
".tsx",
|
|
1021
|
+
".ts",
|
|
1022
|
+
".jsx",
|
|
1023
|
+
".js"
|
|
1024
|
+
]);
|
|
1025
|
+
/**
|
|
1026
|
+
* The teaching half for parents that RESOLVE on disk but cannot be admitted
|
|
1027
|
+
* (excluded by configuration, unsupported source type, outside the root).
|
|
1028
|
+
* Resolution is a best-effort scan of the consumer's own import statements
|
|
1029
|
+
* for the parent binding — enough for the relative-specifier case a
|
|
1030
|
+
* developer actually hits; a parent this scan cannot resolve keeps the
|
|
1031
|
+
* engine's own diagnostic.
|
|
1032
|
+
*/
|
|
1033
|
+
function warnInadmissibleParents(ctx, drops) {
|
|
1034
|
+
const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
|
|
1035
|
+
let warned = warnedVerdicts.get(ctx);
|
|
1036
|
+
if (!warned) {
|
|
1037
|
+
warned = /* @__PURE__ */ new Set();
|
|
1038
|
+
warnedVerdicts.set(ctx, warned);
|
|
1039
|
+
}
|
|
1040
|
+
const active = new Set(drops.map((d) => `${d.file}\0${d.parent}`));
|
|
1041
|
+
for (const key of [...warned]) if (!active.has(key.slice(0, key.lastIndexOf("\0")))) warned.delete(key);
|
|
1042
|
+
for (const drop of drops) {
|
|
1043
|
+
const consumer = ctx.fileCache.get(drop.file);
|
|
1044
|
+
if (!consumer) continue;
|
|
1045
|
+
const specifier = importSpecifierFor(consumer.source, drop.parent);
|
|
1046
|
+
if (!specifier || !specifier.startsWith(".")) continue;
|
|
1047
|
+
const resolved = (0, _animus_ui_extract_pipeline.resolveAbsolutePathSpecifier)((0, path.resolve)(ctx.rootDir, (0, path.dirname)(drop.file), specifier), PARENT_PROBE_EXTENSIONS);
|
|
1048
|
+
if (!resolved) continue;
|
|
1049
|
+
const relPath = (0, path.relative)(ctx.rootDir, resolved);
|
|
1050
|
+
const excludedBy = excludePatterns.find((pattern) => resolved.includes(pattern) || relPath.includes(pattern));
|
|
1051
|
+
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;
|
|
1052
|
+
if (reason === null) continue;
|
|
1053
|
+
const verdictKey = `${drop.file}\0${drop.parent}\0${reason}`;
|
|
1054
|
+
if (warned.has(verdictKey)) continue;
|
|
1055
|
+
warned.add(verdictKey);
|
|
1056
|
+
ctx.warn(`cannot extract '${drop.component}': parent '${drop.parent}' resolves to ${relPath}, ${reason}`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
/** The module specifier a named import binds `binding` from, if scannable. */
|
|
1060
|
+
function importSpecifierFor(source, binding) {
|
|
1061
|
+
const importRe = /import\s+([^;]+?)\s+from\s+['"]([^'"]+)['"]/g;
|
|
1062
|
+
let match;
|
|
1063
|
+
while ((match = importRe.exec(source)) !== null) {
|
|
1064
|
+
const clause = match[1];
|
|
1065
|
+
if (new RegExp(`[{,\\s]${binding}[,\\s}]|^${binding}$`).test(clause)) return match[2];
|
|
1066
|
+
}
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
//#endregion
|
|
852
1070
|
//#region src/transform.ts
|
|
853
1071
|
/**
|
|
854
1072
|
* The dev-mode bridge prepend, shared with the hot-update gate so both
|
|
@@ -863,6 +1081,27 @@ function applyDevBridgeImport(code) {
|
|
|
863
1081
|
return prologue + `import '${VIRTUAL_BRIDGE_ID}';\n` + code.slice(prologue.length);
|
|
864
1082
|
}
|
|
865
1083
|
/**
|
|
1084
|
+
* Live raw-fallback files reachable from `relPath`'s components through
|
|
1085
|
+
* extension provenance — the transitive closure, so a grandparent's serve is
|
|
1086
|
+
* withheld exactly like a direct parent's.
|
|
1087
|
+
*/
|
|
1088
|
+
function rawFallbackDescendants(ctx, relPath) {
|
|
1089
|
+
const conflicted = /* @__PURE__ */ new Set();
|
|
1090
|
+
const queue = [...ctx.storedManifest?.files?.[relPath] ?? []];
|
|
1091
|
+
const seen = new Set(queue);
|
|
1092
|
+
while (queue.length > 0) {
|
|
1093
|
+
const id = queue.shift();
|
|
1094
|
+
for (const childId of ctx.reverseProvenance[id] ?? []) {
|
|
1095
|
+
if (seen.has(childId)) continue;
|
|
1096
|
+
seen.add(childId);
|
|
1097
|
+
queue.push(childId);
|
|
1098
|
+
const childFile = ctx.storedManifest?.components?.[childId]?.file;
|
|
1099
|
+
if (childFile && childFile !== relPath && ctx.rawExtensionFallbacks.has(childFile)) conflicted.add(childFile);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return [...conflicted].sort();
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
866
1105
|
* transform: replace builder chains with `createComponent()` calls using
|
|
867
1106
|
* the pre-built manifest; detect files created after buildStart and fold
|
|
868
1107
|
* them into the analysis.
|
|
@@ -875,17 +1114,17 @@ function applyDevBridgeImport(code) {
|
|
|
875
1114
|
function transformSource(ctx, code, id) {
|
|
876
1115
|
if (!ctx.storedManifest) return null;
|
|
877
1116
|
if (id.startsWith("\0") || id.startsWith("virtual:animus/")) return null;
|
|
878
|
-
const isExternalPkg = ctx.externalPackageDirs.some((dir) =>
|
|
1117
|
+
const isExternalPkg = ctx.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, id));
|
|
879
1118
|
const relativePath = (0, path.relative)(ctx.rootDir, id);
|
|
880
1119
|
if (!isExternalPkg) {
|
|
881
1120
|
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
882
1121
|
if (id.includes("node_modules")) return null;
|
|
883
|
-
if (
|
|
1122
|
+
if (!(0, _animus_ui_extract_pipeline.isPathWithinRoot)(ctx.rootDir, id)) return null;
|
|
884
1123
|
}
|
|
885
1124
|
if (!ctx.storedManifest.files?.[relativePath]?.length) {
|
|
886
1125
|
if (!ctx.isProd && !ctx.fileCache.has(relativePath)) {
|
|
887
1126
|
if (isExternalPkg) {
|
|
888
|
-
const owner = Object.entries(ctx.externalDirOwners).find(([dir]) =>
|
|
1127
|
+
const owner = Object.entries(ctx.externalDirOwners).find(([dir]) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, id));
|
|
889
1128
|
if (owner) ctx.externalFileOwners[relativePath] = owner[1];
|
|
890
1129
|
}
|
|
891
1130
|
const hash = (0, _animus_ui_extract_pipeline.contentHash)(code);
|
|
@@ -893,13 +1132,36 @@ function transformSource(ctx, code, id) {
|
|
|
893
1132
|
hash,
|
|
894
1133
|
source: code
|
|
895
1134
|
});
|
|
1135
|
+
const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
|
|
896
1136
|
const fileEntries = buildFileEntriesFromCache(ctx.fileCache, relativePath);
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1137
|
+
let analysisOk = false;
|
|
1138
|
+
try {
|
|
1139
|
+
analysisOk = ctx.runAnalysis(fileEntries) !== false;
|
|
1140
|
+
} finally {
|
|
1141
|
+
if (!analysisOk) ctx.fileCache.delete(relativePath);
|
|
1142
|
+
}
|
|
1143
|
+
if (analysisOk) {
|
|
1144
|
+
stabilizeSourceUniverse(ctx);
|
|
1145
|
+
invalidateFileModules(ctx, (0, _animus_ui_extract_pipeline.diffFilePlans)(prevPlans, (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest), { exclude: relativePath }));
|
|
1146
|
+
const compCount = ctx.storedManifest.files?.[relativePath]?.length ?? 0;
|
|
1147
|
+
ctx.info(`New file detected: ${relativePath} — ${compCount ? `${compCount} components extracted` : "no components"}`);
|
|
1148
|
+
ctx.invalidateExtractedModules();
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
if (!ctx.storedManifest.files?.[relativePath]?.length) {
|
|
1152
|
+
if (!ctx.isProd) if (unresolvedDropFiles(ctx).has(relativePath)) ctx.rawExtensionFallbacks.add(relativePath);
|
|
1153
|
+
else ctx.rawExtensionFallbacks.delete(relativePath);
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
if (!ctx.isProd && ctx.rawExtensionFallbacks.size > 0) {
|
|
1158
|
+
const conflicted = rawFallbackDescendants(ctx, relativePath);
|
|
1159
|
+
if (conflicted.length > 0) {
|
|
1160
|
+
invalidateFileModules(ctx, conflicted);
|
|
900
1161
|
ctx.invalidateExtractedModules();
|
|
1162
|
+
for (const file of conflicted) ctx.rawExtensionFallbacks.delete(file);
|
|
1163
|
+
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
1164
|
}
|
|
902
|
-
if (!ctx.storedManifest.files?.[relativePath]?.length) return null;
|
|
903
1165
|
}
|
|
904
1166
|
try {
|
|
905
1167
|
const { transformFile } = ctx.engineApi();
|
|
@@ -913,6 +1175,7 @@ function transformSource(ctx, code, id) {
|
|
|
913
1175
|
if (!ctx.isProd) {
|
|
914
1176
|
outputCode = applyDevBridgeImport(result.code);
|
|
915
1177
|
ctx.recordTransformOutput(relativePath, outputCode);
|
|
1178
|
+
ctx.rawExtensionFallbacks.delete(relativePath);
|
|
916
1179
|
}
|
|
917
1180
|
return {
|
|
918
1181
|
code: outputCode,
|
|
@@ -957,7 +1220,6 @@ async function handleHotUpdate(ctx, environment, { type, file, timestamp, module
|
|
|
957
1220
|
}
|
|
958
1221
|
return [];
|
|
959
1222
|
}
|
|
960
|
-
if (type === "create") return;
|
|
961
1223
|
if (type === "delete") {
|
|
962
1224
|
if (ownsEvent) pruneDeletedFile(ctx, absFile);
|
|
963
1225
|
return;
|
|
@@ -965,7 +1227,10 @@ async function handleHotUpdate(ctx, environment, { type, file, timestamp, module
|
|
|
965
1227
|
if (ownsEvent) ctx.hotUpdateEvents.record(file, timestamp, await analyzeChangedFile(ctx, file, absFile, read));
|
|
966
1228
|
const result = ctx.hotUpdateEvents.resultOf(file, timestamp);
|
|
967
1229
|
if (result.kind === "ignored") return;
|
|
968
|
-
if (result.kind === "unchanged")
|
|
1230
|
+
if (result.kind === "unchanged") {
|
|
1231
|
+
if (type === "create") return;
|
|
1232
|
+
return [];
|
|
1233
|
+
}
|
|
969
1234
|
return invalidateStaleModules(ctx, environment, modules, result);
|
|
970
1235
|
}
|
|
971
1236
|
/**
|
|
@@ -1028,7 +1293,8 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
|
|
|
1028
1293
|
const ext = (0, path.extname)(file);
|
|
1029
1294
|
if (!ctx.extensionsSet.has(ext)) return { kind: "ignored" };
|
|
1030
1295
|
const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
|
|
1031
|
-
|
|
1296
|
+
const isExternalPkg = ctx.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, file));
|
|
1297
|
+
if (!isExternalPkg && excludePatterns.some((pattern) => file.includes(pattern) || (0, path.relative)(ctx.rootDir, file).includes(pattern))) return { kind: "ignored" };
|
|
1032
1298
|
const relPath = (0, path.relative)(ctx.rootDir, absFile);
|
|
1033
1299
|
let source;
|
|
1034
1300
|
try {
|
|
@@ -1056,13 +1322,20 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
|
|
|
1056
1322
|
ctx.log(`HMR skip: ${scannerRelPath} (unchanged)`);
|
|
1057
1323
|
return { kind: "unchanged" };
|
|
1058
1324
|
}
|
|
1325
|
+
if (isExternalPkg && !ctx.externalFileOwners[scannerRelPath]) {
|
|
1326
|
+
const owner = Object.entries(ctx.externalDirOwners).find(([dir]) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, absFile));
|
|
1327
|
+
if (owner) ctx.externalFileOwners[scannerRelPath] = owner[1];
|
|
1328
|
+
}
|
|
1059
1329
|
ctx.fileCache.set(scannerRelPath, {
|
|
1060
1330
|
hash,
|
|
1061
1331
|
source
|
|
1062
1332
|
});
|
|
1333
|
+
const restoreEntry = () => {
|
|
1334
|
+
if (cached) ctx.fileCache.set(scannerRelPath, cached);
|
|
1335
|
+
else ctx.fileCache.delete(scannerRelPath);
|
|
1336
|
+
};
|
|
1063
1337
|
const hmrStart = performance.now();
|
|
1064
|
-
const
|
|
1065
|
-
if (ctx.storedManifest?.components) for (const [id, desc] of Object.entries(ctx.storedManifest.components)) prevReplacements.set(id, desc.replacement ?? "");
|
|
1338
|
+
const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
|
|
1066
1339
|
const directComponentIds = ctx.storedManifest?.files?.[scannerRelPath] ?? [];
|
|
1067
1340
|
const invalidatedIds = new Set(directComponentIds);
|
|
1068
1341
|
const queue = [...directComponentIds];
|
|
@@ -1078,17 +1351,28 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
|
|
|
1078
1351
|
}
|
|
1079
1352
|
if (ctx.verbose && invalidatedIds.size > directComponentIds.length) ctx.log(`HMR: ${directComponentIds.length} direct + ${invalidatedIds.size - directComponentIds.length} transitive components invalidated`);
|
|
1080
1353
|
const analysisStart = performance.now();
|
|
1081
|
-
const
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
staleDefinitionFiles.push(defFile);
|
|
1090
|
-
}
|
|
1354
|
+
const fileEntries = buildFileEntriesFromCache(ctx.fileCache, relPath);
|
|
1355
|
+
const systemPropsBefore = systemPropsModuleSource(ctx);
|
|
1356
|
+
let analysisOk;
|
|
1357
|
+
try {
|
|
1358
|
+
analysisOk = ctx.runAnalysis(fileEntries) !== false;
|
|
1359
|
+
} catch (e) {
|
|
1360
|
+
restoreEntry();
|
|
1361
|
+
throw e;
|
|
1091
1362
|
}
|
|
1363
|
+
if (!analysisOk) {
|
|
1364
|
+
restoreEntry();
|
|
1365
|
+
return { kind: "ignored" };
|
|
1366
|
+
}
|
|
1367
|
+
try {
|
|
1368
|
+
stabilizeSourceUniverse(ctx);
|
|
1369
|
+
} catch (e) {
|
|
1370
|
+
restoreEntry();
|
|
1371
|
+
throw e;
|
|
1372
|
+
}
|
|
1373
|
+
const systemPropsChanged = systemPropsModuleSource(ctx) !== systemPropsBefore;
|
|
1374
|
+
const analysisMs = Math.round(performance.now() - analysisStart);
|
|
1375
|
+
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
1376
|
const presentationOnly = isPresentationOnlyEdit(ctx, scannerRelPath, source);
|
|
1093
1377
|
const hmrMs = Math.round(performance.now() - hmrStart);
|
|
1094
1378
|
ctx.log(`HMR update: ${relPath} — analysis ${analysisMs}ms, total ${hmrMs}ms${presentationOnly ? " (presentation-only)" : ""}`);
|
|
@@ -1136,8 +1420,10 @@ function isPresentationOnlyEdit(ctx, scannerRelPath, source) {
|
|
|
1136
1420
|
*/
|
|
1137
1421
|
function pruneDeletedFile(ctx, absFile) {
|
|
1138
1422
|
if (!pruneFileCache(ctx.fileCache, ctx.rootDir, absFile)) return;
|
|
1139
|
-
|
|
1423
|
+
const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
|
|
1424
|
+
const ok = ctx.runAnalysis(buildFileEntriesFromCache(ctx.fileCache)) !== false;
|
|
1140
1425
|
ctx.log(`Deleted file pruned: ${(0, path.relative)(ctx.rootDir, absFile)}`);
|
|
1426
|
+
if (ok) invalidateFileModules(ctx, (0, _animus_ui_extract_pipeline.diffFilePlans)(prevPlans, (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest)));
|
|
1141
1427
|
ctx.invalidateExtractedModules();
|
|
1142
1428
|
}
|
|
1143
1429
|
/**
|
|
@@ -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,CA2EnE"}
|
package/dist/transform.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"
|
|
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.
|
|
3
|
+
"version": "0.1.9",
|
|
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.
|
|
42
|
+
"@animus-ui/extract": "0.1.9",
|
|
43
43
|
"browserslist": "^4.24.0",
|
|
44
44
|
"lightningcss": "^1.29.0"
|
|
45
45
|
},
|