shru-design-system 0.1.5 → 0.1.8

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/scripts/init.js CHANGED
@@ -272,6 +272,129 @@ function createCSSFile() {
272
272
  log('Created src/index.css', 'green');
273
273
  }
274
274
 
275
+ /**
276
+ * Get the library's token files directory
277
+ * When running from node_modules, this will be scripts/tokens
278
+ */
279
+ function getLibraryTokensPath() {
280
+ // __dirname is scripts/ when running from node_modules
281
+ // So scripts/tokens is at __dirname/tokens
282
+ const libraryTokensPath = path.join(__dirname, 'tokens');
283
+
284
+ // Check if tokens exist in scripts/tokens (published package)
285
+ if (fs.existsSync(libraryTokensPath)) {
286
+ return libraryTokensPath;
287
+ }
288
+
289
+ return null;
290
+ }
291
+
292
+ /**
293
+ * Recursively copy directory, preserving user files
294
+ * Only copies/updates files that were created by the library
295
+ */
296
+ function copyDirectory(src, dest) {
297
+ if (!fs.existsSync(src)) {
298
+ return false;
299
+ }
300
+
301
+ // Create destination directory
302
+ if (!fs.existsSync(dest)) {
303
+ fs.mkdirSync(dest, { recursive: true });
304
+ }
305
+
306
+ const entries = fs.readdirSync(src, { withFileTypes: true });
307
+ let copiedCount = 0;
308
+
309
+ for (const entry of entries) {
310
+ const srcPath = path.join(src, entry.name);
311
+ const destPath = path.join(dest, entry.name);
312
+
313
+ if (entry.isDirectory()) {
314
+ const subCopied = copyDirectory(srcPath, destPath);
315
+ if (subCopied) copiedCount += subCopied;
316
+ } else if (entry.isFile() && entry.name.endsWith('.json')) {
317
+ // For JSON files, check if it's a library file or user file
318
+ try {
319
+ const srcContent = JSON.parse(fs.readFileSync(srcPath, 'utf8'));
320
+ const isLibraryFile = srcContent._createdBy && srcContent._createdBy.includes(LIBRARY_NAME.split(' ')[0]);
321
+
322
+ if (isLibraryFile) {
323
+ // Always update library files
324
+ fs.copyFileSync(srcPath, destPath);
325
+ copiedCount++;
326
+ } else if (!fs.existsSync(destPath)) {
327
+ // New library file that doesn't exist in dest
328
+ fs.copyFileSync(srcPath, destPath);
329
+ copiedCount++;
330
+ }
331
+ // If dest exists and is not a library file, preserve it (user's custom file)
332
+ } catch (e) {
333
+ // If JSON parsing fails, just copy it
334
+ if (!fs.existsSync(destPath)) {
335
+ fs.copyFileSync(srcPath, destPath);
336
+ copiedCount++;
337
+ }
338
+ }
339
+ } else {
340
+ // Non-JSON files: copy if doesn't exist
341
+ if (!fs.existsSync(destPath)) {
342
+ fs.copyFileSync(srcPath, destPath);
343
+ copiedCount++;
344
+ }
345
+ }
346
+ }
347
+
348
+ return copiedCount;
349
+ }
350
+
351
+ /**
352
+ * Read a token file from the library
353
+ */
354
+ function readLibraryTokenFile(relativePath) {
355
+ const libraryTokensPath = getLibraryTokensPath();
356
+ if (!libraryTokensPath) {
357
+ return null;
358
+ }
359
+
360
+ const filePath = path.join(libraryTokensPath, relativePath);
361
+ if (!fs.existsSync(filePath)) {
362
+ return null;
363
+ }
364
+
365
+ try {
366
+ const content = fs.readFileSync(filePath, 'utf8');
367
+ return JSON.parse(content);
368
+ } catch (e) {
369
+ return null;
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Migrate old token structure to new structure
375
+ */
376
+ function migrateTokenStructure(data) {
377
+ if (!data || typeof data !== 'object') {
378
+ return data;
379
+ }
380
+
381
+ const migrated = JSON.parse(JSON.stringify(data)); // Deep clone
382
+
383
+ // Migrate typography.font → font
384
+ if (migrated.typography && migrated.typography.font) {
385
+ migrated.font = migrated.typography.font;
386
+ delete migrated.typography;
387
+ }
388
+
389
+ // Migrate shape.radius → radius
390
+ if (migrated.shape && migrated.shape.radius) {
391
+ migrated.radius = migrated.shape.radius;
392
+ delete migrated.shape;
393
+ }
394
+
395
+ return migrated;
396
+ }
397
+
275
398
  function createTokenFiles() {
276
399
  const publicDir = path.join(process.cwd(), 'public');
277
400
  const tokensDir = path.join(publicDir, 'tokens');
@@ -288,381 +411,83 @@ function createTokenFiles() {
288
411
  fs.mkdirSync(themesDir, { recursive: true });
289
412
  }
290
413
 
291
- // Create base.json (or update if old structure exists)
292
- const basePath = path.join(tokensDir, 'base.json');
293
- let needsUpdate = false;
294
- if (fs.existsSync(basePath)) {
295
- try {
296
- const existing = JSON.parse(fs.readFileSync(basePath, 'utf8'));
297
- // Check if it has old structure (typography.font or shape.radius instead of font/radius)
298
- if ((existing.typography && existing.typography.font) || (existing.shape && existing.shape.radius)) {
299
- needsUpdate = true;
300
- log('Found old token structure in base.json, updating...', 'yellow');
301
- }
302
- } catch (e) {
303
- needsUpdate = true;
414
+ // Try to copy token files from library
415
+ const libraryTokensPath = getLibraryTokensPath();
416
+ if (libraryTokensPath) {
417
+ log(`Copying token files from library...`, 'blue');
418
+ const copiedCount = copyDirectory(libraryTokensPath, tokensDir);
419
+ if (copiedCount > 0) {
420
+ log(`Token files copied from library successfully! (${copiedCount} files)`, 'green');
421
+ } else {
422
+ log('All token files already exist. Checking for updates...', 'green');
304
423
  }
305
- }
306
- if (!fs.existsSync(basePath) || needsUpdate) {
307
- const baseJson = {
308
- "_createdBy": LIBRARY_NAME,
309
- "color": {
310
- "primary": "{palette.blue.500}",
311
- "primary-hover": "{palette.blue.600}",
312
- "primary-foreground": "{palette.white}",
313
- "secondary": "{palette.gray.100}",
314
- "secondary-foreground": "{palette.gray.900}",
315
- "background": "{palette.white}",
316
- "foreground": "{palette.gray.900}",
317
- "card": "{palette.white}",
318
- "card-foreground": "{palette.gray.900}",
319
- "popover": "{palette.white}",
320
- "popover-foreground": "{palette.gray.900}",
321
- "muted": "{palette.gray.100}",
322
- "muted-foreground": "{palette.gray.500}",
323
- "accent": "{palette.gray.100}",
324
- "accent-foreground": "{palette.gray.900}",
325
- "destructive": "{palette.red.500}",
326
- "destructive-foreground": "{palette.white}",
327
- "border": "{palette.gray.200}",
328
- "input": "{palette.gray.200}",
329
- "ring": "{palette.gray.400}",
330
- "skeleton": "{palette.gray.200}"
331
- },
332
- "spacing": {
333
- "component": {
334
- "xs": "0.25rem",
335
- "sm": "0.5rem",
336
- "md": "1rem",
337
- "lg": "1.5rem",
338
- "xl": "2rem"
339
- },
340
- "base": "0.25rem"
341
- },
342
- "font": {
343
- "body": "var(--font-sans)",
344
- "sans": "var(--font-sans)",
345
- "mono": "var(--font-mono)"
346
- },
347
- "radius": {
348
- "button": "0.375rem",
349
- "card": "0.5rem",
350
- "input": "0.375rem"
351
- }
352
- };
353
- fs.writeFileSync(basePath, JSON.stringify(baseJson, null, 2));
354
- log('Created/Updated public/tokens/base.json', 'green');
355
424
  } else {
356
- log('public/tokens/base.json already exists with correct structure. Skipping...', 'green');
357
- }
358
-
359
- // Create palettes.json
360
- const palettesPath = path.join(tokensDir, 'palettes.json');
361
- if (!fs.existsSync(palettesPath)) {
362
- const palettesJson = {
363
- "_createdBy": LIBRARY_NAME,
364
- "palette": {
365
- "white": "#ffffff",
366
- "black": "#000000",
367
- "transparent": "transparent",
368
- "gray": {
369
- "50": "#f9fafb",
370
- "100": "#f3f4f6",
371
- "200": "#e5e7eb",
372
- "300": "#d1d5db",
373
- "400": "#9ca3af",
374
- "500": "#6b7280",
375
- "600": "#4b5563",
376
- "700": "#374151",
377
- "800": "#1f2937",
378
- "900": "#111827",
379
- "950": "#030712"
380
- },
381
- "blue": {
382
- "50": "#eff6ff",
383
- "100": "#dbeafe",
384
- "200": "#bfdbfe",
385
- "300": "#93c5fd",
386
- "400": "#60a5fa",
387
- "500": "#3b82f6",
388
- "600": "#2563eb",
389
- "700": "#1d4ed8",
390
- "800": "#1e40af",
391
- "900": "#1e3a8a",
392
- "950": "#172554"
393
- },
394
- "red": {
395
- "50": "#fef2f2",
396
- "100": "#fee2e2",
397
- "200": "#fecaca",
398
- "300": "#fca5a5",
399
- "400": "#f87171",
400
- "500": "#ef4444",
401
- "600": "#dc2626",
402
- "700": "#b91c1c",
403
- "800": "#991b1b",
404
- "900": "#7f1d1d",
405
- "950": "#450a0a"
406
- }
407
- }
408
- };
409
- fs.writeFileSync(palettesPath, JSON.stringify(palettesJson, null, 2));
410
- log('Created public/tokens/palettes.json', 'green');
411
- } else {
412
- log('public/tokens/palettes.json already exists. Skipping...', 'yellow');
413
- }
414
-
415
- // Create theme directories and files
416
- const themeCategories = ['color', 'typography', 'shape', 'density', 'animation', 'custom'];
417
-
418
- themeCategories.forEach(category => {
419
- const categoryDir = path.join(themesDir, category);
420
- if (!fs.existsSync(categoryDir)) {
421
- fs.mkdirSync(categoryDir, { recursive: true });
422
- }
423
- });
425
+ log('Library token files not found, creating default token files...', 'yellow');
426
+ }
427
+
428
+ // Check and migrate old token structures
429
+ const tokenFilesToCheck = [
430
+ 'base.json',
431
+ 'palettes.json',
432
+ 'themes/color/white.json',
433
+ 'themes/color/dark.json',
434
+ 'themes/typography/sans.json',
435
+ 'themes/typography/serif.json',
436
+ 'themes/shape/smooth.json',
437
+ 'themes/shape/sharp.json',
438
+ 'themes/density/comfortable.json',
439
+ 'themes/density/compact.json',
440
+ 'themes/animation/gentle.json',
441
+ 'themes/animation/brisk.json',
442
+ 'themes/custom/brand.json',
443
+ 'themes/custom/minimal.json'
444
+ ];
424
445
 
425
- // Create color/white.json
426
- const whiteThemePath = path.join(themesDir, 'color', 'white.json');
427
- if (!fs.existsSync(whiteThemePath)) {
428
- const whiteTheme = {
429
- "_createdBy": LIBRARY_NAME,
430
- "color": {
431
- "primary": "{palette.blue.500}",
432
- "primary-foreground": "{palette.white}",
433
- "background": "{palette.white}",
434
- "foreground": "{palette.gray.900}",
435
- "card": "{palette.white}",
436
- "card-foreground": "{palette.gray.900}",
437
- "popover": "{palette.white}",
438
- "popover-foreground": "{palette.gray.900}",
439
- "secondary": "{palette.gray.100}",
440
- "secondary-foreground": "{palette.gray.900}",
441
- "muted": "{palette.gray.100}",
442
- "muted-foreground": "{palette.gray.500}",
443
- "accent": "{palette.gray.100}",
444
- "accent-foreground": "{palette.gray.900}",
445
- "destructive": "{palette.red.500}",
446
- "destructive-foreground": "{palette.white}",
447
- "border": "{palette.gray.200}",
448
- "input": "{palette.gray.200}",
449
- "ring": "{palette.gray.400}",
450
- "skeleton": "{palette.gray.200}"
451
- }
452
- };
453
- fs.writeFileSync(whiteThemePath, JSON.stringify(whiteTheme, null, 2));
454
- log('Created public/tokens/themes/color/white.json', 'green');
455
- }
456
-
457
- // Create color/dark.json
458
- const darkThemePath = path.join(themesDir, 'color', 'dark.json');
459
- if (!fs.existsSync(darkThemePath)) {
460
- const darkTheme = {
461
- "_createdBy": LIBRARY_NAME,
462
- "color": {
463
- "primary": "{palette.blue.400}",
464
- "primary-foreground": "{palette.gray.900}",
465
- "background": "{palette.gray.900}",
466
- "foreground": "{palette.gray.50}",
467
- "card": "{palette.gray.800}",
468
- "card-foreground": "{palette.gray.50}",
469
- "popover": "{palette.gray.800}",
470
- "popover-foreground": "{palette.gray.50}",
471
- "secondary": "{palette.gray.800}",
472
- "secondary-foreground": "{palette.gray.50}",
473
- "muted": "{palette.gray.800}",
474
- "muted-foreground": "{palette.gray.400}",
475
- "accent": "{palette.gray.800}",
476
- "accent-foreground": "{palette.gray.50}",
477
- "destructive": "{palette.red.500}",
478
- "destructive-foreground": "{palette.white}",
479
- "border": "{palette.gray.700}",
480
- "input": "{palette.gray.700}",
481
- "ring": "{palette.gray.600}",
482
- "skeleton": "{palette.gray.700}"
483
- }
484
- };
485
- fs.writeFileSync(darkThemePath, JSON.stringify(darkTheme, null, 2));
486
- log('Created public/tokens/themes/color/dark.json', 'green');
487
- }
488
-
489
- // Create typography/sans.json (or update if old structure exists)
490
- const sansThemePath = path.join(themesDir, 'typography', 'sans.json');
491
- let needsUpdate = false;
492
- if (fs.existsSync(sansThemePath)) {
493
- try {
494
- const existing = JSON.parse(fs.readFileSync(sansThemePath, 'utf8'));
495
- // Check if it has old structure (typography.font instead of font)
496
- if (existing.typography && existing.typography.font) {
497
- needsUpdate = true;
498
- log('Found old token structure in typography/sans.json, updating...', 'yellow');
499
- }
500
- } catch (e) {
501
- needsUpdate = true;
502
- }
503
- }
504
- if (!fs.existsSync(sansThemePath) || needsUpdate) {
505
- const sansTheme = {
506
- "_createdBy": LIBRARY_NAME,
507
- "font": {
508
- "body": "system-ui, -apple-system, sans-serif",
509
- "sans": "system-ui, -apple-system, sans-serif"
510
- }
511
- };
512
- fs.writeFileSync(sansThemePath, JSON.stringify(sansTheme, null, 2));
513
- log('Created/Updated public/tokens/themes/typography/sans.json', 'green');
514
- }
515
-
516
- // Create typography/serif.json (or update if old structure exists)
517
- const serifThemePath = path.join(themesDir, 'typography', 'serif.json');
518
- needsUpdate = false;
519
- if (fs.existsSync(serifThemePath)) {
520
- try {
521
- const existing = JSON.parse(fs.readFileSync(serifThemePath, 'utf8'));
522
- if (existing.typography && existing.typography.font) {
523
- needsUpdate = true;
524
- log('Found old token structure in typography/serif.json, updating...', 'yellow');
525
- }
526
- } catch (e) {
527
- needsUpdate = true;
528
- }
529
- }
530
- if (!fs.existsSync(serifThemePath) || needsUpdate) {
531
- const serifTheme = {
532
- "_createdBy": LIBRARY_NAME,
533
- "font": {
534
- "body": "Georgia, serif",
535
- "sans": "Georgia, serif"
536
- }
537
- };
538
- fs.writeFileSync(serifThemePath, JSON.stringify(serifTheme, null, 2));
539
- log('Created/Updated public/tokens/themes/typography/serif.json', 'green');
540
- }
541
-
542
- // Create shape/smooth.json (or update if old structure exists)
543
- const smoothThemePath = path.join(themesDir, 'shape', 'smooth.json');
544
- needsUpdate = false;
545
- if (fs.existsSync(smoothThemePath)) {
546
- try {
547
- const existing = JSON.parse(fs.readFileSync(smoothThemePath, 'utf8'));
548
- if (existing.shape && existing.shape.radius) {
549
- needsUpdate = true;
550
- log('Found old token structure in shape/smooth.json, updating...', 'yellow');
551
- }
552
- } catch (e) {
553
- needsUpdate = true;
554
- }
555
- }
556
- if (!fs.existsSync(smoothThemePath) || needsUpdate) {
557
- const smoothTheme = {
558
- "_createdBy": LIBRARY_NAME,
559
- "radius": {
560
- "button": "0.5rem",
561
- "card": "0.75rem",
562
- "input": "0.5rem"
563
- }
564
- };
565
- fs.writeFileSync(smoothThemePath, JSON.stringify(smoothTheme, null, 2));
566
- log('Created/Updated public/tokens/themes/shape/smooth.json', 'green');
567
- }
568
-
569
- // Create shape/sharp.json (or update if old structure exists)
570
- const sharpThemePath = path.join(themesDir, 'shape', 'sharp.json');
571
- needsUpdate = false;
572
- if (fs.existsSync(sharpThemePath)) {
573
- try {
574
- const existing = JSON.parse(fs.readFileSync(sharpThemePath, 'utf8'));
575
- if (existing.shape && existing.shape.radius) {
576
- needsUpdate = true;
577
- log('Found old token structure in shape/sharp.json, updating...', 'yellow');
578
- }
579
- } catch (e) {
580
- needsUpdate = true;
581
- }
582
- }
583
- if (!fs.existsSync(sharpThemePath) || needsUpdate) {
584
- const sharpTheme = {
585
- "_createdBy": LIBRARY_NAME,
586
- "radius": {
587
- "button": "0",
588
- "card": "0",
589
- "input": "0"
590
- }
591
- };
592
- fs.writeFileSync(sharpThemePath, JSON.stringify(sharpTheme, null, 2));
593
- log('Created/Updated public/tokens/themes/shape/sharp.json', 'green');
594
- }
595
-
596
- // Create density/comfortable.json
597
- const comfortableThemePath = path.join(themesDir, 'density', 'comfortable.json');
598
- if (!fs.existsSync(comfortableThemePath)) {
599
- const comfortableTheme = {
600
- "_createdBy": LIBRARY_NAME,
601
- "spacing": {
602
- "component": {
603
- "xs": "0.5rem",
604
- "sm": "0.75rem",
605
- "md": "1.25rem",
606
- "lg": "2rem",
607
- "xl": "2.5rem"
446
+ tokenFilesToCheck.forEach(relativePath => {
447
+ const destPath = path.join(tokensDir, relativePath);
448
+
449
+ // If file exists, check if it needs migration
450
+ if (fs.existsSync(destPath)) {
451
+ try {
452
+ const existing = JSON.parse(fs.readFileSync(destPath, 'utf8'));
453
+ const migrated = migrateTokenStructure(existing);
454
+
455
+ // Check if migration changed the structure
456
+ if (JSON.stringify(existing) !== JSON.stringify(migrated)) {
457
+ migrated._createdBy = LIBRARY_NAME;
458
+ fs.writeFileSync(destPath, JSON.stringify(migrated, null, 2));
459
+ log(`Migrated ${relativePath} to new structure`, 'yellow');
608
460
  }
609
- }
610
- };
611
- fs.writeFileSync(comfortableThemePath, JSON.stringify(comfortableTheme, null, 2));
612
- log('Created public/tokens/themes/density/comfortable.json', 'green');
613
- }
614
-
615
- // Create density/compact.json
616
- const compactThemePath = path.join(themesDir, 'density', 'compact.json');
617
- if (!fs.existsSync(compactThemePath)) {
618
- const compactTheme = {
619
- "_createdBy": LIBRARY_NAME,
620
- "spacing": {
621
- "component": {
622
- "xs": "0.25rem",
623
- "sm": "0.5rem",
624
- "md": "0.75rem",
625
- "lg": "1rem",
626
- "xl": "1.5rem"
461
+ } catch (e) {
462
+ // If file is corrupted, try to read from library
463
+ const libraryData = readLibraryTokenFile(relativePath);
464
+ if (libraryData) {
465
+ libraryData._createdBy = LIBRARY_NAME;
466
+ fs.writeFileSync(destPath, JSON.stringify(libraryData, null, 2));
467
+ log(`Restored ${relativePath} from library`, 'green');
627
468
  }
628
469
  }
629
- };
630
- fs.writeFileSync(compactThemePath, JSON.stringify(compactTheme, null, 2));
631
- log('Created public/tokens/themes/density/compact.json', 'green');
632
- }
633
-
634
- // Create animation/gentle.json
635
- const gentleThemePath = path.join(themesDir, 'animation', 'gentle.json');
636
- if (!fs.existsSync(gentleThemePath)) {
637
- const gentleTheme = {
638
- "_createdBy": LIBRARY_NAME,
639
- "animation": {
640
- "duration": {
641
- "fast": "150ms",
642
- "normal": "300ms",
643
- "slow": "500ms"
470
+ } else {
471
+ // File doesn't exist, try to read from library
472
+ const libraryData = readLibraryTokenFile(relativePath);
473
+ if (libraryData) {
474
+ // Ensure directory exists
475
+ const destDir = path.dirname(destPath);
476
+ if (!fs.existsSync(destDir)) {
477
+ fs.mkdirSync(destDir, { recursive: true });
644
478
  }
479
+
480
+ libraryData._createdBy = LIBRARY_NAME;
481
+ fs.writeFileSync(destPath, JSON.stringify(libraryData, null, 2));
482
+ log(`Created ${relativePath} from library`, 'green');
645
483
  }
646
- };
647
- fs.writeFileSync(gentleThemePath, JSON.stringify(gentleTheme, null, 2));
648
- log('Created public/tokens/themes/animation/gentle.json', 'green');
649
- }
650
-
651
- // Create animation/brisk.json
652
- const briskThemePath = path.join(themesDir, 'animation', 'brisk.json');
653
- if (!fs.existsSync(briskThemePath)) {
654
- const briskTheme = {
655
- "_createdBy": LIBRARY_NAME,
656
- "animation": {
657
- "duration": {
658
- "fast": "100ms",
659
- "normal": "200ms",
660
- "slow": "300ms"
661
- }
662
- }
663
- };
664
- fs.writeFileSync(briskThemePath, JSON.stringify(briskTheme, null, 2));
665
- log('Created public/tokens/themes/animation/brisk.json', 'green');
484
+ }
485
+ });
486
+
487
+ // If library tokens not found, we can't create files (user needs to install package properly)
488
+ if (!libraryTokensPath) {
489
+ log('Warning: Could not find library token files. Make sure the package is properly installed.', 'yellow');
490
+ return;
666
491
  }
667
492
  }
668
493