@pandacss/config 0.30.2 → 0.32.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.
@@ -1,6 +1,74 @@
1
1
  // src/merge-config.ts
2
2
  import { mergeAndConcat } from "merge-anything";
3
3
 
4
+ // src/merge-hooks.ts
5
+ import { logger } from "@pandacss/logger";
6
+ var mergeHooks = (plugins) => {
7
+ const hooksFns = {};
8
+ plugins.forEach(({ name, hooks }) => {
9
+ Object.entries(hooks ?? {}).forEach(([key, value]) => {
10
+ if (!hooksFns[key]) {
11
+ hooksFns[key] = [];
12
+ }
13
+ hooksFns[key].push([name, value]);
14
+ });
15
+ });
16
+ const mergedHooks = Object.fromEntries(
17
+ Object.entries(hooksFns).map(([key, entries]) => {
18
+ const fns = entries.map(([name, fn]) => tryCatch(name, fn));
19
+ if (key === "cssgen:done") {
20
+ return [key, reducers["cssgen:done"](fns)];
21
+ }
22
+ if (key === "codegen:prepare") {
23
+ return [key, reducers["codegen:prepare"](fns)];
24
+ }
25
+ return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
26
+ })
27
+ );
28
+ return mergedHooks;
29
+ };
30
+ var reducers = {
31
+ "cssgen:done": (fns) => (args) => {
32
+ let content = args.content;
33
+ for (const hookFn of fns) {
34
+ const result = hookFn({ ...args, content, original: args.content });
35
+ if (result !== void 0) {
36
+ content = result;
37
+ }
38
+ }
39
+ return content;
40
+ },
41
+ "codegen:prepare": (fns) => (args) => {
42
+ let artifacts = args.artifacts;
43
+ for (const hookFn of fns) {
44
+ const result = hookFn({ ...args, artifacts, original: args.artifacts });
45
+ if (result) {
46
+ artifacts = result;
47
+ }
48
+ }
49
+ return artifacts;
50
+ }
51
+ };
52
+ var syncHooks = ["context:created", "parser:before", "parser:after", "cssgen:done"];
53
+ var callAllAsync = (...fns) => async (...a) => {
54
+ for (const fn of fns) {
55
+ await fn?.(...a);
56
+ }
57
+ };
58
+ var callAll = (...fns) => (...a) => {
59
+ fns.forEach((fn) => fn?.(...a));
60
+ };
61
+ var tryCatch = (name, fn) => {
62
+ return (...args) => {
63
+ try {
64
+ return fn(...args);
65
+ } catch (e) {
66
+ logger.error("hooks", `The error below comes from the plugin ${name}`);
67
+ console.error(e);
68
+ }
69
+ };
70
+ };
71
+
4
72
  // src/utils.ts
5
73
  import { traverse } from "@pandacss/shared";
6
74
  var isObject = (v) => Object.prototype.toString.call(v) === "[object Object]";
@@ -87,6 +155,11 @@ var compact = (obj) => {
87
155
  }, {});
88
156
  };
89
157
  function mergeConfigs(configs) {
158
+ const [userConfig] = configs;
159
+ const pluginHooks = userConfig.plugins ?? [];
160
+ if (userConfig.hooks) {
161
+ pluginHooks.push({ name: "__panda.config__", hooks: userConfig.hooks });
162
+ }
90
163
  const mergedResult = assign(
91
164
  {
92
165
  conditions: mergeExtensions(configs.map((config) => config.conditions ?? {})),
@@ -94,7 +167,8 @@ function mergeConfigs(configs) {
94
167
  patterns: mergeExtensions(configs.map((config) => config.patterns ?? {})),
95
168
  utilities: mergeExtensions(configs.map((config) => config.utilities ?? {})),
96
169
  globalCss: mergeExtensions(configs.map((config) => config.globalCss ?? {})),
97
- staticCss: mergeExtensions(configs.map((config) => config.staticCss ?? {}))
170
+ staticCss: mergeExtensions(configs.map((config) => config.staticCss ?? {})),
171
+ hooks: mergeHooks(pluginHooks)
98
172
  },
99
173
  ...configs
100
174
  );
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _pandacss_types from '@pandacss/types';
2
- import { Config, ConfigTsOptions } from '@pandacss/types';
2
+ import { Config, ConfigTsOptions, LoadConfigResult } from '@pandacss/types';
3
3
  export { diffConfigs } from './diff-config.mjs';
4
4
  import { P as PathMapping } from './ts-config-paths-2lh9mwzL.mjs';
5
5
  export { c as convertTsPathsToRegexes } from './ts-config-paths-2lh9mwzL.mjs';
@@ -49,4 +49,11 @@ declare function getResolvedConfig(config: ExtendableConfig, cwd: string): Promi
49
49
  */
50
50
  declare function loadConfig(options: ConfigFileOptions): Promise<_pandacss_types.LoadConfigResult>;
51
51
 
52
- export { type BundleConfigResult, type GetDepsOptions, bundleConfig, findConfig, getConfigDependencies, getResolvedConfig, loadConfig };
52
+ /**
53
+ * Resolve the final config (including presets)
54
+ * @pandacss/preset-base: ALWAYS included if NOT using eject: true
55
+ * @pandacss/preset-panda: only included by default if no presets
56
+ */
57
+ declare function resolveConfig(result: BundleConfigResult, cwd: string): Promise<LoadConfigResult>;
58
+
59
+ export { type BundleConfigResult, type GetDepsOptions, bundleConfig, findConfig, getConfigDependencies, getResolvedConfig, loadConfig, resolveConfig };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _pandacss_types from '@pandacss/types';
2
- import { Config, ConfigTsOptions } from '@pandacss/types';
2
+ import { Config, ConfigTsOptions, LoadConfigResult } from '@pandacss/types';
3
3
  export { diffConfigs } from './diff-config.js';
4
4
  import { P as PathMapping } from './ts-config-paths-2lh9mwzL.js';
5
5
  export { c as convertTsPathsToRegexes } from './ts-config-paths-2lh9mwzL.js';
@@ -49,4 +49,11 @@ declare function getResolvedConfig(config: ExtendableConfig, cwd: string): Promi
49
49
  */
50
50
  declare function loadConfig(options: ConfigFileOptions): Promise<_pandacss_types.LoadConfigResult>;
51
51
 
52
- export { type BundleConfigResult, type GetDepsOptions, bundleConfig, findConfig, getConfigDependencies, getResolvedConfig, loadConfig };
52
+ /**
53
+ * Resolve the final config (including presets)
54
+ * @pandacss/preset-base: ALWAYS included if NOT using eject: true
55
+ * @pandacss/preset-panda: only included by default if no presets
56
+ */
57
+ declare function resolveConfig(result: BundleConfigResult, cwd: string): Promise<LoadConfigResult>;
58
+
59
+ export { type BundleConfigResult, type GetDepsOptions, bundleConfig, findConfig, getConfigDependencies, getResolvedConfig, loadConfig, resolveConfig };
package/dist/index.js CHANGED
@@ -37,7 +37,8 @@ __export(src_exports, {
37
37
  getConfigDependencies: () => getConfigDependencies,
38
38
  getResolvedConfig: () => getResolvedConfig,
39
39
  loadConfig: () => loadConfig,
40
- mergeConfigs: () => mergeConfigs
40
+ mergeConfigs: () => mergeConfigs,
41
+ resolveConfig: () => resolveConfig
41
42
  });
42
43
  module.exports = __toCommonJS(src_exports);
43
44
 
@@ -375,6 +376,74 @@ function getConfigDependencies(filePath, tsOptions = { pathMappings: [] }, compi
375
376
  // src/merge-config.ts
376
377
  var import_merge_anything = require("merge-anything");
377
378
 
379
+ // src/merge-hooks.ts
380
+ var import_logger2 = require("@pandacss/logger");
381
+ var mergeHooks = (plugins) => {
382
+ const hooksFns = {};
383
+ plugins.forEach(({ name, hooks }) => {
384
+ Object.entries(hooks ?? {}).forEach(([key, value]) => {
385
+ if (!hooksFns[key]) {
386
+ hooksFns[key] = [];
387
+ }
388
+ hooksFns[key].push([name, value]);
389
+ });
390
+ });
391
+ const mergedHooks = Object.fromEntries(
392
+ Object.entries(hooksFns).map(([key, entries]) => {
393
+ const fns = entries.map(([name, fn]) => tryCatch(name, fn));
394
+ if (key === "cssgen:done") {
395
+ return [key, reducers["cssgen:done"](fns)];
396
+ }
397
+ if (key === "codegen:prepare") {
398
+ return [key, reducers["codegen:prepare"](fns)];
399
+ }
400
+ return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
401
+ })
402
+ );
403
+ return mergedHooks;
404
+ };
405
+ var reducers = {
406
+ "cssgen:done": (fns) => (args) => {
407
+ let content = args.content;
408
+ for (const hookFn of fns) {
409
+ const result = hookFn({ ...args, content, original: args.content });
410
+ if (result !== void 0) {
411
+ content = result;
412
+ }
413
+ }
414
+ return content;
415
+ },
416
+ "codegen:prepare": (fns) => (args) => {
417
+ let artifacts = args.artifacts;
418
+ for (const hookFn of fns) {
419
+ const result = hookFn({ ...args, artifacts, original: args.artifacts });
420
+ if (result) {
421
+ artifacts = result;
422
+ }
423
+ }
424
+ return artifacts;
425
+ }
426
+ };
427
+ var syncHooks = ["context:created", "parser:before", "parser:after", "cssgen:done"];
428
+ var callAllAsync = (...fns) => async (...a) => {
429
+ for (const fn of fns) {
430
+ await fn?.(...a);
431
+ }
432
+ };
433
+ var callAll = (...fns) => (...a) => {
434
+ fns.forEach((fn) => fn?.(...a));
435
+ };
436
+ var tryCatch = (name, fn) => {
437
+ return (...args) => {
438
+ try {
439
+ return fn(...args);
440
+ } catch (e) {
441
+ import_logger2.logger.error("hooks", `The error below comes from the plugin ${name}`);
442
+ console.error(e);
443
+ }
444
+ };
445
+ };
446
+
378
447
  // src/utils.ts
379
448
  var import_shared4 = require("@pandacss/shared");
380
449
  var isObject = (v) => Object.prototype.toString.call(v) === "[object Object]";
@@ -461,6 +530,11 @@ var compact = (obj) => {
461
530
  }, {});
462
531
  };
463
532
  function mergeConfigs(configs2) {
533
+ const [userConfig] = configs2;
534
+ const pluginHooks = userConfig.plugins ?? [];
535
+ if (userConfig.hooks) {
536
+ pluginHooks.push({ name: "__panda.config__", hooks: userConfig.hooks });
537
+ }
464
538
  const mergedResult = assign(
465
539
  {
466
540
  conditions: mergeExtensions(configs2.map((config) => config.conditions ?? {})),
@@ -468,7 +542,8 @@ function mergeConfigs(configs2) {
468
542
  patterns: mergeExtensions(configs2.map((config) => config.patterns ?? {})),
469
543
  utilities: mergeExtensions(configs2.map((config) => config.utilities ?? {})),
470
544
  globalCss: mergeExtensions(configs2.map((config) => config.globalCss ?? {})),
471
- staticCss: mergeExtensions(configs2.map((config) => config.staticCss ?? {}))
545
+ staticCss: mergeExtensions(configs2.map((config) => config.staticCss ?? {})),
546
+ hooks: mergeHooks(pluginHooks)
472
547
  },
473
548
  ...configs2
474
549
  );
@@ -495,6 +570,7 @@ async function getResolvedConfig(config, cwd) {
495
570
  }
496
571
 
497
572
  // src/resolve-config.ts
573
+ var import_logger4 = require("@pandacss/logger");
498
574
  var import_shared8 = require("@pandacss/shared");
499
575
 
500
576
  // src/bundled-preset.ts
@@ -512,7 +588,7 @@ var getBundledPreset = (preset) => {
512
588
  };
513
589
 
514
590
  // src/validate-config.ts
515
- var import_logger2 = require("@pandacss/logger");
591
+ var import_logger3 = require("@pandacss/logger");
516
592
  var import_shared7 = require("@pandacss/shared");
517
593
 
518
594
  // src/validation/validate-artifact.ts
@@ -592,17 +668,11 @@ var validateRecipes = (options) => {
592
668
  // src/validation/validate-tokens.ts
593
669
  var import_shared6 = require("@pandacss/shared");
594
670
 
595
- // src/validation/get-final-paths.ts
596
- var getFinalPaths = (paths) => {
597
- paths.forEach((path2) => {
598
- paths.forEach((potentialExtension) => {
599
- if (potentialExtension.startsWith(path2 + ".")) {
600
- paths.delete(path2);
601
- }
602
- });
603
- });
604
- return paths;
605
- };
671
+ // src/validation/utils.ts
672
+ var isValidToken = (token) => Object.hasOwnProperty.call(token, "value");
673
+ var isTokenReference = (value) => typeof value === "string" && value.startsWith("{");
674
+ var formatPath = (path2) => path2;
675
+ var SEP = ".";
606
676
 
607
677
  // src/validation/validate-token-references.ts
608
678
  var validateTokenReferences = (valueAtPath, refsByPath, addError) => {
@@ -612,17 +682,23 @@ var validateTokenReferences = (valueAtPath, refsByPath, addError) => {
612
682
  }
613
683
  const stack = [path2];
614
684
  while (stack.length > 0) {
615
- const current = stack.pop();
616
- const value = valueAtPath.get(current);
685
+ const currentPath = stack.pop();
686
+ const value = valueAtPath.get(currentPath);
617
687
  if (!value) {
618
- addError("tokens", `Missing token: \`${current}\` used in \`config.semanticTokens.${path2}\``);
688
+ addError("tokens", `Missing token: \`${currentPath}\` used in \`config.semanticTokens.${path2}\``);
619
689
  }
620
- const deps = refsByPath.get(current);
690
+ if (isTokenReference(value) && !refsByPath.has(value)) {
691
+ addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
692
+ }
693
+ const deps = refsByPath.get(currentPath);
621
694
  if (!deps)
622
695
  continue;
623
696
  for (const transitiveDep of deps) {
624
697
  if (path2 === transitiveDep) {
625
- addError("tokens", `Circular token reference: \`${transitiveDep}\` -> \`${current}\` -> ... -> \`${path2}\``);
698
+ addError(
699
+ "tokens",
700
+ `Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path2}\``
701
+ );
626
702
  break;
627
703
  }
628
704
  stack.push(transitiveDep);
@@ -642,43 +718,65 @@ var validateTokens = (options) => {
642
718
  return;
643
719
  const { tokenNames, semanticTokenNames, valueAtPath, refsByPath } = tokens2;
644
720
  if (theme.tokens) {
645
- (0, import_shared6.traverse)(theme.tokens, (node) => {
646
- if (node.depth >= 1) {
647
- tokenNames.add(node.path);
648
- valueAtPath.set(node.path, node.value);
721
+ const tokenPaths = /* @__PURE__ */ new Set();
722
+ (0, import_shared6.walkObject)(
723
+ theme.tokens,
724
+ (value, paths) => {
725
+ const path2 = paths.join(SEP);
726
+ tokenNames.add(path2);
727
+ tokenPaths.add(path2);
728
+ valueAtPath.set(path2, value);
729
+ },
730
+ {
731
+ stop: isValidToken
649
732
  }
650
- });
651
- const finalPaths = getFinalPaths(tokenNames);
652
- finalPaths.forEach((path2) => {
653
- if (!path2.includes("value")) {
654
- addError("tokens", `Token paths must end with 'value': \`theme.tokens.${path2}\``);
733
+ );
734
+ tokenPaths.forEach((path2) => {
735
+ const value = valueAtPath.get(path2);
736
+ const formattedPath = formatPath(path2);
737
+ if (!isValidToken(value)) {
738
+ addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
739
+ return;
655
740
  }
656
- const atPath = valueAtPath.get(path2);
657
- if (typeof atPath === "string" && atPath.startsWith("{")) {
658
- refsByPath.set(path2, /* @__PURE__ */ new Set([]));
741
+ if (isTokenReference(value)) {
742
+ refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
659
743
  }
660
744
  });
661
745
  }
662
746
  if (theme.semanticTokens) {
663
- (0, import_shared6.traverse)(theme.semanticTokens, (node) => {
664
- if (node.depth >= 1) {
665
- semanticTokenNames.add(node.path);
666
- valueAtPath.set(node.path, node.value);
667
- if (typeof node.value === "string" && node.value.startsWith("{") && node.path.includes("value")) {
668
- const tokenPath = node.path.split(".value")[0];
669
- if (!refsByPath.has(tokenPath)) {
670
- refsByPath.set(tokenPath, /* @__PURE__ */ new Set());
747
+ const tokenPaths = /* @__PURE__ */ new Set();
748
+ (0, import_shared6.walkObject)(
749
+ theme.semanticTokens,
750
+ (value, paths) => {
751
+ const path2 = paths.join(SEP);
752
+ semanticTokenNames.add(path2);
753
+ valueAtPath.set(path2, value);
754
+ tokenPaths.add(path2);
755
+ if (!isValidToken(value))
756
+ return;
757
+ (0, import_shared6.walkObject)(value, (itemValue) => {
758
+ if (isTokenReference(itemValue)) {
759
+ const formattedPath = formatPath(path2);
760
+ if (!refsByPath.has(formattedPath)) {
761
+ refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
762
+ }
763
+ const references = refsByPath.get(formattedPath);
764
+ if (!references)
765
+ return;
766
+ const reference = itemValue.slice(1, -1);
767
+ references.add(reference);
671
768
  }
672
- const values = refsByPath.get(tokenPath);
673
- const tokenRef = node.value.slice(1, -1);
674
- values.add(tokenRef);
675
- }
769
+ });
770
+ },
771
+ {
772
+ stop: isValidToken
676
773
  }
677
- });
678
- const finalPaths = getFinalPaths(semanticTokenNames);
679
- finalPaths.forEach((path2) => {
680
- if (!path2.includes("value")) {
681
- addError("tokens", `Semantic token paths must contain 'value': \`theme.semanticTokens.${path2}\``);
774
+ );
775
+ tokenPaths.forEach((path2) => {
776
+ const formattedPath = formatPath(path2);
777
+ const value = valueAtPath.get(path2);
778
+ if (!(0, import_shared6.isObject)(value) && !path2.includes("value")) {
779
+ addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
682
780
  }
683
781
  });
684
782
  validateTokenReferences(valueAtPath, refsByPath, addError);
@@ -691,11 +789,7 @@ var validateConfig = (config) => {
691
789
  return;
692
790
  const warnings = /* @__PURE__ */ new Set();
693
791
  const addError = (scope, message) => {
694
- if (config.validation === "warn") {
695
- warnings.add(`[${scope}]: ` + message);
696
- } else {
697
- throw new import_shared7.PandaError("CONFIG_ERROR", `[${scope}]: ` + message);
698
- }
792
+ warnings.add(`[${scope}] ` + message);
699
793
  };
700
794
  validateBreakpoints(config.theme?.breakpoints, addError);
701
795
  validateConditions(config.conditions, addError);
@@ -717,12 +811,13 @@ var validateConfig = (config) => {
717
811
  validatePatterns(config.patterns, artifacts);
718
812
  validateArtifactNames(artifacts, addError);
719
813
  if (warnings.size) {
720
- import_logger2.logger.warn(
721
- "config",
722
- `\u26A0\uFE0F Invalid config:
814
+ const errors = `\u26A0\uFE0F Invalid config:
723
815
  ${Array.from(warnings).map((err) => "- " + err).join("\n")}
724
- `
725
- );
816
+ `;
817
+ if (config.validation === "error") {
818
+ throw new import_shared7.PandaError("CONFIG_ERROR", errors);
819
+ }
820
+ import_logger3.logger.warn("config", errors);
726
821
  return warnings;
727
822
  }
728
823
  };
@@ -741,12 +836,15 @@ async function resolveConfig(result, cwd) {
741
836
  presets.add(import_preset_panda.preset);
742
837
  }
743
838
  result.config.presets = Array.from(presets);
744
- const config = await getResolvedConfig(result.config, cwd);
745
- validateConfig(config);
746
- const { hooks = {} } = result.config;
839
+ const mergedConfig = await getResolvedConfig(result.config, cwd);
840
+ const hooks = mergedConfig.hooks ?? {};
841
+ if (mergedConfig.logLevel) {
842
+ import_logger4.logger.level = mergedConfig.logLevel;
843
+ }
844
+ validateConfig(mergedConfig);
747
845
  const loadConfigResult = {
748
846
  ...result,
749
- config
847
+ config: mergedConfig
750
848
  };
751
849
  if (hooks["config:resolved"]) {
752
850
  const result2 = await hooks["config:resolved"]({
@@ -778,5 +876,6 @@ async function loadConfig(options) {
778
876
  getConfigDependencies,
779
877
  getResolvedConfig,
780
878
  loadConfig,
781
- mergeConfigs
879
+ mergeConfigs,
880
+ resolveConfig
782
881
  });
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  mergeConfigs,
3
3
  utils
4
- } from "./chunk-XG56JT63.mjs";
4
+ } from "./chunk-MT2A5AKG.mjs";
5
5
  import {
6
6
  diffConfigs
7
7
  } from "./chunk-5YOGLJPA.mjs";
@@ -214,6 +214,7 @@ async function getResolvedConfig(config, cwd) {
214
214
  }
215
215
 
216
216
  // src/resolve-config.ts
217
+ import { logger as logger3 } from "@pandacss/logger";
217
218
  import { parseJson, stringifyJson } from "@pandacss/shared";
218
219
 
219
220
  // src/bundled-preset.ts
@@ -309,19 +310,13 @@ var validateRecipes = (options) => {
309
310
  };
310
311
 
311
312
  // src/validation/validate-tokens.ts
312
- import { traverse } from "@pandacss/shared";
313
+ import { isObject, walkObject } from "@pandacss/shared";
313
314
 
314
- // src/validation/get-final-paths.ts
315
- var getFinalPaths = (paths) => {
316
- paths.forEach((path2) => {
317
- paths.forEach((potentialExtension) => {
318
- if (potentialExtension.startsWith(path2 + ".")) {
319
- paths.delete(path2);
320
- }
321
- });
322
- });
323
- return paths;
324
- };
315
+ // src/validation/utils.ts
316
+ var isValidToken = (token) => Object.hasOwnProperty.call(token, "value");
317
+ var isTokenReference = (value) => typeof value === "string" && value.startsWith("{");
318
+ var formatPath = (path2) => path2;
319
+ var SEP = ".";
325
320
 
326
321
  // src/validation/validate-token-references.ts
327
322
  var validateTokenReferences = (valueAtPath, refsByPath, addError) => {
@@ -331,17 +326,23 @@ var validateTokenReferences = (valueAtPath, refsByPath, addError) => {
331
326
  }
332
327
  const stack = [path2];
333
328
  while (stack.length > 0) {
334
- const current = stack.pop();
335
- const value = valueAtPath.get(current);
329
+ const currentPath = stack.pop();
330
+ const value = valueAtPath.get(currentPath);
336
331
  if (!value) {
337
- addError("tokens", `Missing token: \`${current}\` used in \`config.semanticTokens.${path2}\``);
332
+ addError("tokens", `Missing token: \`${currentPath}\` used in \`config.semanticTokens.${path2}\``);
333
+ }
334
+ if (isTokenReference(value) && !refsByPath.has(value)) {
335
+ addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
338
336
  }
339
- const deps = refsByPath.get(current);
337
+ const deps = refsByPath.get(currentPath);
340
338
  if (!deps)
341
339
  continue;
342
340
  for (const transitiveDep of deps) {
343
341
  if (path2 === transitiveDep) {
344
- addError("tokens", `Circular token reference: \`${transitiveDep}\` -> \`${current}\` -> ... -> \`${path2}\``);
342
+ addError(
343
+ "tokens",
344
+ `Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path2}\``
345
+ );
345
346
  break;
346
347
  }
347
348
  stack.push(transitiveDep);
@@ -361,43 +362,65 @@ var validateTokens = (options) => {
361
362
  return;
362
363
  const { tokenNames, semanticTokenNames, valueAtPath, refsByPath } = tokens;
363
364
  if (theme.tokens) {
364
- traverse(theme.tokens, (node) => {
365
- if (node.depth >= 1) {
366
- tokenNames.add(node.path);
367
- valueAtPath.set(node.path, node.value);
365
+ const tokenPaths = /* @__PURE__ */ new Set();
366
+ walkObject(
367
+ theme.tokens,
368
+ (value, paths) => {
369
+ const path2 = paths.join(SEP);
370
+ tokenNames.add(path2);
371
+ tokenPaths.add(path2);
372
+ valueAtPath.set(path2, value);
373
+ },
374
+ {
375
+ stop: isValidToken
368
376
  }
369
- });
370
- const finalPaths = getFinalPaths(tokenNames);
371
- finalPaths.forEach((path2) => {
372
- if (!path2.includes("value")) {
373
- addError("tokens", `Token paths must end with 'value': \`theme.tokens.${path2}\``);
377
+ );
378
+ tokenPaths.forEach((path2) => {
379
+ const value = valueAtPath.get(path2);
380
+ const formattedPath = formatPath(path2);
381
+ if (!isValidToken(value)) {
382
+ addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
383
+ return;
374
384
  }
375
- const atPath = valueAtPath.get(path2);
376
- if (typeof atPath === "string" && atPath.startsWith("{")) {
377
- refsByPath.set(path2, /* @__PURE__ */ new Set([]));
385
+ if (isTokenReference(value)) {
386
+ refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
378
387
  }
379
388
  });
380
389
  }
381
390
  if (theme.semanticTokens) {
382
- traverse(theme.semanticTokens, (node) => {
383
- if (node.depth >= 1) {
384
- semanticTokenNames.add(node.path);
385
- valueAtPath.set(node.path, node.value);
386
- if (typeof node.value === "string" && node.value.startsWith("{") && node.path.includes("value")) {
387
- const tokenPath = node.path.split(".value")[0];
388
- if (!refsByPath.has(tokenPath)) {
389
- refsByPath.set(tokenPath, /* @__PURE__ */ new Set());
391
+ const tokenPaths = /* @__PURE__ */ new Set();
392
+ walkObject(
393
+ theme.semanticTokens,
394
+ (value, paths) => {
395
+ const path2 = paths.join(SEP);
396
+ semanticTokenNames.add(path2);
397
+ valueAtPath.set(path2, value);
398
+ tokenPaths.add(path2);
399
+ if (!isValidToken(value))
400
+ return;
401
+ walkObject(value, (itemValue) => {
402
+ if (isTokenReference(itemValue)) {
403
+ const formattedPath = formatPath(path2);
404
+ if (!refsByPath.has(formattedPath)) {
405
+ refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
406
+ }
407
+ const references = refsByPath.get(formattedPath);
408
+ if (!references)
409
+ return;
410
+ const reference = itemValue.slice(1, -1);
411
+ references.add(reference);
390
412
  }
391
- const values = refsByPath.get(tokenPath);
392
- const tokenRef = node.value.slice(1, -1);
393
- values.add(tokenRef);
394
- }
413
+ });
414
+ },
415
+ {
416
+ stop: isValidToken
395
417
  }
396
- });
397
- const finalPaths = getFinalPaths(semanticTokenNames);
398
- finalPaths.forEach((path2) => {
399
- if (!path2.includes("value")) {
400
- addError("tokens", `Semantic token paths must contain 'value': \`theme.semanticTokens.${path2}\``);
418
+ );
419
+ tokenPaths.forEach((path2) => {
420
+ const formattedPath = formatPath(path2);
421
+ const value = valueAtPath.get(path2);
422
+ if (!isObject(value) && !path2.includes("value")) {
423
+ addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
401
424
  }
402
425
  });
403
426
  validateTokenReferences(valueAtPath, refsByPath, addError);
@@ -410,11 +433,7 @@ var validateConfig = (config) => {
410
433
  return;
411
434
  const warnings = /* @__PURE__ */ new Set();
412
435
  const addError = (scope, message) => {
413
- if (config.validation === "warn") {
414
- warnings.add(`[${scope}]: ` + message);
415
- } else {
416
- throw new PandaError3("CONFIG_ERROR", `[${scope}]: ` + message);
417
- }
436
+ warnings.add(`[${scope}] ` + message);
418
437
  };
419
438
  validateBreakpoints(config.theme?.breakpoints, addError);
420
439
  validateConditions(config.conditions, addError);
@@ -436,12 +455,13 @@ var validateConfig = (config) => {
436
455
  validatePatterns(config.patterns, artifacts);
437
456
  validateArtifactNames(artifacts, addError);
438
457
  if (warnings.size) {
439
- logger2.warn(
440
- "config",
441
- `\u26A0\uFE0F Invalid config:
458
+ const errors = `\u26A0\uFE0F Invalid config:
442
459
  ${Array.from(warnings).map((err) => "- " + err).join("\n")}
443
- `
444
- );
460
+ `;
461
+ if (config.validation === "error") {
462
+ throw new PandaError3("CONFIG_ERROR", errors);
463
+ }
464
+ logger2.warn("config", errors);
445
465
  return warnings;
446
466
  }
447
467
  };
@@ -460,12 +480,15 @@ async function resolveConfig(result, cwd) {
460
480
  presets.add(presetPanda);
461
481
  }
462
482
  result.config.presets = Array.from(presets);
463
- const config = await getResolvedConfig(result.config, cwd);
464
- validateConfig(config);
465
- const { hooks = {} } = result.config;
483
+ const mergedConfig = await getResolvedConfig(result.config, cwd);
484
+ const hooks = mergedConfig.hooks ?? {};
485
+ if (mergedConfig.logLevel) {
486
+ logger3.level = mergedConfig.logLevel;
487
+ }
488
+ validateConfig(mergedConfig);
466
489
  const loadConfigResult = {
467
490
  ...result,
468
- config
491
+ config: mergedConfig
469
492
  };
470
493
  if (hooks["config:resolved"]) {
471
494
  const result2 = await hooks["config:resolved"]({
@@ -496,5 +519,6 @@ export {
496
519
  getConfigDependencies,
497
520
  getResolvedConfig,
498
521
  loadConfig,
499
- mergeConfigs
522
+ mergeConfigs,
523
+ resolveConfig
500
524
  };
@@ -25,6 +25,74 @@ __export(merge_config_exports, {
25
25
  module.exports = __toCommonJS(merge_config_exports);
26
26
  var import_merge_anything = require("merge-anything");
27
27
 
28
+ // src/merge-hooks.ts
29
+ var import_logger = require("@pandacss/logger");
30
+ var mergeHooks = (plugins) => {
31
+ const hooksFns = {};
32
+ plugins.forEach(({ name, hooks }) => {
33
+ Object.entries(hooks ?? {}).forEach(([key, value]) => {
34
+ if (!hooksFns[key]) {
35
+ hooksFns[key] = [];
36
+ }
37
+ hooksFns[key].push([name, value]);
38
+ });
39
+ });
40
+ const mergedHooks = Object.fromEntries(
41
+ Object.entries(hooksFns).map(([key, entries]) => {
42
+ const fns = entries.map(([name, fn]) => tryCatch(name, fn));
43
+ if (key === "cssgen:done") {
44
+ return [key, reducers["cssgen:done"](fns)];
45
+ }
46
+ if (key === "codegen:prepare") {
47
+ return [key, reducers["codegen:prepare"](fns)];
48
+ }
49
+ return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
50
+ })
51
+ );
52
+ return mergedHooks;
53
+ };
54
+ var reducers = {
55
+ "cssgen:done": (fns) => (args) => {
56
+ let content = args.content;
57
+ for (const hookFn of fns) {
58
+ const result = hookFn({ ...args, content, original: args.content });
59
+ if (result !== void 0) {
60
+ content = result;
61
+ }
62
+ }
63
+ return content;
64
+ },
65
+ "codegen:prepare": (fns) => (args) => {
66
+ let artifacts = args.artifacts;
67
+ for (const hookFn of fns) {
68
+ const result = hookFn({ ...args, artifacts, original: args.artifacts });
69
+ if (result) {
70
+ artifacts = result;
71
+ }
72
+ }
73
+ return artifacts;
74
+ }
75
+ };
76
+ var syncHooks = ["context:created", "parser:before", "parser:after", "cssgen:done"];
77
+ var callAllAsync = (...fns) => async (...a) => {
78
+ for (const fn of fns) {
79
+ await fn?.(...a);
80
+ }
81
+ };
82
+ var callAll = (...fns) => (...a) => {
83
+ fns.forEach((fn) => fn?.(...a));
84
+ };
85
+ var tryCatch = (name, fn) => {
86
+ return (...args) => {
87
+ try {
88
+ return fn(...args);
89
+ } catch (e) {
90
+ import_logger.logger.error("hooks", `The error below comes from the plugin ${name}`);
91
+ console.error(e);
92
+ }
93
+ };
94
+ };
95
+
28
96
  // src/utils.ts
29
97
  var import_shared = require("@pandacss/shared");
30
98
  var isObject = (v) => Object.prototype.toString.call(v) === "[object Object]";
@@ -98,6 +166,11 @@ var compact = (obj) => {
98
166
  }, {});
99
167
  };
100
168
  function mergeConfigs(configs) {
169
+ const [userConfig] = configs;
170
+ const pluginHooks = userConfig.plugins ?? [];
171
+ if (userConfig.hooks) {
172
+ pluginHooks.push({ name: "__panda.config__", hooks: userConfig.hooks });
173
+ }
101
174
  const mergedResult = assign(
102
175
  {
103
176
  conditions: mergeExtensions(configs.map((config) => config.conditions ?? {})),
@@ -105,7 +178,8 @@ function mergeConfigs(configs) {
105
178
  patterns: mergeExtensions(configs.map((config) => config.patterns ?? {})),
106
179
  utilities: mergeExtensions(configs.map((config) => config.utilities ?? {})),
107
180
  globalCss: mergeExtensions(configs.map((config) => config.globalCss ?? {})),
108
- staticCss: mergeExtensions(configs.map((config) => config.staticCss ?? {}))
181
+ staticCss: mergeExtensions(configs.map((config) => config.staticCss ?? {})),
182
+ hooks: mergeHooks(pluginHooks)
109
183
  },
110
184
  ...configs
111
185
  );
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  mergeConfigs
3
- } from "./chunk-XG56JT63.mjs";
3
+ } from "./chunk-MT2A5AKG.mjs";
4
4
  export {
5
5
  mergeConfigs
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pandacss/config",
3
- "version": "0.30.2",
3
+ "version": "0.32.0",
4
4
  "description": "Find and load panda config",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -60,15 +60,15 @@
60
60
  ],
61
61
  "dependencies": {
62
62
  "bundle-n-require": "1.1.1",
63
- "escalade": "3.1.1",
63
+ "escalade": "3.1.2",
64
64
  "merge-anything": "5.1.7",
65
65
  "microdiff": "1.3.2",
66
66
  "typescript": "5.3.3",
67
- "@pandacss/logger": "0.30.2",
68
- "@pandacss/preset-base": "0.30.2",
69
- "@pandacss/preset-panda": "0.30.2",
70
- "@pandacss/shared": "0.30.2",
71
- "@pandacss/types": "0.30.2"
67
+ "@pandacss/logger": "0.32.0",
68
+ "@pandacss/preset-base": "0.32.0",
69
+ "@pandacss/preset-panda": "0.32.0",
70
+ "@pandacss/shared": "0.32.0",
71
+ "@pandacss/types": "0.32.0"
72
72
  },
73
73
  "devDependencies": {
74
74
  "pkg-types": "1.0.3"