@bamboocss/config 1.11.1 → 1.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,630 +1,561 @@
1
- import {
2
- diffConfigs
3
- } from "./chunk-6TQW6KOI.mjs";
4
- import {
5
- SEP,
6
- formatPath,
7
- getReferences,
8
- isTokenReference,
9
- isValidToken,
10
- mergeConfigs,
11
- mergeHooks,
12
- serializeTokenValue
13
- } from "./chunk-RIBK22OM.mjs";
14
- import {
15
- resolveTsPathPattern
16
- } from "./chunk-RPIVZP2I.mjs";
17
-
18
- // src/bundle-config.ts
1
+ import { t as diffConfigs } from "./diff-config-KDQWMnQN.mjs";
2
+ import { resolveTsPathPattern } from "./resolve-ts-path-pattern.mjs";
3
+ import { a as isValidToken, i as isTokenReference, n as formatPath, o as serializeTokenValue, r as getReferences, s as mergeHooks, t as mergeConfigs } from "./merge-config-DI__LOWx.mjs";
19
4
  import { logger } from "@bamboocss/logger";
20
- import { BambooError as BambooError2 } from "@bamboocss/shared";
5
+ import { BAMBOO_CONFIG_NAME, BambooError, getUnit, isObject, isString, omit, parseJson, pick, stringifyJson, traverse, walkObject } from "@bamboocss/shared";
21
6
  import { bundleNRequire } from "bundle-n-require";
22
-
23
- // src/find-config.ts
24
- import { BambooError } from "@bamboocss/shared";
25
7
  import findUp from "escalade/sync";
26
- import { resolve } from "path";
27
-
28
- // src/is-bamboo-config.ts
29
- var configName = "bamboo";
30
- var bambooConfigFiles = /* @__PURE__ */ new Set([
31
- `${configName}.config.ts`,
32
- `${configName}.config.js`,
33
- `${configName}.config.mts`,
34
- `${configName}.config.mjs`,
35
- `${configName}.config.cts`,
36
- `${configName}.config.cjs`
8
+ import path, { resolve } from "path";
9
+ import fs from "fs";
10
+ import ts from "typescript";
11
+ import { preset as presetBase } from "@bamboocss/preset-base";
12
+ import { preset as presetBamboo } from "@bamboocss/preset-bamboo";
13
+ //#region src/is-bamboo-config.ts
14
+ const configName = "bamboo";
15
+ const bambooConfigFiles = new Set([
16
+ `${configName}.config.ts`,
17
+ `${configName}.config.js`,
18
+ `${configName}.config.mts`,
19
+ `${configName}.config.mjs`,
20
+ `${configName}.config.cts`,
21
+ `${configName}.config.cjs`
37
22
  ]);
38
- var isBambooConfig = (file) => bambooConfigFiles.has(file);
39
-
40
- // src/find-config.ts
23
+ const isBambooConfig = (file) => bambooConfigFiles.has(file);
24
+ //#endregion
25
+ //#region src/find-config.ts
41
26
  function findConfig(options) {
42
- const { cwd = process.cwd(), file } = options;
43
- if (file) {
44
- return resolve(cwd, file);
45
- }
46
- const configPath = findUp(cwd, (_dir, paths) => paths.find(isBambooConfig));
47
- if (!configPath) {
48
- throw new BambooError(
49
- "CONFIG_NOT_FOUND",
50
- `Cannot find config file \`bamboo.config.{ts,js,mjs,mts}\`. Did you forget to run \`bamboo init\`?`
51
- );
52
- }
53
- return configPath;
27
+ const { cwd = process.cwd(), file } = options;
28
+ if (file) return resolve(cwd, file);
29
+ const configPath = findUp(cwd, (_dir, paths) => paths.find(isBambooConfig));
30
+ if (!configPath) throw new BambooError("CONFIG_NOT_FOUND", `Cannot find config file \`bamboo.config.{ts,js,mjs,mts}\`. Did you forget to run \`bamboo init\`?`);
31
+ return configPath;
54
32
  }
55
-
56
- // src/bundle-config.ts
33
+ //#endregion
34
+ //#region src/bundle-config.ts
57
35
  async function bundle(filepath, cwd) {
58
- const { mod, dependencies } = await bundleNRequire(filepath, {
59
- cwd,
60
- interopDefault: true
61
- });
62
- const config = mod?.default ?? mod;
63
- return {
64
- config,
65
- dependencies
66
- };
36
+ const { mod, dependencies } = await bundleNRequire(filepath, {
37
+ cwd,
38
+ interopDefault: true
39
+ });
40
+ return {
41
+ config: mod?.default ?? mod,
42
+ dependencies
43
+ };
67
44
  }
68
45
  async function bundleConfig(options) {
69
- const { cwd, file } = options;
70
- const filePath = findConfig({ cwd, file });
71
- logger.debug("config:path", filePath);
72
- const result = await bundle(filePath, cwd);
73
- if (typeof result.config !== "object") {
74
- throw new BambooError2("CONFIG_ERROR", `\u{1F4A5} Config must export or return an object.`);
75
- }
76
- result.config.outdir ??= "styled-system";
77
- result.config.validation ??= "warn";
78
- return {
79
- ...result,
80
- config: result.config,
81
- path: filePath
82
- };
46
+ const { cwd, file } = options;
47
+ const filePath = findConfig({
48
+ cwd,
49
+ file
50
+ });
51
+ logger.debug("config:path", filePath);
52
+ const result = await bundle(filePath, cwd);
53
+ if (typeof result.config !== "object") throw new BambooError("CONFIG_ERROR", `💥 Config must export or return an object.`);
54
+ result.config.outdir ??= "styled-system";
55
+ result.config.validation ??= "warn";
56
+ return {
57
+ ...result,
58
+ config: result.config,
59
+ path: filePath
60
+ };
83
61
  }
84
-
85
- // src/get-mod-deps.ts
86
- import fs from "fs";
87
- import path from "path";
88
- import ts from "typescript";
89
-
90
- // src/ts-config-paths.ts
91
- import { resolve as resolve2 } from "path";
62
+ //#endregion
63
+ //#region src/ts-config-paths.ts
64
+ /**
65
+ * @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/mappings.ts
66
+ */
92
67
  function convertTsPathsToRegexes(paths, baseUrl) {
93
- const sortedPatterns = Object.keys(paths).sort((a, b) => getPrefixLength(b) - getPrefixLength(a));
94
- const resolved = [];
95
- for (let pattern of sortedPatterns) {
96
- const relativePaths = paths[pattern];
97
- pattern = escapeStringRegexp(pattern).replace(/\*/g, "(.+)");
98
- resolved.push({
99
- pattern: new RegExp("^" + pattern + "$"),
100
- paths: relativePaths.map((relativePath) => resolve2(baseUrl, relativePath))
101
- });
102
- }
103
- return resolved;
68
+ const sortedPatterns = Object.keys(paths).sort((a, b) => getPrefixLength(b) - getPrefixLength(a));
69
+ const resolved = [];
70
+ for (let pattern of sortedPatterns) {
71
+ const relativePaths = paths[pattern];
72
+ pattern = escapeStringRegexp(pattern).replace(/\*/g, "(.+)");
73
+ resolved.push({
74
+ pattern: new RegExp("^" + pattern + "$"),
75
+ paths: relativePaths.map((relativePath) => resolve(baseUrl, relativePath))
76
+ });
77
+ }
78
+ return resolved;
104
79
  }
105
80
  function getPrefixLength(pattern) {
106
- const prefixLength = pattern.indexOf("*");
107
- return pattern.substr(0, prefixLength).length;
81
+ const prefixLength = pattern.indexOf("*");
82
+ return pattern.substr(0, prefixLength).length;
108
83
  }
109
84
  function escapeStringRegexp(string) {
110
- return string.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/-/g, "\\x2d");
85
+ return string.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/-/g, "\\x2d");
111
86
  }
112
-
113
- // src/get-mod-deps.ts
114
- var jsExtensions = [".js", ".cjs", ".mjs"];
115
- var jsResolutionOrder = ["", ".js", ".cjs", ".mjs", ".ts", ".cts", ".mts", ".jsx", ".tsx"];
116
- var tsResolutionOrder = ["", ".ts", ".cts", ".mts", ".tsx", ".js", ".cjs", ".mjs", ".jsx"];
87
+ //#endregion
88
+ //#region src/get-mod-deps.ts
89
+ const jsExtensions = [
90
+ ".js",
91
+ ".cjs",
92
+ ".mjs"
93
+ ];
94
+ const jsResolutionOrder = [
95
+ "",
96
+ ".js",
97
+ ".cjs",
98
+ ".mjs",
99
+ ".ts",
100
+ ".cts",
101
+ ".mts",
102
+ ".jsx",
103
+ ".tsx"
104
+ ];
105
+ const tsResolutionOrder = [
106
+ "",
107
+ ".ts",
108
+ ".cts",
109
+ ".mts",
110
+ ".tsx",
111
+ ".js",
112
+ ".cjs",
113
+ ".mjs",
114
+ ".jsx"
115
+ ];
117
116
  function resolveWithExtension(file, extensions) {
118
- for (const ext of extensions) {
119
- const full = `${file}${ext}`;
120
- if (fs.existsSync(full) && fs.statSync(full).isFile()) {
121
- return full;
122
- }
123
- }
124
- for (const ext of extensions) {
125
- const full = `${file}/index${ext}`;
126
- if (fs.existsSync(full)) {
127
- return full;
128
- }
129
- }
130
- return null;
117
+ for (const ext of extensions) {
118
+ const full = `${file}${ext}`;
119
+ if (fs.existsSync(full) && fs.statSync(full).isFile()) return full;
120
+ }
121
+ for (const ext of extensions) {
122
+ const full = `${file}/index${ext}`;
123
+ if (fs.existsSync(full)) return full;
124
+ }
125
+ return null;
131
126
  }
132
- var importRegex = /import[\s\S]*?['"](.{3,}?)['"]/gi;
133
- var importFromRegex = /import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
134
- var requireRegex = /require\(['"`](.+)['"`]\)/gi;
135
- var exportRegex = /export[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
127
+ const importRegex = /import[\s\S]*?['"](.{3,}?)['"]/gi;
128
+ const importFromRegex = /import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
129
+ const requireRegex = /require\(['"`](.+)['"`]\)/gi;
130
+ const exportRegex = /export[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
136
131
  function getDeps(opts, fromAlias) {
137
- const { filename, seen } = opts;
138
- const { moduleResolution: _, ...compilerOptions } = opts.compilerOptions ?? {};
139
- const absoluteFile = resolveWithExtension(
140
- path.resolve(opts.cwd, filename),
141
- jsExtensions.includes(opts.ext) ? jsResolutionOrder : tsResolutionOrder
142
- );
143
- if (absoluteFile === null) return;
144
- if (fromAlias) {
145
- opts.foundModuleAliases.set(fromAlias, absoluteFile);
146
- }
147
- if (seen.size > 1 && seen.has(absoluteFile)) return;
148
- seen.add(absoluteFile);
149
- const contents = fs.readFileSync(absoluteFile, "utf-8");
150
- const fileDeps = [
151
- ...contents.matchAll(importRegex),
152
- ...contents.matchAll(importFromRegex),
153
- ...contents.matchAll(requireRegex),
154
- ...contents.matchAll(exportRegex)
155
- ];
156
- if (!fileDeps.length) return;
157
- const nextOpts = {
158
- // Resolve new base for new imports/requires
159
- cwd: path.dirname(absoluteFile),
160
- ext: path.extname(absoluteFile),
161
- seen,
162
- baseUrl: opts.baseUrl,
163
- pathMappings: opts.pathMappings,
164
- foundModuleAliases: opts.foundModuleAliases
165
- };
166
- fileDeps.forEach((match) => {
167
- const mod = match[1];
168
- if (mod[0] === ".") {
169
- getDeps(Object.assign({}, nextOpts, { filename: mod }));
170
- return;
171
- }
172
- try {
173
- const found = ts.resolveModuleName(mod, absoluteFile, compilerOptions, ts.sys).resolvedModule;
174
- if (found && found.extension.endsWith("ts")) {
175
- getDeps(Object.assign({}, nextOpts, { filename: found.resolvedFileName }));
176
- return;
177
- }
178
- if (!opts.pathMappings) return;
179
- const filename2 = resolveTsPathPattern(opts.pathMappings, mod);
180
- if (!filename2) return;
181
- getDeps(Object.assign({}, nextOpts, { filename: filename2 }), mod);
182
- } catch {
183
- }
184
- });
132
+ const { filename, seen } = opts;
133
+ const { moduleResolution: _, ...compilerOptions } = opts.compilerOptions ?? {};
134
+ const absoluteFile = resolveWithExtension(path.resolve(opts.cwd, filename), jsExtensions.includes(opts.ext) ? jsResolutionOrder : tsResolutionOrder);
135
+ if (absoluteFile === null) return;
136
+ if (fromAlias) opts.foundModuleAliases.set(fromAlias, absoluteFile);
137
+ if (seen.size > 1 && seen.has(absoluteFile)) return;
138
+ seen.add(absoluteFile);
139
+ const contents = fs.readFileSync(absoluteFile, "utf-8");
140
+ const fileDeps = [
141
+ ...contents.matchAll(importRegex),
142
+ ...contents.matchAll(importFromRegex),
143
+ ...contents.matchAll(requireRegex),
144
+ ...contents.matchAll(exportRegex)
145
+ ];
146
+ if (!fileDeps.length) return;
147
+ const nextOpts = {
148
+ cwd: path.dirname(absoluteFile),
149
+ ext: path.extname(absoluteFile),
150
+ seen,
151
+ baseUrl: opts.baseUrl,
152
+ pathMappings: opts.pathMappings,
153
+ foundModuleAliases: opts.foundModuleAliases
154
+ };
155
+ fileDeps.forEach((match) => {
156
+ const mod = match[1];
157
+ if (mod[0] === ".") {
158
+ getDeps(Object.assign({}, nextOpts, { filename: mod }));
159
+ return;
160
+ }
161
+ try {
162
+ const found = ts.resolveModuleName(mod, absoluteFile, compilerOptions, ts.sys).resolvedModule;
163
+ if (found && found.extension.endsWith("ts")) {
164
+ getDeps(Object.assign({}, nextOpts, { filename: found.resolvedFileName }));
165
+ return;
166
+ }
167
+ if (!opts.pathMappings) return;
168
+ const filename = resolveTsPathPattern(opts.pathMappings, mod);
169
+ if (!filename) return;
170
+ getDeps(Object.assign({}, nextOpts, { filename }), mod);
171
+ } catch {}
172
+ });
185
173
  }
186
174
  function getConfigDependencies(filePath, tsOptions = { pathMappings: [] }, compilerOptions) {
187
- if (filePath === null) return { deps: /* @__PURE__ */ new Set(), aliases: /* @__PURE__ */ new Map() };
188
- const foundModuleAliases = /* @__PURE__ */ new Map();
189
- const deps = /* @__PURE__ */ new Set();
190
- deps.add(filePath);
191
- getDeps({
192
- filename: filePath,
193
- ext: path.extname(filePath),
194
- cwd: path.dirname(filePath),
195
- seen: deps,
196
- baseUrl: tsOptions.baseUrl,
197
- pathMappings: tsOptions.pathMappings ?? [],
198
- foundModuleAliases,
199
- compilerOptions
200
- });
201
- return { deps, aliases: foundModuleAliases };
175
+ if (filePath === null) return {
176
+ deps: /* @__PURE__ */ new Set(),
177
+ aliases: /* @__PURE__ */ new Map()
178
+ };
179
+ const foundModuleAliases = /* @__PURE__ */ new Map();
180
+ const deps = /* @__PURE__ */ new Set();
181
+ deps.add(filePath);
182
+ getDeps({
183
+ filename: filePath,
184
+ ext: path.extname(filePath),
185
+ cwd: path.dirname(filePath),
186
+ seen: deps,
187
+ baseUrl: tsOptions.baseUrl,
188
+ pathMappings: tsOptions.pathMappings ?? [],
189
+ foundModuleAliases,
190
+ compilerOptions
191
+ });
192
+ return {
193
+ deps,
194
+ aliases: foundModuleAliases
195
+ };
202
196
  }
203
-
204
- // src/get-resolved-config.ts
205
- import { omit, pick, traverse } from "@bamboocss/shared";
206
- var hookUtils = {
207
- omit,
208
- pick,
209
- traverse
197
+ //#endregion
198
+ //#region src/get-resolved-config.ts
199
+ const hookUtils$1 = {
200
+ omit,
201
+ pick,
202
+ traverse
210
203
  };
204
+ /**
205
+ * Recursively merge all presets into a single config (depth-first using stack)
206
+ */
211
207
  async function getResolvedConfig(config, cwd, hooks) {
212
- const stack = [config];
213
- const configs = [];
214
- while (stack.length > 0) {
215
- const current = stack.pop();
216
- const subPresets = current.presets ?? [];
217
- for (const subPreset of subPresets) {
218
- let presetConfig;
219
- let presetName;
220
- if (typeof subPreset === "string") {
221
- const presetModule = await bundle(subPreset, cwd);
222
- presetConfig = presetModule.config;
223
- presetName = subPreset;
224
- } else {
225
- presetConfig = await subPreset;
226
- presetName = presetConfig.name || "unknown-preset";
227
- }
228
- if (hooks?.["preset:resolved"]) {
229
- const resolvedPreset = await hooks["preset:resolved"]({
230
- preset: presetConfig,
231
- name: presetName,
232
- utils: hookUtils
233
- });
234
- if (resolvedPreset !== void 0) {
235
- presetConfig = resolvedPreset;
236
- }
237
- }
238
- stack.push(presetConfig);
239
- }
240
- configs.unshift(current);
241
- }
242
- const merged = mergeConfigs(configs);
243
- merged.presets = configs.slice(0, -1);
244
- return merged;
208
+ const stack = [config];
209
+ const configs = [];
210
+ while (stack.length > 0) {
211
+ const current = stack.pop();
212
+ const subPresets = current.presets ?? [];
213
+ for (const subPreset of subPresets) {
214
+ let presetConfig;
215
+ let presetName;
216
+ if (typeof subPreset === "string") {
217
+ presetConfig = (await bundle(subPreset, cwd)).config;
218
+ presetName = subPreset;
219
+ } else {
220
+ presetConfig = await subPreset;
221
+ presetName = presetConfig.name || "unknown-preset";
222
+ }
223
+ if (hooks?.["preset:resolved"]) {
224
+ const resolvedPreset = await hooks["preset:resolved"]({
225
+ preset: presetConfig,
226
+ name: presetName,
227
+ utils: hookUtils$1
228
+ });
229
+ if (resolvedPreset !== void 0) presetConfig = resolvedPreset;
230
+ }
231
+ stack.push(presetConfig);
232
+ }
233
+ configs.unshift(current);
234
+ }
235
+ const merged = mergeConfigs(configs);
236
+ merged.presets = configs.slice(0, -1);
237
+ return merged;
245
238
  }
246
-
247
- // src/resolve-config.ts
248
- import { logger as logger3 } from "@bamboocss/logger";
249
- import { BAMBOO_CONFIG_NAME, omit as omit2, parseJson, pick as pick2, stringifyJson, traverse as traverse2 } from "@bamboocss/shared";
250
-
251
- // src/bundled-preset.ts
252
- import { preset as presetBase } from "@bamboocss/preset-base";
253
- import { preset as presetBamboo } from "@bamboocss/preset-bamboo";
254
- var bundledPresets = {
255
- "@bamboocss/preset-base": presetBase,
256
- "@bamboocss/preset-bamboo": presetBamboo,
257
- "@bamboocss/dev/presets": presetBamboo
239
+ //#endregion
240
+ //#region src/bundled-preset.ts
241
+ const bundledPresets = {
242
+ "@bamboocss/preset-base": presetBase,
243
+ "@bamboocss/preset-bamboo": presetBamboo,
244
+ "@bamboocss/dev/presets": presetBamboo
258
245
  };
259
- var bundledPresetsNames = Object.keys(bundledPresets);
260
- var isBundledPreset = (preset) => bundledPresetsNames.includes(preset);
261
- var getBundledPreset = (preset) => {
262
- return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
246
+ const bundledPresetsNames = Object.keys(bundledPresets);
247
+ const isBundledPreset = (preset) => bundledPresetsNames.includes(preset);
248
+ const getBundledPreset = (preset) => {
249
+ return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
263
250
  };
264
-
265
- // src/validate-config.ts
266
- import { logger as logger2 } from "@bamboocss/logger";
267
- import { BambooError as BambooError3 } from "@bamboocss/shared";
268
-
269
- // src/validation/validate-artifact.ts
270
- var validateArtifactNames = (names, addError) => {
271
- names.recipes.forEach((recipeName) => {
272
- if (names.slotRecipes.has(recipeName)) {
273
- addError("recipes", `This recipe name is already used in \`theme.slotRecipes\`: ${recipeName}`);
274
- }
275
- if (names.patterns.has(recipeName)) {
276
- addError("recipes", `This recipe name is already used in \`patterns\`: \`${recipeName}\``);
277
- }
278
- });
279
- names.slotRecipes.forEach((recipeName) => {
280
- if (names.patterns.has(recipeName)) {
281
- addError("recipes", `This recipe name is already used in \`patterns\`: ${recipeName}`);
282
- }
283
- });
251
+ //#endregion
252
+ //#region src/validation/validate-artifact.ts
253
+ const validateArtifactNames = (names, addError) => {
254
+ names.recipes.forEach((recipeName) => {
255
+ if (names.slotRecipes.has(recipeName)) addError("recipes", `This recipe name is already used in \`theme.slotRecipes\`: ${recipeName}`);
256
+ if (names.patterns.has(recipeName)) addError("recipes", `This recipe name is already used in \`patterns\`: \`${recipeName}\``);
257
+ });
258
+ names.slotRecipes.forEach((recipeName) => {
259
+ if (names.patterns.has(recipeName)) addError("recipes", `This recipe name is already used in \`patterns\`: ${recipeName}`);
260
+ });
284
261
  };
285
-
286
- // src/validation/validate-breakpoints.ts
287
- import { getUnit } from "@bamboocss/shared";
288
- var validateBreakpoints = (breakpoints, addError) => {
289
- if (!breakpoints) return;
290
- const units = /* @__PURE__ */ new Set();
291
- const values = Object.values(breakpoints);
292
- for (const value of values) {
293
- const unit = getUnit(value) ?? "px";
294
- units.add(unit);
295
- }
296
- if (units.size > 1) {
297
- addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
298
- }
262
+ //#endregion
263
+ //#region src/validation/validate-breakpoints.ts
264
+ const validateBreakpoints = (breakpoints, addError) => {
265
+ if (!breakpoints) return;
266
+ const units = /* @__PURE__ */ new Set();
267
+ const values = Object.values(breakpoints);
268
+ for (const value of values) {
269
+ const unit = getUnit(value) ?? "px";
270
+ units.add(unit);
271
+ }
272
+ if (units.size > 1) addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
299
273
  };
300
-
301
- // src/validation/validate-condition.ts
302
- import { isString } from "@bamboocss/shared";
303
- var validateObjectCondition = (obj, addError) => {
304
- let hasSlot = false;
305
- for (const [key, value] of Object.entries(obj)) {
306
- if (!key.startsWith("@") && !key.includes("&")) {
307
- addError("conditions", `Selectors should contain the \`&\` character: \`${key}\``);
308
- }
309
- if (value === "@slot") {
310
- hasSlot = true;
311
- continue;
312
- }
313
- if (typeof value === "string") {
314
- addError(
315
- "conditions",
316
- `Object condition leaves must be the literal string \`'@slot'\`, got \`${JSON.stringify(value)}\` at \`${key}\``
317
- );
318
- continue;
319
- }
320
- if (typeof value === "object" && value !== null) {
321
- const nested = validateObjectCondition(value, addError);
322
- if (nested.hasSlot) hasSlot = true;
323
- }
324
- }
325
- return { hasSlot };
274
+ //#endregion
275
+ //#region src/validation/validate-condition.ts
276
+ const validateObjectCondition = (obj, addError) => {
277
+ let hasSlot = false;
278
+ for (const [key, value] of Object.entries(obj)) {
279
+ if (!key.startsWith("@") && !key.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${key}\``);
280
+ if (value === "@slot") {
281
+ hasSlot = true;
282
+ continue;
283
+ }
284
+ if (typeof value === "string") {
285
+ addError("conditions", `Object condition leaves must be the literal string \`'@slot'\`, got \`${JSON.stringify(value)}\` at \`${key}\``);
286
+ continue;
287
+ }
288
+ if (typeof value === "object" && value !== null) {
289
+ if (validateObjectCondition(value, addError).hasSlot) hasSlot = true;
290
+ }
291
+ }
292
+ return { hasSlot };
326
293
  };
327
- var validateConditions = (conditions, addError) => {
328
- if (!conditions) return;
329
- Object.values(conditions).forEach((condition) => {
330
- if (isString(condition)) {
331
- if (!condition.startsWith("@") && !condition.includes("&")) {
332
- addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
333
- }
334
- return;
335
- }
336
- if (Array.isArray(condition)) {
337
- condition.forEach((c) => {
338
- if (!c.startsWith("@") && !c.includes("&")) {
339
- addError("conditions", `Selectors should contain the \`&\` character: \`${c}\``);
340
- }
341
- });
342
- return;
343
- }
344
- const { hasSlot } = validateObjectCondition(condition, addError);
345
- if (!hasSlot) {
346
- addError("conditions", `Object conditions must contain at least one \`'@slot'\` marker`);
347
- }
348
- });
294
+ const validateConditions = (conditions, addError) => {
295
+ if (!conditions) return;
296
+ Object.values(conditions).forEach((condition) => {
297
+ if (isString(condition)) {
298
+ if (!condition.startsWith("@") && !condition.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
299
+ return;
300
+ }
301
+ if (Array.isArray(condition)) {
302
+ condition.forEach((c) => {
303
+ if (!c.startsWith("@") && !c.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${c}\``);
304
+ });
305
+ return;
306
+ }
307
+ const { hasSlot } = validateObjectCondition(condition, addError);
308
+ if (!hasSlot) addError("conditions", `Object conditions must contain at least one \`'@slot'\` marker`);
309
+ });
349
310
  };
350
-
351
- // src/validation/validate-patterns.ts
352
- var validatePatterns = (patterns, names) => {
353
- if (!patterns) return;
354
- Object.keys(patterns).forEach((patternName) => {
355
- names.patterns.add(patternName);
356
- });
311
+ //#endregion
312
+ //#region src/validation/validate-patterns.ts
313
+ const validatePatterns = (patterns, names) => {
314
+ if (!patterns) return;
315
+ Object.keys(patterns).forEach((patternName) => {
316
+ names.patterns.add(patternName);
317
+ });
357
318
  };
358
-
359
- // src/validation/validate-recipes.ts
360
- var validateRecipes = (options) => {
361
- const {
362
- config: { theme },
363
- artifacts
364
- } = options;
365
- if (!theme) return;
366
- if (theme.recipes) {
367
- Object.keys(theme.recipes).forEach((recipeName) => {
368
- artifacts.recipes.add(recipeName);
369
- });
370
- }
371
- if (theme.slotRecipes) {
372
- Object.keys(theme.slotRecipes).forEach((recipeName) => {
373
- artifacts.slotRecipes.add(recipeName);
374
- });
375
- }
376
- return artifacts;
319
+ //#endregion
320
+ //#region src/validation/validate-recipes.ts
321
+ const validateRecipes = (options) => {
322
+ const { config: { theme }, artifacts } = options;
323
+ if (!theme) return;
324
+ if (theme.recipes) Object.keys(theme.recipes).forEach((recipeName) => {
325
+ artifacts.recipes.add(recipeName);
326
+ });
327
+ if (theme.slotRecipes) Object.keys(theme.slotRecipes).forEach((recipeName) => {
328
+ artifacts.slotRecipes.add(recipeName);
329
+ });
330
+ return artifacts;
377
331
  };
378
-
379
- // src/validation/validate-tokens.ts
380
- import { isObject, walkObject } from "@bamboocss/shared";
381
-
382
- // src/validation/validate-token-references.ts
383
- var validateTokenReferences = (props) => {
384
- const { valueAtPath, refsByPath, addError, typeByPath } = props;
385
- refsByPath.forEach((refs, path2) => {
386
- if (refs.has(path2)) {
387
- addError("tokens", `Self token reference: \`${path2}\``);
388
- }
389
- const stack = [path2];
390
- while (stack.length > 0) {
391
- let currentPath = stack.pop();
392
- if (currentPath.includes("/")) {
393
- const [tokenPath] = currentPath.split("/");
394
- currentPath = tokenPath;
395
- }
396
- const value = valueAtPath.get(currentPath);
397
- if (!value) {
398
- const configKey = typeByPath.get(path2);
399
- addError("tokens", `Missing token: \`${currentPath}\` used in \`theme.${configKey}.${path2}\``);
400
- }
401
- if (isTokenReference(value) && !refsByPath.has(value)) {
402
- addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
403
- }
404
- const deps = refsByPath.get(currentPath);
405
- if (!deps) continue;
406
- for (const transitiveDep of deps) {
407
- if (path2 === transitiveDep) {
408
- addError(
409
- "tokens",
410
- `Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path2}\``
411
- );
412
- break;
413
- }
414
- stack.push(transitiveDep);
415
- }
416
- }
417
- });
332
+ //#endregion
333
+ //#region src/validation/validate-token-references.ts
334
+ const validateTokenReferences = (props) => {
335
+ const { valueAtPath, refsByPath, addError, typeByPath } = props;
336
+ refsByPath.forEach((refs, path) => {
337
+ if (refs.has(path)) addError("tokens", `Self token reference: \`${path}\``);
338
+ const stack = [path];
339
+ while (stack.length > 0) {
340
+ let currentPath = stack.pop();
341
+ if (currentPath.includes("/")) {
342
+ const [tokenPath] = currentPath.split("/");
343
+ currentPath = tokenPath;
344
+ }
345
+ const value = valueAtPath.get(currentPath);
346
+ if (!value) {
347
+ const configKey = typeByPath.get(path);
348
+ addError("tokens", `Missing token: \`${currentPath}\` used in \`theme.${configKey}.${path}\``);
349
+ }
350
+ if (isTokenReference(value) && !refsByPath.has(value)) addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
351
+ const deps = refsByPath.get(currentPath);
352
+ if (!deps) continue;
353
+ for (const transitiveDep of deps) {
354
+ if (path === transitiveDep) {
355
+ addError("tokens", `Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path}\``);
356
+ break;
357
+ }
358
+ stack.push(transitiveDep);
359
+ }
360
+ }
361
+ });
418
362
  };
419
-
420
- // src/validation/validate-tokens.ts
421
- var validateTokens = (options) => {
422
- const {
423
- config: { theme },
424
- tokens,
425
- addError
426
- } = options;
427
- if (!theme) return;
428
- const { tokenNames, semanticTokenNames, valueAtPath, refsByPath, typeByPath } = tokens;
429
- if (theme.tokens) {
430
- const tokenPaths = /* @__PURE__ */ new Set();
431
- walkObject(
432
- theme.tokens,
433
- (value, paths) => {
434
- const path2 = paths.join(SEP);
435
- tokenNames.add(path2);
436
- tokenPaths.add(path2);
437
- valueAtPath.set(path2, value);
438
- if (path2.includes("DEFAULT")) {
439
- valueAtPath.set(path2.replace(SEP + "DEFAULT", ""), value);
440
- }
441
- },
442
- {
443
- stop: isValidToken
444
- }
445
- );
446
- tokenPaths.forEach((path2) => {
447
- const itemValue = valueAtPath.get(path2);
448
- const formattedPath = formatPath(path2);
449
- typeByPath.set(formattedPath, "tokens");
450
- if (!isValidToken(itemValue)) {
451
- addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
452
- return;
453
- }
454
- if (path2.includes(" ")) {
455
- addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
456
- return;
457
- }
458
- const valueStr = serializeTokenValue(itemValue.value || itemValue);
459
- if (isTokenReference(valueStr)) {
460
- refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
461
- }
462
- const references = refsByPath.get(formattedPath);
463
- if (!references) return;
464
- getReferences(valueStr).forEach((reference) => {
465
- references.add(reference);
466
- });
467
- });
468
- }
469
- if (theme.semanticTokens) {
470
- const tokenPaths = /* @__PURE__ */ new Set();
471
- walkObject(
472
- theme.semanticTokens,
473
- (value, paths) => {
474
- const path2 = paths.join(SEP);
475
- semanticTokenNames.add(path2);
476
- valueAtPath.set(path2, value);
477
- tokenPaths.add(path2);
478
- if (path2.includes("DEFAULT")) {
479
- valueAtPath.set(path2.replace(SEP + "DEFAULT", ""), value);
480
- }
481
- if (!isValidToken(value)) return;
482
- walkObject(value, (itemValue, paths2) => {
483
- const valuePath = paths2.join(SEP);
484
- const formattedPath = formatPath(path2);
485
- typeByPath.set(formattedPath, "semanticTokens");
486
- const fullPath = formattedPath + "." + paths2.join(SEP);
487
- if (valuePath.includes("value" + SEP + "value")) {
488
- addError("tokens", `You used \`value\` twice resulting in an invalid token \`theme.tokens.${fullPath}\``);
489
- }
490
- const valueStr = serializeTokenValue(itemValue.value || itemValue);
491
- if (isTokenReference(valueStr)) {
492
- if (!refsByPath.has(formattedPath)) {
493
- refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
494
- }
495
- const references = refsByPath.get(formattedPath);
496
- if (!references) return;
497
- getReferences(valueStr).forEach((reference) => {
498
- references.add(reference);
499
- });
500
- }
501
- });
502
- },
503
- {
504
- stop: isValidToken
505
- }
506
- );
507
- tokenPaths.forEach((path2) => {
508
- const formattedPath = formatPath(path2);
509
- const value = valueAtPath.get(path2);
510
- if (path2.includes(" ")) {
511
- addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
512
- return;
513
- }
514
- if (!isObject(value) && !path2.includes("value")) {
515
- addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
516
- }
517
- });
518
- }
519
- validateTokenReferences({ valueAtPath, refsByPath, addError, typeByPath });
363
+ //#endregion
364
+ //#region src/validation/validate-tokens.ts
365
+ const validateTokens = (options) => {
366
+ const { config: { theme }, tokens, addError } = options;
367
+ if (!theme) return;
368
+ const { tokenNames, semanticTokenNames, valueAtPath, refsByPath, typeByPath } = tokens;
369
+ if (theme.tokens) {
370
+ const tokenPaths = /* @__PURE__ */ new Set();
371
+ walkObject(theme.tokens, (value, paths) => {
372
+ const path = paths.join(".");
373
+ tokenNames.add(path);
374
+ tokenPaths.add(path);
375
+ valueAtPath.set(path, value);
376
+ if (path.includes("DEFAULT")) valueAtPath.set(path.replace(".DEFAULT", ""), value);
377
+ }, { stop: isValidToken });
378
+ tokenPaths.forEach((path) => {
379
+ const itemValue = valueAtPath.get(path);
380
+ const formattedPath = formatPath(path);
381
+ typeByPath.set(formattedPath, "tokens");
382
+ if (!isValidToken(itemValue)) {
383
+ addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
384
+ return;
385
+ }
386
+ if (path.includes(" ")) {
387
+ addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
388
+ return;
389
+ }
390
+ const valueStr = serializeTokenValue(itemValue.value || itemValue);
391
+ if (isTokenReference(valueStr)) refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
392
+ const references = refsByPath.get(formattedPath);
393
+ if (!references) return;
394
+ getReferences(valueStr).forEach((reference) => {
395
+ references.add(reference);
396
+ });
397
+ });
398
+ }
399
+ if (theme.semanticTokens) {
400
+ const tokenPaths = /* @__PURE__ */ new Set();
401
+ walkObject(theme.semanticTokens, (value, paths) => {
402
+ const path = paths.join(".");
403
+ semanticTokenNames.add(path);
404
+ valueAtPath.set(path, value);
405
+ tokenPaths.add(path);
406
+ if (path.includes("DEFAULT")) valueAtPath.set(path.replace(".DEFAULT", ""), value);
407
+ if (!isValidToken(value)) return;
408
+ walkObject(value, (itemValue, paths) => {
409
+ const valuePath = paths.join(".");
410
+ const formattedPath = formatPath(path);
411
+ typeByPath.set(formattedPath, "semanticTokens");
412
+ const fullPath = formattedPath + "." + paths.join(".");
413
+ if (valuePath.includes("value.value")) addError("tokens", `You used \`value\` twice resulting in an invalid token \`theme.tokens.${fullPath}\``);
414
+ const valueStr = serializeTokenValue(itemValue.value || itemValue);
415
+ if (isTokenReference(valueStr)) {
416
+ if (!refsByPath.has(formattedPath)) refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
417
+ const references = refsByPath.get(formattedPath);
418
+ if (!references) return;
419
+ getReferences(valueStr).forEach((reference) => {
420
+ references.add(reference);
421
+ });
422
+ }
423
+ });
424
+ }, { stop: isValidToken });
425
+ tokenPaths.forEach((path) => {
426
+ const formattedPath = formatPath(path);
427
+ const value = valueAtPath.get(path);
428
+ if (path.includes(" ")) {
429
+ addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
430
+ return;
431
+ }
432
+ if (!isObject(value) && !path.includes("value")) addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
433
+ });
434
+ }
435
+ validateTokenReferences({
436
+ valueAtPath,
437
+ refsByPath,
438
+ addError,
439
+ typeByPath
440
+ });
520
441
  };
521
-
522
- // src/validate-config.ts
523
- var validateConfig = (config) => {
524
- if (config.validation === "none") return;
525
- const warnings = /* @__PURE__ */ new Set();
526
- const addError = (scope, message) => {
527
- warnings.add(`[${scope}] ` + message);
528
- };
529
- validateBreakpoints(config.theme?.breakpoints, addError);
530
- validateConditions(config.conditions, addError);
531
- const artifacts = {
532
- recipes: /* @__PURE__ */ new Set(),
533
- slotRecipes: /* @__PURE__ */ new Set(),
534
- patterns: /* @__PURE__ */ new Set()
535
- };
536
- const tokens = {
537
- tokenNames: /* @__PURE__ */ new Set(),
538
- semanticTokenNames: /* @__PURE__ */ new Set(),
539
- valueAtPath: /* @__PURE__ */ new Map(),
540
- refsByPath: /* @__PURE__ */ new Map(),
541
- typeByPath: /* @__PURE__ */ new Map()
542
- };
543
- if (config.theme) {
544
- validateTokens({ config, tokens, addError });
545
- validateRecipes({ config, tokens, artifacts, addError });
546
- }
547
- validatePatterns(config.patterns, artifacts);
548
- validateArtifactNames(artifacts, addError);
549
- if (warnings.size) {
550
- const errors = `\u26A0\uFE0F Invalid config:
551
- ${Array.from(warnings).map((err) => "- " + err).join("\n")}
552
- `;
553
- if (config.validation === "error") {
554
- throw new BambooError3("CONFIG_ERROR", errors);
555
- }
556
- logger2.warn("config", errors);
557
- return warnings;
558
- }
442
+ //#endregion
443
+ //#region src/validate-config.ts
444
+ /**
445
+ * Validate the config
446
+ * - Check for duplicate between token & semanticTokens names
447
+ * - Check for duplicate between recipes/patterns/slots names
448
+ * - Check for token / semanticTokens paths (must end/contain 'value')
449
+ * - Check for self/circular token references
450
+ * - Check for missing tokens references
451
+ * - Check for conditions selectors (must contain '&')
452
+ * - Check for breakpoints units (must be the same)
453
+ */
454
+ const validateConfig = (config) => {
455
+ if (config.validation === "none") return;
456
+ const warnings = /* @__PURE__ */ new Set();
457
+ const addError = (scope, message) => {
458
+ warnings.add(`[${scope}] ` + message);
459
+ };
460
+ validateBreakpoints(config.theme?.breakpoints, addError);
461
+ validateConditions(config.conditions, addError);
462
+ const artifacts = {
463
+ recipes: /* @__PURE__ */ new Set(),
464
+ slotRecipes: /* @__PURE__ */ new Set(),
465
+ patterns: /* @__PURE__ */ new Set()
466
+ };
467
+ const tokens = {
468
+ tokenNames: /* @__PURE__ */ new Set(),
469
+ semanticTokenNames: /* @__PURE__ */ new Set(),
470
+ valueAtPath: /* @__PURE__ */ new Map(),
471
+ refsByPath: /* @__PURE__ */ new Map(),
472
+ typeByPath: /* @__PURE__ */ new Map()
473
+ };
474
+ if (config.theme) {
475
+ validateTokens({
476
+ config,
477
+ tokens,
478
+ addError
479
+ });
480
+ validateRecipes({
481
+ config,
482
+ tokens,
483
+ artifacts,
484
+ addError
485
+ });
486
+ }
487
+ validatePatterns(config.patterns, artifacts);
488
+ validateArtifactNames(artifacts, addError);
489
+ if (warnings.size) {
490
+ const errors = `⚠️ Invalid config:\n${Array.from(warnings).map((err) => "- " + err).join("\n")}\n`;
491
+ if (config.validation === "error") throw new BambooError("CONFIG_ERROR", errors);
492
+ logger.warn("config", errors);
493
+ return warnings;
494
+ }
559
495
  };
560
-
561
- // src/resolve-config.ts
562
- var hookUtils2 = {
563
- omit: omit2,
564
- pick: pick2,
565
- traverse: traverse2
496
+ //#endregion
497
+ //#region src/resolve-config.ts
498
+ const hookUtils = {
499
+ omit,
500
+ pick,
501
+ traverse
566
502
  };
503
+ /**
504
+ * Resolve the final config (including presets)
505
+ * @bamboocss/preset-base: ALWAYS included if NOT using eject: true
506
+ * @bamboocss/preset-bamboo: only included by default if no presets
507
+ */
567
508
  async function resolveConfig(result, cwd) {
568
- const presets = /* @__PURE__ */ new Set();
569
- if (!result.config.eject) {
570
- presets.add(presetBase);
571
- }
572
- if (result.config.presets) {
573
- result.config.presets.forEach((preset) => {
574
- presets.add(getBundledPreset(preset) ?? preset);
575
- });
576
- } else if (!result.config.eject) {
577
- presets.add(presetBamboo);
578
- }
579
- result.config.presets = Array.from(presets);
580
- const userConfig = result.config;
581
- const pluginHooks = userConfig.plugins ?? [];
582
- if (userConfig.hooks) {
583
- pluginHooks.push({ name: BAMBOO_CONFIG_NAME, hooks: userConfig.hooks });
584
- }
585
- const earlyHooks = mergeHooks(pluginHooks);
586
- const mergedConfig = await getResolvedConfig(result.config, cwd, earlyHooks);
587
- const hooks = mergedConfig.hooks ?? {};
588
- if (mergedConfig.logLevel) {
589
- logger3.level = mergedConfig.logLevel;
590
- }
591
- validateConfig(mergedConfig);
592
- const loadConfigResult = {
593
- ...result,
594
- config: mergedConfig
595
- };
596
- if (hooks["config:resolved"]) {
597
- const result2 = await hooks["config:resolved"]({
598
- config: loadConfigResult.config,
599
- path: loadConfigResult.path,
600
- dependencies: loadConfigResult.dependencies,
601
- utils: hookUtils2
602
- });
603
- if (result2) {
604
- loadConfigResult.config = result2;
605
- }
606
- }
607
- const serialized = stringifyJson(
608
- Object.assign({}, loadConfigResult.config, { name: BAMBOO_CONFIG_NAME, presets: [] })
609
- );
610
- const deserialize = () => parseJson(serialized);
611
- return { ...loadConfigResult, serialized, deserialize, hooks };
509
+ const presets = /* @__PURE__ */ new Set();
510
+ if (!result.config.eject) presets.add(presetBase);
511
+ if (result.config.presets) result.config.presets.forEach((preset) => {
512
+ presets.add(getBundledPreset(preset) ?? preset);
513
+ });
514
+ else if (!result.config.eject) presets.add(presetBamboo);
515
+ result.config.presets = Array.from(presets);
516
+ const userConfig = result.config;
517
+ const pluginHooks = userConfig.plugins ?? [];
518
+ if (userConfig.hooks) pluginHooks.push({
519
+ name: BAMBOO_CONFIG_NAME,
520
+ hooks: userConfig.hooks
521
+ });
522
+ const earlyHooks = mergeHooks(pluginHooks);
523
+ const mergedConfig = await getResolvedConfig(result.config, cwd, earlyHooks);
524
+ const hooks = mergedConfig.hooks ?? {};
525
+ if (mergedConfig.logLevel) logger.level = mergedConfig.logLevel;
526
+ validateConfig(mergedConfig);
527
+ const loadConfigResult = {
528
+ ...result,
529
+ config: mergedConfig
530
+ };
531
+ if (hooks["config:resolved"]) {
532
+ const result = await hooks["config:resolved"]({
533
+ config: loadConfigResult.config,
534
+ path: loadConfigResult.path,
535
+ dependencies: loadConfigResult.dependencies,
536
+ utils: hookUtils
537
+ });
538
+ if (result) loadConfigResult.config = result;
539
+ }
540
+ const serialized = stringifyJson(Object.assign({}, loadConfigResult.config, {
541
+ name: BAMBOO_CONFIG_NAME,
542
+ presets: []
543
+ }));
544
+ const deserialize = () => parseJson(serialized);
545
+ return {
546
+ ...loadConfigResult,
547
+ serialized,
548
+ deserialize,
549
+ hooks
550
+ };
612
551
  }
613
-
614
- // src/load-config.ts
552
+ //#endregion
553
+ //#region src/load-config.ts
554
+ /**
555
+ * Find, load and resolve the final config (including presets)
556
+ */
615
557
  async function loadConfig(options) {
616
- const result = await bundleConfig(options);
617
- return resolveConfig(result, options.cwd);
558
+ return resolveConfig(await bundleConfig(options), options.cwd);
618
559
  }
619
- export {
620
- bundleConfig,
621
- convertTsPathsToRegexes,
622
- diffConfigs,
623
- findConfig,
624
- getConfigDependencies,
625
- getResolvedConfig,
626
- loadConfig,
627
- mergeConfigs,
628
- mergeHooks,
629
- resolveConfig
630
- };
560
+ //#endregion
561
+ export { bundleConfig, convertTsPathsToRegexes, diffConfigs, findConfig, getConfigDependencies, getResolvedConfig, loadConfig, mergeConfigs, mergeHooks, resolveConfig };