@nons-dev/uikit 0.1.3 → 0.1.5
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/LICENSE +16 -0
- package/dist/font/IRANSansX-ExtraBold.woff +0 -0
- package/dist/font/IRANSansX-Medium.woff +0 -0
- package/dist/font/IRANSansX-Regular.woff +0 -0
- package/dist/font/Satoshi-Bold.woff2 +0 -0
- package/dist/font/Satoshi-Medium.woff2 +0 -0
- package/dist/font/Satoshi-Regular.woff2 +0 -0
- package/dist/index.css +1 -1
- package/generated/components.json +2665 -0
- package/package.json +9 -3
- package/readme.md +66 -16
- package/scripts/cli.mjs +256 -0
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nons-dev/uikit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"license": "BUSL-1.1",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"module": "./dist/index.js",
|
|
@@ -13,9 +14,14 @@
|
|
|
13
14
|
"registry": "https://registry.npmjs.org/",
|
|
14
15
|
"access": "public"
|
|
15
16
|
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"nons": "scripts/cli.mjs"
|
|
19
|
+
},
|
|
16
20
|
"files": [
|
|
17
21
|
"dist",
|
|
18
|
-
"
|
|
22
|
+
"generated/components.json",
|
|
23
|
+
"scripts/cli.mjs",
|
|
24
|
+
"readme.md"
|
|
19
25
|
],
|
|
20
26
|
"exports": {
|
|
21
27
|
".": {
|
|
@@ -30,7 +36,7 @@
|
|
|
30
36
|
},
|
|
31
37
|
"scripts": {
|
|
32
38
|
"dev": "vite",
|
|
33
|
-
"build": "npm run generate && vue-tsc -b && vite build",
|
|
39
|
+
"build": "npm run generate && vue-tsc -b && vite build && node scripts/copy-fonts.mjs",
|
|
34
40
|
"preview": "vite preview",
|
|
35
41
|
"generate": "tsx scripts/generate.ts"
|
|
36
42
|
},
|
package/readme.md
CHANGED
|
@@ -5,32 +5,80 @@ Schema-driven UI component library for the Nons platform — Vue 3, TypeScript,
|
|
|
5
5
|
## Architecture
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
nons.config.yaml Single source of truth for theme, tokens, locale, direction, font
|
|
9
|
+
core/ Schema types, component registry, schema renderer, runtime context
|
|
10
|
+
ui/ Vue 3 components (primitives, composites, layouts, sections)
|
|
11
|
+
generated/ Auto-generated files from nons.config.yaml (tokens.css, theme.css, config.ts, locales/)
|
|
12
|
+
design-system/ CSS reset, global styles (imports generated/ tokens)
|
|
13
|
+
playground/ Demo/explorer app for component development and testing
|
|
14
|
+
plugins/ i18n, direction, permissions
|
|
15
|
+
adapters/ Data provider layer (mock API, storage abstraction)
|
|
16
|
+
locales/ Persian (fa) and English (en) translation YAML files
|
|
17
|
+
scripts/ CLI tools (generate, init, component discovery)
|
|
16
18
|
```
|
|
17
19
|
|
|
18
20
|
## Quick Start
|
|
19
21
|
|
|
22
|
+
```bash
|
|
23
|
+
# Install
|
|
24
|
+
npm install @nons-dev/uikit
|
|
25
|
+
|
|
26
|
+
# Generate config file in your project root
|
|
27
|
+
npx @nons-dev/uikit init
|
|
28
|
+
|
|
29
|
+
# Edit nons.config.yaml to customize theme, tokens, locale, direction, font
|
|
30
|
+
|
|
31
|
+
# Use in your app
|
|
32
|
+
import { bootstrapRegistry, loadConfig } from '@nons-dev/uikit'
|
|
33
|
+
import '@nons-dev/uikit/style.css'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Development (UI Kit only)
|
|
37
|
+
|
|
20
38
|
```bash
|
|
21
39
|
npm install
|
|
22
|
-
npm run dev
|
|
23
|
-
npm run build
|
|
24
|
-
npm run
|
|
40
|
+
npm run dev # Start Vite dev server (playground app)
|
|
41
|
+
npm run build # Generate tokens + type-check + library build
|
|
42
|
+
npm run generate # Regenerate CSS/theme from nons.config.yaml
|
|
43
|
+
npm run preview # Preview production playground build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Config System
|
|
47
|
+
|
|
48
|
+
The `nons.config.yaml` file at your project root is the **single source of truth**:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
theme:
|
|
52
|
+
preset: nons # theme preset (nons, custom)
|
|
53
|
+
|
|
54
|
+
brand:
|
|
55
|
+
name: Nons # brand name
|
|
56
|
+
|
|
57
|
+
direction:
|
|
58
|
+
default: rtl # UI direction (rtl, ltr)
|
|
59
|
+
|
|
60
|
+
locale:
|
|
61
|
+
default: fa # default language
|
|
62
|
+
supported: [fa, en] # supported languages
|
|
63
|
+
|
|
64
|
+
font:
|
|
65
|
+
fa:
|
|
66
|
+
family: IRANSansX
|
|
67
|
+
en:
|
|
68
|
+
family: Satoshi
|
|
25
69
|
```
|
|
26
70
|
|
|
71
|
+
Run `npx @nons-dev/uikit generate` to regenerate CSS variables from your config.
|
|
72
|
+
|
|
27
73
|
## CLI Commands
|
|
28
74
|
|
|
29
75
|
```bash
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
76
|
+
npx @nons-dev/uikit init # Create nons.config.yaml in project root
|
|
77
|
+
npx @nons-dev/uikit generate # Generate CSS from nons.config.yaml
|
|
78
|
+
npx @nons-dev/uikit list # List all components
|
|
79
|
+
npx @nons-dev/uikit show <name> # Show component details (props, events, slots)
|
|
80
|
+
npx @nons-dev/uikit schema <name> # Show schema JSON for a component
|
|
81
|
+
npx @nons-dev/uikit search <query> # Search components by name or description
|
|
34
82
|
```
|
|
35
83
|
|
|
36
84
|
## Documentation
|
|
@@ -43,8 +91,9 @@ npm run ui -- components:search <q> # Search components by name/description
|
|
|
43
91
|
## Key Conventions
|
|
44
92
|
|
|
45
93
|
- **Zero-hardcoding** — all visual values via CSS custom properties (`var(--...)`)
|
|
94
|
+
- **Config-driven** — theme, tokens, direction, locale all from `nons.config.yaml`
|
|
46
95
|
- **RTL-first** — `<html dir="rtl">`, logical properties (`inset-inline-start`, `margin-inline`)
|
|
47
|
-
- **Schema-driven** — every component has a registry key, contract, and
|
|
96
|
+
- **Schema-driven** — every component has a registry key, contract, schema example, and auto-generated metadata (53 components parsed from source)
|
|
48
97
|
- **modelValue** — all form inputs use `modelValue` prop + `update:modelValue` emit
|
|
49
98
|
- **No external margins** — component spacing via parent layout `gap`
|
|
50
99
|
- **Flat UI** — no elevation shadows; separation via themed borders (`--border-primary`)
|
|
@@ -56,3 +105,4 @@ npm run ui -- components:search <q> # Search components by name/description
|
|
|
56
105
|
- `@lucide/vue` icons
|
|
57
106
|
- Tabler Icons (CDN)
|
|
58
107
|
- Plain CSS only (no SCSS/Sass)
|
|
108
|
+
- Font files bundled in `dist/font/` (IRANSansX, Satoshi) — loaded via `@font-face` in generated CSS
|
package/scripts/cli.mjs
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import * as yaml from 'js-yaml'
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
const COMPONENTS_JSON = path.join(__dirname, '..', 'generated', 'components.json')
|
|
10
|
+
const CWD = process.cwd()
|
|
11
|
+
|
|
12
|
+
const RESET = '\x1b[0m'
|
|
13
|
+
const BOLD = '\x1b[1m'
|
|
14
|
+
const DIM = '\x1b[2m'
|
|
15
|
+
const GREEN = '\x1b[32m'
|
|
16
|
+
const CYAN = '\x1b[36m'
|
|
17
|
+
const YELLOW = '\x1b[33m'
|
|
18
|
+
const MAGENTA = '\x1b[35m'
|
|
19
|
+
const WHITE = '\x1b[37m'
|
|
20
|
+
const GRAY = '\x1b[90m'
|
|
21
|
+
const RED = '\x1b[31m'
|
|
22
|
+
const BLUE = '\x1b[34m'
|
|
23
|
+
|
|
24
|
+
const DEFAULT_CONFIG = `theme:
|
|
25
|
+
preset: nons
|
|
26
|
+
|
|
27
|
+
brand:
|
|
28
|
+
name: Nons
|
|
29
|
+
|
|
30
|
+
direction:
|
|
31
|
+
default: rtl
|
|
32
|
+
|
|
33
|
+
locale:
|
|
34
|
+
default: fa
|
|
35
|
+
supported:
|
|
36
|
+
- fa
|
|
37
|
+
- en
|
|
38
|
+
|
|
39
|
+
font:
|
|
40
|
+
fa:
|
|
41
|
+
family: IRANSansX
|
|
42
|
+
en:
|
|
43
|
+
family: Satoshi
|
|
44
|
+
`
|
|
45
|
+
|
|
46
|
+
// ── Data ──
|
|
47
|
+
|
|
48
|
+
function loadComponents() {
|
|
49
|
+
if (!fs.existsSync(COMPONENTS_JSON)) {
|
|
50
|
+
console.error(`${RED}✗${RESET} Component metadata not found.`)
|
|
51
|
+
console.error(` Expected: ${COMPONENTS_JSON}`)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
return JSON.parse(fs.readFileSync(COMPONENTS_JSON, 'utf-8'))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function findComponent(data, query) {
|
|
58
|
+
const lower = query.toLowerCase()
|
|
59
|
+
return data.components.find(c =>
|
|
60
|
+
c.name.toLowerCase() === lower || c.registryKey === lower
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function searchComponents(data, query) {
|
|
65
|
+
const lower = query.toLowerCase()
|
|
66
|
+
return data.components.filter(c =>
|
|
67
|
+
c.name.toLowerCase().includes(lower) ||
|
|
68
|
+
c.registryKey.includes(lower) ||
|
|
69
|
+
(c.description || '').toLowerCase().includes(lower)
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Commands ──
|
|
74
|
+
|
|
75
|
+
function cmdInit() {
|
|
76
|
+
const dest = path.join(CWD, 'nons.config.yaml')
|
|
77
|
+
if (fs.existsSync(dest)) {
|
|
78
|
+
console.log(`${YELLOW}⚠${RESET} nons.config.yaml already exists — skipping.`)
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
fs.writeFileSync(dest, DEFAULT_CONFIG, 'utf-8')
|
|
82
|
+
console.log(`${GREEN}✓${RESET} nons.config.yaml created in`, CWD)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cmdGenerate() {
|
|
86
|
+
const configPath = path.join(CWD, 'nons.config.yaml')
|
|
87
|
+
if (!fs.existsSync(configPath)) {
|
|
88
|
+
console.error(`${RED}✗${RESET} nons.config.yaml not found. Run \`nons init\` first.`)
|
|
89
|
+
process.exit(1)
|
|
90
|
+
}
|
|
91
|
+
const raw = fs.readFileSync(configPath, 'utf-8')
|
|
92
|
+
const config = yaml.load(raw)
|
|
93
|
+
const generatedDir = path.join(CWD, 'generated')
|
|
94
|
+
if (!fs.existsSync(generatedDir)) fs.mkdirSync(generatedDir, { recursive: true })
|
|
95
|
+
|
|
96
|
+
const lines = [':root {']
|
|
97
|
+
const sections = ['colors', 'spacing', 'typography', 'radius', 'z-index', 'opacity', 'button', 'alert']
|
|
98
|
+
for (const section of sections) {
|
|
99
|
+
const tokens = config.tokens?.[section]
|
|
100
|
+
if (!tokens) continue
|
|
101
|
+
for (const [key, val] of Object.entries(tokens)) lines.push(` ${key}: ${val};`)
|
|
102
|
+
}
|
|
103
|
+
lines.push('}')
|
|
104
|
+
fs.writeFileSync(path.join(generatedDir, 'tokens.css'), lines.join('\n'), 'utf-8')
|
|
105
|
+
console.log(`${GREEN}✓${RESET} generated/tokens.css`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function cmdList() {
|
|
109
|
+
const data = loadComponents()
|
|
110
|
+
const grouped = {}
|
|
111
|
+
for (const c of data.components) {
|
|
112
|
+
if (!grouped[c.category]) grouped[c.category] = []
|
|
113
|
+
grouped[c.category].push(c)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log(`\n${BOLD}${CYAN}Components (${data.components.length})${RESET}\n`)
|
|
117
|
+
const order = ['primitive', 'layout', 'component', 'section']
|
|
118
|
+
const labels = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }
|
|
119
|
+
const colors = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }
|
|
120
|
+
|
|
121
|
+
for (const cat of order) {
|
|
122
|
+
const items = grouped[cat]
|
|
123
|
+
if (!items || !items.length) continue
|
|
124
|
+
const color = colors[cat]
|
|
125
|
+
console.log(` ${color}${BOLD}── ${labels[cat]} (${items.length})${RESET}\n`)
|
|
126
|
+
for (const c of items) {
|
|
127
|
+
const desc = c.description ? ` ${GRAY}${c.description.substring(0, 50)}${c.description.length > 50 ? '…' : ''}${RESET}` : ''
|
|
128
|
+
console.log(` ${BOLD}${c.name}${RESET} ${DIM}[${c.registryKey}]${RESET}${desc}`)
|
|
129
|
+
}
|
|
130
|
+
console.log('')
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function cmdShow(name) {
|
|
135
|
+
const data = loadComponents()
|
|
136
|
+
const comp = findComponent(data, name)
|
|
137
|
+
if (!comp) {
|
|
138
|
+
console.log(`\n ${RED}✗ Component "${name}" not found.${RESET}\n`)
|
|
139
|
+
const similar = searchComponents(data, name).filter(c => c.name !== name)
|
|
140
|
+
if (similar.length) {
|
|
141
|
+
console.log(` ${YELLOW}Did you mean:${RESET}`)
|
|
142
|
+
for (const s of similar) console.log(` • ${s.name} ${DIM}[${s.registryKey}]${RESET}`)
|
|
143
|
+
console.log('')
|
|
144
|
+
}
|
|
145
|
+
process.exit(1)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const color = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }[comp.category] || CYAN
|
|
149
|
+
const label = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }[comp.category] || comp.category
|
|
150
|
+
|
|
151
|
+
console.log(`\n${BOLD}${CYAN}${comp.name}${RESET}\n`)
|
|
152
|
+
console.log(` ${DIM}Category:${RESET} ${color}${BOLD}${label}${RESET}`)
|
|
153
|
+
console.log(` ${DIM}Registry Key:${RESET} ${comp.registryKey}`)
|
|
154
|
+
console.log(` ${DIM}File:${RESET} ${comp.filePath}`)
|
|
155
|
+
if (comp.description) console.log(`\n ${comp.description}\n`)
|
|
156
|
+
|
|
157
|
+
if (comp.props.length) {
|
|
158
|
+
console.log(` ${BOLD}Props:${RESET}\n`)
|
|
159
|
+
for (const p of comp.props) {
|
|
160
|
+
const req = p.required ? `${YELLOW}required${RESET}` : `${DIM}optional${RESET}`
|
|
161
|
+
console.log(` ${BOLD}${p.name}${RESET} ${req}`)
|
|
162
|
+
console.log(` ${DIM}type:${RESET} ${CYAN}${p.type}${RESET}`)
|
|
163
|
+
if (p.allowedValues?.length) console.log(` ${DIM}allowed:${RESET} ${p.allowedValues.map(v => `${GREEN}'${v}'${RESET}`).join(' | ')}`)
|
|
164
|
+
if (p.default !== undefined) console.log(` ${DIM}default:${RESET} ${MAGENTA}${p.default}${RESET}`)
|
|
165
|
+
if (p.description) console.log(` ${DIM}desc:${RESET} ${p.description}`)
|
|
166
|
+
console.log('')
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (comp.events.length) {
|
|
171
|
+
console.log(` ${BOLD}Events:${RESET}\n`)
|
|
172
|
+
for (const e of comp.events) {
|
|
173
|
+
const payload = e.payload ? ` ${DIM}→ ${e.payload}${RESET}` : ''
|
|
174
|
+
console.log(` ${YELLOW}@${e.name}${RESET}${payload}`)
|
|
175
|
+
}
|
|
176
|
+
console.log('')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (comp.slots.length) {
|
|
180
|
+
console.log(` ${BOLD}Slots:${RESET}\n`)
|
|
181
|
+
for (const s of comp.slots) console.log(` ${MAGENTA}#${s.name}${RESET}`)
|
|
182
|
+
console.log('')
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (comp.schemaExample) {
|
|
186
|
+
console.log(` ${BOLD}Schema Example:${RESET}\n`)
|
|
187
|
+
for (const line of comp.schemaExample.split('\n')) console.log(` ${GRAY}${line}${RESET}`)
|
|
188
|
+
console.log('')
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function cmdSchema(name) {
|
|
193
|
+
const data = loadComponents()
|
|
194
|
+
const comp = findComponent(data, name)
|
|
195
|
+
if (!comp) {
|
|
196
|
+
console.log(`\n ${RED}✗ Component "${name}" not found.${RESET}\n`)
|
|
197
|
+
process.exit(1)
|
|
198
|
+
}
|
|
199
|
+
console.log(`\n${BOLD}${CYAN}Schema: ${comp.name}${RESET}\n`)
|
|
200
|
+
console.log(comp.schemaExample || JSON.stringify({ type: comp.registryKey, props: {} }, null, 2))
|
|
201
|
+
console.log('')
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function cmdSearch(query) {
|
|
205
|
+
const data = loadComponents()
|
|
206
|
+
const matches = searchComponents(data, query)
|
|
207
|
+
if (!matches.length) {
|
|
208
|
+
console.log(`\n ${YELLOW}No components matched "${query}"${RESET}\n`)
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
const colors = { primitive: GREEN, layout: BLUE, component: CYAN, section: MAGENTA }
|
|
212
|
+
const labels = { primitive: 'Primitive', layout: 'Layout', component: 'Component', section: 'Section' }
|
|
213
|
+
console.log(`\n${BOLD}${CYAN}Search: "${query}"${RESET} ${DIM}(${matches.length} found)${RESET}\n`)
|
|
214
|
+
for (const c of matches) {
|
|
215
|
+
const color = colors[c.category] || CYAN
|
|
216
|
+
console.log(` ${BOLD}${c.name}${RESET} ${color}${labels[c.category] || c.category}${RESET} ${DIM}[${c.registryKey}]${RESET}`)
|
|
217
|
+
if (c.description) console.log(` ${GRAY}${c.description.substring(0, 70)}${c.description.length > 70 ? '…' : ''}${RESET}`)
|
|
218
|
+
}
|
|
219
|
+
console.log('')
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Help ──
|
|
223
|
+
|
|
224
|
+
function printHelp() {
|
|
225
|
+
console.log(`\n${BOLD}${CYAN}Nons UIKit CLI${RESET}\n`)
|
|
226
|
+
console.log(`${BOLD}Commands:${RESET}\n`)
|
|
227
|
+
console.log(` ${GREEN}init${RESET} Create nons.config.yaml in current directory`)
|
|
228
|
+
console.log(` ${GREEN}generate${RESET} Generate CSS from nons.config.yaml`)
|
|
229
|
+
console.log(` ${GREEN}list${RESET} List all components`)
|
|
230
|
+
console.log(` ${GREEN}show${RESET} ${DIM}<name>${RESET} Show full detail for a component`)
|
|
231
|
+
console.log(` ${GREEN}schema${RESET} ${DIM}<name>${RESET} Show schema JSON for a component`)
|
|
232
|
+
console.log(` ${GREEN}search${RESET} ${DIM}<query>${RESET} Search components by name/description`)
|
|
233
|
+
console.log(` ${GREEN}help${RESET} Show this help\n`)
|
|
234
|
+
console.log(`${BOLD}Usage:${RESET}\n`)
|
|
235
|
+
console.log(` ${DIM}npx @nons-dev/uikit list${RESET}`)
|
|
236
|
+
console.log(` ${DIM}npx @nons-dev/uikit show Button${RESET}`)
|
|
237
|
+
console.log(` ${DIM}npx @nons-dev/uikit schema Input${RESET}`)
|
|
238
|
+
console.log(` ${DIM}npx @nons-dev/uikit search table${RESET}`)
|
|
239
|
+
console.log(` ${DIM}npx @nons-dev/uikit init${RESET}`)
|
|
240
|
+
console.log(` ${DIM}npx @nons-dev/uikit generate${RESET}\n`)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ── Main ──
|
|
244
|
+
|
|
245
|
+
const command = process.argv[2]
|
|
246
|
+
const param = process.argv[3]
|
|
247
|
+
|
|
248
|
+
switch (command) {
|
|
249
|
+
case 'init': cmdInit(); break
|
|
250
|
+
case 'generate': cmdGenerate(); break
|
|
251
|
+
case 'list': cmdList(); break
|
|
252
|
+
case 'show': param ? cmdShow(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break
|
|
253
|
+
case 'schema': param ? cmdSchema(param) : (console.error(`${RED}✗${RESET} Missing component name.`), process.exit(1)); break
|
|
254
|
+
case 'search': param ? cmdSearch(param) : (console.error(`${RED}✗${RESET} Missing search query.`), process.exit(1)); break
|
|
255
|
+
case '--help': case '-h': case 'help': default: printHelp(); break
|
|
256
|
+
}
|