preskok-ui 0.0.4 → 0.0.6
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 +4 -2
- package/bin/preskok-ui.mjs +286 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
Thin CLI wrapper around shadcn with Preskok registry wiring.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
+
npx preskok-ui@latest init
|
|
6
7
|
npx preskok-ui@latest init button
|
|
7
8
|
```
|
|
8
9
|
|
|
9
|
-
The CLI delegates to `shadcn@latest`. `init` installs the Preskok base and optional components in one pass
|
|
10
|
+
The CLI delegates to `shadcn@latest`. `init` installs the Preskok base for new projects, only registers the Preskok namespace in projects that already have `components.json`, and can add optional components in one pass. `add` resolves bare component names through the Preskok registry.
|
|
10
11
|
|
|
11
12
|
```bash
|
|
12
13
|
npx preskok-ui@latest registry
|
|
13
|
-
npx preskok-ui@latest
|
|
14
|
+
npx preskok-ui@latest view button
|
|
15
|
+
npx preskok-ui@latest diff button
|
|
14
16
|
```
|
package/bin/preskok-ui.mjs
CHANGED
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process"
|
|
3
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"
|
|
4
|
+
import path from "node:path"
|
|
3
5
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
+
const REGISTRY_BASE_URL = (
|
|
7
|
+
process.env.PRESKOK_REGISTRY_URL ?? "https://ui-three-mu.vercel.app"
|
|
8
|
+
).replace(/\/$/, "")
|
|
9
|
+
const REGISTRY = `@preskok=${REGISTRY_BASE_URL}/r/{name}.json`
|
|
10
|
+
const DEFAULT_REGISTRY_ITEM = `${REGISTRY_BASE_URL}/r/default.json`
|
|
11
|
+
const CWD_OPTION_VALUES = new Set(["-c", "--cwd"])
|
|
12
|
+
const ADD_OPTION_VALUES = new Set(["-c", "--cwd", "-p", "--path", "--diff"])
|
|
13
|
+
const DEPENDENCY_SECTIONS = [
|
|
14
|
+
"dependencies",
|
|
15
|
+
"devDependencies",
|
|
16
|
+
"optionalDependencies",
|
|
17
|
+
"peerDependencies",
|
|
18
|
+
]
|
|
19
|
+
const INIT_OPTION_VALUES = new Set([
|
|
20
|
+
"-t",
|
|
21
|
+
"--template",
|
|
22
|
+
"-b",
|
|
23
|
+
"--base",
|
|
24
|
+
"-p",
|
|
25
|
+
"--preset",
|
|
26
|
+
"-c",
|
|
27
|
+
"--cwd",
|
|
28
|
+
"-n",
|
|
29
|
+
"--name",
|
|
30
|
+
])
|
|
6
31
|
|
|
7
32
|
const args = process.argv.slice(2)
|
|
8
33
|
const [command, ...commandArgs] = args
|
|
34
|
+
const LOCKFILES = [
|
|
35
|
+
"package-lock.json",
|
|
36
|
+
"npm-shrinkwrap.json",
|
|
37
|
+
"pnpm-lock.yaml",
|
|
38
|
+
"yarn.lock",
|
|
39
|
+
"bun.lock",
|
|
40
|
+
"bun.lockb",
|
|
41
|
+
]
|
|
9
42
|
|
|
10
43
|
if (!command || command === "-h" || command === "--help") {
|
|
11
44
|
printHelp()
|
|
@@ -13,20 +46,54 @@ if (!command || command === "-h" || command === "--help") {
|
|
|
13
46
|
}
|
|
14
47
|
|
|
15
48
|
if (command === "add") {
|
|
16
|
-
const registryStatus = runShadcn([
|
|
49
|
+
const registryStatus = runShadcn([
|
|
50
|
+
"registry",
|
|
51
|
+
"add",
|
|
52
|
+
REGISTRY,
|
|
53
|
+
...getCommandOptions(commandArgs, CWD_OPTION_VALUES),
|
|
54
|
+
])
|
|
17
55
|
|
|
18
56
|
if (registryStatus !== 0) {
|
|
19
57
|
process.exit(registryStatus)
|
|
20
58
|
}
|
|
21
59
|
|
|
22
60
|
process.exit(
|
|
23
|
-
|
|
61
|
+
runPreskokAdd(toPreskokCommandItems(commandArgs, ADD_OPTION_VALUES))
|
|
24
62
|
)
|
|
25
63
|
}
|
|
26
64
|
|
|
27
65
|
if (command === "init") {
|
|
66
|
+
const { initArgs, items } = splitInitArgs(commandArgs)
|
|
67
|
+
const cwd = getCwd(initArgs)
|
|
68
|
+
|
|
69
|
+
if (!existsSync(path.join(cwd, "components.json"))) {
|
|
70
|
+
const initStatus = runShadcn(["init", DEFAULT_REGISTRY_ITEM, ...initArgs])
|
|
71
|
+
|
|
72
|
+
if (initStatus !== 0) {
|
|
73
|
+
process.exit(initStatus)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const registryStatus = runShadcn([
|
|
78
|
+
"registry",
|
|
79
|
+
"add",
|
|
80
|
+
REGISTRY,
|
|
81
|
+
...getCommandOptions(initArgs, CWD_OPTION_VALUES),
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
if (registryStatus !== 0) {
|
|
85
|
+
process.exit(registryStatus)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (items.length === 0) {
|
|
89
|
+
process.exit(0)
|
|
90
|
+
}
|
|
91
|
+
|
|
28
92
|
process.exit(
|
|
29
|
-
|
|
93
|
+
runPreskokAdd([
|
|
94
|
+
...getCommandOptions(initArgs, CWD_OPTION_VALUES),
|
|
95
|
+
...toPreskokItems(items),
|
|
96
|
+
])
|
|
30
97
|
)
|
|
31
98
|
}
|
|
32
99
|
|
|
@@ -36,6 +103,25 @@ if (command === "registry") {
|
|
|
36
103
|
process.exit(runShadcn(["registry", "add", REGISTRY, ...registryArgs]))
|
|
37
104
|
}
|
|
38
105
|
|
|
106
|
+
if (command === "view") {
|
|
107
|
+
process.exit(
|
|
108
|
+
runShadcn([
|
|
109
|
+
"view",
|
|
110
|
+
...toPreskokCommandItems(commandArgs, CWD_OPTION_VALUES),
|
|
111
|
+
])
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (command === "diff") {
|
|
116
|
+
process.exit(
|
|
117
|
+
runShadcn([
|
|
118
|
+
"add",
|
|
119
|
+
...toPreskokCommandItems(commandArgs, CWD_OPTION_VALUES),
|
|
120
|
+
"--diff",
|
|
121
|
+
])
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
39
125
|
process.exit(runShadcn(args))
|
|
40
126
|
|
|
41
127
|
function runShadcn(shadcnArgs) {
|
|
@@ -59,58 +145,231 @@ function runShadcn(shadcnArgs) {
|
|
|
59
145
|
return result.status ?? 1
|
|
60
146
|
}
|
|
61
147
|
|
|
148
|
+
function runPreskokAdd(addArgs) {
|
|
149
|
+
const packageState = capturePackageState(addArgs)
|
|
150
|
+
const status = runShadcn(["add", "--yes", ...addArgs])
|
|
151
|
+
restorePackageState(packageState)
|
|
152
|
+
|
|
153
|
+
return status
|
|
154
|
+
}
|
|
155
|
+
|
|
62
156
|
function toPreskokItems(values) {
|
|
157
|
+
return values.map(toPreskokItem)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function toPreskokItem(value) {
|
|
161
|
+
if (value.startsWith("-") || value.startsWith("@") || value.includes("/")) {
|
|
162
|
+
return value
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return `@preskok/${value.replace(/\.(tsx|ts|jsx|js|json)$/, "")}`
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function toPreskokCommandItems(values, optionsWithValues) {
|
|
169
|
+
let skipNext = false
|
|
170
|
+
|
|
63
171
|
return values.map((value) => {
|
|
64
|
-
if (
|
|
172
|
+
if (skipNext) {
|
|
173
|
+
skipNext = false
|
|
65
174
|
return value
|
|
66
175
|
}
|
|
67
176
|
|
|
68
|
-
|
|
177
|
+
if (optionsWithValues.has(value)) {
|
|
178
|
+
skipNext = true
|
|
179
|
+
return value
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return toPreskokItem(value)
|
|
69
183
|
})
|
|
70
184
|
}
|
|
71
185
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"--template",
|
|
76
|
-
"-b",
|
|
77
|
-
"--base",
|
|
78
|
-
"-p",
|
|
79
|
-
"--preset",
|
|
80
|
-
"-c",
|
|
81
|
-
"--cwd",
|
|
82
|
-
"-n",
|
|
83
|
-
"--name",
|
|
84
|
-
])
|
|
186
|
+
function getCommandOptions(values, optionsWithValues) {
|
|
187
|
+
let skipNext = false
|
|
188
|
+
const options = []
|
|
85
189
|
|
|
190
|
+
for (const value of values) {
|
|
191
|
+
if (skipNext) {
|
|
192
|
+
skipNext = false
|
|
193
|
+
options.push(value)
|
|
194
|
+
continue
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (optionsWithValues.has(value)) {
|
|
198
|
+
skipNext = true
|
|
199
|
+
options.push(value)
|
|
200
|
+
continue
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (hasInlineOptionValue(value, optionsWithValues)) {
|
|
204
|
+
options.push(value)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return options
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function splitInitArgs(values) {
|
|
86
212
|
let skipNext = false
|
|
213
|
+
const initArgs = []
|
|
214
|
+
const items = []
|
|
87
215
|
|
|
88
|
-
|
|
216
|
+
for (const value of values) {
|
|
89
217
|
if (skipNext) {
|
|
90
218
|
skipNext = false
|
|
91
|
-
|
|
219
|
+
initArgs.push(value)
|
|
220
|
+
continue
|
|
92
221
|
}
|
|
93
222
|
|
|
94
|
-
if (
|
|
223
|
+
if (INIT_OPTION_VALUES.has(value)) {
|
|
95
224
|
skipNext = true
|
|
96
|
-
|
|
225
|
+
initArgs.push(value)
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (
|
|
230
|
+
hasInlineOptionValue(value, INIT_OPTION_VALUES) ||
|
|
231
|
+
value.startsWith("-")
|
|
232
|
+
) {
|
|
233
|
+
initArgs.push(value)
|
|
234
|
+
continue
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
items.push(value)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { initArgs, items }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function getCwd(values) {
|
|
244
|
+
for (let index = 0; index < values.length; index++) {
|
|
245
|
+
const value = values[index]
|
|
246
|
+
|
|
247
|
+
if (value === "-c" || value === "--cwd") {
|
|
248
|
+
return path.resolve(values[index + 1] ?? process.cwd())
|
|
97
249
|
}
|
|
98
250
|
|
|
99
|
-
|
|
251
|
+
if (value.startsWith("--cwd=")) {
|
|
252
|
+
return path.resolve(value.slice("--cwd=".length))
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return process.cwd()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function hasInlineOptionValue(value, options) {
|
|
260
|
+
return [...options].some((option) => value.startsWith(`${option}=`))
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function capturePackageState(values) {
|
|
264
|
+
const cwd = getCwd(values)
|
|
265
|
+
const packageJsonPath = path.join(cwd, "package.json")
|
|
266
|
+
|
|
267
|
+
if (!existsSync(packageJsonPath)) {
|
|
268
|
+
return null
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const packageJsonContent = readFileSync(packageJsonPath, "utf8")
|
|
272
|
+
const packageJson = JSON.parse(packageJsonContent)
|
|
273
|
+
const lockfiles = new Map()
|
|
274
|
+
|
|
275
|
+
for (const lockfile of LOCKFILES) {
|
|
276
|
+
const lockfilePath = path.join(cwd, lockfile)
|
|
277
|
+
|
|
278
|
+
if (existsSync(lockfilePath)) {
|
|
279
|
+
lockfiles.set(lockfile, readFileSync(lockfilePath))
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
cwd,
|
|
285
|
+
lockfiles,
|
|
286
|
+
packageJson,
|
|
287
|
+
packageJsonContent,
|
|
288
|
+
packageJsonPath,
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function restorePackageState(packageState) {
|
|
293
|
+
if (!packageState || !existsSync(packageState.packageJsonPath)) {
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const packageJson = JSON.parse(
|
|
298
|
+
readFileSync(packageState.packageJsonPath, "utf8")
|
|
299
|
+
)
|
|
300
|
+
const beforeDependencyNames = getDependencyNames(packageState.packageJson)
|
|
301
|
+
const afterDependencyNames = getDependencyNames(packageJson)
|
|
302
|
+
const hasNewDependencies = [...afterDependencyNames].some((dependency) => {
|
|
303
|
+
return !beforeDependencyNames.has(dependency)
|
|
100
304
|
})
|
|
305
|
+
|
|
306
|
+
if (!hasNewDependencies) {
|
|
307
|
+
writeFileSync(packageState.packageJsonPath, packageState.packageJsonContent)
|
|
308
|
+
restoreLockfiles(packageState)
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
restoreExistingDependencySpecs(packageState.packageJson, packageJson)
|
|
313
|
+
writeFileSync(
|
|
314
|
+
packageState.packageJsonPath,
|
|
315
|
+
`${JSON.stringify(packageJson, null, 2)}\n`
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function getDependencyNames(packageJson) {
|
|
320
|
+
const names = new Set()
|
|
321
|
+
|
|
322
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
323
|
+
for (const dependency of Object.keys(packageJson[section] ?? {})) {
|
|
324
|
+
names.add(dependency)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return names
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function restoreExistingDependencySpecs(sourcePackageJson, targetPackageJson) {
|
|
332
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
333
|
+
const sourceDependencies = sourcePackageJson[section] ?? {}
|
|
334
|
+
const targetDependencies = targetPackageJson[section] ?? {}
|
|
335
|
+
|
|
336
|
+
for (const [dependency, version] of Object.entries(sourceDependencies)) {
|
|
337
|
+
if (targetDependencies[dependency] !== undefined) {
|
|
338
|
+
targetDependencies[dependency] = version
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function restoreLockfiles(packageState) {
|
|
345
|
+
for (const lockfile of LOCKFILES) {
|
|
346
|
+
const lockfilePath = path.join(packageState.cwd, lockfile)
|
|
347
|
+
const content = packageState.lockfiles.get(lockfile)
|
|
348
|
+
|
|
349
|
+
if (content) {
|
|
350
|
+
writeFileSync(lockfilePath, content)
|
|
351
|
+
continue
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (existsSync(lockfilePath)) {
|
|
355
|
+
unlinkSync(lockfilePath)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
101
358
|
}
|
|
102
359
|
|
|
103
360
|
function printHelp() {
|
|
104
361
|
console.log(`Usage: preskok-ui <command> [options]
|
|
105
362
|
|
|
106
363
|
Commands:
|
|
107
|
-
init [items...] run shadcn init
|
|
364
|
+
init [items...] run shadcn init, register Preskok, and optionally add items
|
|
108
365
|
add <items...> register Preskok and add items by name
|
|
109
366
|
registry register the @preskok namespace in components.json
|
|
110
367
|
|
|
111
368
|
Examples:
|
|
369
|
+
npx preskok-ui@latest init
|
|
112
370
|
npx preskok-ui@latest init button
|
|
113
371
|
npx preskok-ui@latest add button
|
|
114
|
-
npx preskok-ui@latest
|
|
372
|
+
npx preskok-ui@latest view button
|
|
373
|
+
npx preskok-ui@latest diff button
|
|
115
374
|
npx preskok-ui@latest registry`)
|
|
116
375
|
}
|