@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 +101 -0
- package/dist/core-B2NB6x1D.cjs +425 -0
- package/dist/core-C3ZORO6V.mjs +420 -0
- package/dist/core.d.ts +136 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/esbuild.cjs +17 -0
- package/dist/esbuild.d.ts +11 -0
- package/dist/esbuild.d.ts.map +1 -0
- package/dist/esbuild.mjs +13 -0
- package/dist/index.cjs +24 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +19 -0
- package/dist/options.d.ts +44 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/rollup.cjs +18 -0
- package/dist/rollup.d.ts +12 -0
- package/dist/rollup.d.ts.map +1 -0
- package/dist/rollup.mjs +14 -0
- package/dist/rspack.cjs +17 -0
- package/dist/rspack.d.ts +11 -0
- package/dist/rspack.d.ts.map +1 -0
- package/dist/rspack.mjs +13 -0
- package/dist/webpack.cjs +18 -0
- package/dist/webpack.d.ts +12 -0
- package/dist/webpack.d.ts.map +1 -0
- package/dist/webpack.mjs +14 -0
- package/package.json +66 -0
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
|
+
});
|