@pandacss/config 1.11.2 → 2.0.0-beta.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.
package/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Segun Adebayo
3
+ Copyright (c) 2023 Chakra Systems Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # @pandacss/config
2
+
3
+ Loads, bundles, and serializes a Panda CSS config into the self-contained snapshot consumed by the Rust compiler.
4
+ Internal package used by `@pandacss/cli` / `@pandacss/dev`; you typically don't install it directly.
5
+
6
+ ## Documentation
7
+
8
+ Visit the [Panda CSS documentation](https://panda-css.com) to learn more.
9
+
10
+ ## License
11
+
12
+ MIT © [Chakra Systems Inc.](https://github.com/chakra-ui)
@@ -0,0 +1,250 @@
1
+ // src/error.ts
2
+ var PandaError = class extends Error {
3
+ code;
4
+ hint;
5
+ constructor(code, message, opts) {
6
+ super(message, { cause: opts?.cause });
7
+ this.code = `ERR_PANDA_${code}`;
8
+ this.hint = opts?.hint;
9
+ }
10
+ };
11
+
12
+ // src/sources.ts
13
+ var sectionKeySet = /* @__PURE__ */ new Set([
14
+ "conditions",
15
+ "theme",
16
+ "patterns",
17
+ "utilities",
18
+ "globalCss",
19
+ "globalVars",
20
+ "globalFontface",
21
+ "globalPositionTry",
22
+ "staticCss",
23
+ "themes"
24
+ ]);
25
+ var omitKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
26
+ var tokenKeys = /* @__PURE__ */ new Set(["description", "extensions", "type", "value", "deprecated"]);
27
+ var directFieldSections = /* @__PURE__ */ new Set(["patterns", "utilities"]);
28
+ var themeEntryKeys = /* @__PURE__ */ new Set([
29
+ "animationStyles",
30
+ "breakpoints",
31
+ "containers",
32
+ "keyframes",
33
+ "layerStyles",
34
+ "recipes",
35
+ "slotRecipes",
36
+ "textStyles"
37
+ ]);
38
+ var SourceTracker = class {
39
+ sources;
40
+ constructor(entries) {
41
+ this.sources = { entries, paths: {} };
42
+ }
43
+ record(path, sourceId, mode) {
44
+ const key = path.join(".");
45
+ const current = this.sources.paths[key];
46
+ if (current === void 0 || mode === "replace") {
47
+ this.sources.paths[key] = sourceId;
48
+ return;
49
+ }
50
+ if (Array.isArray(current)) {
51
+ if (!current.includes(sourceId)) current.push(sourceId);
52
+ return;
53
+ }
54
+ if (current !== sourceId) this.sources.paths[key] = [current, sourceId];
55
+ }
56
+ moveIfMissing(from, to) {
57
+ const fromKey = from.join(".");
58
+ const toKey = to.join(".");
59
+ if (this.sources.paths[fromKey] === void 0) return;
60
+ this.sources.paths[toKey] ??= this.sources.paths[fromKey];
61
+ delete this.sources.paths[fromKey];
62
+ }
63
+ delete(path) {
64
+ delete this.sources.paths[path.join(".")];
65
+ }
66
+ };
67
+ function recordSource(context, path, value, current, mode) {
68
+ if (!context || !path || !shouldTrackPath(path, value, current)) return;
69
+ context.tracker.record(path, context.sourceId, mode);
70
+ }
71
+ function recordNestedSources(context, path, value) {
72
+ if (!context || !path || !isPlainObject(value)) return;
73
+ for (const [key, child] of Object.entries(value)) {
74
+ if (child === void 0 || omitKeys.has(key)) continue;
75
+ const childPath = path.concat(key);
76
+ recordSource(context, childPath, child, void 0, "replace");
77
+ recordNestedSources(context, childPath, child);
78
+ }
79
+ }
80
+ function shouldTrackPath(path, value, current) {
81
+ if (path.length === 1) return true;
82
+ const [section, ...rest] = path;
83
+ if (!sectionKeySet.has(section) || rest.length === 0) return false;
84
+ if (section === "theme") return shouldTrackThemePath(rest, value, current);
85
+ if (section === "staticCss") return shouldTrackStaticCssPath(rest);
86
+ if (section === "conditions" || section === "globalVars") return rest.length === 1;
87
+ if (directFieldSections.has(section)) return rest.length <= 2;
88
+ return rest.length === 1;
89
+ }
90
+ function shouldTrackStaticCssPath(rest) {
91
+ return rest.length === 1 || (rest[0] === "recipes" || rest[0] === "patterns") && rest.length === 2;
92
+ }
93
+ function shouldTrackThemePath(rest, value, current) {
94
+ const [key] = rest;
95
+ if (key === "tokens" || key === "semanticTokens") {
96
+ if (rest.length < 3) return false;
97
+ const last = rest.at(-1);
98
+ return tokenKeys.has(last) || isPlainObject(value) || current !== void 0;
99
+ }
100
+ if (themeEntryKeys.has(key)) return rest.length <= 3;
101
+ if (key === "containerNames" || key === "colorPalette") return rest.length <= 2;
102
+ return rest.length <= 2;
103
+ }
104
+ function isPlainObject(value) {
105
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
106
+ const proto = Object.getPrototypeOf(value);
107
+ return proto === Object.prototype || proto === null;
108
+ }
109
+
110
+ // src/shared.ts
111
+ var omitKeys2 = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
112
+ function isPlainObject2(value) {
113
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
114
+ const proto = Object.getPrototypeOf(value);
115
+ return proto === Object.prototype || proto === null;
116
+ }
117
+ function clone(value) {
118
+ if (Array.isArray(value)) return value.map((item) => clone(item));
119
+ if (isPlainObject2(value)) {
120
+ const result = {};
121
+ for (const [key, item] of Object.entries(value)) {
122
+ if (item !== void 0 && !omitKeys2.has(key)) result[key] = clone(item);
123
+ }
124
+ return result;
125
+ }
126
+ return value;
127
+ }
128
+
129
+ // src/merge.ts
130
+ var sectionKeys = [
131
+ "conditions",
132
+ "theme",
133
+ "patterns",
134
+ "utilities",
135
+ "globalCss",
136
+ "globalVars",
137
+ "globalFontface",
138
+ "globalPositionTry",
139
+ "staticCss",
140
+ "themes"
141
+ ];
142
+ var sectionKeySet2 = new Set(sectionKeys);
143
+ var runtimeOnlyKeys = /* @__PURE__ */ new Set(["presets", "plugins", "hooks", "name", "extend"]);
144
+ var tokenKeys2 = /* @__PURE__ */ new Set(["description", "extensions", "type", "value", "deprecated"]);
145
+ function mergeConfigsWithSources(configs) {
146
+ const tracker = new SourceTracker(configs.map((item) => item.source));
147
+ const config = mergeConfigs(configs, tracker);
148
+ return { config, sources: tracker.sources };
149
+ }
150
+ function mergeConfigs(configs, tracker) {
151
+ const result = {};
152
+ const sections = Object.fromEntries(sectionKeys.map((key) => [key, {}]));
153
+ for (let index = 0; index < configs.length; index++) {
154
+ const config = tracker ? configs[index].config : configs[index];
155
+ const context = tracker ? { tracker, sourceId: index, path: [] } : void 0;
156
+ for (const [key, value] of Object.entries(config)) {
157
+ if (value === void 0 || sectionKeySet2.has(key) || runtimeOnlyKeys.has(key) || omitKeys2.has(key)) continue;
158
+ result[key] = clone(value);
159
+ context?.tracker.record([key], context.sourceId, "replace");
160
+ }
161
+ for (const key of sectionKeys) {
162
+ const section = config[key];
163
+ if (isPlainObject2(section)) mergeSectionInto(key, sections[key], section, context);
164
+ }
165
+ }
166
+ for (const key of sectionKeys) {
167
+ if (Object.keys(sections[key]).length > 0) result[key] = sections[key];
168
+ }
169
+ if (result.theme?.tokens) normalizeNestedTokens(result.theme.tokens, tracker);
170
+ return result;
171
+ }
172
+ function mergeSectionInto(sectionName, target, section, context) {
173
+ const childContext = context && { ...context, path: [sectionName] };
174
+ for (const [key, value] of Object.entries(section)) {
175
+ if (key === "extend" || value === void 0 || omitKeys2.has(key)) continue;
176
+ mergeValue(target, key, value, "replace", childContext);
177
+ }
178
+ if (section.extend === void 0) return;
179
+ if (!isPlainObject2(section.extend)) {
180
+ throw new PandaError("CONFIG_ERROR", `\u{1F4A5} Config section \`${sectionName}.extend\` must be an object.`);
181
+ }
182
+ for (const [key, value] of Object.entries(section.extend)) {
183
+ if (value === void 0 || omitKeys2.has(key)) continue;
184
+ mergeValue(target, key, value, "concat", childContext);
185
+ }
186
+ }
187
+ function mergeValue(target, key, value, arrayMode, context) {
188
+ const current = target[key];
189
+ const path = context?.path.concat(key);
190
+ if (Array.isArray(current) && Array.isArray(value)) {
191
+ target[key] = arrayMode === "concat" ? current.concat(clone(value)) : clone(value);
192
+ recordSource(context, path, value, current, arrayMode === "concat" ? "append" : "replace");
193
+ return;
194
+ }
195
+ if (isPlainObject2(current) && isPlainObject2(value)) {
196
+ recordSource(context, path, value, current, "append");
197
+ const childContext = context && path ? { ...context, path } : void 0;
198
+ for (const [childKey, childValue] of Object.entries(value)) {
199
+ if (childValue !== void 0 && !omitKeys2.has(childKey))
200
+ mergeValue(current, childKey, childValue, arrayMode, childContext);
201
+ }
202
+ return;
203
+ }
204
+ target[key] = clone(value);
205
+ recordSource(context, path, value, current, "replace");
206
+ recordNestedSources(context, path, value);
207
+ }
208
+ function normalizeNestedTokens(tokens, tracker) {
209
+ const stack = [{ value: tokens, path: ["theme", "tokens"] }];
210
+ while (stack.length > 0) {
211
+ const current = stack.pop();
212
+ for (const [key, value] of Object.entries(current.value)) {
213
+ if (!isPlainObject2(value)) continue;
214
+ if (isValidToken(value)) {
215
+ normalizeToken(value, tracker, current.path.concat(key));
216
+ } else {
217
+ stack.push({ value, path: current.path.concat(key) });
218
+ }
219
+ }
220
+ }
221
+ }
222
+ function normalizeToken(token, tracker, path) {
223
+ let hasNestedKeys = false;
224
+ for (const key of Object.keys(token)) {
225
+ if (!tokenKeys2.has(key)) {
226
+ hasNestedKeys = true;
227
+ break;
228
+ }
229
+ }
230
+ if (!hasNestedKeys) return;
231
+ token.DEFAULT ||= {};
232
+ for (const key of tokenKeys2) {
233
+ if (token[key] == null) continue;
234
+ const moved = !token.DEFAULT[key];
235
+ token.DEFAULT[key] ||= token[key];
236
+ if (moved) tracker?.moveIfMissing(path.concat(key), path.concat("DEFAULT", key));
237
+ else tracker?.delete(path.concat(key));
238
+ delete token[key];
239
+ }
240
+ }
241
+ function isValidToken(token) {
242
+ return Object.hasOwnProperty.call(token, "value");
243
+ }
244
+
245
+ export {
246
+ PandaError,
247
+ isPlainObject2 as isPlainObject,
248
+ mergeConfigsWithSources,
249
+ mergeConfigs
250
+ };
@@ -0,0 +1,106 @@
1
+ // src/serialize.ts
2
+ import {
3
+ normalizeImportMap
4
+ } from "@pandacss/compiler-shared";
5
+ import { stringify } from "javascript-stringify";
6
+
7
+ // src/hooks.ts
8
+ var compact = (value) => Object.fromEntries(Object.entries(value ?? {}).filter(([, item]) => item !== void 0));
9
+ function serializeHooks(config, callbacks, sanitize2, hashCallbackSource2) {
10
+ const parserBefore = collectPluginHookHandlers(config, "parser:before").map((entry, index) => {
11
+ const hook = normalizeHook(entry.value, "parser:before");
12
+ const id = `plugins.${entry.pluginIndex}.hooks.parser:before.${index}`;
13
+ callbacks["parser:before"] ??= {};
14
+ callbacks["parser:before"][id] = hook.handler;
15
+ return compact({
16
+ id,
17
+ name: entry.name,
18
+ filter: hook.filter ? sanitize2(
19
+ hook.filter,
20
+ ["plugins", String(entry.pluginIndex), "hooks", "parser:before", "filter"],
21
+ callbacks
22
+ ) : void 0,
23
+ hash: hashCallbackSource2(hook.handler)
24
+ });
25
+ });
26
+ return parserBefore.length > 0 ? { "parser:before": parserBefore } : void 0;
27
+ }
28
+ function collectPluginHookHandlers(config, name) {
29
+ const entries = [];
30
+ const plugins = [...config.plugins ?? []];
31
+ plugins.forEach((plugin, pluginIndex) => {
32
+ const value = plugin.hooks?.[name];
33
+ if (value) entries.push({ pluginIndex, name: plugin.name, value });
34
+ });
35
+ return entries;
36
+ }
37
+ function normalizeHook(value, name) {
38
+ if (typeof value === "function") return { handler: value };
39
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
40
+ throw new Error(`Invalid ${name} hook. Expected a function or { filter, handler }.`);
41
+ }
42
+ const record = value;
43
+ if (typeof record.handler !== "function") {
44
+ throw new Error(`Invalid ${name} hook. Expected a function or { filter, handler }.`);
45
+ }
46
+ return { filter: record.filter, handler: record.handler };
47
+ }
48
+
49
+ // src/serialize.ts
50
+ var compact2 = (value) => Object.fromEntries(Object.entries(value ?? {}).filter(([, item]) => item !== void 0));
51
+ var runtimeOnlyKeys = /* @__PURE__ */ new Set(["hooks", "plugins", "presets", "name"]);
52
+ function createConfigSnapshot(config) {
53
+ const callbacks = {};
54
+ const hooks = serializeHooks(config, callbacks, sanitize, hashCallbackSource);
55
+ const serialized = {
56
+ ...sanitize(config, [], callbacks),
57
+ importMap: normalizeImportMap(config)
58
+ };
59
+ attachPatternCodegenSource(serialized, config);
60
+ return { config: serialized, callbacks, ...hooks ? { hooks } : {} };
61
+ }
62
+ function sanitize(value, path, callbacks) {
63
+ if (typeof value === "function") return serializeFunction(value, path, callbacks);
64
+ if (value instanceof RegExp) return { kind: "regex", source: value.source, flags: value.flags };
65
+ if (Array.isArray(value)) return value.map((item, index) => sanitize(item, path.concat(String(index)), callbacks));
66
+ if (!value || typeof value !== "object") return value;
67
+ return Object.fromEntries(
68
+ Object.entries(value).filter(([key]) => path.length > 0 || !runtimeOnlyKeys.has(key)).map(([key, item]) => [key, sanitize(item, path.concat(key), callbacks)]).filter(([, item]) => item !== void 0)
69
+ );
70
+ }
71
+ function serializeFunction(fn, path, callbacks) {
72
+ const ref = getCallbackRef(path);
73
+ if (!ref) return void 0;
74
+ callbacks[ref.kind] ??= {};
75
+ callbacks[ref.kind][ref.id] = fn;
76
+ return { kind: "js-callback", id: ref.id, hash: hashCallbackSource(fn) };
77
+ }
78
+ function hashCallbackSource(fn) {
79
+ const source = stringify(fn) ?? String(fn);
80
+ let hash = 5381;
81
+ for (let i = source.length - 1; i >= 0; i--) hash = hash * 33 ^ source.charCodeAt(i);
82
+ return `fn1-${(hash >>> 0).toString(36)}`;
83
+ }
84
+ function getCallbackRef(path) {
85
+ const key = path.at(-1);
86
+ if (path[0] === "utilities" && key === "transform") return { kind: "utility.transform", id: path.join(".") };
87
+ if (path[0] === "utilities" && key === "values") return { kind: "utility.values", id: path.join(".") };
88
+ if (path[0] === "patterns" && key === "transform") return { kind: "pattern.transform", id: path.join(".") };
89
+ if (path[0] === "patterns" && key === "defaultValues") return { kind: "pattern.defaultValues", id: path.join(".") };
90
+ }
91
+ function attachPatternCodegenSource(serialized, config) {
92
+ const patterns = config.patterns;
93
+ const serializedPatterns = serialized.patterns;
94
+ if (!patterns || !serializedPatterns) return;
95
+ for (const [name, pattern] of Object.entries(patterns)) {
96
+ if (typeof pattern?.transform !== "function") continue;
97
+ const source = stringify(compact2({ transform: pattern.transform, defaultValues: pattern.defaultValues })) ?? "";
98
+ if (serializedPatterns[name]) serializedPatterns[name].codegenSource = source;
99
+ }
100
+ }
101
+
102
+ export {
103
+ collectPluginHookHandlers,
104
+ normalizeHook,
105
+ createConfigSnapshot
106
+ };
package/dist/index.d.ts CHANGED
@@ -1,59 +1,84 @@
1
- import * as _pandacss_types from '@pandacss/types';
2
- import { Config, ConfigTsOptions, PandaHooks, LoadConfigResult } from '@pandacss/types';
3
- export { diffConfigs } from './diff-config.js';
4
- import { TSConfig } from 'pkg-types';
5
- import { P as PathMapping } from './ts-config-paths-qwrwgu2Q.js';
6
- export { c as convertTsPathsToRegexes } from './ts-config-paths-qwrwgu2Q.js';
7
- export { mergeConfigs, mergeHooks } from './merge-config.js';
1
+ import { SerializedConfig, ProjectCallbacks, ProjectHooks, ConfigSnapshot, DiffConfigResult } from '@pandacss/compiler-shared';
2
+ export { DiffConfigResult } from '@pandacss/compiler-shared';
3
+ import { HookRegistry, Config } from '@pandacss/types';
4
+ import { C as ConfigSources } from './merge-Bt4BM1CH.js';
5
+ export { a as ConfigSourceEntry, m as mergeConfigs } from './merge-Bt4BM1CH.js';
6
+ export { ConfigSnapshot, createConfigSnapshot } from './serialize.js';
8
7
 
9
- interface ConfigFileOptions {
8
+ interface PluginHookEntry<Name extends keyof HookRegistry> {
9
+ pluginIndex: number;
10
+ name: string | undefined;
11
+ value: HookRegistry[Name];
12
+ }
13
+ type HostHooks = {
14
+ 'codegen:prepare'?: Array<PluginHookEntry<'codegen:prepare'>>;
15
+ 'codegen:done'?: Array<PluginHookEntry<'codegen:done'>>;
16
+ };
17
+
18
+ interface LoadConfigOptions {
10
19
  cwd: string;
20
+ /** Explicit config file path (relative to `cwd`); otherwise discovered upward. */
11
21
  file?: string;
22
+ /** Track which authored config or preset contributed resolved config entries. */
23
+ trackSources?: boolean;
12
24
  }
13
- interface BundleConfigResult<T = Config> {
14
- config: T;
15
- dependencies: string[];
25
+ interface LoadConfigResult {
26
+ /** Absolute path to the resolved config file. */
16
27
  path: string;
28
+ /** JSON-safe, callback-lowered config; patterns carry `codegenSource`. */
29
+ config: SerializedConfig;
30
+ /** Live utility/pattern transform callbacks, paired with `config`. */
31
+ callbacks: ProjectCallbacks;
32
+ /** JSON-safe hot-path hook metadata paired with live callbacks. */
33
+ hooks?: ProjectHooks;
34
+ /** Live JS host hooks that do not cross into Rust. */
35
+ hostHooks?: HostHooks;
36
+ /** Module ids the config depends on, for watch-mode invalidation. */
37
+ dependencies: string[];
38
+ metadata?: {
39
+ sources?: ConfigSources;
40
+ };
17
41
  }
18
42
 
19
- declare function bundleConfig(options: ConfigFileOptions): Promise<BundleConfigResult>;
20
-
21
- declare function findConfig(options: Partial<ConfigFileOptions>): string;
22
-
23
- interface GetDepsOptions {
24
- filename: string;
25
- ext: string;
26
- cwd: string;
27
- seen: Set<string>;
28
- baseUrl: string | undefined;
29
- pathMappings: PathMapping[];
30
- foundModuleAliases: Map<string, string>;
31
- compilerOptions?: TSConfig['compilerOptions'];
32
- }
33
- declare function getConfigDependencies(filePath: string, tsOptions?: ConfigTsOptions, compilerOptions?: TSConfig['compilerOptions']): {
34
- deps: Set<string>;
35
- aliases: Map<string, string>;
36
- };
37
-
38
- type Extendable<T> = T & {
39
- extend?: T;
40
- };
41
- type ExtendableConfig = Extendable<Config>;
42
43
  /**
43
- * Recursively merge all presets into a single config (depth-first using stack)
44
+ * Load and serialize a user's Panda config into a compiler-ready snapshot.
45
+ * Bundles in memory (Rolldown + `data:` URL), then lowers functions to callback
46
+ * refs and captures pattern transform source. The result feeds
47
+ * `createCompilerFromSnapshot({ config, callbacks })`.
48
+ *
49
+ * Authored presets are resolved before defaults are applied; live hooks remain
50
+ * in the JS host and only parser:before metadata crosses into Rust.
44
51
  */
45
- declare function getResolvedConfig(config: ExtendableConfig, cwd: string, hooks?: Partial<PandaHooks>): Promise<Config>;
52
+ declare function loadConfig(options: LoadConfigOptions): Promise<LoadConfigResult>;
46
53
 
47
54
  /**
48
- * Find, load and resolve the final config (including presets)
55
+ * Structurally diff two serialized configs and map each change to the coarse
56
+ * `CodegenDependency` bits the Rust engine regenerates by. Ported from v1's
57
+ * `diffConfigs` (`packages/config/src/diff-config.ts`), but targeting the
58
+ * engine's `generateAffectedArtifacts(CodegenDependency[])` seam rather than v1's
59
+ * per-file `ArtifactId` set.
60
+ *
61
+ * Callback refs carry a source hash, and hook filters are serialized data in
62
+ * the optional `hooks` snapshot, so callback and filter edits remain visible to
63
+ * this structural diff.
49
64
  */
50
- declare function loadConfig(options: ConfigFileOptions): Promise<_pandacss_types.LoadConfigResult>;
65
+ declare function diffConfig(prev: SerializedConfig | ConfigSnapshot | undefined, next: SerializedConfig | ConfigSnapshot): DiffConfigResult;
66
+
67
+ /** Locate the user's `panda.config.{ts,js,…}`, walking up from `cwd`. */
68
+ declare function findConfig(options: {
69
+ cwd: string;
70
+ file?: string;
71
+ }): string;
51
72
 
73
+ interface BundleConfigResult<T = Config> {
74
+ config: T;
75
+ dependencies: string[];
76
+ }
52
77
  /**
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
78
+ * Bundle a config (or preset) module in memory with Rolldown and evaluate it via
79
+ * a `data:` URL no temp file is written. Mirrors the legacy in-memory loader
80
+ * without re-adding `bundle-n-require`.
56
81
  */
57
- declare function resolveConfig(result: BundleConfigResult, cwd: string): Promise<LoadConfigResult>;
82
+ declare function bundleConfig<T extends Config = Config>(filepath: string, cwd: string): Promise<BundleConfigResult<T>>;
58
83
 
59
- export { type BundleConfigResult, type GetDepsOptions, bundleConfig, findConfig, getConfigDependencies, getResolvedConfig, loadConfig, resolveConfig };
84
+ export { type BundleConfigResult, ConfigSources, type LoadConfigOptions, type LoadConfigResult, bundleConfig, diffConfig, findConfig, loadConfig };