weloop-kosign 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,23 +4,18 @@ A modular component library built with Next.js, similar to shadcn/ui. Install on
4
4
 
5
5
  ## Installation
6
6
 
7
- First, make sure you have the base dependencies installed:
8
-
9
- ```bash
10
- npm install clsx tailwind-merge
11
- ```
12
-
13
- Then install components using the CLI:
7
+ Install components directly - the CLI handles everything automatically:
14
8
 
15
9
  ```bash
16
10
  npx weloop-kosign@latest add button
17
11
  ```
18
12
 
19
- To install the base CSS styles:
13
+ This will automatically:
20
14
 
21
- ```bash
22
- npx weloop-kosign@latest css
23
- ```
15
+ - Install base dependencies (`clsx`, `tailwind-merge`, `tw-animate-css`)
16
+ - Create `components.json` configuration file
17
+ - Install CSS styles with animations
18
+ - Install the component and its dependencies
24
19
 
25
20
  ## Usage
26
21
 
@@ -34,7 +29,7 @@ export function MyComponent() {
34
29
  }
35
30
  ```
36
31
 
37
- The CLI will automatically install any required dependencies for each component. If something's missing, it'll let you know.
32
+ The CLI automatically handles all dependencies, configuration, and CSS setup. No manual setup required.
38
33
 
39
34
  ## Available Commands
40
35
 
@@ -50,7 +45,7 @@ See all available components:
50
45
  npx weloop-kosign@latest list
51
46
  ```
52
47
 
53
- Install or update CSS styles:
48
+ Install or update CSS styles (optional - CSS is auto-installed with components):
54
49
 
55
50
  ```bash
56
51
  npx weloop-kosign@latest css
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weloop-kosign",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "CLI tool for installing Weloop UI components",
5
5
  "keywords": [
6
6
  "weloop",
@@ -405,6 +405,28 @@ function hasWeloopStyles(content) {
405
405
  content.includes('--system-200');
406
406
  }
407
407
 
408
+ function removeDuplicateTwAnimateImports(cssContent) {
409
+ const twAnimatePattern = /@import\s+["']tw-animate-css["'];?\s*\n?/g;
410
+ const matches = cssContent.match(twAnimatePattern);
411
+
412
+ if (matches && matches.length > 1) {
413
+ // Remove all occurrences
414
+ let cleaned = cssContent.replace(twAnimatePattern, '');
415
+ // Add it back once after tailwindcss import
416
+ if (cleaned.includes('@import "tailwindcss"') || cleaned.includes("@import 'tailwindcss'")) {
417
+ cleaned = cleaned.replace(
418
+ /(@import\s+["']tailwindcss["'];?\s*\n?)/,
419
+ '$1@import "tw-animate-css";\n'
420
+ );
421
+ } else {
422
+ cleaned = '@import "tw-animate-css";\n' + cleaned;
423
+ }
424
+ return cleaned;
425
+ }
426
+
427
+ return cssContent;
428
+ }
429
+
408
430
  function processTwAnimateImport(cssContent, hasTwAnimate, forceUpdate = false) {
409
431
  const twAnimatePattern = /@import\s+["']tw-animate-css["'];?\s*\n?/g;
410
432
  let processed = cssContent;
@@ -423,7 +445,10 @@ function processTwAnimateImport(cssContent, hasTwAnimate, forceUpdate = false) {
423
445
  }
424
446
  }
425
447
  } else {
426
- // Ensure import exists if package is installed
448
+ // Remove any duplicates first
449
+ processed = removeDuplicateTwAnimateImports(processed);
450
+
451
+ // Ensure import exists if package is installed (only if not already present)
427
452
  if (!processed.match(/@import\s+["']tw-animate-css["'];?\s*\n?/)) {
428
453
  if (processed.includes('@import "tailwindcss"') || processed.includes("@import 'tailwindcss'")) {
429
454
  processed = processed.replace(
@@ -444,10 +469,27 @@ function removeTailwindImport(cssContent) {
444
469
  }
445
470
 
446
471
  function ensureTwAnimateImport(cssContent, hasTwAnimate) {
447
- if (!hasTwAnimate || cssContent.includes('@import "tw-animate-css"')) {
472
+ if (!hasTwAnimate) {
448
473
  return cssContent;
449
474
  }
450
- return '@import "tw-animate-css";\n' + cssContent;
475
+
476
+ // Remove duplicates first
477
+ let cleaned = removeDuplicateTwAnimateImports(cssContent);
478
+
479
+ // Check if import already exists
480
+ if (cleaned.match(/@import\s+["']tw-animate-css["'];?\s*\n?/)) {
481
+ return cleaned;
482
+ }
483
+
484
+ // Add import after tailwindcss if it exists
485
+ if (cleaned.includes('@import "tailwindcss"') || cleaned.includes("@import 'tailwindcss'")) {
486
+ return cleaned.replace(
487
+ /(@import\s+["']tailwindcss["'];?\s*\n?)/,
488
+ '$1@import "tw-animate-css";\n'
489
+ );
490
+ }
491
+
492
+ return '@import "tw-animate-css";\n' + cleaned;
451
493
  }
452
494
 
453
495
  function mergeCSSWithTailwind(existing, weloopStyles, hasTwAnimate) {
@@ -459,8 +501,12 @@ function mergeCSSWithTailwind(existing, weloopStyles, hasTwAnimate) {
459
501
  const beforeTailwind = existing.substring(0, existing.indexOf(tailwindMatch[0]));
460
502
  const afterTailwind = existing.substring(existing.indexOf(tailwindMatch[0]) + tailwindMatch[0].length);
461
503
 
504
+ // Check if tw-animate-css import already exists in existing or weloopStyles
505
+ const hasTwAnimateInExisting = existing.match(/@import\s+["']tw-animate-css["'];?\s*\n?/);
506
+ const hasTwAnimateInWeloop = weloopStyles.match(/@import\s+["']tw-animate-css["'];?\s*\n?/);
507
+
462
508
  let importsToAdd = '';
463
- if (hasTwAnimate && !existing.includes('@import "tw-animate-css"')) {
509
+ if (hasTwAnimate && !hasTwAnimateInExisting && !hasTwAnimateInWeloop) {
464
510
  importsToAdd = '@import "tw-animate-css";\n';
465
511
  }
466
512
 
@@ -504,7 +550,7 @@ async function fetchCSSFromRegistry(registryUrl) {
504
550
  return await fetchText(sourceCssPath);
505
551
  }
506
552
 
507
- async function installCSSStyles(config, registryUrl, forceUpdate = false) {
553
+ async function installCSSStyles(config, registryUrl, forceUpdate = false, silent = false) {
508
554
  const cssPath = config.tailwind?.css || 'app/globals.css';
509
555
  const fullCssPath = path.join(process.cwd(), cssPath);
510
556
 
@@ -514,13 +560,17 @@ async function installCSSStyles(config, registryUrl, forceUpdate = false) {
514
560
  const existingContent = fs.readFileSync(fullCssPath, 'utf-8');
515
561
  hasWeloopStylesInFile = hasWeloopStyles(existingContent);
516
562
 
517
- if (forceUpdate && hasWeloopStylesInFile) {
518
- info('Updating existing Weloop styles...');
563
+ // If styles already exist and not forcing update, skip silently
564
+ if (hasWeloopStylesInFile && !forceUpdate && silent) {
565
+ return;
519
566
  }
520
567
  }
521
568
 
522
569
  try {
523
- info('Installing CSS styles...');
570
+ if (!silent) {
571
+ info('Installing CSS styles...');
572
+ }
573
+
524
574
  const cssContent = await fetchCSSFromRegistry(registryUrl);
525
575
  ensureDirectoryExists(path.dirname(fullCssPath));
526
576
 
@@ -531,9 +581,11 @@ async function installCSSStyles(config, registryUrl, forceUpdate = false) {
531
581
  if (forceUpdate && fs.existsSync(fullCssPath)) {
532
582
  // --overwrite: Replace entire file
533
583
  fs.writeFileSync(fullCssPath, processedCssContent);
534
- success(`Overwritten ${cssPath} with Weloop styles`);
535
- if (hasTwAnimate) {
536
- info(` tw-animate-css import included`);
584
+ if (!silent) {
585
+ success(`Overwritten ${cssPath} with Weloop styles`);
586
+ if (hasTwAnimate) {
587
+ info(` tw-animate-css import included`);
588
+ }
537
589
  }
538
590
  } else if (fs.existsSync(fullCssPath)) {
539
591
  // Normal mode: Merge intelligently
@@ -542,28 +594,37 @@ async function installCSSStyles(config, registryUrl, forceUpdate = false) {
542
594
  existing.includes('@tailwind base');
543
595
 
544
596
  if (hasWeloopStylesInFile) {
545
- // Replace existing Weloop styles
597
+ // Replace existing Weloop styles (only if different)
546
598
  let weloopStyles = removeTailwindImport(processedCssContent);
547
599
  weloopStyles = ensureTwAnimateImport(weloopStyles, hasTwAnimate);
548
600
  const finalContent = replaceWeloopStyles(existing, weloopStyles, hasTwAnimate);
549
- fs.writeFileSync(fullCssPath, finalContent);
550
- success(`Updated ${cssPath} with Weloop styles`);
551
- info(` Existing Weloop styles were replaced with latest version`);
601
+
602
+ // Only update if content actually changed
603
+ if (finalContent !== existing) {
604
+ fs.writeFileSync(fullCssPath, finalContent);
605
+ if (!silent) {
606
+ success(`Updated ${cssPath} with Weloop styles`);
607
+ }
608
+ }
609
+ // If no changes, silently skip
552
610
  } else if (hasTailwindImport) {
553
611
  // Merge after Tailwind imports
554
612
  let weloopStyles = removeTailwindImport(processedCssContent);
555
613
  weloopStyles = ensureTwAnimateImport(weloopStyles, hasTwAnimate);
556
614
  const merged = mergeCSSWithTailwind(existing, weloopStyles, hasTwAnimate);
557
615
  fs.writeFileSync(fullCssPath, merged);
558
- success(`Updated ${cssPath} with Weloop styles`);
559
- if (hasTwAnimate) {
560
- info(` tw-animate-css import included`);
616
+ if (!silent) {
617
+ success(`Updated ${cssPath} with Weloop styles`);
618
+ if (hasTwAnimate) {
619
+ info(` tw-animate-css import included`);
620
+ }
621
+ info(` Your existing styles are preserved`);
561
622
  }
562
- info(` Your existing styles are preserved`);
563
623
  } else {
564
624
  // No Tailwind imports, prepend everything
565
- let finalCssContent = processedCssContent;
566
- if (hasTwAnimate && !finalCssContent.includes('@import "tw-animate-css"')) {
625
+ let finalCssContent = removeDuplicateTwAnimateImports(processedCssContent);
626
+
627
+ if (hasTwAnimate && !finalCssContent.match(/@import\s+["']tw-animate-css["'];?\s*\n?/)) {
567
628
  if (finalCssContent.includes('@import "tailwindcss"')) {
568
629
  finalCssContent = finalCssContent.replace(
569
630
  /(@import\s+["']tailwindcss["'];?\s*\n?)/,
@@ -574,25 +635,31 @@ async function installCSSStyles(config, registryUrl, forceUpdate = false) {
574
635
  }
575
636
  }
576
637
  fs.writeFileSync(fullCssPath, finalCssContent + '\n\n' + existing);
577
- success(`Updated ${cssPath} with Weloop styles`);
578
- if (hasTwAnimate) {
579
- info(` tw-animate-css import included`);
638
+ if (!silent) {
639
+ success(`Updated ${cssPath} with Weloop styles`);
640
+ if (hasTwAnimate) {
641
+ info(` tw-animate-css import included`);
642
+ }
580
643
  }
581
644
  }
582
645
  } else {
583
646
  // Create new file
584
647
  fs.writeFileSync(fullCssPath, processedCssContent);
585
- success(`Created ${cssPath} with Weloop styles`);
586
- if (hasTwAnimate) {
587
- info(` tw-animate-css import included`);
648
+ if (!silent) {
649
+ success(`Created ${cssPath} with Weloop styles`);
650
+ if (hasTwAnimate) {
651
+ info(` tw-animate-css import included`);
652
+ }
588
653
  }
589
654
  }
590
655
  } catch (err) {
591
- warn(`Could not automatically install CSS styles: ${err.message}`);
592
- info(`\n To add styles manually:`);
593
- info(` 1. Download: ${registryUrl.replace('/registry', '/app/globals.css')}`);
594
- info(` 2. Add the CSS variables to your ${cssPath} file`);
595
- info(` 3. Or copy from: https://gitlab.com/Sophanithchrek/weloop-shadcn-next-app/-/raw/main/app/globals.css\n`);
656
+ if (!silent) {
657
+ warn(`Could not automatically install CSS styles: ${err.message}`);
658
+ info(`\n To add styles manually:`);
659
+ info(` 1. Download: ${registryUrl.replace('/registry', '/app/globals.css')}`);
660
+ info(` 2. Add the CSS variables to your ${cssPath} file`);
661
+ info(` 3. Or copy from: https://gitlab.com/Sophanithchrek/weloop-shadcn-next-app/-/raw/main/app/globals.css\n`);
662
+ }
596
663
  }
597
664
  }
598
665
 
@@ -615,16 +682,25 @@ export function cn(...inputs: ClassValue[]) {
615
682
  `;
616
683
  fs.writeFileSync(utilsPath, utilsContent);
617
684
  success(`Created ${path.relative(process.cwd(), utilsPath)}`);
618
-
619
- // Check if required packages are installed
620
- if (!checkPackageInstalled('clsx') || !checkPackageInstalled('tailwind-merge')) {
621
- warn('utils.ts requires: clsx and tailwind-merge');
622
- info(' Install with: npm install clsx tailwind-merge');
623
- }
624
685
  }
625
686
 
626
687
  async function installComponent(componentName, options = {}) {
627
688
  const { overwrite = false, registryUrl = DEFAULT_REGISTRY_URL } = options;
689
+
690
+ // Install base dependencies first (required for utils.ts)
691
+ const baseDeps = [];
692
+ if (!checkPackageInstalled('clsx')) {
693
+ baseDeps.push('clsx');
694
+ }
695
+ if (!checkPackageInstalled('tailwind-merge')) {
696
+ baseDeps.push('tailwind-merge');
697
+ }
698
+ if (baseDeps.length > 0) {
699
+ info(`Installing base dependencies: ${baseDeps.join(', ')}...`);
700
+ await installPackages(baseDeps);
701
+ }
702
+
703
+ // Load or create components.json (after base deps are installed)
628
704
  const config = loadComponentsConfig();
629
705
 
630
706
  // Install tw-animate-css automatically (required for animations)
@@ -633,8 +709,8 @@ async function installComponent(componentName, options = {}) {
633
709
  await installPackages(['tw-animate-css']);
634
710
  }
635
711
 
636
- // Install CSS styles early (before component installation)
637
- await installCSSStyles(config, registryUrl);
712
+ // Install CSS styles early (before component installation) - silent if already installed
713
+ await installCSSStyles(config, registryUrl, false, true);
638
714
 
639
715
  // Get paths from components.json
640
716
  const uiAlias = config.aliases?.ui || '@/components/ui';
@@ -782,7 +858,7 @@ Examples:
782
858
  case 'css':
783
859
  case 'styles':
784
860
  const config = loadComponentsConfig();
785
- await installCSSStyles(config, registryUrl, options.overwrite);
861
+ await installCSSStyles(config, registryUrl, options.overwrite, false);
786
862
  break;
787
863
 
788
864
  default: