@animus-ui/next-plugin 0.1.1-beta.2 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -4
- package/dist/chunk-y_GBKt04.mjs +5 -0
- package/dist/extraction-session.d.ts +138 -0
- package/dist/extraction-session.d.ts.map +1 -0
- package/dist/index.cjs +762 -541
- package/dist/index.mjs +762 -540
- package/dist/loader-core-CbaYNhBu.cjs +71 -0
- package/dist/loader-core-D6Tm_b8F.mjs +66 -0
- package/dist/loader-core.d.ts +26 -0
- package/dist/loader-core.d.ts.map +1 -0
- package/dist/loader.cjs +12 -43
- package/dist/loader.d.ts +5 -9
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.mjs +12 -43
- package/dist/plugin.d.ts +24 -42
- package/dist/plugin.d.ts.map +1 -1
- package/dist/resolve-packages.d.ts +8 -0
- package/dist/resolve-packages.d.ts.map +1 -0
- package/dist/singleton-BKALu7Mk.mjs +108 -0
- package/dist/{singleton-CttvA4Rg.cjs → singleton-DH7yeWZa.cjs} +29 -60
- package/dist/singleton.d.ts +1 -8
- package/dist/singleton.d.ts.map +1 -1
- package/dist/timing.d.ts +7 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/turbopack-config.d.ts +55 -0
- package/dist/turbopack-config.d.ts.map +1 -0
- package/dist/turbopack-loader.cjs +90 -0
- package/dist/turbopack-loader.d.ts +16 -0
- package/dist/turbopack-loader.d.ts.map +1 -0
- package/dist/turbopack-loader.mjs +91 -0
- package/dist/turbopack-orchestrator.d.ts +31 -0
- package/dist/turbopack-orchestrator.d.ts.map +1 -0
- package/dist/types.d.ts +67 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/with-animus.d.ts +1 -1
- package/dist/with-animus.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/analyze-project-args.d.ts +0 -33
- package/dist/analyze-project-args.d.ts.map +0 -1
- package/dist/manifest-diagnostics.d.ts +0 -10
- package/dist/manifest-diagnostics.d.ts.map +0 -1
- package/dist/singleton-CmwFBCJ5.mjs +0 -136
package/dist/index.cjs
CHANGED
|
@@ -1,68 +1,161 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_singleton = require("./singleton-
|
|
2
|
+
const require_singleton = require("./singleton-DH7yeWZa.cjs");
|
|
3
|
+
let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
|
|
3
4
|
let fs = require("fs");
|
|
4
5
|
let path = require("path");
|
|
5
6
|
let url = require("url");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
7
|
+
//#region src/resolve-packages.ts
|
|
8
|
+
/**
|
|
9
|
+
* Resolve external DS package specifiers to rootDir-relative entry paths.
|
|
10
|
+
* Node-generic (no webpack APIs): Pass 1 walks the consumer's workspaces for
|
|
11
|
+
* matching package names; Pass 2 falls back to `require.resolve`. Specifiers
|
|
12
|
+
* that resolve nowhere are simply omitted (spec: silent skip).
|
|
13
|
+
*/
|
|
14
|
+
function resolvePackagesByName(rootDir, names) {
|
|
15
|
+
if (names.length === 0) return {};
|
|
16
|
+
const nameSet = new Set(names);
|
|
17
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
18
|
+
const packageMap = {};
|
|
19
|
+
try {
|
|
20
|
+
const rootPkg = JSON.parse((0, fs.readFileSync)((0, path.join)(rootDir, "package.json"), "utf-8"));
|
|
21
|
+
const workspaces = Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : rootPkg.workspaces?.packages ?? [];
|
|
22
|
+
for (const ws of workspaces) {
|
|
23
|
+
const wsDir = (0, path.resolve)(rootDir, ws);
|
|
24
|
+
if (!(0, fs.existsSync)(wsDir)) continue;
|
|
25
|
+
try {
|
|
26
|
+
const pkg = JSON.parse((0, fs.readFileSync)((0, path.join)(wsDir, "package.json"), "utf-8"));
|
|
27
|
+
const name = pkg.name || "";
|
|
28
|
+
if (nameSet.has(name)) {
|
|
29
|
+
const entryPath = (0, path.resolve)(wsDir, pkg.main || pkg.module || "index.ts");
|
|
30
|
+
if ((0, fs.existsSync)(entryPath)) {
|
|
31
|
+
packageMap[name] = (0, path.relative)(rootDir, entryPath);
|
|
32
|
+
resolved.add(name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
} catch {}
|
|
38
|
+
for (const name of nameSet) {
|
|
39
|
+
if (resolved.has(name)) continue;
|
|
40
|
+
try {
|
|
41
|
+
packageMap[name] = (0, path.relative)(rootDir, require.resolve(name, { paths: [rootDir] }));
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
return packageMap;
|
|
32
45
|
}
|
|
33
46
|
//#endregion
|
|
34
|
-
//#region src/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
//#region src/timing.ts
|
|
48
|
+
/**
|
|
49
|
+
* Verbose build-timing waterfall: JS phases, analysis sub-phases, and the
|
|
50
|
+
* shared Rust phase table. `ANIMUS_TIMING_JSON=1` additionally dumps a
|
|
51
|
+
* machine-readable merged record.
|
|
52
|
+
*/
|
|
53
|
+
function logBuildTimings(bt, rustTiming, log, verbose) {
|
|
54
|
+
if (!verbose) return;
|
|
55
|
+
const pad = (label, width) => label + " ".repeat(Math.max(0, width - label.length));
|
|
56
|
+
for (const [key, label] of [
|
|
57
|
+
["systemLoad", "system-load"],
|
|
58
|
+
["fileDiscovery", "file-discovery"],
|
|
59
|
+
["fileRead", "file-read+hash"],
|
|
60
|
+
["packageResolve", "pkg-resolve"],
|
|
61
|
+
["analysis", "analysis"]
|
|
62
|
+
]) {
|
|
63
|
+
const ms = bt[key] ?? 0;
|
|
64
|
+
if (ms === 0 && !Object.hasOwn(bt, key)) continue;
|
|
65
|
+
log(` ${pad(label, 17)}${String(ms).padStart(5)}ms`);
|
|
66
|
+
if (key === "analysis") for (const [sk, sl] of [
|
|
67
|
+
["jsonSerialize", "json-serialize"],
|
|
68
|
+
["rustExtract", "rust-extract"],
|
|
69
|
+
["jsonParse", "json-parse"]
|
|
70
|
+
]) {
|
|
71
|
+
const sms = bt[sk] ?? 0;
|
|
72
|
+
log(` ${pad(sl, 15)}${String(sms).padStart(5)}ms`);
|
|
73
|
+
if (sk === "rustExtract" && rustTiming) for (const line of (0, _animus_ui_extract_pipeline.formatRustTimingWaterfall)(rustTiming, {
|
|
74
|
+
indent: " ",
|
|
75
|
+
labelWidth: 13
|
|
76
|
+
})) log(line);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
log(` total ${String(bt.total).padStart(5)}ms`);
|
|
80
|
+
if (process.env.ANIMUS_TIMING_JSON === "1") {
|
|
81
|
+
const merged = {};
|
|
82
|
+
for (const [k, v] of Object.entries(bt)) merged[`buildStart.${k}`] = v;
|
|
83
|
+
if (rustTiming) {
|
|
84
|
+
for (const [k, v] of Object.entries(rustTiming)) if (typeof v === "number") merged[`rust.${k}`] = v;
|
|
85
|
+
}
|
|
86
|
+
console.info(`[animus:timing] ${JSON.stringify(merged)}`);
|
|
39
87
|
}
|
|
40
88
|
}
|
|
41
89
|
//#endregion
|
|
42
|
-
//#region src/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
90
|
+
//#region src/extraction-session.ts
|
|
91
|
+
/**
|
|
92
|
+
* Module id the Rust emitter injects for the extracted stylesheet — also the
|
|
93
|
+
* exact resolve.alias key with-animus registers for it, which the adapter's
|
|
94
|
+
* alias harvesting must skip.
|
|
95
|
+
*/
|
|
96
|
+
const ANIMUS_CSS_MODULE_ID = ".animus/styles.css";
|
|
97
|
+
/** Default path fragments excluded from source discovery (full + watch). */
|
|
98
|
+
const DEFAULT_EXCLUDE = [
|
|
99
|
+
"node_modules",
|
|
100
|
+
"dist",
|
|
101
|
+
".test.",
|
|
102
|
+
".spec.",
|
|
103
|
+
".next",
|
|
104
|
+
".animus"
|
|
105
|
+
];
|
|
106
|
+
/**
|
|
107
|
+
* Bundler-agnostic extraction pipeline: system loading, source discovery
|
|
108
|
+
* and ingestion, external-package collection, analysis, stylesheet and
|
|
109
|
+
* system-props emission, and watch-cycle diffing.
|
|
110
|
+
*
|
|
111
|
+
* The webpack adapter (plugin.ts) owns only bundler wiring: hook
|
|
112
|
+
* registration, alias harvesting from the compiler config, in-memory asset
|
|
113
|
+
* replacement, and translating watch events into `handleWatchUpdate`
|
|
114
|
+
* change sets. Keeping this class free of webpack types is deliberate — a
|
|
115
|
+
* future Turbopack integration must drive the same session from outside
|
|
116
|
+
* the bundler (Turbopack has no compiler-hook surface), reusing everything
|
|
117
|
+
* here unchanged.
|
|
118
|
+
*
|
|
119
|
+
* Outputs are published through the package singleton (shared CSS,
|
|
120
|
+
* manifest, system props) and written to `.animus/` on disk.
|
|
121
|
+
*/
|
|
122
|
+
var ExtractionSession = class {
|
|
123
|
+
/** Set by the adapter before any pipeline run. */
|
|
46
124
|
rootDir = null;
|
|
47
|
-
|
|
48
|
-
groupRegistryJson = "";
|
|
49
|
-
themeJson = "";
|
|
50
|
-
variableMapJson = "";
|
|
51
|
-
variableCss = "";
|
|
52
|
-
contextualVarsJson = null;
|
|
53
|
-
selectorAliasesJson = null;
|
|
54
|
-
globalCss = "";
|
|
55
|
-
globalStyleBlocksJson = null;
|
|
56
|
-
keyframesBlocksJson = null;
|
|
125
|
+
/** Serialized path aliases, harvested by the adapter from bundler config. */
|
|
57
126
|
pathAliasesJson = null;
|
|
127
|
+
/** When set (Turbopack orchestration), every analysis also persists
|
|
128
|
+
* `.animus/analysis-inputs.json` so isolated loader workers can hydrate.
|
|
129
|
+
* Webpack mode leaves this off — its loader shares the process. */
|
|
130
|
+
persistAnalysisInputs = false;
|
|
131
|
+
/** Emitter identity override for the system-props module id. Webpack mode
|
|
132
|
+
* (null) injects the absolute `.animus/system-props.js` path, resolved by
|
|
133
|
+
* NormalModuleReplacement; Turbopack rejects absolute-path imports, so
|
|
134
|
+
* its driver sets the virtual id that `resolveAlias` maps to disk. */
|
|
135
|
+
systemPropsModuleId = null;
|
|
136
|
+
/** Absolute directory prefixes for external DS packages (loader allowlisting). */
|
|
137
|
+
externalPackageDirs = [];
|
|
138
|
+
/** External package specifier → absolute source entry path. */
|
|
139
|
+
externalSourceEntries = /* @__PURE__ */ new Map();
|
|
140
|
+
options;
|
|
141
|
+
staticCssJson;
|
|
142
|
+
system = null;
|
|
143
|
+
/** Full package-resolution map from the last full pipeline — replayed by
|
|
144
|
+
* incremental passes (sourceEntries alone omits dist-resolved packages). */
|
|
145
|
+
lastPackageMap = {};
|
|
58
146
|
fileCache = /* @__PURE__ */ new Map();
|
|
147
|
+
systemDependencyKeys = /* @__PURE__ */ new Set();
|
|
148
|
+
/** Loader-reported dependency paths for bundler watch registration. */
|
|
149
|
+
systemDependencyPaths = [];
|
|
150
|
+
systemDependencyHashes = /* @__PURE__ */ new Map();
|
|
59
151
|
lastCssHash = null;
|
|
60
152
|
lastSystemPropsHash = null;
|
|
61
|
-
|
|
62
|
-
|
|
153
|
+
lastManifestHash = null;
|
|
154
|
+
lastAnalysisInputsHash = null;
|
|
155
|
+
lcssTargets = null;
|
|
63
156
|
constructor(options) {
|
|
64
157
|
this.options = options;
|
|
65
|
-
|
|
158
|
+
this.staticCssJson = (0, _animus_ui_extract_pipeline.serializeStaticCss)(options.staticCss);
|
|
66
159
|
}
|
|
67
160
|
get verbose() {
|
|
68
161
|
return this.options.verbose === true || process.env.ANIMUS_DEBUG === "1" || process.env.ANIMUS_DEBUG === "true";
|
|
@@ -79,176 +172,82 @@ var AnimusWebpackPlugin = class {
|
|
|
79
172
|
elapsed(t) {
|
|
80
173
|
return this.verbose ? Math.round(performance.now() - t) : 0;
|
|
81
174
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const ms = bt[key] ?? 0;
|
|
92
|
-
if (ms === 0 && !Object.hasOwn(bt, key)) continue;
|
|
93
|
-
const pad = " ".repeat(Math.max(0, 17 - label.length));
|
|
94
|
-
this.log(` ${label}${pad}${String(ms).padStart(5)}ms`);
|
|
95
|
-
if (key === "analysis") for (const [sk, sl] of [
|
|
96
|
-
["jsonSerialize", "json-serialize"],
|
|
97
|
-
["rustExtract", "rust-extract"],
|
|
98
|
-
["jsonParse", "json-parse"]
|
|
99
|
-
]) {
|
|
100
|
-
const sms = bt[sk] ?? 0;
|
|
101
|
-
const spad = " ".repeat(Math.max(0, 15 - sl.length));
|
|
102
|
-
this.log(` ${sl}${spad}${String(sms).padStart(5)}ms`);
|
|
103
|
-
if (sk === "rustExtract" && rustTiming) for (const [rk, rl] of [
|
|
104
|
-
["parseAndWalk", "parse+walk"],
|
|
105
|
-
["importResolution", "imports"],
|
|
106
|
-
["extensionProvenance", "provenance"],
|
|
107
|
-
["topologicalSort", "topo-sort"],
|
|
108
|
-
["chainEvaluation", "chains"],
|
|
109
|
-
["jsxScanning", "jsx-scan"],
|
|
110
|
-
["systemPropAggregation", "sys-props"],
|
|
111
|
-
["usageLedger", "usage"],
|
|
112
|
-
["reconciliation", "reconcile"],
|
|
113
|
-
["cssGeneration", "css-gen"],
|
|
114
|
-
["manifestSerialization", "serialize"]
|
|
115
|
-
]) {
|
|
116
|
-
const rms = rustTiming[rk] ?? 0;
|
|
117
|
-
const rpad = " ".repeat(Math.max(0, 13 - rl.length));
|
|
118
|
-
const rextra = rk === "parseAndWalk" ? ` (${rustTiming.fileCount ?? 0} files, ${rustTiming.cacheHits ?? 0} cached)` : "";
|
|
119
|
-
this.log(` ${rl}${rpad}${String(rms).padStart(5)}ms${rextra}`);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
this.log(` total ${String(bt.total).padStart(5)}ms`);
|
|
124
|
-
if (process.env.ANIMUS_TIMING_JSON === "1") {
|
|
125
|
-
const merged = {};
|
|
126
|
-
for (const [k, v] of Object.entries(bt)) merged[`buildStart.${k}`] = v;
|
|
127
|
-
if (rustTiming) {
|
|
128
|
-
for (const [k, v] of Object.entries(rustTiming)) if (typeof v === "number") merged[`rust.${k}`] = v;
|
|
129
|
-
}
|
|
130
|
-
console.info(`[animus:timing] ${JSON.stringify(merged)}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
initialized = false;
|
|
134
|
-
/** Extract path aliases from webpack's resolve.alias config. */
|
|
135
|
-
extractAliases(compiler) {
|
|
136
|
-
const rootDir = compiler.context;
|
|
137
|
-
const rawAlias = compiler.options?.resolve?.alias;
|
|
138
|
-
if (!rawAlias || typeof rawAlias !== "object") return;
|
|
139
|
-
const entries = [];
|
|
140
|
-
for (const [key, value] of Object.entries(rawAlias)) {
|
|
141
|
-
const target = Array.isArray(value) ? value[0] : value;
|
|
142
|
-
if (typeof target !== "string") continue;
|
|
143
|
-
if (key.includes(".animus")) continue;
|
|
144
|
-
const replacement = target.startsWith(rootDir) ? target.slice(rootDir.length + 1) : target;
|
|
145
|
-
if (/\.\w+$/.test(replacement)) entries.push({
|
|
146
|
-
pattern: key,
|
|
147
|
-
replacement,
|
|
148
|
-
type: "exact"
|
|
149
|
-
});
|
|
150
|
-
else entries.push({
|
|
151
|
-
pattern: key.endsWith("/") ? key : key + "/",
|
|
152
|
-
replacement: replacement.endsWith("/") ? replacement : replacement + "/",
|
|
153
|
-
type: "prefix"
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
entries.sort((a, b) => b.pattern.length - a.pattern.length);
|
|
157
|
-
if (entries.length > 0) this.pathAliasesJson = JSON.stringify({ aliases: entries });
|
|
158
|
-
}
|
|
159
|
-
apply(compiler) {
|
|
160
|
-
if (compiler.options?.name === "edge-server") return;
|
|
161
|
-
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
162
|
-
const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL ?? -2e3;
|
|
163
|
-
const RawSource = compiler.webpack?.sources.RawSource;
|
|
164
|
-
compilation.hooks.processAssets.tap({
|
|
165
|
-
name: PLUGIN_NAME,
|
|
166
|
-
stage
|
|
167
|
-
}, () => {
|
|
168
|
-
const css = require_singleton.getSharedCss();
|
|
169
|
-
if (!css || !RawSource) return;
|
|
170
|
-
const cssPath = (0, path.join)(this.rootDir || compiler.context, ".animus", "styles.css");
|
|
171
|
-
if (compilation.getAsset(cssPath)) {
|
|
172
|
-
compilation.updateAsset(cssPath, new RawSource(css));
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
const relPath = ".animus/styles.css";
|
|
176
|
-
if (compilation.getAsset(relPath)) compilation.updateAsset(relPath, new RawSource(css));
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
compiler.hooks.run.tapPromise(PLUGIN_NAME, async (_compiler) => {
|
|
180
|
-
this.rootDir = _compiler.context;
|
|
181
|
-
this.extractAliases(_compiler);
|
|
182
|
-
const existing = require_singleton.getAnalysisPromise();
|
|
183
|
-
if (existing) {
|
|
184
|
-
await existing;
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
const promise = this.runFullPipeline();
|
|
188
|
-
require_singleton.setAnalysisPromise(promise);
|
|
189
|
-
await promise;
|
|
190
|
-
});
|
|
191
|
-
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async (_compiler) => {
|
|
192
|
-
this.rootDir = _compiler.context;
|
|
193
|
-
this.extractAliases(_compiler);
|
|
194
|
-
if (!this.initialized) {
|
|
195
|
-
const existing = require_singleton.getAnalysisPromise();
|
|
196
|
-
if (existing) {
|
|
197
|
-
await existing;
|
|
198
|
-
this.initialized = true;
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
const promise = this.runFullPipeline();
|
|
202
|
-
require_singleton.setAnalysisPromise(promise);
|
|
203
|
-
await promise;
|
|
204
|
-
this.initialized = true;
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
await this.handleWatchUpdate();
|
|
208
|
-
});
|
|
175
|
+
/** Resolve the scan configuration from options — the single source of the
|
|
176
|
+
* exclude/extension policy shared by the full and incremental pipelines. */
|
|
177
|
+
resolveScanConfig() {
|
|
178
|
+
const extensionsSet = new Set(this.options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
179
|
+
return {
|
|
180
|
+
excludePatterns: this.options.exclude ?? DEFAULT_EXCLUDE,
|
|
181
|
+
extensionsSet,
|
|
182
|
+
shouldHandleMdx: extensionsSet.has(".mdx")
|
|
183
|
+
};
|
|
209
184
|
}
|
|
210
|
-
|
|
211
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Incremental watch pass: geological reset when the system file changed,
|
|
187
|
+
* otherwise content-hash diffing restricted to the watcher's change sets
|
|
188
|
+
* (falling back to a full discovery walk when no sets are provided).
|
|
189
|
+
*/
|
|
190
|
+
async handleWatchUpdate(changes) {
|
|
191
|
+
if (!this.system) return;
|
|
212
192
|
const rootDir = this.rootDir;
|
|
213
|
-
const resolvedSystemPath = (0, path.resolve)(rootDir, this.options.system);
|
|
214
193
|
try {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
194
|
+
let systemHit;
|
|
195
|
+
if (!changes.modifiedFiles && !changes.removedFiles) for (const [dep, hash] of this.systemDependencyHashes) {
|
|
196
|
+
let current = "";
|
|
197
|
+
try {
|
|
198
|
+
current = (0, _animus_ui_extract_pipeline.contentHash)((0, fs.readFileSync)(dep, "utf-8"));
|
|
199
|
+
} catch {}
|
|
200
|
+
if (current !== hash) {
|
|
201
|
+
systemHit = dep;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else systemHit = [...changes.modifiedFiles ?? [], ...changes.removedFiles ?? []].find((path$2) => (0, _animus_ui_extract_pipeline.toWatchKeys)(path$2).some((key) => this.systemDependencyKeys.has(key)));
|
|
206
|
+
if (systemHit) {
|
|
207
|
+
this.log(`geological reset: system dependency changed (${(0, path.relative)(rootDir, systemHit)})`);
|
|
220
208
|
this.resetForHmr();
|
|
221
|
-
this.initialized = false;
|
|
222
209
|
const promise = this.runFullPipeline();
|
|
223
210
|
require_singleton.setAnalysisPromise(promise);
|
|
224
211
|
await promise;
|
|
225
|
-
this.initialized = true;
|
|
226
212
|
return;
|
|
227
213
|
}
|
|
228
|
-
} catch {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
".
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
214
|
+
} catch (err) {
|
|
215
|
+
this.warn(`HMR geological-reset check failed: ${String(err)}`);
|
|
216
|
+
}
|
|
217
|
+
const { excludePatterns, extensionsSet, shouldHandleMdx } = this.resolveScanConfig();
|
|
218
|
+
let removedAny = false;
|
|
219
|
+
if (changes.removedFiles) for (const removedPath of changes.removedFiles) {
|
|
220
|
+
const rel = (0, path.relative)(rootDir, removedPath);
|
|
221
|
+
if (this.fileCache.delete(rel) || this.fileCache.delete(rel + ".tsx")) removedAny = true;
|
|
222
|
+
}
|
|
223
|
+
let files;
|
|
224
|
+
if (changes.modifiedFiles) {
|
|
225
|
+
files = [];
|
|
226
|
+
for (const modifiedPath of changes.modifiedFiles) {
|
|
227
|
+
if (!extensionsSet.has((0, path.extname)(modifiedPath))) continue;
|
|
228
|
+
const rel = (0, path.relative)(rootDir, modifiedPath);
|
|
229
|
+
if (rel.startsWith("..")) continue;
|
|
230
|
+
if (excludePatterns.some((pattern) => modifiedPath.includes(pattern) || rel.includes(pattern))) continue;
|
|
231
|
+
files.push(modifiedPath);
|
|
232
|
+
}
|
|
233
|
+
} else files = (0, _animus_ui_extract_pipeline.discoverFiles)(rootDir, rootDir, excludePatterns, extensionsSet);
|
|
240
234
|
const changedPaths = [];
|
|
241
235
|
for (const filePath of files) {
|
|
242
236
|
let relPath = (0, path.relative)(rootDir, filePath);
|
|
243
|
-
let source
|
|
237
|
+
let source;
|
|
238
|
+
try {
|
|
239
|
+
source = (0, fs.readFileSync)(filePath, "utf-8");
|
|
240
|
+
} catch {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
244
243
|
if (shouldHandleMdx && (0, path.extname)(filePath) === ".mdx") {
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
247
|
-
source =
|
|
248
|
-
relPath = relPath
|
|
244
|
+
const processed = await this.preprocessMdxEntry(source, relPath, { warn: false });
|
|
245
|
+
if (!processed) continue;
|
|
246
|
+
source = processed.source;
|
|
247
|
+
relPath = processed.relPath;
|
|
249
248
|
}
|
|
250
249
|
const cached = this.fileCache.get(relPath);
|
|
251
|
-
const hash = (0,
|
|
250
|
+
const hash = (0, _animus_ui_extract_pipeline.contentHash)(source);
|
|
252
251
|
if (!cached || cached.hash !== hash) {
|
|
253
252
|
changedPaths.push(relPath);
|
|
254
253
|
this.fileCache.set(relPath, {
|
|
@@ -257,39 +256,74 @@ var AnimusWebpackPlugin = class {
|
|
|
257
256
|
});
|
|
258
257
|
}
|
|
259
258
|
}
|
|
260
|
-
if (changedPaths.length > 0) {
|
|
261
|
-
const fileEntries = this.buildFileEntriesFromCache(
|
|
259
|
+
if (changedPaths.length > 0 || removedAny) {
|
|
260
|
+
const fileEntries = this.buildFileEntriesFromCache();
|
|
262
261
|
require_singleton.resetAnalysisPromise();
|
|
263
262
|
const promise = this.runIncrementalPipeline(fileEntries);
|
|
264
263
|
require_singleton.setAnalysisPromise(promise);
|
|
265
264
|
await promise;
|
|
266
265
|
}
|
|
267
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Preprocess one `.mdx` entry into scanner-consumable tsx. Returns the
|
|
269
|
+
* rewritten source plus the `relPath + '.tsx'` path on success, or null
|
|
270
|
+
* when the file must be skipped.
|
|
271
|
+
*
|
|
272
|
+
* `warn: false` (incremental watch pass) skips silently — the full
|
|
273
|
+
* pipeline already surfaced the warning. `warn: true` (full pipeline)
|
|
274
|
+
* warns ONCE for a missing @mdx-js/mdx dependency via the shared
|
|
275
|
+
* `missingDepFlag` holder, and every time for a preprocessing error.
|
|
276
|
+
*/
|
|
277
|
+
async preprocessMdxEntry(source, relPath, opts) {
|
|
278
|
+
const result = await (0, _animus_ui_extract_pipeline.preprocessMdx)(source, relPath);
|
|
279
|
+
if (result.kind === "missing-dep") {
|
|
280
|
+
if (opts.warn && opts.missingDepFlag && !opts.missingDepFlag.warned) {
|
|
281
|
+
console.warn("[animus] ⚠ .mdx in extensions but @mdx-js/mdx not installed; MDX files skipped");
|
|
282
|
+
opts.missingDepFlag.warned = true;
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
if (result.kind === "error") {
|
|
287
|
+
if (opts.warn) console.warn(`[animus] ⚠ MDX preprocessing failed for ${relPath}: ${result.error}`);
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
source: result.source,
|
|
292
|
+
relPath: relPath + ".tsx"
|
|
293
|
+
};
|
|
294
|
+
}
|
|
268
295
|
async runFullPipeline() {
|
|
269
296
|
const pipelineStart = this.now();
|
|
270
297
|
const bt = {};
|
|
271
|
-
|
|
272
|
-
const { clearAnalysisCache } = require_singleton.engineApi();
|
|
273
|
-
clearAnalysisCache();
|
|
274
|
-
} catch {}
|
|
298
|
+
(0, _animus_ui_extract_pipeline.clearEngineCache)(require_singleton.engineApi);
|
|
275
299
|
const rootDir = this.rootDir;
|
|
276
300
|
const resolvedSystemPath = (0, path.resolve)(rootDir, this.options.system);
|
|
277
301
|
let t = this.now();
|
|
278
|
-
this.
|
|
302
|
+
this.system = (0, _animus_ui_extract_pipeline.loadSystemConfig)(require_singleton.engineApi, {
|
|
303
|
+
systemPath: resolvedSystemPath,
|
|
304
|
+
rootDir,
|
|
305
|
+
prefix: this.options.prefix
|
|
306
|
+
});
|
|
307
|
+
{
|
|
308
|
+
const deps = this.system.dependencies ?? [];
|
|
309
|
+
const keys = /* @__PURE__ */ new Set();
|
|
310
|
+
for (const key of (0, _animus_ui_extract_pipeline.toWatchKeys)(resolvedSystemPath)) keys.add(key);
|
|
311
|
+
for (const dep of deps) for (const key of (0, _animus_ui_extract_pipeline.toWatchKeys)(dep)) keys.add(key);
|
|
312
|
+
this.systemDependencyKeys = keys;
|
|
313
|
+
this.systemDependencyPaths = deps;
|
|
314
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
315
|
+
for (const dep of deps.length > 0 ? deps : [resolvedSystemPath]) try {
|
|
316
|
+
hashes.set(dep, (0, _animus_ui_extract_pipeline.contentHash)((0, fs.readFileSync)(dep, "utf-8")));
|
|
317
|
+
} catch {
|
|
318
|
+
hashes.set(dep, "");
|
|
319
|
+
}
|
|
320
|
+
this.systemDependencyHashes = hashes;
|
|
321
|
+
}
|
|
279
322
|
bt.systemLoad = this.elapsed(t);
|
|
280
323
|
t = this.now();
|
|
281
|
-
const excludePatterns = this.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
".test.",
|
|
285
|
-
".spec.",
|
|
286
|
-
".next",
|
|
287
|
-
".animus"
|
|
288
|
-
];
|
|
289
|
-
const extensionsSet = new Set(this.options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
290
|
-
const shouldHandleMdx = extensionsSet.has(".mdx");
|
|
291
|
-
let mdxMissingDepWarned = false;
|
|
292
|
-
const files = this.discoverFiles(rootDir, rootDir, excludePatterns, extensionsSet);
|
|
324
|
+
const { excludePatterns, extensionsSet, shouldHandleMdx } = this.resolveScanConfig();
|
|
325
|
+
const missingDepFlag = { warned: false };
|
|
326
|
+
const files = (0, _animus_ui_extract_pipeline.discoverFiles)(rootDir, rootDir, excludePatterns, extensionsSet);
|
|
293
327
|
bt.fileDiscovery = this.elapsed(t);
|
|
294
328
|
t = this.now();
|
|
295
329
|
const fileEntries = [];
|
|
@@ -297,22 +331,15 @@ var AnimusWebpackPlugin = class {
|
|
|
297
331
|
let source = (0, fs.readFileSync)(filePath, "utf-8");
|
|
298
332
|
let relPath = (0, path.relative)(rootDir, filePath);
|
|
299
333
|
if (shouldHandleMdx && (0, path.extname)(filePath) === ".mdx") {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
if (result.kind === "error") {
|
|
309
|
-
console.warn(`[animus] ⚠ MDX preprocessing failed for ${relPath}: ${result.error}`);
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
source = result.source;
|
|
313
|
-
relPath = relPath + ".tsx";
|
|
334
|
+
const processed = await this.preprocessMdxEntry(source, relPath, {
|
|
335
|
+
warn: true,
|
|
336
|
+
missingDepFlag
|
|
337
|
+
});
|
|
338
|
+
if (!processed) continue;
|
|
339
|
+
source = processed.source;
|
|
340
|
+
relPath = processed.relPath;
|
|
314
341
|
}
|
|
315
|
-
const hash = (0,
|
|
342
|
+
const hash = (0, _animus_ui_extract_pipeline.contentHash)(source);
|
|
316
343
|
this.fileCache.set(relPath, {
|
|
317
344
|
hash,
|
|
318
345
|
source
|
|
@@ -327,216 +354,46 @@ var AnimusWebpackPlugin = class {
|
|
|
327
354
|
bt.fileCount = fileEntries.length;
|
|
328
355
|
t = this.now();
|
|
329
356
|
const packageNames = (0, _animus_ui_extract_pipeline.extractSystemFilePackages)(resolvedSystemPath);
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
this.externalSourceEntries.set(specifier, srcEntry);
|
|
349
|
-
}
|
|
350
|
-
const pkgFiles = this.discoverFiles(srcDir, rootDir, excludePatternsForPkgs, extensionsSet);
|
|
351
|
-
for (const pkgFile of pkgFiles) {
|
|
352
|
-
let pkgRelPath = (0, path.relative)(rootDir, pkgFile);
|
|
353
|
-
if (!fileEntries.some((e) => e.path === pkgRelPath)) try {
|
|
354
|
-
let pkgSource = (0, fs.readFileSync)(pkgFile, "utf-8");
|
|
355
|
-
if (shouldHandleMdx && (0, path.extname)(pkgFile) === ".mdx") {
|
|
356
|
-
const result = await (0, _animus_ui_extract_pipeline.preprocessMdx)(pkgSource, pkgRelPath);
|
|
357
|
-
if (result.kind === "missing-dep") {
|
|
358
|
-
if (!mdxMissingDepWarned) {
|
|
359
|
-
console.warn("[animus] ⚠ .mdx in extensions but @mdx-js/mdx not installed; MDX files skipped");
|
|
360
|
-
mdxMissingDepWarned = true;
|
|
361
|
-
}
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
if (result.kind === "error") {
|
|
365
|
-
console.warn(`[animus] ⚠ MDX preprocessing failed for ${pkgRelPath}: ${result.error}`);
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
pkgSource = result.source;
|
|
369
|
-
pkgRelPath = pkgRelPath + ".tsx";
|
|
370
|
-
}
|
|
371
|
-
const pkgHash = (0, crypto.createHash)("md5").update(pkgSource).digest("hex");
|
|
372
|
-
this.fileCache.set(pkgRelPath, {
|
|
373
|
-
hash: pkgHash,
|
|
374
|
-
source: pkgSource
|
|
375
|
-
});
|
|
376
|
-
fileEntries.push({
|
|
377
|
-
path: pkgRelPath,
|
|
378
|
-
source: pkgSource,
|
|
379
|
-
hash: pkgHash
|
|
380
|
-
});
|
|
381
|
-
} catch {}
|
|
382
|
-
}
|
|
383
|
-
} else collectedPkgDirs.push((0, path.dirname)(absEntry));
|
|
384
|
-
}
|
|
385
|
-
this.externalPackageDirs = collectedPkgDirs;
|
|
386
|
-
require_singleton.setSharedExternalDirs(collectedPkgDirs);
|
|
387
|
-
require_singleton.setSharedExternalEntries(this.externalSourceEntries);
|
|
388
|
-
bt.packageResolve = this.elapsed(t);
|
|
389
|
-
const { analyzeProject } = require_singleton.engineApi();
|
|
390
|
-
const animusDirPath = (0, path.join)(rootDir, ".animus");
|
|
391
|
-
const emitterConfig = JSON.stringify({
|
|
392
|
-
runtime_import: "@animus-ui/system/runtime",
|
|
393
|
-
css_module_id: ".animus/styles.css",
|
|
394
|
-
system_props_module_id: (0, path.join)(animusDirPath, "system-props.js")
|
|
395
|
-
});
|
|
396
|
-
t = this.now();
|
|
397
|
-
const fileEntriesJson = JSON.stringify(fileEntries);
|
|
398
|
-
const packageMapJson = JSON.stringify(packageMap);
|
|
399
|
-
bt.jsonSerialize = this.elapsed(t);
|
|
400
|
-
t = this.now();
|
|
401
|
-
const manifestJson = analyzeProject(...buildProductionAnalyzeProjectArgs({
|
|
402
|
-
filesJson: fileEntriesJson,
|
|
403
|
-
scalesJson: this.themeJson,
|
|
404
|
-
variableMapJson: this.variableMapJson,
|
|
405
|
-
contextualVarsJson: this.contextualVarsJson || null,
|
|
406
|
-
propConfigJson: this.configJson,
|
|
407
|
-
groupRegistryJson: this.groupRegistryJson,
|
|
408
|
-
packageResolutionJson: packageMapJson,
|
|
409
|
-
emitterConfigJson: emitterConfig,
|
|
410
|
-
selectorAliasesJson: this.selectorAliasesJson,
|
|
411
|
-
globalStyleBlocksJson: this.globalStyleBlocksJson,
|
|
412
|
-
pathAliasesJson: this.pathAliasesJson,
|
|
413
|
-
keyframesJson: this.keyframesBlocksJson
|
|
414
|
-
}));
|
|
415
|
-
bt.rustExtract = this.elapsed(t);
|
|
416
|
-
t = this.now();
|
|
417
|
-
const manifest = JSON.parse(manifestJson);
|
|
418
|
-
surfaceManifestDiagnostics(manifest, (message) => this.warn(message));
|
|
419
|
-
bt.jsonParse = this.elapsed(t);
|
|
420
|
-
bt.analysis = (bt.jsonSerialize ?? 0) + (bt.rustExtract ?? 0) + (bt.jsonParse ?? 0);
|
|
421
|
-
if (manifest?.report) this.log(`Extracted ${manifest.report.components_extracted ?? "?"}/${manifest.report.components_total ?? "?"} components (${bt.analysis}ms)`);
|
|
422
|
-
this.globalCss = manifest?.sheets?.global || "";
|
|
423
|
-
let componentCss = manifest?.css || "";
|
|
424
|
-
componentCss = (0, _animus_ui_extract_pipeline.applyUnitFallback)(componentCss);
|
|
425
|
-
const { declaration, variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
426
|
-
layers: this.options.layers,
|
|
427
|
-
variableCss: this.variableCss,
|
|
428
|
-
globalCss: this.globalCss,
|
|
429
|
-
componentCss,
|
|
430
|
-
split: true
|
|
357
|
+
const preResolved = resolvePackagesByName(rootDir, packageNames);
|
|
358
|
+
const collected = await (0, _animus_ui_extract_pipeline.collectExternalPackageSources)({
|
|
359
|
+
specifiers: packageNames,
|
|
360
|
+
resolveSpecifier: (name) => preResolved[name] ? (0, path.resolve)(rootDir, preResolved[name]) : null,
|
|
361
|
+
rootDir,
|
|
362
|
+
extensionsSet,
|
|
363
|
+
hasEntry: (relPath) => fileEntries.some((e) => e.path === relPath),
|
|
364
|
+
preprocessFile: async (source, relPath, absPath) => {
|
|
365
|
+
if (shouldHandleMdx && (0, path.extname)(absPath) === ".mdx") return this.preprocessMdxEntry(source, relPath, {
|
|
366
|
+
warn: true,
|
|
367
|
+
missingDepFlag
|
|
368
|
+
});
|
|
369
|
+
return {
|
|
370
|
+
source,
|
|
371
|
+
relPath
|
|
372
|
+
};
|
|
373
|
+
},
|
|
374
|
+
onUnreadable: (relPath, err) => this.warn(`skipped unreadable package file ${relPath}: ${String(err)}`)
|
|
431
375
|
});
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const dynamicPropConfig = {};
|
|
448
|
-
for (const [propName, meta] of Object.entries(dynamicProps)) dynamicPropConfig[propName] = {
|
|
449
|
-
varName: meta.var_name,
|
|
450
|
-
slotClass: meta.slot_class,
|
|
451
|
-
...meta.transform_name ? { transformName: meta.transform_name } : {},
|
|
452
|
-
...meta.scale_values && Object.keys(meta.scale_values).length > 0 ? { scaleValues: meta.scale_values } : {}
|
|
453
|
-
};
|
|
454
|
-
const transformsSource = "{}";
|
|
455
|
-
const animusDir = (0, path.join)(rootDir, ".animus");
|
|
456
|
-
if (!(0, fs.existsSync)(animusDir)) (0, fs.mkdirSync)(animusDir, { recursive: true });
|
|
457
|
-
const systemPropsContent = `export const systemPropMap = ${systemPropMap};\nexport const systemPropGroups = ${this.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${transformsSource};\n`;
|
|
458
|
-
require_singleton.setSharedSystemProps(systemPropsContent);
|
|
459
|
-
(0, fs.writeFileSync)((0, path.join)(animusDir, "system-props.js"), systemPropsContent);
|
|
460
|
-
require_singleton.setManifestJson(manifestJson);
|
|
461
|
-
bt.total = this.elapsed(pipelineStart);
|
|
462
|
-
this.logBuildTimings(bt, manifest?.timing);
|
|
463
|
-
}
|
|
464
|
-
loadSystem(rootDir, systemPath) {
|
|
465
|
-
const { loadSystemModule } = require_singleton.engineApi();
|
|
466
|
-
const config = loadSystemModule(systemPath, rootDir);
|
|
467
|
-
this.configJson = config.propConfig;
|
|
468
|
-
this.groupRegistryJson = config.groupRegistry;
|
|
469
|
-
this.themeJson = config.scalesJson;
|
|
470
|
-
this.variableMapJson = config.variableMapJson;
|
|
471
|
-
this.variableCss = config.variableCss;
|
|
472
|
-
this.contextualVarsJson = config.contextualVarsJson;
|
|
473
|
-
this.selectorAliasesJson = config.selectorAliases || null;
|
|
474
|
-
if (this.options.prefix) {
|
|
475
|
-
const prefixed = (0, _animus_ui_extract_pipeline.applyPrefix)(this.options.prefix, this.variableMapJson, this.variableCss, this.themeJson, this.contextualVarsJson || void 0);
|
|
476
|
-
this.variableMapJson = prefixed.variableMapJson;
|
|
477
|
-
this.variableCss = prefixed.variableCss;
|
|
478
|
-
if (prefixed.themeJson) this.themeJson = prefixed.themeJson;
|
|
479
|
-
if (prefixed.contextualVarsJson) this.contextualVarsJson = prefixed.contextualVarsJson;
|
|
480
|
-
}
|
|
481
|
-
this.globalStyleBlocksJson = config.globalStyleBlocks || null;
|
|
482
|
-
this.keyframesBlocksJson = config.keyframesBlocks || null;
|
|
483
|
-
}
|
|
484
|
-
discoverFiles(dir, rootDir, excludePatterns, extensionsSet) {
|
|
485
|
-
const results = [];
|
|
486
|
-
const walk = (currentDir) => {
|
|
487
|
-
let entries;
|
|
488
|
-
try {
|
|
489
|
-
entries = (0, fs.readdirSync)(currentDir);
|
|
490
|
-
} catch {
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
for (const entry of entries) {
|
|
494
|
-
const fullPath = (0, path.join)(currentDir, entry);
|
|
495
|
-
const relPath = (0, path.relative)(rootDir, fullPath);
|
|
496
|
-
if (excludePatterns.some((p) => relPath.includes(p))) continue;
|
|
497
|
-
let stat;
|
|
498
|
-
try {
|
|
499
|
-
stat = (0, fs.statSync)(fullPath);
|
|
500
|
-
} catch {
|
|
501
|
-
continue;
|
|
502
|
-
}
|
|
503
|
-
if (stat.isDirectory()) walk(fullPath);
|
|
504
|
-
else if (extensionsSet.has((0, path.extname)(entry))) results.push(fullPath);
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
walk(dir);
|
|
508
|
-
return results;
|
|
509
|
-
}
|
|
510
|
-
resolvePackagesByName(rootDir, names) {
|
|
511
|
-
if (names.length === 0) return {};
|
|
512
|
-
const nameSet = new Set(names);
|
|
513
|
-
const resolved = /* @__PURE__ */ new Set();
|
|
514
|
-
const packageMap = {};
|
|
515
|
-
try {
|
|
516
|
-
const workspaces = JSON.parse((0, fs.readFileSync)((0, path.join)(rootDir, "package.json"), "utf-8")).workspaces || [];
|
|
517
|
-
for (const ws of workspaces) {
|
|
518
|
-
const wsDir = (0, path.resolve)(rootDir, ws);
|
|
519
|
-
if (!(0, fs.existsSync)(wsDir)) continue;
|
|
520
|
-
try {
|
|
521
|
-
const pkg = JSON.parse((0, fs.readFileSync)((0, path.join)(wsDir, "package.json"), "utf-8"));
|
|
522
|
-
const name = pkg.name || "";
|
|
523
|
-
if (nameSet.has(name)) {
|
|
524
|
-
const entryPath = (0, path.resolve)(wsDir, pkg.main || pkg.module || "index.ts");
|
|
525
|
-
if ((0, fs.existsSync)(entryPath)) {
|
|
526
|
-
packageMap[name] = (0, path.relative)(rootDir, entryPath);
|
|
527
|
-
resolved.add(name);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
} catch {}
|
|
531
|
-
}
|
|
532
|
-
} catch {}
|
|
533
|
-
for (const name of nameSet) {
|
|
534
|
-
if (resolved.has(name)) continue;
|
|
535
|
-
try {
|
|
536
|
-
packageMap[name] = (0, path.relative)(rootDir, require.resolve(name, { paths: [rootDir] }));
|
|
537
|
-
} catch {}
|
|
376
|
+
for (const record of collected.outcomes) if (record.outcome === "empty") this.warn(`include '${record.specifier}' resolved but discovered no component sources`);
|
|
377
|
+
const packageMap = collected.packageMap;
|
|
378
|
+
this.lastPackageMap = packageMap;
|
|
379
|
+
this.externalSourceEntries = collected.sourceEntries;
|
|
380
|
+
for (const entry of collected.entries) {
|
|
381
|
+
const hash = (0, _animus_ui_extract_pipeline.contentHash)(entry.source);
|
|
382
|
+
this.fileCache.set(entry.path, {
|
|
383
|
+
hash,
|
|
384
|
+
source: entry.source
|
|
385
|
+
});
|
|
386
|
+
fileEntries.push({
|
|
387
|
+
path: entry.path,
|
|
388
|
+
source: entry.source,
|
|
389
|
+
hash
|
|
390
|
+
});
|
|
538
391
|
}
|
|
539
|
-
|
|
392
|
+
this.externalPackageDirs = collected.packageDirs;
|
|
393
|
+
require_singleton.setSharedExternalDirs(collected.packageDirs);
|
|
394
|
+
require_singleton.setSharedExternalEntries(collected.sourceEntries);
|
|
395
|
+
bt.packageResolve = this.elapsed(t);
|
|
396
|
+
await this.analyzeAndEmit(fileEntries, packageMap, false, bt, pipelineStart);
|
|
540
397
|
}
|
|
541
398
|
/**
|
|
542
399
|
* Reset analysis state for HMR geological reset.
|
|
@@ -545,22 +402,21 @@ var AnimusWebpackPlugin = class {
|
|
|
545
402
|
require_singleton.resetAnalysisPromise();
|
|
546
403
|
this.lastCssHash = null;
|
|
547
404
|
this.lastSystemPropsHash = null;
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
} catch {}
|
|
405
|
+
this.lastManifestHash = null;
|
|
406
|
+
this.lastAnalysisInputsHash = null;
|
|
407
|
+
(0, _animus_ui_extract_pipeline.clearEngineCache)(require_singleton.engineApi);
|
|
552
408
|
}
|
|
553
409
|
/**
|
|
554
|
-
* Build
|
|
555
|
-
*
|
|
410
|
+
* Build file entries from cache: every cached file rides with full source.
|
|
411
|
+
* The v2 engine has NO Rust-side cache (extract-v2-spine DEF-7: uncached
|
|
412
|
+
* re-analysis beats a cache-hit path), so it must always receive full sources
|
|
413
|
+
* (openspec: retire-extract-v1 removed the v1 empty-source cache contract).
|
|
556
414
|
*/
|
|
557
|
-
buildFileEntriesFromCache(
|
|
558
|
-
const changedSet = new Set(changedPaths);
|
|
559
|
-
const fullSources = require_singleton.getSharedEngine() === "v2";
|
|
415
|
+
buildFileEntriesFromCache() {
|
|
560
416
|
const entries = [];
|
|
561
|
-
for (const [path$
|
|
562
|
-
path: path$
|
|
563
|
-
source
|
|
417
|
+
for (const [path$3, { hash, source }] of this.fileCache) entries.push({
|
|
418
|
+
path: path$3,
|
|
419
|
+
source,
|
|
564
420
|
hash
|
|
565
421
|
});
|
|
566
422
|
return entries;
|
|
@@ -570,88 +426,238 @@ var AnimusWebpackPlugin = class {
|
|
|
570
426
|
* Reuses system config from the last full pipeline run.
|
|
571
427
|
*/
|
|
572
428
|
async runIncrementalPipeline(fileEntries) {
|
|
573
|
-
const rootDir = this.rootDir;
|
|
574
429
|
const bt = {};
|
|
575
430
|
const pipelineStart = this.now();
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
431
|
+
await this.analyzeAndEmit(fileEntries, this.lastPackageMap, true, bt, pipelineStart);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Shared analysis + emit core for both pipelines — the single call site
|
|
435
|
+
* that routes every manifest through the shared `runProjectAnalysis`,
|
|
436
|
+
* reachable from both runFullPipeline (production) and
|
|
437
|
+
* runIncrementalPipeline (HMR).
|
|
438
|
+
*
|
|
439
|
+
* Owns diagnostic surfacing, CSS assembly + styles.css write guard,
|
|
440
|
+
* system-props module emit, and the timing log. The `devMode` flag is the
|
|
441
|
+
* ONLY behavioral fork:
|
|
442
|
+
*
|
|
443
|
+
* - `false` (production): computes bt.analysis + logs the extraction
|
|
444
|
+
* report, and writes system-props.js UNCONDITIONALLY (no
|
|
445
|
+
* lastSystemPropsHash guard).
|
|
446
|
+
* - `true` (HMR): skips the report log, and guards the system-props.js
|
|
447
|
+
* write by lastSystemPropsHash.
|
|
448
|
+
*
|
|
449
|
+
* The styles.css write guard (lastCssHash) is identical on both paths.
|
|
450
|
+
*/
|
|
451
|
+
async analyzeAndEmit(fileEntries, packageMap, devMode, bt, pipelineStart) {
|
|
452
|
+
const system = this.system;
|
|
453
|
+
const result = (0, _animus_ui_extract_pipeline.runProjectAnalysis)(require_singleton.engineApi, {
|
|
454
|
+
fileEntries,
|
|
455
|
+
packageMap,
|
|
456
|
+
system,
|
|
457
|
+
emitter: {
|
|
458
|
+
runtimeImport: "@animus-ui/system/runtime",
|
|
459
|
+
cssModuleId: ANIMUS_CSS_MODULE_ID,
|
|
460
|
+
systemPropsModuleId: this.systemPropsModuleId ?? (0, path.join)(this.rootDir, ".animus", "system-props.js")
|
|
461
|
+
},
|
|
599
462
|
pathAliasesJson: this.pathAliasesJson,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
bt.jsonParse =
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
463
|
+
staticCssJson: this.staticCssJson,
|
|
464
|
+
devMode,
|
|
465
|
+
warn: (message) => this.warn(message)
|
|
466
|
+
});
|
|
467
|
+
bt.jsonSerialize = result.timings.serializeMs;
|
|
468
|
+
bt.rustExtract = result.timings.extractMs;
|
|
469
|
+
bt.jsonParse = result.timings.parseMs;
|
|
470
|
+
const manifest = result.manifest;
|
|
471
|
+
if (!devMode) {
|
|
472
|
+
bt.analysis = (bt.jsonSerialize ?? 0) + (bt.rustExtract ?? 0) + (bt.jsonParse ?? 0);
|
|
473
|
+
if (manifest?.report) this.log(`Extracted ${manifest.report.components_extracted ?? "?"}/${manifest.report.components_total ?? "?"} components (${bt.analysis}ms)`);
|
|
474
|
+
}
|
|
475
|
+
const { declaration, variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
611
476
|
layers: this.options.layers,
|
|
612
|
-
variableCss:
|
|
613
|
-
globalCss:
|
|
614
|
-
componentCss,
|
|
477
|
+
variableCss: system.variableCss,
|
|
478
|
+
globalCss: result.globalCss,
|
|
479
|
+
componentCss: result.componentCss,
|
|
615
480
|
split: true
|
|
616
481
|
});
|
|
482
|
+
if (this.lcssTargets === null) this.lcssTargets = (0, _animus_ui_extract_pipeline.resolveLightningTargets)(this.options.targets, this.rootDir);
|
|
617
483
|
const fullCss = [
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
484
|
+
declaration,
|
|
485
|
+
variables,
|
|
486
|
+
(0, _animus_ui_extract_pipeline.postProcessCss)(body, {
|
|
487
|
+
minify: this.options.minify ?? process.env.NODE_ENV === "production",
|
|
488
|
+
targets: this.lcssTargets,
|
|
489
|
+
warnFn: (msg) => this.warn(msg)
|
|
490
|
+
})
|
|
621
491
|
].filter(Boolean).join("\n");
|
|
622
492
|
require_singleton.setSharedCss(fullCss);
|
|
623
|
-
const cssHash = (0,
|
|
493
|
+
const cssHash = (0, _animus_ui_extract_pipeline.contentHash)(fullCss);
|
|
624
494
|
if (cssHash !== this.lastCssHash) {
|
|
625
|
-
|
|
626
|
-
if (!(0, fs.existsSync)(animusDir)) (0, fs.mkdirSync)(animusDir, { recursive: true });
|
|
627
|
-
(0, fs.writeFileSync)((0, path.join)(animusDir, "styles.css"), fullCss);
|
|
495
|
+
this.writeAnimusFile("styles.css", fullCss);
|
|
628
496
|
this.lastCssHash = cssHash;
|
|
629
497
|
}
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
slotClass: meta.slot_class,
|
|
636
|
-
...meta.transform_name ? { transformName: meta.transform_name } : {},
|
|
637
|
-
...meta.scale_values && Object.keys(meta.scale_values).length > 0 ? { scaleValues: meta.scale_values } : {}
|
|
638
|
-
};
|
|
639
|
-
const systemPropsContent = `export const systemPropMap = ${systemPropMap};\nexport const systemPropGroups = ${this.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = {};\n`;
|
|
498
|
+
const systemPropsContent = (0, _animus_ui_extract_pipeline.buildSystemPropsModule)({
|
|
499
|
+
systemPropMapJson: JSON.stringify(manifest?.system_prop_map ?? {}),
|
|
500
|
+
groupRegistryJson: system.groupRegistryJson,
|
|
501
|
+
dynamicProps: manifest?.dynamic_props ?? {}
|
|
502
|
+
});
|
|
640
503
|
require_singleton.setSharedSystemProps(systemPropsContent);
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
504
|
+
if (devMode) {
|
|
505
|
+
const systemPropsHash = (0, _animus_ui_extract_pipeline.contentHash)(systemPropsContent);
|
|
506
|
+
if (systemPropsHash !== this.lastSystemPropsHash) {
|
|
507
|
+
this.writeAnimusFile("system-props.js", systemPropsContent);
|
|
508
|
+
this.lastSystemPropsHash = systemPropsHash;
|
|
509
|
+
}
|
|
510
|
+
} else this.writeAnimusFile("system-props.js", systemPropsContent);
|
|
511
|
+
require_singleton.setManifestJson(result.manifestJson);
|
|
512
|
+
const manifestHash = (0, _animus_ui_extract_pipeline.contentHash)(result.manifestJson);
|
|
513
|
+
if (this.lastManifestHash === null) this.lastManifestHash = this.diskArtifactHash("manifest.json");
|
|
514
|
+
if (manifestHash !== this.lastManifestHash) {
|
|
515
|
+
this.writeAnimusFile("manifest.json", result.manifestJson);
|
|
516
|
+
this.lastManifestHash = manifestHash;
|
|
517
|
+
}
|
|
518
|
+
if (this.persistAnalysisInputs) {
|
|
519
|
+
const inputsJson = JSON.stringify(result.inputs);
|
|
520
|
+
const inputsHash = (0, _animus_ui_extract_pipeline.contentHash)(inputsJson);
|
|
521
|
+
if (this.lastAnalysisInputsHash === null) this.lastAnalysisInputsHash = this.diskArtifactHash("analysis-inputs.json");
|
|
522
|
+
if (inputsHash !== this.lastAnalysisInputsHash) {
|
|
523
|
+
this.writeAnimusFile("analysis-inputs.json", inputsJson);
|
|
524
|
+
this.lastAnalysisInputsHash = inputsHash;
|
|
525
|
+
}
|
|
647
526
|
}
|
|
648
|
-
require_singleton.setManifestJson(manifestJson);
|
|
649
527
|
bt.total = this.elapsed(pipelineStart);
|
|
650
|
-
|
|
528
|
+
logBuildTimings(bt, manifest?.timing, (msg) => this.log(msg), this.verbose);
|
|
529
|
+
}
|
|
530
|
+
/** Content hash of an existing `.animus/` artifact, or null when absent or
|
|
531
|
+
* unreadable. Seeds a null write guard so a FRESH session never rewrites a
|
|
532
|
+
* byte-identical artifact (spec: next-turbopack-integration — a rewrite
|
|
533
|
+
* changes mtime/inode and needlessly re-keys Turbopack loader-worker
|
|
534
|
+
* hydration). */
|
|
535
|
+
diskArtifactHash(name) {
|
|
536
|
+
try {
|
|
537
|
+
return (0, _animus_ui_extract_pipeline.contentHash)((0, fs.readFileSync)((0, path.join)(this.rootDir, ".animus", name), "utf-8"));
|
|
538
|
+
} catch {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/** Ensure `.animus/` exists and write one generated artifact into it.
|
|
543
|
+
* Write-then-rename so cross-process readers (Turbopack loader workers)
|
|
544
|
+
* can never observe a torn half-written file. The tmp name carries the
|
|
545
|
+
* pid — Next dev evaluates the config in more than one process, and two
|
|
546
|
+
* sessions writing the same artifact must not race on one tmp path. */
|
|
547
|
+
writeAnimusFile(name, content) {
|
|
548
|
+
const dir = (0, path.join)(this.rootDir, ".animus");
|
|
549
|
+
if (!(0, fs.existsSync)(dir)) (0, fs.mkdirSync)(dir, { recursive: true });
|
|
550
|
+
const tmpPath = (0, path.join)(dir, `.${name}.${process.pid}.tmp`);
|
|
551
|
+
(0, fs.writeFileSync)(tmpPath, content);
|
|
552
|
+
(0, fs.renameSync)(tmpPath, (0, path.join)(dir, name));
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/plugin.ts
|
|
557
|
+
const PLUGIN_NAME = "AnimusWebpackPlugin";
|
|
558
|
+
/**
|
|
559
|
+
* Webpack adapter for the extraction pipeline. All pipeline logic lives in
|
|
560
|
+
* the bundler-agnostic ExtractionSession — this class owns only the webpack
|
|
561
|
+
* wiring: hook registration, cross-compiler analysis dedup, alias
|
|
562
|
+
* harvesting from the compiler config, watch-event translation, and
|
|
563
|
+
* in-memory CSS asset replacement.
|
|
564
|
+
*/
|
|
565
|
+
var AnimusWebpackPlugin = class {
|
|
566
|
+
options;
|
|
567
|
+
session;
|
|
568
|
+
initialized = false;
|
|
569
|
+
aliasesExtracted = false;
|
|
570
|
+
constructor(options) {
|
|
571
|
+
this.options = options;
|
|
572
|
+
(0, _animus_ui_extract_pipeline.assertNoRetiredEngineSelection)(options.engine);
|
|
573
|
+
require_singleton.setSharedEngine(options.engine ?? "v2");
|
|
574
|
+
this.session = new ExtractionSession(options);
|
|
651
575
|
}
|
|
652
|
-
/**
|
|
653
|
-
|
|
654
|
-
|
|
576
|
+
/** Extract path aliases from webpack's resolve.alias config. Runs once per
|
|
577
|
+
* plugin instance — the resolve config is immutable after apply. */
|
|
578
|
+
extractAliases(compiler) {
|
|
579
|
+
if (this.aliasesExtracted) return;
|
|
580
|
+
this.aliasesExtracted = true;
|
|
581
|
+
const rootDir = compiler.context;
|
|
582
|
+
const rawAlias = compiler.options?.resolve?.alias;
|
|
583
|
+
if (!rawAlias || typeof rawAlias !== "object") return;
|
|
584
|
+
const pairs = [];
|
|
585
|
+
for (const [key, value] of Object.entries(rawAlias)) {
|
|
586
|
+
const target = Array.isArray(value) ? value[0] : value;
|
|
587
|
+
if (typeof target !== "string") continue;
|
|
588
|
+
if (key === ".animus/styles.css") continue;
|
|
589
|
+
pairs.push({
|
|
590
|
+
pattern: key,
|
|
591
|
+
target
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
const built = (0, _animus_ui_extract_pipeline.buildPathAliasesJson)(pairs, rootDir);
|
|
595
|
+
if (built) this.session.pathAliasesJson = built.json;
|
|
596
|
+
}
|
|
597
|
+
apply(compiler) {
|
|
598
|
+
if (compiler.options?.name === "edge-server") return;
|
|
599
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
600
|
+
const registerSystemDependencies = () => {
|
|
601
|
+
for (const dep of this.session.systemDependencyPaths) if ((0, fs.existsSync)(dep)) compilation.fileDependencies.add(dep);
|
|
602
|
+
else compilation.missingDependencies.add(dep);
|
|
603
|
+
};
|
|
604
|
+
registerSystemDependencies();
|
|
605
|
+
const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL ?? -2e3;
|
|
606
|
+
const RawSource = compiler.webpack?.sources.RawSource;
|
|
607
|
+
compilation.hooks.processAssets.tap({
|
|
608
|
+
name: PLUGIN_NAME,
|
|
609
|
+
stage
|
|
610
|
+
}, () => {
|
|
611
|
+
registerSystemDependencies();
|
|
612
|
+
const css = require_singleton.getSharedCss();
|
|
613
|
+
if (!css || !RawSource) return;
|
|
614
|
+
const cssPath = (0, path.join)(this.session.rootDir || compiler.context, ".animus", "styles.css");
|
|
615
|
+
if (compilation.getAsset(cssPath)) {
|
|
616
|
+
compilation.updateAsset(cssPath, new RawSource(css));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (compilation.getAsset(".animus/styles.css")) compilation.updateAsset(ANIMUS_CSS_MODULE_ID, new RawSource(css));
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
compiler.hooks.run.tapPromise(PLUGIN_NAME, async (_compiler) => {
|
|
623
|
+
this.session.rootDir = _compiler.context;
|
|
624
|
+
this.extractAliases(_compiler);
|
|
625
|
+
const existing = require_singleton.getAnalysisPromise();
|
|
626
|
+
if (existing) {
|
|
627
|
+
await existing;
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const promise = this.session.runFullPipeline();
|
|
631
|
+
require_singleton.setAnalysisPromise(promise);
|
|
632
|
+
await promise;
|
|
633
|
+
});
|
|
634
|
+
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async (_compiler) => {
|
|
635
|
+
this.session.rootDir = _compiler.context;
|
|
636
|
+
this.extractAliases(_compiler);
|
|
637
|
+
if (!this.initialized) {
|
|
638
|
+
const existing = require_singleton.getAnalysisPromise();
|
|
639
|
+
if (existing) {
|
|
640
|
+
await existing;
|
|
641
|
+
this.initialized = true;
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const promise = this.session.runFullPipeline();
|
|
645
|
+
require_singleton.setAnalysisPromise(promise);
|
|
646
|
+
await promise;
|
|
647
|
+
this.initialized = true;
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
await this.session.handleWatchUpdate({
|
|
651
|
+
modifiedFiles: _compiler.modifiedFiles,
|
|
652
|
+
removedFiles: _compiler.removedFiles
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Reset analysis state for HMR geological reset.
|
|
658
|
+
*/
|
|
659
|
+
resetForHmr() {
|
|
660
|
+
this.session.resetForHmr();
|
|
655
661
|
}
|
|
656
662
|
/** Expose options for the loader */
|
|
657
663
|
getOptions() {
|
|
@@ -660,18 +666,187 @@ var AnimusWebpackPlugin = class {
|
|
|
660
666
|
/** Expose external package directories for webpack loader allowlisting.
|
|
661
667
|
* Falls back to shared globalThis state for non-owning compiler instances. */
|
|
662
668
|
getExternalPackageDirs() {
|
|
663
|
-
return this.externalPackageDirs.length > 0 ? this.externalPackageDirs : require_singleton.getSharedExternalDirs();
|
|
669
|
+
return this.session.externalPackageDirs.length > 0 ? this.session.externalPackageDirs : require_singleton.getSharedExternalDirs();
|
|
664
670
|
}
|
|
665
671
|
/** Expose external package source entries for webpack resolve alias.
|
|
666
672
|
* Falls back to shared globalThis state for non-owning compiler instances. */
|
|
667
673
|
getExternalSourceEntries() {
|
|
668
|
-
return this.externalSourceEntries.size > 0 ? this.externalSourceEntries : require_singleton.getSharedExternalEntries();
|
|
674
|
+
return this.session.externalSourceEntries.size > 0 ? this.session.externalSourceEntries : require_singleton.getSharedExternalEntries();
|
|
669
675
|
}
|
|
670
676
|
};
|
|
671
677
|
//#endregion
|
|
678
|
+
//#region src/turbopack-config.ts
|
|
679
|
+
/** The single glob the Animus loader registers under. `.mjs` is included
|
|
680
|
+
* for webpack parity: an external package without `src/` is ingested via
|
|
681
|
+
* its resolved dist entry (often `dist/index.mjs`) and must still reach
|
|
682
|
+
* the loader — manifest lookup remains the file-level gate. */
|
|
683
|
+
const ANIMUS_TURBOPACK_RULE_GLOB = "*.{ts,tsx,js,jsx,mjs}";
|
|
684
|
+
/** Virtual system-props id emitted into transformed sources under Turbopack
|
|
685
|
+
* (absolute-path imports are rejected there); resolveAlias maps it to the
|
|
686
|
+
* on-disk artifact. */
|
|
687
|
+
const TURBOPACK_SYSTEM_PROPS_ID = "virtual:animus/system-props";
|
|
688
|
+
/**
|
|
689
|
+
* Resolve whether Turbopack wiring is active for this process. Default is
|
|
690
|
+
* `'auto'`: active exactly when the TURBOPACK environment variable is set
|
|
691
|
+
* (Next sets it for every Turbopack dev/build). The stable `turbopack`
|
|
692
|
+
* option wins over the deprecated `unstable_turbopack` alias.
|
|
693
|
+
*/
|
|
694
|
+
function resolveTurbopackMode(options, env = process.env) {
|
|
695
|
+
const mode = options.turbopack?.mode ?? options.unstable_turbopack?.mode ?? "auto";
|
|
696
|
+
if (mode === "on") return true;
|
|
697
|
+
if (mode === "auto") return env.TURBOPACK !== void 0;
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Build the `turbopack` config fragment: one glob-keyed loader rule
|
|
702
|
+
* (file-level allowlisting lives in the loader via manifest lookup — Next 15
|
|
703
|
+
* rules have no condition algebra) plus resolve aliases for the virtual
|
|
704
|
+
* system-props id, the emitter's stylesheet id, and each collected external
|
|
705
|
+
* package specifier redirected to its source entry.
|
|
706
|
+
*/
|
|
707
|
+
function buildTurbopackConfig(args) {
|
|
708
|
+
const { rootDir, loaderPath, options, externalSourceEntries } = args;
|
|
709
|
+
const loaderOptions = {
|
|
710
|
+
rootDir,
|
|
711
|
+
...options.strict !== void 0 ? { strict: options.strict } : {},
|
|
712
|
+
...options.cssImportTarget !== void 0 ? { cssImportTarget: options.cssImportTarget } : {}
|
|
713
|
+
};
|
|
714
|
+
const resolveAlias = {
|
|
715
|
+
[TURBOPACK_SYSTEM_PROPS_ID]: "./.animus/system-props.js",
|
|
716
|
+
".animus/styles.css": "./.animus/styles.css"
|
|
717
|
+
};
|
|
718
|
+
for (const [specifier, srcEntry] of externalSourceEntries) resolveAlias[specifier] = `./${(0, path.relative)(rootDir, srcEntry).replace(/\\/g, "/")}`;
|
|
719
|
+
return {
|
|
720
|
+
rules: { [ANIMUS_TURBOPACK_RULE_GLOB]: { loaders: [{
|
|
721
|
+
loader: loaderPath,
|
|
722
|
+
options: loaderOptions
|
|
723
|
+
}] } },
|
|
724
|
+
resolveAlias
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Locate the Turbopack loader module: built dist first, source fallback for
|
|
729
|
+
* in-repo development. CJS preferred — Turbopack's loader-runner follows
|
|
730
|
+
* the webpack loader contract (`module.exports = fn`), and the .cjs build
|
|
731
|
+
* is loadable by every runner version; ESM default-export acceptance is
|
|
732
|
+
* not guaranteed.
|
|
733
|
+
*/
|
|
734
|
+
function resolveTurbopackLoaderPath(pluginDir) {
|
|
735
|
+
for (const candidate of ["turbopack-loader.cjs", "turbopack-loader.mjs"]) {
|
|
736
|
+
const distPath = (0, path.resolve)(pluginDir, candidate);
|
|
737
|
+
if ((0, fs.existsSync)(distPath)) return distPath;
|
|
738
|
+
}
|
|
739
|
+
return (0, path.resolve)(pluginDir, "turbopack-loader.ts");
|
|
740
|
+
}
|
|
741
|
+
//#endregion
|
|
742
|
+
//#region src/turbopack-orchestrator.ts
|
|
743
|
+
/**
|
|
744
|
+
* Out-of-band pipeline orchestration for Turbopack (spec:
|
|
745
|
+
* next-turbopack-integration). Turbopack exposes no compiler hooks, so the
|
|
746
|
+
* pipeline runs during next.config resolution and, in dev, from a
|
|
747
|
+
* filesystem watcher — the bundler only ever sees the `.animus/` disk
|
|
748
|
+
* artifacts.
|
|
749
|
+
*/
|
|
750
|
+
/** Run the full pipeline with the Turbopack emitter identity: hydration
|
|
751
|
+
* artifacts persisted, and the virtual system-props id (Turbopack rejects
|
|
752
|
+
* the webpack path's absolute-path imports). */
|
|
753
|
+
async function runTurbopackPipeline(session) {
|
|
754
|
+
session.persistAnalysisInputs = true;
|
|
755
|
+
session.systemPropsModuleId = TURBOPACK_SYSTEM_PROPS_ID;
|
|
756
|
+
await session.runFullPipeline();
|
|
757
|
+
}
|
|
758
|
+
const activeWatcherRoots = /* @__PURE__ */ new Set();
|
|
759
|
+
const IGNORED_SEGMENTS = new Set([
|
|
760
|
+
".animus",
|
|
761
|
+
".next",
|
|
762
|
+
"node_modules"
|
|
763
|
+
]);
|
|
764
|
+
/**
|
|
765
|
+
* Start the dev watcher: fs.watch per eligible top-level directory (plus a
|
|
766
|
+
* non-recursive watch on the root itself), debounced into
|
|
767
|
+
* existence-partitioned modified/removed sets feeding
|
|
768
|
+
* `session.handleWatchUpdate` (serialized — updates never overlap).
|
|
769
|
+
* Generated and vendored trees (`.animus`, `.next`, node_modules) are never
|
|
770
|
+
* registered with the OS — recursive registration of those trees exhausts
|
|
771
|
+
* inotify/kqueue descriptors (EMFILE/ENOSPC) on large projects.
|
|
772
|
+
* Idempotent per project root; unref'd so it never holds the process open.
|
|
773
|
+
* Asynchronous FSWatcher errors degrade to no-watch with a warning instead
|
|
774
|
+
* of crashing the dev server. Returns a close handle, or null when this
|
|
775
|
+
* root is already watched or the platform lacks recursive fs.watch (Linux
|
|
776
|
+
* before Node 20 — degrades to no-watch with a warning).
|
|
777
|
+
*/
|
|
778
|
+
function startTurbopackWatcher(session, rootDir, debounceMs = 75, watchFn = fs.watch) {
|
|
779
|
+
if (activeWatcherRoots.has(rootDir)) return null;
|
|
780
|
+
activeWatcherRoots.add(rootDir);
|
|
781
|
+
const pendingPaths = /* @__PURE__ */ new Set();
|
|
782
|
+
const watchers = /* @__PURE__ */ new Map();
|
|
783
|
+
let timer = null;
|
|
784
|
+
let updateChain = Promise.resolve();
|
|
785
|
+
let closed = false;
|
|
786
|
+
const flush = () => {
|
|
787
|
+
timer = null;
|
|
788
|
+
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
789
|
+
const removedFiles = /* @__PURE__ */ new Set();
|
|
790
|
+
for (const path$1 of pendingPaths) ((0, fs.existsSync)(path$1) ? modifiedFiles : removedFiles).add(path$1);
|
|
791
|
+
pendingPaths.clear();
|
|
792
|
+
updateChain = updateChain.then(() => session.handleWatchUpdate({
|
|
793
|
+
modifiedFiles,
|
|
794
|
+
removedFiles
|
|
795
|
+
}).catch((err) => {
|
|
796
|
+
console.warn(`[animus-extract] Turbopack watch update failed: ${String(err)}`);
|
|
797
|
+
}));
|
|
798
|
+
};
|
|
799
|
+
const closeAll = () => {
|
|
800
|
+
if (closed) return;
|
|
801
|
+
closed = true;
|
|
802
|
+
if (timer) clearTimeout(timer);
|
|
803
|
+
timer = null;
|
|
804
|
+
for (const watcher of watchers.values()) watcher.close();
|
|
805
|
+
watchers.clear();
|
|
806
|
+
activeWatcherRoots.delete(rootDir);
|
|
807
|
+
};
|
|
808
|
+
const onWatcherError = (err) => {
|
|
809
|
+
closeAll();
|
|
810
|
+
console.warn(`[animus-extract] Turbopack dev watcher failed (${String(err)}); source edits require a dev-server restart`);
|
|
811
|
+
};
|
|
812
|
+
const onEvent = (baseDir, filename) => {
|
|
813
|
+
if (closed || !filename) return;
|
|
814
|
+
const rel = filename.toString();
|
|
815
|
+
if (rel.split(/[\\/]/).some((segment) => IGNORED_SEGMENTS.has(segment))) return;
|
|
816
|
+
const abs = (0, path.join)(baseDir, rel);
|
|
817
|
+
if (baseDir === rootDir && !watchers.has(abs)) try {
|
|
818
|
+
if ((0, fs.statSync)(abs).isDirectory()) addWatcher(abs, true);
|
|
819
|
+
} catch {}
|
|
820
|
+
pendingPaths.add(abs);
|
|
821
|
+
if (!timer) {
|
|
822
|
+
timer = setTimeout(flush, debounceMs);
|
|
823
|
+
timer.unref?.();
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
const addWatcher = (dir, recursive) => {
|
|
827
|
+
const watcher = watchFn(dir, { recursive }, (_event, filename) => onEvent(dir, filename));
|
|
828
|
+
watcher.on("error", onWatcherError);
|
|
829
|
+
watcher.unref?.();
|
|
830
|
+
watchers.set(dir, watcher);
|
|
831
|
+
};
|
|
832
|
+
try {
|
|
833
|
+
addWatcher(rootDir, false);
|
|
834
|
+
for (const entry of (0, fs.readdirSync)(rootDir, { withFileTypes: true })) {
|
|
835
|
+
if (!entry.isDirectory() || IGNORED_SEGMENTS.has(entry.name)) continue;
|
|
836
|
+
addWatcher((0, path.join)(rootDir, entry.name), true);
|
|
837
|
+
}
|
|
838
|
+
} catch (err) {
|
|
839
|
+
closeAll();
|
|
840
|
+
console.warn(`[animus-extract] Turbopack dev watcher unavailable (${String(err)}); source edits require a dev-server restart`);
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
return { close: closeAll };
|
|
844
|
+
}
|
|
845
|
+
//#endregion
|
|
672
846
|
//#region src/with-animus.ts
|
|
673
847
|
const __dirname$1 = (0, path.dirname)((0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
674
848
|
let warnedGitignore = false;
|
|
849
|
+
let warnedUnstableTurbopack = false;
|
|
675
850
|
/**
|
|
676
851
|
* Wrap a Next.js config to enable Animus static CSS extraction.
|
|
677
852
|
*
|
|
@@ -683,16 +858,30 @@ let warnedGitignore = false;
|
|
|
683
858
|
*/
|
|
684
859
|
function withAnimus(options) {
|
|
685
860
|
if (!options.system) throw new Error("[animus-extract] Missing required option `system`. Provide the path to your SystemInstance module: withAnimus({ system: \"./src/ds.ts\" })");
|
|
861
|
+
if (options.unstable_turbopack && !options.turbopack && !warnedUnstableTurbopack) {
|
|
862
|
+
warnedUnstableTurbopack = true;
|
|
863
|
+
console.warn("[animus-extract] `unstable_turbopack` is deprecated — rename it to `turbopack` (same shape)");
|
|
864
|
+
}
|
|
686
865
|
return (nextConfig) => {
|
|
866
|
+
if (resolveTurbopackMode(options)) return wireTurbopack(nextConfig, options);
|
|
687
867
|
const existingWebpack = nextConfig.webpack;
|
|
688
868
|
return {
|
|
689
869
|
...nextConfig,
|
|
690
870
|
webpack(config, context) {
|
|
871
|
+
if (typeof existingWebpack === "function") config = existingWebpack(config, context);
|
|
691
872
|
const rootDir = process.cwd();
|
|
692
873
|
const animusDir = (0, path.join)(rootDir, ".animus");
|
|
693
874
|
if (!(0, fs.existsSync)(animusDir)) (0, fs.mkdirSync)(animusDir, { recursive: true });
|
|
694
875
|
const stubCssPath = (0, path.join)(animusDir, "styles.css");
|
|
695
|
-
if (!(0, fs.existsSync)(stubCssPath))
|
|
876
|
+
if (!(0, fs.existsSync)(stubCssPath)) {
|
|
877
|
+
const { declaration } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
878
|
+
layers: options.layers,
|
|
879
|
+
variableCss: "",
|
|
880
|
+
globalCss: "",
|
|
881
|
+
split: true
|
|
882
|
+
});
|
|
883
|
+
(0, fs.writeFileSync)(stubCssPath, declaration);
|
|
884
|
+
}
|
|
696
885
|
if (!warnedGitignore) {
|
|
697
886
|
warnedGitignore = true;
|
|
698
887
|
try {
|
|
@@ -702,19 +891,13 @@ function withAnimus(options) {
|
|
|
702
891
|
}
|
|
703
892
|
} catch {}
|
|
704
893
|
}
|
|
705
|
-
const plugin = new AnimusWebpackPlugin(
|
|
706
|
-
|
|
707
|
-
exclude: options.exclude,
|
|
708
|
-
strict: options.strict,
|
|
709
|
-
verbose: options.verbose,
|
|
710
|
-
prefix: options.prefix,
|
|
711
|
-
engine: options.engine
|
|
712
|
-
});
|
|
894
|
+
const plugin = new AnimusWebpackPlugin(options);
|
|
895
|
+
const isExternalPackageFile = (filePath) => plugin.getExternalPackageDirs().some((dir) => filePath.startsWith(dir + path.sep) || filePath === dir);
|
|
713
896
|
config.plugins = config.plugins || [];
|
|
714
897
|
config.plugins.push(plugin);
|
|
715
898
|
config.resolve = config.resolve || {};
|
|
716
899
|
config.resolve.alias = config.resolve.alias || {};
|
|
717
|
-
config.resolve.alias[
|
|
900
|
+
config.resolve.alias[ANIMUS_CSS_MODULE_ID] = (0, path.join)(rootDir, ".animus", "styles.css");
|
|
718
901
|
const systemPropsPath = (0, path.join)(rootDir, ".animus", "system-props.js");
|
|
719
902
|
config.plugins.push({ apply(compiler) {
|
|
720
903
|
compiler.hooks.normalModuleFactory.tap("AnimusVirtualResolve", (nmf) => {
|
|
@@ -733,24 +916,62 @@ function withAnimus(options) {
|
|
|
733
916
|
config.module.rules.push({
|
|
734
917
|
test: (filePath) => {
|
|
735
918
|
if (/\.[jt]sx?$/.test(filePath)) return true;
|
|
736
|
-
|
|
737
|
-
return false;
|
|
919
|
+
return /\.mjs$/.test(filePath) && isExternalPackageFile(filePath);
|
|
738
920
|
},
|
|
739
921
|
exclude: (filePath) => {
|
|
740
922
|
if (!filePath.includes("node_modules")) return false;
|
|
741
|
-
return !
|
|
923
|
+
return !isExternalPackageFile(filePath);
|
|
742
924
|
},
|
|
743
925
|
enforce: "pre",
|
|
744
926
|
use: [{
|
|
745
927
|
loader: actualLoaderPath,
|
|
746
|
-
options: {
|
|
928
|
+
options: {
|
|
929
|
+
strict: options.strict,
|
|
930
|
+
cssImportTarget: options.cssImportTarget
|
|
931
|
+
}
|
|
747
932
|
}]
|
|
748
933
|
});
|
|
749
|
-
if (typeof existingWebpack === "function") return existingWebpack(config, context);
|
|
750
934
|
return config;
|
|
751
935
|
}
|
|
752
936
|
};
|
|
753
937
|
};
|
|
754
938
|
}
|
|
939
|
+
/**
|
|
940
|
+
* Turbopack wiring: run the full extraction now (artifacts on disk before
|
|
941
|
+
* bundling), start the dev watcher, and merge the generated rules/aliases
|
|
942
|
+
* into `nextConfig.turbopack`. Consumer-managed rules for the same glob are
|
|
943
|
+
* a hard error — silently stacking loaders would be undebuggable.
|
|
944
|
+
*/
|
|
945
|
+
async function wireTurbopack(nextConfig, options) {
|
|
946
|
+
const rootDir = process.cwd();
|
|
947
|
+
const session = new ExtractionSession(options);
|
|
948
|
+
session.rootDir = rootDir;
|
|
949
|
+
const builtAliases = (0, _animus_ui_extract_pipeline.buildPathAliasesJson)((0, _animus_ui_extract_pipeline.readTsconfigAliasPairs)(rootDir), rootDir);
|
|
950
|
+
if (builtAliases) session.pathAliasesJson = builtAliases.json;
|
|
951
|
+
await runTurbopackPipeline(session);
|
|
952
|
+
if (process.env.NODE_ENV === "development") startTurbopackWatcher(session, rootDir);
|
|
953
|
+
const fragment = buildTurbopackConfig({
|
|
954
|
+
rootDir,
|
|
955
|
+
loaderPath: resolveTurbopackLoaderPath(__dirname$1),
|
|
956
|
+
options,
|
|
957
|
+
externalSourceEntries: session.externalSourceEntries
|
|
958
|
+
});
|
|
959
|
+
const existing = nextConfig.turbopack ?? {};
|
|
960
|
+
if (existing.rules && "*.{ts,tsx,js,jsx,mjs}" in existing.rules) throw new Error(`[animus-extract] turbopack.rules['${ANIMUS_TURBOPACK_RULE_GLOB}'] is already configured — remove the consumer rule or disable unstable_turbopack`);
|
|
961
|
+
return {
|
|
962
|
+
...nextConfig,
|
|
963
|
+
turbopack: {
|
|
964
|
+
...existing,
|
|
965
|
+
rules: {
|
|
966
|
+
...existing.rules,
|
|
967
|
+
...fragment.rules
|
|
968
|
+
},
|
|
969
|
+
resolveAlias: {
|
|
970
|
+
...existing.resolveAlias,
|
|
971
|
+
...fragment.resolveAlias
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
}
|
|
755
976
|
//#endregion
|
|
756
977
|
exports.withAnimus = withAnimus;
|