onejs-core 2.0.7 → 2.0.9

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.
Files changed (2) hide show
  1. package/bin/oj.js +188 -72
  2. package/package.json +7 -4
package/bin/oj.js CHANGED
@@ -1,18 +1,43 @@
1
1
  #!/usr/bin/env node
2
+ /* eslint-disable no-console */
2
3
  "use strict"
3
4
 
5
+ /*
6
+ * OneJS – oj (shadcn-style) CLI
7
+ * A small utility for listing / installing premade UI components
8
+ * shipped inside a <Assets/> Unity project folder.
9
+ *
10
+ * Commands:
11
+ * oj list – show available components inside tarball
12
+ * oj add <name … | all> – install one or many components
13
+ * oj doctor – verify environment (Assets folder + tarball)
14
+ *
15
+ * Global flags:
16
+ * -y, --yes, --force – skip overwrite prompts
17
+ * --dry – dry-run (no filesystem writes)
18
+ * --assets-dir <dir> – override Assets directory auto-detection
19
+ * --tarball <file> – override tarball filename (default premade-uis.tgz.bytes)
20
+ */
21
+
4
22
  const fs = require("fs")
5
23
  const path = require("path")
6
- const zlib = require("zlib")
7
24
  const readline = require("readline")
8
25
  const tar = require("tar")
26
+ const { Command } = require("commander")
27
+ const chalk = require("chalk")
28
+ const ora = require("ora")
29
+
30
+ const pkg = require("../package.json")
9
31
 
10
- /* ───────────── helpers ───────────── */
32
+ /* ─────────────────────────────── utils ─────────────────────────────── */
11
33
 
12
- function findAssetsDir(startDir) {
34
+ function findAssetsDir(startDir, explicit) {
35
+ if (explicit) {
36
+ if (fs.existsSync(explicit) && fs.statSync(explicit).isDirectory()) return explicit
37
+ return null
38
+ }
13
39
  let dir = startDir
14
40
  const { root } = path.parse(dir)
15
-
16
41
  while (dir !== root) {
17
42
  const probe = path.join(dir, "Assets")
18
43
  if (fs.existsSync(probe) && fs.statSync(probe).isDirectory()) return probe
@@ -21,111 +46,202 @@ function findAssetsDir(startDir) {
21
46
  return null
22
47
  }
23
48
 
24
- function findTarball(assetsDir) {
49
+ function findTarball(assetsDir, tarballName) {
25
50
  const stack = [assetsDir]
26
-
27
51
  while (stack.length) {
28
52
  const curr = stack.pop()
29
53
  for (const e of fs.readdirSync(curr, { withFileTypes: true })) {
30
54
  const p = path.join(curr, e.name)
31
- if (e.isDirectory()) stack.push(p)
32
- else if (e.isFile() && e.name === "premade-uis.tgz.bytes") return p
55
+ if (e.isDirectory()) {
56
+ stack.push(p)
57
+ } else if (e.isFile() && e.name === tarballName) {
58
+ return p
59
+ }
33
60
  }
34
61
  }
35
62
  return null
36
63
  }
37
64
 
38
- function askYN(prompt) {
39
- return new Promise(resolve => {
65
+ async function askYN(prompt) {
66
+ return new Promise((resolve) => {
40
67
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
41
- rl.question(`${prompt} [y/N] `, ans => {
68
+ rl.question(`${prompt} ${chalk.dim("[y/N]")} `, (ans) => {
42
69
  rl.close()
43
70
  resolve(/^(y|yes)$/i.test(ans.trim()))
44
71
  })
45
72
  })
46
73
  }
47
74
 
48
- /* ───────────── main ───────────── */
75
+ /**
76
+ * Return Set of top-level component folders inside tarball.
77
+ */
78
+ async function scanComponents(tarPath) {
79
+ const comps = new Set()
80
+ await tar.t({
81
+ file: tarPath,
82
+ onentry: (entry) => {
83
+ const p = entry.path
84
+ if (!p.startsWith("comps/")) return
85
+ const parts = p.split("/").filter(Boolean) // ["comps","button", ...]
86
+ if (parts.length >= 2) comps.add(parts[1])
87
+ },
88
+ })
89
+ return comps
90
+ }
49
91
 
50
- (async () => {
51
- const [, , cmd, target] = process.argv
92
+ /* ─────────────────────────────── commander ─────────────────────────────── */
52
93
 
53
- if (cmd !== "add" || !target) {
54
- console.error("Usage: npx oj add <all|name>")
55
- process.exit(1)
56
- }
94
+ const program = new Command()
57
95
 
58
- const assetsDir = findAssetsDir(process.cwd()) || findAssetsDir(__dirname)
59
- if (!assetsDir) {
60
- console.error("Assets/ folder not found in any ancestor directory. Make sure you are in a Unity project but outside the Assets/ folder.")
61
- process.exit(1)
62
- }
96
+ program
97
+ .name("oj")
98
+ .description("OneJS premade UI component manager")
99
+ .version(pkg.version, "-v, --version", "print version")
63
100
 
64
- const tarPath = findTarball(assetsDir)
65
- if (!tarPath) {
66
- console.error("premade-uis.tgz not found. It's included in the OneJS Asset Store package. So make sure you have that installed somewhere under the Assets/ folder.")
67
- process.exit(1)
68
- }
101
+ program
102
+ .hook("preAction", (thisCmd, actionCmd) => {
103
+ // Collect global opts and attach to actionCmd for convenience
104
+ actionCmd._globalOpts = {
105
+ yes: thisCmd.opts().yes || thisCmd.opts().force,
106
+ dry: thisCmd.opts().dry,
107
+ assetsDir: thisCmd.opts().assetsDir,
108
+ tarballName: thisCmd.opts().tarball || "premade-uis.tgz.bytes",
109
+ }
110
+ })
111
+ .option("-y, --yes", "skip confirmation prompts")
112
+ .option("--force", "alias for --yes")
113
+ .option("--dry", "dry-run – show actions only")
114
+ .option("--assets-dir <dir>", "override Assets/ directory")
115
+ .option("--tarball <file>", "override tarball filename (default premade-uis.tgz.bytes)")
116
+
117
+ /* ─────────────────────────────── list ─────────────────────────────── */
118
+
119
+ program
120
+ .command("list")
121
+ .description("list available components inside the tarball")
122
+ .action(async function () {
123
+ const { assetsDir, tarballName } = this._globalOpts
124
+ const assets = findAssetsDir(process.cwd(), assetsDir) || findAssetsDir(__dirname, assetsDir)
125
+ if (!assets) {
126
+ console.error(chalk.red("Assets/ folder not found. Are you inside a Unity project?"))
127
+ process.exit(1)
128
+ }
129
+ const tarPath = findTarball(assets, tarballName)
130
+ if (!tarPath) {
131
+ console.error(
132
+ chalk.red(`Tarball '${tarballName}' not found under Assets/. Did you import the OneJS package?`)
133
+ )
134
+ process.exit(1)
135
+ }
136
+ const comps = await scanComponents(tarPath)
137
+ console.log(chalk.bold(`Components in ${path.relative(process.cwd(), tarPath)}:`))
138
+ comps.forEach((c) => console.log(" •", chalk.cyan(c)))
139
+ })
69
140
 
70
- const cwd = process.cwd()
71
- const compsRoot = path.join(cwd, "comps")
72
- fs.mkdirSync(compsRoot, { recursive: true })
141
+ /* ─────────────────────────────── doctor ─────────────────────────────── */
142
+
143
+ program
144
+ .command("doctor")
145
+ .description("check Assets directory and tarball presence")
146
+ .action(async function () {
147
+ const { assetsDir, tarballName } = this._globalOpts
148
+ const spinner = ora("Checking environment").start()
149
+ const assets = findAssetsDir(process.cwd(), assetsDir) || findAssetsDir(__dirname, assetsDir)
150
+ if (!assets) {
151
+ spinner.fail("Assets/ folder not found")
152
+ process.exit(1)
153
+ }
154
+ const tarPath = findTarball(assets, tarballName)
155
+ if (!tarPath) {
156
+ spinner.fail(`Tarball '${tarballName}' not found under Assets/`)
157
+ process.exit(1)
158
+ }
159
+ spinner.succeed("Environment OK")
160
+ console.log(`Assets folder : ${chalk.green(path.relative(process.cwd(), assets))}`)
161
+ console.log(`Tarball : ${chalk.green(path.relative(process.cwd(), tarPath))}`)
162
+ })
73
163
 
74
- /* ── dry-run to detect conflicts ── */
75
- const conflictKeys = new Set()
164
+ /* ─────────────────────────────── add ─────────────────────────────── */
76
165
 
77
- await tar.t({
78
- file: tarPath,
79
- onentry: entry => {
80
- const p = entry.path
81
- if (!p.startsWith("comps/")) return
166
+ program
167
+ .command("add")
168
+ .argument("<names...>", "component names to add, or 'all'")
169
+ .description("add components into ./comps")
170
+ .action(async function (names) {
171
+ const { yes, dry, assetsDir, tarballName } = this._globalOpts
172
+ const spinner = ora("Preparing").start()
173
+
174
+ const assets = findAssetsDir(process.cwd(), assetsDir) || findAssetsDir(__dirname, assetsDir)
175
+ if (!assets) {
176
+ spinner.fail("Assets/ folder not found")
177
+ process.exit(1)
178
+ }
179
+ const tarPath = findTarball(assets, tarballName)
180
+ if (!tarPath) {
181
+ spinner.fail(`Tarball '${tarballName}' not found`)
182
+ process.exit(1)
183
+ }
82
184
 
83
- if (target !== "all") {
84
- const rootFile = p.split("/").length === 2
85
- const inTarget = p === `comps/${target}` || p.startsWith(`comps/${target}/`)
86
- if (!rootFile && !inTarget) return
185
+ const allComps = await scanComponents(tarPath)
186
+ const targetAll = names.length === 1 && names[0] === "all"
187
+ const targets = targetAll ? Array.from(allComps) : names
87
188
 
88
- const dest = path.join(cwd, p)
89
- if (fs.existsSync(dest)) {
90
- conflictKeys.add(rootFile ? p : `comps/${target}`)
189
+ // validate unknown comps
190
+ const unknown = targets.filter((c) => !allComps.has(c))
191
+ if (unknown.length) {
192
+ spinner.fail(`Unknown component(s): ${unknown.join(", ")}`)
193
+ process.exit(1)
194
+ }
195
+
196
+ const cwd = process.cwd()
197
+ const compsRoot = path.join(cwd, "comps")
198
+ fs.mkdirSync(compsRoot, { recursive: true })
199
+
200
+ // conflict detect
201
+ const NO_PROMPT = new Set(["LICENSE"])
202
+ const conflicts = targets.filter(
203
+ (comp) => !NO_PROMPT.has(comp) && fs.existsSync(path.join(compsRoot, comp))
204
+ )
205
+ spinner.stop()
206
+
207
+ if (conflicts.length && !yes) {
208
+ for (const c of conflicts) {
209
+ const ok = await askYN(
210
+ `Overwrite existing ${chalk.yellow(`comps/${c}`)}? (backup your changes first)`
211
+ )
212
+ if (!ok) {
213
+ console.log(chalk.yellow("Aborted."))
214
+ process.exit(0)
91
215
  }
92
- return
93
216
  }
94
-
95
- // ── "all" mode ──
96
- // Ask once per top-level folder (comps/echo, comps/casaul, ...)
97
- const topLevel = p.split("/").slice(0, 2).join("/")
98
- const dest = path.join(cwd, topLevel)
99
- if (fs.existsSync(dest)) conflictKeys.add(topLevel)
100
217
  }
101
- })
102
218
 
103
- for (const key of conflictKeys) {
104
- const ok = await askYN(`Overwrite existing ${key}?`)
105
- if (!ok) {
106
- console.log("Aborted.")
219
+ if (dry) {
220
+ console.log(chalk.green("Dry-run: would install"), targets.join(", "))
107
221
  process.exit(0)
108
222
  }
109
- }
110
223
 
111
- /* ── real extraction ── */
112
- const filter = p => {
113
- if (!p.startsWith("comps/")) return false
114
- if (target === "all") return true
224
+ const extractSpinner = ora("Extracting components").start()
115
225
 
116
- const rootFile = p.split("/").length === 2
117
- return rootFile || p === `comps/${target}` || p.startsWith(`comps/${target}/`)
118
- }
226
+ const filter = (p) => {
227
+ if (!p.startsWith("comps/")) return false
228
+ const [, comp] = p.split("/")
229
+ return targetAll || targets.includes(comp)
230
+ }
119
231
 
120
- await tar.x({
121
- file: tarPath,
122
- cwd,
123
- gzip: true,
124
- filter
232
+ try {
233
+ await tar.x({ file: tarPath, cwd, gzip: true, filter })
234
+ extractSpinner.succeed("Done.")
235
+ } catch (err) {
236
+ extractSpinner.fail("Extraction failed")
237
+ console.error(err)
238
+ process.exit(1)
239
+ }
125
240
  })
126
241
 
127
- console.log("Done.")
128
- })().catch(err => {
242
+ /* ─────────────────────────────── go ─────────────────────────────── */
243
+
244
+ program.parseAsync().catch((err) => {
129
245
  console.error(err)
130
246
  process.exit(1)
131
247
  })
package/package.json CHANGED
@@ -1,16 +1,20 @@
1
1
  {
2
2
  "name": "onejs-core",
3
3
  "description": "The JS part of OneJS, a UI framework and Scripting Engine for Unity.",
4
- "version": "2.0.7",
4
+ "version": "2.0.9",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./typings.d.ts",
7
7
  "bin": {
8
8
  "oj": "./bin/oj.js"
9
9
  },
10
- "dependencies": {
10
+ "dependencies": {
11
+ "chalk": "^4.1.2",
12
+ "commander": "^11.0.0",
11
13
  "css-flatten": "^2.0.0",
12
14
  "css-simple-parser": "^3.0.0",
13
- "progress": "^2.0.3"
15
+ "ora": "^5.4.1",
16
+ "progress": "^2.0.3",
17
+ "tar": "^7.2.0"
14
18
  },
15
19
  "devDependencies": {
16
20
  "esbuild": "^0.20.0",
@@ -19,7 +23,6 @@
19
23
  "postcss-cli": "^11.0.0",
20
24
  "rimraf": "^5.0.7",
21
25
  "tailwindcss": "^3.4.17",
22
- "tar": "^7.2.0",
23
26
  "tiny-glob": "^0.2.9",
24
27
  "xml2js": "^0.6.2"
25
28
  }