@animus-ui/unplugin 0.1.11

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 ADDED
@@ -0,0 +1,101 @@
1
+ # @animus-ui/unplugin
2
+
3
+ Animus transform host for non-plugin bundlers — rollup, esbuild, rspack,
4
+ and webpack entry points over the one extraction session. Transforms
5
+ `@animus-ui/system` builder chains into static CSS at build time, delivers
6
+ the stylesheet as a real emitted asset (`animus.css`), and supplies the
7
+ `__ANIMUS_DEV__` define.
8
+
9
+ Vite users: use [`@animus-ui/vite-plugin`](../vite-plugin). Next.js users:
10
+ use [`@animus-ui/next-plugin`](../next-plugin). The root export's reachable
11
+ `.vite` adapter is **unsupported** — the supported surface is exactly the
12
+ four subpath entries below.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install --save-dev @animus-ui/unplugin
18
+ npm install @animus-ui/system
19
+ ```
20
+
21
+ Requires Node `>=22.12.0`. Native engine binaries ship for darwin-arm64,
22
+ linux-x64-gnu, and linux-arm64-gnu.
23
+
24
+ ## Setup (rollup)
25
+
26
+ <!-- source lane: e2e/rollup-app/rollup.config.mjs (lane pinning removed); verified by running the lane build and the genericized shape against the lane sources — both exit 0 and emit animus.css -->
27
+
28
+ ```js
29
+ // rollup.config.mjs — ESM configs use the NAMED export.
30
+ import { animusRollup as animus } from '@animus-ui/unplugin/rollup';
31
+ import esbuild from 'rollup-plugin-esbuild';
32
+
33
+ export default {
34
+ input: 'src/entry.tsx',
35
+ plugins: [
36
+ // The host FIRST: its transform must see raw TSX (rollup has no
37
+ // enforce ordering — config order is the law).
38
+ animus({ system: './src/ds.ts', strict: true }),
39
+ esbuild({ jsx: 'automatic', target: 'es2022' }),
40
+ ],
41
+ output: { dir: 'dist', format: 'esm' },
42
+ };
43
+ ```
44
+
45
+ The other bundlers follow the same shape from their own subpaths:
46
+ `animusEsbuild` from `@animus-ui/unplugin/esbuild`, `animusRspack` from
47
+ `@animus-ui/unplugin/rspack`, `animusWebpack` from
48
+ `@animus-ui/unplugin/webpack`. In CJS config files use the default export:
49
+
50
+ <!-- source lane: package export shape proven by e2e/rollup-app; verified by running `node -e "require('@animus-ui/unplugin/rollup').default"` from the lane (a function) -->
51
+
52
+ ```js
53
+ // CJS config files:
54
+ const animus = require('@animus-ui/unplugin/rollup').default;
55
+ ```
56
+
57
+ A Node-ESM **default** import of the CJS build binds the exports object,
58
+ not the plugin function — in ESM configs, always use the named export.
59
+
60
+ ## Plugin ordering
61
+
62
+ The extraction engine parses **raw TSX** — this host's transform must run
63
+ before any TS/JSX transpilation. `enforce: 'pre'` orders webpack and rspack
64
+ loader chains automatically; **rollup consumers must list this plugin
65
+ before their transpiler** in the config. Mis-ordered builds emit a
66
+ source-drift warning (`transform-time source … differs from analyze-time
67
+ source`) followed by a bundler parse error on TSX syntax — move the Animus
68
+ plugin first to fix it.
69
+
70
+ ## What it does
71
+
72
+ - Runs the one `ExtractionSession` at `buildStart` inside your bundler's
73
+ process; per-file transforms come from retained engine state. Analysis
74
+ failure is build failure — never a silent passthrough.
75
+ - Resolves the stylesheet id (`.animus/styles.css`), the system-props id,
76
+ and one redirect per admitted design-system kit specifier to the exact
77
+ entry extraction analyzed. tsconfig `paths` are the alias source.
78
+ - Emits the extracted stylesheet as a real `animus.css` asset (for esbuild:
79
+ written into `outdir`, or beside `outfile`).
80
+ - Supplies the `__ANIMUS_DEV__` dev-signal define through each bundler's
81
+ own mechanism.
82
+
83
+ ## Options
84
+
85
+ Options are the shared driver core (`system`, `exclude`, `strict`, `mode`,
86
+ `targets`, `minify`, …); unknown keys fail loud. `mode`
87
+ (`'development' | 'production'`) selects **emission only** — the minify
88
+ default, the dev-signal define, engine dev diagnostics — never process
89
+ lifecycle. When absent, the bundler's own command oracle applies (webpack/
90
+ rspack `compiler.options.mode`, rollup watch mode), else `production`
91
+ (esbuild exposes no signal). `exclude` patterns are glob-aware and MERGE
92
+ with the defaults (`node_modules`, `dist`, `.test.`, `.spec.`, `.next`,
93
+ `.animus`).
94
+
95
+ The full consumer contract — module-id families, the define contract,
96
+ artifact set, platform matrix — lives in the
97
+ [standalone extraction contract](https://github.com/codecaaron/animus/blob/main/docs/standalone-extraction.md).
98
+
99
+ ## License
100
+
101
+ MIT
@@ -0,0 +1,425 @@
1
+ let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
2
+ let _animus_ui_extract_session = require("@animus-ui/extract/session");
3
+ let node_fs = require("node:fs");
4
+ let node_path = require("node:path");
5
+ //#region src/options.ts
6
+ /**
7
+ * Host option intake — the transform host is the fourth driver over the
8
+ * shared option core (openspec: standalone-extraction-cli, D2/D9): one
9
+ * schema, `assertKnownOptionKeys` at the boundary, and an explicit
10
+ * emission `mode` that is plumbed — never environment-sniffed (D10).
11
+ *
12
+ * Unlike the Vite/Next plugin drivers, this host HONORS the core `root`
13
+ * key: rollup and esbuild expose no root authority a plugin could derive,
14
+ * so the host's root is the explicit option, defaulting to the process
15
+ * working directory (the CLI's shape, not the plugin drivers').
16
+ */
17
+ /**
18
+ * Validate raw options and resolve the root authority. Throws
19
+ * `AnimusConfigError` (the shared config-error class) on unknown keys,
20
+ * invalid `mode` values, a retired engine selection, or a missing
21
+ * `system`.
22
+ */
23
+ function resolveHostOptions(raw, cwd = process.cwd()) {
24
+ const options = raw ?? {};
25
+ (0, _animus_ui_extract_pipeline.assertNoRetiredEngineSelection)(options.engine);
26
+ (0, _animus_ui_extract_pipeline.assertKnownOptionKeys)(options);
27
+ if (typeof options.system !== "string" || options.system.length === 0) throw new _animus_ui_extract_pipeline.AnimusConfigError("Missing required option `system` — pass `system: \"./src/ds.ts\"` to the Animus plugin.");
28
+ return {
29
+ root: options.root ? (0, node_path.resolve)(cwd, options.root) : cwd,
30
+ options
31
+ };
32
+ }
33
+ /**
34
+ * Resolve the effective emission mode: the explicit option wins over every
35
+ * bundler signal; otherwise the adapter-supplied command oracle applies;
36
+ * otherwise production (the documented host default — never NODE_ENV).
37
+ */
38
+ function resolveHostMode(explicit, oracle) {
39
+ return (0, _animus_ui_extract_pipeline.resolveMode)(explicit, () => oracle ?? "production").mode;
40
+ }
41
+ //#endregion
42
+ //#region src/core.ts
43
+ /**
44
+ * The Animus transform host core (openspec: standalone-extraction-cli,
45
+ * D4 under D10): an unplugin factory that drives the ONE
46
+ * `ExtractionSession` at buildStart inside the consumer's bundler process
47
+ * and serves per-file transforms from retained engine state. No artifacts,
48
+ * no lock, no staleness protocol — analysis failure IS build failure,
49
+ * never a passthrough (the session's strict/policy seams carry this).
50
+ *
51
+ * Ported from the winning DEF-1 prototype arm
52
+ * (e2e/rollup-app/prototype/animus-t0-plugin.mjs), productized per the
53
+ * inc 05 packet: the emitted CSS reaches the consumer as a real asset
54
+ * (NS3 — the prototype's CSS-as-string module was measurement
55
+ * scaffolding), `__ANIMUS_DEV__` goes through each adapter's define
56
+ * mechanism, kit-specifier redirects come from the session's discovery
57
+ * output (the Turbopack alias assembly generalized), and the session
58
+ * directory is cleaned on success AND failure (inc 04 rider F6).
59
+ *
60
+ * ORDERING CONTRACT: the host transform must run before any TS/JSX
61
+ * transpilation — the engine parses raw TSX. `enforce: 'pre'` covers
62
+ * webpack/rspack; rollup consumers must list this plugin before their
63
+ * transpiler (see the e2e/rollup-app lane config).
64
+ */
65
+ /** Resolved virtual id of the stylesheet import. Deliberately
66
+ * extension-free: esbuild guesses loaders from extensions, and this
67
+ * module is a JS stub in every bundler — the CSS itself is delivered as
68
+ * an emitted asset, never as a module (NS3). No `\0` prefix: webpack's
69
+ * virtual-module bridge requires plain ids. */
70
+ const STYLES_VIRTUAL_ID = "animus:styles";
71
+ /** Resolved virtual id of the emitted system-props runtime module. */
72
+ const PROPS_VIRTUAL_ID = "animus:system-props";
73
+ /** File name of the emitted stylesheet asset. */
74
+ const CSS_ASSET_NAME = "animus.css";
75
+ /** Files the engine transform may rewrite — derived from the ONE shared
76
+ * extension set (also the Turbopack rule glob's source). */
77
+ const ENGINE_TRANSFORM_RE = new RegExp(`\\.(?:${_animus_ui_extract_pipeline.ENGINE_TRANSFORM_EXTENSIONS.join("|")})$`);
78
+ /** Files the transform hook claims at all (define substitution included —
79
+ * `.cjs` carries no components but may read the dev-signal token). */
80
+ const TRANSFORM_INCLUDE_RE = new RegExp(`\\.(?:${[..._animus_ui_extract_pipeline.ENGINE_TRANSFORM_EXTENSIONS, "cjs"].join("|")})$`);
81
+ /** Bare dev-signal token, assignment-guarded like @rollup/plugin-replace's
82
+ * `preventAssignment` — `__ANIMUS_DEV__ = x` is left alone. */
83
+ const DEV_DEFINE_RE = /\b__ANIMUS_DEV__\b(?!\s*=[^=])/g;
84
+ function createHostState() {
85
+ return {
86
+ pipeline: null,
87
+ mode: null,
88
+ cssText: "",
89
+ systemPropsJs: "",
90
+ kitRedirects: /* @__PURE__ */ new Map(),
91
+ redirectTargets: /* @__PURE__ */ new Set(),
92
+ externalPackageDirs: [],
93
+ watchPaths: [],
94
+ transformFile: null,
95
+ sessionDir: null
96
+ };
97
+ }
98
+ /**
99
+ * The transform-claim predicate over module paths. The analysis universe
100
+ * deliberately excludes node_modules (external kits ride their own
101
+ * collection path), so the transform must too — the Next webpack rule's
102
+ * exclude, ported: without it every dependency module in the graph is
103
+ * routed through the engine, scaling build time with dependency-graph size
104
+ * instead of source-file count. node_modules paths are claimed only when
105
+ * they belong to an ADMITTED external package (or are a kit redirect
106
+ * target) — those carry builder chains the engine must rewrite. Such files
107
+ * only enter the module graph after discovery published (their specifiers
108
+ * resolve through the pipeline-gated kitRedirects), so consulting captured
109
+ * state here is race-free.
110
+ */
111
+ function shouldClaimTransform(filePath, state) {
112
+ if (!filePath.includes("node_modules")) return true;
113
+ return state.redirectTargets.has(filePath) || state.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, filePath));
114
+ }
115
+ /** Remove the one-shot session tree; idempotent, runs on success AND
116
+ * failure paths (inc 04 rider F6). */
117
+ function disposeSessionDir(state, removeDir = (dir) => (0, node_fs.rmSync)(dir, {
118
+ recursive: true,
119
+ force: true
120
+ })) {
121
+ if (state.sessionDir) {
122
+ removeDir(state.sessionDir);
123
+ state.sessionDir = null;
124
+ }
125
+ }
126
+ /**
127
+ * Run one pipeline attempt through the shared drive loop, recording it as
128
+ * `state.pipeline` for hook joiners. A failed attempt disposes the session
129
+ * directory before rethrowing — the consumer's build fails; nothing leaks.
130
+ */
131
+ async function drivePipeline(state, run, removeDir) {
132
+ const attempt = run();
133
+ state.pipeline = attempt;
134
+ try {
135
+ await attempt;
136
+ } catch (error) {
137
+ if (!state.sessionDir) state.sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)();
138
+ disposeSessionDir(state, removeDir);
139
+ throw error;
140
+ }
141
+ }
142
+ /**
143
+ * Map an import id onto the host's CONSTANT resolution family: the
144
+ * stylesheet id and the system-props id — the mappings that answer without
145
+ * the pipeline. Kit-specifier redirects are the caller's second step (they
146
+ * exist only after discovery published, behind the pipeline join).
147
+ */
148
+ function resolveAnimusId(id) {
149
+ if (id === _animus_ui_extract_session.ANIMUS_CSS_MODULE_ID || id.endsWith(`/${_animus_ui_extract_session.ANIMUS_CSS_MODULE_ID}`)) return STYLES_VIRTUAL_ID;
150
+ if (id === "animus:styles" || id === "animus:system-props") return id;
151
+ if (id === _animus_ui_extract_session.TURBOPACK_SYSTEM_PROPS_ID) return PROPS_VIRTUAL_ID;
152
+ return null;
153
+ }
154
+ /**
155
+ * Substitute the bare `__ANIMUS_DEV__` token with its boolean literal —
156
+ * the define mechanism for bundlers without a native one (rollup). The
157
+ * token-as-initializer-conditional shape in the system runtime's is-dev
158
+ * module folds under the bundler's own dead-branch elimination once the
159
+ * literal lands. Returns null when the code carries no token.
160
+ */
161
+ function substituteDevDefine(code, isDev) {
162
+ if (!code.includes("__ANIMUS_DEV__")) return null;
163
+ return code.replace(DEV_DEFINE_RE, isDev ? "true" : "false");
164
+ }
165
+ /**
166
+ * Per-file engine transform from retained state. The path handed to the
167
+ * engine is the rootDir-relative posix key the analysis used — external
168
+ * kit sources ride the same derivation (`../…` keys). Files outside the
169
+ * analysis universe come back unchanged (`hasComponents: false`); a
170
+ * transform before analysis fails loud inside the engine adapter.
171
+ */
172
+ function transformWithEngine(code, id, ctx) {
173
+ const filename = (0, node_path.relative)(ctx.rootDir, (0, node_path.resolve)(id)).split("\\").join("/");
174
+ const result = ctx.transformFile(code, filename, ctx.manifestJson);
175
+ return result.hasComponents ? result.code : null;
176
+ }
177
+ /** Strip a bundler query suffix (`?worker`, webpack resource queries). */
178
+ function moduleFilePath(id) {
179
+ const query = id.indexOf("?");
180
+ return query === -1 ? id : id.slice(0, query);
181
+ }
182
+ const PLUGIN_NAME = "animus-host";
183
+ /**
184
+ * The one-live-host-per-process constraint, enforced where the invariant
185
+ * lives: the SESSION singleton (its slots are what concurrent hosts would
186
+ * clobber). Re-exported under the host's historical name; the key sits in
187
+ * `SINGLETON_GLOBAL_KEYS`, so test harness resets clear a leaked claim.
188
+ */
189
+ const claimProcessHost = _animus_ui_extract_session.claimExclusiveSessionOwner;
190
+ /**
191
+ * The unplugin factory. One factory invocation = one host = one session
192
+ * per build; the singleton drive loop stays exactly-one (guardrail G1 —
193
+ * the host defines no session class and no drive loop of its own).
194
+ */
195
+ const unpluginFactory = (rawOptions, meta) => {
196
+ const { root, options } = resolveHostOptions(rawOptions);
197
+ const state = createHostState();
198
+ /** Release handle of this host's process claim, or null when not held. */
199
+ let releaseHostClaim = null;
200
+ /** Adapter-supplied command oracle (null = no bundler signal). */
201
+ let modeOracle = null;
202
+ let esbuildOptions = null;
203
+ /** Resolves once buildStart has begun — hooks that can fire before the
204
+ * bundler-parallel buildStart (webpack's make taps run concurrently)
205
+ * wait on this, then join the pipeline itself. */
206
+ let signalPipelineStarted;
207
+ const pipelineStarted = new Promise((res) => {
208
+ signalPipelineStarted = res;
209
+ });
210
+ const effectiveMode = () => resolveHostMode(options.mode, modeOracle);
211
+ /** The define is supplied natively where the bundler has a mechanism
212
+ * (esbuild define, webpack/rspack DefinePlugin); everywhere else the
213
+ * transform substitutes the token itself. */
214
+ const needsInlineDefine = meta.framework !== "esbuild" && meta.framework !== "webpack" && meta.framework !== "rspack";
215
+ async function startPipeline() {
216
+ signalPipelineStarted();
217
+ releaseHostClaim = claimProcessHost(`${PLUGIN_NAME}:${root}`);
218
+ try {
219
+ await runClaimedPipeline();
220
+ } catch (error) {
221
+ releaseClaim();
222
+ throw error;
223
+ }
224
+ }
225
+ function releaseClaim() {
226
+ releaseHostClaim?.();
227
+ releaseHostClaim = null;
228
+ }
229
+ async function runClaimedPipeline() {
230
+ await drivePipeline(state, async () => {
231
+ const mode = effectiveMode();
232
+ state.mode = mode;
233
+ const session = new _animus_ui_extract_session.ExtractionSession({
234
+ ...options,
235
+ mode
236
+ });
237
+ session.driverLabel = "animus-unplugin";
238
+ session.rootDir = root;
239
+ session.systemPropsModuleId = _animus_ui_extract_session.TURBOPACK_SYSTEM_PROPS_ID;
240
+ const builtAliases = (0, _animus_ui_extract_pipeline.buildPathAliasesJson)((0, _animus_ui_extract_pipeline.readTsconfigAliasPairs)(root), root);
241
+ if (builtAliases) session.pathAliasesJson = builtAliases.json;
242
+ await session.runFullPipeline();
243
+ state.sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)();
244
+ const analyzed = (0, _animus_ui_extract_session.getAnalyzedHashes)();
245
+ if (!analyzed || analyzed.size === 0) throw new Error(`[animus] discovery found zero source files under ${root} — check the plugin's \`root\` and \`exclude\` options`);
246
+ state.cssText = (0, _animus_ui_extract_session.getSharedCss)();
247
+ state.systemPropsJs = (0, _animus_ui_extract_session.getSharedSystemProps)();
248
+ state.kitRedirects = new Map(session.externalSourceEntries);
249
+ state.redirectTargets = new Set(state.kitRedirects.values());
250
+ state.externalPackageDirs = [...session.externalPackageDirs];
251
+ const watchPaths = /* @__PURE__ */ new Set();
252
+ for (const key of (0, _animus_ui_extract_session.getAnalyzedHashes)()?.keys() ?? []) watchPaths.add((0, node_path.resolve)(root, key));
253
+ for (const dep of session.systemDependencyPaths) watchPaths.add(dep);
254
+ for (const dep of session.assetDependencyPaths) watchPaths.add(dep);
255
+ watchPaths.add((0, node_path.join)(root, "tsconfig.json"));
256
+ state.watchPaths = [...watchPaths];
257
+ state.transformFile = (0, _animus_ui_extract_session.engineApi)().transformFile;
258
+ });
259
+ }
260
+ /** Join the analysis: every serving hook waits for buildStart to have
261
+ * begun, then for the pipeline to have published. A rejected pipeline
262
+ * rejects every joiner — analysis failure is build failure, never a
263
+ * passthrough. */
264
+ async function joinPipeline() {
265
+ await pipelineStarted;
266
+ await state.pipeline;
267
+ }
268
+ function emitCssAsset(context) {
269
+ if (!state.cssText) return;
270
+ const assets = (0, _animus_ui_extract_session.collectSessionAssets)(state.sessionDir);
271
+ if (meta.framework === "esbuild") {
272
+ const outDir = esbuildOptions?.outdir ?? (esbuildOptions?.outfile ? (0, node_path.dirname)(esbuildOptions.outfile) : null);
273
+ if (outDir === null) {
274
+ console.warn(`[animus] esbuild build has no outdir/outfile — the extracted stylesheet (${CSS_ASSET_NAME}) was not written`);
275
+ return;
276
+ }
277
+ if (esbuildOptions?.write === false) {
278
+ console.warn(`[animus] esbuild \`write: false\` build — the extracted stylesheet (${CSS_ASSET_NAME}) and its asset files were NOT produced (no in-memory output seam); use \`write: true\` or the standalone \`animus build\` CLI for in-memory pipelines`);
279
+ return;
280
+ }
281
+ const absOut = (0, node_path.resolve)(esbuildOptions?.absWorkingDir ?? process.cwd(), outDir);
282
+ (0, node_fs.mkdirSync)(absOut, { recursive: true });
283
+ (0, node_fs.writeFileSync)((0, node_path.join)(absOut, CSS_ASSET_NAME), state.cssText);
284
+ if (assets.length > 0) {
285
+ (0, node_fs.mkdirSync)((0, node_path.join)(absOut, _animus_ui_extract_session.SESSION_ASSETS_DIR), { recursive: true });
286
+ for (const { name, bytes } of assets) (0, node_fs.writeFileSync)((0, node_path.join)(absOut, _animus_ui_extract_session.SESSION_ASSETS_DIR, name), bytes);
287
+ }
288
+ return;
289
+ }
290
+ context.emitFile({
291
+ type: "asset",
292
+ fileName: CSS_ASSET_NAME,
293
+ source: state.cssText
294
+ });
295
+ for (const { name, bytes } of assets) context.emitFile({
296
+ type: "asset",
297
+ fileName: `${_animus_ui_extract_session.SESSION_ASSETS_DIR}/${name}`,
298
+ source: bytes
299
+ });
300
+ }
301
+ /** Register the analysis universe with the bundler's watcher — the
302
+ * session analyzes a filesystem WALK, so watch mode misses edits to
303
+ * analyzed-but-unimported files (and tsconfig/system deps) without
304
+ * this. esbuild has no per-plugin watch-file seam; its rebuilds rely on
305
+ * the module graph alone. */
306
+ function registerWatchTargets(context) {
307
+ if (meta.framework === "esbuild") return;
308
+ for (const path of state.watchPaths) try {
309
+ context.addWatchFile(path);
310
+ } catch {}
311
+ }
312
+ return {
313
+ name: PLUGIN_NAME,
314
+ enforce: "pre",
315
+ async buildStart() {
316
+ await startPipeline();
317
+ registerWatchTargets(this);
318
+ },
319
+ async resolveId(id) {
320
+ const virtual = resolveAnimusId(id);
321
+ if (virtual !== null) return virtual;
322
+ if (id.startsWith(".") || id.startsWith("/") || id.startsWith("\0") || id.startsWith("animus:")) return null;
323
+ await joinPipeline();
324
+ return state.kitRedirects.get(id) ?? null;
325
+ },
326
+ loadInclude(id) {
327
+ return id === "animus:styles" || id === "animus:system-props" || state.redirectTargets.has(moduleFilePath(id));
328
+ },
329
+ async load(id) {
330
+ if (id === "animus:styles") {
331
+ await joinPipeline();
332
+ return {
333
+ code: "export {};\n",
334
+ map: null
335
+ };
336
+ }
337
+ if (id === "animus:system-props") {
338
+ await joinPipeline();
339
+ return {
340
+ code: state.systemPropsJs,
341
+ map: null
342
+ };
343
+ }
344
+ const filePath = moduleFilePath(id);
345
+ if (state.redirectTargets.has(filePath)) return {
346
+ code: (0, node_fs.readFileSync)(filePath, "utf-8"),
347
+ map: null
348
+ };
349
+ return null;
350
+ },
351
+ transformInclude(id) {
352
+ const filePath = moduleFilePath(id);
353
+ return !id.startsWith("\0") && !id.startsWith("animus:") && TRANSFORM_INCLUDE_RE.test(filePath) && shouldClaimTransform(filePath, state);
354
+ },
355
+ async transform(code, id) {
356
+ await joinPipeline();
357
+ const filePath = moduleFilePath(id);
358
+ let output = code;
359
+ if (ENGINE_TRANSFORM_RE.test(filePath)) {
360
+ const transformFile = state.transformFile ?? (0, _animus_ui_extract_session.engineApi)().transformFile;
361
+ const transformed = transformWithEngine(output, filePath, {
362
+ rootDir: root,
363
+ manifestJson: (0, _animus_ui_extract_session.getManifestJson)() ?? "",
364
+ transformFile
365
+ });
366
+ if (transformed !== null) output = transformed;
367
+ }
368
+ if (needsInlineDefine) {
369
+ const substituted = substituteDevDefine(output, state.mode === "development");
370
+ if (substituted !== null) output = substituted;
371
+ }
372
+ return output === code ? null : {
373
+ code: output,
374
+ map: null
375
+ };
376
+ },
377
+ buildEnd() {
378
+ try {
379
+ emitCssAsset(this);
380
+ } finally {
381
+ disposeSessionDir(state);
382
+ releaseClaim();
383
+ }
384
+ },
385
+ rollup: { async buildStart() {
386
+ modeOracle = this.meta?.watchMode ? "development" : "production";
387
+ await startPipeline();
388
+ registerWatchTargets(this);
389
+ } },
390
+ webpack(compiler) {
391
+ wireWebpackLike(compiler);
392
+ },
393
+ rspack(compiler) {
394
+ wireWebpackLike(compiler);
395
+ },
396
+ esbuild: { config(buildOptions) {
397
+ esbuildOptions = buildOptions;
398
+ buildOptions.define = {
399
+ ...buildOptions.define,
400
+ __ANIMUS_DEV__: JSON.stringify(effectiveMode() === "development")
401
+ };
402
+ } }
403
+ };
404
+ function wireWebpackLike(compiler) {
405
+ modeOracle = compiler.options.mode === "development" ? "development" : "production";
406
+ const DefinePlugin = compiler.webpack?.DefinePlugin ?? compiler.rspack?.DefinePlugin;
407
+ if (!DefinePlugin) throw new Error("[animus] compiler exposes no DefinePlugin — cannot supply the __ANIMUS_DEV__ dev-signal define");
408
+ new DefinePlugin({ __ANIMUS_DEV__: JSON.stringify(effectiveMode() === "development") }).apply(compiler);
409
+ compiler.hooks.done.tap(PLUGIN_NAME, () => {
410
+ disposeSessionDir(state);
411
+ releaseClaim();
412
+ });
413
+ compiler.hooks.failed?.tap(PLUGIN_NAME, () => {
414
+ disposeSessionDir(state);
415
+ releaseClaim();
416
+ });
417
+ }
418
+ };
419
+ //#endregion
420
+ Object.defineProperty(exports, "unpluginFactory", {
421
+ enumerable: true,
422
+ get: function() {
423
+ return unpluginFactory;
424
+ }
425
+ });