@sudajs/cli 0.18.4 → 0.18.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1800,6 +1800,7 @@ declare function fetchJson<T>(url: string, options?: RequestInit & {
1800
1800
  token?: string;
1801
1801
  }): Promise<T>;
1802
1802
  declare function validateThemeTailwindContract(root: string): Promise<void>;
1803
+ declare function validateThemeCssTokenContract(root: string): Promise<void>;
1803
1804
  declare function validateTheme(root: string): Promise<ValidatedTheme>;
1804
1805
  declare function validateThemeLocales(root: string): Promise<void>;
1805
1806
  declare function validateThemePreviewLocales(root: string, previewLocales: string[] | undefined): Promise<void>;
@@ -1827,6 +1828,7 @@ declare const __testUtils: {
1827
1828
  slugifyThemeName: typeof slugifyThemeName;
1828
1829
  validateThemeKey: typeof validateThemeKey;
1829
1830
  validateThemeName: typeof validateThemeName;
1831
+ validateThemeCssTokenContract: typeof validateThemeCssTokenContract;
1830
1832
  validateThemeTailwindContract: typeof validateThemeTailwindContract;
1831
1833
  validateThemeLocales: typeof validateThemeLocales;
1832
1834
  validateThemePreviewLocales: typeof validateThemePreviewLocales;
package/dist/index.js CHANGED
@@ -1123,6 +1123,108 @@ async function validateThemeTailwindContract(root) {
1123
1123
  if (!styles.includes('@import "tailwindcss";') && !styles.includes("@import 'tailwindcss';")) {
1124
1124
  throw new Error('src/styles.css must include `@import "tailwindcss";`.');
1125
1125
  }
1126
+ await validateThemeCssTokenContract(root);
1127
+ }
1128
+ async function collectThemeSourceCssFiles(directory) {
1129
+ const entries = await readdir(directory, { withFileTypes: true });
1130
+ const files = [];
1131
+ for (const entry of entries) {
1132
+ const absolutePath = path2.join(directory, entry.name);
1133
+ if (entry.isDirectory()) {
1134
+ files.push(...await collectThemeSourceCssFiles(absolutePath));
1135
+ } else if (entry.isFile() && entry.name.endsWith(".css")) {
1136
+ files.push(absolutePath);
1137
+ }
1138
+ }
1139
+ return files;
1140
+ }
1141
+ function findThemeInlineBlocks(styles, filePath) {
1142
+ const blocks = [];
1143
+ const pattern = /@theme\s+inline\s*\{/g;
1144
+ for (const match of styles.matchAll(pattern)) {
1145
+ const start = match.index;
1146
+ let depth = 1;
1147
+ let cursor = start + match[0].length;
1148
+ while (cursor < styles.length && depth > 0) {
1149
+ if (styles[cursor] === "{") depth += 1;
1150
+ if (styles[cursor] === "}") depth -= 1;
1151
+ cursor += 1;
1152
+ }
1153
+ if (depth !== 0) {
1154
+ throw new Error(`${filePath} contains an unclosed \`@theme inline\` block.`);
1155
+ }
1156
+ blocks.push({
1157
+ body: styles.slice(start + match[0].length, cursor - 1),
1158
+ end: cursor,
1159
+ start
1160
+ });
1161
+ }
1162
+ return blocks;
1163
+ }
1164
+ async function validateThemeCssTokenContract(root) {
1165
+ const sourceRoot = path2.join(root, "src");
1166
+ const files = await collectThemeSourceCssFiles(sourceRoot);
1167
+ const sources = await Promise.all(
1168
+ files.map(async (filePath) => {
1169
+ const styles = await readFile(filePath, "utf8");
1170
+ return { blocks: findThemeInlineBlocks(styles, filePath), filePath, styles };
1171
+ })
1172
+ );
1173
+ const declarations = /* @__PURE__ */ new Map();
1174
+ for (const source of sources) {
1175
+ for (const block of source.blocks) {
1176
+ for (const match of block.body.matchAll(/(--[a-zA-Z0-9_-]+)\s*:\s*([^;]+);/g)) {
1177
+ const name = match[1];
1178
+ const value = match[2];
1179
+ if (name !== void 0 && value !== void 0) {
1180
+ declarations.set(name, value);
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ const sudaAliases = /* @__PURE__ */ new Set();
1186
+ let changed = true;
1187
+ while (changed) {
1188
+ changed = false;
1189
+ for (const [name, value] of declarations) {
1190
+ const dependsOnSuda = /var\(\s*--suda-/.test(value);
1191
+ const dependsOnAlias = [...sudaAliases].some(
1192
+ (alias) => new RegExp(`var\\(\\s*${escapeRegExp(alias)}(?:\\s*[,\\)])`).test(value)
1193
+ );
1194
+ if ((dependsOnSuda || dependsOnAlias) && !sudaAliases.has(name)) {
1195
+ sudaAliases.add(name);
1196
+ changed = true;
1197
+ }
1198
+ }
1199
+ }
1200
+ const issues = [];
1201
+ for (const source of sources) {
1202
+ const handwrittenStyles = source.styles.split("");
1203
+ for (const block of source.blocks) {
1204
+ for (let index = block.start; index < block.end; index += 1) {
1205
+ if (handwrittenStyles[index] !== "\n") {
1206
+ handwrittenStyles[index] = " ";
1207
+ }
1208
+ }
1209
+ }
1210
+ const searchable = handwrittenStyles.join("");
1211
+ for (const alias of sudaAliases) {
1212
+ const reference = new RegExp(`var\\(\\s*${escapeRegExp(alias)}(?:\\s*[,\\)])`, "g");
1213
+ for (const match of searchable.matchAll(reference)) {
1214
+ const line = searchable.slice(0, match.index ?? 0).split("\n").length;
1215
+ issues.push(`${path2.relative(root, source.filePath)}:${line} uses ${alias}`);
1216
+ }
1217
+ }
1218
+ }
1219
+ if (issues.length > 0) {
1220
+ throw new Error(
1221
+ [
1222
+ "Hand-written theme CSS must not consume Suda-backed Tailwind aliases from `@theme inline`.",
1223
+ ...issues.map((issue) => ` - ${issue}`),
1224
+ "Use `var(--suda-*)` directly, or define and consume a theme-prefixed variable on the theme root. Tailwind utilities such as `bg-primary` remain supported."
1225
+ ].join("\n")
1226
+ );
1227
+ }
1126
1228
  }
1127
1229
  async function validateTheme(root) {
1128
1230
  const packageJsonPath = path2.join(root, "package.json");
@@ -1657,6 +1759,7 @@ var __testUtils = {
1657
1759
  slugifyThemeName,
1658
1760
  validateThemeKey,
1659
1761
  validateThemeName,
1762
+ validateThemeCssTokenContract,
1660
1763
  validateThemeTailwindContract,
1661
1764
  validateThemeLocales,
1662
1765
  validateThemePreviewLocales,