preskok-ui 0.0.5 → 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 CHANGED
@@ -3,10 +3,11 @@
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, and `add` resolves bare component names through the Preskok registry.
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
@@ -1,12 +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 REGISTRY = "@preskok=https://ui-three-mu.vercel.app/r/{name}.json"
5
- const DEFAULT_REGISTRY_ITEM = "https://ui-three-mu.vercel.app/r/default.json"
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`
6
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
+ ])
7
31
 
8
32
  const args = process.argv.slice(2)
9
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
+ ]
10
42
 
11
43
  if (!command || command === "-h" || command === "--help") {
12
44
  printHelp()
@@ -14,20 +46,54 @@ if (!command || command === "-h" || command === "--help") {
14
46
  }
15
47
 
16
48
  if (command === "add") {
17
- const registryStatus = runShadcn(["registry", "add", REGISTRY])
49
+ const registryStatus = runShadcn([
50
+ "registry",
51
+ "add",
52
+ REGISTRY,
53
+ ...getCommandOptions(commandArgs, CWD_OPTION_VALUES),
54
+ ])
18
55
 
19
56
  if (registryStatus !== 0) {
20
57
  process.exit(registryStatus)
21
58
  }
22
59
 
23
60
  process.exit(
24
- runShadcn(["add", "--overwrite", "--yes", ...toPreskokItems(commandArgs)])
61
+ runPreskokAdd(toPreskokCommandItems(commandArgs, ADD_OPTION_VALUES))
25
62
  )
26
63
  }
27
64
 
28
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
+
29
92
  process.exit(
30
- runShadcn(["init", DEFAULT_REGISTRY_ITEM, ...toPreskokInitItems(commandArgs)])
93
+ runPreskokAdd([
94
+ ...getCommandOptions(initArgs, CWD_OPTION_VALUES),
95
+ ...toPreskokItems(items),
96
+ ])
31
97
  )
32
98
  }
33
99
 
@@ -39,7 +105,10 @@ if (command === "registry") {
39
105
 
40
106
  if (command === "view") {
41
107
  process.exit(
42
- runShadcn(["view", ...toPreskokCommandItems(commandArgs, CWD_OPTION_VALUES)])
108
+ runShadcn([
109
+ "view",
110
+ ...toPreskokCommandItems(commandArgs, CWD_OPTION_VALUES),
111
+ ])
43
112
  )
44
113
  }
45
114
 
@@ -76,6 +145,14 @@ function runShadcn(shadcnArgs) {
76
145
  return result.status ?? 1
77
146
  }
78
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
+
79
156
  function toPreskokItems(values) {
80
157
  return values.map(toPreskokItem)
81
158
  }
@@ -106,46 +183,190 @@ function toPreskokCommandItems(values, optionsWithValues) {
106
183
  })
107
184
  }
108
185
 
109
- function toPreskokInitItems(values) {
110
- const initOptionsWithValues = new Set([
111
- "-t",
112
- "--template",
113
- "-b",
114
- "--base",
115
- "-p",
116
- "--preset",
117
- "-c",
118
- "--cwd",
119
- "-n",
120
- "--name",
121
- ])
186
+ function getCommandOptions(values, optionsWithValues) {
187
+ let skipNext = false
188
+ const options = []
122
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) {
123
212
  let skipNext = false
213
+ const initArgs = []
214
+ const items = []
124
215
 
125
- return values.map((value) => {
216
+ for (const value of values) {
126
217
  if (skipNext) {
127
218
  skipNext = false
128
- return value
219
+ initArgs.push(value)
220
+ continue
129
221
  }
130
222
 
131
- if (initOptionsWithValues.has(value)) {
223
+ if (INIT_OPTION_VALUES.has(value)) {
132
224
  skipNext = true
133
- return value
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
134
235
  }
135
236
 
136
- return toPreskokItems([value])[0]
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())
249
+ }
250
+
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)
137
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
+ }
138
358
  }
139
359
 
140
360
  function printHelp() {
141
361
  console.log(`Usage: preskok-ui <command> [options]
142
362
 
143
363
  Commands:
144
- init [items...] run shadcn init with the Preskok base and optional items
364
+ init [items...] run shadcn init, register Preskok, and optionally add items
145
365
  add <items...> register Preskok and add items by name
146
366
  registry register the @preskok namespace in components.json
147
367
 
148
368
  Examples:
369
+ npx preskok-ui@latest init
149
370
  npx preskok-ui@latest init button
150
371
  npx preskok-ui@latest add button
151
372
  npx preskok-ui@latest view button
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preskok-ui",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {