Skip to content

Commit b28ec9e

Browse files
authored
Merge pull request #2 from izadoesdev/main
feat(commands): list to download all components at once
2 parents be4fde8 + 40ba09b commit b28ec9e

File tree

5 files changed

+94
-91
lines changed

5 files changed

+94
-91
lines changed

cli-component/bin/cli.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ welcome();
1010

1111
program
1212
.name('once-ui-cli')
13-
.description('CLI tool for adding Once UI components')
13+
.description('CLI tool for adding Once UI components')
1414
.version('1.0.0');
1515

1616
program
@@ -19,9 +19,15 @@ program
1919
.action(init);
2020

2121
program
22-
.command('add <component>')
23-
.description('Add a specific component')
24-
.action(add);
22+
.command('add [components...]')
23+
.description('Add specific components or start interactive selection')
24+
.action((components) => {
25+
if (components && components.length > 0) {
26+
add(components.join(' '));
27+
} else {
28+
add();
29+
}
30+
});
2531

2632
program
2733
.command('list')

cli-component/package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli-component/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "once-ui-cli",
3-
"version": "3.1.1",
3+
"version": "3.1.2",
44
"description": "CLI tool for managing Once UI components",
55
"type": "module",
66
"bin": {

cli-component/src/commands/add.js

+66-18
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,79 @@
11
import { components } from '../utils/components.js';
22
import { installComponent } from '../utils/installer.js';
3-
import { error, success } from '../utils/logger.js';
3+
import { error, success, info } from '../utils/logger.js';
44
import boxen from 'boxen';
5+
import inquirer from 'inquirer';
6+
import chalk from 'chalk';
7+
8+
function getProgress(current, total) {
9+
const width = 20;
10+
const filled = Math.round((current / total) * width);
11+
const bar = '█'.repeat(filled) + '░'.repeat(width - filled);
12+
const percent = Math.round((current / total) * 100);
13+
return `${bar} ${percent}%`;
14+
}
15+
16+
async function installComponents(componentsToInstall) {
17+
if (componentsToInstall.length === 0) return;
18+
19+
console.log(boxen(
20+
`\n🚀 Adding ${componentsToInstall.length} components: ${componentsToInstall.join(', ')}\n`,
21+
{ padding: 1, borderStyle: 'round', borderColor: 'cyan' }
22+
));
23+
24+
info('Adding components in: ./once-ui/components\n');
25+
26+
for (let i = 0; i < componentsToInstall.length; i++) {
27+
const component = componentsToInstall[i];
28+
try {
29+
const progress = getProgress(i + 1, componentsToInstall.length);
30+
process.stdout.write(chalk.cyan(`[${progress}] Installing ${component}...\r`));
31+
await installComponent(component);
32+
console.log(chalk.green(`✓ ${component} installed `));
33+
} catch (err) {
34+
error(`Failed to add ${component}: ${err.message}`);
35+
}
36+
}
37+
38+
console.log(boxen(
39+
`\n✨ All components have been added!\n`,
40+
{ padding: 1, borderStyle: 'round', borderColor: 'cyan' }
41+
));
42+
}
543

644
export async function add(componentName) {
7-
// Convert component name to lowercase for case-insensitive comparison
8-
const normalizedComponentName = componentName.toLowerCase();
9-
const normalizedComponents = components.map(c => c.toLowerCase());
10-
11-
// Find the original case version of the component
12-
const originalComponentName = components.find(c => c.toLowerCase() === normalizedComponentName);
13-
14-
// Check if the component exists
15-
if (!normalizedComponents.includes(normalizedComponentName)) {
16-
error(`Component "${componentName}" not found.`);
45+
// If no component name provided, show interactive selection
46+
if (!componentName) {
47+
const choices = components.map(c => ({ name: c, value: c }));
48+
const { selectedComponents } = await inquirer.prompt([{
49+
type: 'checkbox',
50+
name: 'selectedComponents',
51+
message: 'Select components to add (space to select, a to select all):',
52+
choices,
53+
pageSize: 15,
54+
loop: true
55+
}]);
56+
57+
return installComponents(selectedComponents);
58+
}
59+
60+
// Handle direct component names
61+
const requestedComponents = componentName.split(' ').filter(Boolean);
62+
const validComponents = requestedComponents.filter(name =>
63+
components.find(c => c.toLowerCase() === name.toLowerCase())
64+
);
65+
66+
if (validComponents.length !== requestedComponents.length) {
67+
const invalidComponents = requestedComponents.filter(name =>
68+
!components.find(c => c.toLowerCase() === name.toLowerCase())
69+
);
70+
error(`Some components were not found: ${invalidComponents.join(', ')}`);
1771
console.log(boxen(
1872
`Available components:\n${components.join(', ')}`,
1973
{ padding: 1, margin: 1, borderStyle: 'round', borderColor: 'cyan' }
2074
));
2175
return;
2276
}
2377

24-
// Proceed to install the component using the original case version
25-
try {
26-
await installComponent(originalComponentName);
27-
success(`${originalComponentName} added successfully!`);
28-
} catch (err) {
29-
error(`Failed to add ${originalComponentName}: ${err.message}`);
30-
}
78+
return installComponents(validComponents);
3179
}

cli-component/src/utils/installer.js

+15-66
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import path from 'path';
33
import axios from 'axios';
44
import { fetchComponentContent, fetchStyleContent, fetchStyleFile, fetchTokenFile } from './github.js';
55
import { getDependencies } from './parser.js';
6-
import { createSpinner } from './spinner.js';
7-
import { info } from './logger.js';
86

97
async function detectProjectStructure() {
108
// Check for Next.js app directory structure
@@ -36,32 +34,22 @@ export async function installComponent(componentName, targetDir = null) {
3634
targetDir = await detectProjectStructure();
3735
}
3836

39-
const spinner = createSpinner(`Adding ${componentName}...`);
40-
4137
try {
4238
await fs.ensureDir(targetDir);
4339

44-
// Show installation directory on first component
45-
if (!global.installPathShown) {
46-
info(` Adding components in: ${targetDir}`);
47-
global.installPathShown = true;
48-
}
49-
5040
// If it's a SCSS file
5141
if (componentName.endsWith('.module.scss')) {
5242
const componentBaseName = componentName.replace('.module.scss', '');
5343
const styleContent = await fetchStyleContent(componentBaseName);
5444
if (styleContent) {
5545
await installFile(componentName, styleContent, targetDir);
56-
spinner.succeed(`Added style: ${componentName}`);
5746
}
5847
return;
5948
}
6049

6150
// Fetch and install the component
6251
const content = await fetchComponentContent(componentName);
6352
if (!content) {
64-
spinner.succeed(`Failed to fetch ${componentName}`);
6553
return;
6654
}
6755

@@ -74,8 +62,6 @@ export async function installComponent(componentName, targetDir = null) {
7462
await installFile(`${componentName}.module.scss`, styleContent, targetDir);
7563
}
7664

77-
spinner.succeed(`${componentName} added`);
78-
7965
// Install dependencies
8066
const dependencies = await getDependencies(content);
8167
for (const dep of dependencies) {
@@ -103,150 +89,113 @@ export async function installComponent(componentName, targetDir = null) {
10389
// Install config.js file
10490
await installConfigFile();
10591
} catch (error) {
106-
// Suppress error messages
92+
throw error;
10793
}
10894
}
10995

11096
async function createIconsFile() {
11197
const iconsFilePath = './once-ui/icons.ts';
11298
const iconsContent = await fetchIconsContent();
113-
11499
await installFile('icons.ts', iconsContent, './once-ui');
115100
}
116101

117102
async function fetchIconsContent() {
118103
const GITHUB_ICONS_URL = 'https://raw.githubusercontent.com/once-ui-system/nextjs-starter/main/src/once-ui/icons.ts';
119-
120104
try {
121105
const response = await axios.get(GITHUB_ICONS_URL);
122106
return response.data;
123107
} catch (err) {
124-
return ''; // Suppress error
108+
return '';
125109
}
126110
}
127111

128112
async function installInterfacesAndTypes() {
129-
const interfacesFilePath = './once-ui/interfaces.ts';
130-
const typesFilePath = './once-ui/types.ts';
131-
132113
const interfacesContent = await fetchInterfacesContent();
133114
const typesContent = await fetchTypesContent();
134-
135115
await installFile('interfaces.ts', interfacesContent, './once-ui');
136116
await installFile('types.ts', typesContent, './once-ui');
137117
}
138118

139119
async function fetchInterfacesContent() {
140120
const GITHUB_INTERFACES_URL = 'https://raw.githubusercontent.com/once-ui-system/nextjs-starter/main/src/once-ui/interfaces.ts';
141-
142121
try {
143122
const response = await axios.get(GITHUB_INTERFACES_URL);
144123
return response.data;
145124
} catch (err) {
146-
return ''; // Suppress error
125+
return '';
147126
}
148127
}
149128

150129
async function fetchTypesContent() {
151130
const GITHUB_TYPES_URL = 'https://raw.githubusercontent.com/once-ui-system/nextjs-starter/main/src/once-ui/types.ts';
152-
153131
try {
154132
const response = await axios.get(GITHUB_TYPES_URL);
155133
return response.data;
156134
} catch (err) {
157-
return ''; // Suppress error
135+
return '';
158136
}
159137
}
160138

161139
async function installUseDebounce() {
162140
const hooksDir = './once-ui/hooks';
163-
await fs.ensureDir(hooksDir); // Create hooks directory if it doesn't exist
164-
165-
const useDebounceFilePath = './once-ui/hooks/useDebounce.ts';
141+
await fs.ensureDir(hooksDir);
166142
const useDebounceContent = await fetchUseDebounceContent();
167-
168143
await installFile('useDebounce.ts', useDebounceContent, hooksDir);
169144
}
170145

171146
async function fetchUseDebounceContent() {
172147
const GITHUB_USE_DEBOUNCE_URL = 'https://raw.githubusercontent.com/once-ui-system/nextjs-starter/main/src/once-ui/hooks/useDebounce.ts';
173-
174148
try {
175149
const response = await axios.get(GITHUB_USE_DEBOUNCE_URL);
176150
return response.data;
177151
} catch (err) {
178-
return ''; // Suppress error
152+
return '';
179153
}
180154
}
181155

182156
async function installConfigFile() {
183157
const resourcesDir = './once-ui/resources';
184-
await fs.ensureDir(resourcesDir); // Create resources directory if it doesn't exist
185-
186-
const configFilePath = './once-ui/resources/config.js';
158+
await fs.ensureDir(resourcesDir);
187159
const configContent = await fetchConfigContent();
188-
189160
await installFile('config.js', configContent, resourcesDir);
190161
}
191162

192163
async function fetchConfigContent() {
193164
const GITHUB_CONFIG_URL = 'https://raw.githubusercontent.com/once-ui-system/nextjs-starter/main/src/once-ui/resources/config.js';
194-
195165
try {
196166
const response = await axios.get(GITHUB_CONFIG_URL);
197167
return response.data;
198168
} catch (err) {
199-
return ''; // Suppress error
169+
return '';
200170
}
201171
}
202172

203173
async function installStylesAndTokens() {
204174
const stylesDir = './once-ui/styles';
205175
const tokensDir = './once-ui/tokens';
206176

207-
// Create directories for styles and tokens
208177
await fs.ensureDir(stylesDir);
209178
await fs.ensureDir(tokensDir);
210179

211-
// List of styles to install
212180
const styles = [
213-
'background.scss',
214-
'border.scss',
215-
'breakpoints.scss',
216-
'color.scss',
217-
'display.scss',
218-
'flex.scss',
219-
'global.scss',
220-
'grid.scss',
221-
'index.scss',
222-
'layout.scss',
223-
'position.scss',
224-
'shadow.scss',
225-
'size.scss',
226-
'spacing.scss',
227-
'typography.scss',
181+
'background.scss', 'border.scss', 'breakpoints.scss',
182+
'color.scss', 'display.scss', 'flex.scss', 'global.scss',
183+
'grid.scss', 'index.scss', 'layout.scss', 'position.scss',
184+
'shadow.scss', 'size.scss', 'spacing.scss', 'typography.scss',
228185
'utilities.scss'
229186
];
230187

231-
// Install styles
232188
for (const style of styles) {
233189
const styleContent = await fetchStyleFile(style);
234190
await installFile(style, styleContent, stylesDir);
235191
}
236192

237-
// List of tokens to install
238193
const tokens = [
239-
'border.scss',
240-
'function.scss',
241-
'index.scss',
242-
'layout.scss',
243-
'scheme.scss',
244-
'shadow.scss',
245-
'theme.scss',
246-
'typography.scss'
194+
'border.scss', 'function.scss', 'index.scss',
195+
'layout.scss', 'scheme.scss', 'shadow.scss',
196+
'theme.scss', 'typography.scss'
247197
];
248198

249-
// Install tokens
250199
for (const token of tokens) {
251200
const tokenContent = await fetchTokenFile(token);
252201
await installFile(token, tokenContent, tokensDir);

0 commit comments

Comments
 (0)