@animus-ui/next-plugin 0.1.0-next.39 → 0.1.0-next.44

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.cjs ADDED
@@ -0,0 +1,465 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_singleton = require("./singleton-VPOM1BCR.cjs");
3
+ let fs = require("fs");
4
+ let path = require("path");
5
+ let url = require("url");
6
+ let crypto = require("crypto");
7
+ let os = require("os");
8
+ let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
9
+ //#region src/plugin.ts
10
+ const PLUGIN_NAME = "AnimusWebpackPlugin";
11
+ var AnimusWebpackPlugin = class {
12
+ options;
13
+ rootDir = null;
14
+ configJson = "";
15
+ groupRegistryJson = "";
16
+ themeJson = "";
17
+ variableMapJson = "";
18
+ variableCss = "";
19
+ contextualVarsJson = null;
20
+ globalCss = "";
21
+ systemResolveScript = null;
22
+ fileCache = /* @__PURE__ */ new Map();
23
+ lastCssHash = null;
24
+ externalPackageDirs = [];
25
+ externalSourceEntries = /* @__PURE__ */ new Map();
26
+ constructor(options) {
27
+ this.options = options;
28
+ }
29
+ initialized = false;
30
+ apply(compiler) {
31
+ compiler.hooks.run.tapPromise(PLUGIN_NAME, async (_compiler) => {
32
+ this.rootDir = _compiler.context;
33
+ const existing = require_singleton.getAnalysisPromise();
34
+ if (existing) {
35
+ await existing;
36
+ return;
37
+ }
38
+ const promise = this.runFullPipeline();
39
+ require_singleton.setAnalysisPromise(promise);
40
+ await promise;
41
+ });
42
+ compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async (_compiler) => {
43
+ this.rootDir = _compiler.context;
44
+ if (!this.initialized) {
45
+ const existing = require_singleton.getAnalysisPromise();
46
+ if (existing) {
47
+ await existing;
48
+ this.initialized = true;
49
+ return;
50
+ }
51
+ const promise = this.runFullPipeline();
52
+ require_singleton.setAnalysisPromise(promise);
53
+ await promise;
54
+ this.initialized = true;
55
+ return;
56
+ }
57
+ await this.handleWatchUpdate();
58
+ });
59
+ }
60
+ async handleWatchUpdate() {
61
+ const rootDir = this.rootDir;
62
+ const resolvedSystemPath = (0, path.resolve)(rootDir, this.options.system);
63
+ try {
64
+ const systemSource = (0, fs.readFileSync)(resolvedSystemPath, "utf-8");
65
+ const systemRelPath = (0, path.relative)(rootDir, resolvedSystemPath);
66
+ const cached = this.fileCache.get(systemRelPath);
67
+ const currentHash = (0, crypto.createHash)("md5").update(systemSource).digest("hex");
68
+ if (cached && cached.hash !== currentHash) {
69
+ this.resetForHmr();
70
+ this.initialized = false;
71
+ const promise = this.runFullPipeline();
72
+ require_singleton.setAnalysisPromise(promise);
73
+ await promise;
74
+ this.initialized = true;
75
+ return;
76
+ }
77
+ } catch {}
78
+ const excludePatterns = this.options.exclude ?? [
79
+ "node_modules",
80
+ "dist",
81
+ ".test.",
82
+ ".spec.",
83
+ ".next",
84
+ ".animus"
85
+ ];
86
+ const files = this.discoverFiles(rootDir, rootDir, excludePatterns);
87
+ let hasChanges = false;
88
+ for (const filePath of files) {
89
+ const relPath = (0, path.relative)(rootDir, filePath);
90
+ const cached = this.fileCache.get(relPath);
91
+ const source = (0, fs.readFileSync)(filePath, "utf-8");
92
+ const hash = (0, crypto.createHash)("md5").update(source).digest("hex");
93
+ if (!cached || cached.hash !== hash) {
94
+ hasChanges = true;
95
+ this.fileCache.set(relPath, {
96
+ hash,
97
+ source
98
+ });
99
+ }
100
+ }
101
+ if (hasChanges) {
102
+ require_singleton.resetAnalysisPromise();
103
+ const promise = this.runFullPipeline();
104
+ require_singleton.setAnalysisPromise(promise);
105
+ await promise;
106
+ }
107
+ }
108
+ async runFullPipeline() {
109
+ const rootDir = this.rootDir;
110
+ const resolvedSystemPath = (0, path.resolve)(rootDir, this.options.system);
111
+ this.loadSystem(rootDir, resolvedSystemPath);
112
+ const excludePatterns = this.options.exclude ?? [
113
+ "node_modules",
114
+ "dist",
115
+ ".test.",
116
+ ".spec.",
117
+ ".next",
118
+ ".animus"
119
+ ];
120
+ const files = this.discoverFiles(rootDir, rootDir, excludePatterns);
121
+ const fileEntries = [];
122
+ for (const filePath of files) {
123
+ const source = (0, fs.readFileSync)(filePath, "utf-8");
124
+ const relPath = (0, path.relative)(rootDir, filePath);
125
+ const hash = (0, crypto.createHash)("md5").update(source).digest("hex");
126
+ this.fileCache.set(relPath, {
127
+ hash,
128
+ source
129
+ });
130
+ fileEntries.push({
131
+ path: relPath,
132
+ source,
133
+ hash
134
+ });
135
+ }
136
+ const packageNames = (0, _animus_ui_extract_pipeline.extractSystemFilePackages)(resolvedSystemPath);
137
+ const packageMap = this.resolvePackagesByName(rootDir, packageNames);
138
+ const excludePatternsForPkgs = [
139
+ "dist",
140
+ ".test.",
141
+ ".spec."
142
+ ];
143
+ const collectedPkgDirs = [];
144
+ this.externalSourceEntries.clear();
145
+ for (const [specifier, entryRelPath] of Object.entries(packageMap)) {
146
+ const absEntry = (0, path.resolve)(rootDir, entryRelPath);
147
+ let pkgRoot = (0, path.dirname)(absEntry);
148
+ while (pkgRoot !== (0, path.dirname)(pkgRoot) && !(0, fs.existsSync)((0, path.join)(pkgRoot, "package.json"))) pkgRoot = (0, path.dirname)(pkgRoot);
149
+ const srcDir = (0, path.join)(pkgRoot, "src");
150
+ if ((0, fs.existsSync)(srcDir)) {
151
+ collectedPkgDirs.push(srcDir);
152
+ const srcEntry = (0, path.join)(srcDir, "index.ts");
153
+ if ((0, fs.existsSync)(srcEntry)) {
154
+ packageMap[specifier] = (0, path.relative)(rootDir, srcEntry);
155
+ this.externalSourceEntries.set(specifier, srcEntry);
156
+ }
157
+ const pkgFiles = this.discoverFiles(srcDir, rootDir, excludePatternsForPkgs);
158
+ for (const pkgFile of pkgFiles) {
159
+ const pkgRelPath = (0, path.relative)(rootDir, pkgFile);
160
+ if (!fileEntries.some((e) => e.path === pkgRelPath)) try {
161
+ const pkgSource = (0, fs.readFileSync)(pkgFile, "utf-8");
162
+ const pkgHash = (0, crypto.createHash)("md5").update(pkgSource).digest("hex");
163
+ this.fileCache.set(pkgRelPath, {
164
+ hash: pkgHash,
165
+ source: pkgSource
166
+ });
167
+ fileEntries.push({
168
+ path: pkgRelPath,
169
+ source: pkgSource,
170
+ hash: pkgHash
171
+ });
172
+ } catch {}
173
+ }
174
+ } else collectedPkgDirs.push((0, path.dirname)(absEntry));
175
+ }
176
+ this.externalPackageDirs = collectedPkgDirs;
177
+ const { analyzeProject } = require("@animus-ui/extract");
178
+ const animusDirPath = (0, path.join)(rootDir, ".animus");
179
+ const emitterConfig = JSON.stringify({
180
+ runtime_import: "@animus-ui/system/runtime",
181
+ css_module_id: ".animus/styles.css",
182
+ system_props_module_id: (0, path.join)(animusDirPath, "system-props.js")
183
+ });
184
+ const manifestJson = analyzeProject(JSON.stringify(fileEntries), this.themeJson, this.variableMapJson, this.contextualVarsJson || null, this.configJson, this.groupRegistryJson, JSON.stringify(packageMap), false, this.options.prefix || null, emitterConfig);
185
+ const manifest = JSON.parse(manifestJson);
186
+ let componentCss = manifest?.css || "";
187
+ if (this.systemResolveScript && componentCss.includes("__TRANSFORM__")) componentCss = this.resolveTransforms(rootDir, componentCss);
188
+ componentCss = (0, _animus_ui_extract_pipeline.applyUnitFallback)(componentCss);
189
+ const fullCss = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
190
+ layers: this.options.layers,
191
+ variableCss: this.variableCss,
192
+ globalCss: this.globalCss,
193
+ componentCss
194
+ });
195
+ const cssHash = (0, crypto.createHash)("md5").update(fullCss).digest("hex");
196
+ if (cssHash !== this.lastCssHash) {
197
+ const animusDir = (0, path.join)(rootDir, ".animus");
198
+ if (!(0, fs.existsSync)(animusDir)) (0, fs.mkdirSync)(animusDir, { recursive: true });
199
+ (0, fs.writeFileSync)((0, path.join)(animusDir, "styles.css"), fullCss);
200
+ this.lastCssHash = cssHash;
201
+ }
202
+ const systemPropMap = JSON.stringify(manifest?.system_prop_map ?? {});
203
+ const dynamicProps = manifest?.dynamic_props ?? {};
204
+ const dynamicPropConfig = {};
205
+ for (const [propName, meta] of Object.entries(dynamicProps)) dynamicPropConfig[propName] = {
206
+ varName: meta.var_name,
207
+ slotClass: meta.slot_class,
208
+ ...meta.transform_name ? { transformName: meta.transform_name } : {},
209
+ ...meta.scale_values && Object.keys(meta.scale_values).length > 0 ? { scaleValues: meta.scale_values } : {}
210
+ };
211
+ let transformsSource = "{}";
212
+ if (this.systemResolveScript) try {
213
+ const resolvedSystemPath = (0, path.resolve)(rootDir, this.options.system);
214
+ const tmpOut = (0, path.join)((0, os.tmpdir)(), `animus-transforms-${Date.now()}.json`);
215
+ (0, _animus_ui_extract_pipeline.execSubprocess)(`const m = require(${JSON.stringify(resolvedSystemPath)});\nconst ds = m.ds || m.default || m.system;\nconst cfg = ds.toConfig();\nconst out = {};\nfor (const [name, fn] of Object.entries(cfg.transforms || {})) {\n out[name] = fn.toString();\n}\nrequire('fs').writeFileSync(${JSON.stringify(tmpOut)}, JSON.stringify(out));\n`, rootDir);
216
+ const fnStrings = JSON.parse((0, fs.readFileSync)(tmpOut, "utf-8"));
217
+ try {
218
+ require("fs").unlinkSync(tmpOut);
219
+ } catch {}
220
+ transformsSource = `{\n${Object.entries(fnStrings).map(([name, src]) => ` ${JSON.stringify(name)}: ${src}`).join(",\n")}\n}`;
221
+ } catch {}
222
+ const animusDir = (0, path.join)(rootDir, ".animus");
223
+ if (!(0, fs.existsSync)(animusDir)) (0, fs.mkdirSync)(animusDir, { recursive: true });
224
+ (0, fs.writeFileSync)((0, path.join)(animusDir, "system-props.js"), `export const systemPropMap = ${systemPropMap};\nexport const systemPropGroups = ${this.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${transformsSource};\n`);
225
+ require_singleton.setManifestJson(manifestJson);
226
+ }
227
+ loadSystem(rootDir, systemPath) {
228
+ const tmpOut = (0, path.join)((0, os.tmpdir)(), `animus-system-${Date.now()}.json`);
229
+ (0, _animus_ui_extract_pipeline.execSubprocess)(`const m = require(${JSON.stringify(systemPath)});\nconst exportNames = Object.keys(m);\nconst candidates = exportNames.filter(k => m[k] && typeof m[k].toConfig === 'function');\nif (candidates.length === 0) {\n throw new Error('[animus-extract] No SystemInstance found. Exports: [' + exportNames.join(', ') + ']. None have a .toConfig() method. A SystemInstance is created by: export const ds = createSystem().build()');\n}\nif (candidates.length > 1) {\n throw new Error('[animus-extract] Multiple SystemInstance candidates found: [' + candidates.join(', ') + ']. Specify which one to use.');\n}\nconst ds = m[candidates[0]];\nconst cfg = ds.toConfig();\nconst tokens = m.tokens || m.theme || null;\nconst serialized = tokens && typeof tokens.serialize === 'function' ? tokens.serialize() : null;\nconst globalStyleBlocks = {};\nfor (const [key, val] of Object.entries(m)) {\n if (val && typeof val === 'object' && val.__brand === 'GlobalStyleBlock') {\n globalStyleBlocks[key] = val.styles;\n }\n}\nconst transformSources = {};\nfor (const [name, fn] of Object.entries(cfg.transforms || {})) {\n transformSources[name] = fn.toString();\n}\nrequire('fs').writeFileSync(${JSON.stringify(tmpOut)}, JSON.stringify({\n propConfig: cfg.propConfig,\n groupRegistry: cfg.groupRegistry,\n serialized: serialized,\n transformNames: Object.keys(cfg.transforms || {}),\n transformSources: transformSources,\n globalStyleBlocks: Object.keys(globalStyleBlocks).length > 0 ? globalStyleBlocks : null\n}));\n`, rootDir);
230
+ const result = (0, fs.readFileSync)(tmpOut, "utf-8");
231
+ try {
232
+ require("fs").unlinkSync(tmpOut);
233
+ } catch {}
234
+ const parsed = JSON.parse(result);
235
+ this.configJson = parsed.propConfig;
236
+ this.groupRegistryJson = parsed.groupRegistry;
237
+ if (parsed.serialized) {
238
+ this.themeJson = parsed.serialized.scalesJson;
239
+ this.variableMapJson = parsed.serialized.variableMapJson;
240
+ this.variableCss = parsed.serialized.variableCss;
241
+ this.contextualVarsJson = parsed.serialized.contextualVarsJson;
242
+ if (this.options.prefix) {
243
+ const prefixed = (0, _animus_ui_extract_pipeline.applyPrefix)(this.options.prefix, this.variableMapJson, this.variableCss, this.themeJson, this.contextualVarsJson || void 0);
244
+ this.variableMapJson = prefixed.variableMapJson;
245
+ this.variableCss = prefixed.variableCss;
246
+ if (prefixed.themeJson) this.themeJson = prefixed.themeJson;
247
+ if (prefixed.contextualVarsJson) this.contextualVarsJson = prefixed.contextualVarsJson;
248
+ }
249
+ } else throw new Error("[animus-extract] Theme must be built with createTheme().build(). No .serialize() method found on tokens export.");
250
+ if (parsed.globalStyleBlocks) {
251
+ if (Object.keys(parsed.globalStyleBlocks).length > 0) try {
252
+ const hasTransforms = parsed.transformNames && parsed.transformNames.length > 0;
253
+ const flat = JSON.parse(this.themeJson);
254
+ const propConfig = JSON.parse(this.configJson);
255
+ const variableMap = {};
256
+ for (const [tokenPath, value] of Object.entries(flat)) if (typeof value === "string" && value.startsWith("var(") && value.endsWith(")")) variableMap[tokenPath] = value.slice(4, -1);
257
+ const transforms = {};
258
+ if (hasTransforms && parsed.transformSources) for (const [name, src] of Object.entries(parsed.transformSources)) try {
259
+ transforms[name] = new Function(`return (${src})`)();
260
+ } catch {}
261
+ const gsResult = (0, _animus_ui_extract_pipeline.resolveGlobalStyles)(parsed.globalStyleBlocks, propConfig, flat, variableMap, transforms);
262
+ const parts = Object.values(gsResult).filter(Boolean);
263
+ if (parts.length > 0) this.globalCss = `@layer global {\n${parts.join("\n\n")}\n}`;
264
+ } catch (e) {
265
+ const msg = e instanceof Error ? e.message : String(e);
266
+ if (this.options.strict) throw new Error(`[animus-extract] Global styles resolution failed: ${msg}`);
267
+ console.warn("[animus-extract] Global styles resolution failed:", msg);
268
+ }
269
+ }
270
+ if (parsed.transformNames && parsed.transformNames.length > 0) {
271
+ const tmpResolve = (0, path.join)((0, os.tmpdir)(), `animus-transforms-resolve-${Date.now()}.js`);
272
+ (0, fs.writeFileSync)(tmpResolve, `const m = require(${JSON.stringify(systemPath)});\nconst ds = m.ds || m.default || m.system;\nconst cfg = ds.toConfig();\nconst css = require('fs').readFileSync(process.argv[2], 'utf-8');\nconst resolved = css.replace(/__TRANSFORM__(\\w+)__(.+?)__/g, (_, name, rawValue) => {\n const fn = cfg.transforms[name];\n if (!fn) return rawValue;\n const value = rawValue !== '' && !isNaN(Number(rawValue)) ? Number(rawValue) : rawValue;\n const result = fn(value);\n return typeof result === 'object' ? JSON.stringify(result) : String(result);\n});\nrequire('fs').writeFileSync(process.argv[3], resolved);\n`);
273
+ this.systemResolveScript = tmpResolve;
274
+ }
275
+ }
276
+ resolveTransforms(rootDir, css) {
277
+ const { execSync } = require("child_process");
278
+ const ts = Date.now();
279
+ const tmpIn = (0, path.join)((0, os.tmpdir)(), `animus-css-${ts}.css`);
280
+ const tmpOut = (0, path.join)((0, os.tmpdir)(), `animus-css-${ts}.out.css`);
281
+ (0, fs.writeFileSync)(tmpIn, css);
282
+ execSync(`${(0, _animus_ui_extract_pipeline.detectRuntime)()} run "${this.systemResolveScript}" "${tmpIn}" "${tmpOut}"`, {
283
+ cwd: rootDir,
284
+ encoding: "utf-8"
285
+ });
286
+ const resolved = (0, fs.readFileSync)(tmpOut, "utf-8");
287
+ try {
288
+ require("fs").unlinkSync(tmpIn);
289
+ require("fs").unlinkSync(tmpOut);
290
+ } catch {}
291
+ return resolved;
292
+ }
293
+ discoverFiles(dir, rootDir, excludePatterns) {
294
+ const results = [];
295
+ const extensions = new Set([
296
+ ".ts",
297
+ ".tsx",
298
+ ".js",
299
+ ".jsx"
300
+ ]);
301
+ const walk = (currentDir) => {
302
+ let entries;
303
+ try {
304
+ entries = (0, fs.readdirSync)(currentDir);
305
+ } catch {
306
+ return;
307
+ }
308
+ for (const entry of entries) {
309
+ const fullPath = (0, path.join)(currentDir, entry);
310
+ const relPath = (0, path.relative)(rootDir, fullPath);
311
+ if (excludePatterns.some((p) => relPath.includes(p))) continue;
312
+ let stat;
313
+ try {
314
+ stat = (0, fs.statSync)(fullPath);
315
+ } catch {
316
+ continue;
317
+ }
318
+ if (stat.isDirectory()) walk(fullPath);
319
+ else if (extensions.has((0, path.extname)(entry))) results.push(fullPath);
320
+ }
321
+ };
322
+ walk(dir);
323
+ return results;
324
+ }
325
+ resolvePackagesByName(rootDir, names) {
326
+ if (names.length === 0) return {};
327
+ const nameSet = new Set(names);
328
+ const resolved = /* @__PURE__ */ new Set();
329
+ const packageMap = {};
330
+ try {
331
+ const workspaces = JSON.parse((0, fs.readFileSync)((0, path.join)(rootDir, "package.json"), "utf-8")).workspaces || [];
332
+ for (const ws of workspaces) {
333
+ const wsDir = (0, path.resolve)(rootDir, ws);
334
+ if (!(0, fs.existsSync)(wsDir)) continue;
335
+ try {
336
+ const pkg = JSON.parse((0, fs.readFileSync)((0, path.join)(wsDir, "package.json"), "utf-8"));
337
+ const name = pkg.name || "";
338
+ if (nameSet.has(name)) {
339
+ const entryPath = (0, path.resolve)(wsDir, pkg.main || pkg.module || "index.ts");
340
+ if ((0, fs.existsSync)(entryPath)) {
341
+ packageMap[name] = (0, path.relative)(rootDir, entryPath);
342
+ resolved.add(name);
343
+ }
344
+ }
345
+ } catch {}
346
+ }
347
+ } catch {}
348
+ for (const name of nameSet) {
349
+ if (resolved.has(name)) continue;
350
+ try {
351
+ packageMap[name] = (0, path.relative)(rootDir, require.resolve(name, { paths: [rootDir] }));
352
+ } catch {}
353
+ }
354
+ return packageMap;
355
+ }
356
+ /**
357
+ * Reset analysis state for HMR geological reset.
358
+ */
359
+ resetForHmr() {
360
+ require_singleton.resetAnalysisPromise();
361
+ this.lastCssHash = null;
362
+ try {
363
+ const { clearAnalysisCache } = require("@animus-ui/extract");
364
+ clearAnalysisCache();
365
+ } catch {}
366
+ }
367
+ /** Expose file cache for HMR change detection */
368
+ getFileCache() {
369
+ return this.fileCache;
370
+ }
371
+ /** Expose options for the loader */
372
+ getOptions() {
373
+ return this.options;
374
+ }
375
+ /** Expose external package directories for webpack loader allowlisting */
376
+ getExternalPackageDirs() {
377
+ return this.externalPackageDirs;
378
+ }
379
+ /** Expose external package source entries for webpack resolve alias */
380
+ getExternalSourceEntries() {
381
+ return this.externalSourceEntries;
382
+ }
383
+ };
384
+ //#endregion
385
+ //#region src/with-animus.ts
386
+ const __dirname$1 = (0, path.dirname)((0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
387
+ let warnedGitignore = false;
388
+ /**
389
+ * Wrap a Next.js config to enable Animus static CSS extraction.
390
+ *
391
+ * ```ts
392
+ * // next.config.ts
393
+ * import { withAnimus } from '@animus-ui/next-plugin';
394
+ * export default withAnimus({ system: './src/ds.ts' })({});
395
+ * ```
396
+ */
397
+ function withAnimus(options) {
398
+ if (!options.system) throw new Error("[animus-extract] Missing required option `system`. Provide the path to your SystemInstance module: withAnimus({ system: \"./src/ds.ts\" })");
399
+ return (nextConfig) => {
400
+ const existingWebpack = nextConfig.webpack;
401
+ return {
402
+ ...nextConfig,
403
+ webpack(config, context) {
404
+ const rootDir = process.cwd();
405
+ const animusDir = (0, path.join)(rootDir, ".animus");
406
+ if (!(0, fs.existsSync)(animusDir)) (0, fs.mkdirSync)(animusDir, { recursive: true });
407
+ if (!warnedGitignore) {
408
+ warnedGitignore = true;
409
+ try {
410
+ const gitignorePath = (0, path.join)(rootDir, ".gitignore");
411
+ if ((0, fs.existsSync)(gitignorePath)) {
412
+ if (!(0, fs.readFileSync)(gitignorePath, "utf-8").includes(".animus")) console.warn("[animus-extract] Add `.animus/` to your .gitignore — it contains generated build artifacts.");
413
+ }
414
+ } catch {}
415
+ }
416
+ const plugin = new AnimusWebpackPlugin({
417
+ system: options.system,
418
+ exclude: options.exclude,
419
+ strict: options.strict,
420
+ verbose: options.verbose,
421
+ prefix: options.prefix
422
+ });
423
+ config.plugins = config.plugins || [];
424
+ config.plugins.push(plugin);
425
+ config.resolve = config.resolve || {};
426
+ config.resolve.alias = config.resolve.alias || {};
427
+ config.resolve.alias[".animus/styles.css"] = (0, path.join)(rootDir, ".animus", "styles.css");
428
+ const systemPropsPath = (0, path.join)(rootDir, ".animus", "system-props.js");
429
+ config.plugins.push({ apply(compiler) {
430
+ compiler.hooks.normalModuleFactory.tap("AnimusVirtualResolve", (nmf) => {
431
+ nmf.hooks.beforeResolve.tap("AnimusVirtualResolve", (resolveData) => {
432
+ if (resolveData.request === "virtual:animus/system-props") resolveData.request = systemPropsPath;
433
+ const srcEntry = plugin.getExternalSourceEntries().get(resolveData.request);
434
+ if (srcEntry) resolveData.request = srcEntry;
435
+ });
436
+ });
437
+ } });
438
+ const loaderPath = (0, path.resolve)(__dirname$1, "loader.mjs");
439
+ const actualLoaderPath = (0, fs.existsSync)(loaderPath) ? loaderPath : (0, path.resolve)(__dirname$1, "loader.ts");
440
+ config.module = config.module || {};
441
+ config.module.rules = config.module.rules || [];
442
+ config.module.rules.push({
443
+ test: (filePath) => {
444
+ if (/\.[jt]sx?$/.test(filePath)) return true;
445
+ if (/\.mjs$/.test(filePath)) return plugin.getExternalPackageDirs().some((dir) => filePath.startsWith(dir + path.sep) || filePath === dir);
446
+ return false;
447
+ },
448
+ exclude: (filePath) => {
449
+ if (!filePath.includes("node_modules")) return false;
450
+ return !plugin.getExternalPackageDirs().some((dir) => filePath.startsWith(dir + path.sep) || filePath === dir);
451
+ },
452
+ enforce: "pre",
453
+ use: [{
454
+ loader: actualLoaderPath,
455
+ options: { strict: options.strict }
456
+ }]
457
+ });
458
+ if (typeof existingWebpack === "function") return existingWebpack(config, context);
459
+ return config;
460
+ }
461
+ };
462
+ };
463
+ }
464
+ //#endregion
465
+ exports.withAnimus = withAnimus;
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { a as setManifestJson, i as setAnalysisPromise, o as __require, r as resetAnalysisPromise, t as getAnalysisPromise } from "./singleton-BZ7W87Ir.mjs";
2
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
3
3
  import { dirname, extname, join, relative, resolve, sep } from "path";
4
+ import { fileURLToPath } from "url";
4
5
  import { createHash } from "crypto";
5
6
  import { tmpdir } from "os";
6
7
  import { applyPrefix, applyUnitFallback, assembleStylesheet, detectRuntime, execSubprocess, extractSystemFilePackages, resolveGlobalStyles } from "@animus-ui/extract/pipeline";
@@ -381,6 +382,7 @@ var AnimusWebpackPlugin = class {
381
382
  };
382
383
  //#endregion
383
384
  //#region src/with-animus.ts
385
+ const __dirname = dirname(fileURLToPath(import.meta.url));
384
386
  let warnedGitignore = false;
385
387
  /**
386
388
  * Wrap a Next.js config to enable Animus static CSS extraction.
@@ -0,0 +1,53 @@
1
+ const require_singleton = require("./singleton-VPOM1BCR.cjs");
2
+ let path = require("path");
3
+ //#region src/loader.ts
4
+ /**
5
+ * Regex matching CSS import lines injected by the Rust emitter.
6
+ * Catches both the alias form (`import '.animus/styles.css'`) and
7
+ * relative forms (`import '../.animus/styles.css'`).
8
+ */
9
+ const CSS_IMPORT_RE = /import\s+['"][^'"]*\.animus\/styles\.css['"];\n?/g;
10
+ /**
11
+ * Root entry file patterns. CSS is imported ONLY here to prevent
12
+ * per-chunk duplication in Next.js builds.
13
+ *
14
+ * - App Router: `app/layout.tsx` (root layout wraps all routes)
15
+ * - Pages Router: `pages/_app.tsx` (custom App wraps all pages)
16
+ *
17
+ * The `(src\/)?` prefix handles projects that use a `src/` directory.
18
+ */
19
+ const ROOT_ENTRY_RE = /^(src\/)?(?:app\/layout|pages\/_app)\.[tj]sx?$/;
20
+ /**
21
+ * Webpack loader for Animus source transformation.
22
+ * Runs with enforce: 'pre' to see original source before Babel/SWC.
23
+ * Replaces builder chains with createComponent() calls using the pre-built manifest.
24
+ *
25
+ * CSS handling: the Rust emitter injects `import '.animus/styles.css'` into every
26
+ * file with extracted components. Without intervention, webpack duplicates the full
27
+ * stylesheet into every route chunk. This loader strips those imports and re-injects
28
+ * a single CSS import in the root layout/app file, ensuring one copy for the whole app.
29
+ */
30
+ function animusLoader(source) {
31
+ const manifestJson = require_singleton.getManifestJson();
32
+ if (!manifestJson) return source;
33
+ const filename = (0, path.relative)(this.rootContext, this.resourcePath);
34
+ const isRootEntry = ROOT_ENTRY_RE.test(filename);
35
+ try {
36
+ const { transformFile } = require("@animus-ui/extract");
37
+ const result = transformFile(source, filename, manifestJson);
38
+ let code = result.hasComponents ? result.code : source;
39
+ code = code.replace(CSS_IMPORT_RE, "");
40
+ if (isRootEntry && !code.includes(".animus/styles.css")) if (code.startsWith("'use client'") || code.startsWith("\"use client\"")) {
41
+ const nl = code.indexOf("\n");
42
+ code = `${code.slice(0, nl + 1)}import '.animus/styles.css';\n${code.slice(nl + 1)}`;
43
+ } else code = `import '.animus/styles.css';\n${code}`;
44
+ return code;
45
+ } catch (e) {
46
+ const msg = e instanceof Error ? e.message : String(e);
47
+ if ((this.getOptions?.() ?? {}).strict) throw new Error(`[animus-extract] Transform failed for ${filename}: ${msg}`);
48
+ console.warn(`[animus-extract] Transform failed for ${filename}:`, msg);
49
+ return source;
50
+ }
51
+ }
52
+ //#endregion
53
+ module.exports = animusLoader;
@@ -0,0 +1,56 @@
1
+ //#region src/singleton.ts
2
+ /**
3
+ * Module-scope singleton for sharing analysis state between the webpack plugin and loader.
4
+ *
5
+ * Uses globalThis to survive the ESM/CJS module boundary: the plugin is loaded via ESM
6
+ * (next.config.ts import) while the loader is loaded by webpack via require(). Without
7
+ * globalThis, each module system gets its own singleton instance.
8
+ */
9
+ const MANIFEST_KEY = "__animus_manifest_json__";
10
+ const PROMISE_KEY = "__animus_analysis_promise__";
11
+ function getManifestJson() {
12
+ return globalThis[MANIFEST_KEY];
13
+ }
14
+ function setManifestJson(json) {
15
+ globalThis[MANIFEST_KEY] = json;
16
+ }
17
+ function getAnalysisPromise() {
18
+ return globalThis[PROMISE_KEY];
19
+ }
20
+ function setAnalysisPromise(promise) {
21
+ globalThis[PROMISE_KEY] = promise;
22
+ }
23
+ function resetAnalysisPromise() {
24
+ globalThis[PROMISE_KEY] = null;
25
+ }
26
+ //#endregion
27
+ Object.defineProperty(exports, "getAnalysisPromise", {
28
+ enumerable: true,
29
+ get: function() {
30
+ return getAnalysisPromise;
31
+ }
32
+ });
33
+ Object.defineProperty(exports, "getManifestJson", {
34
+ enumerable: true,
35
+ get: function() {
36
+ return getManifestJson;
37
+ }
38
+ });
39
+ Object.defineProperty(exports, "resetAnalysisPromise", {
40
+ enumerable: true,
41
+ get: function() {
42
+ return resetAnalysisPromise;
43
+ }
44
+ });
45
+ Object.defineProperty(exports, "setAnalysisPromise", {
46
+ enumerable: true,
47
+ get: function() {
48
+ return setAnalysisPromise;
49
+ }
50
+ });
51
+ Object.defineProperty(exports, "setManifestJson", {
52
+ enumerable: true,
53
+ get: function() {
54
+ return setManifestJson;
55
+ }
56
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"with-animus.d.ts","sourceRoot":"","sources":["../src/with-animus.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,KAAK,aAAa,GAAG;IACnB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAChC,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,KAAK,CAAC;YACZ,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;YAC5C,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;YAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,GAAG,CAAC,EAAE,KAAK,CAAC;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,CAAC,CAAC;SACpE,CAAC,CAAC;KACJ,CAAC;CACH,CAAC;AAEF,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC1C,OAAO,CAAC,EAAE,CACR,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,aAAa,CAAC;CACpB,CAAC;AAIF;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,iBAAiB,GACzB,CAAC,UAAU,EAAE,UAAU,KAAK,UAAU,CA4JxC"}
1
+ {"version":3,"file":"with-animus.d.ts","sourceRoot":"","sources":["../src/with-animus.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAKjD,KAAK,aAAa,GAAG;IACnB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAChC,CAAC;IACF,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,KAAK,CAAC;YACZ,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;YAC5C,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;YAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,GAAG,CAAC,EAAE,KAAK,CAAC;gBAAE,MAAM,EAAE,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,CAAC,CAAC;SACpE,CAAC,CAAC;KACJ,CAAC;CACH,CAAC;AAEF,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC1C,OAAO,CAAC,EAAE,CACR,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,aAAa,CAAC;CACpB,CAAC;AAIF;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,iBAAiB,GACzB,CAAC,UAAU,EAAE,UAAU,KAAK,UAAU,CA4JxC"}
package/package.json CHANGED
@@ -1,22 +1,24 @@
1
1
  {
2
2
  "name": "@animus-ui/next-plugin",
3
- "version": "0.1.0-next.39",
3
+ "version": "0.1.0-next.44",
4
4
  "description": "Animus static CSS extraction for Next.js (webpack)",
5
5
  "author": "codecaaron <airrobb@gmail.com>",
6
6
  "license": "MIT",
7
- "main": "./dist/index.mjs",
7
+ "main": "./dist/index.cjs",
8
8
  "module": "./dist/index.mjs",
9
9
  "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
13
13
  "import": "./dist/index.mjs",
14
- "default": "./dist/index.mjs"
14
+ "require": "./dist/index.cjs",
15
+ "default": "./dist/index.cjs"
15
16
  },
16
17
  "./loader": {
17
18
  "types": "./dist/loader.d.ts",
18
19
  "import": "./dist/loader.mjs",
19
- "default": "./dist/loader.mjs"
20
+ "require": "./dist/loader.cjs",
21
+ "default": "./dist/loader.cjs"
20
22
  }
21
23
  },
22
24
  "files": [
@@ -51,6 +53,6 @@
51
53
  "webpack": ">=5.0.0"
52
54
  },
53
55
  "dependencies": {
54
- "@animus-ui/extract": "0.1.0-next.39"
56
+ "@animus-ui/extract": "0.1.0-next.44"
55
57
  }
56
58
  }