centoui-cli 1.0.0-alpha.3 → 1.0.0-alpha.30

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
@@ -1,44 +1,114 @@
1
- # centoui-cli
1
+ <div align="center">
2
+ <h1>centoui-cli</h1>
3
+ <p><strong>Manage CentoUI components from the terminal.</strong></p>
4
+ <p>
5
+ <a href="https://npmx.dev/package/centoui-cli"><img src="https://img.shields.io/npm/v/centoui-cli.svg?style=plastic&label=NPM Version&color=blue" alt="npm version"></a>
6
+ <a href="https://npmx.dev/package/centoui-cli"><img src="https://img.shields.io/npm/dw/centoui-cli.svg?style=plastic&label=NPM Downloads&color=blue" alt="npm downloads"></a>
7
+ <a href="https://npmx.dev/package/centoui-cli"><img src="https://img.shields.io/npm/unpacked-size/centoui-cli?style=plastic&label=NPM Unpacked Size&color=blue" alt="NPM Unpacked Size"></a>
8
+ </p>
9
+ </div>
2
10
 
3
- [![npm version](https://img.shields.io/npm/v/centoui-cli.svg?style=flat-square)](https://www.npmjs.com/package/centoui-cli)
4
- [![npm downloads](https://img.shields.io/npm/dm/centoui-cli.svg?style=flat-square)](https://www.npmjs.com/package/centoui-cli)
5
- [![license](https://img.shields.io/github/license/favorodera/centoui.svg?style=flat-square)](https://github.com/favorodera/centoui/blob/main/LICENSE)
11
+ <br>
6
12
 
7
- **CentoUI CLI: Manage your CentoUI components with ease.**
8
-
9
- `centoui-cli` is the official command-line interface for [CentoUI](https://github.com/favorodera/centoui). It allows you to initialize CentoUI in your project, add new components, and manage their versions directly from your terminal.
13
+ `centoui-cli` is the command-line interface for [CentoUI](../core). It initializes projects, pulls component source files from the registry into your codebase, resolves dependency trees automatically, and cleans up when you remove components.
10
14
 
11
15
  ## Commands
12
16
 
13
- - **`init`**: Set up a new CentoUI project (generates `centoui.config.ts` and `centoui.css`).
14
- - **`add [component]`**: Add specific components to your project. Peer dependencies install automatically.
15
- - **`remove [component]`**: Cleanly remove components and their dependencies.
17
+ ### `centoui init`
16
18
 
17
- ## Usage
19
+ Scaffolds a new CentoUI project in your current directory.
18
20
 
19
21
  ```bash
20
- # Initialize CentoUI in your project
21
22
  pnpm dlx centoui init
23
+ ```
24
+
25
+ **What it does:**
26
+
27
+ 1. Prompts you for a component directory (default: `src/components/centoui`) and theme CSS path (default: `src/assets/css/centoui.css`).
28
+ 2. Writes `centoui.config.ts` with your chosen paths and default icon mappings.
29
+ 3. Fetches and writes the `centoui.css` theme file with all light/dark color tokens.
30
+ 4. Creates the component directory.
31
+ 5. Installs global peer dependencies (`vue`, `reka-ui`, `tailwindcss`, etc.).
32
+
33
+ ---
34
+
35
+ ### `centoui add <component> [component...]`
36
+
37
+ Adds one or more components to your project.
38
+
39
+ ```bash
40
+ pnpm dlx centoui add button accordion select
41
+ ```
42
+
43
+ **What it does:**
44
+
45
+ 1. Fetches the component registry from GitHub.
46
+ 2. Resolves the full dependency tree — if `select` depends on `popover`, both are installed.
47
+ 3. Asks before overwriting any component that already exists.
48
+ 4. Downloads `.vue` and `index.ts` files for each component into your configured directory.
49
+ 5. Installs any npm packages required by the added components.
22
50
 
23
- # Add components
24
- pnpm dlx centoui add button dialog input select
51
+ ---
52
+
53
+ ### `centoui remove <component>`
54
+
55
+ Removes an installed component and cleans up orphaned packages.
56
+
57
+ ```bash
58
+ pnpm dlx centoui remove accordion
25
59
  ```
26
60
 
61
+ **What it does:**
62
+
63
+ 1. Checks that the component is actually installed.
64
+ 2. Blocks removal if other installed components depend on it (e.g., you can't remove `popover` while `select` is installed).
65
+ 3. Deletes the component's directory.
66
+ 4. Uninstalls npm packages that are no longer needed by any remaining component.
67
+
68
+ ## Version-Locked Asset Fetching
69
+
70
+ Every network request the CLI makes — the registry index, individual component files, the theme CSS, and the default config — is resolved against the **exact git tag that matches the installed version of `centoui-cli`**.
71
+
72
+ The base URL for all assets is derived directly from the CLI's own `package.json` version at build time:
73
+
74
+ ```
75
+ https://raw.githubusercontent.com/favorodera/centoui/refs/tags/v<VERSION>/packages/core/src
76
+ ```
77
+
78
+ This means:
79
+
80
+ - If you have `centoui-cli@1.2.3` installed, all fetched assets come from the `v1.2.3` tag of the `centoui` repository — never from `main` or any other release.
81
+ - The registry, component source files, CSS theme, and config defaults are always in sync with each other and with the CLI you are running.
82
+ - Upgrading the CLI upgrades the version tag automatically — no separate step needed.
83
+
84
+ This design eliminates an entire class of version mismatch bugs where the CLI and the component source could drift out of sync.
85
+
27
86
  ## Configuration
28
87
 
29
- The CLI generates a `centoui.config.ts` file in your root directory. You can customize the component directory, theme directory, and utility directory here.
88
+ After running `centoui init`, your project root will contain a `centoui.config.ts`:
30
89
 
31
90
  ```ts
32
91
  import { defineConfig } from "centoui"
33
92
 
34
93
  export default defineConfig({
35
- version: "1.0.0",
36
94
  componentsDir: "./src/components/centoui",
37
95
  themeFilePath: "./src/assets/css/centoui.css",
38
96
  icons: {
39
97
  check: "lucide:check",
40
98
  close: "lucide:x",
41
- menu: "lucide:menu",
99
+ chevronDown: "lucide:chevron-down",
100
+ chevronUp: "lucide:chevron-up",
101
+ chevronLeft: "lucide:chevron-left",
102
+ chevronRight: "lucide:chevron-right",
103
+ chevronDoubleLeft: "lucide:chevrons-left",
104
+ chevronDoubleRight: "lucide:chevrons-right",
105
+ ellipsis: "lucide:ellipsis",
42
106
  },
43
107
  })
44
- ```
108
+ ```
109
+
110
+ The `icons` map lets you swap icon libraries without touching component code. Components reference icons by their slot name (e.g., `check`, `close`), and the config resolves them to [Iconify](https://iconify.design/) IDs.
111
+
112
+ ## License
113
+
114
+ [MIT](../../LICENSE) &copy; [Favour Emeka](https://github.com/favorodera)
package/dist/index.d.mts CHANGED
@@ -48,8 +48,7 @@ type Registry = {
48
48
  * Generated by `centoui init` and consumed by every subsequent CLI command.
49
49
  */
50
50
  type CentoUIConfig = {
51
- /** The CentoUI version this project was initialised with. */version: string; /** Relative path (from project root) to the directory where components are installed. */
52
- componentsDir: string; /** Relative path (from project root) to the CSS file that receives theme and component styles. */
51
+ /** Relative path (from project root) to the directory where components are installed. */componentsDir: string; /** Relative path (from project root) to the CSS file that receives theme and component styles. */
53
52
  themeFilePath: string;
54
53
  /**
55
54
  * Maps internal CentoUI icon slot names to Iconify icon IDs.
package/dist/index.mjs CHANGED
@@ -4,11 +4,11 @@ import { cancel, confirm, group, intro, isCancel, log, note, outro, tasks, text
4
4
  import { dirname, join } from "pathe";
5
5
  import fsExtra from "fs-extra";
6
6
  import { addDependency, removeDependency } from "nypm";
7
- import { pathToFileURL } from "node:url";
7
+ import { loadConfig } from "c12";
8
8
  //#endregion
9
9
  //#region src/constants.ts
10
10
  /** CentoUI current package version, sourced directly from package.json. */
11
- const VERSION = "1.0.0-alpha.3";
11
+ const VERSION = "1.0.0-alpha.30";
12
12
  /** File name for the user-side CentoUI config (created by `centoui init`). */
13
13
  const CONFIG_FILE_NAME = "centoui.config.ts";
14
14
  /**
@@ -25,7 +25,12 @@ const REGISTRY_INDEX_URL = `${`${CORE_SRC_BASE_URL}/registry`}/index.json`;
25
25
  * Full URL to the CentoUI CSS theme file.
26
26
  * This file is written to the user's project during `centoui init`.
27
27
  */
28
- const THEME_CSS_URL = `${CORE_SRC_BASE_URL}/css/centoui.css`;
28
+ const THEME_CSS_URL = `${CORE_SRC_BASE_URL}/defaults/centoui.css`;
29
+ /**
30
+ * Full URL to the default values file for the CentoUI config.
31
+ * The contents of this file are written to the user's project during `centoui init`.
32
+ */
33
+ const CONFIG_DEFAULTS_URL = `${CORE_SRC_BASE_URL}/defaults/config.ts`;
29
34
  /**
30
35
  * HTTP headers required when fetching raw content from the GitHub API.
31
36
  * These ensure we get the raw file bytes, not GitHub's HTML wrapper.
@@ -185,10 +190,7 @@ async function confirmOverwriteIfExists(label, path) {
185
190
  //#endregion
186
191
  //#region src/utils/config-utils.ts
187
192
  /**
188
- * Loads and returns the user's CentoUI configuration from `centoui.config.ts`.
189
- *
190
- * Uses a dynamic `import()` via a `file://` URL so that TypeScript config files
191
- * compiled by the user's build tooling are resolved correctly at runtime.
193
+ * Loads and returns the user's CentoUI configuration from `centoui.config.ts` using c12.
192
194
  *
193
195
  * @param cwd - Absolute path to the project root.
194
196
  * @returns The default export of `centoui.config.ts` cast as {@link CentoUIConfig}.
@@ -196,13 +198,58 @@ async function confirmOverwriteIfExists(label, path) {
196
198
  * @throws If the file exists but cannot be imported or does not export a default value.
197
199
  */
198
200
  async function loadCentoUIConfig(cwd) {
199
- const configFilePath = join(cwd, CONFIG_FILE_NAME);
201
+ const { config, configFile } = await loadConfig({
202
+ name: "centoui",
203
+ cwd
204
+ });
205
+ if (!configFile) throw new Error(`[loadCentoUIConfig] "${CONFIG_FILE_NAME}" not found in "${cwd}". Run \`centoui init\` first.`);
206
+ return config;
207
+ }
208
+ /**
209
+ * Fetches the raw content of the default CentoUI config file from GitHub.
210
+ *
211
+ * This file contains the default icon mappings and other shared defaults
212
+ * that are merged into the user's generated config.
213
+ *
214
+ * @returns Raw UTF-8 content of the default config file.
215
+ * @throws If the network request fails or the server returns a non-2xx status.
216
+ */
217
+ async function fetchDefaultConfig() {
218
+ let response;
200
219
  try {
201
- if (!await fsExtra.pathExists(configFilePath)) throw new Error(`[loadCentoUIConfig] "${CONFIG_FILE_NAME}" not found in "${cwd}". Run \`centoui init\` first.`);
202
- return (await import(pathToFileURL(configFilePath).href)).default;
220
+ response = await fetch(CONFIG_DEFAULTS_URL, { headers: GITHUB_RAW_FETCH_HEADERS });
203
221
  } catch (error) {
204
- throw new Error(`[loadCentoUIConfig] Failed to import "${configFilePath}": ${error}`);
222
+ throw new Error(`[fetchDefaultConfigContent] Network request for default config failed: ${error}`);
205
223
  }
224
+ if (!response.ok) throw new Error(`[fetchDefaultConfigContent] Server returned ${response.status} ${response.statusText} (URL: ${CONFIG_DEFAULTS_URL})`);
225
+ return response.text();
226
+ }
227
+ /**
228
+ * Extracts the inner config object from the default config file content.
229
+ *
230
+ * Strips the `export default`, `satisfies` clause, and outer braces,
231
+ * returning just the inner properties as a raw string.
232
+ *
233
+ * @example
234
+ * // Input:
235
+ * // export default {
236
+ * // icons: { check: 'lucide:check' },
237
+ * // } satisfies Pick<CentoUIConfig, 'icons'>
238
+ * // Output:
239
+ * // " icons: { check: 'lucide:check' },"
240
+ *
241
+ * @param fileContent - The full source of the default config file.
242
+ * @returns The inner config body, or an empty string if no object is found.
243
+ */
244
+ function extractInnerConfigContent(fileContent) {
245
+ let cleanedConfigContent = fileContent.replace(/^import\s+.*$/gm, "");
246
+ cleanedConfigContent = cleanedConfigContent.replace(/^\s*export\s+default\s+/, "");
247
+ cleanedConfigContent = cleanedConfigContent.replace(/\s+satisfies\s+[^}]*$/, "");
248
+ cleanedConfigContent = cleanedConfigContent.trim();
249
+ const firstBrace = cleanedConfigContent.indexOf("{");
250
+ const lastBrace = cleanedConfigContent.lastIndexOf("}");
251
+ if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) return "";
252
+ return cleanedConfigContent.slice(firstBrace + 1, lastBrace).replace(/^\n/, "").replace(/\n\s*$/, "");
206
253
  }
207
254
  /**
208
255
  * Generates the content of a default `centoui.config.ts` file.
@@ -215,18 +262,24 @@ async function loadCentoUIConfig(cwd) {
215
262
  * @param componentsDir - Relative path (from project root) where components will be installed.
216
263
  * @returns A string containing the complete TypeScript source for the config file.
217
264
  */
218
- function buildDefaultConfigFileContent(themeFilePath, componentsDir) {
265
+ /**
266
+ * Generates the content of a user's default `centoui.config.ts` file.
267
+ *
268
+ * Fetches the default config from GitHub to get the latest default config values (icon mappings, etc.),
269
+ * then merges them with the user-provided paths. The generated file uses
270
+ * `defineConfig` so IDEs can provide type-checking and autocompletion.
271
+ *
272
+ * @param themeFilePath - Relative path (from project root) where the CSS theme file lives.
273
+ * @param componentsDir - Relative path (from project root) where components will be installed.
274
+ * @returns A string containing the complete TypeScript source for the config file.
275
+ */
276
+ async function buildUserDefaultConfigFileContent(themeFilePath, componentsDir) {
219
277
  return `import { defineConfig } from 'centoui'
220
278
 
221
279
  export default defineConfig({
222
- version: '${VERSION}',
223
280
  componentsDir: '${componentsDir}',
224
281
  themeFilePath: '${themeFilePath}',
225
- icons: {
226
- check: 'lucide:check',
227
- close: 'lucide:x',
228
- menu: 'lucide:menu',
229
- },
282
+ ${extractInnerConfigContent(await fetchDefaultConfig())}
230
283
  })
231
284
  `;
232
285
  }
@@ -371,10 +424,11 @@ function init() {
371
424
  let registry;
372
425
  await tasks([
373
426
  {
374
- title: `Writing ${CONFIG_FILE_NAME}`,
427
+ title: "Fetching config defaults",
375
428
  task: async () => {
376
429
  if (!shouldWriteConfig) return `Skipped — "${CONFIG_FILE_NAME}" already exists`;
377
- await fsExtra.outputFile(configPath, buildDefaultConfigFileContent(directories.themeFilePath, directories.componentDir), "utf-8");
430
+ const userConfigContent = await buildUserDefaultConfigFileContent(directories.themeFilePath, directories.componentDir);
431
+ await fsExtra.outputFile(configPath, userConfigContent, "utf-8");
378
432
  return `${CONFIG_FILE_NAME} written`;
379
433
  }
380
434
  },
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "centoui-cli",
3
3
  "type": "module",
4
- "version": "1.0.0-alpha.3",
4
+ "version": "1.0.0-alpha.30",
5
5
  "private": false,
6
6
  "description": "Official CLI for CentoUI.",
7
+ "keywords": [
8
+ "cli",
9
+ "vue",
10
+ "ui",
11
+ "components",
12
+ "tailwindcss"
13
+ ],
7
14
  "author": "Favour Emeka <favorodera@gmail.com>",
8
15
  "license": "MIT",
9
16
  "homepage": "https://centoui.vercel.app",
@@ -30,20 +37,22 @@
30
37
  },
31
38
  "devDependencies": {
32
39
  "@types/fs-extra": "^11.0.4",
40
+ "@vitest/ui": "^4.1.5",
33
41
  "tsdown": "^0.21.7",
34
42
  "type-fest": "^5.6.0",
35
- "@vitest/ui": "^4.1.5",
36
43
  "vitest": "^4.1.2"
37
44
  },
38
45
  "dependencies": {
39
46
  "@clack/prompts": "^1.2.0",
47
+ "c12": "4.0.0-beta.5",
40
48
  "citty": "^0.2.2",
41
49
  "fs-extra": "^11.3.4",
42
50
  "nypm": "^0.6.6",
43
51
  "pathe": "^2.0.3"
44
52
  },
45
53
  "engines": {
46
- "node": ">=22.0.0"
54
+ "node": ">=22.0.0",
55
+ "pnpm": ">=11.0.0"
47
56
  },
48
57
  "scripts": {
49
58
  "build": "tsdown",