@pandacss/config 0.28.0 → 0.29.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.
@@ -27,11 +27,37 @@ function createMatcher(id, patterns) {
27
27
  }
28
28
 
29
29
  // src/config-deps.ts
30
- var all = ["outdir", "forceConsistentTypeExtension", "outExtension"];
31
- var format = ["syntax", "hash", "prefix", "separator"];
32
- var tokens = ["utilities", "conditions", "theme.tokens", "theme.semanticTokens", "theme.breakpoints"];
30
+ var all = [
31
+ "clean",
32
+ "cwd",
33
+ "eject",
34
+ "outdir",
35
+ "forceConsistentTypeExtension",
36
+ "outExtension",
37
+ "emitPackage",
38
+ "emitTokensOnly",
39
+ "presets",
40
+ "hooks"
41
+ ];
42
+ var format = [
43
+ "syntax",
44
+ "hash",
45
+ "prefix",
46
+ "separator",
47
+ "strictTokens",
48
+ "strictPropertyValues",
49
+ "shorthands"
50
+ ];
51
+ var tokens = [
52
+ "utilities",
53
+ "conditions",
54
+ "theme.tokens",
55
+ "theme.semanticTokens",
56
+ "theme.breakpoints",
57
+ "theme.containerNames",
58
+ "theme.containerSizes"
59
+ ];
33
60
  var jsx = ["jsxFramework", "jsxFactory", "jsxStyleProps", "syntax"];
34
- var css = ["layers", "optimize", "minify"];
35
61
  var common = tokens.concat(jsx, format);
36
62
  var artifactConfigDeps = {
37
63
  helpers: ["syntax", "jsxFramework"],
@@ -53,11 +79,7 @@ var artifactConfigDeps = {
53
79
  "jsx-patterns": jsx.concat("patterns"),
54
80
  "jsx-patterns-index": jsx.concat("patterns"),
55
81
  "css-index": ["syntax"],
56
- "reset.css": ["preflight", "layers"],
57
- "global.css": ["globalCss"].concat(css),
58
- "static.css": ["staticCss", "theme.breakpoints"].concat(css),
59
- "styles.css": tokens.concat(format),
60
- "package.json": ["emitPackage"]
82
+ "package.json": ["emitPackage", "forceConsistentTypeExtension", "outExtension"]
61
83
  };
62
84
  var artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
63
85
  if (!paths.length)
@@ -1,8 +1,6 @@
1
1
  import { Config, DiffConfigResult } from '@pandacss/types';
2
- export { DiffConfigResult } from '@pandacss/types';
3
2
 
4
3
  type ConfigOrFn = Config | (() => Config);
5
-
6
4
  /**
7
5
  * Diff the two config objects and return the list of affected properties
8
6
  */
@@ -1,8 +1,6 @@
1
1
  import { Config, DiffConfigResult } from '@pandacss/types';
2
- export { DiffConfigResult } from '@pandacss/types';
3
2
 
4
3
  type ConfigOrFn = Config | (() => Config);
5
-
6
4
  /**
7
5
  * Diff the two config objects and return the list of affected properties
8
6
  */
@@ -61,11 +61,37 @@ function createMatcher(id, patterns) {
61
61
  }
62
62
 
63
63
  // src/config-deps.ts
64
- var all = ["outdir", "forceConsistentTypeExtension", "outExtension"];
65
- var format = ["syntax", "hash", "prefix", "separator"];
66
- var tokens = ["utilities", "conditions", "theme.tokens", "theme.semanticTokens", "theme.breakpoints"];
64
+ var all = [
65
+ "clean",
66
+ "cwd",
67
+ "eject",
68
+ "outdir",
69
+ "forceConsistentTypeExtension",
70
+ "outExtension",
71
+ "emitPackage",
72
+ "emitTokensOnly",
73
+ "presets",
74
+ "hooks"
75
+ ];
76
+ var format = [
77
+ "syntax",
78
+ "hash",
79
+ "prefix",
80
+ "separator",
81
+ "strictTokens",
82
+ "strictPropertyValues",
83
+ "shorthands"
84
+ ];
85
+ var tokens = [
86
+ "utilities",
87
+ "conditions",
88
+ "theme.tokens",
89
+ "theme.semanticTokens",
90
+ "theme.breakpoints",
91
+ "theme.containerNames",
92
+ "theme.containerSizes"
93
+ ];
67
94
  var jsx = ["jsxFramework", "jsxFactory", "jsxStyleProps", "syntax"];
68
- var css = ["layers", "optimize", "minify"];
69
95
  var common = tokens.concat(jsx, format);
70
96
  var artifactConfigDeps = {
71
97
  helpers: ["syntax", "jsxFramework"],
@@ -87,11 +113,7 @@ var artifactConfigDeps = {
87
113
  "jsx-patterns": jsx.concat("patterns"),
88
114
  "jsx-patterns-index": jsx.concat("patterns"),
89
115
  "css-index": ["syntax"],
90
- "reset.css": ["preflight", "layers"],
91
- "global.css": ["globalCss"].concat(css),
92
- "static.css": ["staticCss", "theme.breakpoints"].concat(css),
93
- "styles.css": tokens.concat(format),
94
- "package.json": ["emitPackage"]
116
+ "package.json": ["emitPackage", "forceConsistentTypeExtension", "outExtension"]
95
117
  };
96
118
  var artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
97
119
  if (!paths.length)
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  diffConfigs
3
- } from "./chunk-TP2SOWHU.mjs";
3
+ } from "./chunk-5YOGLJPA.mjs";
4
4
  export {
5
5
  diffConfigs
6
6
  };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  import * as _pandacss_types from '@pandacss/types';
2
2
  import { Config, ConfigTsOptions } from '@pandacss/types';
3
- export { DiffConfigResult } from '@pandacss/types';
4
3
  export { diffConfigs } from './diff-config.mjs';
5
4
  import { P as PathMapping } from './ts-config-paths-2lh9mwzL.mjs';
6
5
  export { c as convertTsPathsToRegexes } from './ts-config-paths-2lh9mwzL.mjs';
@@ -11,15 +10,15 @@ interface ConfigFileOptions {
11
10
  cwd: string;
12
11
  file?: string;
13
12
  }
14
-
15
13
  interface BundleConfigResult<T = Config> {
16
14
  config: T;
17
15
  dependencies: string[];
18
16
  path: string;
19
17
  }
18
+
20
19
  declare function bundleConfig(options: ConfigFileOptions): Promise<BundleConfigResult>;
21
20
 
22
- declare function findConfig({ cwd, file }: Partial<ConfigFileOptions>): string | undefined;
21
+ declare function findConfig(options: Partial<ConfigFileOptions>): string;
23
22
 
24
23
  interface GetDepsOptions {
25
24
  filename: string;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import * as _pandacss_types from '@pandacss/types';
2
2
  import { Config, ConfigTsOptions } from '@pandacss/types';
3
- export { DiffConfigResult } from '@pandacss/types';
4
3
  export { diffConfigs } from './diff-config.js';
5
4
  import { P as PathMapping } from './ts-config-paths-2lh9mwzL.js';
6
5
  export { c as convertTsPathsToRegexes } from './ts-config-paths-2lh9mwzL.js';
@@ -11,15 +10,15 @@ interface ConfigFileOptions {
11
10
  cwd: string;
12
11
  file?: string;
13
12
  }
14
-
15
13
  interface BundleConfigResult<T = Config> {
16
14
  config: T;
17
15
  dependencies: string[];
18
16
  path: string;
19
17
  }
18
+
20
19
  declare function bundleConfig(options: ConfigFileOptions): Promise<BundleConfigResult>;
21
20
 
22
- declare function findConfig({ cwd, file }: Partial<ConfigFileOptions>): string | undefined;
21
+ declare function findConfig(options: Partial<ConfigFileOptions>): string;
23
22
 
24
23
  interface GetDepsOptions {
25
24
  filename: string;
package/dist/index.js CHANGED
@@ -42,47 +42,58 @@ __export(src_exports, {
42
42
  module.exports = __toCommonJS(src_exports);
43
43
 
44
44
  // src/bundle-config.ts
45
- var import_error = require("@pandacss/error");
46
45
  var import_logger = require("@pandacss/logger");
46
+ var import_shared2 = require("@pandacss/shared");
47
47
  var import_bundle_n_require = require("bundle-n-require");
48
48
 
49
49
  // src/find-config.ts
50
+ var import_shared = require("@pandacss/shared");
50
51
  var import_sync = __toESM(require("escalade/sync"));
51
52
  var import_path = require("path");
53
+
54
+ // src/is-panda-config.ts
52
55
  var configs = [".ts", ".js", ".mts", ".mjs", ".cts", ".cjs"];
53
56
  var pandaConfigRegex = new RegExp(`panda.config(${configs.join("|")})$`);
54
57
  var isPandaConfig = (file) => pandaConfigRegex.test(file);
55
- function findConfig({ cwd, file }) {
56
- cwd = cwd ?? process.cwd();
57
- if (file)
58
+
59
+ // src/find-config.ts
60
+ function findConfig(options) {
61
+ const { cwd = process.cwd(), file } = options;
62
+ if (file) {
58
63
  return (0, import_path.resolve)(cwd, file);
59
- const result = (0, import_sync.default)(cwd, (_dir, paths) => paths.find(isPandaConfig));
60
- return result ?? void 0;
64
+ }
65
+ const configPath = (0, import_sync.default)(cwd, (_dir, paths) => paths.find(isPandaConfig));
66
+ if (!configPath) {
67
+ throw new import_shared.PandaError(
68
+ "CONFIG_NOT_FOUND",
69
+ `Cannot find config file \`panda.config.{ts,js,mjs,mts}\`. Did you forget to run \`panda init\`?`
70
+ );
71
+ }
72
+ return configPath;
61
73
  }
62
74
 
63
75
  // src/bundle-config.ts
64
76
  async function bundle(filepath, cwd) {
65
- const { mod: config, dependencies } = await (0, import_bundle_n_require.bundleNRequire)(filepath, {
77
+ const { mod, dependencies } = await (0, import_bundle_n_require.bundleNRequire)(filepath, {
66
78
  cwd,
67
79
  interopDefault: true
68
80
  });
81
+ const config = mod?.default ?? mod;
69
82
  return {
70
- config: config?.default ?? config,
83
+ config,
71
84
  dependencies
72
85
  };
73
86
  }
74
87
  async function bundleConfig(options) {
75
88
  const { cwd, file } = options;
76
89
  const filePath = findConfig({ cwd, file });
77
- if (!filePath) {
78
- throw new import_error.ConfigNotFoundError();
79
- }
80
90
  import_logger.logger.debug("config:path", filePath);
81
91
  const result = await bundle(filePath, cwd);
82
92
  if (typeof result.config !== "object") {
83
- throw new import_error.ConfigError(`\u{1F4A5} Config must export or return an object.`);
93
+ throw new import_shared2.PandaError("CONFIG_ERROR", `\u{1F4A5} Config must export or return an object.`);
84
94
  }
85
95
  result.config.outdir ??= "styled-system";
96
+ result.config.validation ??= "error";
86
97
  return {
87
98
  ...result,
88
99
  config: result.config,
@@ -91,7 +102,7 @@ async function bundleConfig(options) {
91
102
  }
92
103
 
93
104
  // src/diff-config.ts
94
- var import_shared = require("@pandacss/shared");
105
+ var import_shared3 = require("@pandacss/shared");
95
106
  var import_microdiff = __toESM(require("microdiff"));
96
107
 
97
108
  // src/create-matcher.ts
@@ -119,11 +130,37 @@ function createMatcher(id, patterns) {
119
130
  }
120
131
 
121
132
  // src/config-deps.ts
122
- var all = ["outdir", "forceConsistentTypeExtension", "outExtension"];
123
- var format = ["syntax", "hash", "prefix", "separator"];
124
- var tokens = ["utilities", "conditions", "theme.tokens", "theme.semanticTokens", "theme.breakpoints"];
133
+ var all = [
134
+ "clean",
135
+ "cwd",
136
+ "eject",
137
+ "outdir",
138
+ "forceConsistentTypeExtension",
139
+ "outExtension",
140
+ "emitPackage",
141
+ "emitTokensOnly",
142
+ "presets",
143
+ "hooks"
144
+ ];
145
+ var format = [
146
+ "syntax",
147
+ "hash",
148
+ "prefix",
149
+ "separator",
150
+ "strictTokens",
151
+ "strictPropertyValues",
152
+ "shorthands"
153
+ ];
154
+ var tokens = [
155
+ "utilities",
156
+ "conditions",
157
+ "theme.tokens",
158
+ "theme.semanticTokens",
159
+ "theme.breakpoints",
160
+ "theme.containerNames",
161
+ "theme.containerSizes"
162
+ ];
125
163
  var jsx = ["jsxFramework", "jsxFactory", "jsxStyleProps", "syntax"];
126
- var css = ["layers", "optimize", "minify"];
127
164
  var common = tokens.concat(jsx, format);
128
165
  var artifactConfigDeps = {
129
166
  helpers: ["syntax", "jsxFramework"],
@@ -145,11 +182,7 @@ var artifactConfigDeps = {
145
182
  "jsx-patterns": jsx.concat("patterns"),
146
183
  "jsx-patterns-index": jsx.concat("patterns"),
147
184
  "css-index": ["syntax"],
148
- "reset.css": ["preflight", "layers"],
149
- "global.css": ["globalCss"].concat(css),
150
- "static.css": ["staticCss", "theme.breakpoints"].concat(css),
151
- "styles.css": tokens.concat(format),
152
- "package.json": ["emitPackage"]
185
+ "package.json": ["emitPackage", "forceConsistentTypeExtension", "outExtension"]
153
186
  };
154
187
  var artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
155
188
  if (!paths.length)
@@ -182,11 +215,11 @@ function diffConfigs(config, prevConfig) {
182
215
  if (!id)
183
216
  return;
184
217
  if (id === "recipes") {
185
- const name = (0, import_shared.dashCase)(change.path.slice(1, 3).join("."));
218
+ const name = (0, import_shared3.dashCase)(change.path.slice(1, 3).join("."));
186
219
  affected.artifacts.add(name);
187
220
  }
188
221
  if (id === "patterns") {
189
- const name = (0, import_shared.dashCase)(change.path.slice(0, 2).join("."));
222
+ const name = (0, import_shared3.dashCase)(change.path.slice(0, 2).join("."));
190
223
  affected.artifacts.add(name);
191
224
  }
192
225
  affected.artifacts.add(id);
@@ -448,7 +481,7 @@ async function getResolvedConfig(config, cwd) {
448
481
  }
449
482
 
450
483
  // src/resolve-config.ts
451
- var import_shared2 = require("@pandacss/shared");
484
+ var import_shared7 = require("@pandacss/shared");
452
485
 
453
486
  // src/bundled-preset.ts
454
487
  var import_preset_base = require("@pandacss/preset-base");
@@ -464,6 +497,227 @@ var getBundledPreset = (preset) => {
464
497
  return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
465
498
  };
466
499
 
500
+ // src/validate-config.ts
501
+ var import_logger2 = require("@pandacss/logger");
502
+ var import_shared6 = require("@pandacss/shared");
503
+
504
+ // src/validation/validate-artifact.ts
505
+ var validateArtifactNames = (names, addError) => {
506
+ names.recipes.forEach((recipeName) => {
507
+ if (names.slotRecipes.has(recipeName)) {
508
+ addError("recipes", `This recipe name is already used in \`config.theme.slotRecipes\`: ${recipeName}`);
509
+ }
510
+ if (names.patterns.has(recipeName)) {
511
+ addError("recipes", `This recipe name is already used in \`config.patterns\`: \`${recipeName}\``);
512
+ }
513
+ });
514
+ names.slotRecipes.forEach((recipeName) => {
515
+ if (names.patterns.has(recipeName)) {
516
+ addError("recipes", `This recipe name is already used in \`config.patterns\`: ${recipeName}`);
517
+ }
518
+ });
519
+ };
520
+
521
+ // src/validation/validate-breakpoints.ts
522
+ var import_shared4 = require("@pandacss/shared");
523
+ var validateBreakpoints = (breakpoints, addError) => {
524
+ if (!breakpoints)
525
+ return;
526
+ const units = /* @__PURE__ */ new Set();
527
+ const values = Object.values(breakpoints);
528
+ for (const value of values) {
529
+ const unit = (0, import_shared4.getUnit)(value) ?? "px";
530
+ units.add(unit);
531
+ }
532
+ if (units.size > 1) {
533
+ addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
534
+ }
535
+ };
536
+
537
+ // src/validation/validate-condition.ts
538
+ var validateConditions = (conditions, addError) => {
539
+ if (!conditions)
540
+ return;
541
+ Object.values(conditions).forEach((condition) => {
542
+ if (!condition.startsWith("@") && !condition.includes("&")) {
543
+ addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
544
+ }
545
+ });
546
+ };
547
+
548
+ // src/validation/validate-patterns.ts
549
+ var validatePatterns = (patterns, names) => {
550
+ if (!patterns)
551
+ return;
552
+ Object.keys(patterns).forEach((patternName) => {
553
+ names.patterns.add(patternName);
554
+ });
555
+ };
556
+
557
+ // src/validation/validate-recipes.ts
558
+ var validateRecipes = (options) => {
559
+ const {
560
+ config: { theme },
561
+ artifacts
562
+ } = options;
563
+ if (!theme)
564
+ return;
565
+ if (theme.recipes) {
566
+ Object.keys(theme.recipes).forEach((recipeName) => {
567
+ artifacts.recipes.add(recipeName);
568
+ });
569
+ }
570
+ if (theme.slotRecipes) {
571
+ Object.keys(theme.slotRecipes).forEach((recipeName) => {
572
+ artifacts.slotRecipes.add(recipeName);
573
+ });
574
+ }
575
+ return artifacts;
576
+ };
577
+
578
+ // src/validation/validate-tokens.ts
579
+ var import_shared5 = require("@pandacss/shared");
580
+
581
+ // src/validation/get-final-paths.ts
582
+ var getFinalPaths = (paths) => {
583
+ paths.forEach((path2) => {
584
+ paths.forEach((potentialExtension) => {
585
+ if (potentialExtension.startsWith(path2 + ".")) {
586
+ paths.delete(path2);
587
+ }
588
+ });
589
+ });
590
+ return paths;
591
+ };
592
+
593
+ // src/validation/validate-token-references.ts
594
+ var validateTokenReferences = (valueAtPath, refsByPath, addError) => {
595
+ refsByPath.forEach((refs, path2) => {
596
+ if (refs.has(path2)) {
597
+ addError("tokens", `Self token reference: \`${path2}\``);
598
+ }
599
+ const stack = [path2];
600
+ while (stack.length > 0) {
601
+ const current = stack.pop();
602
+ const value = valueAtPath.get(current);
603
+ if (!value) {
604
+ addError("tokens", `Missing token: \`${current}\` used in \`config.semanticTokens.${path2}\``);
605
+ }
606
+ const deps = refsByPath.get(current);
607
+ if (!deps)
608
+ continue;
609
+ for (const transitiveDep of deps) {
610
+ if (path2 === transitiveDep) {
611
+ addError("tokens", `Circular token reference: \`${transitiveDep}\` -> \`${current}\` -> ... -> \`${path2}\``);
612
+ break;
613
+ }
614
+ stack.push(transitiveDep);
615
+ }
616
+ }
617
+ });
618
+ };
619
+
620
+ // src/validation/validate-tokens.ts
621
+ var validateTokens = (options) => {
622
+ const {
623
+ config: { theme },
624
+ tokens: tokens2,
625
+ addError
626
+ } = options;
627
+ if (!theme)
628
+ return;
629
+ const { tokenNames, semanticTokenNames, valueAtPath, refsByPath } = tokens2;
630
+ if (theme.tokens) {
631
+ (0, import_shared5.traverse)(theme.tokens, (node) => {
632
+ if (node.depth >= 1) {
633
+ tokenNames.add(node.path);
634
+ valueAtPath.set(node.path, node.value);
635
+ }
636
+ });
637
+ const finalPaths = getFinalPaths(tokenNames);
638
+ finalPaths.forEach((path2) => {
639
+ if (!path2.includes("value")) {
640
+ addError("tokens", `Token paths must end with 'value': \`theme.tokens.${path2}\``);
641
+ }
642
+ const atPath = valueAtPath.get(path2);
643
+ if (typeof atPath === "string" && atPath.startsWith("{")) {
644
+ refsByPath.set(path2, /* @__PURE__ */ new Set([]));
645
+ }
646
+ });
647
+ }
648
+ if (theme.semanticTokens) {
649
+ (0, import_shared5.traverse)(theme.semanticTokens, (node) => {
650
+ if (node.depth >= 1) {
651
+ semanticTokenNames.add(node.path);
652
+ valueAtPath.set(node.path, node.value);
653
+ if (typeof node.value === "string" && node.value.startsWith("{") && node.path.includes("value")) {
654
+ const tokenPath = node.path.split(".value")[0];
655
+ if (!refsByPath.has(tokenPath)) {
656
+ refsByPath.set(tokenPath, /* @__PURE__ */ new Set());
657
+ }
658
+ const values = refsByPath.get(tokenPath);
659
+ const tokenRef = node.value.slice(1, -1);
660
+ values.add(tokenRef);
661
+ }
662
+ }
663
+ });
664
+ const finalPaths = getFinalPaths(semanticTokenNames);
665
+ finalPaths.forEach((path2) => {
666
+ if (!path2.includes("value")) {
667
+ addError("tokens", `Semantic token paths must contain 'value': \`theme.semanticTokens.${path2}\``);
668
+ }
669
+ });
670
+ validateTokenReferences(valueAtPath, refsByPath, addError);
671
+ }
672
+ semanticTokenNames.forEach((semanticTokenName) => {
673
+ if (tokenNames.has(semanticTokenName)) {
674
+ addError("tokens", `This token name is already used in \`config.theme.token\`: \`${semanticTokenName}\``);
675
+ }
676
+ });
677
+ };
678
+
679
+ // src/validate-config.ts
680
+ var validateConfig = (config) => {
681
+ if (config.validation === "none")
682
+ return;
683
+ const warnings = /* @__PURE__ */ new Set();
684
+ const addError = (scope, message) => {
685
+ if (config.validation === "warn") {
686
+ warnings.add(`[${scope}]: ` + message);
687
+ } else {
688
+ throw new import_shared6.PandaError("CONFIG_ERROR", `[${scope}]: ` + message);
689
+ }
690
+ };
691
+ validateBreakpoints(config.theme?.breakpoints, addError);
692
+ validateConditions(config.conditions, addError);
693
+ const artifacts = {
694
+ recipes: /* @__PURE__ */ new Set(),
695
+ slotRecipes: /* @__PURE__ */ new Set(),
696
+ patterns: /* @__PURE__ */ new Set()
697
+ };
698
+ const tokens2 = {
699
+ tokenNames: /* @__PURE__ */ new Set(),
700
+ semanticTokenNames: /* @__PURE__ */ new Set(),
701
+ valueAtPath: /* @__PURE__ */ new Map(),
702
+ refsByPath: /* @__PURE__ */ new Map()
703
+ };
704
+ if (config.theme) {
705
+ validateTokens({ config, tokens: tokens2, addError });
706
+ validateRecipes({ config, tokens: tokens2, artifacts, addError });
707
+ }
708
+ validatePatterns(config.patterns, artifacts);
709
+ validateArtifactNames(artifacts, addError);
710
+ if (warnings.size) {
711
+ import_logger2.logger.warn(
712
+ "config",
713
+ `\u26A0\uFE0F Invalid config:
714
+ ${Array.from(warnings).map((err) => "- " + err).join("\n")}
715
+ `
716
+ );
717
+ return warnings;
718
+ }
719
+ };
720
+
467
721
  // src/resolve-config.ts
468
722
  async function resolveConfig(result, cwd) {
469
723
  const presets = /* @__PURE__ */ new Set();
@@ -478,12 +732,16 @@ async function resolveConfig(result, cwd) {
478
732
  presets.add(import_preset_panda.preset);
479
733
  }
480
734
  result.config.presets = Array.from(presets);
481
- const mergedConfig = await getResolvedConfig(result.config, cwd);
482
- const hooks = result.config.hooks ?? {};
483
- const loadConfigResult = { ...result, config: mergedConfig };
735
+ const config = await getResolvedConfig(result.config, cwd);
736
+ validateConfig(config);
737
+ const { hooks = {} } = result.config;
738
+ const loadConfigResult = {
739
+ ...result,
740
+ config
741
+ };
484
742
  await hooks["config:resolved"]?.({ conf: loadConfigResult });
485
- const serialized = (0, import_shared2.stringifyJson)(loadConfigResult.config);
486
- const deserialize = () => (0, import_shared2.parseJson)(serialized);
743
+ const serialized = (0, import_shared7.stringifyJson)(loadConfigResult.config);
744
+ const deserialize = () => (0, import_shared7.parseJson)(serialized);
487
745
  return { ...loadConfigResult, serialized, deserialize, hooks };
488
746
  }
489
747
 
package/dist/index.mjs CHANGED
@@ -3,53 +3,64 @@ import {
3
3
  } from "./chunk-PROY5XLZ.mjs";
4
4
  import {
5
5
  diffConfigs
6
- } from "./chunk-TP2SOWHU.mjs";
6
+ } from "./chunk-5YOGLJPA.mjs";
7
7
  import {
8
8
  resolveTsPathPattern
9
9
  } from "./chunk-RPIVZP2I.mjs";
10
10
 
11
11
  // src/bundle-config.ts
12
- import { ConfigError, ConfigNotFoundError } from "@pandacss/error";
13
12
  import { logger } from "@pandacss/logger";
13
+ import { PandaError as PandaError2 } from "@pandacss/shared";
14
14
  import { bundleNRequire } from "bundle-n-require";
15
15
 
16
16
  // src/find-config.ts
17
+ import { PandaError } from "@pandacss/shared";
17
18
  import findUp from "escalade/sync";
18
19
  import { resolve } from "path";
20
+
21
+ // src/is-panda-config.ts
19
22
  var configs = [".ts", ".js", ".mts", ".mjs", ".cts", ".cjs"];
20
23
  var pandaConfigRegex = new RegExp(`panda.config(${configs.join("|")})$`);
21
24
  var isPandaConfig = (file) => pandaConfigRegex.test(file);
22
- function findConfig({ cwd, file }) {
23
- cwd = cwd ?? process.cwd();
24
- if (file)
25
+
26
+ // src/find-config.ts
27
+ function findConfig(options) {
28
+ const { cwd = process.cwd(), file } = options;
29
+ if (file) {
25
30
  return resolve(cwd, file);
26
- const result = findUp(cwd, (_dir, paths) => paths.find(isPandaConfig));
27
- return result ?? void 0;
31
+ }
32
+ const configPath = findUp(cwd, (_dir, paths) => paths.find(isPandaConfig));
33
+ if (!configPath) {
34
+ throw new PandaError(
35
+ "CONFIG_NOT_FOUND",
36
+ `Cannot find config file \`panda.config.{ts,js,mjs,mts}\`. Did you forget to run \`panda init\`?`
37
+ );
38
+ }
39
+ return configPath;
28
40
  }
29
41
 
30
42
  // src/bundle-config.ts
31
43
  async function bundle(filepath, cwd) {
32
- const { mod: config, dependencies } = await bundleNRequire(filepath, {
44
+ const { mod, dependencies } = await bundleNRequire(filepath, {
33
45
  cwd,
34
46
  interopDefault: true
35
47
  });
48
+ const config = mod?.default ?? mod;
36
49
  return {
37
- config: config?.default ?? config,
50
+ config,
38
51
  dependencies
39
52
  };
40
53
  }
41
54
  async function bundleConfig(options) {
42
55
  const { cwd, file } = options;
43
56
  const filePath = findConfig({ cwd, file });
44
- if (!filePath) {
45
- throw new ConfigNotFoundError();
46
- }
47
57
  logger.debug("config:path", filePath);
48
58
  const result = await bundle(filePath, cwd);
49
59
  if (typeof result.config !== "object") {
50
- throw new ConfigError(`\u{1F4A5} Config must export or return an object.`);
60
+ throw new PandaError2("CONFIG_ERROR", `\u{1F4A5} Config must export or return an object.`);
51
61
  }
52
62
  result.config.outdir ??= "styled-system";
63
+ result.config.validation ??= "error";
53
64
  return {
54
65
  ...result,
55
66
  config: result.config,
@@ -218,6 +229,227 @@ var getBundledPreset = (preset) => {
218
229
  return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
219
230
  };
220
231
 
232
+ // src/validate-config.ts
233
+ import { logger as logger2 } from "@pandacss/logger";
234
+ import { PandaError as PandaError3 } from "@pandacss/shared";
235
+
236
+ // src/validation/validate-artifact.ts
237
+ var validateArtifactNames = (names, addError) => {
238
+ names.recipes.forEach((recipeName) => {
239
+ if (names.slotRecipes.has(recipeName)) {
240
+ addError("recipes", `This recipe name is already used in \`config.theme.slotRecipes\`: ${recipeName}`);
241
+ }
242
+ if (names.patterns.has(recipeName)) {
243
+ addError("recipes", `This recipe name is already used in \`config.patterns\`: \`${recipeName}\``);
244
+ }
245
+ });
246
+ names.slotRecipes.forEach((recipeName) => {
247
+ if (names.patterns.has(recipeName)) {
248
+ addError("recipes", `This recipe name is already used in \`config.patterns\`: ${recipeName}`);
249
+ }
250
+ });
251
+ };
252
+
253
+ // src/validation/validate-breakpoints.ts
254
+ import { getUnit } from "@pandacss/shared";
255
+ var validateBreakpoints = (breakpoints, addError) => {
256
+ if (!breakpoints)
257
+ return;
258
+ const units = /* @__PURE__ */ new Set();
259
+ const values = Object.values(breakpoints);
260
+ for (const value of values) {
261
+ const unit = getUnit(value) ?? "px";
262
+ units.add(unit);
263
+ }
264
+ if (units.size > 1) {
265
+ addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
266
+ }
267
+ };
268
+
269
+ // src/validation/validate-condition.ts
270
+ var validateConditions = (conditions, addError) => {
271
+ if (!conditions)
272
+ return;
273
+ Object.values(conditions).forEach((condition) => {
274
+ if (!condition.startsWith("@") && !condition.includes("&")) {
275
+ addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
276
+ }
277
+ });
278
+ };
279
+
280
+ // src/validation/validate-patterns.ts
281
+ var validatePatterns = (patterns, names) => {
282
+ if (!patterns)
283
+ return;
284
+ Object.keys(patterns).forEach((patternName) => {
285
+ names.patterns.add(patternName);
286
+ });
287
+ };
288
+
289
+ // src/validation/validate-recipes.ts
290
+ var validateRecipes = (options) => {
291
+ const {
292
+ config: { theme },
293
+ artifacts
294
+ } = options;
295
+ if (!theme)
296
+ return;
297
+ if (theme.recipes) {
298
+ Object.keys(theme.recipes).forEach((recipeName) => {
299
+ artifacts.recipes.add(recipeName);
300
+ });
301
+ }
302
+ if (theme.slotRecipes) {
303
+ Object.keys(theme.slotRecipes).forEach((recipeName) => {
304
+ artifacts.slotRecipes.add(recipeName);
305
+ });
306
+ }
307
+ return artifacts;
308
+ };
309
+
310
+ // src/validation/validate-tokens.ts
311
+ import { traverse } from "@pandacss/shared";
312
+
313
+ // src/validation/get-final-paths.ts
314
+ var getFinalPaths = (paths) => {
315
+ paths.forEach((path2) => {
316
+ paths.forEach((potentialExtension) => {
317
+ if (potentialExtension.startsWith(path2 + ".")) {
318
+ paths.delete(path2);
319
+ }
320
+ });
321
+ });
322
+ return paths;
323
+ };
324
+
325
+ // src/validation/validate-token-references.ts
326
+ var validateTokenReferences = (valueAtPath, refsByPath, addError) => {
327
+ refsByPath.forEach((refs, path2) => {
328
+ if (refs.has(path2)) {
329
+ addError("tokens", `Self token reference: \`${path2}\``);
330
+ }
331
+ const stack = [path2];
332
+ while (stack.length > 0) {
333
+ const current = stack.pop();
334
+ const value = valueAtPath.get(current);
335
+ if (!value) {
336
+ addError("tokens", `Missing token: \`${current}\` used in \`config.semanticTokens.${path2}\``);
337
+ }
338
+ const deps = refsByPath.get(current);
339
+ if (!deps)
340
+ continue;
341
+ for (const transitiveDep of deps) {
342
+ if (path2 === transitiveDep) {
343
+ addError("tokens", `Circular token reference: \`${transitiveDep}\` -> \`${current}\` -> ... -> \`${path2}\``);
344
+ break;
345
+ }
346
+ stack.push(transitiveDep);
347
+ }
348
+ }
349
+ });
350
+ };
351
+
352
+ // src/validation/validate-tokens.ts
353
+ var validateTokens = (options) => {
354
+ const {
355
+ config: { theme },
356
+ tokens,
357
+ addError
358
+ } = options;
359
+ if (!theme)
360
+ return;
361
+ const { tokenNames, semanticTokenNames, valueAtPath, refsByPath } = tokens;
362
+ if (theme.tokens) {
363
+ traverse(theme.tokens, (node) => {
364
+ if (node.depth >= 1) {
365
+ tokenNames.add(node.path);
366
+ valueAtPath.set(node.path, node.value);
367
+ }
368
+ });
369
+ const finalPaths = getFinalPaths(tokenNames);
370
+ finalPaths.forEach((path2) => {
371
+ if (!path2.includes("value")) {
372
+ addError("tokens", `Token paths must end with 'value': \`theme.tokens.${path2}\``);
373
+ }
374
+ const atPath = valueAtPath.get(path2);
375
+ if (typeof atPath === "string" && atPath.startsWith("{")) {
376
+ refsByPath.set(path2, /* @__PURE__ */ new Set([]));
377
+ }
378
+ });
379
+ }
380
+ if (theme.semanticTokens) {
381
+ traverse(theme.semanticTokens, (node) => {
382
+ if (node.depth >= 1) {
383
+ semanticTokenNames.add(node.path);
384
+ valueAtPath.set(node.path, node.value);
385
+ if (typeof node.value === "string" && node.value.startsWith("{") && node.path.includes("value")) {
386
+ const tokenPath = node.path.split(".value")[0];
387
+ if (!refsByPath.has(tokenPath)) {
388
+ refsByPath.set(tokenPath, /* @__PURE__ */ new Set());
389
+ }
390
+ const values = refsByPath.get(tokenPath);
391
+ const tokenRef = node.value.slice(1, -1);
392
+ values.add(tokenRef);
393
+ }
394
+ }
395
+ });
396
+ const finalPaths = getFinalPaths(semanticTokenNames);
397
+ finalPaths.forEach((path2) => {
398
+ if (!path2.includes("value")) {
399
+ addError("tokens", `Semantic token paths must contain 'value': \`theme.semanticTokens.${path2}\``);
400
+ }
401
+ });
402
+ validateTokenReferences(valueAtPath, refsByPath, addError);
403
+ }
404
+ semanticTokenNames.forEach((semanticTokenName) => {
405
+ if (tokenNames.has(semanticTokenName)) {
406
+ addError("tokens", `This token name is already used in \`config.theme.token\`: \`${semanticTokenName}\``);
407
+ }
408
+ });
409
+ };
410
+
411
+ // src/validate-config.ts
412
+ var validateConfig = (config) => {
413
+ if (config.validation === "none")
414
+ return;
415
+ const warnings = /* @__PURE__ */ new Set();
416
+ const addError = (scope, message) => {
417
+ if (config.validation === "warn") {
418
+ warnings.add(`[${scope}]: ` + message);
419
+ } else {
420
+ throw new PandaError3("CONFIG_ERROR", `[${scope}]: ` + message);
421
+ }
422
+ };
423
+ validateBreakpoints(config.theme?.breakpoints, addError);
424
+ validateConditions(config.conditions, addError);
425
+ const artifacts = {
426
+ recipes: /* @__PURE__ */ new Set(),
427
+ slotRecipes: /* @__PURE__ */ new Set(),
428
+ patterns: /* @__PURE__ */ new Set()
429
+ };
430
+ const tokens = {
431
+ tokenNames: /* @__PURE__ */ new Set(),
432
+ semanticTokenNames: /* @__PURE__ */ new Set(),
433
+ valueAtPath: /* @__PURE__ */ new Map(),
434
+ refsByPath: /* @__PURE__ */ new Map()
435
+ };
436
+ if (config.theme) {
437
+ validateTokens({ config, tokens, addError });
438
+ validateRecipes({ config, tokens, artifacts, addError });
439
+ }
440
+ validatePatterns(config.patterns, artifacts);
441
+ validateArtifactNames(artifacts, addError);
442
+ if (warnings.size) {
443
+ logger2.warn(
444
+ "config",
445
+ `\u26A0\uFE0F Invalid config:
446
+ ${Array.from(warnings).map((err) => "- " + err).join("\n")}
447
+ `
448
+ );
449
+ return warnings;
450
+ }
451
+ };
452
+
221
453
  // src/resolve-config.ts
222
454
  async function resolveConfig(result, cwd) {
223
455
  const presets = /* @__PURE__ */ new Set();
@@ -232,9 +464,13 @@ async function resolveConfig(result, cwd) {
232
464
  presets.add(presetPanda);
233
465
  }
234
466
  result.config.presets = Array.from(presets);
235
- const mergedConfig = await getResolvedConfig(result.config, cwd);
236
- const hooks = result.config.hooks ?? {};
237
- const loadConfigResult = { ...result, config: mergedConfig };
467
+ const config = await getResolvedConfig(result.config, cwd);
468
+ validateConfig(config);
469
+ const { hooks = {} } = result.config;
470
+ const loadConfigResult = {
471
+ ...result,
472
+ config
473
+ };
238
474
  await hooks["config:resolved"]?.({ conf: loadConfigResult });
239
475
  const serialized = stringifyJson(loadConfigResult.config);
240
476
  const deserialize = () => parseJson(serialized);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pandacss/config",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Find and load panda config",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -64,12 +64,11 @@
64
64
  "merge-anything": "^5.1.7",
65
65
  "microdiff": "^1.3.2",
66
66
  "typescript": "^5.3.3",
67
- "@pandacss/error": "0.28.0",
68
- "@pandacss/logger": "0.28.0",
69
- "@pandacss/preset-base": "0.28.0",
70
- "@pandacss/preset-panda": "0.28.0",
71
- "@pandacss/shared": "0.28.0",
72
- "@pandacss/types": "0.28.0"
67
+ "@pandacss/logger": "0.29.0",
68
+ "@pandacss/preset-base": "0.29.0",
69
+ "@pandacss/preset-panda": "0.29.0",
70
+ "@pandacss/shared": "0.29.0",
71
+ "@pandacss/types": "0.29.0"
73
72
  },
74
73
  "devDependencies": {
75
74
  "pkg-types": "1.0.3"