cus-base-ui 0.2.6 → 0.2.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/dist/ui-cli.cjs CHANGED
@@ -68,7 +68,7 @@ var getRegistry = async (isLocal) => {
68
68
  process.exit(1);
69
69
  }
70
70
  };
71
- var installNpmPackages = (packages, cwd) => {
71
+ var installNpmPackages = (packages, cwd, dev = false) => {
72
72
  if (packages.length === 0) return;
73
73
  const pkgJsonPath = import_path.default.join(cwd, "package.json");
74
74
  let toInstall = packages;
@@ -79,12 +79,189 @@ var installNpmPackages = (packages, cwd) => {
79
79
  }
80
80
  if (toInstall.length === 0) return;
81
81
  log(`Installing: ${toInstall.join(", ")}...`);
82
+ const flag = dev ? "--save-dev" : "--save";
82
83
  try {
83
- (0, import_child_process.execSync)(`npm install ${toInstall.join(" ")} --save`, { stdio: "inherit", cwd });
84
+ (0, import_child_process.execSync)(`npm install ${toInstall.join(" ")} ${flag}`, { stdio: "inherit", cwd });
84
85
  } catch (err) {
85
86
  error(`Failed to install packages: ${toInstall.join(", ")}. ${err instanceof Error ? err.message : ""}`);
86
87
  }
87
88
  };
89
+ var VITE_DEV_PACKAGES = ["tailwindcss", "@tailwindcss/vite", "@vitejs/plugin-react", "vite-plugin-babel", "babel-plugin-react-compiler", "@types/node"];
90
+ var RUNTIME_PACKAGES = ["@base-ui/react", "tailwind-variants", "clsx", "tailwind-merge", "tailwindcss-animate"];
91
+ var VITE_CONFIG_TEMPLATE = `import { defineConfig } from 'vite';
92
+ import tailwindcss from '@tailwindcss/vite';
93
+ import react from '@vitejs/plugin-react';
94
+ import babel from 'vite-plugin-babel';
95
+ import { reactCompilerPreset } from 'babel-plugin-react-compiler';
96
+ import path from 'path';
97
+
98
+ // https://vite.dev/config/
99
+ export default defineConfig({
100
+ plugins: [
101
+ tailwindcss(),
102
+ react(),
103
+ babel({ presets: [reactCompilerPreset()] }),
104
+ ],
105
+ resolve: {
106
+ alias: {
107
+ '@': path.resolve(__dirname, './src'),
108
+ '@lib': path.resolve(__dirname, './src/lib'),
109
+ '@components': path.resolve(__dirname, './src/components'),
110
+ '@assets': path.resolve(__dirname, './src/assets'),
111
+ '@pages': path.resolve(__dirname, './src/pages'),
112
+ '@styles': path.resolve(__dirname, './src/styles'),
113
+ },
114
+ },
115
+ });
116
+ `;
117
+ var TSCONFIG_PATHS = {
118
+ "@/*": ["./src/*"],
119
+ "@lib/*": ["./src/lib/*"],
120
+ "@components/*": ["./src/components/*"],
121
+ "@assets/*": ["./src/assets/*"],
122
+ "@pages/*": ["./src/pages/*"],
123
+ "@styles/*": ["./src/styles/*"]
124
+ };
125
+ var setupViteConfig = (cwd) => {
126
+ installNpmPackages(VITE_DEV_PACKAGES, cwd, true);
127
+ const configTs = import_path.default.join(cwd, "vite.config.ts");
128
+ const configJs = import_path.default.join(cwd, "vite.config.js");
129
+ if (!import_fs.default.existsSync(configTs) && !import_fs.default.existsSync(configJs)) {
130
+ import_fs.default.writeFileSync(configTs, VITE_CONFIG_TEMPLATE);
131
+ log("Created vite.config.ts with Tailwind + React Compiler setup.");
132
+ return;
133
+ }
134
+ const existingPath = import_fs.default.existsSync(configTs) ? configTs : configJs;
135
+ let content = import_fs.default.readFileSync(existingPath, "utf-8");
136
+ const missingImports = [];
137
+ if (!content.includes("@tailwindcss/vite")) missingImports.push("import tailwindcss from '@tailwindcss/vite';");
138
+ if (!content.includes("@vitejs/plugin-react")) missingImports.push("import react from '@vitejs/plugin-react';");
139
+ if (!content.includes("vite-plugin-babel")) missingImports.push("import babel from 'vite-plugin-babel';");
140
+ if (!content.includes("babel-plugin-react-compiler")) missingImports.push("import { reactCompilerPreset } from 'babel-plugin-react-compiler';");
141
+ if (!content.includes("from 'path'") && !content.includes('from "path"')) missingImports.push("import path from 'path';");
142
+ const missingPlugins = [];
143
+ if (!content.includes("tailwindcss()")) missingPlugins.push("tailwindcss()");
144
+ if (!content.includes("react()") && !content.includes("react({")) missingPlugins.push("react()");
145
+ if (!content.includes("reactCompilerPreset")) missingPlugins.push("babel({ presets: [reactCompilerPreset()] })");
146
+ const hasAlias = content.includes("alias:") || content.includes("alias(") || content.includes("'@'") || content.includes('"@"');
147
+ if (missingImports.length === 0 && missingPlugins.length === 0 && hasAlias) {
148
+ log("vite.config already configured \u2014 skipping.");
149
+ return;
150
+ }
151
+ if (missingImports.length > 0) {
152
+ const importBlock = missingImports.join("\n");
153
+ const allImports = [...content.matchAll(/^import\s.+$/gm)];
154
+ if (allImports.length > 0) {
155
+ const last = allImports[allImports.length - 1];
156
+ const pos = last.index + last[0].length;
157
+ content = content.slice(0, pos) + "\n" + importBlock + content.slice(pos);
158
+ } else {
159
+ content = importBlock + "\n" + content;
160
+ }
161
+ }
162
+ if (missingPlugins.length > 0) {
163
+ const match = content.match(/plugins:\s*\[/);
164
+ if (match && match.index !== void 0) {
165
+ const pos = match.index + match[0].length;
166
+ const after = content.slice(pos);
167
+ const pluginLines = missingPlugins.map((p) => `
168
+ ${p},`).join("");
169
+ const needsNewline = after.length > 0 && after[0] !== "\n" && after[0] !== "\r";
170
+ content = content.slice(0, pos) + pluginLines + (needsNewline ? "\n " : "") + after;
171
+ }
172
+ }
173
+ if (!hasAlias) {
174
+ const aliasBlock = [
175
+ " resolve: {",
176
+ " alias: {",
177
+ " '@': path.resolve(__dirname, './src'),",
178
+ " '@lib': path.resolve(__dirname, './src/lib'),",
179
+ " '@components': path.resolve(__dirname, './src/components'),",
180
+ " '@assets': path.resolve(__dirname, './src/assets'),",
181
+ " '@pages': path.resolve(__dirname, './src/pages'),",
182
+ " '@styles': path.resolve(__dirname, './src/styles'),",
183
+ " },",
184
+ " },"
185
+ ].join("\n");
186
+ const pluginsStart = content.search(/plugins:\s*\[/);
187
+ if (pluginsStart !== -1) {
188
+ let depth = 0;
189
+ let foundStart = false;
190
+ for (let i = pluginsStart; i < content.length; i++) {
191
+ if (content[i] === "[") {
192
+ depth++;
193
+ foundStart = true;
194
+ }
195
+ if (content[i] === "]") depth--;
196
+ if (foundStart && depth === 0) {
197
+ let lineEnd = content.indexOf("\n", i);
198
+ if (lineEnd === -1) lineEnd = content.length;
199
+ content = content.slice(0, lineEnd + 1) + aliasBlock + "\n" + content.slice(lineEnd + 1);
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ }
205
+ import_fs.default.writeFileSync(existingPath, content);
206
+ log(`Updated ${import_path.default.basename(existingPath)} with Tailwind + React Compiler + path aliases.`);
207
+ };
208
+ var ensureTailwindCss = (cwd) => {
209
+ const candidates = ["src/index.css", "src/App.css", "src/main.css"];
210
+ for (const cssFile of candidates) {
211
+ const cssPath = import_path.default.join(cwd, cssFile);
212
+ if (import_fs.default.existsSync(cssPath)) {
213
+ const content = import_fs.default.readFileSync(cssPath, "utf-8");
214
+ if (!content.includes('@import "tailwindcss"') && !content.includes("@import 'tailwindcss'")) {
215
+ import_fs.default.writeFileSync(cssPath, `@import "tailwindcss";
216
+
217
+ ${content}`);
218
+ log(`Added @import "tailwindcss" to ${cssFile}`);
219
+ } else {
220
+ log(`${cssFile} already imports Tailwind \u2014 skipping.`);
221
+ }
222
+ return;
223
+ }
224
+ }
225
+ const srcDir = import_path.default.join(cwd, "src");
226
+ if (!import_fs.default.existsSync(srcDir)) import_fs.default.mkdirSync(srcDir, { recursive: true });
227
+ import_fs.default.writeFileSync(import_path.default.join(srcDir, "index.css"), '@import "tailwindcss";\n');
228
+ log('Created src/index.css with @import "tailwindcss"');
229
+ };
230
+ var setupTsConfig = (cwd) => {
231
+ const candidates = ["tsconfig.app.json", "tsconfig.json"];
232
+ for (const candidate of candidates) {
233
+ const configPath = import_path.default.join(cwd, candidate);
234
+ if (!import_fs.default.existsSync(configPath)) continue;
235
+ const raw = import_fs.default.readFileSync(configPath, "utf-8");
236
+ if (raw.includes('"@/*"') || raw.includes("'@/*'")) {
237
+ log(`${candidate} already has path aliases \u2014 skipping.`);
238
+ return;
239
+ }
240
+ try {
241
+ const stripped = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[\s,{[\]])\/\/[^\n]*/g, "$1");
242
+ const parsed = JSON.parse(stripped);
243
+ if (!parsed.compilerOptions) parsed.compilerOptions = {};
244
+ parsed.compilerOptions.baseUrl = ".";
245
+ parsed.compilerOptions.paths = TSCONFIG_PATHS;
246
+ import_fs.default.writeFileSync(configPath, JSON.stringify(parsed, null, 2));
247
+ log(`Added path aliases to ${candidate}.`);
248
+ } catch (err) {
249
+ warn(`Could not auto-patch ${candidate}: ${err instanceof Error ? err.message : err}`);
250
+ warn("Add these to compilerOptions manually:");
251
+ console.log('\n "baseUrl": ".",');
252
+ console.log(' "paths": {');
253
+ for (const [alias, targets] of Object.entries(TSCONFIG_PATHS)) {
254
+ console.log(` "${alias}": ["${targets[0]}"],`);
255
+ }
256
+ console.log(" }");
257
+ console.log("");
258
+ }
259
+ return;
260
+ }
261
+ const newConfig = { compilerOptions: { baseUrl: ".", paths: TSCONFIG_PATHS } };
262
+ import_fs.default.writeFileSync(import_path.default.join(cwd, "tsconfig.json"), JSON.stringify(newConfig, null, 2));
263
+ log("Created tsconfig.json with path aliases.");
264
+ };
88
265
  var ensureCore = (registry, cwd) => {
89
266
  const core = registry.core;
90
267
  if (!core) return;
@@ -174,6 +351,14 @@ var main = async () => {
174
351
  error("Usage: npx cus-base-ui add <component-name> [--force]");
175
352
  return;
176
353
  }
354
+ const cnPath = import_path.default.join(cwd, "src/lib/utils/cn.ts");
355
+ if (!import_fs.default.existsSync(cnPath)) {
356
+ log("Project not initialized \u2014 running init first...");
357
+ setupViteConfig(cwd);
358
+ setupTsConfig(cwd);
359
+ ensureTailwindCss(cwd);
360
+ installNpmPackages(RUNTIME_PACKAGES, cwd);
361
+ }
177
362
  for (const name of componentNames) {
178
363
  addComponent(name, registry, cwd, { force: isForce });
179
364
  }
@@ -202,6 +387,10 @@ var main = async () => {
202
387
  break;
203
388
  }
204
389
  case "init": {
390
+ setupViteConfig(cwd);
391
+ setupTsConfig(cwd);
392
+ ensureTailwindCss(cwd);
393
+ installNpmPackages(RUNTIME_PACKAGES, cwd);
205
394
  ensureCore(registry, cwd);
206
395
  log("Initialization complete.");
207
396
  break;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cus-base-ui",
3
3
  "private": false,
4
- "version": "0.2.6",
4
+ "version": "0.2.8",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "cus-base-ui": "./dist/ui-cli.cjs"
package/scripts/ui-cli.ts CHANGED
@@ -126,6 +126,7 @@ const setupViteConfig = (cwd: string) => {
126
126
  const configTs = path.join(cwd, 'vite.config.ts');
127
127
  const configJs = path.join(cwd, 'vite.config.js');
128
128
 
129
+ // No config exists — create from template
129
130
  if (!fs.existsSync(configTs) && !fs.existsSync(configJs)) {
130
131
  fs.writeFileSync(configTs, VITE_CONFIG_TEMPLATE);
131
132
  log('Created vite.config.ts with Tailwind + React Compiler setup.');
@@ -133,49 +134,91 @@ const setupViteConfig = (cwd: string) => {
133
134
  }
134
135
 
135
136
  const existingPath = fs.existsSync(configTs) ? configTs : configJs;
136
- const content = fs.readFileSync(existingPath, 'utf-8');
137
+ let content = fs.readFileSync(existingPath, 'utf-8');
137
138
 
138
139
  const missingImports: string[] = [];
139
140
  if (!content.includes('@tailwindcss/vite')) missingImports.push("import tailwindcss from '@tailwindcss/vite';");
140
141
  if (!content.includes('@vitejs/plugin-react')) missingImports.push("import react from '@vitejs/plugin-react';");
141
142
  if (!content.includes('vite-plugin-babel')) missingImports.push("import babel from 'vite-plugin-babel';");
142
143
  if (!content.includes('babel-plugin-react-compiler')) missingImports.push("import { reactCompilerPreset } from 'babel-plugin-react-compiler';");
144
+ if (!content.includes("from 'path'") && !content.includes('from "path"')) missingImports.push("import path from 'path';");
143
145
 
144
146
  const missingPlugins: string[] = [];
145
147
  if (!content.includes('tailwindcss()')) missingPlugins.push('tailwindcss()');
146
148
  if (!content.includes('react()') && !content.includes('react({')) missingPlugins.push('react()');
147
- if (!content.includes('reactCompilerPreset')) missingPlugins.push('babel({ presets: [reactCompilerPreset()] })');
149
+ if (!content.includes('reactCompilerPreset')) missingPlugins.push("babel({ presets: [reactCompilerPreset()] })");
148
150
 
149
- const hasAlias = content.includes('alias:') || content.includes("'@'") || content.includes('"@"');
151
+ const hasAlias = content.includes('alias:') || content.includes('alias(') || content.includes("'@'") || content.includes('"@"');
150
152
 
151
153
  if (missingImports.length === 0 && missingPlugins.length === 0 && hasAlias) {
152
154
  log('vite.config already configured — skipping.');
153
155
  return;
154
156
  }
155
157
 
156
- warn(`${path.basename(existingPath)} exists but is missing required setup. Add the following manually:`);
158
+ // --- Auto-patch the file ---
159
+
160
+ // 1. Insert missing imports after the last import statement
157
161
  if (missingImports.length > 0) {
158
- console.log('\n // Imports to add:');
159
- for (const imp of missingImports) console.log(` ${imp}`);
162
+ const importBlock = missingImports.join('\n');
163
+ const allImports = [...content.matchAll(/^import\s.+$/gm)];
164
+ if (allImports.length > 0) {
165
+ const last = allImports[allImports.length - 1];
166
+ const pos = last.index! + last[0].length;
167
+ content = content.slice(0, pos) + '\n' + importBlock + content.slice(pos);
168
+ } else {
169
+ content = importBlock + '\n' + content;
170
+ }
160
171
  }
172
+
173
+ // 2. Insert missing plugins into plugins: [...]
161
174
  if (missingPlugins.length > 0) {
162
- console.log('\n // Plugins to add inside defineConfig({ plugins: [...] }):');
163
- for (const plugin of missingPlugins) console.log(` ${plugin},`);
175
+ const match = content.match(/plugins:\s*\[/);
176
+ if (match && match.index !== undefined) {
177
+ const pos = match.index + match[0].length;
178
+ const after = content.slice(pos);
179
+ const pluginLines = missingPlugins.map((p) => `\n ${p},`).join('');
180
+ // If existing content continues on the same line (e.g. `plugins: [react()],`),
181
+ // push it to a new line so formatting stays clean
182
+ const needsNewline = after.length > 0 && after[0] !== '\n' && after[0] !== '\r';
183
+ content = content.slice(0, pos) + pluginLines + (needsNewline ? '\n ' : '') + after;
184
+ }
164
185
  }
186
+
187
+ // 3. Insert resolve.alias block if missing
165
188
  if (!hasAlias) {
166
- console.log('\n // resolve.alias to add inside defineConfig({}):');
167
- console.log(" resolve: {");
168
- console.log(" alias: {");
169
- console.log(" '@': path.resolve(__dirname, './src'),");
170
- console.log(" '@lib': path.resolve(__dirname, './src/lib'),");
171
- console.log(" '@components': path.resolve(__dirname, './src/components'),");
172
- console.log(" '@assets': path.resolve(__dirname, './src/assets'),");
173
- console.log(" '@pages': path.resolve(__dirname, './src/pages'),");
174
- console.log(" '@styles': path.resolve(__dirname, './src/styles'),");
175
- console.log(" },");
176
- console.log(" },");
189
+ const aliasBlock = [
190
+ ' resolve: {',
191
+ ' alias: {',
192
+ " '@': path.resolve(__dirname, './src'),",
193
+ " '@lib': path.resolve(__dirname, './src/lib'),",
194
+ " '@components': path.resolve(__dirname, './src/components'),",
195
+ " '@assets': path.resolve(__dirname, './src/assets'),",
196
+ " '@pages': path.resolve(__dirname, './src/pages'),",
197
+ " '@styles': path.resolve(__dirname, './src/styles'),",
198
+ ' },',
199
+ ' },',
200
+ ].join('\n');
201
+
202
+ // Find end of plugins array, then insert after that line
203
+ const pluginsStart = content.search(/plugins:\s*\[/);
204
+ if (pluginsStart !== -1) {
205
+ let depth = 0;
206
+ let foundStart = false;
207
+ for (let i = pluginsStart; i < content.length; i++) {
208
+ if (content[i] === '[') { depth++; foundStart = true; }
209
+ if (content[i] === ']') depth--;
210
+ if (foundStart && depth === 0) {
211
+ let lineEnd = content.indexOf('\n', i);
212
+ if (lineEnd === -1) lineEnd = content.length;
213
+ content = content.slice(0, lineEnd + 1) + aliasBlock + '\n' + content.slice(lineEnd + 1);
214
+ break;
215
+ }
216
+ }
217
+ }
177
218
  }
178
- console.log('');
219
+
220
+ fs.writeFileSync(existingPath, content);
221
+ log(`Updated ${path.basename(existingPath)} with Tailwind + React Compiler + path aliases.`);
179
222
  };
180
223
 
181
224
  const ensureTailwindCss = (cwd: string) => {
@@ -215,16 +258,20 @@ const setupTsConfig = (cwd: string) => {
215
258
  }
216
259
 
217
260
  try {
218
- // Strip single-line and block comments before parsing
219
- const stripped = raw.replace(/\/\/[^\n]*/g, '').replace(/\/\*[\s\S]*?\*\//g, '');
261
+ // Strip comments safely: block comments first, then single-line comments
262
+ // only when they appear outside of string values (preceded by whitespace or line start)
263
+ const stripped = raw
264
+ .replace(/\/\*[\s\S]*?\*\//g, '')
265
+ .replace(/(^|[\s,{[\]])\/\/[^\n]*/g, '$1');
220
266
  const parsed = JSON.parse(stripped) as { compilerOptions?: Record<string, unknown> };
221
267
  if (!parsed.compilerOptions) parsed.compilerOptions = {};
222
268
  parsed.compilerOptions.baseUrl = '.';
223
269
  parsed.compilerOptions.paths = TSCONFIG_PATHS;
224
270
  fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2));
225
271
  log(`Added path aliases to ${candidate}.`);
226
- } catch {
227
- warn(`Could not auto-patch ${candidate}. Add these to compilerOptions manually:`);
272
+ } catch (err) {
273
+ warn(`Could not auto-patch ${candidate}: ${err instanceof Error ? err.message : err}`);
274
+ warn('Add these to compilerOptions manually:');
228
275
  console.log('\n "baseUrl": ".",');
229
276
  console.log(' "paths": {');
230
277
  for (const [alias, targets] of Object.entries(TSCONFIG_PATHS)) {