rizzo-css 0.0.61 → 0.0.63
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/README.md +9 -5
- package/bin/rizzo-css.js +568 -79
- package/dist/rizzo.min.css +5 -3
- package/package.json +14 -7
- package/scaffold/astro/Footer.astro +8 -0
- package/scaffold/astro/Settings.astro +8 -2
- package/scaffold/astro/Tabs.astro +2 -2
- package/scaffold/react/Accordion.tsx +143 -0
- package/scaffold/react/Alert.tsx +90 -0
- package/scaffold/react/AlertDialog.tsx +80 -0
- package/scaffold/react/AspectRatio.tsx +32 -0
- package/scaffold/react/Avatar.tsx +53 -0
- package/scaffold/react/BackToTop.tsx +62 -0
- package/scaffold/react/Badge.tsx +39 -0
- package/scaffold/react/Breadcrumb.tsx +81 -0
- package/scaffold/react/Button.tsx +40 -0
- package/scaffold/react/ButtonGroup.tsx +24 -0
- package/scaffold/react/Card.tsx +26 -0
- package/scaffold/react/Checkbox.tsx +40 -0
- package/scaffold/react/Collapsible.tsx +58 -0
- package/scaffold/react/ContextMenu.tsx +67 -0
- package/scaffold/react/CopyToClipboard.tsx +128 -0
- package/scaffold/react/Dashboard.tsx +23 -0
- package/scaffold/react/Divider.tsx +47 -0
- package/scaffold/react/DocsSidebar.tsx +48 -0
- package/scaffold/react/Dropdown.tsx +256 -0
- package/scaffold/react/Empty.tsx +29 -0
- package/scaffold/react/FontSwitcher.tsx +68 -0
- package/scaffold/react/Footer.tsx +55 -0
- package/scaffold/react/FormGroup.tsx +57 -0
- package/scaffold/react/HoverCard.tsx +61 -0
- package/scaffold/react/Icons.tsx +22 -0
- package/scaffold/react/Input.tsx +69 -0
- package/scaffold/react/Kbd.tsx +16 -0
- package/scaffold/react/Label.tsx +16 -0
- package/scaffold/react/Modal.tsx +149 -0
- package/scaffold/react/Navbar.tsx +72 -0
- package/scaffold/react/Pagination.tsx +155 -0
- package/scaffold/react/Popover.tsx +66 -0
- package/scaffold/react/ProgressBar.tsx +66 -0
- package/scaffold/react/Radio.tsx +38 -0
- package/scaffold/react/ResizableHandle.tsx +24 -0
- package/scaffold/react/ResizablePane.tsx +29 -0
- package/scaffold/react/ResizablePaneGroup.tsx +29 -0
- package/scaffold/react/ScrollArea.tsx +29 -0
- package/scaffold/react/Search.tsx +62 -0
- package/scaffold/react/Select.tsx +65 -0
- package/scaffold/react/Separator.tsx +33 -0
- package/scaffold/react/Settings.tsx +60 -0
- package/scaffold/react/Sheet.tsx +86 -0
- package/scaffold/react/Skeleton.tsx +32 -0
- package/scaffold/react/Slider.tsx +66 -0
- package/scaffold/react/SoundEffects.tsx +15 -0
- package/scaffold/react/Spinner.tsx +36 -0
- package/scaffold/react/Switch.tsx +52 -0
- package/scaffold/react/Table.tsx +178 -0
- package/scaffold/react/Tabs.tsx +143 -0
- package/scaffold/react/Textarea.tsx +69 -0
- package/scaffold/react/ThemeSwitcher.tsx +89 -0
- package/scaffold/react/Toast.tsx +43 -0
- package/scaffold/react/Toggle.tsx +45 -0
- package/scaffold/react/ToggleGroup.tsx +34 -0
- package/scaffold/react/Tooltip.tsx +40 -0
- package/scaffold/vanilla/README-RIZZO.md +1 -1
- package/scaffold/vanilla/components/accordion.html +40 -0
- package/scaffold/vanilla/components/alert-dialog.html +40 -0
- package/scaffold/vanilla/components/alert.html +40 -0
- package/scaffold/vanilla/components/aspect-ratio.html +40 -0
- package/scaffold/vanilla/components/avatar.html +40 -0
- package/scaffold/vanilla/components/back-to-top.html +40 -0
- package/scaffold/vanilla/components/badge.html +40 -0
- package/scaffold/vanilla/components/breadcrumb.html +40 -0
- package/scaffold/vanilla/components/button-group.html +40 -0
- package/scaffold/vanilla/components/button.html +40 -0
- package/scaffold/vanilla/components/cards.html +40 -0
- package/scaffold/vanilla/components/collapsible.html +40 -0
- package/scaffold/vanilla/components/context-menu.html +40 -0
- package/scaffold/vanilla/components/copy-to-clipboard.html +40 -0
- package/scaffold/vanilla/components/dashboard.html +40 -0
- package/scaffold/vanilla/components/divider.html +40 -0
- package/scaffold/vanilla/components/docs-sidebar.html +40 -0
- package/scaffold/vanilla/components/dropdown.html +40 -0
- package/scaffold/vanilla/components/empty.html +40 -0
- package/scaffold/vanilla/components/font-switcher.html +40 -0
- package/scaffold/vanilla/components/footer.html +40 -0
- package/scaffold/vanilla/components/forms.html +40 -0
- package/scaffold/vanilla/components/hover-card.html +40 -0
- package/scaffold/vanilla/components/icons.html +40 -0
- package/scaffold/vanilla/components/index.html +40 -0
- package/scaffold/vanilla/components/kbd.html +40 -0
- package/scaffold/vanilla/components/label.html +40 -0
- package/scaffold/vanilla/components/modal.html +40 -0
- package/scaffold/vanilla/components/navbar.html +40 -0
- package/scaffold/vanilla/components/pagination.html +40 -0
- package/scaffold/vanilla/components/popover.html +40 -0
- package/scaffold/vanilla/components/progress-bar.html +40 -0
- package/scaffold/vanilla/components/resizable.html +40 -0
- package/scaffold/vanilla/components/scroll-area.html +40 -0
- package/scaffold/vanilla/components/search.html +40 -0
- package/scaffold/vanilla/components/separator.html +40 -0
- package/scaffold/vanilla/components/settings.html +40 -0
- package/scaffold/vanilla/components/sheet.html +40 -0
- package/scaffold/vanilla/components/skeleton.html +40 -0
- package/scaffold/vanilla/components/slider.html +40 -0
- package/scaffold/vanilla/components/sound-effects.html +40 -0
- package/scaffold/vanilla/components/spinner.html +40 -0
- package/scaffold/vanilla/components/switch.html +40 -0
- package/scaffold/vanilla/components/table.html +40 -0
- package/scaffold/vanilla/components/tabs.html +40 -0
- package/scaffold/vanilla/components/theme-switcher.html +40 -0
- package/scaffold/vanilla/components/toast.html +40 -0
- package/scaffold/vanilla/components/toggle-group.html +40 -0
- package/scaffold/vanilla/components/toggle.html +40 -0
- package/scaffold/vanilla/components/tooltip.html +40 -0
- package/scaffold/vanilla/index.html +40 -0
- package/scaffold/vue/Accordion.vue +9 -0
- package/scaffold/vue/Alert.vue +9 -0
- package/scaffold/vue/AlertDialog.vue +9 -0
- package/scaffold/vue/AspectRatio.vue +9 -0
- package/scaffold/vue/Avatar.vue +9 -0
- package/scaffold/vue/BackToTop.vue +9 -0
- package/scaffold/vue/Badge.vue +28 -0
- package/scaffold/vue/Breadcrumb.vue +9 -0
- package/scaffold/vue/Button.vue +23 -0
- package/scaffold/vue/ButtonGroup.vue +9 -0
- package/scaffold/vue/Card.vue +21 -0
- package/scaffold/vue/Checkbox.vue +31 -0
- package/scaffold/vue/Collapsible.vue +9 -0
- package/scaffold/vue/ContextMenu.vue +9 -0
- package/scaffold/vue/CopyToClipboard.vue +9 -0
- package/scaffold/vue/Dashboard.vue +9 -0
- package/scaffold/vue/Divider.vue +23 -0
- package/scaffold/vue/DocsSidebar.vue +9 -0
- package/scaffold/vue/Dropdown.vue +9 -0
- package/scaffold/vue/Empty.vue +9 -0
- package/scaffold/vue/FontSwitcher.vue +9 -0
- package/scaffold/vue/Footer.vue +9 -0
- package/scaffold/vue/FormGroup.vue +45 -0
- package/scaffold/vue/HoverCard.vue +9 -0
- package/scaffold/vue/Icons.vue +9 -0
- package/scaffold/vue/Input.vue +59 -0
- package/scaffold/vue/Kbd.vue +9 -0
- package/scaffold/vue/Label.vue +23 -0
- package/scaffold/vue/Modal.vue +9 -0
- package/scaffold/vue/Navbar.vue +9 -0
- package/scaffold/vue/Pagination.vue +9 -0
- package/scaffold/vue/Popover.vue +9 -0
- package/scaffold/vue/ProgressBar.vue +9 -0
- package/scaffold/vue/Radio.vue +29 -0
- package/scaffold/vue/ResizableHandle.vue +9 -0
- package/scaffold/vue/ResizablePane.vue +9 -0
- package/scaffold/vue/ResizablePaneGroup.vue +9 -0
- package/scaffold/vue/ScrollArea.vue +9 -0
- package/scaffold/vue/Search.vue +9 -0
- package/scaffold/vue/Select.vue +52 -0
- package/scaffold/vue/Separator.vue +9 -0
- package/scaffold/vue/Settings.vue +9 -0
- package/scaffold/vue/Sheet.vue +9 -0
- package/scaffold/vue/Skeleton.vue +9 -0
- package/scaffold/vue/Slider.vue +9 -0
- package/scaffold/vue/SoundEffects.vue +9 -0
- package/scaffold/vue/Spinner.vue +21 -0
- package/scaffold/vue/Switch.vue +9 -0
- package/scaffold/vue/Table.vue +9 -0
- package/scaffold/vue/Tabs.vue +9 -0
- package/scaffold/vue/Textarea.vue +60 -0
- package/scaffold/vue/ThemeSwitcher.vue +9 -0
- package/scaffold/vue/Toast.vue +9 -0
- package/scaffold/vue/Toggle.vue +9 -0
- package/scaffold/vue/ToggleGroup.vue +9 -0
- package/scaffold/vue/Tooltip.vue +9 -0
package/bin/rizzo-css.js
CHANGED
|
@@ -18,7 +18,7 @@ const CLI_BANNER = ` /\\___/\\
|
|
|
18
18
|
|_| \\_\\___/____/____\\___/ \\____|____/____/
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
Design system · Vanilla · Astro · Svelte`;
|
|
21
|
+
Design system · Vanilla · Astro · Svelte · React · Vue`;
|
|
22
22
|
|
|
23
23
|
/** Rainbow theme colors for "RIZZOCSS" block (from our themes: red → orange → yellow → green → blue → purple → pink). */
|
|
24
24
|
const BANNER_RAINBOW = [
|
|
@@ -79,7 +79,7 @@ const RIZZO_SNIPPET_FILE = 'RIZZO-SNIPPET.txt';
|
|
|
79
79
|
const RIZZO_SETUP_FILE = 'RIZZO-SETUP.md';
|
|
80
80
|
|
|
81
81
|
const COMMANDS = ['init', 'add', 'theme', 'doctor', 'help'];
|
|
82
|
-
const FRAMEWORKS = ['vanilla', 'astro', 'svelte'];
|
|
82
|
+
const FRAMEWORKS = ['vanilla', 'astro', 'svelte', 'react', 'vue'];
|
|
83
83
|
/** Supported package managers: detection, install/add commands, and --package-manager override. */
|
|
84
84
|
const VALID_PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'];
|
|
85
85
|
|
|
@@ -95,7 +95,7 @@ const TEMPLATES = [
|
|
|
95
95
|
/** Build RIZZO-SETUP.md content: instructions + snippets. Never overwrites; use for landing or when files were skipped. */
|
|
96
96
|
function buildRizzoSetupMd(framework, opts) {
|
|
97
97
|
const { linkTag, linkHref, theme, defaultDark, defaultLight, skippedFiles = [], exampleMinimalPage } = opts;
|
|
98
|
-
const fw = framework === 'vanilla' ? 'Vanilla' : framework === 'astro' ? 'Astro' : 'Svelte';
|
|
98
|
+
const fw = framework === 'vanilla' ? 'Vanilla' : framework === 'astro' ? 'Astro' : framework === 'svelte' ? 'Svelte' : framework === 'react' ? 'React' : 'Vue';
|
|
99
99
|
let md = `# Rizzo CSS setup
|
|
100
100
|
|
|
101
101
|
This file was generated by \`npx rizzo-css init\` or \`npx rizzo-css add\`. Use it to integrate Rizzo CSS into your project. **We never overwrite your existing files** — add the snippets below where needed.
|
|
@@ -193,6 +193,7 @@ const LIGHT_THEMES = [
|
|
|
193
193
|
'semi-light-purple',
|
|
194
194
|
];
|
|
195
195
|
const THEMES = [...DARK_THEMES, ...LIGHT_THEMES];
|
|
196
|
+
const VALID_THEME_VALUES = ['system', ...THEMES];
|
|
196
197
|
// Components available for scaffold (must match scaffold/svelte and scaffold/astro; missing files are skipped on copy)
|
|
197
198
|
const SVELTE_COMPONENTS = [
|
|
198
199
|
'Button', 'Badge', 'Card', 'Dashboard', 'Divider', 'DocsSidebar', 'Footer', 'Spinner', 'ProgressBar', 'Avatar', 'Alert',
|
|
@@ -212,6 +213,8 @@ const ASTRO_COMPONENTS = [
|
|
|
212
213
|
'Label', 'Kbd', 'Collapsible', 'AlertDialog', 'AspectRatio', 'ButtonGroup', 'Empty', 'Separator',
|
|
213
214
|
'Slider', 'Sheet', 'Popover', 'Toggle', 'ToggleGroup', 'ScrollArea', 'HoverCard', 'ContextMenu', 'ResizablePaneGroup', 'ResizablePane', 'ResizableHandle',
|
|
214
215
|
];
|
|
216
|
+
const REACT_COMPONENTS = [...ASTRO_COMPONENTS];
|
|
217
|
+
const VUE_COMPONENTS = [...ASTRO_COMPONENTS];
|
|
215
218
|
|
|
216
219
|
// Base set for Manual: all interactive components we ship (so manual has a full working set). Full includes everything (same list).
|
|
217
220
|
const RECOMMENDED_COMPONENTS = [
|
|
@@ -232,6 +235,8 @@ const VANILLA_JS_COMPONENTS = ['Modal', 'Dropdown', 'Tabs', 'Toast', 'ThemeSwitc
|
|
|
232
235
|
const COMPONENT_DEPS = {
|
|
233
236
|
astro: { Settings: ['ThemeSwitcher', 'FontSwitcher', 'SoundEffects'], Toast: ['Alert'], Navbar: ['Search', 'Settings'] },
|
|
234
237
|
svelte: { Settings: ['ThemeSwitcher', 'FontSwitcher', 'SoundEffects'], Toast: ['Alert'], Navbar: ['Search', 'Settings'] },
|
|
238
|
+
react: { Settings: ['ThemeSwitcher', 'FontSwitcher', 'SoundEffects'], Toast: ['Alert'], Navbar: ['Search', 'Settings'] },
|
|
239
|
+
vue: { Settings: ['ThemeSwitcher', 'FontSwitcher', 'SoundEffects'], Toast: ['Alert'], Navbar: ['Search', 'Settings'] },
|
|
235
240
|
};
|
|
236
241
|
|
|
237
242
|
function getComponentDeps(framework, componentName) {
|
|
@@ -302,6 +307,8 @@ const C = {
|
|
|
302
307
|
vanilla: '\u001b[38;5;226m', // Vanilla JS yellow
|
|
303
308
|
astro: '\u001b[38;5;208m', // Astro orange #ff5d01
|
|
304
309
|
svelte: '\u001b[38;5;202m', // Svelte orange #ff3e00
|
|
310
|
+
react: '\u001b[38;5;39m', // React blue
|
|
311
|
+
vue: '\u001b[38;5;42m', // Vue green
|
|
305
312
|
};
|
|
306
313
|
|
|
307
314
|
const CIRCLE_EMPTY = '\u25CB '; // ○
|
|
@@ -316,11 +323,57 @@ function getCssPath() {
|
|
|
316
323
|
return join(getPackageRoot(), 'dist', 'rizzo.min.css');
|
|
317
324
|
}
|
|
318
325
|
|
|
319
|
-
/**
|
|
320
|
-
function
|
|
326
|
+
/** Simple semver comparison: true if a > b (e.g. "1.2.3" > "1.2.2"). */
|
|
327
|
+
function semverGt(a, b) {
|
|
328
|
+
const pa = a.split('.').map((n) => parseInt(n, 10) || 0);
|
|
329
|
+
const pb = b.split('.').map((n) => parseInt(n, 10) || 0);
|
|
330
|
+
for (let i = 0; i < 3; i++) {
|
|
331
|
+
const na = pa[i] || 0, nb = pb[i] || 0;
|
|
332
|
+
if (na > nb) return true;
|
|
333
|
+
if (na < nb) return false;
|
|
334
|
+
}
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Optional version check: if not --offline, fetch latest from registry and print one line if newer. Non-blocking (2s timeout). */
|
|
339
|
+
async function checkNewerVersion(argv) {
|
|
340
|
+
if (hasFlag(argv, '--offline')) return;
|
|
341
|
+
let current;
|
|
342
|
+
try {
|
|
343
|
+
const pkgPath = join(getPackageRoot(), 'package.json');
|
|
344
|
+
current = JSON.parse(readFileSync(pkgPath, 'utf8')).version;
|
|
345
|
+
} catch (_) { return; }
|
|
346
|
+
try {
|
|
347
|
+
const ac = new AbortController();
|
|
348
|
+
const timeout = setTimeout(() => ac.abort(), 2500);
|
|
349
|
+
const res = await fetch('https://registry.npmjs.org/rizzo-css/latest', { signal: ac.signal }).finally(() => clearTimeout(timeout));
|
|
350
|
+
if (!res || !res.ok) return;
|
|
351
|
+
const data = await res.json();
|
|
352
|
+
const latest = data.version;
|
|
353
|
+
if (latest && semverGt(latest, current)) {
|
|
354
|
+
console.log('\n \u2139 A newer version of rizzo-css is available: ' + latest + ' (you have ' + current + '). Run npm update rizzo-css or re-run npx rizzo-css add to use it.');
|
|
355
|
+
}
|
|
356
|
+
} catch (_) { /* ignore network or parse errors */ }
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** Copy package dist/fonts into <cssTargetDir>/fonts so CSS url(./fonts/...) resolves. cssTargetDir is framework-specific (static/css | css). Not used for Astro; use copyRizzoCssAndFontsForAstro instead. opts: optional { dryRun, plan }; when dryRun push relative paths to plan.wouldWrite (projectDir must be cwd). */
|
|
360
|
+
function copyRizzoFonts(cssTargetDir, opts) {
|
|
321
361
|
const fontsSrc = join(getPackageRoot(), 'dist', 'fonts');
|
|
322
362
|
if (!existsSync(fontsSrc)) return;
|
|
323
363
|
const dest = join(cssTargetDir, 'fonts');
|
|
364
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
365
|
+
if (plan) {
|
|
366
|
+
function collectPaths(src, d) {
|
|
367
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
368
|
+
for (const e of entries) {
|
|
369
|
+
const destPath = join(d, e.name);
|
|
370
|
+
if (e.isDirectory()) collectPaths(join(src, e.name), destPath);
|
|
371
|
+
else plan.wouldWrite.push(pathRelative(process.cwd(), destPath));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
collectPaths(fontsSrc, dest);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
324
377
|
mkdirSync(dest, { recursive: true });
|
|
325
378
|
const entries = readdirSync(fontsSrc, { withFileTypes: true });
|
|
326
379
|
for (const e of entries) {
|
|
@@ -334,11 +387,19 @@ function copyRizzoFonts(cssTargetDir) {
|
|
|
334
387
|
}
|
|
335
388
|
}
|
|
336
389
|
|
|
337
|
-
/** Copy package dist/sfx (click.mp3 or click.wav) into projectDir/assets/sfx so sound-effects-inline.js can play the click sound. Used for Vanilla init and add. */
|
|
338
|
-
function copyRizzoSfx(projectDir) {
|
|
390
|
+
/** Copy package dist/sfx (click.mp3 or click.wav) into projectDir/assets/sfx so sound-effects-inline.js can play the click sound. Used for Vanilla init and add. opts: optional { dryRun, plan }; when dryRun push relative paths to plan.wouldWrite. */
|
|
391
|
+
function copyRizzoSfx(projectDir, opts) {
|
|
339
392
|
const sfxSrc = join(getPackageRoot(), 'dist', 'sfx');
|
|
340
393
|
if (!existsSync(sfxSrc)) return;
|
|
341
394
|
const dest = join(projectDir, 'assets', 'sfx');
|
|
395
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
396
|
+
if (plan) {
|
|
397
|
+
readdirSync(sfxSrc, { withFileTypes: true }).forEach((e) => {
|
|
398
|
+
if (!e.isDirectory() && (/\.mp3$/i.test(e.name) || /\.wav$/i.test(e.name)))
|
|
399
|
+
plan.wouldWrite.push(pathRelative(projectDir, join(dest, e.name)));
|
|
400
|
+
});
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
342
403
|
mkdirSync(dest, { recursive: true });
|
|
343
404
|
const entries = readdirSync(sfxSrc, { withFileTypes: true });
|
|
344
405
|
for (const e of entries) {
|
|
@@ -348,12 +409,34 @@ function copyRizzoSfx(projectDir) {
|
|
|
348
409
|
}
|
|
349
410
|
}
|
|
350
411
|
|
|
351
|
-
/** Astro only: copy rizzo.min.css to public/css with font URLs rewritten to ../assets/fonts/ (relative so base path works), and copy fonts to public/assets/fonts. */
|
|
352
|
-
function copyRizzoCssAndFontsForAstro(projectDir, cssSource) {
|
|
412
|
+
/** Astro only: copy rizzo.min.css to public/css with font URLs rewritten to ../assets/fonts/ (relative so base path works), and copy fonts to public/assets/fonts. opts: optional { dryRun, plan }; when dryRun push relative paths to plan.wouldWrite. */
|
|
413
|
+
function copyRizzoCssAndFontsForAstro(projectDir, cssSource, opts) {
|
|
353
414
|
const cssDir = join(projectDir, 'public', 'css');
|
|
354
415
|
const cssTarget = join(cssDir, 'rizzo.min.css');
|
|
355
416
|
const fontsDest = join(projectDir, 'public', 'assets', 'fonts');
|
|
356
417
|
const sfxDest = join(projectDir, 'public', 'assets', 'sfx');
|
|
418
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
419
|
+
if (plan) {
|
|
420
|
+
plan.wouldWrite.push(pathRelative(projectDir, cssTarget));
|
|
421
|
+
function collectPaths(src, dest) {
|
|
422
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
423
|
+
for (const e of entries) {
|
|
424
|
+
const destPath = join(dest, e.name);
|
|
425
|
+
if (e.isDirectory()) collectPaths(join(src, e.name), destPath);
|
|
426
|
+
else plan.wouldWrite.push(pathRelative(projectDir, destPath));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const fontsSrc = join(getPackageRoot(), 'dist', 'fonts');
|
|
430
|
+
if (existsSync(fontsSrc)) collectPaths(fontsSrc, fontsDest);
|
|
431
|
+
const sfxSrc = join(getPackageRoot(), 'dist', 'sfx');
|
|
432
|
+
if (existsSync(sfxSrc)) {
|
|
433
|
+
readdirSync(sfxSrc, { withFileTypes: true }).forEach((e) => {
|
|
434
|
+
if (!e.isDirectory() && (/\.mp3$/i.test(e.name) || /\.wav$/i.test(e.name)))
|
|
435
|
+
plan.wouldWrite.push(pathRelative(projectDir, join(sfxDest, e.name)));
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
357
440
|
mkdirSync(cssDir, { recursive: true });
|
|
358
441
|
mkdirSync(fontsDest, { recursive: true });
|
|
359
442
|
mkdirSync(sfxDest, { recursive: true });
|
|
@@ -382,12 +465,34 @@ function copyRizzoCssAndFontsForAstro(projectDir, cssSource) {
|
|
|
382
465
|
}
|
|
383
466
|
}
|
|
384
467
|
|
|
385
|
-
/** SvelteKit only: copy rizzo.min.css to static/css with font URLs rewritten to ../assets/fonts/ (relative so base path works), and copy fonts to static/assets/fonts. */
|
|
386
|
-
function copyRizzoCssAndFontsForSvelte(projectDir, cssSource) {
|
|
468
|
+
/** SvelteKit only: copy rizzo.min.css to static/css with font URLs rewritten to ../assets/fonts/ (relative so base path works), and copy fonts to static/assets/fonts. opts: optional { dryRun, plan }; when dryRun push relative paths to plan.wouldWrite. */
|
|
469
|
+
function copyRizzoCssAndFontsForSvelte(projectDir, cssSource, opts) {
|
|
387
470
|
const cssDir = join(projectDir, 'static', 'css');
|
|
388
471
|
const cssTarget = join(cssDir, 'rizzo.min.css');
|
|
389
472
|
const fontsDest = join(projectDir, 'static', 'assets', 'fonts');
|
|
390
473
|
const sfxDest = join(projectDir, 'static', 'assets', 'sfx');
|
|
474
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
475
|
+
if (plan) {
|
|
476
|
+
plan.wouldWrite.push(pathRelative(projectDir, cssTarget));
|
|
477
|
+
function collectPaths(src, dest) {
|
|
478
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
479
|
+
for (const e of entries) {
|
|
480
|
+
const destPath = join(dest, e.name);
|
|
481
|
+
if (e.isDirectory()) collectPaths(join(src, e.name), destPath);
|
|
482
|
+
else plan.wouldWrite.push(pathRelative(projectDir, destPath));
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
const fontsSrc = join(getPackageRoot(), 'dist', 'fonts');
|
|
486
|
+
if (existsSync(fontsSrc)) collectPaths(fontsSrc, fontsDest);
|
|
487
|
+
const sfxSrc = join(getPackageRoot(), 'dist', 'sfx');
|
|
488
|
+
if (existsSync(sfxSrc)) {
|
|
489
|
+
readdirSync(sfxSrc, { withFileTypes: true }).forEach((e) => {
|
|
490
|
+
if (!e.isDirectory() && (/\.mp3$/i.test(e.name) || /\.wav$/i.test(e.name)))
|
|
491
|
+
plan.wouldWrite.push(pathRelative(projectDir, join(sfxDest, e.name)));
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
391
496
|
mkdirSync(cssDir, { recursive: true });
|
|
392
497
|
mkdirSync(fontsDest, { recursive: true });
|
|
393
498
|
mkdirSync(sfxDest, { recursive: true });
|
|
@@ -416,12 +521,16 @@ function copyRizzoCssAndFontsForSvelte(projectDir, cssSource) {
|
|
|
416
521
|
}
|
|
417
522
|
}
|
|
418
523
|
|
|
419
|
-
/** Copy the package LICENSE into the project dir as LICENSE-RIZZO so we do not overwrite an existing LICENSE. */
|
|
420
|
-
function copyPackageLicense(projectDir) {
|
|
524
|
+
/** Copy the package LICENSE into the project dir as LICENSE-RIZZO so we do not overwrite an existing LICENSE. opts: optional { dryRun, plan }; when dryRun push relative path to plan.wouldWrite. */
|
|
525
|
+
function copyPackageLicense(projectDir, opts) {
|
|
421
526
|
const licensePath = join(getPackageRoot(), 'LICENSE');
|
|
422
|
-
if (existsSync(licensePath))
|
|
423
|
-
|
|
527
|
+
if (!existsSync(licensePath)) return;
|
|
528
|
+
const dest = join(projectDir, SCAFFOLD_LICENSE_FILENAME);
|
|
529
|
+
if (opts && opts.dryRun && opts.plan) {
|
|
530
|
+
opts.plan.wouldWrite.push(pathRelative(projectDir, dest));
|
|
531
|
+
return;
|
|
424
532
|
}
|
|
533
|
+
copyFileSync(licensePath, dest);
|
|
425
534
|
}
|
|
426
535
|
|
|
427
536
|
/** Name of the scaffold gitignore file (no leading dot so npm pack includes it). Copied to project as .gitignore. */
|
|
@@ -615,11 +724,11 @@ function copyVariantOverlay(projectDir, framework, variation, replacements) {
|
|
|
615
724
|
copyDirRecursiveWithReplacements(variantDir, projectDir, replacements);
|
|
616
725
|
}
|
|
617
726
|
|
|
618
|
-
/** Copy variant overlay with no-overwrite (for add to existing). Returns { skipped } like copyDirRecursiveWithReplacementsNoOverwrite. */
|
|
619
|
-
function copyVariantOverlayNoOverwrite(projectDir, framework, variation, replacements) {
|
|
727
|
+
/** Copy variant overlay with no-overwrite (for add to existing). Returns { skipped } like copyDirRecursiveWithReplacementsNoOverwrite. opts: optional { dryRun, plan }. */
|
|
728
|
+
function copyVariantOverlayNoOverwrite(projectDir, framework, variation, replacements, opts) {
|
|
620
729
|
const variantDir = getVariantDir(framework, variation);
|
|
621
730
|
if (!variantDir) return { skipped: [] };
|
|
622
|
-
return copyDirRecursiveWithReplacementsNoOverwrite(variantDir, projectDir, replacements, projectDir);
|
|
731
|
+
return copyDirRecursiveWithReplacementsNoOverwrite(variantDir, projectDir, replacements, projectDir, opts);
|
|
623
732
|
}
|
|
624
733
|
|
|
625
734
|
function question(prompt) {
|
|
@@ -969,13 +1078,13 @@ function multiSelectMenu(options, title, initialSelected) {
|
|
|
969
1078
|
function printHelp() {
|
|
970
1079
|
console.log(getBanner());
|
|
971
1080
|
console.log(`
|
|
972
|
-
rizzo-css CLI — Add Rizzo CSS to your project (Vanilla, Astro, Svelte)
|
|
1081
|
+
rizzo-css CLI — Add Rizzo CSS to your project (Vanilla, Astro, Svelte, React, Vue)
|
|
973
1082
|
|
|
974
1083
|
Available commands: init, add, theme, doctor, help
|
|
975
1084
|
|
|
976
1085
|
Flags summary:
|
|
977
1086
|
init --yes --path <dir> --framework <fw> --template <t> --package-manager <pm> --install --no-install --no-git
|
|
978
|
-
add --path <dir> --framework <fw> --template css-only|landing|docs|dashboard|full --no-snippet --readme --force --vanilla-js
|
|
1087
|
+
add --path <dir> --framework <fw> --template css-only|landing|docs|dashboard|full --dry-run --no-snippet --readme --force --vanilla-js
|
|
979
1088
|
theme (no flags)
|
|
980
1089
|
doctor Check config, CSS file, and optional layout link
|
|
981
1090
|
help (no flags)
|
|
@@ -995,21 +1104,24 @@ Commands:
|
|
|
995
1104
|
Options (init):
|
|
996
1105
|
--yes Non-interactive: scaffold new in cwd with defaults (framework: astro, template: landing)
|
|
997
1106
|
--path <dir> Project directory (relative to cwd or absolute). Scaffold and run install there. With --yes; interactive: "Enter path" option.
|
|
998
|
-
--framework <fw> vanilla | astro | svelte (
|
|
1107
|
+
--framework <fw> vanilla | astro | svelte | react | vue (same 56 components each)
|
|
999
1108
|
--template <t> create new: css-only | landing | docs | dashboard | full (default: landing). add: same options.
|
|
1000
1109
|
--package-manager <pm> npm | pnpm | yarn | bun (with --yes, or skip PM prompt when interactive)
|
|
1001
1110
|
--install After scaffolding, run package manager install in project directory (no prompt)
|
|
1002
1111
|
--no-install Do not run install; do not prompt (skip "Run <pm> install? (Y/n)")
|
|
1112
|
+
--offline Use package manager cache only (no network). Passed to install/add when running PM.
|
|
1003
1113
|
--no-git With --yes, skip initializing a git repository (default with --yes is to run git init)
|
|
1004
1114
|
(Git: only init offers or runs git init. Interactive init: "Initialize git? (Y/n)". With --yes, git init runs unless --no-git. Add does not prompt for git. Init (create new): "Run <pm> install? (Y/n)" uses the package manager you selected (npm, pnpm, yarn, bun). Add: "Run <pm> add rizzo-css? (Y/n)" same. Vanilla has no package manager. rizzo-css.json is written only when the project does not already have one.)
|
|
1005
1115
|
|
|
1006
1116
|
Options (add) — run from your existing project root; you will choose a template then select components (or CSS only):
|
|
1007
1117
|
--template <t> css-only | landing | docs | dashboard | full (CSS only = stylesheet only; others = component picker)
|
|
1008
1118
|
--path <dir> Target directory for rizzo.min.css (overrides config and framework default)
|
|
1009
|
-
--framework <fw> vanilla | astro | svelte (overrides config and detection)
|
|
1119
|
+
--framework <fw> vanilla | astro | svelte | react | vue (overrides config and detection)
|
|
1120
|
+
--dry-run Preview which files would be written without writing; show RIZZO-SETUP.md snippet for skipped files
|
|
1010
1121
|
--package-manager <pm> npm | pnpm | yarn | bun (override detection for install/print commands)
|
|
1011
1122
|
--install-package After copying CSS, run package manager add rizzo-css
|
|
1012
1123
|
--no-install Do not run install or add (overrides --install-package)
|
|
1124
|
+
--offline Use package manager cache only (no network). Passed to add when running PM.
|
|
1013
1125
|
--no-snippet (Full only) Do not write RIZZO-SNIPPET.txt (link + theme copy-paste)
|
|
1014
1126
|
--readme Write README-RIZZO.md into the project
|
|
1015
1127
|
--force Overwrite existing rizzo.min.css without prompting
|
|
@@ -1056,6 +1168,9 @@ Component dependencies (Full template):
|
|
|
1056
1168
|
Some components require others to work. The component picker adds required ones automatically.
|
|
1057
1169
|
Full list of available components and what relies on what: npx rizzo-css help components
|
|
1058
1170
|
|
|
1171
|
+
Frameworks:
|
|
1172
|
+
Scaffolds (init/add): Vanilla, Astro, Svelte, React, Vue. Same 56 components in each framework’s syntax; same CSS and BEM. Docs: https://rizzo-css.vercel.app/docs/react and Vue.
|
|
1173
|
+
|
|
1059
1174
|
Docs: https://rizzo-css.vercel.app
|
|
1060
1175
|
`);
|
|
1061
1176
|
}
|
|
@@ -1071,7 +1186,7 @@ function printHelpComponents() {
|
|
|
1071
1186
|
console.log(`
|
|
1072
1187
|
Components — full list and what relies on what
|
|
1073
1188
|
|
|
1074
|
-
Available to pick (Astro
|
|
1189
|
+
Available to pick (Astro, Svelte, React, Vue; same list):
|
|
1075
1190
|
` + line1 + (line2 ? ',\n ' + line2 : '') + (line3 ? ',\n ' + line3 : '') + `
|
|
1076
1191
|
|
|
1077
1192
|
Dependencies (when you pick the component on the left, the right is added automatically):
|
|
@@ -1085,6 +1200,8 @@ Icons: copied whenever you add any component.
|
|
|
1085
1200
|
Where components are copied:
|
|
1086
1201
|
Astro → src/components/rizzo/ (import from there)
|
|
1087
1202
|
Svelte → src/lib/rizzo/ (import from '$lib/rizzo')
|
|
1203
|
+
React → src/components/rizzo/ (import from there or @/components/rizzo)
|
|
1204
|
+
Vue → src/components/rizzo/ (import from there or @/components/rizzo)
|
|
1088
1205
|
Vanilla → components/ (HTML) (for interactivity add js/main.js; use --vanilla-js on add)
|
|
1089
1206
|
|
|
1090
1207
|
Core = all components above; dependencies are included so everything works.
|
|
@@ -1102,12 +1219,12 @@ function cmdTheme() {
|
|
|
1102
1219
|
process.stdout.write('\nExample: <html lang="en" data-theme="github-dark-classic">\n\n');
|
|
1103
1220
|
}
|
|
1104
1221
|
|
|
1105
|
-
/** Check project for Rizzo CSS: config, CSS file,
|
|
1222
|
+
/** Check project for Rizzo CSS: config, CSS file, layout link, theme, fonts/sfx paths, and optional outdated-CSS hint. */
|
|
1106
1223
|
function cmdDoctor() {
|
|
1107
1224
|
const cwd = process.cwd();
|
|
1108
1225
|
const config = readRizzoConfig(cwd);
|
|
1109
1226
|
console.log(getBanner());
|
|
1110
|
-
console.log(' Doctor — check config, CSS path,
|
|
1227
|
+
console.log(' Doctor — check config, CSS path, layout link, theme, and assets\n');
|
|
1111
1228
|
let ok = true;
|
|
1112
1229
|
if (!config) {
|
|
1113
1230
|
console.log(' ✗ No ' + RIZZO_CONFIG_FILE + '. Run: npx rizzo-css add or init');
|
|
@@ -1117,14 +1234,56 @@ function cmdDoctor() {
|
|
|
1117
1234
|
const fw = config.framework || 'vanilla';
|
|
1118
1235
|
const paths = getFrameworkCssPaths(fw);
|
|
1119
1236
|
const targetDir = (config.targetDir || paths.targetDir);
|
|
1120
|
-
const cssPath = fw === 'astro' ? join(cwd, 'public', 'css', 'rizzo.min.css') : fw === 'svelte' ? join(cwd, 'static', 'css', 'rizzo.min.css') : join(cwd, targetDir, 'rizzo.min.css');
|
|
1237
|
+
const cssPath = (fw === 'astro' || fw === 'react' || fw === 'vue') ? join(cwd, 'public', 'css', 'rizzo.min.css') : fw === 'svelte' ? join(cwd, 'static', 'css', 'rizzo.min.css') : join(cwd, targetDir, 'rizzo.min.css');
|
|
1121
1238
|
if (!existsSync(cssPath)) {
|
|
1122
1239
|
console.log(' ✗ CSS not found at ' + cssPath);
|
|
1123
1240
|
ok = false;
|
|
1124
1241
|
} else {
|
|
1125
1242
|
console.log(' ✓ CSS at ' + cssPath);
|
|
1243
|
+
const cssSize = statSync(cssPath).size;
|
|
1244
|
+
if (cssSize < 5000) {
|
|
1245
|
+
console.log(' ? CSS file is very small (' + cssSize + ' B). Run pnpm build:css in the repo or re-run npx rizzo-css add to refresh.');
|
|
1246
|
+
ok = false;
|
|
1247
|
+
}
|
|
1126
1248
|
}
|
|
1127
|
-
|
|
1249
|
+
if (config.theme != null && config.theme !== '') {
|
|
1250
|
+
if (VALID_THEME_VALUES.includes(config.theme)) {
|
|
1251
|
+
console.log(' ✓ Theme (from config): ' + config.theme);
|
|
1252
|
+
} else {
|
|
1253
|
+
console.log(' ✗ Invalid theme in config: "' + config.theme + '". Use one of: system, ' + THEMES.slice(0, 5).join(', ') + ', ... (run npx rizzo-css theme for full list)');
|
|
1254
|
+
ok = false;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
const fontsDir = (fw === 'astro' || fw === 'react' || fw === 'vue') ? join(cwd, 'public', 'assets', 'fonts') : fw === 'svelte' ? join(cwd, 'static', 'assets', 'fonts') : join(cwd, targetDir, 'fonts');
|
|
1258
|
+
if (!existsSync(fontsDir)) {
|
|
1259
|
+
console.log(' ? Fonts path not found: ' + pathRelative(cwd, fontsDir) + ' (CSS may reference ../assets/fonts/ or ./fonts/; add fonts if you use theme typography)');
|
|
1260
|
+
} else {
|
|
1261
|
+
try {
|
|
1262
|
+
const entries = readdirSync(fontsDir, { withFileTypes: true });
|
|
1263
|
+
const hasFiles = entries.some((e) => e.isFile() && /\.(woff2?|ttf|otf)$/i.test(e.name));
|
|
1264
|
+
if (!hasFiles) {
|
|
1265
|
+
console.log(' ? Fonts dir exists but is empty or has no font files: ' + pathRelative(cwd, fontsDir));
|
|
1266
|
+
} else {
|
|
1267
|
+
console.log(' ✓ Fonts at ' + pathRelative(cwd, fontsDir));
|
|
1268
|
+
}
|
|
1269
|
+
} catch (_) {
|
|
1270
|
+
console.log(' ? Could not read fonts dir: ' + pathRelative(cwd, fontsDir));
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
const sfxDir = (fw === 'astro' || fw === 'react' || fw === 'vue') ? join(cwd, 'public', 'assets', 'sfx') : fw === 'svelte' ? join(cwd, 'static', 'assets', 'sfx') : join(cwd, 'assets', 'sfx');
|
|
1274
|
+
if (!existsSync(sfxDir)) {
|
|
1275
|
+
console.log(' ? Sound effects path not found: ' + pathRelative(cwd, sfxDir) + ' (optional; needed if you use Settings or SoundEffects with click sounds)');
|
|
1276
|
+
} else {
|
|
1277
|
+
try {
|
|
1278
|
+
const entries = readdirSync(sfxDir, { withFileTypes: true });
|
|
1279
|
+
const hasSfx = entries.some((e) => e.isFile() && (/\.(mp3|wav)$/i.test(e.name)));
|
|
1280
|
+
if (hasSfx) console.log(' ✓ Sound effects at ' + pathRelative(cwd, sfxDir));
|
|
1281
|
+
else console.log(' ? Sfx dir exists but has no .mp3/.wav: ' + pathRelative(cwd, sfxDir));
|
|
1282
|
+
} catch (_) {
|
|
1283
|
+
console.log(' ? Could not read sfx dir: ' + pathRelative(cwd, sfxDir));
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const layoutPaths = fw === 'svelte' ? ['src/app.html'] : fw === 'astro' ? ['src/layouts/Layout.astro', 'src/layouts/BaseLayout.astro'] : fw === 'react' ? ['index.html', 'src/main.tsx', 'src/main.jsx', 'src/App.tsx', 'src/App.jsx'] : fw === 'vue' ? ['index.html', 'src/main.ts', 'src/main.js', 'src/App.vue'] : [];
|
|
1128
1287
|
for (const lp of layoutPaths) {
|
|
1129
1288
|
const full = join(cwd, lp);
|
|
1130
1289
|
if (existsSync(full)) {
|
|
@@ -1134,11 +1293,25 @@ function cmdDoctor() {
|
|
|
1134
1293
|
} else {
|
|
1135
1294
|
console.log(' ✓ Layout ' + lp + ' includes Rizzo link');
|
|
1136
1295
|
}
|
|
1296
|
+
if (content.includes('data-theme=')) {
|
|
1297
|
+
const match = content.match(/data-theme=["']([^"']+)["']/);
|
|
1298
|
+
if (match && !VALID_THEME_VALUES.includes(match[1])) {
|
|
1299
|
+
console.log(' ? Layout has data-theme="' + match[1] + '" which is not a known theme. Use: npx rizzo-css theme');
|
|
1300
|
+
ok = false;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1137
1303
|
break;
|
|
1138
1304
|
}
|
|
1139
1305
|
}
|
|
1306
|
+
const pkgPath = join(cwd, 'node_modules', 'rizzo-css', 'package.json');
|
|
1307
|
+
if (existsSync(pkgPath)) {
|
|
1308
|
+
try {
|
|
1309
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
1310
|
+
const ver = pkg.version;
|
|
1311
|
+
if (ver) console.log(' ℹ rizzo-css in node_modules: v' + ver + '. Re-run npx rizzo-css add to refresh CSS if needed.');
|
|
1312
|
+
} catch (_) { /* ignore */ }
|
|
1313
|
+
}
|
|
1140
1314
|
}
|
|
1141
|
-
if (config && config.theme) console.log(' Theme (from config): ' + config.theme);
|
|
1142
1315
|
console.log(ok ? '\nAll checks passed.\n' : '\nFix the items above, then run your dev server.\n');
|
|
1143
1316
|
}
|
|
1144
1317
|
|
|
@@ -1205,16 +1378,27 @@ async function promptComponentChoice(componentList, framework, initialSelection)
|
|
|
1205
1378
|
);
|
|
1206
1379
|
}
|
|
1207
1380
|
|
|
1208
|
-
/** Detect framework from cwd: "svelte" | "astro" | null. */
|
|
1381
|
+
/** Detect framework from cwd: "svelte" | "astro" | "react" | "vue" | null. */
|
|
1209
1382
|
function detectFramework(cwd) {
|
|
1210
1383
|
if (existsSync(join(cwd, 'svelte.config.js')) || existsSync(join(cwd, 'svelte.config.ts'))) return 'svelte';
|
|
1211
1384
|
if (existsSync(join(cwd, 'astro.config.mjs')) || existsSync(join(cwd, 'astro.config.mts')) || existsSync(join(cwd, 'astro.config.js'))) return 'astro';
|
|
1385
|
+
if (existsSync(join(cwd, 'vite.config.js')) || existsSync(join(cwd, 'vite.config.ts'))) {
|
|
1386
|
+
try {
|
|
1387
|
+
const pkg = readFileSync(join(cwd, 'package.json'), 'utf8');
|
|
1388
|
+
const json = JSON.parse(pkg);
|
|
1389
|
+
const deps = { ...json.dependencies, ...(json.devDependencies || {}) };
|
|
1390
|
+
if (deps['vue']) return 'vue';
|
|
1391
|
+
if (deps['react'] || deps['react-dom']) return 'react';
|
|
1392
|
+
} catch (_) { /* ignore */ }
|
|
1393
|
+
}
|
|
1212
1394
|
try {
|
|
1213
1395
|
const pkg = readFileSync(join(cwd, 'package.json'), 'utf8');
|
|
1214
1396
|
const json = JSON.parse(pkg);
|
|
1215
1397
|
const deps = { ...json.dependencies, ...(json.devDependencies || {}) };
|
|
1216
1398
|
if (deps['@sveltejs/kit'] || deps['svelte']) return 'svelte';
|
|
1217
1399
|
if (deps['astro']) return 'astro';
|
|
1400
|
+
if (deps['vue']) return 'vue';
|
|
1401
|
+
if (deps['react'] || deps['react-dom']) return 'react';
|
|
1218
1402
|
} catch (_) { /* ignore */ }
|
|
1219
1403
|
return null;
|
|
1220
1404
|
}
|
|
@@ -1232,6 +1416,12 @@ function getFrameworkCssPaths(framework) {
|
|
|
1232
1416
|
if (framework === 'astro') {
|
|
1233
1417
|
return { targetDir: 'public/css', linkHref: '/css/rizzo.min.css', fontsDir: 'public/assets/fonts', assetsRoot: 'public' };
|
|
1234
1418
|
}
|
|
1419
|
+
if (framework === 'react') {
|
|
1420
|
+
return { targetDir: 'public/css', linkHref: '/css/rizzo.min.css', fontsDir: 'public/assets/fonts', assetsRoot: 'public' };
|
|
1421
|
+
}
|
|
1422
|
+
if (framework === 'vue') {
|
|
1423
|
+
return { targetDir: 'public/css', linkHref: '/css/rizzo.min.css', fontsDir: 'public/assets/fonts', assetsRoot: 'public' };
|
|
1424
|
+
}
|
|
1235
1425
|
return { targetDir: 'css', linkHref: 'css/rizzo.min.css', fontsDir: 'css/fonts', assetsRoot: '' };
|
|
1236
1426
|
}
|
|
1237
1427
|
|
|
@@ -1241,7 +1431,7 @@ function getFrameworkCssPaths(framework) {
|
|
|
1241
1431
|
*/
|
|
1242
1432
|
function getLinkHrefForTargetDir(framework, targetDir) {
|
|
1243
1433
|
const file = 'rizzo.min.css';
|
|
1244
|
-
if (framework === 'astro' && targetDir) {
|
|
1434
|
+
if ((framework === 'astro' || framework === 'react' || framework === 'vue') && targetDir) {
|
|
1245
1435
|
const path = targetDir.replace(/^public\/?/, '').replace(/\/+$/, '') || 'css';
|
|
1246
1436
|
return '/' + (path ? path + '/' : '') + file;
|
|
1247
1437
|
}
|
|
@@ -1266,6 +1456,7 @@ async function cmdAdd(argv) {
|
|
|
1266
1456
|
const writeReadme = hasFlag(argv, '--readme');
|
|
1267
1457
|
const force = hasFlag(argv, '--force');
|
|
1268
1458
|
const copyVanillaJs = hasFlag(argv, '--vanilla-js');
|
|
1459
|
+
const dryRun = hasFlag(argv, '--dry-run');
|
|
1269
1460
|
const positionals = getPositionalArgs(argv, 3);
|
|
1270
1461
|
|
|
1271
1462
|
const cwd = process.cwd();
|
|
@@ -1283,21 +1474,25 @@ async function cmdAdd(argv) {
|
|
|
1283
1474
|
copyVanillaJs,
|
|
1284
1475
|
installPackage: installPackage || undefined,
|
|
1285
1476
|
noInstall: noInstall || undefined,
|
|
1477
|
+
dryRun: dryRun || undefined,
|
|
1478
|
+
plan: dryRun ? { wouldWrite: [], skipped: [] } : undefined,
|
|
1286
1479
|
};
|
|
1287
1480
|
await runAddToExisting(explicitFramework, options);
|
|
1481
|
+
if (options.dryRun) return;
|
|
1288
1482
|
if (installPackage && !noInstall) {
|
|
1289
1483
|
const pm = (pmOverride
|
|
1290
1484
|
? getPackageManagerCommands({ agent: pmOverride, command: pmOverride })
|
|
1291
1485
|
: (config && config.packageManager)
|
|
1292
1486
|
? getPackageManagerCommands({ agent: config.packageManager, command: config.packageManager })
|
|
1293
1487
|
: resolvePackageManager(cwd));
|
|
1294
|
-
const addPkg = pm.add('rizzo-css');
|
|
1488
|
+
const addPkg = pm.add('rizzo-css') + (hasFlag(argv, '--offline') ? ' --offline' : '');
|
|
1295
1489
|
console.log('\nRunning: ' + addPkg);
|
|
1296
1490
|
const code = runInDir(cwd, addPkg);
|
|
1297
1491
|
if (code !== 0) {
|
|
1298
1492
|
console.error('\nInstall failed (exit ' + code + '). You can run manually: ' + addPkg);
|
|
1299
1493
|
}
|
|
1300
1494
|
}
|
|
1495
|
+
await checkNewerVersion(argv).catch(() => {});
|
|
1301
1496
|
}
|
|
1302
1497
|
|
|
1303
1498
|
function getScaffoldSvelteDir() {
|
|
@@ -1308,6 +1503,14 @@ function getScaffoldAstroDir() {
|
|
|
1308
1503
|
return join(getPackageRoot(), 'scaffold', 'astro');
|
|
1309
1504
|
}
|
|
1310
1505
|
|
|
1506
|
+
function getScaffoldReactDir() {
|
|
1507
|
+
return join(getPackageRoot(), 'scaffold', 'react');
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
function getScaffoldVueDir() {
|
|
1511
|
+
return join(getPackageRoot(), 'scaffold', 'vue');
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1311
1514
|
function getScaffoldUtilsDir() {
|
|
1312
1515
|
return join(getPackageRoot(), 'scaffold', 'utils');
|
|
1313
1516
|
}
|
|
@@ -1372,8 +1575,8 @@ function getNavbarHtmlVanilla(title, brandHref) {
|
|
|
1372
1575
|
return html.replace(/\{\{TITLE\}\}/g, title || 'App').replace(/\{\{NAVBAR_BRAND_HREF\}\}/g, brandHref || '#');
|
|
1373
1576
|
}
|
|
1374
1577
|
|
|
1375
|
-
/** Copy selected Vanilla component HTML files into projectDir/components/, with replacements. Writes a simple components/index.html. */
|
|
1376
|
-
function copyVanillaComponents(projectDir, selectedNames, replacements) {
|
|
1578
|
+
/** Copy selected Vanilla component HTML files into projectDir/components/, with replacements. Writes a simple components/index.html. opts: optional { dryRun, plan }; when dryRun push relative paths to plan.wouldWrite. */
|
|
1579
|
+
function copyVanillaComponents(projectDir, selectedNames, replacements, opts) {
|
|
1377
1580
|
const srcDir = getScaffoldVanillaComponentsDir();
|
|
1378
1581
|
if (!existsSync(srcDir)) return;
|
|
1379
1582
|
const linkHref = replacements['{{LINK_HREF}}'] || 'css/rizzo.min.css';
|
|
@@ -1392,6 +1595,12 @@ function copyVanillaComponents(projectDir, selectedNames, replacements) {
|
|
|
1392
1595
|
}
|
|
1393
1596
|
if (slugsToCopy.length === 0) return;
|
|
1394
1597
|
const destDir = join(projectDir, 'components');
|
|
1598
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
1599
|
+
if (plan) {
|
|
1600
|
+
slugsToCopy.forEach((slug) => plan.wouldWrite.push(pathRelative(projectDir, join(destDir, slug + '.html'))));
|
|
1601
|
+
plan.wouldWrite.push(pathRelative(projectDir, join(destDir, 'index.html')));
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1395
1604
|
mkdirSync(destDir, { recursive: true });
|
|
1396
1605
|
for (const slug of slugsToCopy) {
|
|
1397
1606
|
const src = join(srcDir, slug + '.html');
|
|
@@ -1430,24 +1639,56 @@ ${indexLinks}
|
|
|
1430
1639
|
writeFileSync(join(destDir, 'index.html'), indexHtml, 'utf8');
|
|
1431
1640
|
}
|
|
1432
1641
|
|
|
1433
|
-
/** Copy Rizzo icons into the project for the given framework. */
|
|
1434
|
-
function copyRizzoIcons(projectDir, framework) {
|
|
1642
|
+
/** Copy Rizzo icons into the project for the given framework. opts: optional { dryRun, plan }; when dryRun push relative paths to plan.wouldWrite. */
|
|
1643
|
+
function copyRizzoIcons(projectDir, framework, opts) {
|
|
1644
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
1645
|
+
function collectIconPaths(src, dest) {
|
|
1646
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
1647
|
+
for (const e of entries) {
|
|
1648
|
+
const destPath = join(dest, e.name);
|
|
1649
|
+
if (e.isDirectory()) collectIconPaths(join(src, e.name), destPath);
|
|
1650
|
+
else plan.wouldWrite.push(pathRelative(projectDir, destPath));
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1435
1653
|
if (framework === 'astro') {
|
|
1436
1654
|
const iconsSrc = join(getScaffoldAstroDir(), 'icons');
|
|
1437
1655
|
if (!existsSync(iconsSrc)) return;
|
|
1438
1656
|
const targetDir = join(projectDir, 'src', 'components', 'rizzo', 'icons');
|
|
1657
|
+
if (plan) {
|
|
1658
|
+
collectIconPaths(iconsSrc, targetDir);
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1439
1661
|
mkdirSync(targetDir, { recursive: true });
|
|
1440
1662
|
copyDirRecursive(iconsSrc, targetDir);
|
|
1441
1663
|
} else if (framework === 'svelte') {
|
|
1442
1664
|
const iconsSrc = join(getScaffoldSvelteDir(), 'icons');
|
|
1443
1665
|
if (!existsSync(iconsSrc)) return;
|
|
1444
1666
|
const targetDir = join(projectDir, 'src', 'lib', 'rizzo', 'icons');
|
|
1667
|
+
if (plan) {
|
|
1668
|
+
collectIconPaths(iconsSrc, targetDir);
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
mkdirSync(targetDir, { recursive: true });
|
|
1672
|
+
copyDirRecursive(iconsSrc, targetDir);
|
|
1673
|
+
} else if (framework === 'react' || framework === 'vue') {
|
|
1674
|
+
const scaffoldDir = framework === 'react' ? getScaffoldReactDir() : getScaffoldVueDir();
|
|
1675
|
+
const iconsSrc = join(scaffoldDir, 'icons');
|
|
1676
|
+
if (!existsSync(iconsSrc)) return;
|
|
1677
|
+
const targetDir = join(projectDir, 'src', 'components', 'rizzo', 'icons');
|
|
1678
|
+
if (plan) {
|
|
1679
|
+
collectIconPaths(iconsSrc, targetDir);
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1445
1682
|
mkdirSync(targetDir, { recursive: true });
|
|
1446
1683
|
copyDirRecursive(iconsSrc, targetDir);
|
|
1447
1684
|
} else if (framework === 'vanilla') {
|
|
1448
1685
|
const iconsSrc = getScaffoldVanillaIconsDir();
|
|
1449
1686
|
if (!existsSync(iconsSrc)) return;
|
|
1450
1687
|
const targetDir = join(projectDir, 'icons');
|
|
1688
|
+
if (plan) {
|
|
1689
|
+
collectIconPaths(iconsSrc, targetDir);
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1451
1692
|
mkdirSync(targetDir, { recursive: true });
|
|
1452
1693
|
copyDirRecursive(iconsSrc, targetDir);
|
|
1453
1694
|
}
|
|
@@ -1504,13 +1745,15 @@ function copyDirRecursiveWithReplacements(src, dest, replacements) {
|
|
|
1504
1745
|
* Like copyDirRecursiveWithReplacements but never overwrites existing files.
|
|
1505
1746
|
* Returns { skipped: Array<{ relativePath, content }> } for files that already existed (so caller can write RIZZO-SETUP.md).
|
|
1506
1747
|
* relativePath is from projectDir (dest).
|
|
1748
|
+
* opts: optional { dryRun, plan: { wouldWrite: string[], skipped: [] } }. When dryRun, no I/O; plan.wouldWrite gets new file paths, plan.skipped gets skipped entries (merged with returned skipped).
|
|
1507
1749
|
*/
|
|
1508
|
-
function copyDirRecursiveWithReplacementsNoOverwrite(src, dest, replacements, projectDir) {
|
|
1750
|
+
function copyDirRecursiveWithReplacementsNoOverwrite(src, dest, replacements, projectDir, opts) {
|
|
1509
1751
|
const skipped = [];
|
|
1510
1752
|
projectDir = projectDir || dest;
|
|
1753
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
1511
1754
|
const textExtensions = new Set(['.html', '.astro', '.svelte', '.ts', '.js', '.mjs', '.json', '.css', '.md']);
|
|
1512
1755
|
function recurse(s, d) {
|
|
1513
|
-
mkdirSync(d, { recursive: true });
|
|
1756
|
+
if (!plan) mkdirSync(d, { recursive: true });
|
|
1514
1757
|
const entries = readdirSync(s, { withFileTypes: true });
|
|
1515
1758
|
for (const e of entries) {
|
|
1516
1759
|
const srcPath = join(s, e.name);
|
|
@@ -1518,8 +1761,8 @@ function copyDirRecursiveWithReplacementsNoOverwrite(src, dest, replacements, pr
|
|
|
1518
1761
|
if (e.isDirectory()) {
|
|
1519
1762
|
recurse(srcPath, destPath);
|
|
1520
1763
|
} else {
|
|
1764
|
+
const rel = pathRelative(projectDir, destPath);
|
|
1521
1765
|
if (existsSync(destPath)) {
|
|
1522
|
-
const rel = pathRelative(projectDir, destPath);
|
|
1523
1766
|
let content = '';
|
|
1524
1767
|
const ext = srcPath.slice(srcPath.lastIndexOf('.'));
|
|
1525
1768
|
if (textExtensions.has(ext)) {
|
|
@@ -1531,6 +1774,10 @@ function copyDirRecursiveWithReplacementsNoOverwrite(src, dest, replacements, pr
|
|
|
1531
1774
|
skipped.push({ relativePath: rel, content });
|
|
1532
1775
|
continue;
|
|
1533
1776
|
}
|
|
1777
|
+
if (plan) {
|
|
1778
|
+
plan.wouldWrite.push(rel);
|
|
1779
|
+
continue;
|
|
1780
|
+
}
|
|
1534
1781
|
const ext = srcPath.slice(srcPath.lastIndexOf('.'));
|
|
1535
1782
|
if (textExtensions.has(ext)) {
|
|
1536
1783
|
let content = readFileSync(srcPath, 'utf8');
|
|
@@ -1548,7 +1795,7 @@ function copyDirRecursiveWithReplacementsNoOverwrite(src, dest, replacements, pr
|
|
|
1548
1795
|
return { skipped };
|
|
1549
1796
|
}
|
|
1550
1797
|
|
|
1551
|
-
function copySvelteComponents(projectDir, selectedNames) {
|
|
1798
|
+
function copySvelteComponents(projectDir, selectedNames, opts) {
|
|
1552
1799
|
const scaffoldDir = getScaffoldSvelteDir();
|
|
1553
1800
|
if (!existsSync(scaffoldDir)) {
|
|
1554
1801
|
console.log('\n Component templates not in this package; use CSS only or copy from repo: https://github.com/mingleusa/rizzo-css/tree/main/src/components/svelte');
|
|
@@ -1563,6 +1810,28 @@ function copySvelteComponents(projectDir, selectedNames) {
|
|
|
1563
1810
|
return;
|
|
1564
1811
|
}
|
|
1565
1812
|
const targetDir = join(projectDir, 'src', 'lib', 'rizzo');
|
|
1813
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
1814
|
+
if (plan) {
|
|
1815
|
+
toCopy.forEach((name) => { plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, name + '.svelte'))); });
|
|
1816
|
+
const iconsSrc = join(scaffoldDir, 'icons');
|
|
1817
|
+
if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) {
|
|
1818
|
+
(function collect(s, d) {
|
|
1819
|
+
readdirSync(s, { withFileTypes: true }).forEach((e) => {
|
|
1820
|
+
const destPath = join(d, e.name);
|
|
1821
|
+
if (e.isDirectory()) collect(join(s, e.name), destPath);
|
|
1822
|
+
else plan.wouldWrite.push(pathRelative(projectDir, destPath));
|
|
1823
|
+
});
|
|
1824
|
+
})(iconsSrc, join(targetDir, 'icons'));
|
|
1825
|
+
}
|
|
1826
|
+
if (toCopy.includes('ThemeSwitcher') || toCopy.includes('ThemeIcon')) {
|
|
1827
|
+
if (existsSync(join(scaffoldDir, 'themes.ts'))) plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, 'themes.ts')));
|
|
1828
|
+
if (existsSync(join(scaffoldDir, 'theme.ts'))) plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, 'theme.ts')));
|
|
1829
|
+
}
|
|
1830
|
+
if (toCopy.includes('Settings') && existsSync(join(getScaffoldConfigDir(), 'fonts.ts')))
|
|
1831
|
+
plan.wouldWrite.push(pathRelative(projectDir, join(projectDir, 'src', 'lib', 'config', 'fonts.ts')));
|
|
1832
|
+
if (toCopy.length > 0) plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, 'index.ts')));
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1566
1835
|
mkdirSync(targetDir, { recursive: true });
|
|
1567
1836
|
const exports = [];
|
|
1568
1837
|
for (const name of toCopy) {
|
|
@@ -1602,7 +1871,7 @@ function copySvelteComponents(projectDir, selectedNames) {
|
|
|
1602
1871
|
}
|
|
1603
1872
|
}
|
|
1604
1873
|
|
|
1605
|
-
function copyAstroComponents(projectDir, selectedNames) {
|
|
1874
|
+
function copyAstroComponents(projectDir, selectedNames, opts) {
|
|
1606
1875
|
const scaffoldDir = getScaffoldAstroDir();
|
|
1607
1876
|
if (!existsSync(scaffoldDir)) {
|
|
1608
1877
|
console.log('\n Astro component templates not in this package; use CSS only or copy from repo: https://github.com/mingleusa/rizzo-css/tree/main/src/components');
|
|
@@ -1617,6 +1886,27 @@ function copyAstroComponents(projectDir, selectedNames) {
|
|
|
1617
1886
|
return;
|
|
1618
1887
|
}
|
|
1619
1888
|
const targetDir = join(projectDir, 'src', 'components', 'rizzo');
|
|
1889
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
1890
|
+
if (plan) {
|
|
1891
|
+
toCopy.forEach((name) => { plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, name + '.astro'))); });
|
|
1892
|
+
const iconsSrc = join(scaffoldDir, 'icons');
|
|
1893
|
+
if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) {
|
|
1894
|
+
(function collect(s, d) {
|
|
1895
|
+
readdirSync(s, { withFileTypes: true }).forEach((e) => {
|
|
1896
|
+
const destPath = join(d, e.name);
|
|
1897
|
+
if (e.isDirectory()) collect(join(s, e.name), destPath);
|
|
1898
|
+
else plan.wouldWrite.push(pathRelative(projectDir, destPath));
|
|
1899
|
+
});
|
|
1900
|
+
})(iconsSrc, join(targetDir, 'icons'));
|
|
1901
|
+
}
|
|
1902
|
+
if (toCopy.includes('ThemeSwitcher') || toCopy.includes('ThemeIcon')) {
|
|
1903
|
+
if (existsSync(join(scaffoldDir, 'themes.ts'))) plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, 'themes.ts')));
|
|
1904
|
+
if (existsSync(join(getScaffoldUtilsDir(), 'theme.ts'))) plan.wouldWrite.push(pathRelative(projectDir, join(projectDir, 'src', 'components', 'utils', 'theme.ts')));
|
|
1905
|
+
}
|
|
1906
|
+
if (toCopy.includes('Settings') && existsSync(join(getScaffoldConfigDir(), 'fonts.ts')))
|
|
1907
|
+
plan.wouldWrite.push(pathRelative(projectDir, join(projectDir, 'src', 'components', 'config', 'fonts.ts')));
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1620
1910
|
mkdirSync(targetDir, { recursive: true });
|
|
1621
1911
|
let count = 0;
|
|
1622
1912
|
for (const name of toCopy) {
|
|
@@ -1663,6 +1953,139 @@ function copyAstroComponents(projectDir, selectedNames) {
|
|
|
1663
1953
|
}
|
|
1664
1954
|
}
|
|
1665
1955
|
|
|
1956
|
+
function copyReactComponents(projectDir, selectedNames, opts) {
|
|
1957
|
+
const scaffoldDir = getScaffoldReactDir();
|
|
1958
|
+
if (!existsSync(scaffoldDir)) {
|
|
1959
|
+
console.log('\n React component templates not in this package; use CSS only or see docs.');
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
const files = readdirSync(scaffoldDir).filter((f) => f.endsWith('.tsx') || f.endsWith('.jsx'));
|
|
1963
|
+
const available = files.map((f) => f.replace(/\.(tsx|jsx)$/, ''));
|
|
1964
|
+
const toCopy = selectedNames.filter((n) => n !== 'Icons' && available.includes(n));
|
|
1965
|
+
const copyIconsOnly = selectedNames.includes('Icons') && toCopy.length === 0;
|
|
1966
|
+
if (toCopy.length === 0 && !copyIconsOnly) {
|
|
1967
|
+
console.log('\n No matching React components in scaffold; use CSS only or see docs.');
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
const targetDir = join(projectDir, 'src', 'components', 'rizzo');
|
|
1971
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
1972
|
+
if (plan) {
|
|
1973
|
+
toCopy.forEach((name) => {
|
|
1974
|
+
const f = files.find((file) => file.replace(/\.(tsx|jsx)$/, '') === name);
|
|
1975
|
+
const ext = f ? (f.endsWith('.tsx') ? '.tsx' : '.jsx') : '.tsx';
|
|
1976
|
+
plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, name + ext)));
|
|
1977
|
+
});
|
|
1978
|
+
const iconsSrc = join(scaffoldDir, 'icons');
|
|
1979
|
+
if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) {
|
|
1980
|
+
(function collect(s, d) {
|
|
1981
|
+
readdirSync(s, { withFileTypes: true }).forEach((e) => {
|
|
1982
|
+
const destPath = join(d, e.name);
|
|
1983
|
+
if (e.isDirectory()) collect(join(s, e.name), destPath);
|
|
1984
|
+
else plan.wouldWrite.push(pathRelative(projectDir, destPath));
|
|
1985
|
+
});
|
|
1986
|
+
})(iconsSrc, join(targetDir, 'icons'));
|
|
1987
|
+
}
|
|
1988
|
+
if (toCopy.length > 0) plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, 'index.ts')));
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
mkdirSync(targetDir, { recursive: true });
|
|
1992
|
+
const exports = [];
|
|
1993
|
+
const extUsed = {};
|
|
1994
|
+
for (const name of toCopy) {
|
|
1995
|
+
const tsxPath = join(scaffoldDir, name + '.tsx');
|
|
1996
|
+
const jsxPath = join(scaffoldDir, name + '.jsx');
|
|
1997
|
+
const src = existsSync(tsxPath) ? tsxPath : existsSync(jsxPath) ? jsxPath : null;
|
|
1998
|
+
if (src) {
|
|
1999
|
+
const ext = src.endsWith('.tsx') ? '.tsx' : '.jsx';
|
|
2000
|
+
copyFileSync(src, join(targetDir, name + ext));
|
|
2001
|
+
exports.push(`export { default as ${name} } from './${name}${ext}';`);
|
|
2002
|
+
extUsed[name] = ext;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
const iconsSrc = join(scaffoldDir, 'icons');
|
|
2006
|
+
if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) copyDirRecursive(iconsSrc, join(targetDir, 'icons'));
|
|
2007
|
+
if (toCopy.includes('Settings')) {
|
|
2008
|
+
const configDir = getScaffoldConfigDir();
|
|
2009
|
+
const fontsSrc = join(configDir, 'fonts.ts');
|
|
2010
|
+
if (existsSync(fontsSrc)) {
|
|
2011
|
+
const projectConfigDir = join(projectDir, 'src', 'components', 'config');
|
|
2012
|
+
mkdirSync(projectConfigDir, { recursive: true });
|
|
2013
|
+
copyFileSync(fontsSrc, join(projectConfigDir, 'fonts.ts'));
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
if (exports.length > 0 || copyIconsOnly) {
|
|
2017
|
+
if (exports.length > 0) {
|
|
2018
|
+
const indexContent = `/** Rizzo CSS React components — selected via npx rizzo-css add */\n${exports.join('\n')}\n`;
|
|
2019
|
+
writeFileSync(join(targetDir, 'index.ts'), indexContent, 'utf8');
|
|
2020
|
+
}
|
|
2021
|
+
const msg = copyIconsOnly ? 'Icons' : exports.length + ' React components' + (existsSync(iconsSrc) ? ' + icons' : '');
|
|
2022
|
+
console.log('\n ✓ ' + msg + ' copied to ' + targetDir);
|
|
2023
|
+
console.log(' Import in your app: import { Button, Badge, ... } from \'../components/rizzo\' or \'@/components/rizzo\';\n');
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
function copyVueComponents(projectDir, selectedNames, opts) {
|
|
2028
|
+
const scaffoldDir = getScaffoldVueDir();
|
|
2029
|
+
if (!existsSync(scaffoldDir)) {
|
|
2030
|
+
console.log('\n Vue component templates not in this package; use CSS only or see docs.');
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
const files = readdirSync(scaffoldDir).filter((f) => f.endsWith('.vue'));
|
|
2034
|
+
const available = files.map((f) => f.replace('.vue', ''));
|
|
2035
|
+
const toCopy = selectedNames.filter((n) => n !== 'Icons' && available.includes(n));
|
|
2036
|
+
const copyIconsOnly = selectedNames.includes('Icons') && toCopy.length === 0;
|
|
2037
|
+
if (toCopy.length === 0 && !copyIconsOnly) {
|
|
2038
|
+
console.log('\n No matching Vue components in scaffold; use CSS only or see docs.');
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
const targetDir = join(projectDir, 'src', 'components', 'rizzo');
|
|
2042
|
+
const plan = opts && opts.dryRun && opts.plan ? opts.plan : null;
|
|
2043
|
+
if (plan) {
|
|
2044
|
+
toCopy.forEach((name) => { plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, name + '.vue'))); });
|
|
2045
|
+
const iconsSrc = join(scaffoldDir, 'icons');
|
|
2046
|
+
if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) {
|
|
2047
|
+
(function collect(s, d) {
|
|
2048
|
+
readdirSync(s, { withFileTypes: true }).forEach((e) => {
|
|
2049
|
+
const destPath = join(d, e.name);
|
|
2050
|
+
if (e.isDirectory()) collect(join(s, e.name), destPath);
|
|
2051
|
+
else plan.wouldWrite.push(pathRelative(projectDir, destPath));
|
|
2052
|
+
});
|
|
2053
|
+
})(iconsSrc, join(targetDir, 'icons'));
|
|
2054
|
+
}
|
|
2055
|
+
if (toCopy.length > 0) plan.wouldWrite.push(pathRelative(projectDir, join(targetDir, 'index.ts')));
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
mkdirSync(targetDir, { recursive: true });
|
|
2059
|
+
const exports = [];
|
|
2060
|
+
for (const name of toCopy) {
|
|
2061
|
+
const src = join(scaffoldDir, name + '.vue');
|
|
2062
|
+
if (existsSync(src)) {
|
|
2063
|
+
copyFileSync(src, join(targetDir, name + '.vue'));
|
|
2064
|
+
exports.push(`export { default as ${name} } from './${name}.vue';`);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
const iconsSrc = join(scaffoldDir, 'icons');
|
|
2068
|
+
if (existsSync(iconsSrc) && (toCopy.length > 0 || copyIconsOnly)) copyDirRecursive(iconsSrc, join(targetDir, 'icons'));
|
|
2069
|
+
if (toCopy.includes('Settings')) {
|
|
2070
|
+
const configDir = getScaffoldConfigDir();
|
|
2071
|
+
const fontsSrc = join(configDir, 'fonts.ts');
|
|
2072
|
+
if (existsSync(fontsSrc)) {
|
|
2073
|
+
const projectConfigDir = join(projectDir, 'src', 'components', 'config');
|
|
2074
|
+
mkdirSync(projectConfigDir, { recursive: true });
|
|
2075
|
+
copyFileSync(fontsSrc, join(projectConfigDir, 'fonts.ts'));
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
if (exports.length > 0 || copyIconsOnly) {
|
|
2079
|
+
if (exports.length > 0) {
|
|
2080
|
+
const indexContent = `/** Rizzo CSS Vue components — selected via npx rizzo-css add */\n${exports.join('\n')}\n`;
|
|
2081
|
+
writeFileSync(join(targetDir, 'index.ts'), indexContent, 'utf8');
|
|
2082
|
+
}
|
|
2083
|
+
const msg = copyIconsOnly ? 'Icons' : exports.length + ' Vue components' + (existsSync(iconsSrc) ? ' + icons' : '');
|
|
2084
|
+
console.log('\n ✓ ' + msg + ' copied to ' + targetDir);
|
|
2085
|
+
console.log(' Import in your app: import { Button, Badge, ... } from \'@/components/rizzo\' or relative path;\n');
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
1666
2089
|
/** Add Rizzo CSS to an existing project. Same variation as create new: Landing | Docs | Dashboard. We never overwrite existing files (including config files). frameworkOverride: when set (from init), skip framework prompt. options: { config?, targetDir?, template? } — template is variation: landing|docs|dashboard (or full→landing). */
|
|
1667
2090
|
async function runAddToExisting(frameworkOverride, options) {
|
|
1668
2091
|
const cwd = process.cwd();
|
|
@@ -1675,6 +2098,8 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1675
2098
|
{ value: 'vanilla', label: 'Vanilla JS (HTML + CSS)', color: C.vanilla },
|
|
1676
2099
|
{ value: 'astro', label: 'Astro', color: C.astro },
|
|
1677
2100
|
{ value: 'svelte', label: 'Svelte', color: C.svelte },
|
|
2101
|
+
{ value: 'react', label: 'React', color: C.react },
|
|
2102
|
+
{ value: 'vue', label: 'Vue', color: C.vue },
|
|
1678
2103
|
];
|
|
1679
2104
|
let frameworkPrompt = '? Framework';
|
|
1680
2105
|
if (detected) {
|
|
@@ -1689,7 +2114,7 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1689
2114
|
selectedVariation = await promptTemplate();
|
|
1690
2115
|
}
|
|
1691
2116
|
|
|
1692
|
-
const componentList = framework === 'svelte' ? SVELTE_COMPONENTS : framework === 'astro' ? ASTRO_COMPONENTS : framework === 'vanilla' ? Object.keys(VANILLA_COMPONENT_SLUGS) : [];
|
|
2117
|
+
const componentList = framework === 'svelte' ? SVELTE_COMPONENTS : framework === 'astro' ? ASTRO_COMPONENTS : framework === 'react' ? REACT_COMPONENTS : framework === 'vue' ? VUE_COMPONENTS : framework === 'vanilla' ? Object.keys(VANILLA_COMPONENT_SLUGS) : [];
|
|
1693
2118
|
const preselected = options.preselectedComponents && options.preselectedComponents.length > 0 ? options.preselectedComponents : null;
|
|
1694
2119
|
let selectedComponents;
|
|
1695
2120
|
if (selectedVariation === 'css-only') {
|
|
@@ -1749,7 +2174,7 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1749
2174
|
const paths = getFrameworkCssPaths(framework);
|
|
1750
2175
|
const targetDirRaw = (options && options.targetDir) || (config && config.targetDir) || paths.targetDir;
|
|
1751
2176
|
let cssTarget;
|
|
1752
|
-
if (framework === 'astro') {
|
|
2177
|
+
if (framework === 'astro' || framework === 'react' || framework === 'vue') {
|
|
1753
2178
|
cssTarget = join(cwd, 'public', 'css', 'rizzo.min.css');
|
|
1754
2179
|
} else if (framework === 'svelte') {
|
|
1755
2180
|
cssTarget = join(cwd, 'static', 'css', 'rizzo.min.css');
|
|
@@ -1758,7 +2183,9 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1758
2183
|
cssTarget = join(targetDir, 'rizzo.min.css');
|
|
1759
2184
|
}
|
|
1760
2185
|
const cssExists = existsSync(cssTarget);
|
|
1761
|
-
if (
|
|
2186
|
+
if (options.dryRun) {
|
|
2187
|
+
options._overwriteCss = true;
|
|
2188
|
+
} else if (cssExists && !options.force) {
|
|
1762
2189
|
const answer = await question('\nCSS already exists at ' + cssTarget + '. Overwrite? (y/N) ');
|
|
1763
2190
|
if (answer !== '' && !/^y(es)?$/i.test(answer)) {
|
|
1764
2191
|
console.log('Skipping CSS copy. Updating config and components only.');
|
|
@@ -1776,9 +2203,10 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1776
2203
|
const astroCoreDir = getScaffoldAstroCoreDir();
|
|
1777
2204
|
const svelteCoreDir = getScaffoldSvelteCoreDir();
|
|
1778
2205
|
const themeCommentAdd = ' <!-- Initial: ' + theme + '; dark: ' + defaultDark + '; light: ' + defaultLight + ' (all 14 themes in CSS) -->';
|
|
2206
|
+
const copyOpts = options.dryRun && options.plan ? options : undefined;
|
|
1779
2207
|
if (framework === 'vanilla' && getVariantDir('vanilla', selectedVariation)) {
|
|
1780
2208
|
const vanillaRepl = { '{{LINK_HREF}}': 'css/rizzo.min.css', '{{TITLE}}': 'App', '{{DATA_THEME}}': theme, '{{THEME_LIST_COMMENT}}': themeCommentAdd };
|
|
1781
|
-
const variantResult = copyVariantOverlayNoOverwrite(cwd, 'vanilla', selectedVariation, vanillaRepl);
|
|
2209
|
+
const variantResult = copyVariantOverlayNoOverwrite(cwd, 'vanilla', selectedVariation, vanillaRepl, copyOpts);
|
|
1782
2210
|
if (variantResult && variantResult.skipped) addSkippedFiles = variantResult.skipped;
|
|
1783
2211
|
} else if (selectedVariation !== 'css-only' && ((framework === 'astro' && existsSync(astroCoreDir)) || (framework === 'svelte' && existsSync(svelteCoreDir)))) {
|
|
1784
2212
|
const themeComment = themeCommentAdd;
|
|
@@ -1818,48 +2246,62 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1818
2246
|
}
|
|
1819
2247
|
}
|
|
1820
2248
|
}
|
|
1821
|
-
mkdirSync(cwd, { recursive: true });
|
|
2249
|
+
if (!options.dryRun) mkdirSync(cwd, { recursive: true });
|
|
1822
2250
|
if (framework === 'astro') {
|
|
1823
|
-
const baseResult = copyDirRecursiveWithReplacementsNoOverwrite(astroCoreDir, cwd, replacements, cwd);
|
|
1824
|
-
const variantResult = copyVariantOverlayNoOverwrite(cwd, 'astro', selectedVariation, replacements);
|
|
2251
|
+
const baseResult = copyDirRecursiveWithReplacementsNoOverwrite(astroCoreDir, cwd, replacements, cwd, copyOpts);
|
|
2252
|
+
const variantResult = copyVariantOverlayNoOverwrite(cwd, 'astro', selectedVariation, replacements, copyOpts);
|
|
1825
2253
|
addSkippedFiles = baseResult.skipped.concat(variantResult.skipped || []);
|
|
1826
2254
|
} else if (framework === 'svelte') {
|
|
1827
|
-
const baseResult = copyDirRecursiveWithReplacementsNoOverwrite(svelteCoreDir, cwd, replacements, cwd);
|
|
1828
|
-
const variantResult = copyVariantOverlayNoOverwrite(cwd, 'svelte', selectedVariation, replacements);
|
|
2255
|
+
const baseResult = copyDirRecursiveWithReplacementsNoOverwrite(svelteCoreDir, cwd, replacements, cwd, copyOpts);
|
|
2256
|
+
const variantResult = copyVariantOverlayNoOverwrite(cwd, 'svelte', selectedVariation, replacements, copyOpts);
|
|
1829
2257
|
addSkippedFiles = baseResult.skipped.concat(variantResult.skipped || []);
|
|
1830
2258
|
}
|
|
1831
2259
|
}
|
|
1832
2260
|
|
|
1833
2261
|
if (options._overwriteCss) {
|
|
1834
|
-
if (framework === 'astro') {
|
|
1835
|
-
copyRizzoCssAndFontsForAstro(cwd, cssSource);
|
|
2262
|
+
if (framework === 'astro' || framework === 'react' || framework === 'vue') {
|
|
2263
|
+
copyRizzoCssAndFontsForAstro(cwd, cssSource, copyOpts);
|
|
1836
2264
|
} else if (framework === 'svelte') {
|
|
1837
|
-
copyRizzoCssAndFontsForSvelte(cwd, cssSource);
|
|
2265
|
+
copyRizzoCssAndFontsForSvelte(cwd, cssSource, copyOpts);
|
|
1838
2266
|
} else {
|
|
1839
2267
|
const targetDir = join(cwd, targetDirRaw);
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
2268
|
+
if (copyOpts && copyOpts.plan) {
|
|
2269
|
+
copyOpts.plan.wouldWrite.push(pathRelative(cwd, cssTarget));
|
|
2270
|
+
copyRizzoFonts(dirname(cssTarget), copyOpts);
|
|
2271
|
+
if (framework === 'vanilla' && selectedVariation !== 'css-only') copyRizzoSfx(cwd, copyOpts);
|
|
2272
|
+
} else {
|
|
2273
|
+
mkdirSync(targetDir, { recursive: true });
|
|
2274
|
+
copyFileSync(cssSource, cssTarget);
|
|
2275
|
+
copyRizzoFonts(dirname(cssTarget));
|
|
2276
|
+
if (framework === 'vanilla' && selectedVariation !== 'css-only') copyRizzoSfx(cwd);
|
|
2277
|
+
}
|
|
1844
2278
|
}
|
|
1845
2279
|
}
|
|
1846
2280
|
|
|
1847
|
-
if (selectedVariation !== 'css-only') copyRizzoIcons(cwd, framework);
|
|
2281
|
+
if (selectedVariation !== 'css-only') copyRizzoIcons(cwd, framework, copyOpts);
|
|
1848
2282
|
if (framework === 'svelte' && selectedComponents.length > 0) {
|
|
1849
2283
|
const expanded = expandWithDeps('svelte', selectedComponents);
|
|
1850
2284
|
logAddedDeps(selectedComponents, expanded, 'svelte');
|
|
1851
|
-
copySvelteComponents(cwd, expanded);
|
|
2285
|
+
copySvelteComponents(cwd, expanded, copyOpts);
|
|
1852
2286
|
} else if (framework === 'astro' && selectedComponents.length > 0) {
|
|
1853
2287
|
const expanded = expandWithDeps('astro', selectedComponents);
|
|
1854
2288
|
logAddedDeps(selectedComponents, expanded, 'astro');
|
|
1855
|
-
copyAstroComponents(cwd, expanded);
|
|
2289
|
+
copyAstroComponents(cwd, expanded, copyOpts);
|
|
2290
|
+
} else if (framework === 'react' && selectedComponents.length > 0) {
|
|
2291
|
+
const expanded = expandWithDeps('react', selectedComponents);
|
|
2292
|
+
logAddedDeps(selectedComponents, expanded, 'react');
|
|
2293
|
+
copyReactComponents(cwd, expanded, copyOpts);
|
|
2294
|
+
} else if (framework === 'vue' && selectedComponents.length > 0) {
|
|
2295
|
+
const expanded = expandWithDeps('vue', selectedComponents);
|
|
2296
|
+
logAddedDeps(selectedComponents, expanded, 'vue');
|
|
2297
|
+
copyVueComponents(cwd, expanded, copyOpts);
|
|
1856
2298
|
} else if (framework === 'vanilla' && selectedComponents.length > 0) {
|
|
1857
2299
|
const linkHrefForVanilla = (options && options.targetDir) ? getLinkHrefForTargetDir(framework, options.targetDir) : paths.linkHref;
|
|
1858
2300
|
const vanillaRepl = { '{{LINK_HREF}}': linkHrefForVanilla, '{{DATA_THEME}}': theme };
|
|
1859
|
-
copyVanillaComponents(cwd, selectedComponents, vanillaRepl);
|
|
2301
|
+
copyVanillaComponents(cwd, selectedComponents, vanillaRepl, copyOpts);
|
|
1860
2302
|
const needsJs = selectedComponents.some((c) => VANILLA_JS_COMPONENTS.includes(c));
|
|
1861
2303
|
const vanillaJsPath = join(cwd, 'js', 'main.js');
|
|
1862
|
-
if (needsJs && !existsSync(vanillaJsPath) && (options.copyVanillaJs || (!preselected && (await confirmCopyVanillaJs())))) {
|
|
2304
|
+
if (!options.dryRun && needsJs && !existsSync(vanillaJsPath) && (options.copyVanillaJs || (!preselected && (await confirmCopyVanillaJs())))) {
|
|
1863
2305
|
const vanillaJsSrc = join(getPackageRoot(), 'scaffold', 'vanilla', 'js', 'main.js');
|
|
1864
2306
|
if (existsSync(vanillaJsSrc)) {
|
|
1865
2307
|
mkdirSync(join(cwd, 'js'), { recursive: true });
|
|
@@ -1880,9 +2322,10 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1880
2322
|
} else if (needsJs && !existsSync(vanillaJsPath)) {
|
|
1881
2323
|
options._vanillaJsHint = true;
|
|
1882
2324
|
}
|
|
2325
|
+
if (options.dryRun && needsJs && !existsSync(vanillaJsPath) && options.plan) options.plan.wouldWrite.push('js/main.js');
|
|
1883
2326
|
}
|
|
1884
2327
|
|
|
1885
|
-
const linkHref = (framework === 'astro' || framework === 'svelte') ? paths.linkHref : ((options && options.targetDir) ? getLinkHrefForTargetDir(framework, options.targetDir) : paths.linkHref);
|
|
2328
|
+
const linkHref = (framework === 'astro' || framework === 'svelte' || framework === 'react' || framework === 'vue') ? paths.linkHref : ((options && options.targetDir) ? getLinkHrefForTargetDir(framework, options.targetDir) : paths.linkHref);
|
|
1886
2329
|
const pmFromOption = options && options.packageManager && VALID_PACKAGE_MANAGERS.includes(options.packageManager);
|
|
1887
2330
|
const pm = pmFromOption
|
|
1888
2331
|
? getPackageManagerCommands({ agent: options.packageManager, command: options.packageManager })
|
|
@@ -1890,9 +2333,23 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1890
2333
|
? getPackageManagerCommands({ agent: config.packageManager, command: config.packageManager })
|
|
1891
2334
|
: resolvePackageManager(cwd);
|
|
1892
2335
|
const cliExample = pm.dlx('rizzo-css theme');
|
|
1893
|
-
const configTargetDir = framework === 'astro' ? 'public/css' : framework === 'svelte' ? 'static/css' : targetDirRaw;
|
|
2336
|
+
const configTargetDir = (framework === 'astro' || framework === 'react' || framework === 'vue') ? 'public/css' : framework === 'svelte' ? 'static/css' : targetDirRaw;
|
|
1894
2337
|
const configPath = join(cwd, RIZZO_CONFIG_FILE);
|
|
1895
2338
|
const hadConfig = existsSync(configPath);
|
|
2339
|
+
if (options.dryRun) {
|
|
2340
|
+
if (!hadConfig) options.plan.wouldWrite.push(RIZZO_CONFIG_FILE);
|
|
2341
|
+
options.plan.wouldWrite.push(RIZZO_SETUP_FILE);
|
|
2342
|
+
if (options.writeSnippet !== false) options.plan.wouldWrite.push(RIZZO_SNIPPET_FILE);
|
|
2343
|
+
if (options.writeReadme) options.plan.wouldWrite.push(SCAFFOLD_README_FILENAME);
|
|
2344
|
+
copyPackageLicense(cwd, copyOpts);
|
|
2345
|
+
console.log('\n Dry run — no files written.');
|
|
2346
|
+
console.log(' Would write (' + options.plan.wouldWrite.length + ' paths):');
|
|
2347
|
+
options.plan.wouldWrite.sort().forEach((p) => console.log(' ' + p));
|
|
2348
|
+
const setupMdContent = buildRizzoSetupMd(framework, { linkHref, theme, defaultDark, defaultLight, skippedFiles: addSkippedFiles.length > 0 ? addSkippedFiles : undefined });
|
|
2349
|
+
console.log('\n --- RIZZO-SETUP.md snippet (for skipped files) ---\n');
|
|
2350
|
+
console.log(setupMdContent);
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
1896
2353
|
if (!hadConfig) {
|
|
1897
2354
|
writeRizzoConfig(cwd, { targetDir: configTargetDir, framework, packageManager: pm.agent, theme });
|
|
1898
2355
|
}
|
|
@@ -1902,7 +2359,7 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1902
2359
|
copyPackageLicense(cwd);
|
|
1903
2360
|
const writeSnippet = options.writeSnippet !== false;
|
|
1904
2361
|
if (writeSnippet) {
|
|
1905
|
-
const where = framework === 'svelte' ? 'Root layout (e.g. src/app.html)' : framework === 'astro' ? 'Layout (e.g. src/layouts/Layout.astro)' : 'HTML or layout';
|
|
2362
|
+
const where = framework === 'svelte' ? 'Root layout (e.g. src/app.html)' : framework === 'astro' ? 'Layout (e.g. src/layouts/Layout.astro)' : (framework === 'react' || framework === 'vue') ? 'index.html or root component' : 'HTML or layout';
|
|
1906
2363
|
const snippetBody = [
|
|
1907
2364
|
'Add to ' + where + ':',
|
|
1908
2365
|
'',
|
|
@@ -1944,6 +2401,11 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1944
2401
|
console.log(' <link rel="stylesheet" href="' + linkHref + '" />');
|
|
1945
2402
|
console.log(' data-theme="' + theme + '" on <html> (themes: ' + cliExample + ')');
|
|
1946
2403
|
console.log(' Components: src/components/rizzo — import from there.');
|
|
2404
|
+
} else if (framework === 'react' || framework === 'vue') {
|
|
2405
|
+
console.log('\nIn index.html or root component, add:');
|
|
2406
|
+
console.log(' <link rel="stylesheet" href="' + linkHref + '" />');
|
|
2407
|
+
console.log(' data-theme="' + theme + '" on <html> (themes: ' + cliExample + ')');
|
|
2408
|
+
console.log(' Components: src/components/rizzo — import from there or @/components/rizzo.');
|
|
1947
2409
|
} else {
|
|
1948
2410
|
console.log('\nIn your HTML or layout, add:');
|
|
1949
2411
|
console.log(' <link rel="stylesheet" href="' + linkHref + '" />');
|
|
@@ -1955,7 +2417,7 @@ async function runAddToExisting(frameworkOverride, options) {
|
|
|
1955
2417
|
}
|
|
1956
2418
|
const addCmd = pm.add('rizzo-css');
|
|
1957
2419
|
console.log('\n To add the rizzo-css package to this project (optional): ' + addCmd);
|
|
1958
|
-
const shouldPromptAdd = (framework === 'astro' || framework === 'svelte') && options.noInstall !== true && options.installPackage !== true && process.stdin.isTTY;
|
|
2420
|
+
const shouldPromptAdd = (framework === 'astro' || framework === 'svelte' || framework === 'react' || framework === 'vue') && options.noInstall !== true && options.installPackage !== true && process.stdin.isTTY;
|
|
1959
2421
|
if (shouldPromptAdd) {
|
|
1960
2422
|
console.log('\n Add rizzo-css to your package.json so you can import components from the package.');
|
|
1961
2423
|
const answer = await question(' Run ' + addCmd + '? (Y/n) ');
|
|
@@ -2012,6 +2474,8 @@ async function cmdInit(argv) {
|
|
|
2012
2474
|
fullAllComponents = true;
|
|
2013
2475
|
if (framework === 'svelte') selectedComponents = [...SVELTE_COMPONENTS];
|
|
2014
2476
|
else if (framework === 'astro') selectedComponents = [...ASTRO_COMPONENTS];
|
|
2477
|
+
else if (framework === 'react') selectedComponents = [...REACT_COMPONENTS];
|
|
2478
|
+
else if (framework === 'vue') selectedComponents = [...VUE_COMPONENTS];
|
|
2015
2479
|
else selectedComponents = Object.keys(VANILLA_COMPONENT_SLUGS);
|
|
2016
2480
|
}
|
|
2017
2481
|
const projectDir = customProjectPath ? pathResolve(cwd, customProjectPath) : cwd;
|
|
@@ -2029,6 +2493,8 @@ async function cmdInit(argv) {
|
|
|
2029
2493
|
{ value: 'vanilla', label: 'Vanilla JS (HTML + CSS + same styles & components)', color: C.vanilla },
|
|
2030
2494
|
{ value: 'astro', label: 'Astro', color: C.astro },
|
|
2031
2495
|
{ value: 'svelte', label: 'Svelte', color: C.svelte },
|
|
2496
|
+
{ value: 'react', label: 'React', color: C.react },
|
|
2497
|
+
{ value: 'vue', label: 'Vue', color: C.vue },
|
|
2032
2498
|
],
|
|
2033
2499
|
'? Framework — all get the same CSS and component styles'
|
|
2034
2500
|
);
|
|
@@ -2074,7 +2540,7 @@ async function cmdInit(argv) {
|
|
|
2074
2540
|
fullAllComponents = false;
|
|
2075
2541
|
} else if (selectedVariation === 'full' && hasFullVariant(framework)) {
|
|
2076
2542
|
fullAllComponents = true;
|
|
2077
|
-
selectedComponents = framework === 'svelte' ? [...SVELTE_COMPONENTS] : framework === 'astro' ? [...ASTRO_COMPONENTS] : Object.keys(VANILLA_COMPONENT_SLUGS);
|
|
2543
|
+
selectedComponents = framework === 'svelte' ? [...SVELTE_COMPONENTS] : framework === 'astro' ? [...ASTRO_COMPONENTS] : framework === 'react' ? [...REACT_COMPONENTS] : framework === 'vue' ? [...VUE_COMPONENTS] : Object.keys(VANILLA_COMPONENT_SLUGS);
|
|
2078
2544
|
} else {
|
|
2079
2545
|
const addChoice = await selectMenu(
|
|
2080
2546
|
[
|
|
@@ -2085,9 +2551,9 @@ async function cmdInit(argv) {
|
|
|
2085
2551
|
);
|
|
2086
2552
|
fullAllComponents = (addChoice === 'all');
|
|
2087
2553
|
if (fullAllComponents) {
|
|
2088
|
-
selectedComponents = framework === 'svelte' ? [...SVELTE_COMPONENTS] : framework === 'astro' ? [...ASTRO_COMPONENTS] : Object.keys(VANILLA_COMPONENT_SLUGS);
|
|
2554
|
+
selectedComponents = framework === 'svelte' ? [...SVELTE_COMPONENTS] : framework === 'astro' ? [...ASTRO_COMPONENTS] : framework === 'react' ? [...REACT_COMPONENTS] : framework === 'vue' ? [...VUE_COMPONENTS] : Object.keys(VANILLA_COMPONENT_SLUGS);
|
|
2089
2555
|
} else {
|
|
2090
|
-
const componentList = framework === 'svelte' ? SVELTE_COMPONENTS : framework === 'astro' ? ASTRO_COMPONENTS : Object.keys(VANILLA_COMPONENT_SLUGS);
|
|
2556
|
+
const componentList = framework === 'svelte' ? SVELTE_COMPONENTS : framework === 'astro' ? ASTRO_COMPONENTS : framework === 'react' ? REACT_COMPONENTS : framework === 'vue' ? VUE_COMPONENTS : Object.keys(VANILLA_COMPONENT_SLUGS);
|
|
2091
2557
|
const recommended = RECOMMENDED_COMPONENTS.filter((c) => componentList.includes(c));
|
|
2092
2558
|
selectedComponents = await promptComponentChoice(componentList, framework, recommended);
|
|
2093
2559
|
}
|
|
@@ -2152,7 +2618,7 @@ async function cmdInit(argv) {
|
|
|
2152
2618
|
|
|
2153
2619
|
// Full gets all required dependencies so everything works; manual gets deps when user picks (see prompt labels).
|
|
2154
2620
|
let componentsToCopy = selectedComponents;
|
|
2155
|
-
if ((framework === 'astro' || framework === 'svelte') && selectedComponents.length > 0) {
|
|
2621
|
+
if ((framework === 'astro' || framework === 'svelte' || framework === 'react' || framework === 'vue') && selectedComponents.length > 0) {
|
|
2156
2622
|
componentsToCopy = expandWithDeps(framework, selectedComponents);
|
|
2157
2623
|
logAddedDeps(selectedComponents, componentsToCopy, framework);
|
|
2158
2624
|
}
|
|
@@ -2431,8 +2897,8 @@ async function cmdInit(argv) {
|
|
|
2431
2897
|
copyPackageLicense(projectDir);
|
|
2432
2898
|
copyVanillaGitignore(projectDir);
|
|
2433
2899
|
} else {
|
|
2434
|
-
// Add-to-existing (landing) or Vanilla create new with picked components: CSS, fonts, icons, sfx (framework-appropriate)
|
|
2435
|
-
if (framework === 'astro') {
|
|
2900
|
+
// Add-to-existing (landing) or Vanilla/React/Vue create new with picked components: CSS, fonts, icons, sfx (framework-appropriate)
|
|
2901
|
+
if (framework === 'astro' || framework === 'react' || framework === 'vue') {
|
|
2436
2902
|
copyRizzoCssAndFontsForAstro(projectDir, cssSource);
|
|
2437
2903
|
cssTarget = join(projectDir, 'public', 'css', 'rizzo.min.css');
|
|
2438
2904
|
} else if (framework === 'svelte') {
|
|
@@ -2452,7 +2918,7 @@ async function cmdInit(argv) {
|
|
|
2452
2918
|
if (statSync(cssTarget).size < 5000) {
|
|
2453
2919
|
console.warn('\nWarning: rizzo.min.css is very small. From repo root run: pnpm build:css');
|
|
2454
2920
|
}
|
|
2455
|
-
const linkHrefForSetup = framework === 'vanilla' ? 'css/rizzo.min.css' : '/css/rizzo.min.css';
|
|
2921
|
+
const linkHrefForSetup = (framework === 'vanilla') ? 'css/rizzo.min.css' : '/css/rizzo.min.css';
|
|
2456
2922
|
const setupMdContent = buildRizzoSetupMd(framework, {
|
|
2457
2923
|
linkHref: linkHrefForSetup,
|
|
2458
2924
|
theme,
|
|
@@ -2477,10 +2943,19 @@ async function cmdInit(argv) {
|
|
|
2477
2943
|
mkdirSync(join(projectDir, 'static'), { recursive: true });
|
|
2478
2944
|
indexPath = svelteIndex;
|
|
2479
2945
|
writeFileSync(svelteIndex, landingHtmlForAdd, 'utf8');
|
|
2946
|
+
} else if ((framework === 'react' || framework === 'vue') && !existsSync(join(projectDir, 'public', 'index.html'))) {
|
|
2947
|
+
mkdirSync(join(projectDir, 'public'), { recursive: true });
|
|
2948
|
+
indexPath = join(projectDir, 'public', 'index.html');
|
|
2949
|
+
writeFileSync(indexPath, landingHtmlForAdd, 'utf8');
|
|
2480
2950
|
}
|
|
2481
2951
|
writeFileSync(join(projectDir, RIZZO_SETUP_FILE), setupMdContent, 'utf8');
|
|
2482
2952
|
copyPackageLicense(projectDir);
|
|
2483
2953
|
if (framework === 'vanilla') copyVanillaGitignore(projectDir);
|
|
2954
|
+
if ((framework === 'react' || framework === 'vue') && componentsToCopy.length > 0) {
|
|
2955
|
+
copyRizzoIcons(projectDir, framework);
|
|
2956
|
+
if (framework === 'react') copyReactComponents(projectDir, componentsToCopy);
|
|
2957
|
+
else copyVueComponents(projectDir, componentsToCopy);
|
|
2958
|
+
}
|
|
2484
2959
|
} else if (framework === 'vanilla' && selectedTemplate === 'full' && !fullAllComponents) {
|
|
2485
2960
|
indexPath = join(projectDir, 'index.html');
|
|
2486
2961
|
if (!existsSync(indexPath)) {
|
|
@@ -2519,6 +2994,18 @@ async function cmdInit(argv) {
|
|
|
2519
2994
|
else writeFileSync(join(projectDir, RIZZO_SETUP_FILE), buildRizzoSetupMd(framework, { linkHref: '/css/rizzo.min.css', theme, defaultDark, defaultLight, skippedFiles: [{ relativePath: 'static/index.html', content: landingHtml }] }), 'utf8');
|
|
2520
2995
|
writeFileSync(join(projectDir, SCAFFOLD_README_FILENAME), FALLBACK_MINIMAL_README, 'utf8');
|
|
2521
2996
|
copyPackageLicense(projectDir);
|
|
2997
|
+
} else if (framework === 'react' || framework === 'vue') {
|
|
2998
|
+
indexPath = join(projectDir, 'public', 'index.html');
|
|
2999
|
+
mkdirSync(join(projectDir, 'public'), { recursive: true });
|
|
3000
|
+
if (!existsSync(indexPath)) writeFileSync(indexPath, landingHtml, 'utf8');
|
|
3001
|
+
else writeFileSync(join(projectDir, RIZZO_SETUP_FILE), buildRizzoSetupMd(framework, { linkHref: '/css/rizzo.min.css', theme, defaultDark, defaultLight, skippedFiles: [{ relativePath: 'public/index.html', content: landingHtml }] }), 'utf8');
|
|
3002
|
+
writeFileSync(join(projectDir, SCAFFOLD_README_FILENAME), FALLBACK_MINIMAL_README, 'utf8');
|
|
3003
|
+
if (componentsToCopy.length > 0) {
|
|
3004
|
+
copyRizzoIcons(projectDir, framework);
|
|
3005
|
+
if (framework === 'react') copyReactComponents(projectDir, componentsToCopy);
|
|
3006
|
+
else copyVueComponents(projectDir, componentsToCopy);
|
|
3007
|
+
}
|
|
3008
|
+
copyPackageLicense(projectDir);
|
|
2522
3009
|
} else {
|
|
2523
3010
|
copyPackageLicense(projectDir);
|
|
2524
3011
|
}
|
|
@@ -2569,12 +3056,13 @@ async function cmdInit(argv) {
|
|
|
2569
3056
|
}
|
|
2570
3057
|
|
|
2571
3058
|
// Package manager install: only for Astro/Svelte (Vanilla has no package.json)
|
|
3059
|
+
const installCmd = pm.install + (hasFlag(argv, '--offline') ? ' --offline' : '');
|
|
2572
3060
|
if (runInstallAfterScaffold && !noInstall && hasPackageJson) {
|
|
2573
3061
|
const dirLabel = projectDir !== cwd ? ' in ' + pathRelative(cwd, projectDir) : ' (current directory)';
|
|
2574
|
-
console.log('\n Running' + dirLabel + ': ' +
|
|
2575
|
-
const code = runInDir(projectDir,
|
|
3062
|
+
console.log('\n Running' + dirLabel + ': ' + installCmd);
|
|
3063
|
+
const code = runInDir(projectDir, installCmd);
|
|
2576
3064
|
if (code !== 0) {
|
|
2577
|
-
console.error('\n Install failed (exit ' + code + '). Run manually: ' + runPrefix +
|
|
3065
|
+
console.error('\n Install failed (exit ' + code + '). Run manually: ' + runPrefix + installCmd);
|
|
2578
3066
|
} else {
|
|
2579
3067
|
console.log(' ✓ Dependencies installed.');
|
|
2580
3068
|
}
|
|
@@ -2582,15 +3070,15 @@ async function cmdInit(argv) {
|
|
|
2582
3070
|
const shouldRun = await confirmRunInstall(pm);
|
|
2583
3071
|
if (shouldRun) {
|
|
2584
3072
|
const dirLabel = projectDir !== cwd ? ' in ' + pathRelative(cwd, projectDir) : ' here';
|
|
2585
|
-
console.log('\n Running' + dirLabel + ': ' +
|
|
2586
|
-
const code = runInDir(projectDir,
|
|
3073
|
+
console.log('\n Running' + dirLabel + ': ' + installCmd);
|
|
3074
|
+
const code = runInDir(projectDir, installCmd);
|
|
2587
3075
|
if (code !== 0) {
|
|
2588
|
-
console.error('\n Install failed (exit ' + code + '). Run manually: ' + runPrefix +
|
|
3076
|
+
console.error('\n Install failed (exit ' + code + '). Run manually: ' + runPrefix + installCmd);
|
|
2589
3077
|
} else {
|
|
2590
3078
|
console.log(' ✓ Dependencies installed.');
|
|
2591
3079
|
}
|
|
2592
3080
|
} else {
|
|
2593
|
-
console.log('\n Skipped. When ready, run: ' + runPrefix +
|
|
3081
|
+
console.log('\n Skipped. When ready, run: ' + runPrefix + installCmd);
|
|
2594
3082
|
}
|
|
2595
3083
|
}
|
|
2596
3084
|
|
|
@@ -2621,6 +3109,7 @@ async function cmdInit(argv) {
|
|
|
2621
3109
|
: 'open index.html or serve the folder (e.g. npx serve .)';
|
|
2622
3110
|
console.log('\n Next: ' + vanillaNext);
|
|
2623
3111
|
}
|
|
3112
|
+
await checkNewerVersion(argv).catch(() => {});
|
|
2624
3113
|
console.log('\n Docs: https://rizzo-css.vercel.app\n');
|
|
2625
3114
|
}
|
|
2626
3115
|
|