@lyda/kilo-ui 0.1.1 → 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/README.md +6 -4
- package/bin/kilo-ui.mjs +274 -19
- package/bin/postinstall.mjs +26 -5
- package/config/define-config.d.ts +32 -0
- package/config/define-config.mjs +4 -0
- package/config/kilo.config.example.ts +34 -0
- package/package.json +12 -2
- package/registry/vue/env.d.ts +7 -0
package/README.md
CHANGED
|
@@ -12,12 +12,13 @@ After you **publish** this project to npm, install the **exact** `name` from its
|
|
|
12
12
|
**Do not** run `npm install -D kilo-ui` expecting this Vue CLI — the public npm name `kilo-ui` is already used by a different project.
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
npm install -D @lyda/kilo-ui
|
|
16
|
-
npx kilo-ui init
|
|
15
|
+
npm install -D @lyda/kilo-ui@latest
|
|
17
16
|
npx kilo-ui add data-table
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
**First install in a new project:** the package **postinstall** runs `kilo-ui init` once (unless `kilo.config.ts` already exists). That creates **`kilo.config.ts` in your app root** (next to `package.json`) — not inside `node_modules`. Edit **`ui.componentsDir`** (or **`componentsPrefix`**) for the install folder (e.g. `ki` → `src/components/ki/...`). If the folder is not `ui`, **`kilo-ui add`** injects **`defineOptions({ name: "KiCardBox" })`** so you can use **`<KiCardBox />`** (prefix + file name). Use **`ui.componentTagPrefix: false`** to skip that. An annotated example ships at **`node_modules/@lyda/kilo-ui/config/kilo.config.example.ts`**. If install used **`--ignore-scripts`**, run **`npx kilo-ui init`** once yourself.
|
|
20
|
+
|
|
21
|
+
You can still run **`npx kilo-ui init`** anytime to add missing scaffold files; use **`init --force`** to reset **`kilo.config.ts`** to defaults (back up first if you customized it).
|
|
21
22
|
|
|
22
23
|
**Note:** The public npm name `kilo-ui` may point to a different project (not this Vue CLI). If `npm install -D kilo-ui` pulls SvelteKit or other wrong peers, use a scoped name or install from `file:` / Git. See [Installation](docs/docs/installation.md) in the docs site.
|
|
23
24
|
|
|
@@ -25,9 +26,10 @@ If the package was published with an installer hook, `npm install -D ...` will r
|
|
|
25
26
|
|
|
26
27
|
`npx kilo-ui init` creates:
|
|
27
28
|
|
|
28
|
-
-
|
|
29
|
+
- **`kilo.config.ts`** - TypeScript project config (`defineConfig` from `@lyda/kilo-ui/config`), aliases, and **`ui.componentsDir`** (subfolder for installed components, default `ui`)
|
|
29
30
|
- `src/styles/teamwork-ui.css` - Tailwind v4 import + Kilo UI theme tokens
|
|
30
31
|
- `src/lib/utils.ts` - shared helper utilities
|
|
32
|
+
- `env.d.ts` (project root) - Vue + Vite TypeScript declarations for `*.vue` modules (skip if the file already exists); `init` also updates `tsconfig.app.json` / `tsconfig.json` with `paths` for `@/*` when possible
|
|
31
33
|
|
|
32
34
|
Add the stylesheet once in your app entry, usually `src/main.ts`:
|
|
33
35
|
|
package/bin/kilo-ui.mjs
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createJiti } from 'jiti'
|
|
2
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
-
import { dirname, join, relative, resolve } from 'node:path'
|
|
4
|
+
import { basename, dirname, join, relative, resolve } from 'node:path'
|
|
4
5
|
import { fileURLToPath } from 'node:url'
|
|
5
6
|
|
|
6
7
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
8
|
const packageRoot = resolve(__dirname, '..')
|
|
8
9
|
const registryRoot = join(packageRoot, 'registry')
|
|
10
|
+
const cliEntry = fileURLToPath(import.meta.url)
|
|
9
11
|
|
|
10
12
|
const pkg = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'))
|
|
11
13
|
const cliName = typeof pkg.name === 'string' ? pkg.name : 'kilo-ui'
|
|
14
|
+
const pkgName = cliName
|
|
12
15
|
|
|
13
16
|
const commands = new Set(['init', 'add', 'list', 'help'])
|
|
14
17
|
const [, , maybeCommand, ...args] = process.argv
|
|
15
18
|
const command = commands.has(maybeCommand) ? maybeCommand : 'help'
|
|
16
19
|
|
|
17
|
-
const
|
|
20
|
+
const KILO_TS = 'kilo.config.ts'
|
|
21
|
+
const KILO_MTS = 'kilo.config.mts'
|
|
22
|
+
const KILO_JS = 'kilo.config.js'
|
|
23
|
+
const KILO_MJS = 'kilo.config.mjs'
|
|
24
|
+
const CONFIG_MODULES = [KILO_TS, KILO_MTS, KILO_JS, KILO_MJS]
|
|
25
|
+
|
|
26
|
+
const UI_JSON = 'ui.json'
|
|
27
|
+
const LEGACY_JSON = 'components.json'
|
|
18
28
|
|
|
19
29
|
function log(message = '') {
|
|
20
30
|
console.log(message)
|
|
@@ -26,7 +36,8 @@ function fail(message) {
|
|
|
26
36
|
}
|
|
27
37
|
|
|
28
38
|
function readJson(path) {
|
|
29
|
-
|
|
39
|
+
const raw = readFileSync(path, 'utf8').replace(/^\uFEFF/, '')
|
|
40
|
+
return JSON.parse(raw)
|
|
30
41
|
}
|
|
31
42
|
|
|
32
43
|
function writeJson(path, value) {
|
|
@@ -51,17 +62,121 @@ function registry() {
|
|
|
51
62
|
return readJson(join(registryRoot, 'index.json'))
|
|
52
63
|
}
|
|
53
64
|
|
|
65
|
+
const defaultAliases = {
|
|
66
|
+
components: '@/components',
|
|
67
|
+
composables: '@/composables',
|
|
68
|
+
lib: '@/lib',
|
|
69
|
+
types: '@/types',
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeConfig(raw) {
|
|
73
|
+
const c = typeof raw === 'object' && raw !== null ? { ...raw } : {}
|
|
74
|
+
c.srcDir = c.srcDir || 'src'
|
|
75
|
+
c.aliases = { ...defaultAliases, ...(c.aliases || {}) }
|
|
76
|
+
c.tailwind = c.tailwind || { css: `${c.srcDir}/styles/teamwork-ui.css` }
|
|
77
|
+
const uiRaw =
|
|
78
|
+
typeof c.ui === 'object' && c.ui !== null ? { ...c.ui } : {}
|
|
79
|
+
const componentsPrefix = uiRaw.componentsPrefix
|
|
80
|
+
delete uiRaw.componentsPrefix
|
|
81
|
+
c.ui = { componentsDir: 'ui', ...uiRaw }
|
|
82
|
+
if (
|
|
83
|
+
(c.ui.componentsDir === undefined || c.ui.componentsDir === null) &&
|
|
84
|
+
componentsPrefix !== undefined &&
|
|
85
|
+
componentsPrefix !== null
|
|
86
|
+
) {
|
|
87
|
+
c.ui.componentsDir = componentsPrefix
|
|
88
|
+
}
|
|
89
|
+
if (c.ui.componentsDir === undefined || c.ui.componentsDir === null) {
|
|
90
|
+
c.ui.componentsDir = 'ui'
|
|
91
|
+
}
|
|
92
|
+
return c
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** "ki" → "Ki", "my-kit" → "MyKit" */
|
|
96
|
+
function segmentToPascal(segment) {
|
|
97
|
+
return String(segment)
|
|
98
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
99
|
+
.filter(Boolean)
|
|
100
|
+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase())
|
|
101
|
+
.join('')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Pascal prefix for defineOptions name, or null if disabled / default ui folder. */
|
|
105
|
+
function effectiveDevTagPrefix(config) {
|
|
106
|
+
const v = config.ui?.componentTagPrefix
|
|
107
|
+
if (v === false) return null
|
|
108
|
+
if (typeof v === 'string' && v.trim().length) return segmentToPascal(v.trim())
|
|
109
|
+
|
|
110
|
+
const segs = componentDirSegments(config)
|
|
111
|
+
const first = segs[0]
|
|
112
|
+
if (!first) return null
|
|
113
|
+
if (first.toLowerCase() === 'ui') return null
|
|
114
|
+
return segmentToPascal(first)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function vueResolvedComponentName(config, fileBaseNoExt) {
|
|
118
|
+
const prefix = effectiveDevTagPrefix(config)
|
|
119
|
+
if (!prefix) return null
|
|
120
|
+
return `${prefix}${fileBaseNoExt}`
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function injectVueComponentName(content, name) {
|
|
124
|
+
if (/\bdefineOptions\s*\(/.test(content)) return content
|
|
125
|
+
const safe = String(name).replace(/[^A-Za-z0-9_]/g, '')
|
|
126
|
+
if (!safe) return content
|
|
127
|
+
const injectLine = `defineOptions({ name: '${safe}' })\n`
|
|
128
|
+
const m = content.match(/<script setup[^>]*>/)
|
|
129
|
+
if (!m || m.index === undefined) return content
|
|
130
|
+
const end = m.index + m[0].length
|
|
131
|
+
return `${content.slice(0, end)}\n${injectLine}${content.slice(end)}`
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function hasKiloModuleConfig(cwd) {
|
|
135
|
+
return CONFIG_MODULES.some((name) => existsSync(join(cwd, name)))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function loadProjectConfigFromDisk(cwd) {
|
|
139
|
+
const jiti = createJiti(cliEntry, { interopDefault: true, cwd })
|
|
140
|
+
|
|
141
|
+
for (const name of CONFIG_MODULES) {
|
|
142
|
+
const p = join(cwd, name)
|
|
143
|
+
if (!existsSync(p)) continue
|
|
144
|
+
try {
|
|
145
|
+
const mod = jiti(p)
|
|
146
|
+
const raw = mod?.default ?? mod
|
|
147
|
+
if (!raw || typeof raw !== 'object') {
|
|
148
|
+
fail(`${name} must default-export a config object.`)
|
|
149
|
+
}
|
|
150
|
+
return normalizeConfig(raw)
|
|
151
|
+
} catch (e) {
|
|
152
|
+
const msg = e instanceof Error ? e.message : String(e)
|
|
153
|
+
fail(`Failed to load ${name}: ${msg}`)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const uiPath = join(cwd, UI_JSON)
|
|
158
|
+
if (existsSync(uiPath)) return normalizeConfig(readJson(uiPath))
|
|
159
|
+
|
|
160
|
+
const legacyPath = join(cwd, LEGACY_JSON)
|
|
161
|
+
if (existsSync(legacyPath)) return normalizeConfig(readJson(legacyPath))
|
|
162
|
+
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
|
|
54
166
|
function projectConfig() {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
167
|
+
const cwd = process.cwd()
|
|
168
|
+
const config = loadProjectConfigFromDisk(cwd)
|
|
169
|
+
if (!config) {
|
|
170
|
+
fail(
|
|
171
|
+
`Missing kilo.config.ts (or kilo.config.mjs, ${UI_JSON}, ${LEGACY_JSON}). Run "npx ${cliName} init" first.`,
|
|
172
|
+
)
|
|
58
173
|
}
|
|
59
|
-
return
|
|
174
|
+
return config
|
|
60
175
|
}
|
|
61
176
|
|
|
62
177
|
function aliasPath(config, key) {
|
|
63
178
|
const value = config.aliases?.[key]
|
|
64
|
-
if (!value) fail(`Missing aliases.${key} in ${
|
|
179
|
+
if (!value) fail(`Missing aliases.${key} in kilo.config / ${UI_JSON} / ${LEGACY_JSON}`)
|
|
65
180
|
return value.replace(/^@\//, config.srcDir ? `${config.srcDir}/` : 'src/')
|
|
66
181
|
}
|
|
67
182
|
|
|
@@ -69,28 +184,141 @@ function resolveTarget(config, alias, fileName) {
|
|
|
69
184
|
return join(process.cwd(), aliasPath(config, alias), fileName)
|
|
70
185
|
}
|
|
71
186
|
|
|
187
|
+
/** Path segments under the components alias (e.g. ["ui"] → …/components/ui/…). Empty string = no extra folder. */
|
|
188
|
+
function componentDirSegments(config) {
|
|
189
|
+
const dir = config.ui?.componentsDir
|
|
190
|
+
if (dir === '') return []
|
|
191
|
+
if (dir === undefined) return ['ui']
|
|
192
|
+
return String(dir).replace(/\\/g, '/').split('/').filter(Boolean)
|
|
193
|
+
}
|
|
194
|
+
|
|
72
195
|
function componentFilePath(config, componentName, fileName) {
|
|
73
|
-
return join(
|
|
196
|
+
return join(
|
|
197
|
+
process.cwd(),
|
|
198
|
+
aliasPath(config, 'components'),
|
|
199
|
+
...componentDirSegments(config),
|
|
200
|
+
componentName,
|
|
201
|
+
fileName,
|
|
202
|
+
)
|
|
74
203
|
}
|
|
75
204
|
|
|
76
205
|
function copyRegistryFile(sourceRelative, targetPath, config, force) {
|
|
77
206
|
const sourcePath = join(registryRoot, sourceRelative)
|
|
78
207
|
if (!existsSync(sourcePath)) fail(`Registry file not found: ${sourceRelative}`)
|
|
79
|
-
|
|
208
|
+
let content = readFileSync(sourcePath, 'utf8')
|
|
80
209
|
.replaceAll('__COMPONENTS_ALIAS__', config.aliases.components)
|
|
81
210
|
.replaceAll('__COMPOSABLES_ALIAS__', config.aliases.composables)
|
|
82
211
|
.replaceAll('__LIB_ALIAS__', config.aliases.lib)
|
|
83
212
|
.replaceAll('__TYPES_ALIAS__', config.aliases.types)
|
|
213
|
+
|
|
214
|
+
if (targetPath.endsWith('.vue')) {
|
|
215
|
+
const base = basename(targetPath, '.vue')
|
|
216
|
+
const devName = vueResolvedComponentName(config, base)
|
|
217
|
+
if (devName) content = injectVueComponentName(content, devName)
|
|
218
|
+
}
|
|
219
|
+
|
|
84
220
|
writeFile(targetPath, content, force)
|
|
85
221
|
}
|
|
86
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Vite resolves `@` from vite.config; TypeScript needs matching paths or `@/…` imports fail in the editor / vue-tsc.
|
|
225
|
+
* Vue templates usually keep env.d.ts at the project root next to tsconfig.app.json (not under src/).
|
|
226
|
+
*/
|
|
227
|
+
function kiloConfigSerializable(config) {
|
|
228
|
+
const n = normalizeConfig(config)
|
|
229
|
+
return {
|
|
230
|
+
style: n.style ?? 'default',
|
|
231
|
+
srcDir: n.srcDir,
|
|
232
|
+
ui: n.ui,
|
|
233
|
+
tailwind: n.tailwind,
|
|
234
|
+
aliases: n.aliases,
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function buildKiloConfigFileContent(config) {
|
|
239
|
+
const obj = kiloConfigSerializable(config)
|
|
240
|
+
const body = JSON.stringify(obj, null, 2)
|
|
241
|
+
const header = `// Kilo UI — app config (generated in your project root by ${pkgName}, not inside the library package)\n// ui.componentsDir / componentsPrefix = folder under aliases.components (default "ui")\n// If folder is not "ui", copied .vue files get defineOptions({ name: "<Prefix><FileName>" }) for <PrefixFileName /> in templates (override with ui.componentTagPrefix: false or a string).\n\n`
|
|
242
|
+
return `${header}import { defineConfig } from '${pkgName}/config'\n\nexport default defineConfig(${body})\n`
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Create kilo.config.ts when missing, or overwrite when force (unless a non-.ts kilo module is the only config). */
|
|
246
|
+
function writeKiloConfigTs(cwd, config, force) {
|
|
247
|
+
const kiloPath = join(cwd, KILO_TS)
|
|
248
|
+
if (hasKiloModuleConfig(cwd) && !force) return
|
|
249
|
+
if (!force && existsSync(kiloPath)) return
|
|
250
|
+
|
|
251
|
+
const rel = relative(cwd, kiloPath)
|
|
252
|
+
const existed = existsSync(kiloPath)
|
|
253
|
+
ensureDir(dirname(kiloPath))
|
|
254
|
+
writeFileSync(kiloPath, buildKiloConfigFileContent(config), 'utf8')
|
|
255
|
+
log(`${force || existed ? 'write' : 'add'} ${rel}`)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function mergeTsconfigForAliases(cwd, srcDir = 'src') {
|
|
259
|
+
const srcNorm = String(srcDir || 'src').replace(/\\/g, '/').replace(/\/$/, '') || 'src'
|
|
260
|
+
const pathPattern = `./${srcNorm}/*`
|
|
261
|
+
const appPath = join(cwd, 'tsconfig.app.json')
|
|
262
|
+
const rootPath = join(cwd, 'tsconfig.json')
|
|
263
|
+
|
|
264
|
+
function tryMerge(filePath) {
|
|
265
|
+
if (!existsSync(filePath)) return false
|
|
266
|
+
let parsed
|
|
267
|
+
try {
|
|
268
|
+
parsed = readJson(filePath)
|
|
269
|
+
} catch {
|
|
270
|
+
log(`skip ${relative(cwd, filePath)} (invalid JSON — fix or add paths manually)`)
|
|
271
|
+
return false
|
|
272
|
+
}
|
|
273
|
+
const relName = relative(cwd, filePath)
|
|
274
|
+
if (relName === 'tsconfig.json' && parsed.references?.length && !parsed.compilerOptions) {
|
|
275
|
+
return false
|
|
276
|
+
}
|
|
277
|
+
parsed.compilerOptions = parsed.compilerOptions || {}
|
|
278
|
+
const co = parsed.compilerOptions
|
|
279
|
+
const paths = { ...(co.paths || {}) }
|
|
280
|
+
const key = '@/*'
|
|
281
|
+
if (paths[key]?.[0] === pathPattern) {
|
|
282
|
+
log(`skip ${relName} (paths "@/*" already set)`)
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
285
|
+
if (paths[key]?.length) {
|
|
286
|
+
log(
|
|
287
|
+
`skip ${relName} (paths "@/*" already defined — ensure it points at your ${srcNorm}/ folder for kilo-ui imports)`,
|
|
288
|
+
)
|
|
289
|
+
return true
|
|
290
|
+
}
|
|
291
|
+
paths[key] = [pathPattern]
|
|
292
|
+
co.paths = paths
|
|
293
|
+
if (co.baseUrl === undefined) co.baseUrl = '.'
|
|
294
|
+
|
|
295
|
+
if (Array.isArray(parsed.include)) {
|
|
296
|
+
const hasEnv = parsed.include.some((entry) => String(entry).includes('env.d.ts'))
|
|
297
|
+
if (!hasEnv) parsed.include.unshift('env.d.ts')
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
writeJson(filePath, parsed)
|
|
301
|
+
log(`update ${relName} (TypeScript paths for @ + env.d.ts in include)`)
|
|
302
|
+
return true
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (tryMerge(appPath)) return
|
|
306
|
+
if (tryMerge(rootPath)) return
|
|
307
|
+
|
|
308
|
+
log('\nTip: Add TypeScript paths so editors resolve @ imports (same folder as vite alias):')
|
|
309
|
+
log(` "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["${pathPattern}"] } }`)
|
|
310
|
+
}
|
|
311
|
+
|
|
87
312
|
function init(args) {
|
|
88
313
|
const force = args.includes('--force')
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
314
|
+
const cwd = process.cwd()
|
|
315
|
+
const uiPath = join(cwd, UI_JSON)
|
|
316
|
+
const legacyPath = join(cwd, LEGACY_JSON)
|
|
317
|
+
|
|
318
|
+
const defaultConfig = normalizeConfig({
|
|
92
319
|
style: 'default',
|
|
93
320
|
srcDir: 'src',
|
|
321
|
+
ui: { componentsDir: 'ui' },
|
|
94
322
|
tailwind: {
|
|
95
323
|
css: 'src/styles/teamwork-ui.css',
|
|
96
324
|
},
|
|
@@ -100,23 +328,48 @@ function init(args) {
|
|
|
100
328
|
lib: '@/lib',
|
|
101
329
|
types: '@/types',
|
|
102
330
|
},
|
|
103
|
-
}
|
|
104
|
-
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
let config
|
|
105
334
|
|
|
106
|
-
if (
|
|
107
|
-
|
|
335
|
+
if (!force) {
|
|
336
|
+
if (hasKiloModuleConfig(cwd)) {
|
|
337
|
+
config = loadProjectConfigFromDisk(cwd)
|
|
338
|
+
if (!config) fail('Could not read kilo config.')
|
|
339
|
+
log(`skip kilo.config.* (already exists)`)
|
|
340
|
+
} else if (existsSync(uiPath)) {
|
|
341
|
+
config = normalizeConfig(readJson(uiPath))
|
|
342
|
+
log(`using ${UI_JSON} (legacy). Prefer ${KILO_TS} — run init on a fresh branch or add the file from docs.`)
|
|
343
|
+
} else if (existsSync(legacyPath)) {
|
|
344
|
+
config = normalizeConfig(readJson(legacyPath))
|
|
345
|
+
log(`using ${LEGACY_JSON} (legacy fields). Adding ${KILO_TS} if missing.`)
|
|
346
|
+
} else {
|
|
347
|
+
config = defaultConfig
|
|
348
|
+
}
|
|
108
349
|
} else {
|
|
109
|
-
|
|
110
|
-
log(`write ${configFile}`)
|
|
350
|
+
config = defaultConfig
|
|
111
351
|
}
|
|
112
352
|
|
|
353
|
+
writeKiloConfigTs(cwd, config, force)
|
|
354
|
+
|
|
355
|
+
// Create the base folder for installed components so users can see it immediately after install/init.
|
|
356
|
+
// Actual component subfolders are created by `kilo-ui add`.
|
|
357
|
+
const baseComponents = join(cwd, aliasPath(config, 'components'), ...componentDirSegments(config))
|
|
358
|
+
if (baseComponents) ensureDir(baseComponents)
|
|
359
|
+
|
|
113
360
|
copyRegistryFile('styles/teamwork-ui.css', join(process.cwd(), config.tailwind.css), config, force)
|
|
114
361
|
copyRegistryFile('lib/utils.ts', resolveTarget(config, 'lib', 'utils.ts'), config, force)
|
|
362
|
+
copyRegistryFile('vue/env.d.ts', join(process.cwd(), 'env.d.ts'), config, force)
|
|
363
|
+
|
|
364
|
+
mergeTsconfigForAliases(process.cwd(), config.srcDir || 'src')
|
|
115
365
|
|
|
366
|
+
const sub = componentDirSegments(config).join('/') || '(components root)'
|
|
116
367
|
log('\nDone. Add this once in your app entry or main stylesheet:')
|
|
117
368
|
log(` import './styles/${config.tailwind.css.replace(/^.*\//, '')}'`)
|
|
369
|
+
log(`\nComponents install under: ${aliasPath(config, 'components').replace(/\\/g, '/')}/${sub}/<name>/`)
|
|
118
370
|
log('\nThen add components:')
|
|
119
371
|
log(` npx ${cliName} add data-table app-navbar activity-log`)
|
|
372
|
+
log(`\nEdit ${KILO_TS} to change where components install (aliases, ui.componentsDir).`)
|
|
120
373
|
}
|
|
121
374
|
|
|
122
375
|
function add(args) {
|
|
@@ -163,6 +416,8 @@ Usage:
|
|
|
163
416
|
npx ${cliName} add <component...> [--force]
|
|
164
417
|
npx ${cliName} list
|
|
165
418
|
|
|
419
|
+
Config: ${KILO_TS} (or ${KILO_MJS}, legacy ${UI_JSON} / ${LEGACY_JSON}). Use defineConfig from "${pkgName}/config". Set ui.componentsDir for the folder under your components alias (default "ui").
|
|
420
|
+
|
|
166
421
|
Examples:
|
|
167
422
|
npx ${cliName} init
|
|
168
423
|
npx ${cliName} add data-table app-navbar
|
package/bin/postinstall.mjs
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync } from 'node:fs'
|
|
3
|
-
import { dirname, join, resolve } from 'node:path'
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { spawnSync } from 'node:child_process'
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
8
8
|
const packageRoot = resolve(__dirname, '..')
|
|
9
9
|
|
|
10
|
-
const initCwd =
|
|
10
|
+
const initCwd = resolveConsumerProjectRoot()
|
|
11
11
|
if (!initCwd) process.exit(0)
|
|
12
12
|
|
|
13
13
|
// Avoid running when installing this repo itself.
|
|
14
|
-
if (resolve(initCwd) === packageRoot) process.exit(0)
|
|
14
|
+
if (resolve(initCwd) === resolve(packageRoot)) process.exit(0)
|
|
15
15
|
|
|
16
|
-
// Only
|
|
17
|
-
|
|
16
|
+
// Only skip auto-init when a kilo config module already exists.
|
|
17
|
+
// Do not treat components.json / ui.json as "done" — shadcn and other tools use those too,
|
|
18
|
+
// and we still need to add kilo.config.ts.
|
|
19
|
+
const kiloModuleNames = ['kilo.config.ts', 'kilo.config.mts', 'kilo.config.js', 'kilo.config.mjs']
|
|
20
|
+
if (kiloModuleNames.some((name) => existsSync(join(initCwd, name)))) process.exit(0)
|
|
18
21
|
|
|
19
22
|
const cliEntry = join(packageRoot, 'bin', 'kilo-ui.mjs')
|
|
20
23
|
|
|
@@ -24,3 +27,21 @@ const result = spawnSync(process.execPath, [cliEntry, 'init'], {
|
|
|
24
27
|
})
|
|
25
28
|
|
|
26
29
|
process.exit(result.status ?? 1)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Prefer npm's INIT_CWD or npm_config_local_prefix. If missing, infer the app root only when
|
|
33
|
+
* this package lives under node_modules (never guess from a bare repo path).
|
|
34
|
+
*/
|
|
35
|
+
function resolveConsumerProjectRoot() {
|
|
36
|
+
const fromEnv = process.env.INIT_CWD || process.env.npm_config_local_prefix
|
|
37
|
+
if (fromEnv) return resolve(fromEnv)
|
|
38
|
+
|
|
39
|
+
const norm = packageRoot.replace(/\\/g, '/')
|
|
40
|
+
if (!norm.includes('/node_modules/')) return null
|
|
41
|
+
|
|
42
|
+
const parent = dirname(packageRoot)
|
|
43
|
+
if (basename(parent).startsWith('@')) {
|
|
44
|
+
return resolve(parent, '..', '..')
|
|
45
|
+
}
|
|
46
|
+
return resolve(parent, '..')
|
|
47
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface KiloUiUserConfig {
|
|
2
|
+
/** @deprecated informational only */
|
|
3
|
+
$schema?: string
|
|
4
|
+
style?: string
|
|
5
|
+
srcDir?: string
|
|
6
|
+
ui?: {
|
|
7
|
+
/**
|
|
8
|
+
* Folder prefix under `aliases.components` where `kilo-ui add` places each component
|
|
9
|
+
* (default `"ui"` → e.g. `src/components/ui/<name>/`). Use `""` for `…/components/<name>/`.
|
|
10
|
+
*/
|
|
11
|
+
componentsDir?: string
|
|
12
|
+
/** Same as `componentsDir` if you prefer the name “prefix”. */
|
|
13
|
+
componentsPrefix?: string
|
|
14
|
+
/**
|
|
15
|
+
* Pascal prefix for Vue `defineOptions({ name })` on copied `.vue` files so you can use
|
|
16
|
+
* `<KiCardBox />` in templates when folder prefix is `ki` (auto: from first folder segment unless it is `ui`).
|
|
17
|
+
* Set a string to force (e.g. `"Ki"`). Set `false` to disable injection.
|
|
18
|
+
*/
|
|
19
|
+
componentTagPrefix?: string | false
|
|
20
|
+
}
|
|
21
|
+
tailwind?: {
|
|
22
|
+
css: string
|
|
23
|
+
}
|
|
24
|
+
aliases: {
|
|
25
|
+
components: string
|
|
26
|
+
composables: string
|
|
27
|
+
lib: string
|
|
28
|
+
types: string
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function defineConfig(config: KiloUiUserConfig): KiloUiUserConfig
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example config shipped with the npm package at:
|
|
3
|
+
* node_modules/@lyda/kilo-ui/config/kilo.config.example.ts
|
|
4
|
+
*
|
|
5
|
+
* Your Vue app does NOT edit this file. On `npm install`, the CLI creates
|
|
6
|
+
* **`kilo.config.ts` in your project root** (next to package.json).
|
|
7
|
+
*
|
|
8
|
+
* Change **`ui.componentsDir`** to set the folder prefix under your components path
|
|
9
|
+
* (e.g. `"ui"` → src/components/ui/data-table/…). Use `componentsPrefix` as an alias.
|
|
10
|
+
*
|
|
11
|
+
* With a non-`ui` folder (e.g. `"ki"`), `kilo-ui add` injects `defineOptions({ name: "KiCardBox" })`
|
|
12
|
+
* so you can use `<KiCardBox />` in templates. Override with `ui.componentTagPrefix` (`string` or `false`).
|
|
13
|
+
*/
|
|
14
|
+
import { defineConfig } from '@lyda/kilo-ui/config'
|
|
15
|
+
|
|
16
|
+
export default defineConfig({
|
|
17
|
+
style: 'default',
|
|
18
|
+
srcDir: 'src',
|
|
19
|
+
ui: {
|
|
20
|
+
/** Folder prefix under `aliases.components` for copied registry components */
|
|
21
|
+
componentsDir: 'ui',
|
|
22
|
+
/** Set `false` to skip defineOptions name injection; set a string to force tag prefix */
|
|
23
|
+
// componentTagPrefix: false,
|
|
24
|
+
},
|
|
25
|
+
tailwind: {
|
|
26
|
+
css: 'src/styles/teamwork-ui.css',
|
|
27
|
+
},
|
|
28
|
+
aliases: {
|
|
29
|
+
components: '@/components',
|
|
30
|
+
composables: '@/composables',
|
|
31
|
+
lib: '@/lib',
|
|
32
|
+
types: '@/types',
|
|
33
|
+
},
|
|
34
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lyda/kilo-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "shadcn-style Vue component registry for team CRUD apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -17,8 +17,15 @@
|
|
|
17
17
|
"bin": {
|
|
18
18
|
"kilo-ui": "bin/kilo-ui.mjs"
|
|
19
19
|
},
|
|
20
|
+
"exports": {
|
|
21
|
+
"./config": {
|
|
22
|
+
"types": "./config/define-config.d.ts",
|
|
23
|
+
"default": "./config/define-config.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
20
26
|
"files": [
|
|
21
27
|
"bin",
|
|
28
|
+
"config",
|
|
22
29
|
"registry",
|
|
23
30
|
"README.md"
|
|
24
31
|
],
|
|
@@ -49,5 +56,8 @@
|
|
|
49
56
|
"crud",
|
|
50
57
|
"ui"
|
|
51
58
|
],
|
|
52
|
-
"license": "MIT"
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"jiti": "^2.7.0"
|
|
62
|
+
}
|
|
53
63
|
}
|