@animus-ui/next-plugin 0.1.25 → 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/dist/index.mjs CHANGED
@@ -1,9 +1,31 @@
1
- import { a as resolveTurbopackMode, i as resolveTurbopackLoaderPath, n as buildTurbopackConfig, o as turbopackSideEffectsLimits, r as isTurbopackDevelopment, s as resolveAnimusLoaderPath, t as ANIMUS_TURBOPACK_RULE_GLOB } from "./turbopack-config-BdfqxwT0.mjs";
2
- import { assembleStylesheet, assertKnownOptionKeys, assertNoRetiredEngineSelection, buildPathAliasesJson, isEngineTransformExtension, isPathWithinRoot, keepsKitSourceEffects, readTsconfigAliasPairs, resolveMode, sourceKitDependencies } from "@animus-ui/extract/pipeline";
3
- import { ANIMUS_CSS_MODULE_ID, ANIMUS_CSS_MODULE_ID as ANIMUS_CSS_MODULE_ID$1, ExtractionSession, TURBOPACK_SYSTEM_PROPS_ID, getAnalysisStartedPromise, getReplacementEpoch, getSharedCss, getSharedExternalDirs, getSharedExternalEntries, getSharedExternalSideEffects, replacementEpochPath, resetAnalysisStartedPromise, runSessionPipeline, sessionArtifactDir, setAnalysisStartedPromise, setSharedEngine, startTurbopackWatcher, stylesPath, systemPropsPath } from "@animus-ui/extract/session";
4
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
- import { dirname, join } from "path";
1
+ import { a as resolveTurbopackMode, i as resolveTurbopackLoaderPath, n as buildTurbopackConfig, o as turbopackSideEffectsLimits, r as isTurbopackDevelopment, s as resolveAnimusLoaderPath, t as ANIMUS_TURBOPACK_RULE_GLOB } from "./turbopack-config-3jtLDAIM.mjs";
2
+ import { AnimusConfigError, assembleStylesheet, assertKnownOptionKeys, assertSupportedEngine, buildPathAliasesJson, builtInAssetClassification, isEngineTransformExtension, isPathWithinRoot, keepsKitSourceEffects, nextImageAssetClassification, outputExclusion, resolveMode, sourceKitDependencies, turbopackExtensionPolicy, webpackExtensionPolicy } from "@animus-ui/extract/pipeline";
3
+ import { ANIMUS_CSS_MODULE_ID, ANIMUS_CSS_MODULE_ID as ANIMUS_CSS_MODULE_ID$1, ExtractionSession, TURBOPACK_SYSTEM_PROPS_ID, createBuildId, getAnalysisStartedPromise, getReplacementEpoch, getSharedCss, getSharedExternalDirs, getSharedExternalEntries, getSharedExternalSideEffects, replacementEpochPath, requireEngine, resetAnalysisStartedPromise, runSessionPipeline, sessionArtifactDir, setAnalysisStartedPromise, startTurbopackWatcher, stylesPath, systemPropsPath } from "@animus-ui/extract/session";
4
+ import { existsSync, mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "fs";
5
+ import { dirname, join, resolve } from "path";
6
6
  import { fileURLToPath } from "url";
7
+ //#region src/same-directory.ts
8
+ /** Whether two paths name one directory: by real path, which resolves a
9
+ * trailing slash, a symlink and the filesystem's own spelling, else by
10
+ * device and inode. */
11
+ function sameDirectory(a, b) {
12
+ const real = (path) => {
13
+ try {
14
+ return realpathSync.native(path);
15
+ } catch {
16
+ return resolve(path);
17
+ }
18
+ };
19
+ if (real(a) === real(b)) return true;
20
+ try {
21
+ const left = statSync(a);
22
+ const right = statSync(b);
23
+ return left.dev === right.dev && left.ino === right.ino;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ //#endregion
7
29
  //#region src/plugin.ts
8
30
  /** The value arrives from a consumer's `next.config`, so the union above is a
9
31
  * model of what webpack accepts, not a proof about the value in hand. */
@@ -15,6 +37,9 @@ var AnimusWebpackPlugin = class {
15
37
  options;
16
38
  session;
17
39
  initialized = false;
40
+ /** Left its build: the next turn joins again and catches up, retried
41
+ * until it succeeds, never reusing the startup it already consumed. */
42
+ rejoining = false;
18
43
  aliasesExtracted = false;
19
44
  animusLoaderPaths;
20
45
  lastBuiltEpoch = null;
@@ -24,11 +49,10 @@ var AnimusWebpackPlugin = class {
24
49
  watchFailure = null;
25
50
  /** `development` is `next dev`: an error-level diagnostic reports and the
26
51
  * compilation keeps publishing, whatever the emission mode. */
27
- constructor(options, { development = false } = {}) {
52
+ constructor(options, { development = false, build } = {}) {
28
53
  this.options = options;
29
- assertNoRetiredEngineSelection(options.engine);
30
- setSharedEngine(options.engine ?? "v2");
31
- this.session = new ExtractionSession(options);
54
+ assertSupportedEngine(options.engine);
55
+ this.session = new ExtractionSession(options, { build });
32
56
  this.session.development = development;
33
57
  this.session.systemPropsModuleId = TURBOPACK_SYSTEM_PROPS_ID;
34
58
  this.animusLoaderPaths = /* @__PURE__ */ new Set([resolveAnimusLoaderPath()]);
@@ -54,10 +78,18 @@ var AnimusWebpackPlugin = class {
54
78
  else if (isWatchIgnoreMatcher(ignored)) watchOptions.ignored = (path) => path === epochPath || Boolean(ignored(path));
55
79
  }
56
80
  /** Webpack's export conditions select the system's kit files as they
57
- * select the bundle's; `...`, webpack's own defaults, names none. */
81
+ * select the bundle's; `...`, webpack's own defaults, names none. Its
82
+ * extension order and aliases are captured alongside. */
58
83
  extractConditions(compiler) {
59
- const names = compiler.options?.resolve?.conditionNames ?? [];
84
+ const resolve = compiler.options?.resolve;
85
+ const names = resolve?.conditionNames ?? [];
60
86
  this.session.conditions = names.filter((name) => name !== "...");
87
+ const resolverFactory = compiler.resolverFactory;
88
+ if (resolverFactory !== void 0) this.session.hostResolve = (specifier, importer) => new Promise((done) => {
89
+ resolverFactory.get("normal", { dependencyType: "esm" }).resolve({}, importer === void 0 ? compiler.context : dirname(importer), specifier, {}, (error, result) => done(error !== null || !result ? null : result));
90
+ });
91
+ this.session.assetClassification = builtInAssetClassification("hostOptionUnreadable", "next-webpack rules not modelled");
92
+ this.session.extensionPolicy = webpackExtensionPolicy("next-webpack", resolve ?? {}, compiler.webpack?.version);
61
93
  }
62
94
  extractAliases(compiler) {
63
95
  if (this.aliasesExtracted) return;
@@ -81,6 +113,13 @@ var AnimusWebpackPlugin = class {
81
113
  if (built) this.session.pathAliasesJson = built.json;
82
114
  }
83
115
  apply(compiler) {
116
+ const leave = () => {
117
+ this.session.close();
118
+ this.initialized = false;
119
+ this.rejoining = true;
120
+ };
121
+ compiler.hooks.shutdown?.tap(PLUGIN_NAME, leave);
122
+ compiler.hooks.watchClose?.tap(PLUGIN_NAME, leave);
84
123
  if (compiler.options?.name === "edge-server") return;
85
124
  const NormalModule = compiler.webpack?.NormalModule;
86
125
  if (NormalModule?.getCompilationHooks === void 0) throw new Error(UNSUPPORTED_WEBPACK_MESSAGE);
@@ -103,6 +142,7 @@ var AnimusWebpackPlugin = class {
103
142
  const registerSystemDependencies = () => {
104
143
  for (const dep of this.session.systemDependencyPaths) if (existsSync(dep)) compilation.fileDependencies.add(dep);
105
144
  else compilation.missingDependencies.add(dep);
145
+ for (const path of this.session.systemMissingPaths) compilation.missingDependencies.add(path);
106
146
  for (const dep of this.session.assetDependencyPaths) if (existsSync(dep)) compilation.fileDependencies.add(dep);
107
147
  else compilation.missingDependencies.add(dep);
108
148
  for (const root of this.session.externalWatchRoots) compilation.contextDependencies.add(root);
@@ -115,7 +155,7 @@ var AnimusWebpackPlugin = class {
115
155
  stage
116
156
  }, () => {
117
157
  registerSystemDependencies();
118
- const css = getSharedCss();
158
+ const css = this.session.rootDir ? getSharedCss(this.session.key) : "";
119
159
  if (!css || !RawSource) return;
120
160
  const rootDir = this.session.rootDir || compiler.context;
121
161
  const cssPath = stylesPath(sessionArtifactDir(rootDir, this.session.sessionId));
@@ -130,13 +170,18 @@ var AnimusWebpackPlugin = class {
130
170
  this.adoptCompilerContext(_compiler);
131
171
  this.extractAliases(_compiler);
132
172
  this.extractConditions(_compiler);
133
- const existing = getAnalysisStartedPromise();
173
+ if (await this.rejoin()) {
174
+ this.session.agreeWithSystemAuthority();
175
+ return;
176
+ }
177
+ const existing = getAnalysisStartedPromise(this.session.key);
134
178
  if (existing) {
135
179
  await existing;
180
+ this.session.agreeWithSystemAuthority();
136
181
  return;
137
182
  }
138
183
  const promise = this.session.runFullPipeline();
139
- setAnalysisStartedPromise(promise);
184
+ setAnalysisStartedPromise(this.session.key, promise);
140
185
  await promise;
141
186
  });
142
187
  compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async (_compiler) => {
@@ -153,21 +198,38 @@ var AnimusWebpackPlugin = class {
153
198
  }
154
199
  /** Rejects with the analysis error; the published generation stays the
155
200
  * last successful one. */
201
+ /** Joins the build this compiler left and catches up with it: true when
202
+ * the build was live, false when it starts as at first. */
203
+ async rejoin() {
204
+ if (!this.rejoining) return false;
205
+ this.session.reattach();
206
+ const live = await this.session.catchUp();
207
+ this.rejoining = false;
208
+ return live;
209
+ }
156
210
  async analyzeWatchTurn(compiler) {
211
+ if (!this.initialized && await this.rejoin()) {
212
+ this.session.agreeWithSystemAuthority();
213
+ this.initialized = true;
214
+ this.lastBuiltEpoch = getReplacementEpoch(this.session.key);
215
+ this.epochMovedForNextCompilation = true;
216
+ return;
217
+ }
157
218
  if (!this.initialized) {
158
- let startup = getAnalysisStartedPromise();
219
+ let startup = getAnalysisStartedPromise(this.session.key);
159
220
  if (!startup) {
160
221
  startup = this.session.runFullPipeline();
161
- setAnalysisStartedPromise(startup);
222
+ setAnalysisStartedPromise(this.session.key, startup);
162
223
  }
163
224
  try {
164
225
  await startup;
165
226
  } catch (err) {
166
- if (getAnalysisStartedPromise() === startup) resetAnalysisStartedPromise();
227
+ if (getAnalysisStartedPromise(this.session.key) === startup) resetAnalysisStartedPromise(this.session.key);
167
228
  throw err;
168
229
  }
230
+ this.session.agreeWithSystemAuthority();
169
231
  this.initialized = true;
170
- this.lastBuiltEpoch = getReplacementEpoch();
232
+ this.lastBuiltEpoch = getReplacementEpoch(this.session.key);
171
233
  this.epochMovedForNextCompilation = this.watchFailure !== null;
172
234
  return;
173
235
  }
@@ -175,7 +237,8 @@ var AnimusWebpackPlugin = class {
175
237
  modifiedFiles: compiler.modifiedFiles,
176
238
  removedFiles: compiler.removedFiles
177
239
  });
178
- const epoch = getReplacementEpoch();
240
+ this.session.agreeWithSystemAuthority();
241
+ const epoch = getReplacementEpoch(this.session.key);
179
242
  this.epochMovedForNextCompilation = this.lastBuiltEpoch !== null && epoch !== null && epoch !== this.lastBuiltEpoch;
180
243
  if (epoch !== null) this.lastBuiltEpoch = epoch;
181
244
  }
@@ -188,21 +251,23 @@ var AnimusWebpackPlugin = class {
188
251
  setRootDir(rootDir) {
189
252
  this.session.rootDir = rootDir;
190
253
  }
191
- warnedRootDivergence = false;
254
+ /** Keeps Next's output directory out of discovery, whatever the user's
255
+ * exclude list says. Call after `setRootDir`. */
256
+ excludeOutputDir(outDir) {
257
+ const pattern = outputExclusion(this.session.rootDir, outDir);
258
+ if (pattern !== null) this.session.structuralExclude = [pattern];
259
+ }
192
260
  /** Adopts `compiler.context` only when no config-time root exists: every
193
261
  * frozen derivation came from that root, so an overwrite re-keys it.
194
262
  * Stub path, css alias, virtual system-props target and watch-ignore are
195
- * all frozen from it, so a disagreement is warned, never reconciled. */
263
+ * all frozen from it, so a disagreement fails, never reconciled. */
196
264
  adoptCompilerContext(compiler) {
197
265
  const context = compiler.context;
198
266
  if (!this.session.rootDir) {
199
267
  this.session.rootDir = context;
200
268
  return;
201
269
  }
202
- if (context && this.session.rootDir !== context && !this.warnedRootDivergence) {
203
- this.warnedRootDivergence = true;
204
- console.warn(`[animus-extract] compiler.context (${context}) differs from the configured project root (${this.session.rootDir}); keeping the configured root — run next against the app directory (or align your custom webpack context) so both agree`);
205
- }
270
+ if (context && !sameDirectory(context, this.session.rootDir)) throw new AnimusConfigError(`[animus-extract] compiler.context (${context}) differs from the configured project root (${this.session.rootDir}). Run next against the app directory, or align your custom webpack context, so both agree.`);
206
271
  }
207
272
  get sessionDir() {
208
273
  return this.session.sessionDir;
@@ -211,15 +276,20 @@ var AnimusWebpackPlugin = class {
211
276
  return this.options;
212
277
  }
213
278
  getExternalPackageDirs() {
214
- return this.session.externalPackageDirs.length > 0 ? this.session.externalPackageDirs : getSharedExternalDirs();
279
+ return this.session.externalPackageDirs.length > 0 ? this.session.externalPackageDirs : getSharedExternalDirs(this.session.key);
280
+ }
281
+ /** An import webpack resolved while bundling, with the resolve options of
282
+ * the rules its importer matches (spec §2.2 A4). */
283
+ reportHostResolution(importer, request, resolved) {
284
+ this.session.reportHostResolution(importer, request, resolved);
215
285
  }
216
286
  getExternalSourceEntries() {
217
- return this.session.externalSourceEntries.size > 0 ? this.session.externalSourceEntries : getSharedExternalEntries();
287
+ return this.session.externalSourceEntries.size > 0 ? this.session.externalSourceEntries : getSharedExternalEntries(this.session.key);
218
288
  }
219
289
  /** The classification of the entries `getExternalSourceEntries` returns,
220
290
  * from the same source. */
221
291
  getExternalSourceSideEffects() {
222
- return this.session.externalSourceEntries.size > 0 ? this.session.externalSourceSideEffects : getSharedExternalSideEffects();
292
+ return this.session.externalSourceEntries.size > 0 ? this.session.externalSourceSideEffects : getSharedExternalSideEffects(this.session.key);
223
293
  }
224
294
  };
225
295
  //#endregion
@@ -233,6 +303,7 @@ const PHASE_PRODUCTION_BUILD = "phase-production-build";
233
303
  let warnedGitignore = false;
234
304
  let warnedUnstableTurbopack = false;
235
305
  function withAnimus(options) {
306
+ assertSupportedEngine(options.engine);
236
307
  if (!options.system) throw new Error("[animus-extract] Missing required option `system`. Provide the path to your SystemInstance module: withAnimus({ system: \"./src/ds.ts\" })");
237
308
  assertKnownOptionKeys({ ...options }, [
238
309
  "cssImportTarget",
@@ -240,10 +311,7 @@ function withAnimus(options) {
240
311
  "unstable_turbopack",
241
312
  "loaderPath",
242
313
  "phase"
243
- ], [{
244
- key: "root",
245
- reason: "this driver derives its own root — Next's `dir` under webpack, `process.cwd()` under Turbopack (Next passes no dir to a config module)"
246
- }], {
314
+ ], [], {
247
315
  onUnknownKey: "warn",
248
316
  warn: (message) => console.warn(`[animus-extract] ${message}`)
249
317
  });
@@ -251,21 +319,31 @@ function withAnimus(options) {
251
319
  warnedUnstableTurbopack = true;
252
320
  console.warn("[animus-extract] `unstable_turbopack` is deprecated — rename it to `turbopack` (same shape)");
253
321
  }
322
+ const build = createBuildId();
323
+ const appRoot = options.root === void 0 ? process.cwd() : resolve(options.root);
254
324
  return (authoredConfig) => {
255
- const nextConfig = withSourceKits(authoredConfig, process.cwd());
256
- if (resolveTurbopackMode(options)) return turbopackConfig(nextConfig, options);
325
+ const nextConfig = withSourceKits(authoredConfig, appRoot);
326
+ if (resolveTurbopackMode(options)) {
327
+ warnTurbopackCwdRoot(options, appRoot);
328
+ return turbopackConfig(nextConfig, options, build, appRoot);
329
+ }
257
330
  const existingWebpack = nextConfig.webpack;
258
331
  return {
259
332
  ...nextConfig,
260
333
  webpack(config, context) {
261
334
  if (existingWebpack) config = existingWebpack(config, context);
262
- const rootDir = context.dir?.length ? context.dir : process.cwd();
335
+ if (context.dir?.length && !sameDirectory(context.dir, appRoot)) throw new AnimusConfigError(`[animus-extract] Next builds ${context.dir}, but Animus read the app's source kits from ${appRoot}. Run next from the app directory, or pass withAnimus({ root }) naming it.`);
336
+ const rootDir = appRoot;
263
337
  const { mode } = resolveMode(options.mode, () => context.dev === true ? "development" : "production");
264
338
  const plugin = new AnimusWebpackPlugin({
265
339
  ...options,
266
340
  mode
267
- }, { development: context.dev === true });
341
+ }, {
342
+ development: context.dev === true,
343
+ build
344
+ });
268
345
  plugin.setRootDir(rootDir);
346
+ if (nextConfig.distDir) plugin.excludeOutputDir(resolve(rootDir, nextConfig.distDir));
269
347
  const sessionDir = plugin.sessionDir;
270
348
  if (!existsSync(sessionDir)) mkdirSync(sessionDir, { recursive: true });
271
349
  const stubCssPath = stylesPath(sessionDir);
@@ -310,6 +388,9 @@ function withAnimus(options) {
310
388
  }
311
389
  });
312
390
  nmf.hooks.afterResolve.tap("AnimusVirtualResolve", (resolveData) => {
391
+ const issuer = resolveData.contextInfo?.issuer;
392
+ const resolved = resolveData.createData?.resource;
393
+ if (issuer && resolved !== void 0) plugin.reportHostResolution(issuer, resolveData.request, resolved);
313
394
  const sideEffects = redirectSideEffects.get(resolveData);
314
395
  const settings = resolveData.createData?.settings;
315
396
  if (!settings) return;
@@ -335,9 +416,10 @@ function withAnimus(options) {
335
416
  use: [{
336
417
  loader: actualLoaderPath,
337
418
  options: {
338
- strict: options.strict,
339
419
  development: context.dev === true,
340
- cssImportTarget: options.cssImportTarget
420
+ cssImportTarget: options.cssImportTarget,
421
+ build,
422
+ root: rootDir
341
423
  }
342
424
  }]
343
425
  });
@@ -358,17 +440,35 @@ function bindTurbopackWatchDeathReport(outcome, rootDir) {
358
440
  * source, but Next's server keeps an installed package external, and so
359
441
  * loads its runtime entry, unless the package is transpiled; transpiling
360
442
  * also compiles its TypeScript source. */
443
+ let warnedTurbopackCwd = false;
444
+ /** Turbopack's loaders get no app dir either, so a cwd that is not the app
445
+ * cannot be detected later: warn at startup when the cwd holds no Next
446
+ * config and no `root` names the app. */
447
+ function warnTurbopackCwdRoot(options, appRoot) {
448
+ if (options.root !== void 0 || warnedTurbopackCwd) return;
449
+ if ([
450
+ "js",
451
+ "mjs",
452
+ "cjs",
453
+ "ts",
454
+ "mts"
455
+ ].some((ext) => existsSync(join(appRoot, `next.config.${ext}`)))) return;
456
+ warnedTurbopackCwd = true;
457
+ console.warn(`[animus-extract] the current directory (${appRoot}) holds no next.config: under Turbopack, Animus reads the app from the current directory. Run next from the app directory, or pass withAnimus({ root }) naming it.`);
458
+ }
361
459
  function withSourceKits(nextConfig, rootDir) {
362
460
  const declared = nextConfig.transpilePackages ?? [];
363
- const kits = sourceKitDependencies(rootDir).map((kit) => kit.name).filter((name) => !declared.includes(name));
461
+ const kits = sourceKitDependencies(rootDir, requireEngine()).map((kit) => kit.name).filter((name) => !declared.includes(name));
364
462
  if (kits.length === 0) return nextConfig;
365
463
  return {
366
464
  ...nextConfig,
367
465
  transpilePackages: [...declared, ...kits]
368
466
  };
369
467
  }
370
- function turbopackConfig(nextConfig, options) {
371
- const load = (phase) => loadTurbopackFragment(options, phase).then((fragment) => mergeTurbopackFragment(nextConfig, fragment));
468
+ function turbopackConfig(nextConfig, options, build, appRoot) {
469
+ const resolveExtensions = nextConfig.turbopack?.resolveExtensions ?? nextConfig.experimental?.turbo?.resolveExtensions;
470
+ const outputExclude = nextConfig.distDir ? outputExclusion(appRoot, resolve(appRoot, nextConfig.distDir)) : null;
471
+ const load = (phase) => loadTurbopackFragment(options, phase, resolveExtensions, outputExclude, nextConfig.images?.disableStaticImages === true, build, appRoot).then((fragment) => mergeTurbopackFragment(nextConfig, fragment));
372
472
  function then(onResolved, onRejected) {
373
473
  const phase = options.phase ?? (isTurbopackDevelopment() ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_BUILD);
374
474
  return load(phase).then(onResolved, onRejected);
@@ -377,49 +477,67 @@ function turbopackConfig(nextConfig, options) {
377
477
  Object.defineProperty(config, "then", { enumerable: false });
378
478
  return config;
379
479
  }
380
- let liveTurbopackSession = null;
381
- let liveTurbopackWatcher = null;
382
- /** The load the live session came from. Next loads the config again after
383
- * Ready (validateTurboNextConfig), and a user config may await this one
384
- * besides: the same phase and root share one analysis and keep the watcher.
385
- * A rejected load is dropped, so a repaired source loads again. */
386
- let liveTurbopackLoad = null;
387
- function loadTurbopackFragment(options, phase) {
388
- const rootDir = process.cwd();
389
- const key = JSON.stringify([phase, rootDir]);
390
- if (liveTurbopackLoad?.key === key) return liveTurbopackLoad.fragment;
391
- const fragment = analyzeForTurbopack(options, phase, rootDir);
480
+ /** Each wrapper's owner, by the build it created: another wrapper of the
481
+ * same root is another build and never shares or closes this one's. */
482
+ const turbopackOwners = /* @__PURE__ */ new Map();
483
+ function turbopackOwner(build) {
484
+ let owner = turbopackOwners.get(build);
485
+ if (owner === void 0) {
486
+ owner = {
487
+ session: null,
488
+ watcher: null,
489
+ load: null
490
+ };
491
+ turbopackOwners.set(build, owner);
492
+ }
493
+ return owner;
494
+ }
495
+ function loadTurbopackFragment(options, phase, resolveExtensions, outputExclude, disableStaticImages, build, rootDir) {
496
+ const key = JSON.stringify([
497
+ phase,
498
+ rootDir,
499
+ resolveExtensions ?? null,
500
+ disableStaticImages
501
+ ]);
502
+ const owner = turbopackOwner(build);
503
+ if (owner.load?.key === key) return owner.load.fragment;
504
+ const fragment = analyzeForTurbopack(options, phase, rootDir, resolveExtensions, outputExclude, disableStaticImages, build);
392
505
  const load = {
393
506
  key,
394
507
  fragment
395
508
  };
396
- liveTurbopackLoad = load;
509
+ owner.load = load;
397
510
  fragment.catch(() => {
398
- if (liveTurbopackLoad === load) liveTurbopackLoad = null;
511
+ if (owner.load === load) owner.load = null;
399
512
  });
400
513
  return fragment;
401
514
  }
402
- async function analyzeForTurbopack(options, phase, rootDir) {
515
+ async function analyzeForTurbopack(options, phase, rootDir, resolveExtensions, outputExclude, disableStaticImages, build) {
403
516
  const development = phase === PHASE_DEVELOPMENT_SERVER;
404
517
  const { mode } = resolveMode(options.mode, () => development ? "development" : "production");
405
518
  const session = new ExtractionSession({
406
519
  ...options,
407
520
  mode
408
- }, { unbundledComputedImports: true });
409
- liveTurbopackSession?.close();
410
- liveTurbopackWatcher?.close();
411
- liveTurbopackWatcher = null;
412
- liveTurbopackSession = session;
521
+ }, {
522
+ unbundledComputedImports: true,
523
+ build: `${build}-${phase}`
524
+ });
525
+ const owner = turbopackOwner(build);
526
+ owner.session?.close();
527
+ owner.watcher?.close();
528
+ owner.watcher = null;
529
+ owner.session = session;
413
530
  session.development = development;
414
531
  session.rootDir = rootDir;
415
- const aliasPairs = readTsconfigAliasPairs(rootDir);
416
- const builtAliases = buildPathAliasesJson(aliasPairs, rootDir);
417
- if (builtAliases) session.pathAliasesJson = builtAliases.json;
532
+ if (outputExclude !== null) session.structuralExclude = [outputExclude];
533
+ session.extensionPolicy = turbopackExtensionPolicy(resolveExtensions);
534
+ session.assetClassification = nextImageAssetClassification(rootDir, disableStaticImages);
535
+ session.aliasesFromTsconfig = true;
418
536
  await runSessionPipeline(session);
419
- if (development && liveTurbopackSession === session) {
537
+ if (development && owner.session === session) {
420
538
  const outcome = startTurbopackWatcher(session, rootDir);
421
539
  bindTurbopackWatchDeathReport(outcome, rootDir);
422
- if (outcome.kind === "started") liveTurbopackWatcher = outcome.handle;
540
+ if (outcome.kind === "started") owner.watcher = outcome.handle;
423
541
  }
424
542
  for (const line of turbopackSideEffectsLimits(rootDir, session.externalSourceEntries, session.externalSourceSideEffects)) console.warn(line);
425
543
  return buildTurbopackConfig({
@@ -61,12 +61,26 @@ function extensionLineage(files, analyzedHashes) {
61
61
  const lineage = files.map((file) => `${file}\0${analyzedHashes?.get(file) ?? ""}`).join("\0");
62
62
  return `\nvoid "animus-lineage:${(0, _animus_ui_extract_pipeline.contentHash)(lineage)}";\n`;
63
63
  }
64
+ let parsedManifest = null;
65
+ /** What a manifest's transform serves, parsed once per manifest: every
66
+ * module of a compilation reads the same one. */
67
+ function manifestServedFiles(manifestJson) {
68
+ if (parsedManifest?.json !== manifestJson) parsedManifest = {
69
+ json: manifestJson,
70
+ served: (0, _animus_ui_extract_pipeline.servedFiles)(JSON.parse(manifestJson))
71
+ };
72
+ return parsedManifest.served;
73
+ }
64
74
  function transformWithManifest(args) {
65
75
  const { source, filename, manifestJson, engineApi, opts } = args;
66
76
  const isRootEntry = isCssImportTarget(filename, opts.cssImportTarget);
67
77
  try {
68
78
  const { transformFile } = engineApi();
69
79
  const result = transformFile(source, filename, manifestJson);
80
+ const served = manifestServedFiles(manifestJson);
81
+ const drop = served.dropped.get(filename);
82
+ if (drop !== void 0) throw new Error((0, _animus_ui_extract_pipeline.droppedExtensionMessage)(drop));
83
+ if (!result.hasComponents && served.listed.has(filename)) throw new Error(`the analysis extracted components from ${filename}, but its transform found none`);
70
84
  let code = result.hasComponents ? result.code : source;
71
85
  code = code.replace(CSS_IMPORT_RE, "");
72
86
  if (isRootEntry && !code.includes(_animus_ui_extract_session.ANIMUS_CSS_MODULE_ID)) {
@@ -79,10 +93,7 @@ function transformWithManifest(args) {
79
93
  return code;
80
94
  } catch (e) {
81
95
  const msg = e instanceof Error ? e.message : String(e);
82
- if (opts.strict && !opts.development) throw new Error(`[animus-extract] Transform failed for ${filename}: ${msg}`, { cause: e });
83
- if (opts.strict) console.error(`[animus-extract] Transform failed for ${filename}: ${msg}`);
84
- else console.warn(`[animus-extract] Transform failed for ${filename}:`, msg);
85
- return source;
96
+ throw new Error(`[animus-extract] Transform failed for ${filename}: ${msg}`, { cause: e });
86
97
  }
87
98
  }
88
99
  //#endregion
@@ -1,4 +1,4 @@
1
- import { contentHash } from "@animus-ui/extract/pipeline";
1
+ import { contentHash, droppedExtensionMessage, servedFiles } from "@animus-ui/extract/pipeline";
2
2
  import { ANIMUS_CSS_MODULE_ID } from "@animus-ui/extract/session";
3
3
  //#region src/loader-core.ts
4
4
  /** Matches every already-emitted stylesheet import — arbitrary relative
@@ -61,12 +61,26 @@ function extensionLineage(files, analyzedHashes) {
61
61
  const lineage = files.map((file) => `${file}\0${analyzedHashes?.get(file) ?? ""}`).join("\0");
62
62
  return `\nvoid "animus-lineage:${contentHash(lineage)}";\n`;
63
63
  }
64
+ let parsedManifest = null;
65
+ /** What a manifest's transform serves, parsed once per manifest: every
66
+ * module of a compilation reads the same one. */
67
+ function manifestServedFiles(manifestJson) {
68
+ if (parsedManifest?.json !== manifestJson) parsedManifest = {
69
+ json: manifestJson,
70
+ served: servedFiles(JSON.parse(manifestJson))
71
+ };
72
+ return parsedManifest.served;
73
+ }
64
74
  function transformWithManifest(args) {
65
75
  const { source, filename, manifestJson, engineApi, opts } = args;
66
76
  const isRootEntry = isCssImportTarget(filename, opts.cssImportTarget);
67
77
  try {
68
78
  const { transformFile } = engineApi();
69
79
  const result = transformFile(source, filename, manifestJson);
80
+ const served = manifestServedFiles(manifestJson);
81
+ const drop = served.dropped.get(filename);
82
+ if (drop !== void 0) throw new Error(droppedExtensionMessage(drop));
83
+ if (!result.hasComponents && served.listed.has(filename)) throw new Error(`the analysis extracted components from ${filename}, but its transform found none`);
70
84
  let code = result.hasComponents ? result.code : source;
71
85
  code = code.replace(CSS_IMPORT_RE, "");
72
86
  if (isRootEntry && !code.includes(ANIMUS_CSS_MODULE_ID)) {
@@ -79,10 +93,7 @@ function transformWithManifest(args) {
79
93
  return code;
80
94
  } catch (e) {
81
95
  const msg = e instanceof Error ? e.message : String(e);
82
- if (opts.strict && !opts.development) throw new Error(`[animus-extract] Transform failed for ${filename}: ${msg}`, { cause: e });
83
- if (opts.strict) console.error(`[animus-extract] Transform failed for ${filename}: ${msg}`);
84
- else console.warn(`[animus-extract] Transform failed for ${filename}:`, msg);
85
- return source;
96
+ throw new Error(`[animus-extract] Transform failed for ${filename}: ${msg}`, { cause: e });
86
97
  }
87
98
  }
88
99
  //#endregion
@@ -1,9 +1,14 @@
1
1
  export interface LoaderPolicyOptions {
2
- strict?: boolean;
3
- /** `next dev`: a strict transform failure reports and delivers the
4
- * source, as a non-strict one does, instead of failing the module. */
2
+ /** `next dev`: the Turbopack loader waits for and re-delivers the dev
3
+ * watcher's generations. A transform failure fails the module in every
4
+ * mode (SA1). */
5
5
  development?: boolean;
6
6
  cssImportTarget?: string;
7
+ /** The build the config wrapper created, which the loader reads the
8
+ * analysis of. */
9
+ build?: string;
10
+ /** The root that build's plugin analyses. */
11
+ root?: string;
7
12
  }
8
13
  export interface LoaderContextBase<O extends LoaderPolicyOptions = LoaderPolicyOptions> {
9
14
  resourcePath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"loader-core.d.ts","sourceRoot":"","sources":["../src/loader-core.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;2EACuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB,CAChC,CAAC,SAAS,mBAAmB,GAAG,mBAAmB;IAEnD,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,CAAC;IACpB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AA0DD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,SAAS,MAAM,EAAE,CAKnB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,MAAM,CAMR;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IAErB,SAAS,EAAE,MAAM,GAAG,CAAC;IACrB,IAAI,EAAE,mBAAmB,CAAC;CAC3B,GAAG,MAAM,CA6CT"}
1
+ {"version":3,"file":"loader-core.d.ts","sourceRoot":"","sources":["../src/loader-core.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,mBAAmB;IAClC;;sBAEkB;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;uBACmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB,CAChC,CAAC,SAAS,mBAAmB,GAAG,mBAAmB;IAEnD,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,CAAC;IACpB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AA0DD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,SAAS,MAAM,EAAE,CAKnB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,MAAM,CAMR;AAiBD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IAErB,SAAS,EAAE,MAAM,GAAG,CAAC;IACrB,IAAI,EAAE,mBAAmB,CAAC;CAC3B,GAAG,MAAM,CAiDT"}
package/dist/loader.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_loader_core = require("./loader-core-CAr8opFH.cjs");
1
+ const require_loader_core = require("./loader-core-Bz52QNm3.cjs");
2
2
  let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
3
3
  let _animus_ui_extract_session = require("@animus-ui/extract/session");
4
4
  let fs = require("fs");
@@ -6,8 +6,11 @@ let path = require("path");
6
6
  //#region src/loader.ts
7
7
  const epochSeenForSessionDir = /* @__PURE__ */ new Set();
8
8
  function animusLoader(source) {
9
+ const opts = this.getOptions?.() ?? {};
10
+ if (opts.build === void 0) throw new _animus_ui_extract_pipeline.AnimusConfigError(`[animus-extract] the Animus loader for ${(0, path.relative)(this.rootContext, this.resourcePath)} names no Animus build; it runs only as withAnimus() configures it.`);
11
+ const key = (0, _animus_ui_extract_session.sessionKey)(opts.build, opts.root ?? this.rootContext);
9
12
  if (this.addDependency !== void 0) {
10
- const sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)();
13
+ const sessionDir = (0, _animus_ui_extract_session.getSessionArtifactDir)(key);
11
14
  if (sessionDir) {
12
15
  const epochPath = (0, _animus_ui_extract_session.replacementEpochPath)(sessionDir);
13
16
  if (epochSeenForSessionDir.has(sessionDir) || (0, fs.existsSync)(epochPath)) {
@@ -16,29 +19,29 @@ function animusLoader(source) {
16
19
  }
17
20
  }
18
21
  }
19
- let manifestJson = (0, _animus_ui_extract_session.getManifestJson)();
20
- if (!manifestJson) return source;
22
+ let manifestJson = (0, _animus_ui_extract_session.getManifestJson)(key);
23
+ if (!manifestJson) throw new Error(`[animus-extract] no analysis is available to transform ${(0, path.relative)(this.rootContext, this.resourcePath)}: the project analysis failed; see the error above.`);
21
24
  const filename = (0, path.relative)(this.rootContext, this.resourcePath);
22
- const analyzedHash = (0, _animus_ui_extract_session.getAnalyzedHashes)()?.get(filename);
25
+ const analyzedHash = (0, _animus_ui_extract_session.getAnalyzedHashes)(key)?.get(filename);
23
26
  if (analyzedHash !== void 0) {
24
27
  const sourceHash = (0, _animus_ui_extract_pipeline.contentHash)(source);
25
28
  if (sourceHash !== analyzedHash) {
26
- const refreshedHash = (0, _animus_ui_extract_session.getAnalyzedHashes)()?.get(filename);
29
+ const refreshedHash = (0, _animus_ui_extract_session.getAnalyzedHashes)(key)?.get(filename);
27
30
  if (refreshedHash === void 0 || sourceHash !== refreshedHash) throw new Error(`ANIMUS_ANALYSIS_CATCHING_UP: ${filename} changed after the current analysis; retrying on the next watch turn`);
28
- manifestJson = (0, _animus_ui_extract_session.getManifestJson)() ?? manifestJson;
31
+ manifestJson = (0, _animus_ui_extract_session.getManifestJson)(key) ?? manifestJson;
29
32
  }
30
33
  }
31
34
  const code = require_loader_core.transformWithManifest({
32
35
  source,
33
36
  filename,
34
37
  manifestJson,
35
- engineApi: _animus_ui_extract_session.engineApi,
36
- opts: this.getOptions?.() ?? {}
38
+ engineApi: () => (0, _animus_ui_extract_session.engineApi)(key),
39
+ opts
37
40
  });
38
41
  if (!(this.mode !== "production") || this.addDependency === void 0) return code;
39
42
  const extended = require_loader_core.extendedFiles(manifestJson, filename);
40
43
  for (const file of extended) this.addDependency((0, path.join)(this.rootContext, file));
41
- return code + require_loader_core.extensionLineage(extended, (0, _animus_ui_extract_session.getAnalyzedHashes)());
44
+ return code + require_loader_core.extensionLineage(extended, (0, _animus_ui_extract_session.getAnalyzedHashes)(key));
42
45
  }
43
46
  //#endregion
44
47
  module.exports = animusLoader;
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAIvD,KAAK,aAAa,GAAG,iBAAiB,GAAG;IACvC,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,GAAG,MAAM,CAAC;CAC9C,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,MAAM,GACb,MAAM,CAkDR"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAIvD,KAAK,aAAa,GAAG,iBAAiB,GAAG;IACvC,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,GAAG,MAAM,CAAC;CAC9C,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAClC,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,MAAM,GACb,MAAM,CAuER"}