🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@bamboocss/config

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bamboocss/config - npm Package Compare versions

Comparing version
1.11.1
to
1.11.2
+197
dist/diff-config-Co_3mDXE.cjs
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let _bamboocss_shared = require("@bamboocss/shared");
let microdiff = require("microdiff");
microdiff = __toESM(microdiff);
//#region src/create-matcher.ts
/**
* Acts like a .gitignore matcher
* e.g a list of string to search for nested path with glob and exclusion allowed
* ["outdir", "theme.recipes", '*.css', '!aaa.*.bbb']
*/
function createMatcher(id, patterns) {
if (!patterns?.length) return () => void 0;
const includePatterns = [];
const excludePatterns = [];
new Set(patterns).forEach((pattern) => {
const regexString = pattern.replace(/\*/g, ".*");
if (pattern.startsWith("!")) excludePatterns.push(regexString.slice(1));
else includePatterns.push(regexString);
});
const include = new RegExp(includePatterns.join("|"));
const exclude = new RegExp(excludePatterns.join("|"));
return (path) => {
if (excludePatterns.length && exclude.test(path)) return;
return include.test(path) ? id : void 0;
};
}
//#endregion
//#region src/config-deps.ts
const all = [
"clean",
"cwd",
"eject",
"outdir",
"forceConsistentTypeExtension",
"outExtension",
"emitTokensOnly",
"presets",
"plugins",
"hooks"
];
const format = [
"syntax",
"hash",
"prefix",
"separator",
"strictTokens",
"strictPropertyValues",
"shorthands"
];
const tokens = [
"utilities",
"conditions",
"theme.tokens",
"theme.semanticTokens",
"theme.breakpoints",
"theme.containerNames",
"theme.containerSizes"
];
const jsx = [
"jsxFramework",
"jsxFactory",
"jsxStyleProps",
"syntax"
];
const common = tokens.concat(jsx, format);
const artifactConfigDeps = {
helpers: ["syntax", "jsxFramework"],
keyframes: ["theme.keyframes", "layers"],
"design-tokens": ["layers", "!utilities.*.className"].concat(tokens),
types: ["!utilities.*.className"].concat(common),
"css-fn": common,
cva: ["syntax"],
sva: ["syntax"],
cx: [],
"create-recipe": [
"separator",
"prefix",
"hash"
],
"recipes-index": ["theme.recipes", "theme.slotRecipes"],
recipes: ["theme.recipes", "theme.slotRecipes"],
"patterns-index": ["syntax", "patterns"],
patterns: ["syntax", "patterns"],
"jsx-is-valid-prop": common,
"jsx-factory": jsx,
"jsx-helpers": jsx,
"jsx-patterns": jsx.concat("patterns"),
"jsx-patterns-index": jsx.concat("patterns"),
"jsx-create-style-context": jsx,
"css-index": ["syntax"],
"package.json": ["forceConsistentTypeExtension", "outExtension"],
"types-styles": ["shorthands"],
"types-conditions": ["conditions"],
"types-jsx": jsx,
"types-entry": [],
"types-gen": [],
"types-gen-system": [],
themes: ["themes"].concat(tokens),
"static-css": [
"staticCss",
"patterns",
"theme.recipes",
"theme.slotRecipes"
].concat(tokens),
styles: [],
"styles.css": []
};
const artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
if (!paths.length) return () => void 0;
return createMatcher(key, paths.concat(all));
});
//#endregion
//#region src/diff-config.ts
const runIfFn = (fn) => typeof fn === "function" ? fn() : fn;
/**
* Check if recipes were empty before and non-empty now (or vice versa)
*/
const hasRecipeStateTransition = (prevConfig, nextConfig) => {
const prevRecipes = prevConfig.theme?.recipes ?? {};
const prevSlotRecipes = prevConfig.theme?.slotRecipes ?? {};
const prevHasRecipes = Object.keys(prevRecipes).length > 0 || Object.keys(prevSlotRecipes).length > 0;
const nextRecipes = nextConfig.theme?.recipes ?? {};
const nextSlotRecipes = nextConfig.theme?.slotRecipes ?? {};
return prevHasRecipes !== (Object.keys(nextRecipes).length > 0 || Object.keys(nextSlotRecipes).length > 0);
};
/**
* Diff the two config objects and return the list of affected properties
*/
function diffConfigs(config, prevConfig) {
const affected = {
artifacts: /* @__PURE__ */ new Set(),
hasConfigChanged: false,
diffs: []
};
if (!prevConfig) {
affected.hasConfigChanged = true;
return affected;
}
const configDiff = (0, microdiff.default)(prevConfig, runIfFn(config));
if (!configDiff.length) return affected;
affected.hasConfigChanged = true;
affected.diffs = configDiff;
configDiff.forEach((change) => {
const changePath = change.path.join(".");
artifactMatchers.forEach((matcher) => {
const id = matcher(changePath);
if (!id) return;
if (id === "recipes") {
const name = (0, _bamboocss_shared.dashCase)(change.path.slice(1, 3).join("."));
affected.artifacts.add(name);
}
if (id === "patterns") {
const name = (0, _bamboocss_shared.dashCase)(change.path.slice(0, 2).join("."));
affected.artifacts.add(name);
}
affected.artifacts.add(id);
});
});
if (affected.artifacts.has("recipes") || affected.artifacts.has("recipes-index")) {
if (hasRecipeStateTransition(prevConfig, runIfFn(config))) affected.artifacts.add("create-recipe");
}
return affected;
}
//#endregion
Object.defineProperty(exports, "__toESM", {
enumerable: true,
get: function() {
return __toESM;
}
});
Object.defineProperty(exports, "diffConfigs", {
enumerable: true,
get: function() {
return diffConfigs;
}
});
import { dashCase } from "@bamboocss/shared";
import microdiff from "microdiff";
//#region src/create-matcher.ts
/**
* Acts like a .gitignore matcher
* e.g a list of string to search for nested path with glob and exclusion allowed
* ["outdir", "theme.recipes", '*.css', '!aaa.*.bbb']
*/
function createMatcher(id, patterns) {
if (!patterns?.length) return () => void 0;
const includePatterns = [];
const excludePatterns = [];
new Set(patterns).forEach((pattern) => {
const regexString = pattern.replace(/\*/g, ".*");
if (pattern.startsWith("!")) excludePatterns.push(regexString.slice(1));
else includePatterns.push(regexString);
});
const include = new RegExp(includePatterns.join("|"));
const exclude = new RegExp(excludePatterns.join("|"));
return (path) => {
if (excludePatterns.length && exclude.test(path)) return;
return include.test(path) ? id : void 0;
};
}
//#endregion
//#region src/config-deps.ts
const all = [
"clean",
"cwd",
"eject",
"outdir",
"forceConsistentTypeExtension",
"outExtension",
"emitTokensOnly",
"presets",
"plugins",
"hooks"
];
const format = [
"syntax",
"hash",
"prefix",
"separator",
"strictTokens",
"strictPropertyValues",
"shorthands"
];
const tokens = [
"utilities",
"conditions",
"theme.tokens",
"theme.semanticTokens",
"theme.breakpoints",
"theme.containerNames",
"theme.containerSizes"
];
const jsx = [
"jsxFramework",
"jsxFactory",
"jsxStyleProps",
"syntax"
];
const common = tokens.concat(jsx, format);
const artifactConfigDeps = {
helpers: ["syntax", "jsxFramework"],
keyframes: ["theme.keyframes", "layers"],
"design-tokens": ["layers", "!utilities.*.className"].concat(tokens),
types: ["!utilities.*.className"].concat(common),
"css-fn": common,
cva: ["syntax"],
sva: ["syntax"],
cx: [],
"create-recipe": [
"separator",
"prefix",
"hash"
],
"recipes-index": ["theme.recipes", "theme.slotRecipes"],
recipes: ["theme.recipes", "theme.slotRecipes"],
"patterns-index": ["syntax", "patterns"],
patterns: ["syntax", "patterns"],
"jsx-is-valid-prop": common,
"jsx-factory": jsx,
"jsx-helpers": jsx,
"jsx-patterns": jsx.concat("patterns"),
"jsx-patterns-index": jsx.concat("patterns"),
"jsx-create-style-context": jsx,
"css-index": ["syntax"],
"package.json": ["forceConsistentTypeExtension", "outExtension"],
"types-styles": ["shorthands"],
"types-conditions": ["conditions"],
"types-jsx": jsx,
"types-entry": [],
"types-gen": [],
"types-gen-system": [],
themes: ["themes"].concat(tokens),
"static-css": [
"staticCss",
"patterns",
"theme.recipes",
"theme.slotRecipes"
].concat(tokens),
styles: [],
"styles.css": []
};
const artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
if (!paths.length) return () => void 0;
return createMatcher(key, paths.concat(all));
});
//#endregion
//#region src/diff-config.ts
const runIfFn = (fn) => typeof fn === "function" ? fn() : fn;
/**
* Check if recipes were empty before and non-empty now (or vice versa)
*/
const hasRecipeStateTransition = (prevConfig, nextConfig) => {
const prevRecipes = prevConfig.theme?.recipes ?? {};
const prevSlotRecipes = prevConfig.theme?.slotRecipes ?? {};
const prevHasRecipes = Object.keys(prevRecipes).length > 0 || Object.keys(prevSlotRecipes).length > 0;
const nextRecipes = nextConfig.theme?.recipes ?? {};
const nextSlotRecipes = nextConfig.theme?.slotRecipes ?? {};
return prevHasRecipes !== (Object.keys(nextRecipes).length > 0 || Object.keys(nextSlotRecipes).length > 0);
};
/**
* Diff the two config objects and return the list of affected properties
*/
function diffConfigs(config, prevConfig) {
const affected = {
artifacts: /* @__PURE__ */ new Set(),
hasConfigChanged: false,
diffs: []
};
if (!prevConfig) {
affected.hasConfigChanged = true;
return affected;
}
const configDiff = microdiff(prevConfig, runIfFn(config));
if (!configDiff.length) return affected;
affected.hasConfigChanged = true;
affected.diffs = configDiff;
configDiff.forEach((change) => {
const changePath = change.path.join(".");
artifactMatchers.forEach((matcher) => {
const id = matcher(changePath);
if (!id) return;
if (id === "recipes") {
const name = dashCase(change.path.slice(1, 3).join("."));
affected.artifacts.add(name);
}
if (id === "patterns") {
const name = dashCase(change.path.slice(0, 2).join("."));
affected.artifacts.add(name);
}
affected.artifacts.add(id);
});
});
if (affected.artifacts.has("recipes") || affected.artifacts.has("recipes-index")) {
if (hasRecipeStateTransition(prevConfig, runIfFn(config))) affected.artifacts.add("create-recipe");
}
return affected;
}
//#endregion
export { diffConfigs as t };
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_diff_config = require("./diff-config-Co_3mDXE.cjs");
exports.diffConfigs = require_diff_config.diffConfigs;
import { Config, DiffConfigResult } from "@bamboocss/types";
//#region src/diff-config.d.ts
type ConfigOrFn = Config | (() => Config);
/**
* Diff the two config objects and return the list of affected properties
*/
declare function diffConfigs(config: ConfigOrFn, prevConfig: Config | undefined): DiffConfigResult;
//#endregion
export { diffConfigs };
import { Config, DiffConfigResult } from "@bamboocss/types";
//#region src/diff-config.d.ts
type ConfigOrFn = Config | (() => Config);
/**
* Diff the two config objects and return the list of affected properties
*/
declare function diffConfigs(config: ConfigOrFn, prevConfig: Config | undefined): DiffConfigResult;
//#endregion
export { diffConfigs };
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_diff_config = require("./diff-config-Co_3mDXE.cjs");
const require_resolve_ts_path_pattern = require("./resolve-ts-path-pattern.cjs");
const require_merge_config = require("./merge-config-CcNpHJit.cjs");
let _bamboocss_logger = require("@bamboocss/logger");
let _bamboocss_shared = require("@bamboocss/shared");
let bundle_n_require = require("bundle-n-require");
let escalade_sync = require("escalade/sync");
escalade_sync = require_diff_config.__toESM(escalade_sync);
let path = require("path");
path = require_diff_config.__toESM(path);
let fs = require("fs");
fs = require_diff_config.__toESM(fs);
let typescript = require("typescript");
typescript = require_diff_config.__toESM(typescript);
let _bamboocss_preset_base = require("@bamboocss/preset-base");
let _bamboocss_preset_bamboo = require("@bamboocss/preset-bamboo");
//#region src/is-bamboo-config.ts
const configName = "bamboo";
const bambooConfigFiles = new Set([
`${configName}.config.ts`,
`${configName}.config.js`,
`${configName}.config.mts`,
`${configName}.config.mjs`,
`${configName}.config.cts`,
`${configName}.config.cjs`
]);
const isBambooConfig = (file) => bambooConfigFiles.has(file);
//#endregion
//#region src/find-config.ts
function findConfig(options) {
const { cwd = process.cwd(), file } = options;
if (file) return (0, path.resolve)(cwd, file);
const configPath = (0, escalade_sync.default)(cwd, (_dir, paths) => paths.find(isBambooConfig));
if (!configPath) throw new _bamboocss_shared.BambooError("CONFIG_NOT_FOUND", `Cannot find config file \`bamboo.config.{ts,js,mjs,mts}\`. Did you forget to run \`bamboo init\`?`);
return configPath;
}
//#endregion
//#region src/bundle-config.ts
async function bundle(filepath, cwd) {
const { mod, dependencies } = await (0, bundle_n_require.bundleNRequire)(filepath, {
cwd,
interopDefault: true
});
return {
config: mod?.default ?? mod,
dependencies
};
}
async function bundleConfig(options) {
const { cwd, file } = options;
const filePath = findConfig({
cwd,
file
});
_bamboocss_logger.logger.debug("config:path", filePath);
const result = await bundle(filePath, cwd);
if (typeof result.config !== "object") throw new _bamboocss_shared.BambooError("CONFIG_ERROR", `💥 Config must export or return an object.`);
result.config.outdir ??= "styled-system";
result.config.validation ??= "warn";
return {
...result,
config: result.config,
path: filePath
};
}
//#endregion
//#region src/ts-config-paths.ts
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/mappings.ts
*/
function convertTsPathsToRegexes(paths, baseUrl) {
const sortedPatterns = Object.keys(paths).sort((a, b) => getPrefixLength(b) - getPrefixLength(a));
const resolved = [];
for (let pattern of sortedPatterns) {
const relativePaths = paths[pattern];
pattern = escapeStringRegexp(pattern).replace(/\*/g, "(.+)");
resolved.push({
pattern: new RegExp("^" + pattern + "$"),
paths: relativePaths.map((relativePath) => (0, path.resolve)(baseUrl, relativePath))
});
}
return resolved;
}
function getPrefixLength(pattern) {
const prefixLength = pattern.indexOf("*");
return pattern.substr(0, prefixLength).length;
}
function escapeStringRegexp(string) {
return string.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/-/g, "\\x2d");
}
//#endregion
//#region src/get-mod-deps.ts
const jsExtensions = [
".js",
".cjs",
".mjs"
];
const jsResolutionOrder = [
"",
".js",
".cjs",
".mjs",
".ts",
".cts",
".mts",
".jsx",
".tsx"
];
const tsResolutionOrder = [
"",
".ts",
".cts",
".mts",
".tsx",
".js",
".cjs",
".mjs",
".jsx"
];
function resolveWithExtension(file, extensions) {
for (const ext of extensions) {
const full = `${file}${ext}`;
if (fs.default.existsSync(full) && fs.default.statSync(full).isFile()) return full;
}
for (const ext of extensions) {
const full = `${file}/index${ext}`;
if (fs.default.existsSync(full)) return full;
}
return null;
}
const importRegex = /import[\s\S]*?['"](.{3,}?)['"]/gi;
const importFromRegex = /import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
const requireRegex = /require\(['"`](.+)['"`]\)/gi;
const exportRegex = /export[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
function getDeps(opts, fromAlias) {
const { filename, seen } = opts;
const { moduleResolution: _, ...compilerOptions } = opts.compilerOptions ?? {};
const absoluteFile = resolveWithExtension(path.default.resolve(opts.cwd, filename), jsExtensions.includes(opts.ext) ? jsResolutionOrder : tsResolutionOrder);
if (absoluteFile === null) return;
if (fromAlias) opts.foundModuleAliases.set(fromAlias, absoluteFile);
if (seen.size > 1 && seen.has(absoluteFile)) return;
seen.add(absoluteFile);
const contents = fs.default.readFileSync(absoluteFile, "utf-8");
const fileDeps = [
...contents.matchAll(importRegex),
...contents.matchAll(importFromRegex),
...contents.matchAll(requireRegex),
...contents.matchAll(exportRegex)
];
if (!fileDeps.length) return;
const nextOpts = {
cwd: path.default.dirname(absoluteFile),
ext: path.default.extname(absoluteFile),
seen,
baseUrl: opts.baseUrl,
pathMappings: opts.pathMappings,
foundModuleAliases: opts.foundModuleAliases
};
fileDeps.forEach((match) => {
const mod = match[1];
if (mod[0] === ".") {
getDeps(Object.assign({}, nextOpts, { filename: mod }));
return;
}
try {
const found = typescript.default.resolveModuleName(mod, absoluteFile, compilerOptions, typescript.default.sys).resolvedModule;
if (found && found.extension.endsWith("ts")) {
getDeps(Object.assign({}, nextOpts, { filename: found.resolvedFileName }));
return;
}
if (!opts.pathMappings) return;
const filename = require_resolve_ts_path_pattern.resolveTsPathPattern(opts.pathMappings, mod);
if (!filename) return;
getDeps(Object.assign({}, nextOpts, { filename }), mod);
} catch {}
});
}
function getConfigDependencies(filePath, tsOptions = { pathMappings: [] }, compilerOptions) {
if (filePath === null) return {
deps: /* @__PURE__ */ new Set(),
aliases: /* @__PURE__ */ new Map()
};
const foundModuleAliases = /* @__PURE__ */ new Map();
const deps = /* @__PURE__ */ new Set();
deps.add(filePath);
getDeps({
filename: filePath,
ext: path.default.extname(filePath),
cwd: path.default.dirname(filePath),
seen: deps,
baseUrl: tsOptions.baseUrl,
pathMappings: tsOptions.pathMappings ?? [],
foundModuleAliases,
compilerOptions
});
return {
deps,
aliases: foundModuleAliases
};
}
//#endregion
//#region src/get-resolved-config.ts
const hookUtils$1 = {
omit: _bamboocss_shared.omit,
pick: _bamboocss_shared.pick,
traverse: _bamboocss_shared.traverse
};
/**
* Recursively merge all presets into a single config (depth-first using stack)
*/
async function getResolvedConfig(config, cwd, hooks) {
const stack = [config];
const configs = [];
while (stack.length > 0) {
const current = stack.pop();
const subPresets = current.presets ?? [];
for (const subPreset of subPresets) {
let presetConfig;
let presetName;
if (typeof subPreset === "string") {
presetConfig = (await bundle(subPreset, cwd)).config;
presetName = subPreset;
} else {
presetConfig = await subPreset;
presetName = presetConfig.name || "unknown-preset";
}
if (hooks?.["preset:resolved"]) {
const resolvedPreset = await hooks["preset:resolved"]({
preset: presetConfig,
name: presetName,
utils: hookUtils$1
});
if (resolvedPreset !== void 0) presetConfig = resolvedPreset;
}
stack.push(presetConfig);
}
configs.unshift(current);
}
const merged = require_merge_config.mergeConfigs(configs);
merged.presets = configs.slice(0, -1);
return merged;
}
//#endregion
//#region src/bundled-preset.ts
const bundledPresets = {
"@bamboocss/preset-base": _bamboocss_preset_base.preset,
"@bamboocss/preset-bamboo": _bamboocss_preset_bamboo.preset,
"@bamboocss/dev/presets": _bamboocss_preset_bamboo.preset
};
const bundledPresetsNames = Object.keys(bundledPresets);
const isBundledPreset = (preset) => bundledPresetsNames.includes(preset);
const getBundledPreset = (preset) => {
return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
};
//#endregion
//#region src/validation/validate-artifact.ts
const validateArtifactNames = (names, addError) => {
names.recipes.forEach((recipeName) => {
if (names.slotRecipes.has(recipeName)) addError("recipes", `This recipe name is already used in \`theme.slotRecipes\`: ${recipeName}`);
if (names.patterns.has(recipeName)) addError("recipes", `This recipe name is already used in \`patterns\`: \`${recipeName}\``);
});
names.slotRecipes.forEach((recipeName) => {
if (names.patterns.has(recipeName)) addError("recipes", `This recipe name is already used in \`patterns\`: ${recipeName}`);
});
};
//#endregion
//#region src/validation/validate-breakpoints.ts
const validateBreakpoints = (breakpoints, addError) => {
if (!breakpoints) return;
const units = /* @__PURE__ */ new Set();
const values = Object.values(breakpoints);
for (const value of values) {
const unit = (0, _bamboocss_shared.getUnit)(value) ?? "px";
units.add(unit);
}
if (units.size > 1) addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
};
//#endregion
//#region src/validation/validate-condition.ts
const validateObjectCondition = (obj, addError) => {
let hasSlot = false;
for (const [key, value] of Object.entries(obj)) {
if (!key.startsWith("@") && !key.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${key}\``);
if (value === "@slot") {
hasSlot = true;
continue;
}
if (typeof value === "string") {
addError("conditions", `Object condition leaves must be the literal string \`'@slot'\`, got \`${JSON.stringify(value)}\` at \`${key}\``);
continue;
}
if (typeof value === "object" && value !== null) {
if (validateObjectCondition(value, addError).hasSlot) hasSlot = true;
}
}
return { hasSlot };
};
const validateConditions = (conditions, addError) => {
if (!conditions) return;
Object.values(conditions).forEach((condition) => {
if ((0, _bamboocss_shared.isString)(condition)) {
if (!condition.startsWith("@") && !condition.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
return;
}
if (Array.isArray(condition)) {
condition.forEach((c) => {
if (!c.startsWith("@") && !c.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${c}\``);
});
return;
}
const { hasSlot } = validateObjectCondition(condition, addError);
if (!hasSlot) addError("conditions", `Object conditions must contain at least one \`'@slot'\` marker`);
});
};
//#endregion
//#region src/validation/validate-patterns.ts
const validatePatterns = (patterns, names) => {
if (!patterns) return;
Object.keys(patterns).forEach((patternName) => {
names.patterns.add(patternName);
});
};
//#endregion
//#region src/validation/validate-recipes.ts
const validateRecipes = (options) => {
const { config: { theme }, artifacts } = options;
if (!theme) return;
if (theme.recipes) Object.keys(theme.recipes).forEach((recipeName) => {
artifacts.recipes.add(recipeName);
});
if (theme.slotRecipes) Object.keys(theme.slotRecipes).forEach((recipeName) => {
artifacts.slotRecipes.add(recipeName);
});
return artifacts;
};
//#endregion
//#region src/validation/validate-token-references.ts
const validateTokenReferences = (props) => {
const { valueAtPath, refsByPath, addError, typeByPath } = props;
refsByPath.forEach((refs, path) => {
if (refs.has(path)) addError("tokens", `Self token reference: \`${path}\``);
const stack = [path];
while (stack.length > 0) {
let currentPath = stack.pop();
if (currentPath.includes("/")) {
const [tokenPath] = currentPath.split("/");
currentPath = tokenPath;
}
const value = valueAtPath.get(currentPath);
if (!value) {
const configKey = typeByPath.get(path);
addError("tokens", `Missing token: \`${currentPath}\` used in \`theme.${configKey}.${path}\``);
}
if (require_merge_config.isTokenReference(value) && !refsByPath.has(value)) addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
const deps = refsByPath.get(currentPath);
if (!deps) continue;
for (const transitiveDep of deps) {
if (path === transitiveDep) {
addError("tokens", `Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path}\``);
break;
}
stack.push(transitiveDep);
}
}
});
};
//#endregion
//#region src/validation/validate-tokens.ts
const validateTokens = (options) => {
const { config: { theme }, tokens, addError } = options;
if (!theme) return;
const { tokenNames, semanticTokenNames, valueAtPath, refsByPath, typeByPath } = tokens;
if (theme.tokens) {
const tokenPaths = /* @__PURE__ */ new Set();
(0, _bamboocss_shared.walkObject)(theme.tokens, (value, paths) => {
const path = paths.join(".");
tokenNames.add(path);
tokenPaths.add(path);
valueAtPath.set(path, value);
if (path.includes("DEFAULT")) valueAtPath.set(path.replace(".DEFAULT", ""), value);
}, { stop: require_merge_config.isValidToken });
tokenPaths.forEach((path) => {
const itemValue = valueAtPath.get(path);
const formattedPath = require_merge_config.formatPath(path);
typeByPath.set(formattedPath, "tokens");
if (!require_merge_config.isValidToken(itemValue)) {
addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
return;
}
if (path.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
const valueStr = require_merge_config.serializeTokenValue(itemValue.value || itemValue);
if (require_merge_config.isTokenReference(valueStr)) refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
const references = refsByPath.get(formattedPath);
if (!references) return;
require_merge_config.getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
});
}
if (theme.semanticTokens) {
const tokenPaths = /* @__PURE__ */ new Set();
(0, _bamboocss_shared.walkObject)(theme.semanticTokens, (value, paths) => {
const path = paths.join(".");
semanticTokenNames.add(path);
valueAtPath.set(path, value);
tokenPaths.add(path);
if (path.includes("DEFAULT")) valueAtPath.set(path.replace(".DEFAULT", ""), value);
if (!require_merge_config.isValidToken(value)) return;
(0, _bamboocss_shared.walkObject)(value, (itemValue, paths) => {
const valuePath = paths.join(".");
const formattedPath = require_merge_config.formatPath(path);
typeByPath.set(formattedPath, "semanticTokens");
const fullPath = formattedPath + "." + paths.join(".");
if (valuePath.includes("value.value")) addError("tokens", `You used \`value\` twice resulting in an invalid token \`theme.tokens.${fullPath}\``);
const valueStr = require_merge_config.serializeTokenValue(itemValue.value || itemValue);
if (require_merge_config.isTokenReference(valueStr)) {
if (!refsByPath.has(formattedPath)) refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
const references = refsByPath.get(formattedPath);
if (!references) return;
require_merge_config.getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
}
});
}, { stop: require_merge_config.isValidToken });
tokenPaths.forEach((path) => {
const formattedPath = require_merge_config.formatPath(path);
const value = valueAtPath.get(path);
if (path.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
if (!(0, _bamboocss_shared.isObject)(value) && !path.includes("value")) addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
});
}
validateTokenReferences({
valueAtPath,
refsByPath,
addError,
typeByPath
});
};
//#endregion
//#region src/validate-config.ts
/**
* Validate the config
* - Check for duplicate between token & semanticTokens names
* - Check for duplicate between recipes/patterns/slots names
* - Check for token / semanticTokens paths (must end/contain 'value')
* - Check for self/circular token references
* - Check for missing tokens references
* - Check for conditions selectors (must contain '&')
* - Check for breakpoints units (must be the same)
*/
const validateConfig = (config) => {
if (config.validation === "none") return;
const warnings = /* @__PURE__ */ new Set();
const addError = (scope, message) => {
warnings.add(`[${scope}] ` + message);
};
validateBreakpoints(config.theme?.breakpoints, addError);
validateConditions(config.conditions, addError);
const artifacts = {
recipes: /* @__PURE__ */ new Set(),
slotRecipes: /* @__PURE__ */ new Set(),
patterns: /* @__PURE__ */ new Set()
};
const tokens = {
tokenNames: /* @__PURE__ */ new Set(),
semanticTokenNames: /* @__PURE__ */ new Set(),
valueAtPath: /* @__PURE__ */ new Map(),
refsByPath: /* @__PURE__ */ new Map(),
typeByPath: /* @__PURE__ */ new Map()
};
if (config.theme) {
validateTokens({
config,
tokens,
addError
});
validateRecipes({
config,
tokens,
artifacts,
addError
});
}
validatePatterns(config.patterns, artifacts);
validateArtifactNames(artifacts, addError);
if (warnings.size) {
const errors = `⚠️ Invalid config:\n${Array.from(warnings).map((err) => "- " + err).join("\n")}\n`;
if (config.validation === "error") throw new _bamboocss_shared.BambooError("CONFIG_ERROR", errors);
_bamboocss_logger.logger.warn("config", errors);
return warnings;
}
};
//#endregion
//#region src/resolve-config.ts
const hookUtils = {
omit: _bamboocss_shared.omit,
pick: _bamboocss_shared.pick,
traverse: _bamboocss_shared.traverse
};
/**
* Resolve the final config (including presets)
* @bamboocss/preset-base: ALWAYS included if NOT using eject: true
* @bamboocss/preset-bamboo: only included by default if no presets
*/
async function resolveConfig(result, cwd) {
const presets = /* @__PURE__ */ new Set();
if (!result.config.eject) presets.add(_bamboocss_preset_base.preset);
if (result.config.presets) result.config.presets.forEach((preset) => {
presets.add(getBundledPreset(preset) ?? preset);
});
else if (!result.config.eject) presets.add(_bamboocss_preset_bamboo.preset);
result.config.presets = Array.from(presets);
const userConfig = result.config;
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) pluginHooks.push({
name: _bamboocss_shared.BAMBOO_CONFIG_NAME,
hooks: userConfig.hooks
});
const earlyHooks = require_merge_config.mergeHooks(pluginHooks);
const mergedConfig = await getResolvedConfig(result.config, cwd, earlyHooks);
const hooks = mergedConfig.hooks ?? {};
if (mergedConfig.logLevel) _bamboocss_logger.logger.level = mergedConfig.logLevel;
validateConfig(mergedConfig);
const loadConfigResult = {
...result,
config: mergedConfig
};
if (hooks["config:resolved"]) {
const result = await hooks["config:resolved"]({
config: loadConfigResult.config,
path: loadConfigResult.path,
dependencies: loadConfigResult.dependencies,
utils: hookUtils
});
if (result) loadConfigResult.config = result;
}
const serialized = (0, _bamboocss_shared.stringifyJson)(Object.assign({}, loadConfigResult.config, {
name: _bamboocss_shared.BAMBOO_CONFIG_NAME,
presets: []
}));
const deserialize = () => (0, _bamboocss_shared.parseJson)(serialized);
return {
...loadConfigResult,
serialized,
deserialize,
hooks
};
}
//#endregion
//#region src/load-config.ts
/**
* Find, load and resolve the final config (including presets)
*/
async function loadConfig(options) {
return resolveConfig(await bundleConfig(options), options.cwd);
}
//#endregion
exports.bundleConfig = bundleConfig;
exports.convertTsPathsToRegexes = convertTsPathsToRegexes;
exports.diffConfigs = require_diff_config.diffConfigs;
exports.findConfig = findConfig;
exports.getConfigDependencies = getConfigDependencies;
exports.getResolvedConfig = getResolvedConfig;
exports.loadConfig = loadConfig;
exports.mergeConfigs = require_merge_config.mergeConfigs;
exports.mergeHooks = require_merge_config.mergeHooks;
exports.resolveConfig = resolveConfig;
import { diffConfigs } from "./diff-config.cjs";
import { n as convertTsPathsToRegexes, t as PathMapping } from "./ts-config-paths-wVx39QZ0.cjs";
import { n as mergeHooks, t as mergeConfigs } from "./merge-config-DVOlBQJY.cjs";
import { BambooHooks, Config, ConfigTsOptions, LoadConfigResult as LoadConfigResult$1 } from "@bamboocss/types";
import { CompilerOptions, TypeAcquisition } from "typescript";
//#region src/types.d.ts
interface ConfigFileOptions {
cwd: string;
file?: string;
}
interface BundleConfigResult<T = Config> {
config: T;
dependencies: string[];
path: string;
}
//#endregion
//#region src/bundle-config.d.ts
declare function bundleConfig(options: ConfigFileOptions): Promise<BundleConfigResult>;
//#endregion
//#region src/find-config.d.ts
declare function findConfig(options: Partial<ConfigFileOptions>): string;
//#endregion
//#region ../../node_modules/.pnpm/pkg-types@2.3.0/node_modules/pkg-types/dist/index.d.mts
type StripEnums<T extends Record<string, any>> = { [K in keyof T]: T[K] extends boolean ? T[K] : T[K] extends string ? T[K] : T[K] extends object ? T[K] : T[K] extends Array<any> ? T[K] : T[K] extends undefined ? undefined : any };
interface TSConfig {
compilerOptions?: StripEnums<CompilerOptions>;
exclude?: string[];
compileOnSave?: boolean;
extends?: string | string[];
files?: string[];
include?: string[];
typeAcquisition?: TypeAcquisition;
references?: {
path: string;
}[];
}
/**
* Defines a TSConfig structure.
* @param tsconfig - The contents of `tsconfig.json` as an object. See {@link TSConfig}.
* @returns the same `tsconfig.json` object.
*/
//#endregion
//#region src/get-mod-deps.d.ts
interface GetDepsOptions {
filename: string;
ext: string;
cwd: string;
seen: Set<string>;
baseUrl: string | undefined;
pathMappings: PathMapping[];
foundModuleAliases: Map<string, string>;
compilerOptions?: TSConfig['compilerOptions'];
}
declare function getConfigDependencies(filePath: string, tsOptions?: ConfigTsOptions, compilerOptions?: TSConfig['compilerOptions']): {
deps: Set<string>;
aliases: Map<string, string>;
};
//#endregion
//#region src/get-resolved-config.d.ts
type Extendable<T> = T & {
extend?: T;
};
type ExtendableConfig = Extendable<Config>;
/**
* Recursively merge all presets into a single config (depth-first using stack)
*/
declare function getResolvedConfig(config: ExtendableConfig, cwd: string, hooks?: Partial<BambooHooks>): Promise<Config>;
//#endregion
//#region src/load-config.d.ts
/**
* Find, load and resolve the final config (including presets)
*/
declare function loadConfig(options: ConfigFileOptions): Promise<LoadConfigResult>;
//#endregion
//#region src/resolve-config.d.ts
/**
* Resolve the final config (including presets)
* @bamboocss/preset-base: ALWAYS included if NOT using eject: true
* @bamboocss/preset-bamboo: only included by default if no presets
*/
declare function resolveConfig(result: BundleConfigResult, cwd: string): Promise<LoadConfigResult$1>;
//#endregion
export { type BundleConfigResult, type GetDepsOptions, bundleConfig, convertTsPathsToRegexes, diffConfigs, findConfig, getConfigDependencies, getResolvedConfig, loadConfig, mergeConfigs, mergeHooks, resolveConfig };
import { diffConfigs } from "./diff-config.mjs";
import { n as convertTsPathsToRegexes, t as PathMapping } from "./ts-config-paths-CvGId8kq.mjs";
import { n as mergeHooks, t as mergeConfigs } from "./merge-config-oEiBYbfB.mjs";
import { CompilerOptions, TypeAcquisition } from "typescript";
import { BambooHooks, Config, ConfigTsOptions, LoadConfigResult as LoadConfigResult$1 } from "@bamboocss/types";
//#region src/types.d.ts
interface ConfigFileOptions {
cwd: string;
file?: string;
}
interface BundleConfigResult<T = Config> {
config: T;
dependencies: string[];
path: string;
}
//#endregion
//#region src/bundle-config.d.ts
declare function bundleConfig(options: ConfigFileOptions): Promise<BundleConfigResult>;
//#endregion
//#region src/find-config.d.ts
declare function findConfig(options: Partial<ConfigFileOptions>): string;
//#endregion
//#region ../../node_modules/.pnpm/pkg-types@2.3.0/node_modules/pkg-types/dist/index.d.mts
type StripEnums<T extends Record<string, any>> = { [K in keyof T]: T[K] extends boolean ? T[K] : T[K] extends string ? T[K] : T[K] extends object ? T[K] : T[K] extends Array<any> ? T[K] : T[K] extends undefined ? undefined : any };
interface TSConfig {
compilerOptions?: StripEnums<CompilerOptions>;
exclude?: string[];
compileOnSave?: boolean;
extends?: string | string[];
files?: string[];
include?: string[];
typeAcquisition?: TypeAcquisition;
references?: {
path: string;
}[];
}
/**
* Defines a TSConfig structure.
* @param tsconfig - The contents of `tsconfig.json` as an object. See {@link TSConfig}.
* @returns the same `tsconfig.json` object.
*/
//#endregion
//#region src/get-mod-deps.d.ts
interface GetDepsOptions {
filename: string;
ext: string;
cwd: string;
seen: Set<string>;
baseUrl: string | undefined;
pathMappings: PathMapping[];
foundModuleAliases: Map<string, string>;
compilerOptions?: TSConfig['compilerOptions'];
}
declare function getConfigDependencies(filePath: string, tsOptions?: ConfigTsOptions, compilerOptions?: TSConfig['compilerOptions']): {
deps: Set<string>;
aliases: Map<string, string>;
};
//#endregion
//#region src/get-resolved-config.d.ts
type Extendable<T> = T & {
extend?: T;
};
type ExtendableConfig = Extendable<Config>;
/**
* Recursively merge all presets into a single config (depth-first using stack)
*/
declare function getResolvedConfig(config: ExtendableConfig, cwd: string, hooks?: Partial<BambooHooks>): Promise<Config>;
//#endregion
//#region src/load-config.d.ts
/**
* Find, load and resolve the final config (including presets)
*/
declare function loadConfig(options: ConfigFileOptions): Promise<LoadConfigResult>;
//#endregion
//#region src/resolve-config.d.ts
/**
* Resolve the final config (including presets)
* @bamboocss/preset-base: ALWAYS included if NOT using eject: true
* @bamboocss/preset-bamboo: only included by default if no presets
*/
declare function resolveConfig(result: BundleConfigResult, cwd: string): Promise<LoadConfigResult$1>;
//#endregion
export { type BundleConfigResult, type GetDepsOptions, bundleConfig, convertTsPathsToRegexes, diffConfigs, findConfig, getConfigDependencies, getResolvedConfig, loadConfig, mergeConfigs, mergeHooks, resolveConfig };
require("./diff-config-Co_3mDXE.cjs");
let _bamboocss_logger = require("@bamboocss/logger");
let _bamboocss_shared = require("@bamboocss/shared");
//#region src/merge-hooks.ts
const mergeHooks = (plugins) => {
const hooksFns = {};
plugins.forEach(({ name, hooks }) => {
Object.entries(hooks ?? {}).forEach(([key, value]) => {
if (!hooksFns[key]) hooksFns[key] = [];
hooksFns[key].push([name, value]);
});
});
return Object.fromEntries(Object.entries(hooksFns).map(([key, entries]) => {
const fns = entries.map(([name, fn]) => tryCatch(name, fn));
const reducer = key in reducers ? reducers[key] : void 0;
if (reducer) return [key, reducer(fns)];
return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
}));
};
const createReducer = (reducer) => reducer;
const reducers = {
"config:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.config;
let config = args.config;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, {
config,
original
}));
if (result !== void 0) config = result;
}
return config;
}),
"parser:before": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
content,
original
}));
if (result !== void 0) content = result;
}
return content;
}),
"parser:preprocess": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.data;
let data = args.data;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
data,
original
}));
if (result !== void 0) data = result;
}
return data;
}),
"cssgen:done": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
content,
original
}));
if (result !== void 0) content = result;
}
return content;
}),
"codegen:prepare": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.artifacts;
let artifacts = args.artifacts;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, {
artifacts,
original
}));
if (result) artifacts = result;
}
return artifacts;
}),
"preset:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.preset;
let preset = args.preset;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, {
preset,
original
}));
if (result !== void 0) preset = result;
}
return preset;
}),
"css:optimize": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.css;
let css = args.css;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
css,
original
}));
if (result !== void 0) css = result;
}
return css;
})
};
const syncHooks = [
"context:created",
"parser:before",
"parser:preprocess",
"parser:after",
"cssgen:done",
"css:optimize"
];
const callAllAsync = (...fns) => async (...a) => {
for (const fn of fns) await fn?.(...a);
};
const callAll = (...fns) => (...a) => {
fns.forEach((fn) => fn?.(...a));
};
const tryCatch = (name, fn) => {
return (...args) => {
try {
return fn(...args);
} catch (e) {
_bamboocss_logger.logger.caughtError("hooks", `Error in plugin "${name}"`, e);
}
};
};
//#endregion
//#region src/validation/utils.ts
const REFERENCE_REGEX = /({([^}]*)})/g;
const curlyBracketRegex = /[{}]/g;
const isValidToken = (token) => (0, _bamboocss_shared.isObject)(token) && Object.hasOwnProperty.call(token, "value");
const isTokenReference = (value) => typeof value === "string" && REFERENCE_REGEX.test(value);
const formatPath = (path) => path;
function getReferences(value) {
if (typeof value !== "string") return [];
const matches = value.match(REFERENCE_REGEX);
if (!matches) return [];
return matches.map((match) => match.replace(curlyBracketRegex, "")).map((value) => {
return value.trim().split("/")[0];
});
}
const serializeTokenValue = (value) => {
if ((0, _bamboocss_shared.isString)(value)) return value;
if ((0, _bamboocss_shared.isObject)(value)) return Object.values(value).map((v) => serializeTokenValue(v)).join(" ");
if (Array.isArray(value)) return value.map((v) => serializeTokenValue(v)).join(" ");
return value.toString();
};
//#endregion
//#region src/merge-config.ts
/**
* Collect all `extend` properties into an array (to avoid mutation)
*/
function getExtends(items) {
return items.reduce((merged, { extend }) => {
if (!extend) return merged;
return (0, _bamboocss_shared.mergeWith)(merged, extend, (originalValue, newValue) => {
if (newValue === void 0) return originalValue ?? [];
if (originalValue === void 0) return [newValue];
if (Array.isArray(originalValue)) return [newValue, ...originalValue];
return [newValue, originalValue];
});
}, {});
}
/**
* Separate the `extend` properties from the rest of the object
*/
function mergeRecords(records) {
return {
...records.reduce((acc, record) => (0, _bamboocss_shared.assign)(acc, record), {}),
extend: getExtends(records)
};
}
/**
* Merge all `extend` properties into the rest of the object
*/
function mergeExtensions(records) {
const { extend = [], ...restProps } = mergeRecords(records);
return (0, _bamboocss_shared.mergeWith)(restProps, extend, (obj, extensions) => {
return (0, _bamboocss_shared.mergeAndConcat)({}, obj, ...extensions);
});
}
const isEmptyObject = (obj) => typeof obj === "object" && Object.keys(obj).length === 0;
const compact = (obj) => {
return Object.keys(obj).reduce((acc, key) => {
if (obj[key] !== void 0 && !isEmptyObject(obj[key])) acc[key] = obj[key];
return acc;
}, {});
};
const tokenKeys = [
"description",
"extensions",
"type",
"value",
"deprecated"
];
/**
* Merge all configs into a single config
*/
function mergeConfigs(configs) {
const userConfig = configs.at(-1);
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) pluginHooks.push({
name: _bamboocss_shared.BAMBOO_CONFIG_NAME,
hooks: userConfig.hooks
});
const reversed = Array.from(configs).reverse();
const withoutEmpty = compact((0, _bamboocss_shared.assign)({
conditions: mergeExtensions(reversed.map((config) => config.conditions ?? {})),
theme: mergeExtensions(reversed.map((config) => config.theme ?? {})),
patterns: mergeExtensions(reversed.map((config) => config.patterns ?? {})),
utilities: mergeExtensions(reversed.map((config) => config.utilities ?? {})),
globalCss: mergeExtensions(reversed.map((config) => config.globalCss ?? {})),
globalVars: mergeExtensions(reversed.map((config) => config.globalVars ?? {})),
globalFontface: mergeExtensions(reversed.map((config) => config.globalFontface ?? {})),
globalPositionTry: mergeExtensions(reversed.map((config) => config.globalPositionTry ?? {})),
staticCss: mergeExtensions(reversed.map((config) => config.staticCss ?? {})),
themes: mergeExtensions(reversed.map((config) => config.themes ?? {})),
hooks: mergeHooks(pluginHooks)
}, ...reversed));
/**
* Properly merge tokens between flat/nested forms by setting the flat form as the default
* preset:
* ```
* tokens: {
* black: {
* value: "black"
* }
* }
* // color: "black"
* ```
*
* config:
* ```
* tokens: {
* black: {
* 0: { value: "black" },
* 10: { value: "black/10" },
* 20: { value: "black/20" },
* // ...
* }
* }
*
* // color: "black.20"
* ```
*/
if (withoutEmpty.theme?.tokens) (0, _bamboocss_shared.walkObject)(withoutEmpty.theme.tokens, (args) => args, { stop(token) {
if (!isValidToken(token)) return false;
if (Object.keys(token).filter((k) => !tokenKeys.includes(k)).length > 0) {
token.DEFAULT ||= {};
tokenKeys.forEach((key) => {
if (token[key] == null) return;
token.DEFAULT[key] ||= token[key];
delete token[key];
});
}
return true;
} });
return withoutEmpty;
}
//#endregion
Object.defineProperty(exports, "formatPath", {
enumerable: true,
get: function() {
return formatPath;
}
});
Object.defineProperty(exports, "getReferences", {
enumerable: true,
get: function() {
return getReferences;
}
});
Object.defineProperty(exports, "isTokenReference", {
enumerable: true,
get: function() {
return isTokenReference;
}
});
Object.defineProperty(exports, "isValidToken", {
enumerable: true,
get: function() {
return isValidToken;
}
});
Object.defineProperty(exports, "mergeConfigs", {
enumerable: true,
get: function() {
return mergeConfigs;
}
});
Object.defineProperty(exports, "mergeHooks", {
enumerable: true,
get: function() {
return mergeHooks;
}
});
Object.defineProperty(exports, "serializeTokenValue", {
enumerable: true,
get: function() {
return serializeTokenValue;
}
});
import { logger } from "@bamboocss/logger";
import { BAMBOO_CONFIG_NAME, assign, isObject, isString, mergeAndConcat, mergeWith, walkObject } from "@bamboocss/shared";
//#region src/merge-hooks.ts
const mergeHooks = (plugins) => {
const hooksFns = {};
plugins.forEach(({ name, hooks }) => {
Object.entries(hooks ?? {}).forEach(([key, value]) => {
if (!hooksFns[key]) hooksFns[key] = [];
hooksFns[key].push([name, value]);
});
});
return Object.fromEntries(Object.entries(hooksFns).map(([key, entries]) => {
const fns = entries.map(([name, fn]) => tryCatch(name, fn));
const reducer = key in reducers ? reducers[key] : void 0;
if (reducer) return [key, reducer(fns)];
return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
}));
};
const createReducer = (reducer) => reducer;
const reducers = {
"config:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.config;
let config = args.config;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, {
config,
original
}));
if (result !== void 0) config = result;
}
return config;
}),
"parser:before": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
content,
original
}));
if (result !== void 0) content = result;
}
return content;
}),
"parser:preprocess": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.data;
let data = args.data;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
data,
original
}));
if (result !== void 0) data = result;
}
return data;
}),
"cssgen:done": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
content,
original
}));
if (result !== void 0) content = result;
}
return content;
}),
"codegen:prepare": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.artifacts;
let artifacts = args.artifacts;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, {
artifacts,
original
}));
if (result) artifacts = result;
}
return artifacts;
}),
"preset:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.preset;
let preset = args.preset;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, {
preset,
original
}));
if (result !== void 0) preset = result;
}
return preset;
}),
"css:optimize": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.css;
let css = args.css;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, {
css,
original
}));
if (result !== void 0) css = result;
}
return css;
})
};
const syncHooks = [
"context:created",
"parser:before",
"parser:preprocess",
"parser:after",
"cssgen:done",
"css:optimize"
];
const callAllAsync = (...fns) => async (...a) => {
for (const fn of fns) await fn?.(...a);
};
const callAll = (...fns) => (...a) => {
fns.forEach((fn) => fn?.(...a));
};
const tryCatch = (name, fn) => {
return (...args) => {
try {
return fn(...args);
} catch (e) {
logger.caughtError("hooks", `Error in plugin "${name}"`, e);
}
};
};
//#endregion
//#region src/validation/utils.ts
const REFERENCE_REGEX = /({([^}]*)})/g;
const curlyBracketRegex = /[{}]/g;
const isValidToken = (token) => isObject(token) && Object.hasOwnProperty.call(token, "value");
const isTokenReference = (value) => typeof value === "string" && REFERENCE_REGEX.test(value);
const formatPath = (path) => path;
function getReferences(value) {
if (typeof value !== "string") return [];
const matches = value.match(REFERENCE_REGEX);
if (!matches) return [];
return matches.map((match) => match.replace(curlyBracketRegex, "")).map((value) => {
return value.trim().split("/")[0];
});
}
const serializeTokenValue = (value) => {
if (isString(value)) return value;
if (isObject(value)) return Object.values(value).map((v) => serializeTokenValue(v)).join(" ");
if (Array.isArray(value)) return value.map((v) => serializeTokenValue(v)).join(" ");
return value.toString();
};
//#endregion
//#region src/merge-config.ts
/**
* Collect all `extend` properties into an array (to avoid mutation)
*/
function getExtends(items) {
return items.reduce((merged, { extend }) => {
if (!extend) return merged;
return mergeWith(merged, extend, (originalValue, newValue) => {
if (newValue === void 0) return originalValue ?? [];
if (originalValue === void 0) return [newValue];
if (Array.isArray(originalValue)) return [newValue, ...originalValue];
return [newValue, originalValue];
});
}, {});
}
/**
* Separate the `extend` properties from the rest of the object
*/
function mergeRecords(records) {
return {
...records.reduce((acc, record) => assign(acc, record), {}),
extend: getExtends(records)
};
}
/**
* Merge all `extend` properties into the rest of the object
*/
function mergeExtensions(records) {
const { extend = [], ...restProps } = mergeRecords(records);
return mergeWith(restProps, extend, (obj, extensions) => {
return mergeAndConcat({}, obj, ...extensions);
});
}
const isEmptyObject = (obj) => typeof obj === "object" && Object.keys(obj).length === 0;
const compact = (obj) => {
return Object.keys(obj).reduce((acc, key) => {
if (obj[key] !== void 0 && !isEmptyObject(obj[key])) acc[key] = obj[key];
return acc;
}, {});
};
const tokenKeys = [
"description",
"extensions",
"type",
"value",
"deprecated"
];
/**
* Merge all configs into a single config
*/
function mergeConfigs(configs) {
const userConfig = configs.at(-1);
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) pluginHooks.push({
name: BAMBOO_CONFIG_NAME,
hooks: userConfig.hooks
});
const reversed = Array.from(configs).reverse();
const withoutEmpty = compact(assign({
conditions: mergeExtensions(reversed.map((config) => config.conditions ?? {})),
theme: mergeExtensions(reversed.map((config) => config.theme ?? {})),
patterns: mergeExtensions(reversed.map((config) => config.patterns ?? {})),
utilities: mergeExtensions(reversed.map((config) => config.utilities ?? {})),
globalCss: mergeExtensions(reversed.map((config) => config.globalCss ?? {})),
globalVars: mergeExtensions(reversed.map((config) => config.globalVars ?? {})),
globalFontface: mergeExtensions(reversed.map((config) => config.globalFontface ?? {})),
globalPositionTry: mergeExtensions(reversed.map((config) => config.globalPositionTry ?? {})),
staticCss: mergeExtensions(reversed.map((config) => config.staticCss ?? {})),
themes: mergeExtensions(reversed.map((config) => config.themes ?? {})),
hooks: mergeHooks(pluginHooks)
}, ...reversed));
/**
* Properly merge tokens between flat/nested forms by setting the flat form as the default
* preset:
* ```
* tokens: {
* black: {
* value: "black"
* }
* }
* // color: "black"
* ```
*
* config:
* ```
* tokens: {
* black: {
* 0: { value: "black" },
* 10: { value: "black/10" },
* 20: { value: "black/20" },
* // ...
* }
* }
*
* // color: "black.20"
* ```
*/
if (withoutEmpty.theme?.tokens) walkObject(withoutEmpty.theme.tokens, (args) => args, { stop(token) {
if (!isValidToken(token)) return false;
if (Object.keys(token).filter((k) => !tokenKeys.includes(k)).length > 0) {
token.DEFAULT ||= {};
tokenKeys.forEach((key) => {
if (token[key] == null) return;
token.DEFAULT[key] ||= token[key];
delete token[key];
});
}
return true;
} });
return withoutEmpty;
}
//#endregion
export { isValidToken as a, isTokenReference as i, formatPath as n, serializeTokenValue as o, getReferences as r, mergeHooks as s, mergeConfigs as t };
import { BambooPlugin, Config } from "@bamboocss/types";
//#region src/merge-hooks.d.ts
declare const mergeHooks: (plugins: BambooPlugin[]) => BambooHooks;
//#endregion
//#region src/merge-config.d.ts
type Extendable<T> = T & {
extend?: T;
};
type ExtendableConfig = Extendable<Config>;
/**
* Merge all configs into a single config
*/
declare function mergeConfigs(configs: ExtendableConfig[]): any;
//#endregion
export { mergeHooks as n, mergeConfigs as t };
import { BambooPlugin, Config } from "@bamboocss/types";
//#region src/merge-hooks.d.ts
declare const mergeHooks: (plugins: BambooPlugin[]) => BambooHooks;
//#endregion
//#region src/merge-config.d.ts
type Extendable<T> = T & {
extend?: T;
};
type ExtendableConfig = Extendable<Config>;
/**
* Merge all configs into a single config
*/
declare function mergeConfigs(configs: ExtendableConfig[]): any;
//#endregion
export { mergeHooks as n, mergeConfigs as t };
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_merge_config = require("./merge-config-CcNpHJit.cjs");
exports.mergeConfigs = require_merge_config.mergeConfigs;
exports.mergeHooks = require_merge_config.mergeHooks;
import { n as mergeHooks, t as mergeConfigs } from "./merge-config-DVOlBQJY.cjs";
export { mergeConfigs, mergeHooks };
import { n as mergeHooks, t as mergeConfigs } from "./merge-config-oEiBYbfB.mjs";
export { mergeConfigs, mergeHooks };
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
require("./diff-config-Co_3mDXE.cjs");
let path = require("path");
//#region src/resolve-ts-path-pattern.ts
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/index.ts#LL231C57-L231C57
*/
const resolveTsPathPattern = (pathMappings, moduleSpecifier) => {
for (const mapping of pathMappings) {
const match = moduleSpecifier.match(mapping.pattern);
if (!match) continue;
for (const pathTemplate of mapping.paths) {
let starCount = 0;
return pathTemplate.replace(/\*/g, () => {
return match[Math.min(++starCount, match.length - 1)];
}).split(path.sep).join(path.posix.sep);
}
}
};
//#endregion
exports.resolveTsPathPattern = resolveTsPathPattern;
import { t as PathMapping } from "./ts-config-paths-wVx39QZ0.cjs";
//#region src/resolve-ts-path-pattern.d.ts
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/index.ts#LL231C57-L231C57
*/
declare const resolveTsPathPattern: (pathMappings: PathMapping[], moduleSpecifier: string) => string | undefined;
//#endregion
export { resolveTsPathPattern };
import { t as PathMapping } from "./ts-config-paths-CvGId8kq.mjs";
//#region src/resolve-ts-path-pattern.d.ts
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/index.ts#LL231C57-L231C57
*/
declare const resolveTsPathPattern: (pathMappings: PathMapping[], moduleSpecifier: string) => string | undefined;
//#endregion
export { resolveTsPathPattern };
//#region src/ts-config-paths.d.ts
interface PathMapping {
pattern: RegExp;
paths: string[];
}
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/mappings.ts
*/
declare function convertTsPathsToRegexes(paths: Record<string, string[]>, baseUrl: string): PathMapping[];
//#endregion
export { convertTsPathsToRegexes as n, PathMapping as t };
//#region src/ts-config-paths.d.ts
interface PathMapping {
pattern: RegExp;
paths: string[];
}
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/mappings.ts
*/
declare function convertTsPathsToRegexes(paths: Record<string, string[]>, baseUrl: string): PathMapping[];
//#endregion
export { convertTsPathsToRegexes as n, PathMapping as t };
+2
-6

@@ -1,6 +0,2 @@

import {
diffConfigs
} from "./chunk-6TQW6KOI.mjs";
export {
diffConfigs
};
import { t as diffConfigs } from "./diff-config-KDQWMnQN.mjs";
export { diffConfigs };

@@ -1,630 +0,561 @@

import {
diffConfigs
} from "./chunk-6TQW6KOI.mjs";
import {
SEP,
formatPath,
getReferences,
isTokenReference,
isValidToken,
mergeConfigs,
mergeHooks,
serializeTokenValue
} from "./chunk-RIBK22OM.mjs";
import {
resolveTsPathPattern
} from "./chunk-RPIVZP2I.mjs";
// src/bundle-config.ts
import { t as diffConfigs } from "./diff-config-KDQWMnQN.mjs";
import { resolveTsPathPattern } from "./resolve-ts-path-pattern.mjs";
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";
import { logger } from "@bamboocss/logger";
import { BambooError as BambooError2 } from "@bamboocss/shared";
import { BAMBOO_CONFIG_NAME, BambooError, getUnit, isObject, isString, omit, parseJson, pick, stringifyJson, traverse, walkObject } from "@bamboocss/shared";
import { bundleNRequire } from "bundle-n-require";
// src/find-config.ts
import { BambooError } from "@bamboocss/shared";
import findUp from "escalade/sync";
import { resolve } from "path";
// src/is-bamboo-config.ts
var configName = "bamboo";
var bambooConfigFiles = /* @__PURE__ */ new Set([
`${configName}.config.ts`,
`${configName}.config.js`,
`${configName}.config.mts`,
`${configName}.config.mjs`,
`${configName}.config.cts`,
`${configName}.config.cjs`
import path, { resolve } from "path";
import fs from "fs";
import ts from "typescript";
import { preset as presetBase } from "@bamboocss/preset-base";
import { preset as presetBamboo } from "@bamboocss/preset-bamboo";
//#region src/is-bamboo-config.ts
const configName = "bamboo";
const bambooConfigFiles = new Set([
`${configName}.config.ts`,
`${configName}.config.js`,
`${configName}.config.mts`,
`${configName}.config.mjs`,
`${configName}.config.cts`,
`${configName}.config.cjs`
]);
var isBambooConfig = (file) => bambooConfigFiles.has(file);
// src/find-config.ts
const isBambooConfig = (file) => bambooConfigFiles.has(file);
//#endregion
//#region src/find-config.ts
function findConfig(options) {
const { cwd = process.cwd(), file } = options;
if (file) {
return resolve(cwd, file);
}
const configPath = findUp(cwd, (_dir, paths) => paths.find(isBambooConfig));
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\`?`
);
}
return configPath;
const { cwd = process.cwd(), file } = options;
if (file) return resolve(cwd, file);
const configPath = findUp(cwd, (_dir, paths) => paths.find(isBambooConfig));
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\`?`);
return configPath;
}
// src/bundle-config.ts
//#endregion
//#region src/bundle-config.ts
async function bundle(filepath, cwd) {
const { mod, dependencies } = await bundleNRequire(filepath, {
cwd,
interopDefault: true
});
const config = mod?.default ?? mod;
return {
config,
dependencies
};
const { mod, dependencies } = await bundleNRequire(filepath, {
cwd,
interopDefault: true
});
return {
config: mod?.default ?? mod,
dependencies
};
}
async function bundleConfig(options) {
const { cwd, file } = options;
const filePath = findConfig({ cwd, file });
logger.debug("config:path", filePath);
const result = await bundle(filePath, cwd);
if (typeof result.config !== "object") {
throw new BambooError2("CONFIG_ERROR", `\u{1F4A5} Config must export or return an object.`);
}
result.config.outdir ??= "styled-system";
result.config.validation ??= "warn";
return {
...result,
config: result.config,
path: filePath
};
const { cwd, file } = options;
const filePath = findConfig({
cwd,
file
});
logger.debug("config:path", filePath);
const result = await bundle(filePath, cwd);
if (typeof result.config !== "object") throw new BambooError("CONFIG_ERROR", `💥 Config must export or return an object.`);
result.config.outdir ??= "styled-system";
result.config.validation ??= "warn";
return {
...result,
config: result.config,
path: filePath
};
}
// src/get-mod-deps.ts
import fs from "fs";
import path from "path";
import ts from "typescript";
// src/ts-config-paths.ts
import { resolve as resolve2 } from "path";
//#endregion
//#region src/ts-config-paths.ts
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/mappings.ts
*/
function convertTsPathsToRegexes(paths, baseUrl) {
const sortedPatterns = Object.keys(paths).sort((a, b) => getPrefixLength(b) - getPrefixLength(a));
const resolved = [];
for (let pattern of sortedPatterns) {
const relativePaths = paths[pattern];
pattern = escapeStringRegexp(pattern).replace(/\*/g, "(.+)");
resolved.push({
pattern: new RegExp("^" + pattern + "$"),
paths: relativePaths.map((relativePath) => resolve2(baseUrl, relativePath))
});
}
return resolved;
const sortedPatterns = Object.keys(paths).sort((a, b) => getPrefixLength(b) - getPrefixLength(a));
const resolved = [];
for (let pattern of sortedPatterns) {
const relativePaths = paths[pattern];
pattern = escapeStringRegexp(pattern).replace(/\*/g, "(.+)");
resolved.push({
pattern: new RegExp("^" + pattern + "$"),
paths: relativePaths.map((relativePath) => resolve(baseUrl, relativePath))
});
}
return resolved;
}
function getPrefixLength(pattern) {
const prefixLength = pattern.indexOf("*");
return pattern.substr(0, prefixLength).length;
const prefixLength = pattern.indexOf("*");
return pattern.substr(0, prefixLength).length;
}
function escapeStringRegexp(string) {
return string.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/-/g, "\\x2d");
return string.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/-/g, "\\x2d");
}
// src/get-mod-deps.ts
var jsExtensions = [".js", ".cjs", ".mjs"];
var jsResolutionOrder = ["", ".js", ".cjs", ".mjs", ".ts", ".cts", ".mts", ".jsx", ".tsx"];
var tsResolutionOrder = ["", ".ts", ".cts", ".mts", ".tsx", ".js", ".cjs", ".mjs", ".jsx"];
//#endregion
//#region src/get-mod-deps.ts
const jsExtensions = [
".js",
".cjs",
".mjs"
];
const jsResolutionOrder = [
"",
".js",
".cjs",
".mjs",
".ts",
".cts",
".mts",
".jsx",
".tsx"
];
const tsResolutionOrder = [
"",
".ts",
".cts",
".mts",
".tsx",
".js",
".cjs",
".mjs",
".jsx"
];
function resolveWithExtension(file, extensions) {
for (const ext of extensions) {
const full = `${file}${ext}`;
if (fs.existsSync(full) && fs.statSync(full).isFile()) {
return full;
}
}
for (const ext of extensions) {
const full = `${file}/index${ext}`;
if (fs.existsSync(full)) {
return full;
}
}
return null;
for (const ext of extensions) {
const full = `${file}${ext}`;
if (fs.existsSync(full) && fs.statSync(full).isFile()) return full;
}
for (const ext of extensions) {
const full = `${file}/index${ext}`;
if (fs.existsSync(full)) return full;
}
return null;
}
var importRegex = /import[\s\S]*?['"](.{3,}?)['"]/gi;
var importFromRegex = /import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
var requireRegex = /require\(['"`](.+)['"`]\)/gi;
var exportRegex = /export[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
const importRegex = /import[\s\S]*?['"](.{3,}?)['"]/gi;
const importFromRegex = /import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
const requireRegex = /require\(['"`](.+)['"`]\)/gi;
const exportRegex = /export[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
function getDeps(opts, fromAlias) {
const { filename, seen } = opts;
const { moduleResolution: _, ...compilerOptions } = opts.compilerOptions ?? {};
const absoluteFile = resolveWithExtension(
path.resolve(opts.cwd, filename),
jsExtensions.includes(opts.ext) ? jsResolutionOrder : tsResolutionOrder
);
if (absoluteFile === null) return;
if (fromAlias) {
opts.foundModuleAliases.set(fromAlias, absoluteFile);
}
if (seen.size > 1 && seen.has(absoluteFile)) return;
seen.add(absoluteFile);
const contents = fs.readFileSync(absoluteFile, "utf-8");
const fileDeps = [
...contents.matchAll(importRegex),
...contents.matchAll(importFromRegex),
...contents.matchAll(requireRegex),
...contents.matchAll(exportRegex)
];
if (!fileDeps.length) return;
const nextOpts = {
// Resolve new base for new imports/requires
cwd: path.dirname(absoluteFile),
ext: path.extname(absoluteFile),
seen,
baseUrl: opts.baseUrl,
pathMappings: opts.pathMappings,
foundModuleAliases: opts.foundModuleAliases
};
fileDeps.forEach((match) => {
const mod = match[1];
if (mod[0] === ".") {
getDeps(Object.assign({}, nextOpts, { filename: mod }));
return;
}
try {
const found = ts.resolveModuleName(mod, absoluteFile, compilerOptions, ts.sys).resolvedModule;
if (found && found.extension.endsWith("ts")) {
getDeps(Object.assign({}, nextOpts, { filename: found.resolvedFileName }));
return;
}
if (!opts.pathMappings) return;
const filename2 = resolveTsPathPattern(opts.pathMappings, mod);
if (!filename2) return;
getDeps(Object.assign({}, nextOpts, { filename: filename2 }), mod);
} catch {
}
});
const { filename, seen } = opts;
const { moduleResolution: _, ...compilerOptions } = opts.compilerOptions ?? {};
const absoluteFile = resolveWithExtension(path.resolve(opts.cwd, filename), jsExtensions.includes(opts.ext) ? jsResolutionOrder : tsResolutionOrder);
if (absoluteFile === null) return;
if (fromAlias) opts.foundModuleAliases.set(fromAlias, absoluteFile);
if (seen.size > 1 && seen.has(absoluteFile)) return;
seen.add(absoluteFile);
const contents = fs.readFileSync(absoluteFile, "utf-8");
const fileDeps = [
...contents.matchAll(importRegex),
...contents.matchAll(importFromRegex),
...contents.matchAll(requireRegex),
...contents.matchAll(exportRegex)
];
if (!fileDeps.length) return;
const nextOpts = {
cwd: path.dirname(absoluteFile),
ext: path.extname(absoluteFile),
seen,
baseUrl: opts.baseUrl,
pathMappings: opts.pathMappings,
foundModuleAliases: opts.foundModuleAliases
};
fileDeps.forEach((match) => {
const mod = match[1];
if (mod[0] === ".") {
getDeps(Object.assign({}, nextOpts, { filename: mod }));
return;
}
try {
const found = ts.resolveModuleName(mod, absoluteFile, compilerOptions, ts.sys).resolvedModule;
if (found && found.extension.endsWith("ts")) {
getDeps(Object.assign({}, nextOpts, { filename: found.resolvedFileName }));
return;
}
if (!opts.pathMappings) return;
const filename = resolveTsPathPattern(opts.pathMappings, mod);
if (!filename) return;
getDeps(Object.assign({}, nextOpts, { filename }), mod);
} catch {}
});
}
function getConfigDependencies(filePath, tsOptions = { pathMappings: [] }, compilerOptions) {
if (filePath === null) return { deps: /* @__PURE__ */ new Set(), aliases: /* @__PURE__ */ new Map() };
const foundModuleAliases = /* @__PURE__ */ new Map();
const deps = /* @__PURE__ */ new Set();
deps.add(filePath);
getDeps({
filename: filePath,
ext: path.extname(filePath),
cwd: path.dirname(filePath),
seen: deps,
baseUrl: tsOptions.baseUrl,
pathMappings: tsOptions.pathMappings ?? [],
foundModuleAliases,
compilerOptions
});
return { deps, aliases: foundModuleAliases };
if (filePath === null) return {
deps: /* @__PURE__ */ new Set(),
aliases: /* @__PURE__ */ new Map()
};
const foundModuleAliases = /* @__PURE__ */ new Map();
const deps = /* @__PURE__ */ new Set();
deps.add(filePath);
getDeps({
filename: filePath,
ext: path.extname(filePath),
cwd: path.dirname(filePath),
seen: deps,
baseUrl: tsOptions.baseUrl,
pathMappings: tsOptions.pathMappings ?? [],
foundModuleAliases,
compilerOptions
});
return {
deps,
aliases: foundModuleAliases
};
}
// src/get-resolved-config.ts
import { omit, pick, traverse } from "@bamboocss/shared";
var hookUtils = {
omit,
pick,
traverse
//#endregion
//#region src/get-resolved-config.ts
const hookUtils$1 = {
omit,
pick,
traverse
};
/**
* Recursively merge all presets into a single config (depth-first using stack)
*/
async function getResolvedConfig(config, cwd, hooks) {
const stack = [config];
const configs = [];
while (stack.length > 0) {
const current = stack.pop();
const subPresets = current.presets ?? [];
for (const subPreset of subPresets) {
let presetConfig;
let presetName;
if (typeof subPreset === "string") {
const presetModule = await bundle(subPreset, cwd);
presetConfig = presetModule.config;
presetName = subPreset;
} else {
presetConfig = await subPreset;
presetName = presetConfig.name || "unknown-preset";
}
if (hooks?.["preset:resolved"]) {
const resolvedPreset = await hooks["preset:resolved"]({
preset: presetConfig,
name: presetName,
utils: hookUtils
});
if (resolvedPreset !== void 0) {
presetConfig = resolvedPreset;
}
}
stack.push(presetConfig);
}
configs.unshift(current);
}
const merged = mergeConfigs(configs);
merged.presets = configs.slice(0, -1);
return merged;
const stack = [config];
const configs = [];
while (stack.length > 0) {
const current = stack.pop();
const subPresets = current.presets ?? [];
for (const subPreset of subPresets) {
let presetConfig;
let presetName;
if (typeof subPreset === "string") {
presetConfig = (await bundle(subPreset, cwd)).config;
presetName = subPreset;
} else {
presetConfig = await subPreset;
presetName = presetConfig.name || "unknown-preset";
}
if (hooks?.["preset:resolved"]) {
const resolvedPreset = await hooks["preset:resolved"]({
preset: presetConfig,
name: presetName,
utils: hookUtils$1
});
if (resolvedPreset !== void 0) presetConfig = resolvedPreset;
}
stack.push(presetConfig);
}
configs.unshift(current);
}
const merged = mergeConfigs(configs);
merged.presets = configs.slice(0, -1);
return merged;
}
// src/resolve-config.ts
import { logger as logger3 } from "@bamboocss/logger";
import { BAMBOO_CONFIG_NAME, omit as omit2, parseJson, pick as pick2, stringifyJson, traverse as traverse2 } from "@bamboocss/shared";
// src/bundled-preset.ts
import { preset as presetBase } from "@bamboocss/preset-base";
import { preset as presetBamboo } from "@bamboocss/preset-bamboo";
var bundledPresets = {
"@bamboocss/preset-base": presetBase,
"@bamboocss/preset-bamboo": presetBamboo,
"@bamboocss/dev/presets": presetBamboo
//#endregion
//#region src/bundled-preset.ts
const bundledPresets = {
"@bamboocss/preset-base": presetBase,
"@bamboocss/preset-bamboo": presetBamboo,
"@bamboocss/dev/presets": presetBamboo
};
var bundledPresetsNames = Object.keys(bundledPresets);
var isBundledPreset = (preset) => bundledPresetsNames.includes(preset);
var getBundledPreset = (preset) => {
return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
const bundledPresetsNames = Object.keys(bundledPresets);
const isBundledPreset = (preset) => bundledPresetsNames.includes(preset);
const getBundledPreset = (preset) => {
return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
};
// src/validate-config.ts
import { logger as logger2 } from "@bamboocss/logger";
import { BambooError as BambooError3 } from "@bamboocss/shared";
// src/validation/validate-artifact.ts
var validateArtifactNames = (names, addError) => {
names.recipes.forEach((recipeName) => {
if (names.slotRecipes.has(recipeName)) {
addError("recipes", `This recipe name is already used in \`theme.slotRecipes\`: ${recipeName}`);
}
if (names.patterns.has(recipeName)) {
addError("recipes", `This recipe name is already used in \`patterns\`: \`${recipeName}\``);
}
});
names.slotRecipes.forEach((recipeName) => {
if (names.patterns.has(recipeName)) {
addError("recipes", `This recipe name is already used in \`patterns\`: ${recipeName}`);
}
});
//#endregion
//#region src/validation/validate-artifact.ts
const validateArtifactNames = (names, addError) => {
names.recipes.forEach((recipeName) => {
if (names.slotRecipes.has(recipeName)) addError("recipes", `This recipe name is already used in \`theme.slotRecipes\`: ${recipeName}`);
if (names.patterns.has(recipeName)) addError("recipes", `This recipe name is already used in \`patterns\`: \`${recipeName}\``);
});
names.slotRecipes.forEach((recipeName) => {
if (names.patterns.has(recipeName)) addError("recipes", `This recipe name is already used in \`patterns\`: ${recipeName}`);
});
};
// src/validation/validate-breakpoints.ts
import { getUnit } from "@bamboocss/shared";
var validateBreakpoints = (breakpoints, addError) => {
if (!breakpoints) return;
const units = /* @__PURE__ */ new Set();
const values = Object.values(breakpoints);
for (const value of values) {
const unit = getUnit(value) ?? "px";
units.add(unit);
}
if (units.size > 1) {
addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
}
//#endregion
//#region src/validation/validate-breakpoints.ts
const validateBreakpoints = (breakpoints, addError) => {
if (!breakpoints) return;
const units = /* @__PURE__ */ new Set();
const values = Object.values(breakpoints);
for (const value of values) {
const unit = getUnit(value) ?? "px";
units.add(unit);
}
if (units.size > 1) addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
};
// src/validation/validate-condition.ts
import { isString } from "@bamboocss/shared";
var validateObjectCondition = (obj, addError) => {
let hasSlot = false;
for (const [key, value] of Object.entries(obj)) {
if (!key.startsWith("@") && !key.includes("&")) {
addError("conditions", `Selectors should contain the \`&\` character: \`${key}\``);
}
if (value === "@slot") {
hasSlot = true;
continue;
}
if (typeof value === "string") {
addError(
"conditions",
`Object condition leaves must be the literal string \`'@slot'\`, got \`${JSON.stringify(value)}\` at \`${key}\``
);
continue;
}
if (typeof value === "object" && value !== null) {
const nested = validateObjectCondition(value, addError);
if (nested.hasSlot) hasSlot = true;
}
}
return { hasSlot };
//#endregion
//#region src/validation/validate-condition.ts
const validateObjectCondition = (obj, addError) => {
let hasSlot = false;
for (const [key, value] of Object.entries(obj)) {
if (!key.startsWith("@") && !key.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${key}\``);
if (value === "@slot") {
hasSlot = true;
continue;
}
if (typeof value === "string") {
addError("conditions", `Object condition leaves must be the literal string \`'@slot'\`, got \`${JSON.stringify(value)}\` at \`${key}\``);
continue;
}
if (typeof value === "object" && value !== null) {
if (validateObjectCondition(value, addError).hasSlot) hasSlot = true;
}
}
return { hasSlot };
};
var validateConditions = (conditions, addError) => {
if (!conditions) return;
Object.values(conditions).forEach((condition) => {
if (isString(condition)) {
if (!condition.startsWith("@") && !condition.includes("&")) {
addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
}
return;
}
if (Array.isArray(condition)) {
condition.forEach((c) => {
if (!c.startsWith("@") && !c.includes("&")) {
addError("conditions", `Selectors should contain the \`&\` character: \`${c}\``);
}
});
return;
}
const { hasSlot } = validateObjectCondition(condition, addError);
if (!hasSlot) {
addError("conditions", `Object conditions must contain at least one \`'@slot'\` marker`);
}
});
const validateConditions = (conditions, addError) => {
if (!conditions) return;
Object.values(conditions).forEach((condition) => {
if (isString(condition)) {
if (!condition.startsWith("@") && !condition.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
return;
}
if (Array.isArray(condition)) {
condition.forEach((c) => {
if (!c.startsWith("@") && !c.includes("&")) addError("conditions", `Selectors should contain the \`&\` character: \`${c}\``);
});
return;
}
const { hasSlot } = validateObjectCondition(condition, addError);
if (!hasSlot) addError("conditions", `Object conditions must contain at least one \`'@slot'\` marker`);
});
};
// src/validation/validate-patterns.ts
var validatePatterns = (patterns, names) => {
if (!patterns) return;
Object.keys(patterns).forEach((patternName) => {
names.patterns.add(patternName);
});
//#endregion
//#region src/validation/validate-patterns.ts
const validatePatterns = (patterns, names) => {
if (!patterns) return;
Object.keys(patterns).forEach((patternName) => {
names.patterns.add(patternName);
});
};
// src/validation/validate-recipes.ts
var validateRecipes = (options) => {
const {
config: { theme },
artifacts
} = options;
if (!theme) return;
if (theme.recipes) {
Object.keys(theme.recipes).forEach((recipeName) => {
artifacts.recipes.add(recipeName);
});
}
if (theme.slotRecipes) {
Object.keys(theme.slotRecipes).forEach((recipeName) => {
artifacts.slotRecipes.add(recipeName);
});
}
return artifacts;
//#endregion
//#region src/validation/validate-recipes.ts
const validateRecipes = (options) => {
const { config: { theme }, artifacts } = options;
if (!theme) return;
if (theme.recipes) Object.keys(theme.recipes).forEach((recipeName) => {
artifacts.recipes.add(recipeName);
});
if (theme.slotRecipes) Object.keys(theme.slotRecipes).forEach((recipeName) => {
artifacts.slotRecipes.add(recipeName);
});
return artifacts;
};
// src/validation/validate-tokens.ts
import { isObject, walkObject } from "@bamboocss/shared";
// src/validation/validate-token-references.ts
var validateTokenReferences = (props) => {
const { valueAtPath, refsByPath, addError, typeByPath } = props;
refsByPath.forEach((refs, path2) => {
if (refs.has(path2)) {
addError("tokens", `Self token reference: \`${path2}\``);
}
const stack = [path2];
while (stack.length > 0) {
let currentPath = stack.pop();
if (currentPath.includes("/")) {
const [tokenPath] = currentPath.split("/");
currentPath = tokenPath;
}
const value = valueAtPath.get(currentPath);
if (!value) {
const configKey = typeByPath.get(path2);
addError("tokens", `Missing token: \`${currentPath}\` used in \`theme.${configKey}.${path2}\``);
}
if (isTokenReference(value) && !refsByPath.has(value)) {
addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
}
const deps = refsByPath.get(currentPath);
if (!deps) continue;
for (const transitiveDep of deps) {
if (path2 === transitiveDep) {
addError(
"tokens",
`Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path2}\``
);
break;
}
stack.push(transitiveDep);
}
}
});
//#endregion
//#region src/validation/validate-token-references.ts
const validateTokenReferences = (props) => {
const { valueAtPath, refsByPath, addError, typeByPath } = props;
refsByPath.forEach((refs, path) => {
if (refs.has(path)) addError("tokens", `Self token reference: \`${path}\``);
const stack = [path];
while (stack.length > 0) {
let currentPath = stack.pop();
if (currentPath.includes("/")) {
const [tokenPath] = currentPath.split("/");
currentPath = tokenPath;
}
const value = valueAtPath.get(currentPath);
if (!value) {
const configKey = typeByPath.get(path);
addError("tokens", `Missing token: \`${currentPath}\` used in \`theme.${configKey}.${path}\``);
}
if (isTokenReference(value) && !refsByPath.has(value)) addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
const deps = refsByPath.get(currentPath);
if (!deps) continue;
for (const transitiveDep of deps) {
if (path === transitiveDep) {
addError("tokens", `Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path}\``);
break;
}
stack.push(transitiveDep);
}
}
});
};
// src/validation/validate-tokens.ts
var validateTokens = (options) => {
const {
config: { theme },
tokens,
addError
} = options;
if (!theme) return;
const { tokenNames, semanticTokenNames, valueAtPath, refsByPath, typeByPath } = tokens;
if (theme.tokens) {
const tokenPaths = /* @__PURE__ */ new Set();
walkObject(
theme.tokens,
(value, paths) => {
const path2 = paths.join(SEP);
tokenNames.add(path2);
tokenPaths.add(path2);
valueAtPath.set(path2, value);
if (path2.includes("DEFAULT")) {
valueAtPath.set(path2.replace(SEP + "DEFAULT", ""), value);
}
},
{
stop: isValidToken
}
);
tokenPaths.forEach((path2) => {
const itemValue = valueAtPath.get(path2);
const formattedPath = formatPath(path2);
typeByPath.set(formattedPath, "tokens");
if (!isValidToken(itemValue)) {
addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
return;
}
if (path2.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
const valueStr = serializeTokenValue(itemValue.value || itemValue);
if (isTokenReference(valueStr)) {
refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
}
const references = refsByPath.get(formattedPath);
if (!references) return;
getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
});
}
if (theme.semanticTokens) {
const tokenPaths = /* @__PURE__ */ new Set();
walkObject(
theme.semanticTokens,
(value, paths) => {
const path2 = paths.join(SEP);
semanticTokenNames.add(path2);
valueAtPath.set(path2, value);
tokenPaths.add(path2);
if (path2.includes("DEFAULT")) {
valueAtPath.set(path2.replace(SEP + "DEFAULT", ""), value);
}
if (!isValidToken(value)) return;
walkObject(value, (itemValue, paths2) => {
const valuePath = paths2.join(SEP);
const formattedPath = formatPath(path2);
typeByPath.set(formattedPath, "semanticTokens");
const fullPath = formattedPath + "." + paths2.join(SEP);
if (valuePath.includes("value" + SEP + "value")) {
addError("tokens", `You used \`value\` twice resulting in an invalid token \`theme.tokens.${fullPath}\``);
}
const valueStr = serializeTokenValue(itemValue.value || itemValue);
if (isTokenReference(valueStr)) {
if (!refsByPath.has(formattedPath)) {
refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
}
const references = refsByPath.get(formattedPath);
if (!references) return;
getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
}
});
},
{
stop: isValidToken
}
);
tokenPaths.forEach((path2) => {
const formattedPath = formatPath(path2);
const value = valueAtPath.get(path2);
if (path2.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
if (!isObject(value) && !path2.includes("value")) {
addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
}
});
}
validateTokenReferences({ valueAtPath, refsByPath, addError, typeByPath });
//#endregion
//#region src/validation/validate-tokens.ts
const validateTokens = (options) => {
const { config: { theme }, tokens, addError } = options;
if (!theme) return;
const { tokenNames, semanticTokenNames, valueAtPath, refsByPath, typeByPath } = tokens;
if (theme.tokens) {
const tokenPaths = /* @__PURE__ */ new Set();
walkObject(theme.tokens, (value, paths) => {
const path = paths.join(".");
tokenNames.add(path);
tokenPaths.add(path);
valueAtPath.set(path, value);
if (path.includes("DEFAULT")) valueAtPath.set(path.replace(".DEFAULT", ""), value);
}, { stop: isValidToken });
tokenPaths.forEach((path) => {
const itemValue = valueAtPath.get(path);
const formattedPath = formatPath(path);
typeByPath.set(formattedPath, "tokens");
if (!isValidToken(itemValue)) {
addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
return;
}
if (path.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
const valueStr = serializeTokenValue(itemValue.value || itemValue);
if (isTokenReference(valueStr)) refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
const references = refsByPath.get(formattedPath);
if (!references) return;
getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
});
}
if (theme.semanticTokens) {
const tokenPaths = /* @__PURE__ */ new Set();
walkObject(theme.semanticTokens, (value, paths) => {
const path = paths.join(".");
semanticTokenNames.add(path);
valueAtPath.set(path, value);
tokenPaths.add(path);
if (path.includes("DEFAULT")) valueAtPath.set(path.replace(".DEFAULT", ""), value);
if (!isValidToken(value)) return;
walkObject(value, (itemValue, paths) => {
const valuePath = paths.join(".");
const formattedPath = formatPath(path);
typeByPath.set(formattedPath, "semanticTokens");
const fullPath = formattedPath + "." + paths.join(".");
if (valuePath.includes("value.value")) addError("tokens", `You used \`value\` twice resulting in an invalid token \`theme.tokens.${fullPath}\``);
const valueStr = serializeTokenValue(itemValue.value || itemValue);
if (isTokenReference(valueStr)) {
if (!refsByPath.has(formattedPath)) refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
const references = refsByPath.get(formattedPath);
if (!references) return;
getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
}
});
}, { stop: isValidToken });
tokenPaths.forEach((path) => {
const formattedPath = formatPath(path);
const value = valueAtPath.get(path);
if (path.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
if (!isObject(value) && !path.includes("value")) addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
});
}
validateTokenReferences({
valueAtPath,
refsByPath,
addError,
typeByPath
});
};
// src/validate-config.ts
var validateConfig = (config) => {
if (config.validation === "none") return;
const warnings = /* @__PURE__ */ new Set();
const addError = (scope, message) => {
warnings.add(`[${scope}] ` + message);
};
validateBreakpoints(config.theme?.breakpoints, addError);
validateConditions(config.conditions, addError);
const artifacts = {
recipes: /* @__PURE__ */ new Set(),
slotRecipes: /* @__PURE__ */ new Set(),
patterns: /* @__PURE__ */ new Set()
};
const tokens = {
tokenNames: /* @__PURE__ */ new Set(),
semanticTokenNames: /* @__PURE__ */ new Set(),
valueAtPath: /* @__PURE__ */ new Map(),
refsByPath: /* @__PURE__ */ new Map(),
typeByPath: /* @__PURE__ */ new Map()
};
if (config.theme) {
validateTokens({ config, tokens, addError });
validateRecipes({ config, tokens, artifacts, addError });
}
validatePatterns(config.patterns, artifacts);
validateArtifactNames(artifacts, addError);
if (warnings.size) {
const errors = `\u26A0\uFE0F Invalid config:
${Array.from(warnings).map((err) => "- " + err).join("\n")}
`;
if (config.validation === "error") {
throw new BambooError3("CONFIG_ERROR", errors);
}
logger2.warn("config", errors);
return warnings;
}
//#endregion
//#region src/validate-config.ts
/**
* Validate the config
* - Check for duplicate between token & semanticTokens names
* - Check for duplicate between recipes/patterns/slots names
* - Check for token / semanticTokens paths (must end/contain 'value')
* - Check for self/circular token references
* - Check for missing tokens references
* - Check for conditions selectors (must contain '&')
* - Check for breakpoints units (must be the same)
*/
const validateConfig = (config) => {
if (config.validation === "none") return;
const warnings = /* @__PURE__ */ new Set();
const addError = (scope, message) => {
warnings.add(`[${scope}] ` + message);
};
validateBreakpoints(config.theme?.breakpoints, addError);
validateConditions(config.conditions, addError);
const artifacts = {
recipes: /* @__PURE__ */ new Set(),
slotRecipes: /* @__PURE__ */ new Set(),
patterns: /* @__PURE__ */ new Set()
};
const tokens = {
tokenNames: /* @__PURE__ */ new Set(),
semanticTokenNames: /* @__PURE__ */ new Set(),
valueAtPath: /* @__PURE__ */ new Map(),
refsByPath: /* @__PURE__ */ new Map(),
typeByPath: /* @__PURE__ */ new Map()
};
if (config.theme) {
validateTokens({
config,
tokens,
addError
});
validateRecipes({
config,
tokens,
artifacts,
addError
});
}
validatePatterns(config.patterns, artifacts);
validateArtifactNames(artifacts, addError);
if (warnings.size) {
const errors = `⚠️ Invalid config:\n${Array.from(warnings).map((err) => "- " + err).join("\n")}\n`;
if (config.validation === "error") throw new BambooError("CONFIG_ERROR", errors);
logger.warn("config", errors);
return warnings;
}
};
// src/resolve-config.ts
var hookUtils2 = {
omit: omit2,
pick: pick2,
traverse: traverse2
//#endregion
//#region src/resolve-config.ts
const hookUtils = {
omit,
pick,
traverse
};
/**
* Resolve the final config (including presets)
* @bamboocss/preset-base: ALWAYS included if NOT using eject: true
* @bamboocss/preset-bamboo: only included by default if no presets
*/
async function resolveConfig(result, cwd) {
const presets = /* @__PURE__ */ new Set();
if (!result.config.eject) {
presets.add(presetBase);
}
if (result.config.presets) {
result.config.presets.forEach((preset) => {
presets.add(getBundledPreset(preset) ?? preset);
});
} else if (!result.config.eject) {
presets.add(presetBamboo);
}
result.config.presets = Array.from(presets);
const userConfig = result.config;
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) {
pluginHooks.push({ name: BAMBOO_CONFIG_NAME, hooks: userConfig.hooks });
}
const earlyHooks = mergeHooks(pluginHooks);
const mergedConfig = await getResolvedConfig(result.config, cwd, earlyHooks);
const hooks = mergedConfig.hooks ?? {};
if (mergedConfig.logLevel) {
logger3.level = mergedConfig.logLevel;
}
validateConfig(mergedConfig);
const loadConfigResult = {
...result,
config: mergedConfig
};
if (hooks["config:resolved"]) {
const result2 = await hooks["config:resolved"]({
config: loadConfigResult.config,
path: loadConfigResult.path,
dependencies: loadConfigResult.dependencies,
utils: hookUtils2
});
if (result2) {
loadConfigResult.config = result2;
}
}
const serialized = stringifyJson(
Object.assign({}, loadConfigResult.config, { name: BAMBOO_CONFIG_NAME, presets: [] })
);
const deserialize = () => parseJson(serialized);
return { ...loadConfigResult, serialized, deserialize, hooks };
const presets = /* @__PURE__ */ new Set();
if (!result.config.eject) presets.add(presetBase);
if (result.config.presets) result.config.presets.forEach((preset) => {
presets.add(getBundledPreset(preset) ?? preset);
});
else if (!result.config.eject) presets.add(presetBamboo);
result.config.presets = Array.from(presets);
const userConfig = result.config;
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) pluginHooks.push({
name: BAMBOO_CONFIG_NAME,
hooks: userConfig.hooks
});
const earlyHooks = mergeHooks(pluginHooks);
const mergedConfig = await getResolvedConfig(result.config, cwd, earlyHooks);
const hooks = mergedConfig.hooks ?? {};
if (mergedConfig.logLevel) logger.level = mergedConfig.logLevel;
validateConfig(mergedConfig);
const loadConfigResult = {
...result,
config: mergedConfig
};
if (hooks["config:resolved"]) {
const result = await hooks["config:resolved"]({
config: loadConfigResult.config,
path: loadConfigResult.path,
dependencies: loadConfigResult.dependencies,
utils: hookUtils
});
if (result) loadConfigResult.config = result;
}
const serialized = stringifyJson(Object.assign({}, loadConfigResult.config, {
name: BAMBOO_CONFIG_NAME,
presets: []
}));
const deserialize = () => parseJson(serialized);
return {
...loadConfigResult,
serialized,
deserialize,
hooks
};
}
// src/load-config.ts
//#endregion
//#region src/load-config.ts
/**
* Find, load and resolve the final config (including presets)
*/
async function loadConfig(options) {
const result = await bundleConfig(options);
return resolveConfig(result, options.cwd);
return resolveConfig(await bundleConfig(options), options.cwd);
}
export {
bundleConfig,
convertTsPathsToRegexes,
diffConfigs,
findConfig,
getConfigDependencies,
getResolvedConfig,
loadConfig,
mergeConfigs,
mergeHooks,
resolveConfig
};
//#endregion
export { bundleConfig, convertTsPathsToRegexes, diffConfigs, findConfig, getConfigDependencies, getResolvedConfig, loadConfig, mergeConfigs, mergeHooks, resolveConfig };

@@ -1,8 +0,2 @@

import {
mergeConfigs,
mergeHooks
} from "./chunk-RIBK22OM.mjs";
export {
mergeConfigs,
mergeHooks
};
import { s as mergeHooks, t as mergeConfigs } from "./merge-config-DI__LOWx.mjs";
export { mergeConfigs, mergeHooks };

@@ -1,6 +0,19 @@

import {
resolveTsPathPattern
} from "./chunk-RPIVZP2I.mjs";
export {
resolveTsPathPattern
import { posix, sep } from "path";
//#region src/resolve-ts-path-pattern.ts
/**
* @see https://github.com/aleclarson/vite-tsconfig-paths/blob/e8f0acf7adfcfbf77edbe937f64b4e5d39557ad0/src/index.ts#LL231C57-L231C57
*/
const resolveTsPathPattern = (pathMappings, moduleSpecifier) => {
for (const mapping of pathMappings) {
const match = moduleSpecifier.match(mapping.pattern);
if (!match) continue;
for (const pathTemplate of mapping.paths) {
let starCount = 0;
return pathTemplate.replace(/\*/g, () => {
return match[Math.min(++starCount, match.length - 1)];
}).split(sep).join(posix.sep);
}
}
};
//#endregion
export { resolveTsPathPattern };
{
"name": "@bamboocss/config",
"version": "1.11.1",
"version": "1.11.2",
"description": "Find and load bamboo config",

@@ -10,3 +10,3 @@ "homepage": "https://bamboo-css.com",

"type": "git",
"url": "git+https://github.com/chakra-ui/bamboo.git",
"url": "git+https://github.com/bamboocss/bamboo.git",
"directory": "packages/config"

@@ -18,10 +18,10 @@ },

"sideEffects": false,
"main": "dist/index.js",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"types": "dist/index.d.cts",
"exports": {
".": {
"source": "./src/index.ts",
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"types": "./dist/index.d.cts",
"require": "./dist/index.cjs",
"import": {

@@ -34,4 +34,4 @@ "types": "./dist/index.d.mts",

"source": "./src/merge-config.ts",
"types": "./dist/merge-config.d.ts",
"require": "./dist/merge-config.js",
"types": "./dist/merge-config.d.cts",
"require": "./dist/merge-config.cjs",
"import": {

@@ -44,4 +44,4 @@ "types": "./dist/merge-config.d.mts",

"source": "./src/diff-config.ts",
"types": "./dist/diff-config.d.ts",
"require": "./dist/diff-config.js",
"types": "./dist/diff-config.d.cts",
"require": "./dist/diff-config.cjs",
"import": {

@@ -54,4 +54,4 @@ "types": "./dist/diff-config.d.mts",

"source": "./src/resolve-ts-path-pattern.ts",
"types": "./dist/resolve-ts-path-pattern.d.ts",
"require": "./dist/resolve-ts-path-pattern.js",
"types": "./dist/resolve-ts-path-pattern.d.cts",
"require": "./dist/resolve-ts-path-pattern.cjs",
"import": {

@@ -72,7 +72,7 @@ "types": "./dist/resolve-ts-path-pattern.d.mts",

"typescript": "6.0.2",
"@bamboocss/logger": "1.11.1",
"@bamboocss/preset-base": "1.11.1",
"@bamboocss/preset-bamboo": "1.11.1",
"@bamboocss/shared": "1.11.1",
"@bamboocss/types": "1.11.1"
"@bamboocss/logger": "1.11.2",
"@bamboocss/preset-base": "1.11.2",
"@bamboocss/preset-bamboo": "1.11.2",
"@bamboocss/shared": "1.11.2",
"@bamboocss/types": "1.11.2"
},

@@ -83,6 +83,6 @@ "devDependencies": {

"scripts": {
"build": "tsup --tsconfig tsconfig.build.json --dts",
"build-fast": "tsup --no-dts",
"build": "tsdown --tsconfig tsconfig.build.json --dts",
"build-fast": "tsdown --dts=false",
"dev": "pnpm build-fast --watch"
}
}
// src/diff-config.ts
import { dashCase } from "@bamboocss/shared";
import microdiff from "microdiff";
// src/create-matcher.ts
function createMatcher(id, patterns) {
if (!patterns?.length) return () => void 0;
const includePatterns = [];
const excludePatterns = [];
const deduped = new Set(patterns);
deduped.forEach((pattern) => {
const regexString = pattern.replace(/\*/g, ".*");
if (pattern.startsWith("!")) {
excludePatterns.push(regexString.slice(1));
} else {
includePatterns.push(regexString);
}
});
const include = new RegExp(includePatterns.join("|"));
const exclude = new RegExp(excludePatterns.join("|"));
return (path) => {
if (excludePatterns.length && exclude.test(path)) return;
return include.test(path) ? id : void 0;
};
}
// src/config-deps.ts
var all = [
"clean",
"cwd",
"eject",
"outdir",
"forceConsistentTypeExtension",
"outExtension",
"emitTokensOnly",
"presets",
"plugins",
"hooks"
];
var format = [
"syntax",
"hash",
"prefix",
"separator",
"strictTokens",
"strictPropertyValues",
"shorthands"
];
var tokens = [
"utilities",
"conditions",
"theme.tokens",
"theme.semanticTokens",
"theme.breakpoints",
"theme.containerNames",
"theme.containerSizes"
];
var jsx = ["jsxFramework", "jsxFactory", "jsxStyleProps", "syntax"];
var common = tokens.concat(jsx, format);
var artifactConfigDeps = {
helpers: ["syntax", "jsxFramework"],
keyframes: ["theme.keyframes", "layers"],
"design-tokens": ["layers", "!utilities.*.className"].concat(tokens),
types: ["!utilities.*.className"].concat(common),
"css-fn": common,
cva: ["syntax"],
sva: ["syntax"],
cx: [],
"create-recipe": ["separator", "prefix", "hash"],
"recipes-index": ["theme.recipes", "theme.slotRecipes"],
recipes: ["theme.recipes", "theme.slotRecipes"],
"patterns-index": ["syntax", "patterns"],
patterns: ["syntax", "patterns"],
"jsx-is-valid-prop": common,
"jsx-factory": jsx,
"jsx-helpers": jsx,
"jsx-patterns": jsx.concat("patterns"),
"jsx-patterns-index": jsx.concat("patterns"),
"jsx-create-style-context": jsx,
"css-index": ["syntax"],
"package.json": ["forceConsistentTypeExtension", "outExtension"],
"types-styles": ["shorthands"],
"types-conditions": ["conditions"],
"types-jsx": jsx,
"types-entry": [],
"types-gen": [],
"types-gen-system": [],
themes: ["themes"].concat(tokens),
// staticCss depends on tokens (for wildcards) and recipes (for recipe rules)
"static-css": ["staticCss", "patterns", "theme.recipes", "theme.slotRecipes"].concat(tokens),
// Split CSS artifacts (generated via cssgen --splitting)
styles: [],
"styles.css": []
};
var artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
if (!paths.length) return () => void 0;
return createMatcher(key, paths.concat(all));
});
// src/diff-config.ts
var runIfFn = (fn) => typeof fn === "function" ? fn() : fn;
var hasRecipeStateTransition = (prevConfig, nextConfig) => {
const prevRecipes = prevConfig.theme?.recipes ?? {};
const prevSlotRecipes = prevConfig.theme?.slotRecipes ?? {};
const prevHasRecipes = Object.keys(prevRecipes).length > 0 || Object.keys(prevSlotRecipes).length > 0;
const nextRecipes = nextConfig.theme?.recipes ?? {};
const nextSlotRecipes = nextConfig.theme?.slotRecipes ?? {};
const nextHasRecipes = Object.keys(nextRecipes).length > 0 || Object.keys(nextSlotRecipes).length > 0;
return prevHasRecipes !== nextHasRecipes;
};
function diffConfigs(config, prevConfig) {
const affected = {
artifacts: /* @__PURE__ */ new Set(),
hasConfigChanged: false,
diffs: []
};
if (!prevConfig) {
affected.hasConfigChanged = true;
return affected;
}
const configDiff = microdiff(prevConfig, runIfFn(config));
if (!configDiff.length) {
return affected;
}
affected.hasConfigChanged = true;
affected.diffs = configDiff;
configDiff.forEach((change) => {
const changePath = change.path.join(".");
artifactMatchers.forEach((matcher) => {
const id = matcher(changePath);
if (!id) return;
if (id === "recipes") {
const name = dashCase(change.path.slice(1, 3).join("."));
affected.artifacts.add(name);
}
if (id === "patterns") {
const name = dashCase(change.path.slice(0, 2).join("."));
affected.artifacts.add(name);
}
affected.artifacts.add(id);
});
});
if (affected.artifacts.has("recipes") || affected.artifacts.has("recipes-index")) {
const nextConfig = runIfFn(config);
if (hasRecipeStateTransition(prevConfig, nextConfig)) {
affected.artifacts.add("create-recipe");
}
}
return affected;
}
export {
diffConfigs
};
// src/merge-config.ts
import { BAMBOO_CONFIG_NAME, assign, mergeWith, mergeAndConcat, walkObject } from "@bamboocss/shared";
// src/merge-hooks.ts
import { logger } from "@bamboocss/logger";
var mergeHooks = (plugins) => {
const hooksFns = {};
plugins.forEach(({ name, hooks }) => {
Object.entries(hooks ?? {}).forEach(([key, value]) => {
if (!hooksFns[key]) {
hooksFns[key] = [];
}
hooksFns[key].push([name, value]);
});
});
const mergedHooks = Object.fromEntries(
Object.entries(hooksFns).map(([key, entries]) => {
const fns = entries.map(([name, fn]) => tryCatch(name, fn));
const reducer = key in reducers ? reducers[key] : void 0;
if (reducer) {
return [key, reducer(fns)];
}
return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
})
);
return mergedHooks;
};
var createReducer = (reducer) => reducer;
var reducers = {
"config:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.config;
let config = args.config;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { config, original }));
if (result !== void 0) {
config = result;
}
}
return config;
}),
"parser:before": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { content, original }));
if (result !== void 0) {
content = result;
}
}
return content;
}),
"parser:preprocess": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.data;
let data = args.data;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { data, original }));
if (result !== void 0) {
data = result;
}
}
return data;
}),
"cssgen:done": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { content, original }));
if (result !== void 0) {
content = result;
}
}
return content;
}),
"codegen:prepare": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.artifacts;
let artifacts = args.artifacts;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { artifacts, original }));
if (result) {
artifacts = result;
}
}
return artifacts;
}),
"preset:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.preset;
let preset = args.preset;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { preset, original }));
if (result !== void 0) {
preset = result;
}
}
return preset;
}),
"css:optimize": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.css;
let css = args.css;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { css, original }));
if (result !== void 0) {
css = result;
}
}
return css;
})
};
var syncHooks = [
"context:created",
"parser:before",
"parser:preprocess",
"parser:after",
"cssgen:done",
"css:optimize"
];
var callAllAsync = (...fns) => async (...a) => {
for (const fn of fns) {
await fn?.(...a);
}
};
var callAll = (...fns) => (...a) => {
fns.forEach((fn) => fn?.(...a));
};
var tryCatch = (name, fn) => {
return (...args) => {
try {
return fn(...args);
} catch (e) {
logger.caughtError("hooks", `Error in plugin "${name}"`, e);
}
};
};
// src/validation/utils.ts
import { isObject, isString } from "@bamboocss/shared";
var REFERENCE_REGEX = /({([^}]*)})/g;
var curlyBracketRegex = /[{}]/g;
var isValidToken = (token) => isObject(token) && Object.hasOwnProperty.call(token, "value");
var isTokenReference = (value) => typeof value === "string" && REFERENCE_REGEX.test(value);
var formatPath = (path) => path;
var SEP = ".";
function getReferences(value) {
if (typeof value !== "string") return [];
const matches = value.match(REFERENCE_REGEX);
if (!matches) return [];
return matches.map((match) => match.replace(curlyBracketRegex, "")).map((value2) => {
return value2.trim().split("/")[0];
});
}
var serializeTokenValue = (value) => {
if (isString(value)) {
return value;
}
if (isObject(value)) {
return Object.values(value).map((v) => serializeTokenValue(v)).join(" ");
}
if (Array.isArray(value)) {
return value.map((v) => serializeTokenValue(v)).join(" ");
}
return value.toString();
};
// src/merge-config.ts
function getExtends(items) {
return items.reduce((merged, { extend }) => {
if (!extend) return merged;
return mergeWith(merged, extend, (originalValue, newValue) => {
if (newValue === void 0) {
return originalValue ?? [];
}
if (originalValue === void 0) {
return [newValue];
}
if (Array.isArray(originalValue)) {
return [newValue, ...originalValue];
}
return [newValue, originalValue];
});
}, {});
}
function mergeRecords(records) {
return {
...records.reduce((acc, record) => assign(acc, record), {}),
extend: getExtends(records)
};
}
function mergeExtensions(records) {
const { extend = [], ...restProps } = mergeRecords(records);
return mergeWith(restProps, extend, (obj, extensions) => {
return mergeAndConcat({}, obj, ...extensions);
});
}
var isEmptyObject = (obj) => typeof obj === "object" && Object.keys(obj).length === 0;
var compact = (obj) => {
return Object.keys(obj).reduce((acc, key) => {
if (obj[key] !== void 0 && !isEmptyObject(obj[key])) {
acc[key] = obj[key];
}
return acc;
}, {});
};
var tokenKeys = ["description", "extensions", "type", "value", "deprecated"];
function mergeConfigs(configs) {
const userConfig = configs.at(-1);
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) {
pluginHooks.push({ name: BAMBOO_CONFIG_NAME, hooks: userConfig.hooks });
}
const reversed = Array.from(configs).reverse();
const mergedResult = assign(
{
conditions: mergeExtensions(reversed.map((config) => config.conditions ?? {})),
theme: mergeExtensions(reversed.map((config) => config.theme ?? {})),
patterns: mergeExtensions(reversed.map((config) => config.patterns ?? {})),
utilities: mergeExtensions(reversed.map((config) => config.utilities ?? {})),
globalCss: mergeExtensions(reversed.map((config) => config.globalCss ?? {})),
globalVars: mergeExtensions(reversed.map((config) => config.globalVars ?? {})),
globalFontface: mergeExtensions(reversed.map((config) => config.globalFontface ?? {})),
globalPositionTry: mergeExtensions(reversed.map((config) => config.globalPositionTry ?? {})),
staticCss: mergeExtensions(reversed.map((config) => config.staticCss ?? {})),
themes: mergeExtensions(reversed.map((config) => config.themes ?? {})),
hooks: mergeHooks(pluginHooks)
},
...reversed
);
const withoutEmpty = compact(mergedResult);
if (withoutEmpty.theme?.tokens) {
walkObject(withoutEmpty.theme.tokens, (args) => args, {
stop(token) {
if (!isValidToken(token)) return false;
const keys = Object.keys(token);
const nestedKeys = keys.filter((k) => !tokenKeys.includes(k));
const nested = nestedKeys.length > 0;
if (nested) {
token.DEFAULT ||= {};
tokenKeys.forEach((key) => {
if (token[key] == null) return;
token.DEFAULT[key] ||= token[key];
delete token[key];
});
}
return true;
}
});
}
return withoutEmpty;
}
export {
mergeHooks,
isValidToken,
isTokenReference,
formatPath,
SEP,
getReferences,
serializeTokenValue,
mergeConfigs
};
// src/resolve-ts-path-pattern.ts
import { posix, sep } from "path";
var resolveTsPathPattern = (pathMappings, moduleSpecifier) => {
for (const mapping of pathMappings) {
const match = moduleSpecifier.match(mapping.pattern);
if (!match) {
continue;
}
for (const pathTemplate of mapping.paths) {
let starCount = 0;
const mappedId = pathTemplate.replace(/\*/g, () => {
const matchIndex = Math.min(++starCount, match.length - 1);
return match[matchIndex];
});
return mappedId.split(sep).join(posix.sep);
}
}
};
export {
resolveTsPathPattern
};
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all2) => {
for (var name in all2)
__defProp(target, name, { get: all2[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/diff-config.ts
var diff_config_exports = {};
__export(diff_config_exports, {
diffConfigs: () => diffConfigs
});
module.exports = __toCommonJS(diff_config_exports);
var import_shared = require("@bamboocss/shared");
var import_microdiff = __toESM(require("microdiff"));
// src/create-matcher.ts
function createMatcher(id, patterns) {
if (!patterns?.length) return () => void 0;
const includePatterns = [];
const excludePatterns = [];
const deduped = new Set(patterns);
deduped.forEach((pattern) => {
const regexString = pattern.replace(/\*/g, ".*");
if (pattern.startsWith("!")) {
excludePatterns.push(regexString.slice(1));
} else {
includePatterns.push(regexString);
}
});
const include = new RegExp(includePatterns.join("|"));
const exclude = new RegExp(excludePatterns.join("|"));
return (path) => {
if (excludePatterns.length && exclude.test(path)) return;
return include.test(path) ? id : void 0;
};
}
// src/config-deps.ts
var all = [
"clean",
"cwd",
"eject",
"outdir",
"forceConsistentTypeExtension",
"outExtension",
"emitTokensOnly",
"presets",
"plugins",
"hooks"
];
var format = [
"syntax",
"hash",
"prefix",
"separator",
"strictTokens",
"strictPropertyValues",
"shorthands"
];
var tokens = [
"utilities",
"conditions",
"theme.tokens",
"theme.semanticTokens",
"theme.breakpoints",
"theme.containerNames",
"theme.containerSizes"
];
var jsx = ["jsxFramework", "jsxFactory", "jsxStyleProps", "syntax"];
var common = tokens.concat(jsx, format);
var artifactConfigDeps = {
helpers: ["syntax", "jsxFramework"],
keyframes: ["theme.keyframes", "layers"],
"design-tokens": ["layers", "!utilities.*.className"].concat(tokens),
types: ["!utilities.*.className"].concat(common),
"css-fn": common,
cva: ["syntax"],
sva: ["syntax"],
cx: [],
"create-recipe": ["separator", "prefix", "hash"],
"recipes-index": ["theme.recipes", "theme.slotRecipes"],
recipes: ["theme.recipes", "theme.slotRecipes"],
"patterns-index": ["syntax", "patterns"],
patterns: ["syntax", "patterns"],
"jsx-is-valid-prop": common,
"jsx-factory": jsx,
"jsx-helpers": jsx,
"jsx-patterns": jsx.concat("patterns"),
"jsx-patterns-index": jsx.concat("patterns"),
"jsx-create-style-context": jsx,
"css-index": ["syntax"],
"package.json": ["forceConsistentTypeExtension", "outExtension"],
"types-styles": ["shorthands"],
"types-conditions": ["conditions"],
"types-jsx": jsx,
"types-entry": [],
"types-gen": [],
"types-gen-system": [],
themes: ["themes"].concat(tokens),
// staticCss depends on tokens (for wildcards) and recipes (for recipe rules)
"static-css": ["staticCss", "patterns", "theme.recipes", "theme.slotRecipes"].concat(tokens),
// Split CSS artifacts (generated via cssgen --splitting)
styles: [],
"styles.css": []
};
var artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
if (!paths.length) return () => void 0;
return createMatcher(key, paths.concat(all));
});
// src/diff-config.ts
var runIfFn = (fn) => typeof fn === "function" ? fn() : fn;
var hasRecipeStateTransition = (prevConfig, nextConfig) => {
const prevRecipes = prevConfig.theme?.recipes ?? {};
const prevSlotRecipes = prevConfig.theme?.slotRecipes ?? {};
const prevHasRecipes = Object.keys(prevRecipes).length > 0 || Object.keys(prevSlotRecipes).length > 0;
const nextRecipes = nextConfig.theme?.recipes ?? {};
const nextSlotRecipes = nextConfig.theme?.slotRecipes ?? {};
const nextHasRecipes = Object.keys(nextRecipes).length > 0 || Object.keys(nextSlotRecipes).length > 0;
return prevHasRecipes !== nextHasRecipes;
};
function diffConfigs(config, prevConfig) {
const affected = {
artifacts: /* @__PURE__ */ new Set(),
hasConfigChanged: false,
diffs: []
};
if (!prevConfig) {
affected.hasConfigChanged = true;
return affected;
}
const configDiff = (0, import_microdiff.default)(prevConfig, runIfFn(config));
if (!configDiff.length) {
return affected;
}
affected.hasConfigChanged = true;
affected.diffs = configDiff;
configDiff.forEach((change) => {
const changePath = change.path.join(".");
artifactMatchers.forEach((matcher) => {
const id = matcher(changePath);
if (!id) return;
if (id === "recipes") {
const name = (0, import_shared.dashCase)(change.path.slice(1, 3).join("."));
affected.artifacts.add(name);
}
if (id === "patterns") {
const name = (0, import_shared.dashCase)(change.path.slice(0, 2).join("."));
affected.artifacts.add(name);
}
affected.artifacts.add(id);
});
});
if (affected.artifacts.has("recipes") || affected.artifacts.has("recipes-index")) {
const nextConfig = runIfFn(config);
if (hasRecipeStateTransition(prevConfig, nextConfig)) {
affected.artifacts.add("create-recipe");
}
}
return affected;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
diffConfigs
});
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all2) => {
for (var name in all2)
__defProp(target, name, { get: all2[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
bundleConfig: () => bundleConfig,
convertTsPathsToRegexes: () => convertTsPathsToRegexes,
diffConfigs: () => diffConfigs,
findConfig: () => findConfig,
getConfigDependencies: () => getConfigDependencies,
getResolvedConfig: () => getResolvedConfig,
loadConfig: () => loadConfig,
mergeConfigs: () => mergeConfigs,
mergeHooks: () => mergeHooks,
resolveConfig: () => resolveConfig
});
module.exports = __toCommonJS(index_exports);
// src/bundle-config.ts
var import_logger = require("@bamboocss/logger");
var import_shared2 = require("@bamboocss/shared");
var import_bundle_n_require = require("bundle-n-require");
// src/find-config.ts
var import_shared = require("@bamboocss/shared");
var import_sync = __toESM(require("escalade/sync"));
var import_path = require("path");
// src/is-bamboo-config.ts
var configName = "bamboo";
var bambooConfigFiles = /* @__PURE__ */ new Set([
`${configName}.config.ts`,
`${configName}.config.js`,
`${configName}.config.mts`,
`${configName}.config.mjs`,
`${configName}.config.cts`,
`${configName}.config.cjs`
]);
var isBambooConfig = (file) => bambooConfigFiles.has(file);
// src/find-config.ts
function findConfig(options) {
const { cwd = process.cwd(), file } = options;
if (file) {
return (0, import_path.resolve)(cwd, file);
}
const configPath = (0, import_sync.default)(cwd, (_dir, paths) => paths.find(isBambooConfig));
if (!configPath) {
throw new import_shared.BambooError(
"CONFIG_NOT_FOUND",
`Cannot find config file \`bamboo.config.{ts,js,mjs,mts}\`. Did you forget to run \`bamboo init\`?`
);
}
return configPath;
}
// src/bundle-config.ts
async function bundle(filepath, cwd) {
const { mod, dependencies } = await (0, import_bundle_n_require.bundleNRequire)(filepath, {
cwd,
interopDefault: true
});
const config = mod?.default ?? mod;
return {
config,
dependencies
};
}
async function bundleConfig(options) {
const { cwd, file } = options;
const filePath = findConfig({ cwd, file });
import_logger.logger.debug("config:path", filePath);
const result = await bundle(filePath, cwd);
if (typeof result.config !== "object") {
throw new import_shared2.BambooError("CONFIG_ERROR", `\u{1F4A5} Config must export or return an object.`);
}
result.config.outdir ??= "styled-system";
result.config.validation ??= "warn";
return {
...result,
config: result.config,
path: filePath
};
}
// src/diff-config.ts
var import_shared3 = require("@bamboocss/shared");
var import_microdiff = __toESM(require("microdiff"));
// src/create-matcher.ts
function createMatcher(id, patterns) {
if (!patterns?.length) return () => void 0;
const includePatterns = [];
const excludePatterns = [];
const deduped = new Set(patterns);
deduped.forEach((pattern) => {
const regexString = pattern.replace(/\*/g, ".*");
if (pattern.startsWith("!")) {
excludePatterns.push(regexString.slice(1));
} else {
includePatterns.push(regexString);
}
});
const include = new RegExp(includePatterns.join("|"));
const exclude = new RegExp(excludePatterns.join("|"));
return (path2) => {
if (excludePatterns.length && exclude.test(path2)) return;
return include.test(path2) ? id : void 0;
};
}
// src/config-deps.ts
var all = [
"clean",
"cwd",
"eject",
"outdir",
"forceConsistentTypeExtension",
"outExtension",
"emitTokensOnly",
"presets",
"plugins",
"hooks"
];
var format = [
"syntax",
"hash",
"prefix",
"separator",
"strictTokens",
"strictPropertyValues",
"shorthands"
];
var tokens = [
"utilities",
"conditions",
"theme.tokens",
"theme.semanticTokens",
"theme.breakpoints",
"theme.containerNames",
"theme.containerSizes"
];
var jsx = ["jsxFramework", "jsxFactory", "jsxStyleProps", "syntax"];
var common = tokens.concat(jsx, format);
var artifactConfigDeps = {
helpers: ["syntax", "jsxFramework"],
keyframes: ["theme.keyframes", "layers"],
"design-tokens": ["layers", "!utilities.*.className"].concat(tokens),
types: ["!utilities.*.className"].concat(common),
"css-fn": common,
cva: ["syntax"],
sva: ["syntax"],
cx: [],
"create-recipe": ["separator", "prefix", "hash"],
"recipes-index": ["theme.recipes", "theme.slotRecipes"],
recipes: ["theme.recipes", "theme.slotRecipes"],
"patterns-index": ["syntax", "patterns"],
patterns: ["syntax", "patterns"],
"jsx-is-valid-prop": common,
"jsx-factory": jsx,
"jsx-helpers": jsx,
"jsx-patterns": jsx.concat("patterns"),
"jsx-patterns-index": jsx.concat("patterns"),
"jsx-create-style-context": jsx,
"css-index": ["syntax"],
"package.json": ["forceConsistentTypeExtension", "outExtension"],
"types-styles": ["shorthands"],
"types-conditions": ["conditions"],
"types-jsx": jsx,
"types-entry": [],
"types-gen": [],
"types-gen-system": [],
themes: ["themes"].concat(tokens),
// staticCss depends on tokens (for wildcards) and recipes (for recipe rules)
"static-css": ["staticCss", "patterns", "theme.recipes", "theme.slotRecipes"].concat(tokens),
// Split CSS artifacts (generated via cssgen --splitting)
styles: [],
"styles.css": []
};
var artifactMatchers = Object.entries(artifactConfigDeps).map(([key, paths]) => {
if (!paths.length) return () => void 0;
return createMatcher(key, paths.concat(all));
});
// src/diff-config.ts
var runIfFn = (fn) => typeof fn === "function" ? fn() : fn;
var hasRecipeStateTransition = (prevConfig, nextConfig) => {
const prevRecipes = prevConfig.theme?.recipes ?? {};
const prevSlotRecipes = prevConfig.theme?.slotRecipes ?? {};
const prevHasRecipes = Object.keys(prevRecipes).length > 0 || Object.keys(prevSlotRecipes).length > 0;
const nextRecipes = nextConfig.theme?.recipes ?? {};
const nextSlotRecipes = nextConfig.theme?.slotRecipes ?? {};
const nextHasRecipes = Object.keys(nextRecipes).length > 0 || Object.keys(nextSlotRecipes).length > 0;
return prevHasRecipes !== nextHasRecipes;
};
function diffConfigs(config, prevConfig) {
const affected = {
artifacts: /* @__PURE__ */ new Set(),
hasConfigChanged: false,
diffs: []
};
if (!prevConfig) {
affected.hasConfigChanged = true;
return affected;
}
const configDiff = (0, import_microdiff.default)(prevConfig, runIfFn(config));
if (!configDiff.length) {
return affected;
}
affected.hasConfigChanged = true;
affected.diffs = configDiff;
configDiff.forEach((change) => {
const changePath = change.path.join(".");
artifactMatchers.forEach((matcher) => {
const id = matcher(changePath);
if (!id) return;
if (id === "recipes") {
const name = (0, import_shared3.dashCase)(change.path.slice(1, 3).join("."));
affected.artifacts.add(name);
}
if (id === "patterns") {
const name = (0, import_shared3.dashCase)(change.path.slice(0, 2).join("."));
affected.artifacts.add(name);
}
affected.artifacts.add(id);
});
});
if (affected.artifacts.has("recipes") || affected.artifacts.has("recipes-index")) {
const nextConfig = runIfFn(config);
if (hasRecipeStateTransition(prevConfig, nextConfig)) {
affected.artifacts.add("create-recipe");
}
}
return affected;
}
// src/get-mod-deps.ts
var import_fs = __toESM(require("fs"));
var import_path4 = __toESM(require("path"));
var import_typescript = __toESM(require("typescript"));
// src/resolve-ts-path-pattern.ts
var import_path2 = require("path");
var resolveTsPathPattern = (pathMappings, moduleSpecifier) => {
for (const mapping of pathMappings) {
const match = moduleSpecifier.match(mapping.pattern);
if (!match) {
continue;
}
for (const pathTemplate of mapping.paths) {
let starCount = 0;
const mappedId = pathTemplate.replace(/\*/g, () => {
const matchIndex = Math.min(++starCount, match.length - 1);
return match[matchIndex];
});
return mappedId.split(import_path2.sep).join(import_path2.posix.sep);
}
}
};
// src/ts-config-paths.ts
var import_path3 = require("path");
function convertTsPathsToRegexes(paths, baseUrl) {
const sortedPatterns = Object.keys(paths).sort((a, b) => getPrefixLength(b) - getPrefixLength(a));
const resolved = [];
for (let pattern of sortedPatterns) {
const relativePaths = paths[pattern];
pattern = escapeStringRegexp(pattern).replace(/\*/g, "(.+)");
resolved.push({
pattern: new RegExp("^" + pattern + "$"),
paths: relativePaths.map((relativePath) => (0, import_path3.resolve)(baseUrl, relativePath))
});
}
return resolved;
}
function getPrefixLength(pattern) {
const prefixLength = pattern.indexOf("*");
return pattern.substr(0, prefixLength).length;
}
function escapeStringRegexp(string) {
return string.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/-/g, "\\x2d");
}
// src/get-mod-deps.ts
var jsExtensions = [".js", ".cjs", ".mjs"];
var jsResolutionOrder = ["", ".js", ".cjs", ".mjs", ".ts", ".cts", ".mts", ".jsx", ".tsx"];
var tsResolutionOrder = ["", ".ts", ".cts", ".mts", ".tsx", ".js", ".cjs", ".mjs", ".jsx"];
function resolveWithExtension(file, extensions) {
for (const ext of extensions) {
const full = `${file}${ext}`;
if (import_fs.default.existsSync(full) && import_fs.default.statSync(full).isFile()) {
return full;
}
}
for (const ext of extensions) {
const full = `${file}/index${ext}`;
if (import_fs.default.existsSync(full)) {
return full;
}
}
return null;
}
var importRegex = /import[\s\S]*?['"](.{3,}?)['"]/gi;
var importFromRegex = /import[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
var requireRegex = /require\(['"`](.+)['"`]\)/gi;
var exportRegex = /export[\s\S]*from[\s\S]*?['"](.{3,}?)['"]/gi;
function getDeps(opts, fromAlias) {
const { filename, seen } = opts;
const { moduleResolution: _, ...compilerOptions } = opts.compilerOptions ?? {};
const absoluteFile = resolveWithExtension(
import_path4.default.resolve(opts.cwd, filename),
jsExtensions.includes(opts.ext) ? jsResolutionOrder : tsResolutionOrder
);
if (absoluteFile === null) return;
if (fromAlias) {
opts.foundModuleAliases.set(fromAlias, absoluteFile);
}
if (seen.size > 1 && seen.has(absoluteFile)) return;
seen.add(absoluteFile);
const contents = import_fs.default.readFileSync(absoluteFile, "utf-8");
const fileDeps = [
...contents.matchAll(importRegex),
...contents.matchAll(importFromRegex),
...contents.matchAll(requireRegex),
...contents.matchAll(exportRegex)
];
if (!fileDeps.length) return;
const nextOpts = {
// Resolve new base for new imports/requires
cwd: import_path4.default.dirname(absoluteFile),
ext: import_path4.default.extname(absoluteFile),
seen,
baseUrl: opts.baseUrl,
pathMappings: opts.pathMappings,
foundModuleAliases: opts.foundModuleAliases
};
fileDeps.forEach((match) => {
const mod = match[1];
if (mod[0] === ".") {
getDeps(Object.assign({}, nextOpts, { filename: mod }));
return;
}
try {
const found = import_typescript.default.resolveModuleName(mod, absoluteFile, compilerOptions, import_typescript.default.sys).resolvedModule;
if (found && found.extension.endsWith("ts")) {
getDeps(Object.assign({}, nextOpts, { filename: found.resolvedFileName }));
return;
}
if (!opts.pathMappings) return;
const filename2 = resolveTsPathPattern(opts.pathMappings, mod);
if (!filename2) return;
getDeps(Object.assign({}, nextOpts, { filename: filename2 }), mod);
} catch {
}
});
}
function getConfigDependencies(filePath, tsOptions = { pathMappings: [] }, compilerOptions) {
if (filePath === null) return { deps: /* @__PURE__ */ new Set(), aliases: /* @__PURE__ */ new Map() };
const foundModuleAliases = /* @__PURE__ */ new Map();
const deps = /* @__PURE__ */ new Set();
deps.add(filePath);
getDeps({
filename: filePath,
ext: import_path4.default.extname(filePath),
cwd: import_path4.default.dirname(filePath),
seen: deps,
baseUrl: tsOptions.baseUrl,
pathMappings: tsOptions.pathMappings ?? [],
foundModuleAliases,
compilerOptions
});
return { deps, aliases: foundModuleAliases };
}
// src/get-resolved-config.ts
var import_shared6 = require("@bamboocss/shared");
// src/merge-config.ts
var import_shared5 = require("@bamboocss/shared");
// src/merge-hooks.ts
var import_logger2 = require("@bamboocss/logger");
var mergeHooks = (plugins) => {
const hooksFns = {};
plugins.forEach(({ name, hooks }) => {
Object.entries(hooks ?? {}).forEach(([key, value]) => {
if (!hooksFns[key]) {
hooksFns[key] = [];
}
hooksFns[key].push([name, value]);
});
});
const mergedHooks = Object.fromEntries(
Object.entries(hooksFns).map(([key, entries]) => {
const fns = entries.map(([name, fn]) => tryCatch(name, fn));
const reducer = key in reducers ? reducers[key] : void 0;
if (reducer) {
return [key, reducer(fns)];
}
return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
})
);
return mergedHooks;
};
var createReducer = (reducer) => reducer;
var reducers = {
"config:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.config;
let config = args.config;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { config, original }));
if (result !== void 0) {
config = result;
}
}
return config;
}),
"parser:before": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { content, original }));
if (result !== void 0) {
content = result;
}
}
return content;
}),
"parser:preprocess": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.data;
let data = args.data;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { data, original }));
if (result !== void 0) {
data = result;
}
}
return data;
}),
"cssgen:done": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { content, original }));
if (result !== void 0) {
content = result;
}
}
return content;
}),
"codegen:prepare": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.artifacts;
let artifacts = args.artifacts;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { artifacts, original }));
if (result) {
artifacts = result;
}
}
return artifacts;
}),
"preset:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.preset;
let preset = args.preset;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { preset, original }));
if (result !== void 0) {
preset = result;
}
}
return preset;
}),
"css:optimize": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.css;
let css = args.css;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { css, original }));
if (result !== void 0) {
css = result;
}
}
return css;
})
};
var syncHooks = [
"context:created",
"parser:before",
"parser:preprocess",
"parser:after",
"cssgen:done",
"css:optimize"
];
var callAllAsync = (...fns) => async (...a) => {
for (const fn of fns) {
await fn?.(...a);
}
};
var callAll = (...fns) => (...a) => {
fns.forEach((fn) => fn?.(...a));
};
var tryCatch = (name, fn) => {
return (...args) => {
try {
return fn(...args);
} catch (e) {
import_logger2.logger.caughtError("hooks", `Error in plugin "${name}"`, e);
}
};
};
// src/validation/utils.ts
var import_shared4 = require("@bamboocss/shared");
var REFERENCE_REGEX = /({([^}]*)})/g;
var curlyBracketRegex = /[{}]/g;
var isValidToken = (token) => (0, import_shared4.isObject)(token) && Object.hasOwnProperty.call(token, "value");
var isTokenReference = (value) => typeof value === "string" && REFERENCE_REGEX.test(value);
var formatPath = (path2) => path2;
var SEP = ".";
function getReferences(value) {
if (typeof value !== "string") return [];
const matches = value.match(REFERENCE_REGEX);
if (!matches) return [];
return matches.map((match) => match.replace(curlyBracketRegex, "")).map((value2) => {
return value2.trim().split("/")[0];
});
}
var serializeTokenValue = (value) => {
if ((0, import_shared4.isString)(value)) {
return value;
}
if ((0, import_shared4.isObject)(value)) {
return Object.values(value).map((v) => serializeTokenValue(v)).join(" ");
}
if (Array.isArray(value)) {
return value.map((v) => serializeTokenValue(v)).join(" ");
}
return value.toString();
};
// src/merge-config.ts
function getExtends(items) {
return items.reduce((merged, { extend }) => {
if (!extend) return merged;
return (0, import_shared5.mergeWith)(merged, extend, (originalValue, newValue) => {
if (newValue === void 0) {
return originalValue ?? [];
}
if (originalValue === void 0) {
return [newValue];
}
if (Array.isArray(originalValue)) {
return [newValue, ...originalValue];
}
return [newValue, originalValue];
});
}, {});
}
function mergeRecords(records) {
return {
...records.reduce((acc, record) => (0, import_shared5.assign)(acc, record), {}),
extend: getExtends(records)
};
}
function mergeExtensions(records) {
const { extend = [], ...restProps } = mergeRecords(records);
return (0, import_shared5.mergeWith)(restProps, extend, (obj, extensions) => {
return (0, import_shared5.mergeAndConcat)({}, obj, ...extensions);
});
}
var isEmptyObject = (obj) => typeof obj === "object" && Object.keys(obj).length === 0;
var compact = (obj) => {
return Object.keys(obj).reduce((acc, key) => {
if (obj[key] !== void 0 && !isEmptyObject(obj[key])) {
acc[key] = obj[key];
}
return acc;
}, {});
};
var tokenKeys = ["description", "extensions", "type", "value", "deprecated"];
function mergeConfigs(configs) {
const userConfig = configs.at(-1);
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) {
pluginHooks.push({ name: import_shared5.BAMBOO_CONFIG_NAME, hooks: userConfig.hooks });
}
const reversed = Array.from(configs).reverse();
const mergedResult = (0, import_shared5.assign)(
{
conditions: mergeExtensions(reversed.map((config) => config.conditions ?? {})),
theme: mergeExtensions(reversed.map((config) => config.theme ?? {})),
patterns: mergeExtensions(reversed.map((config) => config.patterns ?? {})),
utilities: mergeExtensions(reversed.map((config) => config.utilities ?? {})),
globalCss: mergeExtensions(reversed.map((config) => config.globalCss ?? {})),
globalVars: mergeExtensions(reversed.map((config) => config.globalVars ?? {})),
globalFontface: mergeExtensions(reversed.map((config) => config.globalFontface ?? {})),
globalPositionTry: mergeExtensions(reversed.map((config) => config.globalPositionTry ?? {})),
staticCss: mergeExtensions(reversed.map((config) => config.staticCss ?? {})),
themes: mergeExtensions(reversed.map((config) => config.themes ?? {})),
hooks: mergeHooks(pluginHooks)
},
...reversed
);
const withoutEmpty = compact(mergedResult);
if (withoutEmpty.theme?.tokens) {
(0, import_shared5.walkObject)(withoutEmpty.theme.tokens, (args) => args, {
stop(token) {
if (!isValidToken(token)) return false;
const keys = Object.keys(token);
const nestedKeys = keys.filter((k) => !tokenKeys.includes(k));
const nested = nestedKeys.length > 0;
if (nested) {
token.DEFAULT ||= {};
tokenKeys.forEach((key) => {
if (token[key] == null) return;
token.DEFAULT[key] ||= token[key];
delete token[key];
});
}
return true;
}
});
}
return withoutEmpty;
}
// src/get-resolved-config.ts
var hookUtils = {
omit: import_shared6.omit,
pick: import_shared6.pick,
traverse: import_shared6.traverse
};
async function getResolvedConfig(config, cwd, hooks) {
const stack = [config];
const configs = [];
while (stack.length > 0) {
const current = stack.pop();
const subPresets = current.presets ?? [];
for (const subPreset of subPresets) {
let presetConfig;
let presetName;
if (typeof subPreset === "string") {
const presetModule = await bundle(subPreset, cwd);
presetConfig = presetModule.config;
presetName = subPreset;
} else {
presetConfig = await subPreset;
presetName = presetConfig.name || "unknown-preset";
}
if (hooks?.["preset:resolved"]) {
const resolvedPreset = await hooks["preset:resolved"]({
preset: presetConfig,
name: presetName,
utils: hookUtils
});
if (resolvedPreset !== void 0) {
presetConfig = resolvedPreset;
}
}
stack.push(presetConfig);
}
configs.unshift(current);
}
const merged = mergeConfigs(configs);
merged.presets = configs.slice(0, -1);
return merged;
}
// src/resolve-config.ts
var import_logger4 = require("@bamboocss/logger");
var import_shared11 = require("@bamboocss/shared");
// src/bundled-preset.ts
var import_preset_base = require("@bamboocss/preset-base");
var import_preset_bamboo = require("@bamboocss/preset-bamboo");
var bundledPresets = {
"@bamboocss/preset-base": import_preset_base.preset,
"@bamboocss/preset-bamboo": import_preset_bamboo.preset,
"@bamboocss/dev/presets": import_preset_bamboo.preset
};
var bundledPresetsNames = Object.keys(bundledPresets);
var isBundledPreset = (preset) => bundledPresetsNames.includes(preset);
var getBundledPreset = (preset) => {
return typeof preset === "string" && isBundledPreset(preset) ? bundledPresets[preset] : void 0;
};
// src/validate-config.ts
var import_logger3 = require("@bamboocss/logger");
var import_shared10 = require("@bamboocss/shared");
// src/validation/validate-artifact.ts
var validateArtifactNames = (names, addError) => {
names.recipes.forEach((recipeName) => {
if (names.slotRecipes.has(recipeName)) {
addError("recipes", `This recipe name is already used in \`theme.slotRecipes\`: ${recipeName}`);
}
if (names.patterns.has(recipeName)) {
addError("recipes", `This recipe name is already used in \`patterns\`: \`${recipeName}\``);
}
});
names.slotRecipes.forEach((recipeName) => {
if (names.patterns.has(recipeName)) {
addError("recipes", `This recipe name is already used in \`patterns\`: ${recipeName}`);
}
});
};
// src/validation/validate-breakpoints.ts
var import_shared7 = require("@bamboocss/shared");
var validateBreakpoints = (breakpoints, addError) => {
if (!breakpoints) return;
const units = /* @__PURE__ */ new Set();
const values = Object.values(breakpoints);
for (const value of values) {
const unit = (0, import_shared7.getUnit)(value) ?? "px";
units.add(unit);
}
if (units.size > 1) {
addError("breakpoints", `All breakpoints must use the same unit: \`${values.join(", ")}\``);
}
};
// src/validation/validate-condition.ts
var import_shared8 = require("@bamboocss/shared");
var validateObjectCondition = (obj, addError) => {
let hasSlot = false;
for (const [key, value] of Object.entries(obj)) {
if (!key.startsWith("@") && !key.includes("&")) {
addError("conditions", `Selectors should contain the \`&\` character: \`${key}\``);
}
if (value === "@slot") {
hasSlot = true;
continue;
}
if (typeof value === "string") {
addError(
"conditions",
`Object condition leaves must be the literal string \`'@slot'\`, got \`${JSON.stringify(value)}\` at \`${key}\``
);
continue;
}
if (typeof value === "object" && value !== null) {
const nested = validateObjectCondition(value, addError);
if (nested.hasSlot) hasSlot = true;
}
}
return { hasSlot };
};
var validateConditions = (conditions, addError) => {
if (!conditions) return;
Object.values(conditions).forEach((condition) => {
if ((0, import_shared8.isString)(condition)) {
if (!condition.startsWith("@") && !condition.includes("&")) {
addError("conditions", `Selectors should contain the \`&\` character: \`${condition}\``);
}
return;
}
if (Array.isArray(condition)) {
condition.forEach((c) => {
if (!c.startsWith("@") && !c.includes("&")) {
addError("conditions", `Selectors should contain the \`&\` character: \`${c}\``);
}
});
return;
}
const { hasSlot } = validateObjectCondition(condition, addError);
if (!hasSlot) {
addError("conditions", `Object conditions must contain at least one \`'@slot'\` marker`);
}
});
};
// src/validation/validate-patterns.ts
var validatePatterns = (patterns, names) => {
if (!patterns) return;
Object.keys(patterns).forEach((patternName) => {
names.patterns.add(patternName);
});
};
// src/validation/validate-recipes.ts
var validateRecipes = (options) => {
const {
config: { theme },
artifacts
} = options;
if (!theme) return;
if (theme.recipes) {
Object.keys(theme.recipes).forEach((recipeName) => {
artifacts.recipes.add(recipeName);
});
}
if (theme.slotRecipes) {
Object.keys(theme.slotRecipes).forEach((recipeName) => {
artifacts.slotRecipes.add(recipeName);
});
}
return artifacts;
};
// src/validation/validate-tokens.ts
var import_shared9 = require("@bamboocss/shared");
// src/validation/validate-token-references.ts
var validateTokenReferences = (props) => {
const { valueAtPath, refsByPath, addError, typeByPath } = props;
refsByPath.forEach((refs, path2) => {
if (refs.has(path2)) {
addError("tokens", `Self token reference: \`${path2}\``);
}
const stack = [path2];
while (stack.length > 0) {
let currentPath = stack.pop();
if (currentPath.includes("/")) {
const [tokenPath] = currentPath.split("/");
currentPath = tokenPath;
}
const value = valueAtPath.get(currentPath);
if (!value) {
const configKey = typeByPath.get(path2);
addError("tokens", `Missing token: \`${currentPath}\` used in \`theme.${configKey}.${path2}\``);
}
if (isTokenReference(value) && !refsByPath.has(value)) {
addError("tokens", `Unknown token reference: \`${currentPath}\` used in \`${value}\``);
}
const deps = refsByPath.get(currentPath);
if (!deps) continue;
for (const transitiveDep of deps) {
if (path2 === transitiveDep) {
addError(
"tokens",
`Circular token reference: \`${transitiveDep}\` -> \`${currentPath}\` -> ... -> \`${path2}\``
);
break;
}
stack.push(transitiveDep);
}
}
});
};
// src/validation/validate-tokens.ts
var validateTokens = (options) => {
const {
config: { theme },
tokens: tokens2,
addError
} = options;
if (!theme) return;
const { tokenNames, semanticTokenNames, valueAtPath, refsByPath, typeByPath } = tokens2;
if (theme.tokens) {
const tokenPaths = /* @__PURE__ */ new Set();
(0, import_shared9.walkObject)(
theme.tokens,
(value, paths) => {
const path2 = paths.join(SEP);
tokenNames.add(path2);
tokenPaths.add(path2);
valueAtPath.set(path2, value);
if (path2.includes("DEFAULT")) {
valueAtPath.set(path2.replace(SEP + "DEFAULT", ""), value);
}
},
{
stop: isValidToken
}
);
tokenPaths.forEach((path2) => {
const itemValue = valueAtPath.get(path2);
const formattedPath = formatPath(path2);
typeByPath.set(formattedPath, "tokens");
if (!isValidToken(itemValue)) {
addError("tokens", `Token must contain 'value': \`theme.tokens.${formattedPath}\``);
return;
}
if (path2.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
const valueStr = serializeTokenValue(itemValue.value || itemValue);
if (isTokenReference(valueStr)) {
refsByPath.set(formattedPath, /* @__PURE__ */ new Set([]));
}
const references = refsByPath.get(formattedPath);
if (!references) return;
getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
});
}
if (theme.semanticTokens) {
const tokenPaths = /* @__PURE__ */ new Set();
(0, import_shared9.walkObject)(
theme.semanticTokens,
(value, paths) => {
const path2 = paths.join(SEP);
semanticTokenNames.add(path2);
valueAtPath.set(path2, value);
tokenPaths.add(path2);
if (path2.includes("DEFAULT")) {
valueAtPath.set(path2.replace(SEP + "DEFAULT", ""), value);
}
if (!isValidToken(value)) return;
(0, import_shared9.walkObject)(value, (itemValue, paths2) => {
const valuePath = paths2.join(SEP);
const formattedPath = formatPath(path2);
typeByPath.set(formattedPath, "semanticTokens");
const fullPath = formattedPath + "." + paths2.join(SEP);
if (valuePath.includes("value" + SEP + "value")) {
addError("tokens", `You used \`value\` twice resulting in an invalid token \`theme.tokens.${fullPath}\``);
}
const valueStr = serializeTokenValue(itemValue.value || itemValue);
if (isTokenReference(valueStr)) {
if (!refsByPath.has(formattedPath)) {
refsByPath.set(formattedPath, /* @__PURE__ */ new Set());
}
const references = refsByPath.get(formattedPath);
if (!references) return;
getReferences(valueStr).forEach((reference) => {
references.add(reference);
});
}
});
},
{
stop: isValidToken
}
);
tokenPaths.forEach((path2) => {
const formattedPath = formatPath(path2);
const value = valueAtPath.get(path2);
if (path2.includes(" ")) {
addError("tokens", `Token key must not contain spaces: \`theme.tokens.${formattedPath}\``);
return;
}
if (!(0, import_shared9.isObject)(value) && !path2.includes("value")) {
addError("tokens", `Token must contain 'value': \`theme.semanticTokens.${formattedPath}\``);
}
});
}
validateTokenReferences({ valueAtPath, refsByPath, addError, typeByPath });
};
// src/validate-config.ts
var validateConfig = (config) => {
if (config.validation === "none") return;
const warnings = /* @__PURE__ */ new Set();
const addError = (scope, message) => {
warnings.add(`[${scope}] ` + message);
};
validateBreakpoints(config.theme?.breakpoints, addError);
validateConditions(config.conditions, addError);
const artifacts = {
recipes: /* @__PURE__ */ new Set(),
slotRecipes: /* @__PURE__ */ new Set(),
patterns: /* @__PURE__ */ new Set()
};
const tokens2 = {
tokenNames: /* @__PURE__ */ new Set(),
semanticTokenNames: /* @__PURE__ */ new Set(),
valueAtPath: /* @__PURE__ */ new Map(),
refsByPath: /* @__PURE__ */ new Map(),
typeByPath: /* @__PURE__ */ new Map()
};
if (config.theme) {
validateTokens({ config, tokens: tokens2, addError });
validateRecipes({ config, tokens: tokens2, artifacts, addError });
}
validatePatterns(config.patterns, artifacts);
validateArtifactNames(artifacts, addError);
if (warnings.size) {
const errors = `\u26A0\uFE0F Invalid config:
${Array.from(warnings).map((err) => "- " + err).join("\n")}
`;
if (config.validation === "error") {
throw new import_shared10.BambooError("CONFIG_ERROR", errors);
}
import_logger3.logger.warn("config", errors);
return warnings;
}
};
// src/resolve-config.ts
var hookUtils2 = {
omit: import_shared11.omit,
pick: import_shared11.pick,
traverse: import_shared11.traverse
};
async function resolveConfig(result, cwd) {
const presets = /* @__PURE__ */ new Set();
if (!result.config.eject) {
presets.add(import_preset_base.preset);
}
if (result.config.presets) {
result.config.presets.forEach((preset) => {
presets.add(getBundledPreset(preset) ?? preset);
});
} else if (!result.config.eject) {
presets.add(import_preset_bamboo.preset);
}
result.config.presets = Array.from(presets);
const userConfig = result.config;
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) {
pluginHooks.push({ name: import_shared11.BAMBOO_CONFIG_NAME, hooks: userConfig.hooks });
}
const earlyHooks = mergeHooks(pluginHooks);
const mergedConfig = await getResolvedConfig(result.config, cwd, earlyHooks);
const hooks = mergedConfig.hooks ?? {};
if (mergedConfig.logLevel) {
import_logger4.logger.level = mergedConfig.logLevel;
}
validateConfig(mergedConfig);
const loadConfigResult = {
...result,
config: mergedConfig
};
if (hooks["config:resolved"]) {
const result2 = await hooks["config:resolved"]({
config: loadConfigResult.config,
path: loadConfigResult.path,
dependencies: loadConfigResult.dependencies,
utils: hookUtils2
});
if (result2) {
loadConfigResult.config = result2;
}
}
const serialized = (0, import_shared11.stringifyJson)(
Object.assign({}, loadConfigResult.config, { name: import_shared11.BAMBOO_CONFIG_NAME, presets: [] })
);
const deserialize = () => (0, import_shared11.parseJson)(serialized);
return { ...loadConfigResult, serialized, deserialize, hooks };
}
// src/load-config.ts
async function loadConfig(options) {
const result = await bundleConfig(options);
return resolveConfig(result, options.cwd);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
bundleConfig,
convertTsPathsToRegexes,
diffConfigs,
findConfig,
getConfigDependencies,
getResolvedConfig,
loadConfig,
mergeConfigs,
mergeHooks,
resolveConfig
});
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/merge-config.ts
var merge_config_exports = {};
__export(merge_config_exports, {
mergeConfigs: () => mergeConfigs,
mergeHooks: () => mergeHooks
});
module.exports = __toCommonJS(merge_config_exports);
var import_shared2 = require("@bamboocss/shared");
// src/merge-hooks.ts
var import_logger = require("@bamboocss/logger");
var mergeHooks = (plugins) => {
const hooksFns = {};
plugins.forEach(({ name, hooks }) => {
Object.entries(hooks ?? {}).forEach(([key, value]) => {
if (!hooksFns[key]) {
hooksFns[key] = [];
}
hooksFns[key].push([name, value]);
});
});
const mergedHooks = Object.fromEntries(
Object.entries(hooksFns).map(([key, entries]) => {
const fns = entries.map(([name, fn]) => tryCatch(name, fn));
const reducer = key in reducers ? reducers[key] : void 0;
if (reducer) {
return [key, reducer(fns)];
}
return [key, syncHooks.includes(key) ? callAll(...fns) : callAllAsync(...fns)];
})
);
return mergedHooks;
};
var createReducer = (reducer) => reducer;
var reducers = {
"config:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.config;
let config = args.config;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { config, original }));
if (result !== void 0) {
config = result;
}
}
return config;
}),
"parser:before": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { content, original }));
if (result !== void 0) {
content = result;
}
}
return content;
}),
"parser:preprocess": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.data;
let data = args.data;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { data, original }));
if (result !== void 0) {
data = result;
}
}
return data;
}),
"cssgen:done": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.content;
let content = args.content;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { content, original }));
if (result !== void 0) {
content = result;
}
}
return content;
}),
"codegen:prepare": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.artifacts;
let artifacts = args.artifacts;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { artifacts, original }));
if (result) {
artifacts = result;
}
}
return artifacts;
}),
"preset:resolved": createReducer((fns) => async (_args) => {
const args = Object.assign({}, _args);
const original = _args.preset;
let preset = args.preset;
for (const hookFn of fns) {
const result = await hookFn(Object.assign(args, { preset, original }));
if (result !== void 0) {
preset = result;
}
}
return preset;
}),
"css:optimize": createReducer((fns) => (_args) => {
const args = Object.assign({}, _args);
const original = _args.css;
let css = args.css;
for (const hookFn of fns) {
const result = hookFn(Object.assign(args, { css, original }));
if (result !== void 0) {
css = result;
}
}
return css;
})
};
var syncHooks = [
"context:created",
"parser:before",
"parser:preprocess",
"parser:after",
"cssgen:done",
"css:optimize"
];
var callAllAsync = (...fns) => async (...a) => {
for (const fn of fns) {
await fn?.(...a);
}
};
var callAll = (...fns) => (...a) => {
fns.forEach((fn) => fn?.(...a));
};
var tryCatch = (name, fn) => {
return (...args) => {
try {
return fn(...args);
} catch (e) {
import_logger.logger.caughtError("hooks", `Error in plugin "${name}"`, e);
}
};
};
// src/validation/utils.ts
var import_shared = require("@bamboocss/shared");
var isValidToken = (token) => (0, import_shared.isObject)(token) && Object.hasOwnProperty.call(token, "value");
// src/merge-config.ts
function getExtends(items) {
return items.reduce((merged, { extend }) => {
if (!extend) return merged;
return (0, import_shared2.mergeWith)(merged, extend, (originalValue, newValue) => {
if (newValue === void 0) {
return originalValue ?? [];
}
if (originalValue === void 0) {
return [newValue];
}
if (Array.isArray(originalValue)) {
return [newValue, ...originalValue];
}
return [newValue, originalValue];
});
}, {});
}
function mergeRecords(records) {
return {
...records.reduce((acc, record) => (0, import_shared2.assign)(acc, record), {}),
extend: getExtends(records)
};
}
function mergeExtensions(records) {
const { extend = [], ...restProps } = mergeRecords(records);
return (0, import_shared2.mergeWith)(restProps, extend, (obj, extensions) => {
return (0, import_shared2.mergeAndConcat)({}, obj, ...extensions);
});
}
var isEmptyObject = (obj) => typeof obj === "object" && Object.keys(obj).length === 0;
var compact = (obj) => {
return Object.keys(obj).reduce((acc, key) => {
if (obj[key] !== void 0 && !isEmptyObject(obj[key])) {
acc[key] = obj[key];
}
return acc;
}, {});
};
var tokenKeys = ["description", "extensions", "type", "value", "deprecated"];
function mergeConfigs(configs) {
const userConfig = configs.at(-1);
const pluginHooks = userConfig.plugins ?? [];
if (userConfig.hooks) {
pluginHooks.push({ name: import_shared2.BAMBOO_CONFIG_NAME, hooks: userConfig.hooks });
}
const reversed = Array.from(configs).reverse();
const mergedResult = (0, import_shared2.assign)(
{
conditions: mergeExtensions(reversed.map((config) => config.conditions ?? {})),
theme: mergeExtensions(reversed.map((config) => config.theme ?? {})),
patterns: mergeExtensions(reversed.map((config) => config.patterns ?? {})),
utilities: mergeExtensions(reversed.map((config) => config.utilities ?? {})),
globalCss: mergeExtensions(reversed.map((config) => config.globalCss ?? {})),
globalVars: mergeExtensions(reversed.map((config) => config.globalVars ?? {})),
globalFontface: mergeExtensions(reversed.map((config) => config.globalFontface ?? {})),
globalPositionTry: mergeExtensions(reversed.map((config) => config.globalPositionTry ?? {})),
staticCss: mergeExtensions(reversed.map((config) => config.staticCss ?? {})),
themes: mergeExtensions(reversed.map((config) => config.themes ?? {})),
hooks: mergeHooks(pluginHooks)
},
...reversed
);
const withoutEmpty = compact(mergedResult);
if (withoutEmpty.theme?.tokens) {
(0, import_shared2.walkObject)(withoutEmpty.theme.tokens, (args) => args, {
stop(token) {
if (!isValidToken(token)) return false;
const keys = Object.keys(token);
const nestedKeys = keys.filter((k) => !tokenKeys.includes(k));
const nested = nestedKeys.length > 0;
if (nested) {
token.DEFAULT ||= {};
tokenKeys.forEach((key) => {
if (token[key] == null) return;
token.DEFAULT[key] ||= token[key];
delete token[key];
});
}
return true;
}
});
}
return withoutEmpty;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
mergeConfigs,
mergeHooks
});
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/resolve-ts-path-pattern.ts
var resolve_ts_path_pattern_exports = {};
__export(resolve_ts_path_pattern_exports, {
resolveTsPathPattern: () => resolveTsPathPattern
});
module.exports = __toCommonJS(resolve_ts_path_pattern_exports);
var import_path = require("path");
var resolveTsPathPattern = (pathMappings, moduleSpecifier) => {
for (const mapping of pathMappings) {
const match = moduleSpecifier.match(mapping.pattern);
if (!match) {
continue;
}
for (const pathTemplate of mapping.paths) {
let starCount = 0;
const mappedId = pathTemplate.replace(/\*/g, () => {
const matchIndex = Math.min(++starCount, match.length - 1);
return match[matchIndex];
});
return mappedId.split(import_path.sep).join(import_path.posix.sep);
}
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
resolveTsPathPattern
});