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.
- package/README.md +220 -220
- package/README_CLI.md +46 -46
- package/dist/assets/index-1YAQdTE0.css +2 -0
- package/dist/assets/index-BsQ6nn74.js +237 -0
- package/dist/favicon.svg +1 -0
- package/dist/icons.svg +24 -0
- package/dist/index.html +13 -0
- package/dist/ui-cli.cjs +34 -1
- package/dist/ui-cli.js +124 -0
- package/package.json +3 -2
- package/registry.json +33 -32
- package/scripts/build-registry.ts +55 -11
- 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 +61 -1
|
@@ -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:
|
|
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
|
|
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}`);
|
|
@@ -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}`);
|
package/scripts/set-version.mjs
CHANGED
|
@@ -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.
|
|
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 ───────────────────────────────────────────────────────────────
|