@animus-ui/unplugin 0.1.24 → 0.1.26

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 CHANGED
@@ -88,9 +88,18 @@ Options are the shared driver core (`system`, `exclude`, `strict`, `mode`,
88
88
  default, the dev-signal define, engine dev diagnostics — never process
89
89
  lifecycle. When absent, the bundler's own command oracle applies (webpack/
90
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`).
91
+ (esbuild exposes no signal). `exclude` patterns are glob-aware; a list
92
+ REPLACES the `dist`, `.test.` and `.spec.` defaults, while `node_modules`,
93
+ `.next` and `.animus` always apply. Wherever the bundler writes is
94
+ always excluded too: its output directories (every Vite environment's,
95
+ each output's `dir`, webpack's `output.path` up to its first `[…]`
96
+ placeholder) and its output files by exact name (`outfile`,
97
+ `output.file`). webpack, rspack, esbuild and Vite name them before
98
+ analysis. Rollup and Rolldown name them only after analysis, so a watch
99
+ excludes them from its second build on, and a build that has already
100
+ analysed one reports it (an output file as earlier output; a file inside
101
+ an output directory as earlier output or a source beside the output),
102
+ naming what to add to `exclude`; under `strict` that build fails.
94
103
 
95
104
  The full consumer contract — module-id families, the define contract,
96
105
  artifact set, platform matrix — lives in the
@@ -1,5 +1,5 @@
1
- import { AnimusConfigError, DiagnosticFailure, ENGINE_TRANSFORM_EXTENSIONS, assertKnownOptionKeys, assertNoRetiredEngineSelection, buildPathAliasesJson, isEngineTransformExtension, isPathWithinRoot, keepsKitSourceEffects, readTsconfigAliasPairs, resolveMode } from "@animus-ui/extract/pipeline";
2
- import { ANIMUS_CSS_MODULE_ID, ExtractionSession, SESSION_ASSETS_DIR, TURBOPACK_SYSTEM_PROPS_ID, collectSessionAssets, engineApi, getAnalyzedHashes, getManifestJson, getSessionArtifactDir, getSharedCss, getSharedSystemProps } from "@animus-ui/extract/session";
1
+ import { AnimusConfigError, BUILT_IN_EXTENSION_POLICY, DiagnosticFailure, ENGINE_TRANSFORM_EXTENSIONS, analysedOutputs, assertKnownOptionKeys, assertSupportedEngine, builtInAssetClassification, droppedExtensionMessage, esbuildAssetClassification, esbuildExtensionPolicy, esbuildOutputTargets, isEngineTransformExtension, isPathWithinRoot, keepsKitSourceEffects, mergeOutputTargets, noOutputTargets, outputExclusions, resolveMode, rollupExtensionPolicy, rollupOutputTargets, servedFiles, viteAssetClassification, viteExtensionPolicy, viteHostExpander, viteOutputTargets, webpackExtensionPolicy, webpackHostExpander, webpackOutputTargets } from "@animus-ui/extract/pipeline";
2
+ import { ANIMUS_CSS_MODULE_ID, ExtractionSession, SESSION_ASSETS_DIR, TURBOPACK_SYSTEM_PROPS_ID, collectSessionAssets, createBuildId, engineApi, getAnalyzedHashes, getManifestJson, getSessionArtifactDir, getSharedCss, getSharedSystemProps } from "@animus-ui/extract/session";
3
3
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join, relative, resolve } from "node:path";
5
5
  //#region src/options.ts
@@ -19,7 +19,7 @@ function hasRequiredSystem(options) {
19
19
  }
20
20
  function resolveHostOptions(raw, cwd = process.cwd()) {
21
21
  const options = raw ?? {};
22
- assertNoRetiredEngineSelection(options.engine);
22
+ assertSupportedEngine(options.engine);
23
23
  assertKnownOptionKeys(options, ["watch"]);
24
24
  if (!hasRequiredSystem(options)) throw new AnimusConfigError("Missing required option `system` — pass `system: \"./src/ds.ts\"` to the Animus plugin.");
25
25
  const root = options.root ? resolve(cwd, options.root) : cwd;
@@ -52,6 +52,11 @@ function createHostState() {
52
52
  pipeline: null,
53
53
  mode: null,
54
54
  cssText: "",
55
+ served: {
56
+ listed: /* @__PURE__ */ new Set(),
57
+ dropped: /* @__PURE__ */ new Map()
58
+ },
59
+ analyzedFiles: [],
55
60
  systemPropsJs: "",
56
61
  kitRedirects: /* @__PURE__ */ new Map(),
57
62
  kitSideEffects: /* @__PURE__ */ new Map(),
@@ -59,7 +64,8 @@ function createHostState() {
59
64
  externalPackageDirs: [],
60
65
  watchPaths: [],
61
66
  transformFile: null,
62
- sessionDir: null
67
+ sessionDir: null,
68
+ key: null
63
69
  };
64
70
  }
65
71
  function shouldClaimTransform(filePath, state) {
@@ -81,7 +87,7 @@ async function drivePipeline(state, run, removeDir) {
81
87
  try {
82
88
  await attempt;
83
89
  } catch (error) {
84
- if (!state.sessionDir) state.sessionDir = getSessionArtifactDir();
90
+ if (!state.sessionDir && state.key !== null) state.sessionDir = getSessionArtifactDir(state.key);
85
91
  disposeSessionDir(state, removeDir);
86
92
  throw error;
87
93
  }
@@ -98,8 +104,20 @@ function substituteDevDefine(code, isDev) {
98
104
  }
99
105
  function transformWithEngine(code, id, ctx) {
100
106
  const filename = relative(ctx.rootDir, resolve(id)).split("\\").join("/");
107
+ const drop = ctx.served?.dropped.get(filename);
108
+ if (drop !== void 0) throw new Error(droppedExtensionMessage(drop));
101
109
  const result = ctx.transformFile(code, filename, ctx.manifestJson);
102
- return result.hasComponents ? result.code : null;
110
+ if (result.hasComponents) return result.code;
111
+ if (ctx.served?.listed.has(filename)) throw new Error(`[animus] the analysis extracted components from ${filename}, but its transform found none`);
112
+ return null;
113
+ }
114
+ /** What a manifest's transform serves, parsed once per run. */
115
+ function manifestServedFiles(manifestJson) {
116
+ if (manifestJson === null) return {
117
+ listed: /* @__PURE__ */ new Set(),
118
+ dropped: /* @__PURE__ */ new Map()
119
+ };
120
+ return servedFiles(JSON.parse(manifestJson));
103
121
  }
104
122
  /** Whether esbuild's `external` option names `specifier`: exactly, as the
105
123
  * package a subpath belongs to, or through a `*` wildcard. */
@@ -118,13 +136,52 @@ function moduleFilePath(id) {
118
136
  function canResolve(context) {
119
137
  return "resolve" in context;
120
138
  }
139
+ /** Whether a hook context names the environment it builds (Vite). */
140
+ function hasEnvironment(context) {
141
+ return "environment" in context;
142
+ }
121
143
  /** Marks the esbuild resolve that `esbuild.setup` makes for itself. */
122
144
  const KIT_SOURCE_RESOLVE = "animus:kit-source-resolve";
145
+ /** Rules as comparable text: a RegExp by its source and flags, a function
146
+ * by its text. */
147
+ function ruleText(rules) {
148
+ return JSON.stringify(rules ?? null, (_key, value) => value instanceof RegExp ? `/${value.source}/${value.flags}` : value instanceof Function ? String(value) : value);
149
+ }
150
+ /** Whether `module.defaultRules` is webpack's own default list: the list
151
+ * webpack's defaults give the compiler's experiments, unchanged. Throws
152
+ * where webpack's option API is not available. */
153
+ function defaultRulesAreWebpacks(compiler) {
154
+ const config = compiler.webpack?.config;
155
+ if (config === void 0) throw new Error("webpack's option defaults are not available");
156
+ const { css, asyncWebAssembly, syncWebAssembly } = compiler.options.experiments ?? {};
157
+ const fresh = config.getNormalizedWebpackOptions({ experiments: {
158
+ css,
159
+ asyncWebAssembly,
160
+ syncWebAssembly
161
+ } });
162
+ config.applyWebpackOptionsDefaults(fresh);
163
+ return ruleText(fresh.module?.defaultRules) === ruleText(compiler.options.module?.defaultRules);
164
+ }
165
+ /** Whether every webpack JavaScript parser keeps the default wrapped
166
+ * context, `/.*\/` and recursive. */
167
+ function wrappedContextDefaults(parser) {
168
+ return Object.entries(parser ?? {}).filter(([type]) => type === "javascript" || type.startsWith("javascript/")).every(([, options]) => (options?.wrappedContextRegExp === void 0 || options.wrappedContextRegExp.source === ".*" && options.wrappedContextRegExp.flags === "") && options?.wrappedContextRecursive !== false);
169
+ }
170
+ function hasPluginResolve(context) {
171
+ return "resolve" in context;
172
+ }
173
+ /** The bundler's resolution through a Rollup-family plugin context (Rollup,
174
+ * Rolldown, Vite); none where the context cannot resolve. */
175
+ function pluginResolver(context) {
176
+ if (!hasPluginResolve(context)) return null;
177
+ return async (specifier, importer) => (await context.resolve(specifier, importer, { skipSelf: true }))?.id ?? null;
178
+ }
123
179
  const PLUGIN_NAME = "animus-host";
124
180
  const ESBUILD_WATCH_HINT = "If this is an esbuild watch or serve context, pass watch: true to the Animus esbuild plugin.";
125
181
  const unpluginFactory = (rawOptions, meta) => {
126
182
  const { root, options, watch } = resolveHostOptions(rawOptions);
127
183
  const state = createHostState();
184
+ const buildId = createBuildId();
128
185
  let activeSession = null;
129
186
  let modeOracle = null;
130
187
  /** Whether the host is watching, where an error-level diagnostic reports
@@ -137,6 +194,23 @@ const unpluginFactory = (rawOptions, meta) => {
137
194
  /** The bundler's export conditions, in its order, for the system loader;
138
195
  * Rollup exposes none. */
139
196
  let hostConditions = [];
197
+ /** The bundler's extension order, as its resolved configuration holds
198
+ * it; the built-in policy for a host none is read from (Rolldown). */
199
+ let hostExtensions = BUILT_IN_EXTENSION_POLICY;
200
+ let hostAssets = builtInAssetClassification("noHost");
201
+ /** How the bundler expands computed loads (spec §6.3); none where its
202
+ * expansion is not reproduced. */
203
+ let hostLoadExpander;
204
+ /** The bundler's own resolution, which package discovery asks first
205
+ * (spec §2.2 A4); none where the host exposes no resolver. */
206
+ let hostResolver = null;
207
+ /** What Rollup and Rolldown named at render, which they name only after
208
+ * analysis: this process's later builds exclude it. */
209
+ let learnedOutputTargets = noOutputTargets();
210
+ /** Where the bundler writes, which discovery excludes whatever the
211
+ * user's `exclude` list: read when a run starts, after the bundler
212
+ * applied its defaults. */
213
+ let hostOutputTargets = () => learnedOutputTargets;
140
214
  /** Hooks can fire before buildStart (webpack's make taps run
141
215
  * concurrently), so joiners await this before awaiting the pipeline. */
142
216
  let signalPipelineStarted;
@@ -150,6 +224,10 @@ const unpluginFactory = (rawOptions, meta) => {
150
224
  const unbundledComputedImports = meta.framework === "rollup" || meta.framework === "rolldown" || meta.framework === "esbuild" || meta.framework === "vite";
151
225
  async function startPipeline() {
152
226
  signalPipelineStarted();
227
+ if (activeSession !== null && (meta.framework === "webpack" || meta.framework === "rspack")) {
228
+ disposeSessionDir(state);
229
+ releaseClaim();
230
+ }
153
231
  try {
154
232
  await runClaimedPipeline();
155
233
  } catch (error) {
@@ -169,34 +247,100 @@ const unpluginFactory = (rawOptions, meta) => {
169
247
  const session = new ExtractionSession({
170
248
  ...options,
171
249
  mode
172
- }, { unbundledComputedImports });
250
+ }, {
251
+ unbundledComputedImports,
252
+ build: buildId
253
+ });
173
254
  activeSession = session;
174
255
  session.development = watching();
175
256
  session.driverLabel = "animus-unplugin";
176
257
  session.rootDir = root;
177
258
  session.conditions = hostConditions;
259
+ session.extensionPolicy = hostExtensions;
260
+ session.assetClassification = hostAssets;
261
+ session.loadExpander = hostLoadExpander;
262
+ session.hostResolve = hostResolver;
178
263
  session.systemPropsModuleId = TURBOPACK_SYSTEM_PROPS_ID;
179
- const aliasPairs = readTsconfigAliasPairs(root);
180
- const builtAliases = buildPathAliasesJson(aliasPairs, root);
181
- if (builtAliases) session.pathAliasesJson = builtAliases.json;
182
- await session.runFullPipeline();
183
- state.sessionDir = getSessionArtifactDir();
184
- const analyzed = getAnalyzedHashes();
264
+ session.structuralExclude = outputExclusions(root, hostOutputTargets());
265
+ session.aliasesFromTsconfig = true;
266
+ state.key = session.key;
267
+ try {
268
+ await session.runFullPipeline();
269
+ } catch (error) {
270
+ state.watchPaths = [
271
+ resolve(root, options.system),
272
+ ...session.systemDependencyPaths,
273
+ ...session.systemMissingPaths,
274
+ join(root, "tsconfig.json")
275
+ ];
276
+ throw error;
277
+ }
278
+ state.sessionDir = getSessionArtifactDir(session.key);
279
+ const analyzed = getAnalyzedHashes(session.key);
185
280
  if (!analyzed || analyzed.size === 0) throw new Error(`[animus] discovery found zero source files under ${root} — check the plugin's \`root\` and \`exclude\` options`);
186
- state.cssText = getSharedCss();
187
- state.systemPropsJs = getSharedSystemProps();
281
+ state.cssText = getSharedCss(session.key);
282
+ state.served = manifestServedFiles(getManifestJson(session.key));
283
+ state.systemPropsJs = getSharedSystemProps(session.key);
188
284
  state.kitRedirects = new Map(session.externalSourceEntries);
189
285
  state.kitSideEffects = new Map(session.externalSourceSideEffects);
190
286
  state.redirectTargets = new Set(state.kitRedirects.values());
191
287
  state.externalPackageDirs = [...session.externalPackageDirs];
192
- const watchPaths = /* @__PURE__ */ new Set();
193
- for (const key of getAnalyzedHashes()?.keys() ?? []) watchPaths.add(resolve(root, key));
288
+ state.analyzedFiles = [...analyzed.keys()].map((key) => resolve(root, key));
289
+ const watchPaths = new Set(state.analyzedFiles);
194
290
  for (const dep of session.systemDependencyPaths) watchPaths.add(dep);
291
+ for (const path of session.systemMissingPaths) watchPaths.add(path);
195
292
  for (const dep of session.assetDependencyPaths) watchPaths.add(dep);
196
293
  watchPaths.add(join(root, "tsconfig.json"));
197
294
  state.watchPaths = [...watchPaths];
198
- state.transformFile = engineApi().transformFile;
295
+ state.transformFile = engineApi(session.key).transformFile;
296
+ });
297
+ }
298
+ /** Each Rollup-family environment build that started and has not ended,
299
+ * by name, with the promise its end settles. A Vite buildApp can build
300
+ * environments in parallel on one plugin instance; they share one
301
+ * pipeline and one Session, retired by the last to end. A build of an
302
+ * environment whose build is open (a reused builder, or a second
303
+ * environment under one name) waits for that build's end, so each build
304
+ * ends only its own generation, once, in buildEnd. webpack-like hosts run
305
+ * one compile at a time and are not tracked. */
306
+ const openBuilds = /* @__PURE__ */ new Map();
307
+ const singleEnvironment = { name: "build" };
308
+ const tracksEnvironments = meta.framework === "rollup" || meta.framework === "rolldown" || meta.framework === "vite";
309
+ function environmentOf(context) {
310
+ return hasEnvironment(context) ? context.environment : singleEnvironment;
311
+ }
312
+ /** Opens this hook's environment build once no build under its name is
313
+ * open; whether another environment's build is open, whose pipeline this
314
+ * build shares. */
315
+ async function openEnvironmentBuild(context) {
316
+ if (!tracksEnvironments) return false;
317
+ const environment = environmentOf(context);
318
+ for (;;) {
319
+ const open = openBuilds.get(environment.name);
320
+ if (open === void 0) break;
321
+ await open.ended;
322
+ }
323
+ let end = () => {};
324
+ const ended = new Promise((settle) => {
325
+ end = settle;
199
326
  });
327
+ openBuilds.set(environment.name, {
328
+ environment,
329
+ ended,
330
+ end
331
+ });
332
+ return openBuilds.size > 1;
333
+ }
334
+ /** Ends this hook's environment build; whether none is left open. Only
335
+ * the build that opened an entry ends it. */
336
+ function closeEnvironmentBuild(context) {
337
+ if (!tracksEnvironments) return true;
338
+ const environment = environmentOf(context);
339
+ const open = openBuilds.get(environment.name);
340
+ if (open === void 0 || open.environment !== environment) return false;
341
+ openBuilds.delete(environment.name);
342
+ open.end();
343
+ return openBuilds.size === 0;
200
344
  }
201
345
  async function joinPipeline() {
202
346
  await pipelineStarted;
@@ -236,6 +380,17 @@ const unpluginFactory = (rawOptions, meta) => {
236
380
  source: bytes
237
381
  });
238
382
  }
383
+ /** Rollup and Rolldown name their output only at render, after
384
+ * analysis: this process's later builds exclude it, and an analysis that
385
+ * already read a file there is reported, failing a build under `strict`. */
386
+ function checkOwnOutput(context, output) {
387
+ const targets = rollupOutputTargets(output);
388
+ learnedOutputTargets = mergeOutputTargets([learnedOutputTargets, targets]);
389
+ const message = analysedOutputs(root, targets, state.analyzedFiles);
390
+ if (message === null) return;
391
+ if (options.strict && !watching()) context.error(message);
392
+ context.warn(message);
393
+ }
239
394
  /** Analysis is a filesystem walk, so watch mode misses edits to
240
395
  * analyzed-but-unimported files. esbuild has no watch-file seam. */
241
396
  function registerWatchTargets(context) {
@@ -248,8 +403,13 @@ const unpluginFactory = (rawOptions, meta) => {
248
403
  name: PLUGIN_NAME,
249
404
  enforce: "pre",
250
405
  async buildStart() {
251
- await startPipeline();
252
- registerWatchTargets(this);
406
+ if (rollupLike) hostResolver = pluginResolver(this);
407
+ try {
408
+ if (await openEnvironmentBuild(this)) await joinPipeline();
409
+ else await startPipeline();
410
+ } finally {
411
+ registerWatchTargets(this);
412
+ }
253
413
  },
254
414
  async resolveId(id, importer) {
255
415
  const virtual = resolveAnimusId(id);
@@ -309,12 +469,14 @@ const unpluginFactory = (rawOptions, meta) => {
309
469
  await joinPipeline();
310
470
  const filePath = moduleFilePath(id);
311
471
  let output = code;
312
- if (isEngineTransformExtension(filePath)) {
313
- const transformFile = state.transformFile ?? engineApi().transformFile;
472
+ const key = state.key;
473
+ if (key !== null && isEngineTransformExtension(filePath)) {
474
+ const transformFile = state.transformFile ?? engineApi(key).transformFile;
314
475
  const transformed = transformWithEngine(output, filePath, {
315
476
  rootDir: root,
316
- manifestJson: getManifestJson() ?? "",
317
- transformFile
477
+ manifestJson: getManifestJson(key) ?? "",
478
+ transformFile,
479
+ served: state.served
318
480
  });
319
481
  if (transformed !== null) output = transformed;
320
482
  }
@@ -331,18 +493,39 @@ const unpluginFactory = (rawOptions, meta) => {
331
493
  try {
332
494
  emitCssAsset(this);
333
495
  } finally {
334
- disposeSessionDir(state);
335
- releaseClaim();
496
+ if (closeEnvironmentBuild(this)) {
497
+ disposeSessionDir(state);
498
+ releaseClaim();
499
+ }
336
500
  }
337
501
  },
338
502
  vite: { configResolved(config) {
503
+ hostExtensions = viteExtensionPolicy(config.resolve.extensions);
504
+ hostAssets = viteAssetClassification(config.assetsInclude);
505
+ hostLoadExpander = viteHostExpander(config);
506
+ hostOutputTargets = () => viteOutputTargets(config);
339
507
  const serving = config.command === "serve" || Boolean(config.build.watch);
340
508
  watching = () => serving;
341
509
  } },
342
- rollup: { async buildStart() {
343
- modeOracle = this.meta?.watchMode ? "development" : "production";
344
- await startPipeline();
345
- registerWatchTargets(this);
510
+ rollup: {
511
+ async buildStart(inputOptions) {
512
+ hostExtensions = rollupExtensionPolicy((inputOptions?.plugins ?? []).some((plugin) => plugin?.name === "node-resolve"));
513
+ hostAssets = builtInAssetClassification("hostOptionUnreadable", "rollup");
514
+ modeOracle = this.meta?.watchMode ? "development" : "production";
515
+ hostResolver = pluginResolver(this);
516
+ try {
517
+ if (await openEnvironmentBuild(this)) await joinPipeline();
518
+ else await startPipeline();
519
+ } finally {
520
+ registerWatchTargets(this);
521
+ }
522
+ },
523
+ renderStart(outputOptions) {
524
+ checkOwnOutput(this, outputOptions);
525
+ }
526
+ },
527
+ rolldown: { renderStart(outputOptions) {
528
+ checkOwnOutput(this, outputOptions);
346
529
  } },
347
530
  webpack(compiler) {
348
531
  wireWebpackLike(compiler);
@@ -396,7 +579,10 @@ const unpluginFactory = (rawOptions, meta) => {
396
579
  },
397
580
  config(buildOptions) {
398
581
  esbuildOptions = buildOptions;
582
+ hostOutputTargets = () => esbuildOutputTargets(buildOptions);
399
583
  hostConditions = buildOptions.conditions ?? [];
584
+ hostExtensions = esbuildExtensionPolicy(buildOptions.resolveExtensions);
585
+ hostAssets = esbuildAssetClassification(buildOptions.loader);
400
586
  watching = () => watch;
401
587
  buildOptions.define = {
402
588
  ...buildOptions.define,
@@ -448,7 +634,30 @@ const unpluginFactory = (rawOptions, meta) => {
448
634
  function wireWebpackLike(compiler) {
449
635
  modeOracle = compiler.options.mode === "development" ? "development" : "production";
450
636
  hostConditions = (compiler.options.resolve?.conditionNames ?? []).filter((name) => name !== "...");
637
+ hostExtensions = webpackExtensionPolicy(meta.framework, compiler.options.resolve ?? {}, compiler.webpack?.version ?? compiler.rspack?.rspackVersion);
638
+ hostAssets = builtInAssetClassification("hostOptionUnreadable", `${meta.framework === "rspack" ? "rspack" : "webpack"} rules not modelled`);
451
639
  watching = () => compiler.watchMode === true;
640
+ hostOutputTargets = () => webpackOutputTargets(compiler.options.output?.path);
641
+ const resolverFactory = compiler.resolverFactory;
642
+ hostLoadExpander = meta.framework === "webpack" && resolverFactory !== void 0 ? webpackHostExpander({
643
+ resolveReading: (category) => {
644
+ const read = resolverFactory.get("normal", { dependencyType: category }).options;
645
+ const context = resolverFactory.get("context", { dependencyType: category }).options;
646
+ return {
647
+ extensions: [...read.extensions],
648
+ mainFiles: [...read.mainFiles],
649
+ modules: read.modules,
650
+ fullySpecified: read.fullySpecified === true,
651
+ enforceExtension: read.enforceExtension === true,
652
+ aliasNames: [read, context].flatMap((resolver) => [...resolver.alias ?? [], ...resolver.fallback ?? []].map((entry) => entry.name))
653
+ };
654
+ },
655
+ options: () => ({
656
+ rules: compiler.options.module?.rules ?? [],
657
+ defaultRulesAreWebpacks: defaultRulesAreWebpacks(compiler),
658
+ wrappedDefaults: wrappedContextDefaults(compiler.options.module?.parser)
659
+ })
660
+ }) : void 0;
452
661
  const DefinePlugin = compiler.webpack?.DefinePlugin ?? compiler.rspack?.DefinePlugin;
453
662
  if (!DefinePlugin) throw new Error("[animus] compiler exposes no DefinePlugin — cannot supply the __ANIMUS_DEV__ dev-signal define");
454
663
  new DefinePlugin({ __ANIMUS_DEV__: JSON.stringify(effectiveMode() === "development") }).apply(compiler);
@@ -460,6 +669,10 @@ const unpluginFactory = (rawOptions, meta) => {
460
669
  disposeSessionDir(state);
461
670
  releaseClaim();
462
671
  });
672
+ compiler.hooks.watchClose?.tap(PLUGIN_NAME, () => {
673
+ disposeSessionDir(state);
674
+ releaseClaim();
675
+ });
463
676
  }
464
677
  };
465
678
  //#endregion
@@ -19,7 +19,7 @@ function hasRequiredSystem(options) {
19
19
  }
20
20
  function resolveHostOptions(raw, cwd = process.cwd()) {
21
21
  const options = raw ?? {};
22
- (0, _animus_ui_extract_pipeline.assertNoRetiredEngineSelection)(options.engine);
22
+ (0, _animus_ui_extract_pipeline.assertSupportedEngine)(options.engine);
23
23
  (0, _animus_ui_extract_pipeline.assertKnownOptionKeys)(options, ["watch"]);
24
24
  if (!hasRequiredSystem(options)) throw new _animus_ui_extract_pipeline.AnimusConfigError("Missing required option `system` — pass `system: \"./src/ds.ts\"` to the Animus plugin.");
25
25
  const root = options.root ? (0, node_path.resolve)(cwd, options.root) : cwd;
@@ -52,6 +52,11 @@ function createHostState() {
52
52
  pipeline: null,
53
53
  mode: null,
54
54
  cssText: "",
55
+ served: {
56
+ listed: /* @__PURE__ */ new Set(),
57
+ dropped: /* @__PURE__ */ new Map()
58
+ },
59
+ analyzedFiles: [],
55
60
  systemPropsJs: "",
56
61
  kitRedirects: /* @__PURE__ */ new Map(),
57
62
  kitSideEffects: /* @__PURE__ */ new Map(),
@@ -59,7 +64,8 @@ function createHostState() {
59
64
  externalPackageDirs: [],
60
65
  watchPaths: [],
61
66
  transformFile: null,
62
- sessionDir: null
67
+ sessionDir: null,
68
+ key: null
63
69
  };
64
70
  }
65
71
  function shouldClaimTransform(filePath, state) {
@@ -81,7 +87,7 @@ async function drivePipeline(state, run, removeDir) {
81
87
  try {
82
88
  await attempt;
83
89
  } catch (error) {
84
- if (!state.sessionDir) state.sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)();
90
+ if (!state.sessionDir && state.key !== null) state.sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)(state.key);
85
91
  disposeSessionDir(state, removeDir);
86
92
  throw error;
87
93
  }
@@ -98,8 +104,20 @@ function substituteDevDefine(code, isDev) {
98
104
  }
99
105
  function transformWithEngine(code, id, ctx) {
100
106
  const filename = (0, node_path.relative)(ctx.rootDir, (0, node_path.resolve)(id)).split("\\").join("/");
107
+ const drop = ctx.served?.dropped.get(filename);
108
+ if (drop !== void 0) throw new Error((0, _animus_ui_extract_pipeline.droppedExtensionMessage)(drop));
101
109
  const result = ctx.transformFile(code, filename, ctx.manifestJson);
102
- return result.hasComponents ? result.code : null;
110
+ if (result.hasComponents) return result.code;
111
+ if (ctx.served?.listed.has(filename)) throw new Error(`[animus] the analysis extracted components from ${filename}, but its transform found none`);
112
+ return null;
113
+ }
114
+ /** What a manifest's transform serves, parsed once per run. */
115
+ function manifestServedFiles(manifestJson) {
116
+ if (manifestJson === null) return {
117
+ listed: /* @__PURE__ */ new Set(),
118
+ dropped: /* @__PURE__ */ new Map()
119
+ };
120
+ return (0, _animus_ui_extract_pipeline.servedFiles)(JSON.parse(manifestJson));
103
121
  }
104
122
  /** Whether esbuild's `external` option names `specifier`: exactly, as the
105
123
  * package a subpath belongs to, or through a `*` wildcard. */
@@ -118,13 +136,52 @@ function moduleFilePath(id) {
118
136
  function canResolve(context) {
119
137
  return "resolve" in context;
120
138
  }
139
+ /** Whether a hook context names the environment it builds (Vite). */
140
+ function hasEnvironment(context) {
141
+ return "environment" in context;
142
+ }
121
143
  /** Marks the esbuild resolve that `esbuild.setup` makes for itself. */
122
144
  const KIT_SOURCE_RESOLVE = "animus:kit-source-resolve";
145
+ /** Rules as comparable text: a RegExp by its source and flags, a function
146
+ * by its text. */
147
+ function ruleText(rules) {
148
+ return JSON.stringify(rules ?? null, (_key, value) => value instanceof RegExp ? `/${value.source}/${value.flags}` : value instanceof Function ? String(value) : value);
149
+ }
150
+ /** Whether `module.defaultRules` is webpack's own default list: the list
151
+ * webpack's defaults give the compiler's experiments, unchanged. Throws
152
+ * where webpack's option API is not available. */
153
+ function defaultRulesAreWebpacks(compiler) {
154
+ const config = compiler.webpack?.config;
155
+ if (config === void 0) throw new Error("webpack's option defaults are not available");
156
+ const { css, asyncWebAssembly, syncWebAssembly } = compiler.options.experiments ?? {};
157
+ const fresh = config.getNormalizedWebpackOptions({ experiments: {
158
+ css,
159
+ asyncWebAssembly,
160
+ syncWebAssembly
161
+ } });
162
+ config.applyWebpackOptionsDefaults(fresh);
163
+ return ruleText(fresh.module?.defaultRules) === ruleText(compiler.options.module?.defaultRules);
164
+ }
165
+ /** Whether every webpack JavaScript parser keeps the default wrapped
166
+ * context, `/.*\/` and recursive. */
167
+ function wrappedContextDefaults(parser) {
168
+ return Object.entries(parser ?? {}).filter(([type]) => type === "javascript" || type.startsWith("javascript/")).every(([, options]) => (options?.wrappedContextRegExp === void 0 || options.wrappedContextRegExp.source === ".*" && options.wrappedContextRegExp.flags === "") && options?.wrappedContextRecursive !== false);
169
+ }
170
+ function hasPluginResolve(context) {
171
+ return "resolve" in context;
172
+ }
173
+ /** The bundler's resolution through a Rollup-family plugin context (Rollup,
174
+ * Rolldown, Vite); none where the context cannot resolve. */
175
+ function pluginResolver(context) {
176
+ if (!hasPluginResolve(context)) return null;
177
+ return async (specifier, importer) => (await context.resolve(specifier, importer, { skipSelf: true }))?.id ?? null;
178
+ }
123
179
  const PLUGIN_NAME = "animus-host";
124
180
  const ESBUILD_WATCH_HINT = "If this is an esbuild watch or serve context, pass watch: true to the Animus esbuild plugin.";
125
181
  const unpluginFactory = (rawOptions, meta) => {
126
182
  const { root, options, watch } = resolveHostOptions(rawOptions);
127
183
  const state = createHostState();
184
+ const buildId = (0, _animus_ui_extract_session.createBuildId)();
128
185
  let activeSession = null;
129
186
  let modeOracle = null;
130
187
  /** Whether the host is watching, where an error-level diagnostic reports
@@ -137,6 +194,23 @@ const unpluginFactory = (rawOptions, meta) => {
137
194
  /** The bundler's export conditions, in its order, for the system loader;
138
195
  * Rollup exposes none. */
139
196
  let hostConditions = [];
197
+ /** The bundler's extension order, as its resolved configuration holds
198
+ * it; the built-in policy for a host none is read from (Rolldown). */
199
+ let hostExtensions = _animus_ui_extract_pipeline.BUILT_IN_EXTENSION_POLICY;
200
+ let hostAssets = (0, _animus_ui_extract_pipeline.builtInAssetClassification)("noHost");
201
+ /** How the bundler expands computed loads (spec §6.3); none where its
202
+ * expansion is not reproduced. */
203
+ let hostLoadExpander;
204
+ /** The bundler's own resolution, which package discovery asks first
205
+ * (spec §2.2 A4); none where the host exposes no resolver. */
206
+ let hostResolver = null;
207
+ /** What Rollup and Rolldown named at render, which they name only after
208
+ * analysis: this process's later builds exclude it. */
209
+ let learnedOutputTargets = (0, _animus_ui_extract_pipeline.noOutputTargets)();
210
+ /** Where the bundler writes, which discovery excludes whatever the
211
+ * user's `exclude` list: read when a run starts, after the bundler
212
+ * applied its defaults. */
213
+ let hostOutputTargets = () => learnedOutputTargets;
140
214
  /** Hooks can fire before buildStart (webpack's make taps run
141
215
  * concurrently), so joiners await this before awaiting the pipeline. */
142
216
  let signalPipelineStarted;
@@ -150,6 +224,10 @@ const unpluginFactory = (rawOptions, meta) => {
150
224
  const unbundledComputedImports = meta.framework === "rollup" || meta.framework === "rolldown" || meta.framework === "esbuild" || meta.framework === "vite";
151
225
  async function startPipeline() {
152
226
  signalPipelineStarted();
227
+ if (activeSession !== null && (meta.framework === "webpack" || meta.framework === "rspack")) {
228
+ disposeSessionDir(state);
229
+ releaseClaim();
230
+ }
153
231
  try {
154
232
  await runClaimedPipeline();
155
233
  } catch (error) {
@@ -169,34 +247,100 @@ const unpluginFactory = (rawOptions, meta) => {
169
247
  const session = new _animus_ui_extract_session.ExtractionSession({
170
248
  ...options,
171
249
  mode
172
- }, { unbundledComputedImports });
250
+ }, {
251
+ unbundledComputedImports,
252
+ build: buildId
253
+ });
173
254
  activeSession = session;
174
255
  session.development = watching();
175
256
  session.driverLabel = "animus-unplugin";
176
257
  session.rootDir = root;
177
258
  session.conditions = hostConditions;
259
+ session.extensionPolicy = hostExtensions;
260
+ session.assetClassification = hostAssets;
261
+ session.loadExpander = hostLoadExpander;
262
+ session.hostResolve = hostResolver;
178
263
  session.systemPropsModuleId = _animus_ui_extract_session.TURBOPACK_SYSTEM_PROPS_ID;
179
- const aliasPairs = (0, _animus_ui_extract_pipeline.readTsconfigAliasPairs)(root);
180
- const builtAliases = (0, _animus_ui_extract_pipeline.buildPathAliasesJson)(aliasPairs, root);
181
- if (builtAliases) session.pathAliasesJson = builtAliases.json;
182
- await session.runFullPipeline();
183
- state.sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)();
184
- const analyzed = (0, _animus_ui_extract_session.getAnalyzedHashes)();
264
+ session.structuralExclude = (0, _animus_ui_extract_pipeline.outputExclusions)(root, hostOutputTargets());
265
+ session.aliasesFromTsconfig = true;
266
+ state.key = session.key;
267
+ try {
268
+ await session.runFullPipeline();
269
+ } catch (error) {
270
+ state.watchPaths = [
271
+ (0, node_path.resolve)(root, options.system),
272
+ ...session.systemDependencyPaths,
273
+ ...session.systemMissingPaths,
274
+ (0, node_path.join)(root, "tsconfig.json")
275
+ ];
276
+ throw error;
277
+ }
278
+ state.sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)(session.key);
279
+ const analyzed = (0, _animus_ui_extract_session.getAnalyzedHashes)(session.key);
185
280
  if (!analyzed || analyzed.size === 0) throw new Error(`[animus] discovery found zero source files under ${root} — check the plugin's \`root\` and \`exclude\` options`);
186
- state.cssText = (0, _animus_ui_extract_session.getSharedCss)();
187
- state.systemPropsJs = (0, _animus_ui_extract_session.getSharedSystemProps)();
281
+ state.cssText = (0, _animus_ui_extract_session.getSharedCss)(session.key);
282
+ state.served = manifestServedFiles((0, _animus_ui_extract_session.getManifestJson)(session.key));
283
+ state.systemPropsJs = (0, _animus_ui_extract_session.getSharedSystemProps)(session.key);
188
284
  state.kitRedirects = new Map(session.externalSourceEntries);
189
285
  state.kitSideEffects = new Map(session.externalSourceSideEffects);
190
286
  state.redirectTargets = new Set(state.kitRedirects.values());
191
287
  state.externalPackageDirs = [...session.externalPackageDirs];
192
- const watchPaths = /* @__PURE__ */ new Set();
193
- for (const key of (0, _animus_ui_extract_session.getAnalyzedHashes)()?.keys() ?? []) watchPaths.add((0, node_path.resolve)(root, key));
288
+ state.analyzedFiles = [...analyzed.keys()].map((key) => (0, node_path.resolve)(root, key));
289
+ const watchPaths = new Set(state.analyzedFiles);
194
290
  for (const dep of session.systemDependencyPaths) watchPaths.add(dep);
291
+ for (const path of session.systemMissingPaths) watchPaths.add(path);
195
292
  for (const dep of session.assetDependencyPaths) watchPaths.add(dep);
196
293
  watchPaths.add((0, node_path.join)(root, "tsconfig.json"));
197
294
  state.watchPaths = [...watchPaths];
198
- state.transformFile = (0, _animus_ui_extract_session.engineApi)().transformFile;
295
+ state.transformFile = (0, _animus_ui_extract_session.engineApi)(session.key).transformFile;
296
+ });
297
+ }
298
+ /** Each Rollup-family environment build that started and has not ended,
299
+ * by name, with the promise its end settles. A Vite buildApp can build
300
+ * environments in parallel on one plugin instance; they share one
301
+ * pipeline and one Session, retired by the last to end. A build of an
302
+ * environment whose build is open (a reused builder, or a second
303
+ * environment under one name) waits for that build's end, so each build
304
+ * ends only its own generation, once, in buildEnd. webpack-like hosts run
305
+ * one compile at a time and are not tracked. */
306
+ const openBuilds = /* @__PURE__ */ new Map();
307
+ const singleEnvironment = { name: "build" };
308
+ const tracksEnvironments = meta.framework === "rollup" || meta.framework === "rolldown" || meta.framework === "vite";
309
+ function environmentOf(context) {
310
+ return hasEnvironment(context) ? context.environment : singleEnvironment;
311
+ }
312
+ /** Opens this hook's environment build once no build under its name is
313
+ * open; whether another environment's build is open, whose pipeline this
314
+ * build shares. */
315
+ async function openEnvironmentBuild(context) {
316
+ if (!tracksEnvironments) return false;
317
+ const environment = environmentOf(context);
318
+ for (;;) {
319
+ const open = openBuilds.get(environment.name);
320
+ if (open === void 0) break;
321
+ await open.ended;
322
+ }
323
+ let end = () => {};
324
+ const ended = new Promise((settle) => {
325
+ end = settle;
199
326
  });
327
+ openBuilds.set(environment.name, {
328
+ environment,
329
+ ended,
330
+ end
331
+ });
332
+ return openBuilds.size > 1;
333
+ }
334
+ /** Ends this hook's environment build; whether none is left open. Only
335
+ * the build that opened an entry ends it. */
336
+ function closeEnvironmentBuild(context) {
337
+ if (!tracksEnvironments) return true;
338
+ const environment = environmentOf(context);
339
+ const open = openBuilds.get(environment.name);
340
+ if (open === void 0 || open.environment !== environment) return false;
341
+ openBuilds.delete(environment.name);
342
+ open.end();
343
+ return openBuilds.size === 0;
200
344
  }
201
345
  async function joinPipeline() {
202
346
  await pipelineStarted;
@@ -236,6 +380,17 @@ const unpluginFactory = (rawOptions, meta) => {
236
380
  source: bytes
237
381
  });
238
382
  }
383
+ /** Rollup and Rolldown name their output only at render, after
384
+ * analysis: this process's later builds exclude it, and an analysis that
385
+ * already read a file there is reported, failing a build under `strict`. */
386
+ function checkOwnOutput(context, output) {
387
+ const targets = (0, _animus_ui_extract_pipeline.rollupOutputTargets)(output);
388
+ learnedOutputTargets = (0, _animus_ui_extract_pipeline.mergeOutputTargets)([learnedOutputTargets, targets]);
389
+ const message = (0, _animus_ui_extract_pipeline.analysedOutputs)(root, targets, state.analyzedFiles);
390
+ if (message === null) return;
391
+ if (options.strict && !watching()) context.error(message);
392
+ context.warn(message);
393
+ }
239
394
  /** Analysis is a filesystem walk, so watch mode misses edits to
240
395
  * analyzed-but-unimported files. esbuild has no watch-file seam. */
241
396
  function registerWatchTargets(context) {
@@ -248,8 +403,13 @@ const unpluginFactory = (rawOptions, meta) => {
248
403
  name: PLUGIN_NAME,
249
404
  enforce: "pre",
250
405
  async buildStart() {
251
- await startPipeline();
252
- registerWatchTargets(this);
406
+ if (rollupLike) hostResolver = pluginResolver(this);
407
+ try {
408
+ if (await openEnvironmentBuild(this)) await joinPipeline();
409
+ else await startPipeline();
410
+ } finally {
411
+ registerWatchTargets(this);
412
+ }
253
413
  },
254
414
  async resolveId(id, importer) {
255
415
  const virtual = resolveAnimusId(id);
@@ -309,12 +469,14 @@ const unpluginFactory = (rawOptions, meta) => {
309
469
  await joinPipeline();
310
470
  const filePath = moduleFilePath(id);
311
471
  let output = code;
312
- if ((0, _animus_ui_extract_pipeline.isEngineTransformExtension)(filePath)) {
313
- const transformFile = state.transformFile ?? (0, _animus_ui_extract_session.engineApi)().transformFile;
472
+ const key = state.key;
473
+ if (key !== null && (0, _animus_ui_extract_pipeline.isEngineTransformExtension)(filePath)) {
474
+ const transformFile = state.transformFile ?? (0, _animus_ui_extract_session.engineApi)(key).transformFile;
314
475
  const transformed = transformWithEngine(output, filePath, {
315
476
  rootDir: root,
316
- manifestJson: (0, _animus_ui_extract_session.getManifestJson)() ?? "",
317
- transformFile
477
+ manifestJson: (0, _animus_ui_extract_session.getManifestJson)(key) ?? "",
478
+ transformFile,
479
+ served: state.served
318
480
  });
319
481
  if (transformed !== null) output = transformed;
320
482
  }
@@ -331,18 +493,39 @@ const unpluginFactory = (rawOptions, meta) => {
331
493
  try {
332
494
  emitCssAsset(this);
333
495
  } finally {
334
- disposeSessionDir(state);
335
- releaseClaim();
496
+ if (closeEnvironmentBuild(this)) {
497
+ disposeSessionDir(state);
498
+ releaseClaim();
499
+ }
336
500
  }
337
501
  },
338
502
  vite: { configResolved(config) {
503
+ hostExtensions = (0, _animus_ui_extract_pipeline.viteExtensionPolicy)(config.resolve.extensions);
504
+ hostAssets = (0, _animus_ui_extract_pipeline.viteAssetClassification)(config.assetsInclude);
505
+ hostLoadExpander = (0, _animus_ui_extract_pipeline.viteHostExpander)(config);
506
+ hostOutputTargets = () => (0, _animus_ui_extract_pipeline.viteOutputTargets)(config);
339
507
  const serving = config.command === "serve" || Boolean(config.build.watch);
340
508
  watching = () => serving;
341
509
  } },
342
- rollup: { async buildStart() {
343
- modeOracle = this.meta?.watchMode ? "development" : "production";
344
- await startPipeline();
345
- registerWatchTargets(this);
510
+ rollup: {
511
+ async buildStart(inputOptions) {
512
+ hostExtensions = (0, _animus_ui_extract_pipeline.rollupExtensionPolicy)((inputOptions?.plugins ?? []).some((plugin) => plugin?.name === "node-resolve"));
513
+ hostAssets = (0, _animus_ui_extract_pipeline.builtInAssetClassification)("hostOptionUnreadable", "rollup");
514
+ modeOracle = this.meta?.watchMode ? "development" : "production";
515
+ hostResolver = pluginResolver(this);
516
+ try {
517
+ if (await openEnvironmentBuild(this)) await joinPipeline();
518
+ else await startPipeline();
519
+ } finally {
520
+ registerWatchTargets(this);
521
+ }
522
+ },
523
+ renderStart(outputOptions) {
524
+ checkOwnOutput(this, outputOptions);
525
+ }
526
+ },
527
+ rolldown: { renderStart(outputOptions) {
528
+ checkOwnOutput(this, outputOptions);
346
529
  } },
347
530
  webpack(compiler) {
348
531
  wireWebpackLike(compiler);
@@ -396,7 +579,10 @@ const unpluginFactory = (rawOptions, meta) => {
396
579
  },
397
580
  config(buildOptions) {
398
581
  esbuildOptions = buildOptions;
582
+ hostOutputTargets = () => (0, _animus_ui_extract_pipeline.esbuildOutputTargets)(buildOptions);
399
583
  hostConditions = buildOptions.conditions ?? [];
584
+ hostExtensions = (0, _animus_ui_extract_pipeline.esbuildExtensionPolicy)(buildOptions.resolveExtensions);
585
+ hostAssets = (0, _animus_ui_extract_pipeline.esbuildAssetClassification)(buildOptions.loader);
400
586
  watching = () => watch;
401
587
  buildOptions.define = {
402
588
  ...buildOptions.define,
@@ -448,7 +634,30 @@ const unpluginFactory = (rawOptions, meta) => {
448
634
  function wireWebpackLike(compiler) {
449
635
  modeOracle = compiler.options.mode === "development" ? "development" : "production";
450
636
  hostConditions = (compiler.options.resolve?.conditionNames ?? []).filter((name) => name !== "...");
637
+ hostExtensions = (0, _animus_ui_extract_pipeline.webpackExtensionPolicy)(meta.framework, compiler.options.resolve ?? {}, compiler.webpack?.version ?? compiler.rspack?.rspackVersion);
638
+ hostAssets = (0, _animus_ui_extract_pipeline.builtInAssetClassification)("hostOptionUnreadable", `${meta.framework === "rspack" ? "rspack" : "webpack"} rules not modelled`);
451
639
  watching = () => compiler.watchMode === true;
640
+ hostOutputTargets = () => (0, _animus_ui_extract_pipeline.webpackOutputTargets)(compiler.options.output?.path);
641
+ const resolverFactory = compiler.resolverFactory;
642
+ hostLoadExpander = meta.framework === "webpack" && resolverFactory !== void 0 ? (0, _animus_ui_extract_pipeline.webpackHostExpander)({
643
+ resolveReading: (category) => {
644
+ const read = resolverFactory.get("normal", { dependencyType: category }).options;
645
+ const context = resolverFactory.get("context", { dependencyType: category }).options;
646
+ return {
647
+ extensions: [...read.extensions],
648
+ mainFiles: [...read.mainFiles],
649
+ modules: read.modules,
650
+ fullySpecified: read.fullySpecified === true,
651
+ enforceExtension: read.enforceExtension === true,
652
+ aliasNames: [read, context].flatMap((resolver) => [...resolver.alias ?? [], ...resolver.fallback ?? []].map((entry) => entry.name))
653
+ };
654
+ },
655
+ options: () => ({
656
+ rules: compiler.options.module?.rules ?? [],
657
+ defaultRulesAreWebpacks: defaultRulesAreWebpacks(compiler),
658
+ wrappedDefaults: wrappedContextDefaults(compiler.options.module?.parser)
659
+ })
660
+ }) : void 0;
452
661
  const DefinePlugin = compiler.webpack?.DefinePlugin ?? compiler.rspack?.DefinePlugin;
453
662
  if (!DefinePlugin) throw new Error("[animus] compiler exposes no DefinePlugin — cannot supply the __ANIMUS_DEV__ dev-signal define");
454
663
  new DefinePlugin({ __ANIMUS_DEV__: JSON.stringify(effectiveMode() === "development") }).apply(compiler);
@@ -460,6 +669,10 @@ const unpluginFactory = (rawOptions, meta) => {
460
669
  disposeSessionDir(state);
461
670
  releaseClaim();
462
671
  });
672
+ compiler.hooks.watchClose?.tap(PLUGIN_NAME, () => {
673
+ disposeSessionDir(state);
674
+ releaseClaim();
675
+ });
463
676
  }
464
677
  };
465
678
  //#endregion
package/dist/core.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AnimusUnpluginOptions } from './options';
2
- import type { AnimusMode } from '@animus-ui/extract/pipeline';
2
+ import type { AnimusMode, ServedFiles } from '@animus-ui/extract/pipeline';
3
+ import type { SessionKey } from '@animus-ui/extract/session';
3
4
  import type { UnpluginFactory } from 'unplugin';
4
5
  /** Extension-free: esbuild picks loaders by extension. No `\0` prefix:
5
6
  * webpack's virtual-module bridge requires plain ids. */
@@ -23,6 +24,14 @@ export interface HostState {
23
24
  hasComponents: boolean;
24
25
  }) | null;
25
26
  sessionDir: string | null;
27
+ /** The build of the root this plugin instance analyses, once a pipeline
28
+ * run's session joined it. */
29
+ key: SessionKey | null;
30
+ /** The files the run's analysis extracted components from, and the
31
+ * extensions it dropped over their parent. */
32
+ served: ServedFiles;
33
+ /** Every file the run's analysis read, absolute. */
34
+ analyzedFiles: string[];
26
35
  }
27
36
  export declare function createHostState(): HostState;
28
37
  export declare function shouldClaimTransform(filePath: string, state: Pick<HostState, 'externalPackageDirs' | 'redirectTargets'>): boolean;
@@ -37,6 +46,9 @@ export declare function transformWithEngine(code: string, id: string, ctx: {
37
46
  code: string;
38
47
  hasComponents: boolean;
39
48
  };
49
+ /** The root-relative files the analysis extracted components from,
50
+ * which must transform, and those it dropped over their parent. */
51
+ served?: ServedFiles;
40
52
  }): string | null;
41
53
  export declare const unpluginFactory: UnpluginFactory<AnimusUnpluginOptions | undefined>;
42
54
  //# sourceMappingURL=core.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAIV,eAAe,EAChB,MAAM,UAAU,CAAC;AAElB;0DAC0D;AAC1D,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AAQtD,eAAO,MAAM,cAAc,eAAe,CAAC;AAQ3C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;wCACoC;IACpC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EACT,CAAC,CACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,KACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,CAAC,GAC9C,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,eAAe,IAAI,SAAS,CAc3C;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,qBAAqB,GAAG,iBAAiB,CAAC,GAChE,OAAO,CAMT;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,EAChB,SAAS,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IACmB,GAC9C,IAAI,CAKN;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACxB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASzD;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,IAAI,CAGf;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,GAAG,EAAE;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,CACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,KACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,CAAC;CAC/C,GACA,MAAM,GAAG,IAAI,CAIf;AAsHD,eAAO,MAAM,eAAe,EAAE,eAAe,CAC3C,qBAAqB,GAAG,SAAS,CA2gBlC,CAAC"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AA8CA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,KAAK,EACV,UAAU,EAQV,WAAW,EACZ,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAIV,eAAe,EAChB,MAAM,UAAU,CAAC;AAElB;0DAC0D;AAC1D,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AAQtD,eAAO,MAAM,cAAc,eAAe,CAAC;AAQ3C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;wCACoC;IACpC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EACT,CAAC,CACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,KACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,CAAC,GAC9C,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;mCAC+B;IAC/B,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACvB;mDAC+C;IAC/C,MAAM,EAAE,WAAW,CAAC;IACpB,oDAAoD;IACpD,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAgB,eAAe,IAAI,SAAS,CAiB3C;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,qBAAqB,GAAG,iBAAiB,CAAC,GAChE,OAAO,CAMT;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,EAChB,SAAS,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IACmB,GAC9C,IAAI,CAKN;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAChB,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACxB,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASzD;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,IAAI,CAGf;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,GAAG,EAAE;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,CACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,KACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAE,CAAC;IAC9C;wEACoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GACA,MAAM,GAAG,IAAI,CAgBf;AA8SD,eAAO,MAAM,eAAe,EAAE,eAAe,CAC3C,qBAAqB,GAAG,SAAS,CAqvBlC,CAAC"}
package/dist/esbuild.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_core = require("./core-BqEruPuj.cjs");
5
+ const require_core = require("./core-zGWPVkIb.cjs");
6
6
  //#region src/esbuild.ts
7
7
  const animusEsbuild = (0, require("unplugin").createEsbuildPlugin)(require_core.unpluginFactory);
8
8
  //#endregion
package/dist/esbuild.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as unpluginFactory } from "./core-CP2SnWlP.mjs";
1
+ import { t as unpluginFactory } from "./core-CrtP5_Sj.mjs";
2
2
  import { createEsbuildPlugin } from "unplugin";
3
3
  //#region src/esbuild.ts
4
4
  const animusEsbuild = createEsbuildPlugin(unpluginFactory);
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_core = require("./core-BqEruPuj.cjs");
5
+ const require_core = require("./core-zGWPVkIb.cjs");
6
6
  //#region src/index.ts
7
7
  const animusUnplugin = (0, require("unplugin").createUnplugin)(require_core.unpluginFactory);
8
8
  //#endregion
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as unpluginFactory } from "./core-CP2SnWlP.mjs";
1
+ import { t as unpluginFactory } from "./core-CrtP5_Sj.mjs";
2
2
  import { createUnplugin } from "unplugin";
3
3
  //#region src/index.ts
4
4
  const animusUnplugin = createUnplugin(unpluginFactory);
package/dist/rollup.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_core = require("./core-BqEruPuj.cjs");
5
+ const require_core = require("./core-zGWPVkIb.cjs");
6
6
  //#region src/rollup.ts
7
7
  const animusRollup = (0, require("unplugin").createRollupPlugin)(require_core.unpluginFactory);
8
8
  //#endregion
package/dist/rollup.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as unpluginFactory } from "./core-CP2SnWlP.mjs";
1
+ import { t as unpluginFactory } from "./core-CrtP5_Sj.mjs";
2
2
  import { createRollupPlugin } from "unplugin";
3
3
  //#region src/rollup.ts
4
4
  const animusRollup = createRollupPlugin(unpluginFactory);
package/dist/rspack.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_core = require("./core-BqEruPuj.cjs");
5
+ const require_core = require("./core-zGWPVkIb.cjs");
6
6
  //#region src/rspack.ts
7
7
  const animusRspack = (0, require("unplugin").createRspackPlugin)(require_core.unpluginFactory);
8
8
  //#endregion
package/dist/rspack.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as unpluginFactory } from "./core-CP2SnWlP.mjs";
1
+ import { t as unpluginFactory } from "./core-CrtP5_Sj.mjs";
2
2
  import { createRspackPlugin } from "unplugin";
3
3
  //#region src/rspack.ts
4
4
  const animusRspack = createRspackPlugin(unpluginFactory);
package/dist/webpack.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_core = require("./core-BqEruPuj.cjs");
5
+ const require_core = require("./core-zGWPVkIb.cjs");
6
6
  //#region src/webpack.ts
7
7
  const animusWebpack = (0, require("unplugin").createWebpackPlugin)(require_core.unpluginFactory);
8
8
  //#endregion
package/dist/webpack.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as unpluginFactory } from "./core-CP2SnWlP.mjs";
1
+ import { t as unpluginFactory } from "./core-CrtP5_Sj.mjs";
2
2
  import { createWebpackPlugin } from "unplugin";
3
3
  //#region src/webpack.ts
4
4
  const animusWebpack = createWebpackPlugin(unpluginFactory);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animus-ui/unplugin",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Animus transform host for non-plugin bundlers — unplugin-based rollup/esbuild/rspack/webpack entry points over the one extraction session",
5
5
  "keywords": [
6
6
  "animus",
@@ -57,7 +57,7 @@
57
57
  "compile": "tsc --noEmit"
58
58
  },
59
59
  "dependencies": {
60
- "@animus-ui/extract": "0.1.24",
60
+ "@animus-ui/extract": "0.1.26",
61
61
  "unplugin": "^3.3.0"
62
62
  },
63
63
  "engines": {