@animus-ui/next-plugin 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,18 @@
1
- import { t as transformWithManifest } from "./loader-core-DwiidYBi.mjs";
1
+ import { t as transformWithManifest } from "./loader-core-x6cOzF9v.mjs";
2
2
  import { createRequire } from "node:module";
3
- import { buildAnalyzeProjectArgs, contentHash, createV2EngineApi } from "@animus-ui/extract/pipeline";
3
+ import { buildAnalyzeProjectArgs, contentHash, createV2EngineApi, parseFilesJson } from "@animus-ui/extract/pipeline";
4
4
  import { analysisCommitPath, analysisInputsPath, analysisStatusPath, manifestPath, readJsonEnvelope, replacementEpochPath, sessionArtifactDir } from "@animus-ui/extract/session";
5
5
  import * as nodeFs from "fs";
6
6
  import { relative } from "path";
7
7
  //#region \0rolldown/runtime.js
8
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
8
+ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
9
9
  //#endregion
10
10
  //#region src/turbopack-loader.ts
11
11
  let engine = null;
12
12
  let sentSources = null;
13
13
  let driftWarned = false;
14
- /** Hydration identity: `${commitPath}:${contentHash(commitRaw)}` — the
15
- * commit CONTENT keys hydration; mtime/size never do (design D1). */
16
14
  let hydratedKey = null;
17
15
  let hydratedManifestJson = null;
18
- /** Committed per-file analyzed content hashes (from the inputs). */
19
16
  let hydratedFileHashes = null;
20
17
  const engineModuleId = "@animus-ui/extract";
21
18
  const engineApi$1 = createV2EngineApi({
@@ -38,15 +35,18 @@ const engineApi$1 = createV2EngineApi({
38
35
  }
39
36
  }
40
37
  });
38
+ let engineApiImpl = engineApi$1;
39
+ /** @internal test seam. */
40
+ function __setTurbopackLoaderEngineApiForTests(api) {
41
+ engineApiImpl = api ?? engineApi$1;
42
+ }
41
43
  let fsImpl = nodeFs;
42
- /** @internal test seam — pass null to restore the real fs. */
44
+ /** @internal test seam. */
43
45
  function __setTurbopackLoaderFsForTests(fs) {
44
46
  fsImpl = fs ?? nodeFs;
45
47
  }
46
- /** Epoch artifact paths SEEN on disk (per-worker) — the artifact is never
47
- * deleted once written, so a positive probe is stable; negatives re-probe. */
48
48
  const epochSeenPaths = /* @__PURE__ */ new Set();
49
- /** @internal test seam — drop worker-local hydration state. */
49
+ /** @internal test seam. */
50
50
  function __resetTurbopackLoaderStateForTests() {
51
51
  hydratedKey = null;
52
52
  hydratedManifestJson = null;
@@ -60,16 +60,10 @@ function readFileOrNull(path) {
60
60
  return null;
61
61
  }
62
62
  }
63
- const SEQLOCK_RETRIES = 5;
64
- /**
65
- * Seqlock-style artifact read: commit C0 → payloads → hash verification
66
- * against C0 → commit re-read; accept only if unchanged, else retry
67
- * (bounded) — a torn or mismatched set is never consumed (design D1).
68
- * Hydration replays the committed analysis inputs once per commit content.
69
- */
63
+ const ARTIFACT_READ_RETRIES = 5;
70
64
  function hydrateSession(sessionDir, sessionId) {
71
65
  const commitPath = analysisCommitPath(sessionDir);
72
- for (let attempt = 0; attempt < SEQLOCK_RETRIES; attempt++) {
66
+ for (let attempt = 0; attempt < ARTIFACT_READ_RETRIES; attempt++) {
73
67
  const c0raw = readFileOrNull(commitPath);
74
68
  if (c0raw === null) return { kind: "absent" };
75
69
  let c0;
@@ -106,11 +100,11 @@ function hydrateSession(sessionDir, sessionId) {
106
100
  };
107
101
  const inputsParsed = JSON.parse(inputsRaw);
108
102
  inputs = inputsParsed;
109
- fileHashes = inputsParsed.analyzedHashes ? new Map(Object.entries(inputsParsed.analyzedHashes)) : new Map(JSON.parse(inputs.filesJson).map((entry) => [entry.path, entry.hash ?? contentHash(entry.source)]));
103
+ fileHashes = inputsParsed.analyzedHashes ? new Map(Object.entries(inputsParsed.analyzedHashes)) : new Map(parseFilesJson(inputs.filesJson, "animus-next-turbopack").map((entry) => [entry.path, entry.hash ?? contentHash(entry.source)]));
110
104
  } catch {
111
105
  return { kind: "torn" };
112
106
  }
113
- const { analyzeProject } = engineApi$1();
107
+ const { analyzeProject } = engineApiImpl();
114
108
  hydratedManifestJson = analyzeProject(...buildAnalyzeProjectArgs(inputs));
115
109
  hydratedFileHashes = fileHashes;
116
110
  hydratedKey = key;
@@ -134,33 +128,28 @@ function readStatus(sessionDir) {
134
128
  return null;
135
129
  }
136
130
  }
131
+ const hasReadableDeadline = (status) => Object.prototype.toString.call(status.deadlineAt) === "[object Number]";
137
132
  const CATCHUP_POLL_INITIAL_MS = 10;
138
133
  const CATCHUP_POLL_MAX_MS = 25;
139
134
  const CATCHUP_WAIT_MARGIN_MS = 50;
140
- const CATCHUP_WATCHDOG_MS = 2e3;
141
- /** The loader cannot read the orchestrator's configured debounce, so its
142
- * own ceiling assumes the default watcher debounce. */
143
- const DEFAULT_DEBOUNCE_CEILING_MS = 75;
135
+ /** Bounds the one wait carrying no deadline — a status whose `deadlineAt` is
136
+ * not numeric. The session-published deadline bounds every other wait.
137
+ * Equal to the session's status watchdog by coincidence only: never unify
138
+ * them, and never re-derive a deadline from the watcher debounce. */
139
+ const CATCHUP_NO_DEADLINE_CAP_MS = 2e3;
144
140
  function sleep(ms) {
145
141
  return new Promise((resolve) => setTimeout(resolve, ms));
146
142
  }
147
- const ACTIVE_STATES = new Set([
143
+ const ACTIVE_STATES = /* @__PURE__ */ new Set([
148
144
  "starting",
149
145
  "debouncing",
150
146
  "analyzing",
151
147
  "committing"
152
148
  ]);
153
- /**
154
- * Turbopack loader for Animus source transformation. Derives everything
155
- * from the incoming source, its JSON-serializable options (carrying the
156
- * session identity), and the session's COMMITTED disk artifacts written by
157
- * the out-of-band orchestrator. Async by contract — the evidence-based
158
- * catch-up (design D3) waits, bounded, for an imminent commit.
159
- */
160
149
  function animusTurbopackLoader(source) {
161
- if (typeof this.async !== "function") throw new Error("[animus-extract] the Animus Turbopack loader requires an async-capable loader runner (this.async is unavailable)");
150
+ if (this.async === void 0) throw new Error("[animus-extract] the Animus Turbopack loader requires an async-capable loader runner (this.async is unavailable)");
162
151
  const callback = this.async();
163
- runLoader(this, source).then((code) => callback(null, code), (err) => callback(err instanceof Error ? err : new Error(String(err))));
152
+ runLoader(this, source).then((code) => callback(null, code), (thrown) => callback(thrown instanceof Error ? thrown : new Error(String(thrown))));
164
153
  }
165
154
  async function runLoader(ctx, source) {
166
155
  const opts = ctx.getOptions?.() ?? {};
@@ -171,17 +160,15 @@ async function runLoader(ctx, source) {
171
160
  const commitPath = analysisCommitPath(sessionDir);
172
161
  const statusPath = analysisStatusPath(sessionDir);
173
162
  const epochPath = replacementEpochPath(sessionDir);
174
- if (typeof ctx.addDependency === "function" && (epochSeenPaths.has(epochPath) || fsImpl.existsSync(epochPath))) {
163
+ if (ctx.addDependency !== void 0 && (epochSeenPaths.has(epochPath) || fsImpl.existsSync(epochPath))) {
175
164
  epochSeenPaths.add(epochPath);
176
165
  ctx.addDependency(epochPath);
177
166
  }
178
167
  const filename = relative(rootDir, ctx.resourcePath);
179
- /** EVERY failing path registers the commit + status artifacts as
180
- * dependencies BEFORE throwing (the loader-runner sends dependency IPC
181
- * ahead of the error check upstream), so a landing commit or status
182
- * transition re-triggers the invocation — the documented
183
- * read→registration race makes this a recovery aid, never the primary
184
- * wakeup (design D3). Successful paths add NO commit/status dependency. */
168
+ /** Registers commit and status as dependencies before throwing: the runner
169
+ * sends dependency IPC ahead of the error, so a landing commit re-fires.
170
+ * Failure paths only — successful paths register neither, so this must not
171
+ * be hoisted out of the failure helper. */
185
172
  const coverageFailure = (message) => {
186
173
  ctx.addDependency?.(commitPath);
187
174
  ctx.addDependency?.(statusPath);
@@ -191,12 +178,12 @@ async function runLoader(ctx, source) {
191
178
  source,
192
179
  filename,
193
180
  manifestJson,
194
- engineApi: engineApi$1,
181
+ engineApi: engineApiImpl,
195
182
  opts
196
183
  });
197
184
  const outcome = hydrateSession(sessionDir, sessionId);
198
185
  if (outcome.kind === "foreign") throw coverageFailure(`ANIMUS_FOREIGN_SESSION: artifacts under ${sessionDir} belong to session ${outcome.artifactSessionId}, not ${sessionId} — refusing to transform from a foreign generation`);
199
- if (outcome.kind === "torn") throw coverageFailure(`ANIMUS_ARTIFACT_READ_TORN: could not obtain a consistent committed artifact set for ${filename} after ${SEQLOCK_RETRIES} attempts`);
186
+ if (outcome.kind === "torn") throw coverageFailure(`ANIMUS_ARTIFACT_READ_TORN: could not obtain a consistent committed artifact set for ${filename} after ${ARTIFACT_READ_RETRIES} attempts`);
200
187
  let sourceHash = null;
201
188
  if (outcome.kind === "ok") {
202
189
  const analyzedHash = outcome.hydration.fileHashes.get(filename);
@@ -214,17 +201,9 @@ async function runLoader(ctx, source) {
214
201
  transform
215
202
  });
216
203
  }
217
- /**
218
- * The catch-up decision table (design D3 — implemented verbatim). Waits —
219
- * polled, bounded — ONLY in the two evidence states: commit absent while an
220
- * attempt is active, or the active attempt's pending set contains this file
221
- * at its current hash. Everything else fails immediately with its stable
222
- * diagnostic. The moment a commit lands that covers the input, the seqlock
223
- * hydration consumes it and the transform proceeds with no diagnostic.
224
- */
225
204
  async function awaitCoverage(args) {
226
205
  const { source, sourceHash, filename, sessionId, sessionDir, coverageFailure, transform } = args;
227
- const ceiling = Date.now() + DEFAULT_DEBOUNCE_CEILING_MS + CATCHUP_WATCHDOG_MS + CATCHUP_WAIT_MARGIN_MS;
206
+ const noDeadlineCap = Date.now() + CATCHUP_NO_DEADLINE_CAP_MS + CATCHUP_WAIT_MARGIN_MS;
228
207
  let poll = CATCHUP_POLL_INITIAL_MS;
229
208
  for (;;) {
230
209
  const outcome = hydrateSession(sessionDir, sessionId);
@@ -246,11 +225,12 @@ async function awaitCoverage(args) {
246
225
  if (!(status.pending ?? []).some(([key, hash]) => key === filename && hash === sourceHash)) throw coverageFailure(`ANIMUS_ANALYSIS_NOT_SCHEDULED: the active analysis attempt (${status.attemptId}) has not observed ${filename} at its current content`);
247
226
  }
248
227
  const now = Date.now();
249
- if (typeof status.deadlineAt === "number" && now > status.deadlineAt) throw coverageFailure(`ANIMUS_ANALYSIS_STALLED: analysis attempt ${status.attemptId} exceeded its deadline while ${filename} waited for coverage`);
250
- if (now > ceiling) throw coverageFailure(`ANIMUS_ANALYSIS_CATCHING_UP: ${filename} changed after the committed analysis; timed out waiting for the commit to advance — retrying on the next invalidation`);
228
+ if (hasReadableDeadline(status)) {
229
+ if (now > status.deadlineAt) throw coverageFailure(`ANIMUS_ANALYSIS_STALLED: analysis attempt ${status.attemptId} exceeded its deadline while ${filename} waited for coverage`);
230
+ } else if (now > noDeadlineCap) throw coverageFailure(`ANIMUS_ANALYSIS_CATCHING_UP: ${filename} changed after the committed analysis; timed out waiting for the commit to advance — retrying on the next invalidation`);
251
231
  await sleep(poll);
252
232
  poll = CATCHUP_POLL_MAX_MS;
253
233
  }
254
234
  }
255
235
  //#endregion
256
- export { __resetTurbopackLoaderStateForTests, __setTurbopackLoaderFsForTests, animusTurbopackLoader as default };
236
+ export { __resetTurbopackLoaderStateForTests, __setTurbopackLoaderEngineApiForTests, __setTurbopackLoaderFsForTests, animusTurbopackLoader as default };
package/dist/types.d.ts CHANGED
@@ -2,116 +2,50 @@ import type { StaticCssConfig } from '@animus-ui/extract/pipeline';
2
2
  export interface AnimusNextOptions {
3
3
  /** Path to a module exporting a SystemInstance from `@animus-ui/system`. */
4
4
  system: string;
5
- /**
6
- * Exclusion patterns (substrings, or globs when `*`/`?` present). When
7
- * set, REPLACES the replaceable defaults (`dist`, `.test.`, `.spec.`);
8
- * `node_modules`, `.next`, and `.animus` are always excluded.
9
- */
5
+ /** Substrings, or globs when `*`/`?` present. Replaces `dist`/`.test.`/
6
+ * `.spec.`; `node_modules`, `.next` and `.animus` are always excluded. */
10
7
  exclude?: string[];
11
- /**
12
- * File extensions to scan for component definitions and JSX usages.
13
- * Replaces the default list entirely (not additive). Include `.mdx` to
14
- * extract components rendered from MDX files — `@mdx-js/mdx` must be
15
- * installed as a peer for MDX files to be preprocessed; otherwise the
16
- * plugin warns once at buildStart and skips them.
17
- *
18
- * @default ['.ts', '.tsx', '.js', '.jsx', '.mdx']
19
- */
8
+ /** Extensions scanned for definitions and usages; replaces the default
9
+ * `['.ts','.tsx','.js','.jsx','.mdx']`. `.mdx` needs `@mdx-js/mdx`. */
20
10
  extensions?: string[];
21
11
  /** When true, extraction failures throw instead of warning. */
22
12
  strict?: boolean;
23
- /**
24
- * Project-root-relative path of the single file that receives the
25
- * `import '.animus/styles.css'` injection (the stylesheet import is
26
- * stripped from every other file to prevent per-chunk CSS duplication).
27
- * Set this when your root entry doesn't match the default detection of
28
- * `app/layout.*` / `pages/_app.*`, optionally under `src/` — e.g. a
29
- * nested App Router root layout like `'src/app/[locale]/layout.tsx'`.
30
- */
13
+ /** Project-root-relative path of the one file that receives the stylesheet
14
+ * import; replaces the `app/layout.*` / `pages/_app.*` detection. */
31
15
  cssImportTarget?: string;
32
- /** Enable verbose logging. */
33
16
  verbose?: boolean;
34
17
  /** Namespace prefix for CSS variables and class names. */
35
18
  prefix?: string;
36
- /**
37
- * Forced-emission declarations for usage the scanner cannot observe
38
- * (CMS-driven variants, spread-hidden props, dynamically selected
39
- * components). Declared variants/states/system-prop values and custom
40
- * dynamic slots are emitted as if used; entries are labeled as forced
41
- * in the extraction report. Empty/absent is a no-op.
42
- */
19
+ /** Forced-emission declarations for usage the scanner cannot observe
20
+ * (CMS-driven variants, spread-hidden props). Absent is a no-op. */
43
21
  staticCss?: StaticCssConfig;
44
- /**
45
- * Browser targets for CSS autoprefixing and syntax lowering.
46
- * Accepts a browserslist query string or array of queries.
47
- * Falls back to project's browserslist config, then to `defaults`.
48
- */
22
+ /** Browserslist query string or array. Falls back to the project's
23
+ * browserslist config, then to `defaults`. */
49
24
  targets?: string | string[];
50
- /**
51
- * Control CSS minification of the emitted stylesheet.
52
- * - `true`: always minify (dev + prod)
53
- * - `false`: never minify (autoprefixing still applies)
54
- * - `undefined` (default): minify in production (per `mode`, else
55
- * `NODE_ENV === 'production'`)
56
- */
25
+ /** `true`/`false` force minification on or off; absent minifies in
26
+ * production. Autoprefixing applies either way. */
57
27
  minify?: boolean;
58
- /**
59
- * Explicit dev/prod emission mode. Wins over the environment signal.
60
- * When absent, the documented default applies: `NODE_ENV === 'production'`
61
- * decides the minify default, and the compiler's dev flag decides the
62
- * dev-diagnostics define.
63
- */
28
+ /** Explicit emission mode, winning over the environment signal that
29
+ * otherwise decides minification and the dev-diagnostics define. */
64
30
  mode?: 'development' | 'production';
65
- /**
66
- * Extraction engine selection. `'v2'` is the only engine and the default,
67
- * propagated to every compiler instance (including non-owning ones). The v1
68
- * engine was retired (openspec: retire-extract-v1); configuring
69
- * `engine: 'v1'` (or setting `ANIMUS_ENGINE=v1`) throws — the selection is
70
- * never silently upgraded.
71
- *
72
- * @default 'v2'
73
- */
31
+ /** `'v2'` is the only engine and the default. `engine: 'v1'` or
32
+ * `ANIMUS_ENGINE=v1` throws — the selection is never silently upgraded. */
74
33
  engine?: 'v2';
75
- /**
76
- * Turbopack integration mode.
77
- *
78
- * - `'auto'` (default): activate whenever the process runs under
79
- * Turbopack (Next sets the `TURBOPACK` environment variable for every
80
- * Turbopack dev/build). Zero-config support for Next 16 defaults and
81
- * `next dev --turbopack` on Next 15.
82
- * - `'on'`: always generate Turbopack wiring.
83
- * - `'off'`: webpack-only; no Turbopack keys are generated.
84
- *
85
- * When active, extraction runs during next.config resolution (the wrapped
86
- * config resolves asynchronously — Next awaits it), a dev watcher re-runs
87
- * analysis on source changes, and per-file transforms run in a stateless
88
- * loader fed by `.animus/` disk artifacts.
89
- */
34
+ /** `'auto'` (default) generates Turbopack wiring whenever the `TURBOPACK`
35
+ * environment variable is set; `'on'` always, `'off'` never. */
90
36
  turbopack?: {
91
37
  mode?: 'auto' | 'on' | 'off';
92
38
  };
93
- /**
94
- * @deprecated Use `turbopack` — same shape. Honored for one release
95
- * cycle with a one-time warning; `turbopack` wins when both are set.
96
- */
39
+ /** @deprecated Use `turbopack` — same shape, and it wins when both are
40
+ * set. This alias warns once. */
97
41
  unstable_turbopack?: {
98
42
  mode?: 'off' | 'auto' | 'on';
99
43
  };
100
- /**
101
- * @internal Absolute path of the loader module registered in the webpack
102
- * rule when it is not this package's own dist/src loader (test harnesses
103
- * wrapping the real loader in a require-able shim). Feeds the needBuild
104
- * loader-chain predicate; `withAnimus` never sets it.
105
- */
44
+ /** @internal Absolute path of a loader module registered in place of this
45
+ * package's own; feeds the needBuild loader-chain predicate. */
106
46
  loaderPath?: string;
107
- /**
108
- * Full `@layer` declaration order. Must include all 7 Animus `anm-*`
109
- * layers (`anm-global`, `anm-base`, `anm-variants`, `anm-compounds`,
110
- * `anm-states`, `anm-system`, `anm-custom`) as a subsequence. Consumer
111
- * layers may be interleaved. Names are emitted as-is.
112
- *
113
- * @example ['reset', 'anm-global', 'anm-base', 'anm-variants', 'anm-compounds', 'anm-states', 'anm-system', 'anm-custom', 'overrides']
114
- */
47
+ /** Full `@layer` declaration order, emitted as-is. Must contain the seven
48
+ * `anm-*` layers as a subsequence; consumer layers may be interleaved. */
115
49
  layers?: string[];
116
50
  }
117
51
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACpC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC;IACd;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAA;KAAE,CAAC;IAC7C;;;OAGG;IACH,kBAAkB,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtD;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf;+EAC2E;IAC3E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;4EACwE;IACxE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;0EACsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;yEACqE;IACrE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;mDAC+C;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;wDACoD;IACpD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;yEACqE;IACrE,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACpC;gFAC4E;IAC5E,MAAM,CAAC,EAAE,IAAI,CAAC;IACd;qEACiE;IACjE,SAAS,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAA;KAAE,CAAC;IAC7C;sCACkC;IAClC,kBAAkB,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtD;qEACiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;+EAC2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB"}
@@ -1,33 +1,48 @@
1
1
  import type { AnimusNextOptions } from './types';
2
- type WebpackConfig = {
3
- plugins?: unknown[];
2
+ import type { TurbopackWatchOutcome } from '@animus-ui/extract/session';
3
+ import type { NextConfig as NextOwnedConfig, TurbopackOptions } from 'next/dist/server/config-shared';
4
+ type WebpackPluginEntry = object | false | null | undefined;
5
+ interface WebpackLoaderUse {
6
+ loader: string;
7
+ options?: object | string;
8
+ }
9
+ interface WebpackRule {
10
+ test?: RegExp | ((path: string) => boolean);
11
+ exclude?: RegExp | ((path: string) => boolean);
12
+ enforce?: string;
13
+ use?: WebpackLoaderUse[];
14
+ }
15
+ interface WebpackConfig {
16
+ plugins?: WebpackPluginEntry[];
4
17
  resolve?: {
5
18
  alias?: Record<string, string>;
6
19
  };
7
20
  module?: {
8
- rules?: Array<{
9
- test?: RegExp | ((path: string) => boolean);
10
- exclude?: RegExp | ((path: string) => boolean);
11
- enforce?: string;
12
- use?: Array<{
13
- loader: string;
14
- options?: Record<string, unknown>;
15
- }>;
16
- }>;
21
+ rules?: WebpackRule[];
17
22
  };
23
+ }
24
+ type DefinePluginConstructor = new (definitions: Record<string, string>) => object;
25
+ interface NextWebpackContext {
26
+ dir?: string;
27
+ dev?: boolean;
28
+ webpack?: {
29
+ DefinePlugin?: DefinePluginConstructor;
30
+ };
31
+ }
32
+ type NextWebpackHook = (config: WebpackConfig, context: NextWebpackContext) => WebpackConfig;
33
+ interface NextConfigBoundary {
34
+ webpack?: NextWebpackHook | null;
35
+ turbopack?: TurbopackOptions;
36
+ }
37
+ type CallableNextConfig = (...args: never[]) => NextOwnedConfig | Promise<NextOwnedConfig>;
38
+ export type { NextConfigBoundary as AnimusNextConfigBoundary };
39
+ export type NextConfigInput<Config extends NextOwnedConfig> = Config extends CallableNextConfig ? never : Config & NextConfigBoundary;
40
+ export type WebpackNextConfig<Config extends NextOwnedConfig> = Omit<Config, 'webpack'> & {
41
+ webpack: NextWebpackHook;
18
42
  };
19
- type NextConfig = Record<string, unknown> & {
20
- webpack?: (config: WebpackConfig, context: Record<string, unknown>) => WebpackConfig;
43
+ export type TurbopackNextConfig<Config extends NextOwnedConfig> = Omit<Config, 'turbopack'> & {
44
+ turbopack: TurbopackOptions;
21
45
  };
22
- /**
23
- * Wrap a Next.js config to enable Animus static CSS extraction.
24
- *
25
- * ```ts
26
- * // next.config.ts
27
- * import { withAnimus } from '@animus-ui/next-plugin';
28
- * export default withAnimus({ system: './src/ds.ts' })({});
29
- * ```
30
- */
31
- export declare function withAnimus(options: AnimusNextOptions): (nextConfig: NextConfig) => NextConfig | Promise<NextConfig>;
32
- export {};
46
+ export declare function withAnimus(options: AnimusNextOptions): <Config extends NextOwnedConfig>(nextConfig: NextConfigInput<Config>) => WebpackNextConfig<Config> | Promise<TurbopackNextConfig<Config>>;
47
+ export declare function bindTurbopackWatchDeathReport(outcome: TurbopackWatchOutcome, rootDir: string): void;
33
48
  //# sourceMappingURL=with-animus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"with-animus.d.ts","sourceRoot":"","sources":["../src/with-animus.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAKjD,KAAK,aAAa,GAAG;IACnB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAChC,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,KAAK,CAAC;YACZ,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;YAC5C,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;YAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,GAAG,CAAC,EAAE,KAAK,CAAC;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,CAAC,CAAC;SACpE,CAAC,CAAC;KACJ,CAAC;CACH,CAAC;AAEF,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC1C,OAAO,CAAC,EAAE,CACR,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,aAAa,CAAC;CACpB,CAAC;AAKF;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,iBAAiB,GACzB,CAAC,UAAU,EAAE,UAAU,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAoP9D"}
1
+ {"version":3,"file":"with-animus.d.ts","sourceRoot":"","sources":["../src/with-animus.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EACV,UAAU,IAAI,eAAe,EAC7B,gBAAgB,EACjB,MAAM,gCAAgC,CAAC;AAKxC,KAAK,kBAAkB,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;AAE5D,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,UAAU,WAAW;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC1B;AAED,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAChC,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;KACvB,CAAC;CACH;AAED,KAAK,uBAAuB,GAAG,KAC7B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAChC,MAAM,CAAC;AAEZ,UAAU,kBAAkB;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE;QACR,YAAY,CAAC,EAAE,uBAAuB,CAAC;KACxC,CAAC;CACH;AAED,KAAK,eAAe,GAAG,CACrB,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,kBAAkB,KACxB,aAAa,CAAC;AAEnB,UAAU,kBAAkB;IAC1B,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,KAAK,kBAAkB,GAAG,CACxB,GAAG,IAAI,EAAE,KAAK,EAAE,KACb,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAIhD,YAAY,EAAE,kBAAkB,IAAI,wBAAwB,EAAE,CAAC;AAE/D,MAAM,MAAM,eAAe,CAAC,MAAM,SAAS,eAAe,IACxD,MAAM,SAAS,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,kBAAkB,CAAC;AAE1E,MAAM,MAAM,iBAAiB,CAAC,MAAM,SAAS,eAAe,IAAI,IAAI,CAClE,MAAM,EACN,SAAS,CACV,GAAG;IACF,OAAO,EAAE,eAAe,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,MAAM,SAAS,eAAe,IAAI,IAAI,CACpE,MAAM,EACN,WAAW,CACZ,GAAG;IACF,SAAS,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAKF,wBAAgB,UAAU,CACxB,OAAO,EAAE,iBAAiB,GACzB,CAAC,MAAM,SAAS,eAAe,EAChC,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,KAChC,iBAAiB,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CA4LpE;AAED,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,MAAM,GACd,IAAI,CASN"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animus-ui/next-plugin",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Animus static CSS extraction for Next.js (webpack)",
5
5
  "keywords": [
6
6
  "animus",
@@ -40,7 +40,10 @@
40
40
  "compile": "tsc --noEmit"
41
41
  },
42
42
  "dependencies": {
43
- "@animus-ui/extract": "0.1.11"
43
+ "@animus-ui/extract": "0.1.13"
44
+ },
45
+ "devDependencies": {
46
+ "@animus-ui/assertions": "0.1.13"
44
47
  },
45
48
  "peerDependencies": {
46
49
  "@mdx-js/mdx": "^3.0.0",
@@ -1,71 +0,0 @@
1
- //#region src/loader-core.ts
2
- /**
3
- * Regex matching CSS import lines injected by the Rust emitter.
4
- * Catches both the alias form (`import '.animus/styles.css'`),
5
- * relative forms (`import '../.animus/styles.css'`), and the Vite
6
- * virtual module form (`import 'virtual:animus/styles.css'`) which
7
- * appears in pre-built external packages compiled with the Vite plugin.
8
- */
9
- const CSS_IMPORT_RE = /import\s+['"](?:[^'"]*\.animus\/styles\.css|virtual:animus\/styles\.css)['"];\n?/g;
10
- /**
11
- * Default root entry file patterns. CSS is imported ONLY in the root entry
12
- * to prevent per-chunk duplication in Next.js builds.
13
- *
14
- * - App Router: `app/layout.tsx` (root layout wraps all routes)
15
- * - Pages Router: `pages/_app.tsx` (custom App wraps all pages)
16
- *
17
- * The `(src\/)?` prefix handles projects that use a `src/` directory.
18
- * Projects whose root entry doesn't match (nested layouts, monorepo roots)
19
- * set the `cssImportTarget` plugin option, which replaces this detection.
20
- */
21
- const ROOT_ENTRY_RE = /^(src\/)?(?:app\/layout|pages\/_app)\.[tj]sx?$/;
22
- /** Normalize separators and strip a leading './' for path comparison. */
23
- function normalizePath(p) {
24
- return p.replace(/\\/g, "/").replace(/^\.\//, "");
25
- }
26
- /**
27
- * Decide whether `filename` (project-root-relative) is the file that
28
- * receives the single CSS import. An explicit `cssImportTarget` replaces
29
- * the default filename-convention detection. Separators are normalized
30
- * first — Windows `relative()` yields backslashes the patterns would miss.
31
- */
32
- function isCssImportTarget(filename, cssImportTarget) {
33
- const normalized = normalizePath(filename);
34
- if (cssImportTarget) return normalized === normalizePath(cssImportTarget);
35
- return ROOT_ENTRY_RE.test(normalized);
36
- }
37
- /**
38
- * Transform one source file against a manifest: replace builder chains via
39
- * the engine, strip emitter-injected stylesheet imports everywhere, and
40
- * re-inject a single import at the root entry (after any leading
41
- * `'use client'` directive). On failure: throw in strict mode, otherwise
42
- * warn and return the source unchanged.
43
- */
44
- function transformWithManifest(args) {
45
- const { source, filename, manifestJson, engineApi, opts } = args;
46
- const isRootEntry = isCssImportTarget(filename, opts.cssImportTarget);
47
- try {
48
- const { transformFile } = engineApi();
49
- const result = transformFile(source, filename, manifestJson);
50
- let code = result.hasComponents ? result.code : source;
51
- code = code.replace(CSS_IMPORT_RE, "");
52
- if (isRootEntry && !code.includes(".animus/styles.css")) if (code.startsWith("'use client'") || code.startsWith("\"use client\"")) {
53
- const nl = code.indexOf("\n");
54
- if (nl === -1) code = `${code}\nimport '.animus/styles.css';\n`;
55
- else code = `${code.slice(0, nl + 1)}import '.animus/styles.css';\n${code.slice(nl + 1)}`;
56
- } else code = `import '.animus/styles.css';\n${code}`;
57
- return code;
58
- } catch (e) {
59
- const msg = e instanceof Error ? e.message : String(e);
60
- if (opts.strict) throw new Error(`[animus-extract] Transform failed for ${filename}: ${msg}`, { cause: e });
61
- console.warn(`[animus-extract] Transform failed for ${filename}:`, msg);
62
- return source;
63
- }
64
- }
65
- //#endregion
66
- Object.defineProperty(exports, "transformWithManifest", {
67
- enumerable: true,
68
- get: function() {
69
- return transformWithManifest;
70
- }
71
- });
@@ -1,66 +0,0 @@
1
- //#region src/loader-core.ts
2
- /**
3
- * Regex matching CSS import lines injected by the Rust emitter.
4
- * Catches both the alias form (`import '.animus/styles.css'`),
5
- * relative forms (`import '../.animus/styles.css'`), and the Vite
6
- * virtual module form (`import 'virtual:animus/styles.css'`) which
7
- * appears in pre-built external packages compiled with the Vite plugin.
8
- */
9
- const CSS_IMPORT_RE = /import\s+['"](?:[^'"]*\.animus\/styles\.css|virtual:animus\/styles\.css)['"];\n?/g;
10
- /**
11
- * Default root entry file patterns. CSS is imported ONLY in the root entry
12
- * to prevent per-chunk duplication in Next.js builds.
13
- *
14
- * - App Router: `app/layout.tsx` (root layout wraps all routes)
15
- * - Pages Router: `pages/_app.tsx` (custom App wraps all pages)
16
- *
17
- * The `(src\/)?` prefix handles projects that use a `src/` directory.
18
- * Projects whose root entry doesn't match (nested layouts, monorepo roots)
19
- * set the `cssImportTarget` plugin option, which replaces this detection.
20
- */
21
- const ROOT_ENTRY_RE = /^(src\/)?(?:app\/layout|pages\/_app)\.[tj]sx?$/;
22
- /** Normalize separators and strip a leading './' for path comparison. */
23
- function normalizePath(p) {
24
- return p.replace(/\\/g, "/").replace(/^\.\//, "");
25
- }
26
- /**
27
- * Decide whether `filename` (project-root-relative) is the file that
28
- * receives the single CSS import. An explicit `cssImportTarget` replaces
29
- * the default filename-convention detection. Separators are normalized
30
- * first — Windows `relative()` yields backslashes the patterns would miss.
31
- */
32
- function isCssImportTarget(filename, cssImportTarget) {
33
- const normalized = normalizePath(filename);
34
- if (cssImportTarget) return normalized === normalizePath(cssImportTarget);
35
- return ROOT_ENTRY_RE.test(normalized);
36
- }
37
- /**
38
- * Transform one source file against a manifest: replace builder chains via
39
- * the engine, strip emitter-injected stylesheet imports everywhere, and
40
- * re-inject a single import at the root entry (after any leading
41
- * `'use client'` directive). On failure: throw in strict mode, otherwise
42
- * warn and return the source unchanged.
43
- */
44
- function transformWithManifest(args) {
45
- const { source, filename, manifestJson, engineApi, opts } = args;
46
- const isRootEntry = isCssImportTarget(filename, opts.cssImportTarget);
47
- try {
48
- const { transformFile } = engineApi();
49
- const result = transformFile(source, filename, manifestJson);
50
- let code = result.hasComponents ? result.code : source;
51
- code = code.replace(CSS_IMPORT_RE, "");
52
- if (isRootEntry && !code.includes(".animus/styles.css")) if (code.startsWith("'use client'") || code.startsWith("\"use client\"")) {
53
- const nl = code.indexOf("\n");
54
- if (nl === -1) code = `${code}\nimport '.animus/styles.css';\n`;
55
- else code = `${code.slice(0, nl + 1)}import '.animus/styles.css';\n${code.slice(nl + 1)}`;
56
- } else code = `import '.animus/styles.css';\n${code}`;
57
- return code;
58
- } catch (e) {
59
- const msg = e instanceof Error ? e.message : String(e);
60
- if (opts.strict) throw new Error(`[animus-extract] Transform failed for ${filename}: ${msg}`, { cause: e });
61
- console.warn(`[animus-extract] Transform failed for ${filename}:`, msg);
62
- return source;
63
- }
64
- }
65
- //#endregion
66
- export { transformWithManifest as t };