@sudajs/cli 0.18.4 → 0.18.5
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 +2 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/templates/theme/AGENTS.md +22 -4
- package/templates/theme/package.json +2 -2
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,
|