@styleframe/plugin 2.4.0 → 3.0.0

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/astro.d.ts +1 -1
  3. package/dist/astro.d.ts.map +1 -1
  4. package/dist/astro.js +2 -3
  5. package/dist/astro.js.map +1 -1
  6. package/dist/esbuild.d.ts +3 -3
  7. package/dist/esbuild.js +1 -2
  8. package/dist/esbuild.js.map +1 -1
  9. package/dist/farm.d.ts +1 -1
  10. package/dist/farm.js +1 -2
  11. package/dist/farm.js.map +1 -1
  12. package/dist/index-CXVebONK.d.ts +36 -0
  13. package/dist/index-CXVebONK.d.ts.map +1 -0
  14. package/dist/index.d.ts +2 -10
  15. package/dist/index.js +2 -3
  16. package/dist/nuxt.d.ts +1 -1
  17. package/dist/nuxt.js +3 -4
  18. package/dist/nuxt.js.map +1 -1
  19. package/dist/plugin-WmJKFcn_.js +595 -0
  20. package/dist/plugin-WmJKFcn_.js.map +1 -0
  21. package/dist/rollup.d.ts +1 -1
  22. package/dist/rollup.js +1 -2
  23. package/dist/rollup.js.map +1 -1
  24. package/dist/rspack.d.ts +1 -1
  25. package/dist/rspack.js +1 -2
  26. package/dist/rspack.js.map +1 -1
  27. package/dist/{vite-Cd6Z9GAd.js → vite-zWsCwvYT.js} +2 -2
  28. package/dist/vite-zWsCwvYT.js.map +1 -0
  29. package/dist/vite.d.ts +1 -1
  30. package/dist/vite.js +2 -3
  31. package/dist/{webpack-yNnIZ6Rq.js → webpack-De3KlGzm.js} +2 -2
  32. package/dist/webpack-De3KlGzm.js.map +1 -0
  33. package/dist/webpack.d.ts +3 -3
  34. package/dist/webpack.js +2 -3
  35. package/package.json +9 -5
  36. package/dist/constants-CnbAL4bY.js +0 -17
  37. package/dist/constants-CnbAL4bY.js.map +0 -1
  38. package/dist/constants.d.ts +0 -17
  39. package/dist/constants.d.ts.map +0 -1
  40. package/dist/constants.js +0 -3
  41. package/dist/index.d.ts.map +0 -1
  42. package/dist/src-BfsicZRP.js +0 -160
  43. package/dist/src-BfsicZRP.js.map +0 -1
  44. package/dist/types-Clt7t35T.d.ts +0 -12
  45. package/dist/types-Clt7t35T.d.ts.map +0 -1
  46. package/dist/types.d.ts +0 -2
  47. package/dist/types.js +0 -1
  48. package/dist/vite-Cd6Z9GAd.js.map +0 -1
  49. package/dist/webpack-yNnIZ6Rq.js.map +0 -1
@@ -0,0 +1,595 @@
1
+ import path from "node:path";
2
+ import { consola } from "consola";
3
+ import { transform } from "esbuild";
4
+ import { createUnplugin } from "unplugin";
5
+ import { glob } from "tinyglobby";
6
+ import { loadExtensionModule, loadModule } from "@styleframe/loader";
7
+ import fs from "node:fs";
8
+ import os from "node:os";
9
+ import { transpile } from "@styleframe/transpiler";
10
+ import { getLicenseKeyFromEnv, validateInstanceLicense } from "@styleframe/license";
11
+ import fs$1 from "node:fs/promises";
12
+ import { createScanner } from "@styleframe/scanner";
13
+
14
+ //#region src/plugin/constants.ts
15
+ const DEFAULT_ENTRY = "./styleframe.config.ts";
16
+ const DEFAULT_OPTIONS = {
17
+ entry: DEFAULT_ENTRY,
18
+ silent: false
19
+ };
20
+ const PLUGIN_NAME = "styleframe";
21
+ const IMPORT_V_PREFIX = "virtual:";
22
+ const ROLLUP_V_PREFIX = "\0";
23
+ const VIRTUAL_CSS_MODULE_ID = `${IMPORT_V_PREFIX}${PLUGIN_NAME}.css`;
24
+ const RESOLVED_VIRTUAL_CSS_MODULE_ID = `${ROLLUP_V_PREFIX}${VIRTUAL_CSS_MODULE_ID}`;
25
+ const VIRTUAL_TS_MODULE_ID = `${IMPORT_V_PREFIX}${PLUGIN_NAME}`;
26
+ const RESOLVED_VIRTUAL_TS_MODULE_ID = `${ROLLUP_V_PREFIX}${VIRTUAL_TS_MODULE_ID}`;
27
+ const RESOLVED_VIRTUAL_EXTENSION_ID = `${ROLLUP_V_PREFIX}${IMPORT_V_PREFIX}${PLUGIN_NAME}:extension`;
28
+ const RESOLVED_VIRTUAL_CONSUMER_ID = `${ROLLUP_V_PREFIX}${IMPORT_V_PREFIX}${PLUGIN_NAME}:consumer`;
29
+ const DEFAULT_IGNORE_PATTERNS = [
30
+ "**/node_modules/**",
31
+ "**/.git/**",
32
+ "**/dist/**",
33
+ "**/build/**",
34
+ "**/.next/**",
35
+ "**/.nuxt/**",
36
+ "**/coverage/**"
37
+ ];
38
+
39
+ //#endregion
40
+ //#region src/plugin/state.ts
41
+ /**
42
+ * Create a new plugin state instance
43
+ */
44
+ function createPluginState(configPath) {
45
+ return {
46
+ globalInstance: null,
47
+ configFile: {
48
+ path: configPath,
49
+ loadOrder: -1,
50
+ exports: /* @__PURE__ */ new Map(),
51
+ lastModified: 0
52
+ },
53
+ files: /* @__PURE__ */ new Map(),
54
+ loadingFiles: /* @__PURE__ */ new Set(),
55
+ initialized: false
56
+ };
57
+ }
58
+ /**
59
+ * Find if an export name is already used by another file.
60
+ * Returns the source file path if collision found, null otherwise.
61
+ */
62
+ function findExportCollision(state, exportName, excludeFile) {
63
+ if (state.configFile && state.configFile.path !== excludeFile && state.configFile.exports.has(exportName)) return state.configFile.path;
64
+ for (const [filePath, fileInfo] of state.files) {
65
+ if (filePath === excludeFile) continue;
66
+ if (fileInfo.exports.has(exportName)) return filePath;
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Reset all state (used when config changes)
72
+ */
73
+ function resetState(state) {
74
+ state.globalInstance = null;
75
+ if (state.configFile) {
76
+ state.configFile.exports.clear();
77
+ state.configFile.lastModified = 0;
78
+ }
79
+ state.files.clear();
80
+ state.loadingFiles.clear();
81
+ state.initialized = false;
82
+ }
83
+
84
+ //#endregion
85
+ //#region src/plugin/discovery.ts
86
+ /**
87
+ * Discover all *.styleframe.ts files in the project
88
+ */
89
+ async function discoverStyleframeFiles(options) {
90
+ return await glob(options.include.length > 0 ? options.include : ["**/*.styleframe.ts"], {
91
+ cwd: options.cwd,
92
+ absolute: true,
93
+ ignore: [...DEFAULT_IGNORE_PATTERNS, ...options.exclude]
94
+ });
95
+ }
96
+ /**
97
+ * Sort files by the specified load order strategy
98
+ */
99
+ function sortByLoadOrder(files, strategy) {
100
+ const sorted = [...files];
101
+ switch (strategy) {
102
+ case "alphabetical": return sorted.sort((a, b) => a.localeCompare(b));
103
+ case "depth-first": return sorted.sort((a, b) => {
104
+ const depthA = a.split(path.sep).length;
105
+ const depthB = b.split(path.sep).length;
106
+ if (depthA !== depthB) return depthA - depthB;
107
+ return a.localeCompare(b);
108
+ });
109
+ default: {
110
+ const _exhaustive = strategy;
111
+ throw new Error(`Unknown load order strategy: ${_exhaustive}`);
112
+ }
113
+ }
114
+ }
115
+
116
+ //#endregion
117
+ //#region src/plugin/errors.ts
118
+ /**
119
+ * Base error class for Styleframe plugin errors
120
+ */
121
+ var StyleframePluginError = class extends Error {
122
+ constructor(message) {
123
+ super(`[styleframe] ${message}`);
124
+ this.name = "StyleframePluginError";
125
+ }
126
+ };
127
+ /**
128
+ * Thrown when two different files export the same name
129
+ */
130
+ var ExportCollisionError = class extends StyleframePluginError {
131
+ constructor(exportName, source1, source2) {
132
+ super(`Export collision: "${exportName}" is exported from both:\n - ${source1}\n - ${source2}\n\nRename one of the exports to resolve this collision.`);
133
+ this.name = "ExportCollisionError";
134
+ }
135
+ };
136
+ /**
137
+ * Thrown when trying to use the global instance before it's initialized
138
+ */
139
+ var GlobalInstanceNotInitializedError = class extends StyleframePluginError {
140
+ constructor() {
141
+ super("Global instance not initialized. Make sure styleframe.config.ts is loaded before *.styleframe.ts files.");
142
+ this.name = "GlobalInstanceNotInitializedError";
143
+ }
144
+ };
145
+ /**
146
+ * Thrown when a circular dependency is detected
147
+ */
148
+ var CircularDependencyError = class extends StyleframePluginError {
149
+ constructor(filePath, chain) {
150
+ super(`Circular dependency detected:\n ${chain.join(" -> ")} -> ${filePath}`);
151
+ this.name = "CircularDependencyError";
152
+ }
153
+ };
154
+
155
+ //#endregion
156
+ //#region src/plugin/global-loader.ts
157
+ const GLOBAL_INSTANCE_KEY = "__STYLEFRAME_GLOBAL_INSTANCE__";
158
+ let extensionShimPath = null;
159
+ /**
160
+ * Get or create the extension shim file.
161
+ * The shim reads the global instance from globalThis and exports it.
162
+ */
163
+ function getExtensionShimPath() {
164
+ if (extensionShimPath && fs.existsSync(extensionShimPath)) return extensionShimPath;
165
+ const shimContent = `
166
+ export function styleframe() {
167
+ const instance = globalThis["${GLOBAL_INSTANCE_KEY}"];
168
+ if (!instance) {
169
+ throw new Error('[styleframe] Global instance not available during loading');
170
+ }
171
+ return instance;
172
+ }
173
+
174
+ export default { styleframe };
175
+ `;
176
+ const tempDir = os.tmpdir();
177
+ extensionShimPath = path.join(tempDir, `styleframe-shim-${process.pid}.mjs`);
178
+ fs.writeFileSync(extensionShimPath, shimContent);
179
+ return extensionShimPath;
180
+ }
181
+ /**
182
+ * Check exports for collisions and throw if found.
183
+ */
184
+ function checkExportCollisions(state, exports, filePath) {
185
+ for (const [name] of exports) {
186
+ const collision = findExportCollision(state, name, filePath);
187
+ if (collision) throw new ExportCollisionError(name, collision, filePath);
188
+ }
189
+ }
190
+ /**
191
+ * Load the config file and initialize the global instance
192
+ */
193
+ async function loadConfigFile(state) {
194
+ if (!state.configFile) throw new Error("[styleframe] Config file not set");
195
+ const configPath = state.configFile.path;
196
+ const { instance, exports } = await loadModule(configPath);
197
+ checkExportCollisions(state, exports, configPath);
198
+ state.globalInstance = instance;
199
+ state.configFile.exports = exports;
200
+ state.configFile.lastModified = Date.now();
201
+ return instance;
202
+ }
203
+ /**
204
+ * Load a single *.styleframe.ts file using the global instance
205
+ */
206
+ async function loadStyleframeFile(state, filePath, loadOrder) {
207
+ if (!state.globalInstance) throw new GlobalInstanceNotInitializedError();
208
+ if (state.loadingFiles.has(filePath)) throw new CircularDependencyError(filePath, [...state.loadingFiles]);
209
+ state.loadingFiles.add(filePath);
210
+ try {
211
+ globalThis[GLOBAL_INSTANCE_KEY] = state.globalInstance;
212
+ const { exports } = await loadExtensionModule(filePath, { alias: { "virtual:styleframe": getExtensionShimPath() } });
213
+ checkExportCollisions(state, exports, filePath);
214
+ state.files.set(filePath, {
215
+ path: filePath,
216
+ loadOrder,
217
+ exports,
218
+ lastModified: Date.now()
219
+ });
220
+ } finally {
221
+ state.loadingFiles.delete(filePath);
222
+ delete globalThis[GLOBAL_INSTANCE_KEY];
223
+ }
224
+ }
225
+ /**
226
+ * Load all styleframe files in order
227
+ */
228
+ async function loadAllStyleframeFiles(state, files) {
229
+ for (let i = 0; i < files.length; i++) {
230
+ const file = files[i];
231
+ if (file) await loadStyleframeFile(state, file, i);
232
+ }
233
+ }
234
+ /**
235
+ * Reload the config and all styleframe files
236
+ */
237
+ async function reloadAll(state, files) {
238
+ resetState(state);
239
+ await loadConfigFile(state);
240
+ await loadAllStyleframeFiles(state, files);
241
+ }
242
+
243
+ //#endregion
244
+ //#region src/plugin/generate/extension-module.ts
245
+ /**
246
+ * Generate the extension module content.
247
+ * This is what *.styleframe.ts files receive when they import from 'virtual:styleframe'.
248
+ * It imports the config directly and returns the same instance.
249
+ */
250
+ function generateExtensionModule(configPath) {
251
+ return `
252
+ import config from '${configPath.replace(/\\/g, "/")}';
253
+
254
+ export function styleframe() {
255
+ return config;
256
+ }
257
+
258
+ export default config;
259
+ `;
260
+ }
261
+
262
+ //#endregion
263
+ //#region src/plugin/generate/consumer-module.ts
264
+ /**
265
+ * Generate the consumer module content using the transpiler.
266
+ * Uses the TypeScript transpiler to generate proper recipe exports with serialized runtime data.
267
+ */
268
+ async function generateConsumerModule(state) {
269
+ if (!state.globalInstance) return `// Styleframe not initialized`;
270
+ return (await transpile(state.globalInstance, { type: "ts" })).files.find((f) => f.name === "index.ts")?.content ?? "";
271
+ }
272
+
273
+ //#endregion
274
+ //#region src/plugin/generate/global-css.ts
275
+ /**
276
+ * Generate global CSS from the global instance.
277
+ */
278
+ async function generateGlobalCSS(state, isBuild, options) {
279
+ if (!state.globalInstance) return { code: "/* Styleframe not initialized */" };
280
+ await validateInstanceLicense(state.globalInstance, {
281
+ licenseKey: getLicenseKeyFromEnv() || "",
282
+ environment: process.env.NODE_ENV || "development",
283
+ isBuild
284
+ });
285
+ const css = (await transpile(state.globalInstance, { type: "css" })).files.map((f) => f.content).join("\n");
286
+ if (!options.silent) consola.success(`[styleframe] Built global CSS successfully.`);
287
+ return { code: css };
288
+ }
289
+
290
+ //#endregion
291
+ //#region src/plugin/generate/dts.ts
292
+ const DEFAULT_OUT_DIR = ".styleframe";
293
+ /**
294
+ * Generates type declarations for virtual:styleframe module.
295
+ * Uses the transpiler's dts mode to generate the types.
296
+ */
297
+ async function generateTypeDeclarations(state, cwd, options = {}, silent = false) {
298
+ if (!state.globalInstance) return;
299
+ const outDir = path.resolve(cwd, options.outDir ?? DEFAULT_OUT_DIR);
300
+ await fs$1.mkdir(outDir, { recursive: true });
301
+ const content = (await transpile(state.globalInstance, { type: "dts" })).files.find((f) => f.name === "index.d.ts")?.content ?? "";
302
+ const outputPath = path.join(outDir, "styleframe.d.ts");
303
+ await fs$1.writeFile(outputPath, content);
304
+ if (!silent) consola.success(`[styleframe] Generated type declarations.`);
305
+ }
306
+
307
+ //#endregion
308
+ //#region src/plugin/scanner.ts
309
+ /**
310
+ * Create a plugin scanner instance.
311
+ *
312
+ * @param contentPatterns Glob patterns for content files to scan
313
+ * @param cwd Working directory for glob resolution
314
+ * @returns Plugin scanner state
315
+ */
316
+ function createPluginScanner(contentPatterns, cwd) {
317
+ return {
318
+ scanner: createScanner({
319
+ content: contentPatterns,
320
+ cwd
321
+ }),
322
+ scannedFiles: /* @__PURE__ */ new Set()
323
+ };
324
+ }
325
+ /**
326
+ * Full scan: scan all content files and register detected utilities.
327
+ *
328
+ * Used at build start and after reloadAll.
329
+ *
330
+ * @param pluginState The plugin global state
331
+ * @param scannerState The scanner state
332
+ * @param options Options for logging
333
+ * @returns Number of newly registered utility values
334
+ */
335
+ async function scanAndRegister(pluginState, scannerState, options) {
336
+ if (!pluginState.globalInstance) return 0;
337
+ const result = await scannerState.scanner.scan();
338
+ scannerState.scannedFiles.clear();
339
+ for (const filePath of result.files.keys()) scannerState.scannedFiles.add(filePath);
340
+ const matches = scannerState.scanner.match(result.allParsed, pluginState.globalInstance.root);
341
+ const registered = registerMatchedUtilities(pluginState, matches);
342
+ if (!options?.silent) {
343
+ const unmatched = matches.filter((m) => m.factory === null);
344
+ if (unmatched.length > 0) {
345
+ const names = [...new Set(unmatched.map((m) => m.parsed.raw))];
346
+ consola.warn(`[styleframe] Scanner found ${unmatched.length} utility class(es) without a matching factory: ${names.join(", ")}`);
347
+ }
348
+ if (registered > 0) consola.info(`[styleframe] Scanner registered ${registered} utility value(s) from ${result.files.size} content file(s).`);
349
+ }
350
+ return registered;
351
+ }
352
+ /**
353
+ * Incremental scan: scan a single file and register any new utilities.
354
+ *
355
+ * Used for HMR when a content file changes.
356
+ *
357
+ * @param pluginState The plugin global state
358
+ * @param scannerState The scanner state
359
+ * @param filePath The changed file path
360
+ * @returns true if new values were registered (CSS needs invalidation)
361
+ */
362
+ async function scanFileAndRegister(pluginState, scannerState, filePath) {
363
+ if (!pluginState.globalInstance) return false;
364
+ scannerState.scanner.invalidate(filePath);
365
+ const fileResult = await scannerState.scanner.scanFile(filePath);
366
+ return registerMatchedUtilities(pluginState, scannerState.scanner.match(fileResult.parsed, pluginState.globalInstance.root)) > 0;
367
+ }
368
+ /**
369
+ * Check if a file is a tracked content file.
370
+ */
371
+ function isContentFile(scannerState, filePath) {
372
+ return scannerState?.scannedFiles.has(filePath) ?? false;
373
+ }
374
+ /**
375
+ * Register utility values detected by the scanner that don't yet exist on the root.
376
+ *
377
+ * For non-arbitrary values, uses the factory's autogenerate function (treating the
378
+ * value key as a token reference). For arbitrary values, registers with the literal
379
+ * CSS value directly.
380
+ *
381
+ * @param pluginState The plugin global state
382
+ * @param matches Scanner match results
383
+ * @returns Number of newly registered values
384
+ */
385
+ function registerMatchedUtilities(pluginState, matches) {
386
+ const unregistered = matches.filter((m) => m.factory !== null && !m.exists);
387
+ if (unregistered.length === 0) return 0;
388
+ const groups = /* @__PURE__ */ new Map();
389
+ for (const match of unregistered) {
390
+ const factory = match.factory;
391
+ const key = `${factory.name}:${match.parsed.value}`;
392
+ const existing = groups.get(key);
393
+ if (!existing) groups.set(key, {
394
+ factory,
395
+ parsed: match.parsed,
396
+ modifiers: [...match.modifierFactories]
397
+ });
398
+ else for (const mod of match.modifierFactories) {
399
+ const modKey = mod.key.join(",");
400
+ if (!existing.modifiers.some((m) => m.key.join(",") === modKey)) existing.modifiers.push(mod);
401
+ }
402
+ }
403
+ let count = 0;
404
+ for (const { factory, parsed, modifiers } of groups.values()) {
405
+ const modifierArgs = modifiers.length > 0 ? modifiers : void 0;
406
+ if (parsed.isArbitrary && parsed.arbitraryValue !== void 0) factory.create({ [parsed.value]: parsed.arbitraryValue }, modifierArgs);
407
+ else {
408
+ const autoGenerated = factory.autogenerate(`@${parsed.value}`);
409
+ factory.create(autoGenerated, modifierArgs);
410
+ }
411
+ count++;
412
+ }
413
+ return count;
414
+ }
415
+
416
+ //#endregion
417
+ //#region src/plugin/index.ts
418
+ const STYLEFRAME_SOURCE_REGEX = /\.styleframe\.ts$/;
419
+ function isStyleframeSourceFile(id) {
420
+ return STYLEFRAME_SOURCE_REGEX.test(id);
421
+ }
422
+ const unpluginFactory = (options = DEFAULT_OPTIONS) => {
423
+ const cwd = process.cwd();
424
+ const entry = options.entry ?? DEFAULT_ENTRY;
425
+ const configPath = path.isAbsolute(entry) ? entry : path.resolve(cwd, entry);
426
+ const state = createPluginState(configPath);
427
+ let isBuildCommand = false;
428
+ let scannerState = null;
429
+ let typeGenTimeout = null;
430
+ const scheduleTypeGeneration = () => {
431
+ if (options.dts?.enabled === false) return;
432
+ if (typeGenTimeout) clearTimeout(typeGenTimeout);
433
+ typeGenTimeout = setTimeout(async () => {
434
+ try {
435
+ await generateTypeDeclarations(state, cwd, options.dts, options.silent);
436
+ } catch (error) {
437
+ consola.error(`[styleframe] Type generation failed:`, error);
438
+ }
439
+ }, 100);
440
+ };
441
+ return {
442
+ name: PLUGIN_NAME,
443
+ enforce: "pre",
444
+ async buildStart() {
445
+ isBuildCommand = process.argv.includes("build");
446
+ if (!options.silent) {
447
+ console.log("");
448
+ consola.info(`[styleframe] Initializing...`);
449
+ }
450
+ try {
451
+ await loadConfigFile(state);
452
+ const sortedFiles = sortByLoadOrder((await discoverStyleframeFiles({
453
+ cwd,
454
+ include: options.include ?? [],
455
+ exclude: options.exclude ?? []
456
+ })).filter((f) => f !== configPath), options.loadOrder ?? "alphabetical");
457
+ await loadAllStyleframeFiles(state, sortedFiles);
458
+ if (options.content?.length) {
459
+ scannerState = createPluginScanner(options.content, cwd);
460
+ await scanAndRegister(state, scannerState, { silent: options.silent });
461
+ }
462
+ this.addWatchFile(configPath);
463
+ for (const file of sortedFiles) this.addWatchFile(file);
464
+ if (scannerState) for (const file of scannerState.scannedFiles) this.addWatchFile(file);
465
+ state.initialized = true;
466
+ if (options.dts?.enabled !== false) await generateTypeDeclarations(state, cwd, options.dts, options.silent);
467
+ if (!options.silent) {
468
+ consola.success(`[styleframe] Initialized with ${sortedFiles.length} styleframe file(s).`);
469
+ console.log("");
470
+ }
471
+ } catch (error) {
472
+ consola.error(`[styleframe] Initialization failed:`, error);
473
+ throw error;
474
+ }
475
+ },
476
+ resolveId(id, importer) {
477
+ if (id === VIRTUAL_TS_MODULE_ID) {
478
+ if (importer && isStyleframeSourceFile(importer)) return RESOLVED_VIRTUAL_EXTENSION_ID;
479
+ return RESOLVED_VIRTUAL_CONSUMER_ID;
480
+ }
481
+ if (id === VIRTUAL_CSS_MODULE_ID) return RESOLVED_VIRTUAL_CSS_MODULE_ID;
482
+ return null;
483
+ },
484
+ async load(id) {
485
+ if (id === RESOLVED_VIRTUAL_EXTENSION_ID) return { code: generateExtensionModule(configPath) };
486
+ if (id === RESOLVED_VIRTUAL_CONSUMER_ID) return { code: await generateConsumerModule(state) };
487
+ if (id === RESOLVED_VIRTUAL_CSS_MODULE_ID) return generateGlobalCSS(state, isBuildCommand, options);
488
+ return null;
489
+ },
490
+ vite: {
491
+ async transform(code, id) {
492
+ if (id === RESOLVED_VIRTUAL_TS_MODULE_ID || id === RESOLVED_VIRTUAL_EXTENSION_ID || id === RESOLVED_VIRTUAL_CONSUMER_ID) {
493
+ const result = await transform(code, {
494
+ loader: "ts",
495
+ format: "esm",
496
+ target: "esnext"
497
+ });
498
+ return {
499
+ code: result.code,
500
+ map: result.map || null
501
+ };
502
+ }
503
+ return null;
504
+ },
505
+ configureServer(server) {
506
+ server.watcher.on("add", async (file) => {
507
+ if (isStyleframeSourceFile(file) && file !== configPath && !state.files.has(file)) try {
508
+ const loadOrder = state.files.size;
509
+ await loadStyleframeFile(state, file, loadOrder);
510
+ if (!options.silent) consola.info(`[styleframe] Discovered new file: ${path.relative(cwd, file)}`);
511
+ if (scannerState) await scanAndRegister(state, scannerState, { silent: options.silent });
512
+ const consumerMod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_CONSUMER_ID);
513
+ const cssMod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_CSS_MODULE_ID);
514
+ if (consumerMod) server.moduleGraph.invalidateModule(consumerMod);
515
+ if (cssMod) server.moduleGraph.invalidateModule(cssMod);
516
+ scheduleTypeGeneration();
517
+ server.ws.send({ type: "full-reload" });
518
+ } catch (error) {
519
+ consola.error(`[styleframe] Failed to load new file: ${file}`, error);
520
+ }
521
+ });
522
+ server.watcher.on("unlink", async (file) => {
523
+ if (state.files.has(file)) {
524
+ if (!options.silent) consola.info(`[styleframe] File removed: ${path.relative(cwd, file)}`);
525
+ try {
526
+ await reloadAll(state, [...state.files.entries()].filter(([filePath]) => filePath !== file).sort(([, a], [, b]) => a.loadOrder - b.loadOrder).map(([filePath]) => filePath));
527
+ if (scannerState) await scanAndRegister(state, scannerState, { silent: options.silent });
528
+ const consumerMod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_CONSUMER_ID);
529
+ const cssMod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_CSS_MODULE_ID);
530
+ if (consumerMod) server.moduleGraph.invalidateModule(consumerMod);
531
+ if (cssMod) server.moduleGraph.invalidateModule(cssMod);
532
+ scheduleTypeGeneration();
533
+ server.ws.send({ type: "full-reload" });
534
+ } catch (error) {
535
+ consola.error(`[styleframe] Failed to reload after file removal: ${file}`, error);
536
+ }
537
+ }
538
+ });
539
+ },
540
+ async handleHotUpdate(ctx) {
541
+ const getModuleById = async (id) => {
542
+ return ctx.server?.moduleGraph.getModuleById(id) ?? await ctx.server?.moduleGraph.getModuleByUrl(id);
543
+ };
544
+ if (ctx.file === configPath) {
545
+ if (!options.silent) consola.info(`[styleframe] Config changed, reloading...`);
546
+ try {
547
+ await reloadAll(state, [...state.files.keys()]);
548
+ if (scannerState) await scanAndRegister(state, scannerState, { silent: options.silent });
549
+ const modulesToInvalidate = [
550
+ await getModuleById(RESOLVED_VIRTUAL_CSS_MODULE_ID),
551
+ await getModuleById(RESOLVED_VIRTUAL_EXTENSION_ID),
552
+ await getModuleById(RESOLVED_VIRTUAL_CONSUMER_ID)
553
+ ].filter(Boolean);
554
+ scheduleTypeGeneration();
555
+ if (modulesToInvalidate.length > 0) return modulesToInvalidate;
556
+ } catch (error) {
557
+ consola.error(`[styleframe] Reload failed:`, error);
558
+ }
559
+ return ctx.modules;
560
+ }
561
+ if (state.files.has(ctx.file)) {
562
+ if (!options.silent) consola.info(`[styleframe] File changed: ${path.relative(cwd, ctx.file)}`);
563
+ try {
564
+ await reloadAll(state, [...state.files.keys()]);
565
+ if (scannerState) await scanAndRegister(state, scannerState, { silent: options.silent });
566
+ const modulesToInvalidate = [await getModuleById(RESOLVED_VIRTUAL_CSS_MODULE_ID), await getModuleById(RESOLVED_VIRTUAL_CONSUMER_ID)].filter(Boolean);
567
+ scheduleTypeGeneration();
568
+ if (modulesToInvalidate.length > 0) return modulesToInvalidate;
569
+ } catch (error) {
570
+ consola.error(`[styleframe] Failed to reload: ${ctx.file}`, error);
571
+ }
572
+ return ctx.modules;
573
+ }
574
+ if (isContentFile(scannerState, ctx.file)) {
575
+ try {
576
+ if (await scanFileAndRegister(state, scannerState, ctx.file)) {
577
+ const cssModule = await getModuleById(RESOLVED_VIRTUAL_CSS_MODULE_ID);
578
+ if (cssModule) return [cssModule, ...ctx.modules];
579
+ }
580
+ } catch (error) {
581
+ consola.error(`[styleframe] Failed to scan content file: ${ctx.file}`, error);
582
+ }
583
+ return ctx.modules;
584
+ }
585
+ return ctx.modules;
586
+ }
587
+ }
588
+ };
589
+ };
590
+ const unplugin = /* @__PURE__ */ createUnplugin(unpluginFactory);
591
+ var plugin_default = unplugin;
592
+
593
+ //#endregion
594
+ export { plugin_default, unplugin, unpluginFactory };
595
+ //# sourceMappingURL=plugin-WmJKFcn_.js.map