@almadar/ui 5.128.0 → 5.129.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +296 -5
- package/dist/avl/index.js +296 -5
- package/dist/components/index.cjs +299 -5
- package/dist/components/index.d.cts +119 -1
- package/dist/components/index.d.ts +119 -1
- package/dist/components/index.js +300 -6
- package/dist/providers/index.cjs +296 -5
- package/dist/providers/index.js +296 -5
- package/dist/runtime/index.cjs +296 -5
- package/dist/runtime/index.js +296 -5
- package/package.json +6 -5
- package/scripts/audit-tailwind-safelist.ts +237 -0
- package/scripts/export-static.js +61 -0
- package/scripts/generate-design-system.ts +344 -0
- package/scripts/generate-theme-from-schema.ts +480 -0
- package/scripts/generate.ts +835 -0
- package/scripts/strip-base-tokens-from-themes.mjs +179 -0
- package/scripts/suggest-components.ts +508 -0
- package/scripts/theme-contrast-audit.mjs +132 -0
- package/scripts/types.ts +282 -0
- package/themes/_contract.md +1 -1
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Design System Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates a complete client-specific design system from an .orb schema:
|
|
5
|
+
* 1. Generates custom theme CSS
|
|
6
|
+
* 2. Suggests domain-specific components
|
|
7
|
+
* 3. Generates component stubs
|
|
8
|
+
* 4. Builds Storybook documentation
|
|
9
|
+
* 5. Exports static design system package
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from "fs";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
import { execSync } from "child_process";
|
|
15
|
+
import { generateThemeFromSchema } from "./generate-theme-from-schema";
|
|
16
|
+
import {
|
|
17
|
+
suggestComponents,
|
|
18
|
+
generateComponentStubs,
|
|
19
|
+
} from "./suggest-components";
|
|
20
|
+
|
|
21
|
+
interface GeneratorOptions {
|
|
22
|
+
schemaPath: string;
|
|
23
|
+
outputDir: string;
|
|
24
|
+
clientName?: string;
|
|
25
|
+
buildStorybook?: boolean;
|
|
26
|
+
generateComponents?: boolean;
|
|
27
|
+
clientOnly?: boolean; // Only include client-specific components in Storybook
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface GeneratorResult {
|
|
31
|
+
themePath: string;
|
|
32
|
+
suggestedComponents: number;
|
|
33
|
+
generatedComponents: number;
|
|
34
|
+
storybookPath?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Main generator function
|
|
39
|
+
*/
|
|
40
|
+
export async function generateDesignSystem(
|
|
41
|
+
options: GeneratorOptions,
|
|
42
|
+
): Promise<GeneratorResult> {
|
|
43
|
+
const {
|
|
44
|
+
schemaPath,
|
|
45
|
+
outputDir,
|
|
46
|
+
clientName,
|
|
47
|
+
buildStorybook = true,
|
|
48
|
+
generateComponents = true,
|
|
49
|
+
clientOnly = false,
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
console.log("🎨 Orbital Design System Generator");
|
|
53
|
+
console.log("==================================\n");
|
|
54
|
+
|
|
55
|
+
// Validate schema exists
|
|
56
|
+
if (!fs.existsSync(schemaPath)) {
|
|
57
|
+
throw new Error(`Schema file not found: ${schemaPath}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Read schema to get name
|
|
61
|
+
const schemaContent = fs.readFileSync(schemaPath, "utf-8");
|
|
62
|
+
const schema = JSON.parse(schemaContent);
|
|
63
|
+
const name =
|
|
64
|
+
clientName || schema.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
65
|
+
|
|
66
|
+
console.log(`📋 Processing schema: ${schema.name}`);
|
|
67
|
+
console.log(`📁 Output directory: ${outputDir}\n`);
|
|
68
|
+
|
|
69
|
+
// Create output directory
|
|
70
|
+
const clientOutputDir = path.join(outputDir, name);
|
|
71
|
+
if (!fs.existsSync(clientOutputDir)) {
|
|
72
|
+
fs.mkdirSync(clientOutputDir, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Step 1: Generate theme
|
|
76
|
+
console.log("1️⃣ Generating theme...");
|
|
77
|
+
const themePath = path.join(clientOutputDir, `${name}-theme.css`);
|
|
78
|
+
generateThemeFromSchema(schemaPath, themePath);
|
|
79
|
+
console.log(` ✅ Theme generated: ${themePath}\n`);
|
|
80
|
+
|
|
81
|
+
// Step 2: Suggest components
|
|
82
|
+
console.log("2️⃣ Analyzing schema for component suggestions...");
|
|
83
|
+
const { domains, suggestions } = suggestComponents(schemaPath);
|
|
84
|
+
console.log(` 📦 Detected domains: ${domains.join(", ") || "none"}`);
|
|
85
|
+
console.log(` 💡 Suggested components: ${suggestions.length}\n`);
|
|
86
|
+
|
|
87
|
+
// Step 3: Generate component stubs
|
|
88
|
+
let generatedCount = 0;
|
|
89
|
+
if (generateComponents && suggestions.length > 0) {
|
|
90
|
+
console.log("3️⃣ Generating component stubs...");
|
|
91
|
+
const componentsDir = path.join(clientOutputDir, "components");
|
|
92
|
+
generateComponentStubs(suggestions, componentsDir);
|
|
93
|
+
generatedCount = suggestions.length;
|
|
94
|
+
console.log(` ✅ Generated ${generatedCount} component stubs\n`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Step 4: Copy theme to design system themes folder for Storybook
|
|
98
|
+
const designSystemThemesDir = path.join(__dirname, "..", "themes");
|
|
99
|
+
const targetThemePath = path.join(designSystemThemesDir, `${name}.css`);
|
|
100
|
+
fs.copyFileSync(themePath, targetThemePath);
|
|
101
|
+
console.log(`4️⃣ Theme copied to design system: ${targetThemePath}\n`);
|
|
102
|
+
|
|
103
|
+
// Update index.css to include the new theme
|
|
104
|
+
const indexCssPath = path.join(__dirname, "..", "index.css");
|
|
105
|
+
const indexCss = fs.readFileSync(indexCssPath, "utf-8");
|
|
106
|
+
const importStatement = `@import './themes/${name}.css';`;
|
|
107
|
+
|
|
108
|
+
if (!indexCss.includes(importStatement)) {
|
|
109
|
+
const updatedCss = indexCss.replace(
|
|
110
|
+
"@tailwind base;",
|
|
111
|
+
`${importStatement}\n\n@tailwind base;`,
|
|
112
|
+
);
|
|
113
|
+
fs.writeFileSync(indexCssPath, updatedCss);
|
|
114
|
+
console.log(` Updated index.css with theme import\n`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Step 5: Update Storybook preview to include the new theme
|
|
118
|
+
const previewPath = path.join(__dirname, "..", ".storybook", "preview.tsx");
|
|
119
|
+
const previewContent = fs.readFileSync(previewPath, "utf-8");
|
|
120
|
+
|
|
121
|
+
if (!previewContent.includes(`'${name}':`)) {
|
|
122
|
+
const updatedPreview = previewContent.replace(
|
|
123
|
+
"defaultTheme: 'minimalist',",
|
|
124
|
+
`'${name}': '${name}',\n },\n defaultTheme: '${name}',`,
|
|
125
|
+
);
|
|
126
|
+
fs.writeFileSync(previewPath, updatedPreview);
|
|
127
|
+
console.log(` Updated Storybook preview with theme: ${name}\n`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Step 6: Build Storybook (optional)
|
|
131
|
+
let storybookPath: string | undefined;
|
|
132
|
+
if (buildStorybook) {
|
|
133
|
+
console.log("5️⃣ Building Storybook...");
|
|
134
|
+
const designSystemDir = path.join(__dirname, "..");
|
|
135
|
+
|
|
136
|
+
// Determine output path for storybook
|
|
137
|
+
// For client-only, output directly to the output directory (e.g., projects/winning-11/design-system/storybook-static)
|
|
138
|
+
// For full library, output to clientOutputDir/storybook
|
|
139
|
+
storybookPath = clientOnly
|
|
140
|
+
? path.join(outputDir, "storybook-static")
|
|
141
|
+
: path.join(clientOutputDir, "storybook");
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// Install dependencies if needed
|
|
145
|
+
if (!fs.existsSync(path.join(designSystemDir, "node_modules"))) {
|
|
146
|
+
console.log(" Installing dependencies...");
|
|
147
|
+
execSync("npm install", { cwd: designSystemDir, stdio: "inherit" });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (clientOnly) {
|
|
151
|
+
// Use the dedicated .storybook-client config and output directly to project
|
|
152
|
+
console.log(` 📦 Building client-only Storybook for: ${name}`);
|
|
153
|
+
console.log(` 📁 Output: ${storybookPath}`);
|
|
154
|
+
|
|
155
|
+
// Build using client-only config with custom output path
|
|
156
|
+
execSync(
|
|
157
|
+
`npx storybook build -c .storybook-client -o "${storybookPath}"`,
|
|
158
|
+
{
|
|
159
|
+
cwd: designSystemDir,
|
|
160
|
+
stdio: "inherit",
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
// Build full storybook
|
|
165
|
+
execSync("npm run build-storybook", {
|
|
166
|
+
cwd: designSystemDir,
|
|
167
|
+
stdio: "inherit",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Copy to output
|
|
171
|
+
const storybookStaticDir = path.join(
|
|
172
|
+
designSystemDir,
|
|
173
|
+
"storybook-static",
|
|
174
|
+
);
|
|
175
|
+
if (fs.existsSync(storybookStaticDir)) {
|
|
176
|
+
fs.cpSync(storybookStaticDir, storybookPath, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(` ✅ Storybook built: ${storybookPath}\n`);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.log(
|
|
183
|
+
" ⚠️ Storybook build skipped (run manually with npm run build-storybook)\n",
|
|
184
|
+
);
|
|
185
|
+
storybookPath = undefined;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Step 7: Generate client portal and deployment files
|
|
190
|
+
console.log("6️⃣ Generating client portal and deployment files...");
|
|
191
|
+
const templatesDir = path.join(__dirname, "..", "..", "templates");
|
|
192
|
+
const projectRoot = path.join(outputDir, "..");
|
|
193
|
+
|
|
194
|
+
// Template variables
|
|
195
|
+
const projectDisplayName =
|
|
196
|
+
schema.name ||
|
|
197
|
+
name.replace(/-/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase());
|
|
198
|
+
const projectId = name.toLowerCase().replace(/[^a-z0-9-]/g, "");
|
|
199
|
+
const generatedDate = new Date().toISOString().split("T")[0];
|
|
200
|
+
const version = schema.version || "1.0.0";
|
|
201
|
+
|
|
202
|
+
const replaceTemplateVars = (content: string): string => {
|
|
203
|
+
return content
|
|
204
|
+
.replace(/\{\{PROJECT_NAME\}\}/g, projectDisplayName)
|
|
205
|
+
.replace(/\{\{PROJECT_ID\}\}/g, projectId)
|
|
206
|
+
.replace(/\{\{GENERATED_DATE\}\}/g, generatedDate)
|
|
207
|
+
.replace(/\{\{VERSION\}\}/g, version);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Files to copy and process
|
|
211
|
+
const templateFiles = [
|
|
212
|
+
{ src: "client-portal.html", dest: "index.html" },
|
|
213
|
+
{ src: "firebase.json", dest: "firebase.json" },
|
|
214
|
+
{ src: ".firebaserc", dest: ".firebaserc" },
|
|
215
|
+
{ src: "deploy.sh", dest: "deploy.sh", executable: true },
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
for (const file of templateFiles) {
|
|
219
|
+
const srcPath = path.join(templatesDir, file.src);
|
|
220
|
+
const destPath = path.join(projectRoot, file.dest);
|
|
221
|
+
|
|
222
|
+
if (fs.existsSync(srcPath)) {
|
|
223
|
+
let content = fs.readFileSync(srcPath, "utf-8");
|
|
224
|
+
content = replaceTemplateVars(content);
|
|
225
|
+
fs.writeFileSync(destPath, content);
|
|
226
|
+
|
|
227
|
+
if (file.executable) {
|
|
228
|
+
fs.chmodSync(destPath, "755");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log(` ✅ Generated: ${file.dest}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
console.log("");
|
|
235
|
+
|
|
236
|
+
// Step 8: Generate manifest
|
|
237
|
+
const manifest = {
|
|
238
|
+
name: schema.name,
|
|
239
|
+
clientName: name,
|
|
240
|
+
generatedAt: new Date().toISOString(),
|
|
241
|
+
domains,
|
|
242
|
+
theme: {
|
|
243
|
+
path: `${name}-theme.css`,
|
|
244
|
+
variables: extractCssVariables(themePath),
|
|
245
|
+
},
|
|
246
|
+
components: {
|
|
247
|
+
suggested: suggestions.map((s) => ({
|
|
248
|
+
name: s.name,
|
|
249
|
+
category: s.category,
|
|
250
|
+
priority: s.priority,
|
|
251
|
+
})),
|
|
252
|
+
generated: generatedCount,
|
|
253
|
+
},
|
|
254
|
+
storybook: storybookPath ? "storybook/index.html" : null,
|
|
255
|
+
clientPortal: "../index.html",
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const manifestPath = path.join(clientOutputDir, "manifest.json");
|
|
259
|
+
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
260
|
+
console.log(`📄 Manifest generated: ${manifestPath}\n`);
|
|
261
|
+
|
|
262
|
+
// Summary
|
|
263
|
+
console.log("🎉 Design System Generation Complete!");
|
|
264
|
+
console.log("=====================================");
|
|
265
|
+
console.log(` Theme: ${themePath}`);
|
|
266
|
+
console.log(` Suggested Components: ${suggestions.length}`);
|
|
267
|
+
console.log(` Generated Components: ${generatedCount}`);
|
|
268
|
+
if (storybookPath) {
|
|
269
|
+
console.log(` Storybook: ${storybookPath}/index.html`);
|
|
270
|
+
}
|
|
271
|
+
console.log(` Manifest: ${manifestPath}`);
|
|
272
|
+
console.log("");
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
themePath,
|
|
276
|
+
suggestedComponents: suggestions.length,
|
|
277
|
+
generatedComponents: generatedCount,
|
|
278
|
+
storybookPath,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Extract CSS variable names from a theme file
|
|
284
|
+
*/
|
|
285
|
+
function extractCssVariables(themePath: string): string[] {
|
|
286
|
+
const content = fs.readFileSync(themePath, "utf-8");
|
|
287
|
+
const variablePattern = /--([a-z-]+):/g;
|
|
288
|
+
const variables: string[] = [];
|
|
289
|
+
let match;
|
|
290
|
+
|
|
291
|
+
while ((match = variablePattern.exec(content)) !== null) {
|
|
292
|
+
if (!variables.includes(`--${match[1]}`)) {
|
|
293
|
+
variables.push(`--${match[1]}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return variables;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// CLI usage
|
|
301
|
+
if (require.main === module) {
|
|
302
|
+
const args = process.argv.slice(2);
|
|
303
|
+
|
|
304
|
+
if (args.length < 1) {
|
|
305
|
+
console.log(`
|
|
306
|
+
Orbital Design System Generator
|
|
307
|
+
|
|
308
|
+
Usage:
|
|
309
|
+
npx tsx generate-design-system.ts <schema.orb> [options]
|
|
310
|
+
|
|
311
|
+
Options:
|
|
312
|
+
--output <dir> Output directory (default: ./exports)
|
|
313
|
+
--name <name> Client name override
|
|
314
|
+
--no-storybook Skip Storybook build
|
|
315
|
+
--no-components Skip component generation
|
|
316
|
+
--client-only Only include client-specific components in Storybook (not the full library)
|
|
317
|
+
|
|
318
|
+
Examples:
|
|
319
|
+
npx tsx generate-design-system.ts winning-11.orb
|
|
320
|
+
npx tsx generate-design-system.ts winning-11.orb --output ./clients
|
|
321
|
+
npx tsx generate-design-system.ts winning-11.orb --name garden-app --no-storybook
|
|
322
|
+
npx tsx generate-design-system.ts winning-11.orb --client-only
|
|
323
|
+
`);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const schemaPath = args[0];
|
|
328
|
+
const outputIndex = args.indexOf("--output");
|
|
329
|
+
const nameIndex = args.indexOf("--name");
|
|
330
|
+
|
|
331
|
+
const options: GeneratorOptions = {
|
|
332
|
+
schemaPath,
|
|
333
|
+
outputDir:
|
|
334
|
+
outputIndex !== -1
|
|
335
|
+
? args[outputIndex + 1]
|
|
336
|
+
: path.join(__dirname, "..", "exports"),
|
|
337
|
+
clientName: nameIndex !== -1 ? args[nameIndex + 1] : undefined,
|
|
338
|
+
buildStorybook: !args.includes("--no-storybook"),
|
|
339
|
+
generateComponents: !args.includes("--no-components"),
|
|
340
|
+
clientOnly: args.includes("--client-only"),
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
generateDesignSystem(options).catch(console.error);
|
|
344
|
+
}
|