basuicn 0.2.11 → 0.3.1

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.
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
 
4
4
  const COMPONENTS_DIR = './src/components/ui';
5
+ const HOOKS_DIR = './src/hooks';
5
6
  const OUTPUT_FILE = './registry.json';
6
7
 
7
8
  // Directories to exclude from registry (not reusable components)
@@ -110,6 +111,44 @@ const getInternalDeps = (content: string, currentDirName: string): string[] => {
110
111
  return [...internalDeps];
111
112
  };
112
113
 
114
+ /** Collect hook files imported via `@/hooks/xxx` in any of the given source files */
115
+ const collectHookFiles = (sourceFiles: string[]): RegistryFile[] => {
116
+ const seen = new Set<string>();
117
+ const hookFiles: RegistryFile[] = [];
118
+
119
+ for (const file of sourceFiles) {
120
+ let content: string;
121
+ try { content = fs.readFileSync(file, 'utf-8'); } catch { continue; }
122
+
123
+ const hookRegex = /from\s+['"]@\/hooks\/([^'"]+)['"]/g;
124
+ let match;
125
+ while ((match = hookRegex.exec(content)) !== null) {
126
+ // Normalise: strip extension if present, then try .ts and .tsx
127
+ const rawName = match[1].replace(/\.(ts|tsx)$/, '');
128
+ if (seen.has(rawName)) continue;
129
+
130
+ const candidates = [
131
+ path.join(HOOKS_DIR, `${rawName}.ts`),
132
+ path.join(HOOKS_DIR, `${rawName}.tsx`),
133
+ path.join(HOOKS_DIR, rawName, 'index.ts'),
134
+ ];
135
+
136
+ for (const candidate of candidates) {
137
+ if (fs.existsSync(candidate)) {
138
+ seen.add(rawName);
139
+ hookFiles.push({
140
+ path: candidate.replace(/\\/g, '/').replace(/^\.\//, ''),
141
+ content: fs.readFileSync(candidate, 'utf-8'),
142
+ });
143
+ break;
144
+ }
145
+ }
146
+ }
147
+ }
148
+
149
+ return hookFiles;
150
+ };
151
+
113
152
  const buildRegistry = () => {
114
153
  console.log('Building component registry...');
115
154
 
@@ -183,21 +222,26 @@ const buildRegistry = () => {
183
222
  }
184
223
  }
185
224
 
225
+ // Collect any hook files this component imports from @/hooks/
226
+ const hookFiles = collectHookFiles(files);
227
+
228
+ const componentFiles: RegistryFile[] = files.map((f) => {
229
+ try {
230
+ return {
231
+ path: f.replace(/\\/g, '/').replace(/^\.\//, ''),
232
+ content: fs.readFileSync(f, 'utf-8'),
233
+ };
234
+ } catch (err) {
235
+ console.warn(`Failed to read ${f}: ${err}`);
236
+ return { path: f.replace(/\\/g, '/').replace(/^\.\//, ''), content: '' };
237
+ }
238
+ }).filter(f => f.content !== '');
239
+
186
240
  registry.components[dirName] = {
187
241
  name: dirName,
188
242
  dependencies: [...allDependencies],
189
243
  internalDependencies: [...allInternalDeps],
190
- files: files.map((f) => {
191
- try {
192
- return {
193
- path: f.replace(/\\/g, '/').replace(/^\.\//, ''),
194
- content: fs.readFileSync(f, 'utf-8'),
195
- };
196
- } catch (err) {
197
- console.warn(`Failed to read ${f}: ${err}`);
198
- return { path: f.replace(/\\/g, '/').replace(/^\.\//, ''), content: '' };
199
- }
200
- }).filter(f => f.content !== ''),
244
+ files: [...componentFiles, ...hookFiles],
201
245
  };
202
246
  }
203
247
 
@@ -1,22 +1,22 @@
1
- import fs from 'fs';
2
- import { execSync } from 'child_process';
3
-
4
- // Skip if triggered by a version bump commit (avoid infinite loop)
5
- const lastMsg = execSync('git log -1 --pretty=%s').toString().trim();
6
- if (lastMsg.startsWith('chore: bump version')) process.exit(0);
7
-
8
- const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
9
- const [major, minor, patch] = pkg.version.split('.').map(Number);
10
- const newVersion = `${major}.${minor}.${patch + 1}`;
11
-
12
- pkg.version = newVersion;
13
- fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
14
-
15
- let cli = fs.readFileSync('./scripts/ui-cli.ts', 'utf-8');
16
- cli = cli.replace(/const VERSION = '[^']+';/, `const VERSION = '${newVersion}';`);
17
- fs.writeFileSync('./scripts/ui-cli.ts', cli);
18
-
19
- execSync('git add package.json scripts/ui-cli.ts');
20
- execSync(`git commit --no-verify -m "chore: bump version to ${newVersion}"`);
21
-
22
- console.log(`\x1b[32m✔\x1b[0m Bumped version → ${newVersion}`);
1
+ import fs from 'fs';
2
+ import { execSync } from 'child_process';
3
+
4
+ // Skip if triggered by a version bump commit (avoid infinite loop)
5
+ const lastMsg = execSync('git log -1 --pretty=%s').toString().trim();
6
+ if (lastMsg.startsWith('chore: bump version')) process.exit(0);
7
+
8
+ const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
9
+ const [major, minor, patch] = pkg.version.split('.').map(Number);
10
+ const newVersion = `${major}.${minor}.${patch + 1}`;
11
+
12
+ pkg.version = newVersion;
13
+ fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
14
+
15
+ let cli = fs.readFileSync('./scripts/ui-cli.ts', 'utf-8');
16
+ cli = cli.replace(/const VERSION = '[^']+';/, `const VERSION = '${newVersion}';`);
17
+ fs.writeFileSync('./scripts/ui-cli.ts', cli);
18
+
19
+ execSync('git add package.json scripts/ui-cli.ts');
20
+ execSync(`git commit --no-verify -m "chore: bump version to ${newVersion}"`);
21
+
22
+ console.log(`\x1b[32m✔\x1b[0m Bumped version → ${newVersion}`);
@@ -1,74 +1,74 @@
1
- /**
2
- * Syncs the default theme CSS variables from themes.ts → src/styles/index.css.
3
- * Run via: npm run theme:sync
4
- *
5
- * Replaces the content between:
6
- * /* GENERATED:theme-start *\/
7
- * /* GENERATED:theme-end *\/
8
- * in index.css with fresh values from themes[0].
9
- */
10
-
11
- import fs from 'fs';
12
- import path from 'path';
13
- import { themes } from '../src/lib/theme/themes';
14
-
15
- const CSS_PATH = path.resolve('src/styles/index.css');
16
- const START_MARKER = '/* GENERATED:theme-start */';
17
- const END_MARKER = '/* GENERATED:theme-end */';
18
-
19
- const INDENT = ' '; // 8 spaces — matches :root { } indentation in index.css
20
-
21
- const { colors: c } = themes[0];
22
- const vars: [string, string][] = [
23
- ['--background', c.background],
24
- ['--foreground', c.foreground],
25
- ['--primary', c.primary],
26
- ['--primary-foreground', c.primaryForeground],
27
- ['--secondary', c.secondary],
28
- ['--secondary-foreground', c.secondaryForeground],
29
- ['--muted', c.muted],
30
- ['--muted-foreground', c.mutedForeground],
31
- ['--accent', c.accent],
32
- ['--accent-foreground', c.accentForeground],
33
- ['--success', c.success],
34
- ['--success-foreground', c.successForeground],
35
- ['--warning', c.warning],
36
- ['--warning-foreground', c.warningForeground],
37
- ['--danger', c.danger],
38
- ['--danger-foreground', c.dangerForeground],
39
- ['--destructive', c.danger],
40
- ['--destructive-foreground', c.dangerForeground],
41
- ['--border', c.border],
42
- ['--input', c.input],
43
- ['--ring', c.ring],
44
- ['--popover', c.popover],
45
- ['--popover-foreground', c.popoverForeground],
46
- ];
47
-
48
- const generated = [
49
- START_MARKER,
50
- `${INDENT}/* Auto-generated from themes.ts — run \`npm run theme:sync\` to update */`,
51
- ...vars.map(([k, v]) => `${INDENT}${k}: ${v};`),
52
- `${INDENT}${END_MARKER}`,
53
- ].join('\n');
54
-
55
- const css = fs.readFileSync(CSS_PATH, 'utf-8');
56
-
57
- const startIdx = css.indexOf(START_MARKER);
58
- const endIdx = css.indexOf(END_MARKER);
59
-
60
- if (startIdx === -1 || endIdx === -1) {
61
- console.error(
62
- `[theme:sync] Markers not found in ${CSS_PATH}.\n` +
63
- `Add these two comments inside @layer base :root { }:\n\n` +
64
- ` ${START_MARKER}\n ${END_MARKER}\n`
65
- );
66
- process.exit(1);
67
- }
68
-
69
- const before = css.slice(0, startIdx);
70
- const after = css.slice(endIdx + END_MARKER.length);
71
- const result = before + generated + after;
72
-
73
- fs.writeFileSync(CSS_PATH, result, 'utf-8');
74
- console.log(`[theme:sync] Synced default theme "${themes[0].name}" → ${CSS_PATH}`);
1
+ /**
2
+ * Syncs the default theme CSS variables from themes.ts → src/styles/index.css.
3
+ * Run via: npm run theme:sync
4
+ *
5
+ * Replaces the content between:
6
+ * /* GENERATED:theme-start *\/
7
+ * /* GENERATED:theme-end *\/
8
+ * in index.css with fresh values from themes[0].
9
+ */
10
+
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import { themes } from '../src/lib/theme/themes';
14
+
15
+ const CSS_PATH = path.resolve('src/styles/index.css');
16
+ const START_MARKER = '/* GENERATED:theme-start */';
17
+ const END_MARKER = '/* GENERATED:theme-end */';
18
+
19
+ const INDENT = ' '; // 8 spaces — matches :root { } indentation in index.css
20
+
21
+ const { colors: c } = themes[0];
22
+ const vars: [string, string][] = [
23
+ ['--background', c.background],
24
+ ['--foreground', c.foreground],
25
+ ['--primary', c.primary],
26
+ ['--primary-foreground', c.primaryForeground],
27
+ ['--secondary', c.secondary],
28
+ ['--secondary-foreground', c.secondaryForeground],
29
+ ['--muted', c.muted],
30
+ ['--muted-foreground', c.mutedForeground],
31
+ ['--accent', c.accent],
32
+ ['--accent-foreground', c.accentForeground],
33
+ ['--success', c.success],
34
+ ['--success-foreground', c.successForeground],
35
+ ['--warning', c.warning],
36
+ ['--warning-foreground', c.warningForeground],
37
+ ['--danger', c.danger],
38
+ ['--danger-foreground', c.dangerForeground],
39
+ ['--destructive', c.danger],
40
+ ['--destructive-foreground', c.dangerForeground],
41
+ ['--border', c.border],
42
+ ['--input', c.input],
43
+ ['--ring', c.ring],
44
+ ['--popover', c.popover],
45
+ ['--popover-foreground', c.popoverForeground],
46
+ ];
47
+
48
+ const generated = [
49
+ START_MARKER,
50
+ `${INDENT}/* Auto-generated from themes.ts — run \`npm run theme:sync\` to update */`,
51
+ ...vars.map(([k, v]) => `${INDENT}${k}: ${v};`),
52
+ `${INDENT}${END_MARKER}`,
53
+ ].join('\n');
54
+
55
+ const css = fs.readFileSync(CSS_PATH, 'utf-8');
56
+
57
+ const startIdx = css.indexOf(START_MARKER);
58
+ const endIdx = css.indexOf(END_MARKER);
59
+
60
+ if (startIdx === -1 || endIdx === -1) {
61
+ console.error(
62
+ `[theme:sync] Markers not found in ${CSS_PATH}.\n` +
63
+ `Add these two comments inside @layer base :root { }:\n\n` +
64
+ ` ${START_MARKER}\n ${END_MARKER}\n`
65
+ );
66
+ process.exit(1);
67
+ }
68
+
69
+ const before = css.slice(0, startIdx);
70
+ const after = css.slice(endIdx + END_MARKER.length);
71
+ const result = before + generated + after;
72
+
73
+ fs.writeFileSync(CSS_PATH, result, 'utf-8');
74
+ console.log(`[theme:sync] Synced default theme "${themes[0].name}" → ${CSS_PATH}`);
@@ -1,31 +1,31 @@
1
- import fs from 'fs';
2
-
3
- const arg = process.argv[2];
4
- if (!arg) {
5
- console.error('Usage: node scripts/set-version.mjs <version|major|minor|patch>');
6
- console.error(' Examples: node scripts/set-version.mjs 1.0.0');
7
- console.error(' node scripts/set-version.mjs major');
8
- process.exit(1);
9
- }
10
-
11
- const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
12
- const [major, minor, patch] = pkg.version.split('.').map(Number);
13
-
14
- let newVersion;
15
- if (arg === 'major') newVersion = `${major + 1}.0.0`;
16
- else if (arg === 'minor') newVersion = `${major}.${minor + 1}.0`;
17
- else if (arg === 'patch') newVersion = `${major}.${minor}.${patch + 1}`;
18
- else if (/^\d+\.\d+\.\d+$/.test(arg)) newVersion = arg;
19
- else {
20
- console.error(`Invalid version: "${arg}"`);
21
- process.exit(1);
22
- }
23
-
24
- pkg.version = newVersion;
25
- fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
26
-
27
- let cli = fs.readFileSync('./scripts/ui-cli.ts', 'utf-8');
28
- cli = cli.replace(/const VERSION = '[^']+';/, `const VERSION = '${newVersion}';`);
29
- fs.writeFileSync('./scripts/ui-cli.ts', cli);
30
-
31
- console.log(`\x1b[32m✔\x1b[0m Version set → ${newVersion}`);
1
+ import fs from 'fs';
2
+
3
+ const arg = process.argv[2];
4
+ if (!arg) {
5
+ console.error('Usage: node scripts/set-version.mjs <version|major|minor|patch>');
6
+ console.error(' Examples: node scripts/set-version.mjs 1.0.0');
7
+ console.error(' node scripts/set-version.mjs major');
8
+ process.exit(1);
9
+ }
10
+
11
+ const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));
12
+ const [major, minor, patch] = pkg.version.split('.').map(Number);
13
+
14
+ let newVersion;
15
+ if (arg === 'major') newVersion = `${major + 1}.0.0`;
16
+ else if (arg === 'minor') newVersion = `${major}.${minor + 1}.0`;
17
+ else if (arg === 'patch') newVersion = `${major}.${minor}.${patch + 1}`;
18
+ else if (/^\d+\.\d+\.\d+$/.test(arg)) newVersion = arg;
19
+ else {
20
+ console.error(`Invalid version: "${arg}"`);
21
+ process.exit(1);
22
+ }
23
+
24
+ pkg.version = newVersion;
25
+ fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
26
+
27
+ let cli = fs.readFileSync('./scripts/ui-cli.ts', 'utf-8');
28
+ cli = cli.replace(/const VERSION = '[^']+';/, `const VERSION = '${newVersion}';`);
29
+ fs.writeFileSync('./scripts/ui-cli.ts', cli);
30
+
31
+ console.log(`\x1b[32m✔\x1b[0m Version set → ${newVersion}`);
package/scripts/ui-cli.ts CHANGED
@@ -6,7 +6,7 @@ import readline from 'readline';
6
6
 
7
7
  // ─── Constants ────────────────────────────────────────────────────────────────
8
8
 
9
- const VERSION = '0.2.11';
9
+ const VERSION = '0.3.1';
10
10
  const REGISTRY_LOCAL = './registry.json';
11
11
  const REGISTRY_REMOTE = 'https://raw.githubusercontent.com/Basuicn/basuicn-core/main/registry.json';
12
12
 
@@ -685,6 +685,62 @@ const patchMainTsxComponent = (cwd: string, componentName: string) => {
685
685
  ok(`Added <${tagName}> to ${path.relative(cwd, mainPath)}.`);
686
686
  };
687
687
 
688
+ // ─── index.ts barrel update ───────────────────────────────────────────────────
689
+
690
+ const UI_INDEX_PATH = 'src/components/ui/index.ts';
691
+ const UI_INDEX_DIR = 'src/components/ui';
692
+
693
+ /** Pick the primary .tsx component file (skip tests, stories, hooks) */
694
+ const pickMainFile = (files: RegistryFile[]): RegistryFile | undefined =>
695
+ files.find(
696
+ (f) =>
697
+ f.path.startsWith(UI_INDEX_DIR + '/') &&
698
+ f.path.endsWith('.tsx') &&
699
+ !f.path.includes('.test.') &&
700
+ !f.path.includes('.stories.'),
701
+ );
702
+
703
+ /** Append `export * from './dir/File';` to index.ts if not already present */
704
+ const addToComponentIndex = (componentFiles: RegistryFile[], cwd: string) => {
705
+ const indexPath = path.join(cwd, UI_INDEX_PATH);
706
+ if (!fs.existsSync(indexPath)) return;
707
+
708
+ const mainFile = pickMainFile(componentFiles);
709
+ if (!mainFile) return;
710
+
711
+ const withoutExt = mainFile.path.replace(/\.tsx$/, '');
712
+ const relPath = './' + path.relative(UI_INDEX_DIR, withoutExt).replace(/\\/g, '/');
713
+ const exportLine = `export * from '${relPath}';`;
714
+
715
+ const content = fs.readFileSync(indexPath, 'utf-8');
716
+ if (content.includes(relPath)) return;
717
+
718
+ fs.writeFileSync(indexPath, content.trimEnd() + '\n' + exportLine + '\n');
719
+ ok(`Updated index.ts: added ${c.dim}${relPath}${c.reset}`);
720
+ };
721
+
722
+ /** Remove the export line from index.ts */
723
+ const removeFromComponentIndex = (componentFiles: RegistryFile[], cwd: string) => {
724
+ const indexPath = path.join(cwd, UI_INDEX_PATH);
725
+ if (!fs.existsSync(indexPath)) return;
726
+
727
+ const mainFile = pickMainFile(componentFiles);
728
+ if (!mainFile) return;
729
+
730
+ const withoutExt = mainFile.path.replace(/\.tsx$/, '');
731
+ const relPath = './' + path.relative(UI_INDEX_DIR, withoutExt).replace(/\\/g, '/');
732
+
733
+ const content = fs.readFileSync(indexPath, 'utf-8');
734
+ const filtered = content
735
+ .split('\n')
736
+ .filter((line) => !line.includes(relPath))
737
+ .join('\n');
738
+
739
+ if (filtered === content) return;
740
+ fs.writeFileSync(indexPath, filtered);
741
+ ok(`Updated index.ts: removed ${c.dim}${relPath}${c.reset}`);
742
+ };
743
+
688
744
  // ─── Component add/remove ─────────────────────────────────────────────────────
689
745
 
690
746
  const addComponent = (
@@ -738,6 +794,8 @@ const addComponent = (
738
794
  fs.writeFileSync(targetPath, content);
739
795
  ok(`Created: ${file.path}`);
740
796
  }
797
+
798
+ addToComponentIndex(component.files, cwd);
741
799
  };
742
800
 
743
801
  const removeComponent = (
@@ -772,6 +830,8 @@ const removeComponent = (
772
830
  warn(`Could not remove directory: ${err instanceof Error ? err.message : err}`);
773
831
  }
774
832
  }
833
+
834
+ removeFromComponentIndex(component.files as RegistryFile[], cwd);
775
835
  };
776
836
 
777
837
  // ─── Help texts ───────────────────────────────────────────────────────────────