basuicn 0.3.6 → 0.3.9
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 +220 -220
- package/README_CLI.md +46 -46
- package/dist/ui-cli.cjs +1 -1
- package/package.json +1 -1
- package/registry.json +25 -25
- package/scripts/build-registry.ts +261 -261
- package/scripts/bump-version.mjs +22 -22
- package/scripts/generate-theme-css.ts +74 -74
- package/scripts/set-version.mjs +31 -31
- package/scripts/ui-cli.ts +1 -1
- package/dist/assets/index-1YAQdTE0.css +0 -2
- package/dist/assets/index-BsQ6nn74.js +0 -237
- package/dist/favicon.svg +0 -1
- package/dist/icons.svg +0 -24
- package/dist/index.html +0 -13
- package/dist/ui-cli.js +0 -124
|
@@ -1,261 +1,261 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
const COMPONENTS_DIR = './src/components/ui';
|
|
5
|
-
const HOOKS_DIR = './src/hooks';
|
|
6
|
-
const OUTPUT_FILE = './registry.json';
|
|
7
|
-
|
|
8
|
-
// Directories to exclude from registry (not reusable components)
|
|
9
|
-
const EXCLUDE_DIRS = new Set(['icons', 'layout', 'vs-code', 'Showcase.tsx']);
|
|
10
|
-
|
|
11
|
-
interface RegistryFile {
|
|
12
|
-
path: string;
|
|
13
|
-
content: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
interface RegistryComponent {
|
|
17
|
-
name: string;
|
|
18
|
-
dependencies: string[];
|
|
19
|
-
internalDependencies: string[];
|
|
20
|
-
files: RegistryFile[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface Registry {
|
|
24
|
-
core: {
|
|
25
|
-
dependencies: string[];
|
|
26
|
-
files: RegistryFile[];
|
|
27
|
-
};
|
|
28
|
-
components: Record<string, RegistryComponent>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const getFiles = (dir: string): string[] => {
|
|
32
|
-
const files: string[] = [];
|
|
33
|
-
let entries: string[];
|
|
34
|
-
try {
|
|
35
|
-
entries = fs.readdirSync(dir);
|
|
36
|
-
} catch (err) {
|
|
37
|
-
console.warn(`Cannot read directory ${dir}: ${err}`);
|
|
38
|
-
return files;
|
|
39
|
-
}
|
|
40
|
-
for (const file of entries) {
|
|
41
|
-
const fullPath = path.join(dir, file);
|
|
42
|
-
try {
|
|
43
|
-
const stat = fs.statSync(fullPath);
|
|
44
|
-
if (stat.isDirectory()) {
|
|
45
|
-
files.push(...getFiles(fullPath));
|
|
46
|
-
} else if (file.endsWith('.tsx') || file.endsWith('.ts')) {
|
|
47
|
-
if (!file.includes('.test.') && !file.includes('.stories.')) {
|
|
48
|
-
files.push(fullPath);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
} catch (err) {
|
|
52
|
-
console.warn(`Skipping ${fullPath}: ${err}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return files;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const INTERNAL_ALIAS_PREFIXES = ['@/', '@lib/', '@components/', '@app/'];
|
|
59
|
-
|
|
60
|
-
const parseDependencies = (content: string): string[] => {
|
|
61
|
-
const dependencies = new Set<string>();
|
|
62
|
-
const importRegex = /(?:from|import)\s+['"]([^'"]+)['"]/g;
|
|
63
|
-
let match;
|
|
64
|
-
|
|
65
|
-
while ((match = importRegex.exec(content)) !== null) {
|
|
66
|
-
const pkg = match[1];
|
|
67
|
-
|
|
68
|
-
// Skip relative imports
|
|
69
|
-
if (pkg.startsWith('.')) continue;
|
|
70
|
-
// Skip internal aliases
|
|
71
|
-
if (INTERNAL_ALIAS_PREFIXES.some(prefix => pkg.startsWith(prefix))) continue;
|
|
72
|
-
|
|
73
|
-
// Extract package name
|
|
74
|
-
const parts = pkg.split('/');
|
|
75
|
-
if (pkg.startsWith('@') && parts.length >= 2) {
|
|
76
|
-
dependencies.add(`${parts[0]}/${parts[1]}`);
|
|
77
|
-
} else {
|
|
78
|
-
dependencies.add(parts[0]);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Remove React (always a peer dependency)
|
|
83
|
-
dependencies.delete('react');
|
|
84
|
-
dependencies.delete('react-dom');
|
|
85
|
-
|
|
86
|
-
return [...dependencies];
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const getInternalDeps = (content: string, currentDirName: string): string[] => {
|
|
90
|
-
const internalDeps = new Set<string>();
|
|
91
|
-
|
|
92
|
-
// 1. Alias imports: @/components/ui/xxx or @components/ui/xxx
|
|
93
|
-
const aliasRegex = /from\s+['"](?:@\/?components\/ui)\/([^'"]+)['"]/g;
|
|
94
|
-
let match;
|
|
95
|
-
while ((match = aliasRegex.exec(content)) !== null) {
|
|
96
|
-
const depPath = match[1].split('/')[0];
|
|
97
|
-
if (depPath !== currentDirName && depPath !== 'icons') {
|
|
98
|
-
internalDeps.add(depPath);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 2. Relative imports: ../spinner/Spinner
|
|
103
|
-
const relativeRegex = /from\s+['"]\.\.\/([^'"]+)['"]/g;
|
|
104
|
-
while ((match = relativeRegex.exec(content)) !== null) {
|
|
105
|
-
const depPath = match[1].split('/')[0];
|
|
106
|
-
if (depPath !== currentDirName) {
|
|
107
|
-
internalDeps.add(depPath);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return [...internalDeps];
|
|
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
|
-
|
|
152
|
-
const buildRegistry = () => {
|
|
153
|
-
console.log('Building component registry...');
|
|
154
|
-
|
|
155
|
-
const registry: Registry = {
|
|
156
|
-
core: {
|
|
157
|
-
dependencies: [
|
|
158
|
-
'@base-ui/react',
|
|
159
|
-
'clsx',
|
|
160
|
-
'tailwind-merge',
|
|
161
|
-
'tailwind-variants',
|
|
162
|
-
'tailwindcss-animate',
|
|
163
|
-
'@tailwindcss/vite',
|
|
164
|
-
'autoprefixer',
|
|
165
|
-
'tailwindcss',
|
|
166
|
-
'postcss',
|
|
167
|
-
],
|
|
168
|
-
files: [
|
|
169
|
-
{
|
|
170
|
-
path: 'src/lib/utils/cn.ts',
|
|
171
|
-
content: fs.readFileSync('./src/lib/utils/cn.ts', 'utf-8'),
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
path: 'src/styles/index.css',
|
|
175
|
-
content: fs.readFileSync('./src/styles/index.css', 'utf-8'),
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
path: 'src/lib/theme/themes.ts',
|
|
179
|
-
content: fs.readFileSync('./src/lib/theme/themes.ts', 'utf-8'),
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
path: 'src/lib/theme/ThemeProvider.tsx',
|
|
183
|
-
content: fs.readFileSync('./src/lib/theme/ThemeProvider.tsx', 'utf-8'),
|
|
184
|
-
},
|
|
185
|
-
],
|
|
186
|
-
},
|
|
187
|
-
components: {},
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const componentDirs = fs.readdirSync(COMPONENTS_DIR);
|
|
191
|
-
|
|
192
|
-
for (const dirName of componentDirs) {
|
|
193
|
-
if (EXCLUDE_DIRS.has(dirName)) continue;
|
|
194
|
-
|
|
195
|
-
const dirPath = path.join(COMPONENTS_DIR, dirName);
|
|
196
|
-
if (!fs.statSync(dirPath).isDirectory()) continue;
|
|
197
|
-
|
|
198
|
-
const files = getFiles(dirPath);
|
|
199
|
-
if (files.length === 0) continue;
|
|
200
|
-
|
|
201
|
-
// Find main file: prefer <DirName>.tsx, then any .tsx
|
|
202
|
-
const mainFile =
|
|
203
|
-
files.find(
|
|
204
|
-
(f) =>
|
|
205
|
-
f.toLowerCase().includes(dirName.toLowerCase()) &&
|
|
206
|
-
f.endsWith('.tsx')
|
|
207
|
-
) || files.find((f) => f.endsWith('.tsx'));
|
|
208
|
-
|
|
209
|
-
if (!mainFile) continue;
|
|
210
|
-
|
|
211
|
-
// Parse all files in the component directory for dependencies
|
|
212
|
-
const allDependencies = new Set<string>();
|
|
213
|
-
const allInternalDeps = new Set<string>();
|
|
214
|
-
|
|
215
|
-
for (const file of files) {
|
|
216
|
-
try {
|
|
217
|
-
const content = fs.readFileSync(file, 'utf-8');
|
|
218
|
-
for (const dep of parseDependencies(content)) allDependencies.add(dep);
|
|
219
|
-
for (const dep of getInternalDeps(content, dirName)) allInternalDeps.add(dep);
|
|
220
|
-
} catch (err) {
|
|
221
|
-
console.warn(`Failed to read ${file}: ${err}`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
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
|
-
|
|
240
|
-
registry.components[dirName] = {
|
|
241
|
-
name: dirName,
|
|
242
|
-
dependencies: [...allDependencies],
|
|
243
|
-
internalDependencies: [...allInternalDeps],
|
|
244
|
-
files: [...componentFiles, ...hookFiles],
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const componentCount = Object.keys(registry.components).length;
|
|
249
|
-
if (componentCount === 0) {
|
|
250
|
-
console.warn('Warning: No components found in registry');
|
|
251
|
-
}
|
|
252
|
-
try {
|
|
253
|
-
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(registry, null, 2));
|
|
254
|
-
} catch (err) {
|
|
255
|
-
console.error(`Failed to write registry: ${err}`);
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
console.log(`Registry built: ${componentCount} components → ${OUTPUT_FILE}`);
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
buildRegistry();
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const COMPONENTS_DIR = './src/components/ui';
|
|
5
|
+
const HOOKS_DIR = './src/hooks';
|
|
6
|
+
const OUTPUT_FILE = './registry.json';
|
|
7
|
+
|
|
8
|
+
// Directories to exclude from registry (not reusable components)
|
|
9
|
+
const EXCLUDE_DIRS = new Set(['icons', 'layout', 'vs-code', 'Showcase.tsx']);
|
|
10
|
+
|
|
11
|
+
interface RegistryFile {
|
|
12
|
+
path: string;
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface RegistryComponent {
|
|
17
|
+
name: string;
|
|
18
|
+
dependencies: string[];
|
|
19
|
+
internalDependencies: string[];
|
|
20
|
+
files: RegistryFile[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Registry {
|
|
24
|
+
core: {
|
|
25
|
+
dependencies: string[];
|
|
26
|
+
files: RegistryFile[];
|
|
27
|
+
};
|
|
28
|
+
components: Record<string, RegistryComponent>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getFiles = (dir: string): string[] => {
|
|
32
|
+
const files: string[] = [];
|
|
33
|
+
let entries: string[];
|
|
34
|
+
try {
|
|
35
|
+
entries = fs.readdirSync(dir);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.warn(`Cannot read directory ${dir}: ${err}`);
|
|
38
|
+
return files;
|
|
39
|
+
}
|
|
40
|
+
for (const file of entries) {
|
|
41
|
+
const fullPath = path.join(dir, file);
|
|
42
|
+
try {
|
|
43
|
+
const stat = fs.statSync(fullPath);
|
|
44
|
+
if (stat.isDirectory()) {
|
|
45
|
+
files.push(...getFiles(fullPath));
|
|
46
|
+
} else if (file.endsWith('.tsx') || file.endsWith('.ts')) {
|
|
47
|
+
if (!file.includes('.test.') && !file.includes('.stories.')) {
|
|
48
|
+
files.push(fullPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.warn(`Skipping ${fullPath}: ${err}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return files;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const INTERNAL_ALIAS_PREFIXES = ['@/', '@lib/', '@components/', '@app/'];
|
|
59
|
+
|
|
60
|
+
const parseDependencies = (content: string): string[] => {
|
|
61
|
+
const dependencies = new Set<string>();
|
|
62
|
+
const importRegex = /(?:from|import)\s+['"]([^'"]+)['"]/g;
|
|
63
|
+
let match;
|
|
64
|
+
|
|
65
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
66
|
+
const pkg = match[1];
|
|
67
|
+
|
|
68
|
+
// Skip relative imports
|
|
69
|
+
if (pkg.startsWith('.')) continue;
|
|
70
|
+
// Skip internal aliases
|
|
71
|
+
if (INTERNAL_ALIAS_PREFIXES.some(prefix => pkg.startsWith(prefix))) continue;
|
|
72
|
+
|
|
73
|
+
// Extract package name
|
|
74
|
+
const parts = pkg.split('/');
|
|
75
|
+
if (pkg.startsWith('@') && parts.length >= 2) {
|
|
76
|
+
dependencies.add(`${parts[0]}/${parts[1]}`);
|
|
77
|
+
} else {
|
|
78
|
+
dependencies.add(parts[0]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Remove React (always a peer dependency)
|
|
83
|
+
dependencies.delete('react');
|
|
84
|
+
dependencies.delete('react-dom');
|
|
85
|
+
|
|
86
|
+
return [...dependencies];
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const getInternalDeps = (content: string, currentDirName: string): string[] => {
|
|
90
|
+
const internalDeps = new Set<string>();
|
|
91
|
+
|
|
92
|
+
// 1. Alias imports: @/components/ui/xxx or @components/ui/xxx
|
|
93
|
+
const aliasRegex = /from\s+['"](?:@\/?components\/ui)\/([^'"]+)['"]/g;
|
|
94
|
+
let match;
|
|
95
|
+
while ((match = aliasRegex.exec(content)) !== null) {
|
|
96
|
+
const depPath = match[1].split('/')[0];
|
|
97
|
+
if (depPath !== currentDirName && depPath !== 'icons') {
|
|
98
|
+
internalDeps.add(depPath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 2. Relative imports: ../spinner/Spinner
|
|
103
|
+
const relativeRegex = /from\s+['"]\.\.\/([^'"]+)['"]/g;
|
|
104
|
+
while ((match = relativeRegex.exec(content)) !== null) {
|
|
105
|
+
const depPath = match[1].split('/')[0];
|
|
106
|
+
if (depPath !== currentDirName) {
|
|
107
|
+
internalDeps.add(depPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return [...internalDeps];
|
|
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
|
+
|
|
152
|
+
const buildRegistry = () => {
|
|
153
|
+
console.log('Building component registry...');
|
|
154
|
+
|
|
155
|
+
const registry: Registry = {
|
|
156
|
+
core: {
|
|
157
|
+
dependencies: [
|
|
158
|
+
'@base-ui/react',
|
|
159
|
+
'clsx',
|
|
160
|
+
'tailwind-merge',
|
|
161
|
+
'tailwind-variants',
|
|
162
|
+
'tailwindcss-animate',
|
|
163
|
+
'@tailwindcss/vite',
|
|
164
|
+
'autoprefixer',
|
|
165
|
+
'tailwindcss',
|
|
166
|
+
'postcss',
|
|
167
|
+
],
|
|
168
|
+
files: [
|
|
169
|
+
{
|
|
170
|
+
path: 'src/lib/utils/cn.ts',
|
|
171
|
+
content: fs.readFileSync('./src/lib/utils/cn.ts', 'utf-8'),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
path: 'src/styles/index.css',
|
|
175
|
+
content: fs.readFileSync('./src/styles/index.css', 'utf-8'),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
path: 'src/lib/theme/themes.ts',
|
|
179
|
+
content: fs.readFileSync('./src/lib/theme/themes.ts', 'utf-8'),
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
path: 'src/lib/theme/ThemeProvider.tsx',
|
|
183
|
+
content: fs.readFileSync('./src/lib/theme/ThemeProvider.tsx', 'utf-8'),
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
components: {},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const componentDirs = fs.readdirSync(COMPONENTS_DIR);
|
|
191
|
+
|
|
192
|
+
for (const dirName of componentDirs) {
|
|
193
|
+
if (EXCLUDE_DIRS.has(dirName)) continue;
|
|
194
|
+
|
|
195
|
+
const dirPath = path.join(COMPONENTS_DIR, dirName);
|
|
196
|
+
if (!fs.statSync(dirPath).isDirectory()) continue;
|
|
197
|
+
|
|
198
|
+
const files = getFiles(dirPath);
|
|
199
|
+
if (files.length === 0) continue;
|
|
200
|
+
|
|
201
|
+
// Find main file: prefer <DirName>.tsx, then any .tsx
|
|
202
|
+
const mainFile =
|
|
203
|
+
files.find(
|
|
204
|
+
(f) =>
|
|
205
|
+
f.toLowerCase().includes(dirName.toLowerCase()) &&
|
|
206
|
+
f.endsWith('.tsx')
|
|
207
|
+
) || files.find((f) => f.endsWith('.tsx'));
|
|
208
|
+
|
|
209
|
+
if (!mainFile) continue;
|
|
210
|
+
|
|
211
|
+
// Parse all files in the component directory for dependencies
|
|
212
|
+
const allDependencies = new Set<string>();
|
|
213
|
+
const allInternalDeps = new Set<string>();
|
|
214
|
+
|
|
215
|
+
for (const file of files) {
|
|
216
|
+
try {
|
|
217
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
218
|
+
for (const dep of parseDependencies(content)) allDependencies.add(dep);
|
|
219
|
+
for (const dep of getInternalDeps(content, dirName)) allInternalDeps.add(dep);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
console.warn(`Failed to read ${file}: ${err}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
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
|
+
|
|
240
|
+
registry.components[dirName] = {
|
|
241
|
+
name: dirName,
|
|
242
|
+
dependencies: [...allDependencies],
|
|
243
|
+
internalDependencies: [...allInternalDeps],
|
|
244
|
+
files: [...componentFiles, ...hookFiles],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const componentCount = Object.keys(registry.components).length;
|
|
249
|
+
if (componentCount === 0) {
|
|
250
|
+
console.warn('Warning: No components found in registry');
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(registry, null, 2));
|
|
254
|
+
} catch (err) {
|
|
255
|
+
console.error(`Failed to write registry: ${err}`);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
console.log(`Registry built: ${componentCount} components → ${OUTPUT_FILE}`);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
buildRegistry();
|
package/scripts/bump-version.mjs
CHANGED
|
@@ -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}`);
|