@xnoxs/flux-lang 3.4.9 → 3.5.0

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/src/self/pkg.flux CHANGED
@@ -36,11 +36,33 @@ val C = {
36
36
  gray: "\u001b[90m",
37
37
  }
38
38
 
39
- fn clr(c, s): return c + s + C.reset
39
+ val hasColor = process.env.COLORTERM or process.env.FORCE_COLOR or (process.stdout.isTTY and process.env.TERM != "dumb")
40
+ val noColor = process.env.NO_COLOR or not hasColor
41
+
42
+ fn clr(c, s): return noColor ? s : c + s + C.reset
40
43
  fn ok(msg): console.log(clr(C.green, "✓ ") + msg)
41
44
  fn err(msg): console.error(clr(C.red, "✗ ") + msg)
42
45
  fn info(msg): console.log(clr(C.cyan, " ") + msg)
43
46
 
47
+ // ── Spinner animation ─────────────────────────────────────────
48
+ fn createSpinner(msg):
49
+ if noColor:
50
+ process.stdout.write(" " + msg + " ...\n")
51
+ fn _stop(ok_, done):
52
+ process.stdout.write(" " + (ok_ ? "✓" : "✗") + " " + done + "\n")
53
+ return { stop: _stop }
54
+ val frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
55
+ var fi = 0
56
+ fn tick():
57
+ process.stdout.write("\r " + clr(C.cyan, frames[fi % frames.length]) + " " + msg + " ")
58
+ fi = fi + 1
59
+ val timer = setInterval(tick, 80)
60
+ fn stop(ok_, done):
61
+ clearInterval(timer)
62
+ val icon = ok_ ? clr(C.green, "✓") : clr(C.red, "✗")
63
+ process.stdout.write("\r " + icon + " " + done + " \n")
64
+ return { stop: stop }
65
+
44
66
  // ── HTTP fetch (Node built-in, no dependencies) ───────────────
45
67
  async fn fetchJson(url):
46
68
  val mod = url.startsWith("https") ? Https : Http
@@ -100,10 +122,12 @@ export async fn cmdAdd(specs, opts):
100
122
  val isDev = opts?.dev ?? false
101
123
  val cwd = process.cwd()
102
124
  val pkg = ensureFluxJson(cwd)
125
+ var addedCount = 0
103
126
 
127
+ console.log()
104
128
  for spec in specs:
105
129
  val { name, version } = parsePackageSpec(spec)
106
- console.log(clr(C.cyan, "\n Adding ") + clr(C.bold, name) + clr(C.gray, "@" + version) + " ...")
130
+ val spinner = createSpinner(clr(C.bold, name) + clr(C.gray, "@" + version))
107
131
 
108
132
  try:
109
133
  val info_ = await fetchJson(REGISTRY_URL + "/" + name)
@@ -111,25 +135,25 @@ export async fn cmdAdd(specs, opts):
111
135
 
112
136
  val versionInfo = info_.versions?.[resolvedVersion]
113
137
  if not versionInfo:
114
- err("Version " + resolvedVersion + " not found for " + name)
138
+ spinner.stop(false, clr(C.bold, name) + clr(C.gray, "@" + resolvedVersion) + " — version not found")
115
139
  continue
116
140
 
117
141
  val depKey = isDev ? "devDependencies" : "dependencies"
118
142
  if not pkg[depKey]: pkg[depKey] = {}
119
143
  pkg[depKey][name] = "^" + resolvedVersion
120
-
121
144
  saveFluxJson(pkg, cwd)
122
145
 
123
- val desc = versionInfo.description ? clr(C.gray, " — " + versionInfo.description) : ""
124
- ok(name + clr(C.green, "@" + resolvedVersion) + desc)
125
- info("Added to " + (isDev ? "devDependencies" : "dependencies"))
146
+ val shortDesc = versionInfo.description ? clr(C.gray, " — " + versionInfo.description.slice(0, 55)) : ""
147
+ spinner.stop(true, clr(C.bold, name) + clr(C.green, "@" + resolvedVersion) + shortDesc)
148
+ addedCount = addedCount + 1
126
149
 
127
150
  catch(e):
128
- err("Failed to fetch " + name + ": " + e.message)
151
+ spinner.stop(false, clr(C.bold, name) + " " + e.message)
129
152
 
130
153
  console.log()
131
- console.log(clr(C.gray, " Run ") + clr(C.yellow, "flux install") + clr(C.gray, " to install packages"))
132
- console.log()
154
+ if addedCount > 0:
155
+ console.log(" " + clr(C.gray, "Run ") + clr(C.yellow, "flux install") + clr(C.gray, " to install packages"))
156
+ console.log()
133
157
 
134
158
  // ── flux remove <pkg> ─────────────────────────────────────────
135
159
  export fn cmdRemove(names, opts):
@@ -169,15 +193,19 @@ export async fn cmdInstall(opts):
169
193
  val names = Object.keys(all)
170
194
 
171
195
  if names.length == 0:
196
+ console.log()
172
197
  info("No dependencies found in flux.json")
198
+ console.log()
173
199
  return
174
200
 
175
- console.log(clr(C.cyan, "\n Installing ") + names.length + " package(s) into flux_modules/...\n")
176
-
201
+ console.log()
202
+ console.log(" " + clr(C.bold, pkg.name ?? "project") + clr(C.gray, " — " + names.length + " package(s)"))
203
+ console.log()
177
204
  for name in names:
178
205
  val spec = all[name]
179
- console.log(clr(C.gray, " ○ ") + name + clr(C.gray, "@" + spec))
180
-
206
+ val isDev_ = Object.prototype.hasOwnProperty.call(devDeps, name)
207
+ val tag = isDev_ ? clr(C.gray, " dev") : ""
208
+ console.log(" " + clr(C.gray, "○") + " " + clr(isDev_ ? C.blue : C.green, name) + clr(C.gray, "@" + spec) + tag)
181
209
  console.log()
182
210
 
183
211
  // Ensure flux_modules/ exists with a minimal package.json
@@ -191,27 +219,26 @@ export async fn cmdInstall(opts):
191
219
 
192
220
  val prodNames = Object.keys(deps)
193
221
  val devNames = Object.keys(devDeps)
222
+ val spinner = createSpinner("Installing packages into flux_modules/")
194
223
 
195
224
  try:
196
225
  if prodNames.length > 0:
197
226
  val prodPkgs = prodNames.map(n -> n + "@" + (deps[n] ?? "latest")).join(" ")
198
227
  val installCmd = "npm install --prefix " + fluxModDir + " " + prodPkgs
199
- info("Running: " + installCmd)
200
- execSync(installCmd, { cwd: cwd, stdio: "inherit" })
228
+ execSync(installCmd, { cwd: cwd, stdio: "pipe" })
201
229
 
202
230
  if devNames.length > 0:
203
231
  val devPkgs = devNames.map(n -> n + "@" + (devDeps[n] ?? "latest")).join(" ")
204
232
  val devCmd = "npm install --prefix " + fluxModDir + " --save-dev " + devPkgs
205
- info("Running: " + devCmd)
206
- execSync(devCmd, { cwd: cwd, stdio: "inherit" })
233
+ execSync(devCmd, { cwd: cwd, stdio: "pipe" })
207
234
 
235
+ spinner.stop(true, names.length + " package(s) installed into " + clr(C.gray, "flux_modules/"))
208
236
  console.log()
209
- ok(names.length + " package(s) installed into flux_modules/node_modules/")
210
- info("Packages resolve automatically when using: flux run, flux bundle")
237
+ console.log(" " + clr(C.gray, "Packages resolve automatically when using: ") + clr(C.yellow, "flux run") + clr(C.gray, ", ") + clr(C.yellow, "flux bundle"))
211
238
  catch(e):
239
+ spinner.stop(false, "Install failed: " + e.message)
212
240
  console.log()
213
- err("Install failed: " + e.message)
214
- info("Try manually: npm install --prefix ./flux_modules <package>")
241
+ console.log(" " + clr(C.gray, "Try: ") + clr(C.yellow, "npm install --prefix ./flux_modules <package>"))
215
242
 
216
243
  console.log()
217
244
 
@@ -249,58 +276,138 @@ export fn cmdList(opts):
249
276
 
250
277
  // ── flux search <query> ───────────────────────────────────────
251
278
  export async fn cmdSearch(query, opts):
252
- console.log(clr(C.cyan, "\n Searching for ") + clr(C.bold, '"' + query + '"') + " ...\n")
279
+ console.log()
280
+ val spinner = createSpinner("Searching for " + clr(C.bold, '"' + query + '"'))
253
281
  try:
254
- val url = REGISTRY_URL + "/-/v1/search?text=" + encodeURIComponent(query + " keywords:flux") + "&size=10"
282
+ val url = REGISTRY_URL + "/-/v1/search?text=" + encodeURIComponent(query) + "&size=10"
255
283
  val results = await fetchJson(url)
256
284
  val objects = results.objects ?? []
257
285
 
258
286
  if objects.length == 0:
259
- info("No packages found for: " + query)
287
+ spinner.stop(false, "No packages found for: " + query)
260
288
  console.log()
261
289
  return
262
290
 
291
+ spinner.stop(true, "Found " + objects.length + " result(s) for " + clr(C.bold, '"' + query + '"'))
292
+ console.log()
293
+
263
294
  for obj in objects:
264
295
  val p = obj.package
265
296
  console.log(" " + clr(C.bold, p.name) + clr(C.gray, " v" + p.version))
266
297
  if p.description:
267
298
  console.log(" " + clr(C.gray, p.description))
268
- console.log(" " + clr(C.blue, p.links?.npm ?? ""))
299
+ if p.links?.npm:
300
+ console.log(" " + clr(C.blue, p.links.npm))
269
301
  console.log()
270
302
 
271
303
  catch(e):
272
- err("Search failed: " + e.message)
304
+ spinner.stop(false, "Search failed: " + e.message)
305
+ console.log()
273
306
 
274
307
  // ── flux info <pkg> ───────────────────────────────────────────
275
308
  export async fn cmdInfo(name, opts):
276
- console.log(clr(C.cyan, "\n Fetching info for ") + clr(C.bold, name) + " ...\n")
309
+ console.log()
310
+ val spinner = createSpinner("Fetching info for " + clr(C.bold, name))
277
311
  try:
278
312
  val info_ = await fetchJson(REGISTRY_URL + "/" + name)
279
313
  val latest = info_["dist-tags"]?.latest ?? "unknown"
280
314
  val ver = info_.versions?.[latest] ?? {}
281
315
 
282
- console.log(clr(C.bold, " " + name) + clr(C.gray, " v" + latest))
283
- if ver.description: console.log(" " + ver.description)
316
+ spinner.stop(true, clr(C.bold, name) + clr(C.green, "@" + latest))
284
317
  console.log()
285
- console.log(clr(C.gray, " license: ") + (ver.license ?? "unknown"))
286
- console.log(clr(C.gray, " author: ") + (ver.author?.name ?? ver.author ?? "unknown"))
318
+ if ver.description: console.log(" " + clr(C.gray, ver.description))
319
+ console.log()
320
+ console.log(" " + clr(C.gray, "license:") + " " + (ver.license ?? "unknown"))
321
+ console.log(" " + clr(C.gray, "author: ") + " " + (ver.author?.name ?? ver.author ?? "unknown"))
287
322
 
288
323
  val homepage = ver.homepage ?? info_.homepage
289
- if homepage: console.log(clr(C.gray, " home: ") + clr(C.blue, homepage))
324
+ if homepage: console.log(" " + clr(C.gray, "home: ") + " " + clr(C.blue, homepage))
290
325
 
291
326
  val repo = ver.repository?.url ?? info_.repository?.url
292
- if repo: console.log(clr(C.gray, " repo: ") + clr(C.blue, repo.replace("git+", "").replace(".git", "")))
327
+ if repo: console.log(" " + clr(C.gray, "repo: ") + " " + clr(C.blue, repo.replace("git+", "").replace(".git", "")))
293
328
 
294
329
  val keywords = ver.keywords ?? []
295
330
  if keywords.length > 0:
296
- console.log(clr(C.gray, " tags: ") + keywords.slice(0, 8).join(", "))
331
+ console.log(" " + clr(C.gray, "tags: ") + " " + keywords.slice(0, 8).join(", "))
297
332
 
298
333
  console.log()
299
- console.log(clr(C.gray, " Install: ") + clr(C.yellow, "flux add " + name))
334
+ console.log(" " + clr(C.gray, "Install: ") + clr(C.yellow, "flux add " + name))
300
335
  console.log()
301
336
 
302
337
  catch(e):
303
- err("Could not fetch info for " + name + ": " + e.message)
338
+ spinner.stop(false, "Could not fetch info for " + name + ": " + e.message)
339
+ console.log()
340
+
341
+ // ── flux upgrade [--check] ────────────────────────────────────
342
+ export async fn cmdUpgrade(opts):
343
+ val isCheck = opts?.check ?? false
344
+ val cwd = process.cwd()
345
+ val pkg = ensureFluxJson(cwd)
346
+
347
+ val deps = pkg.dependencies ?? {}
348
+ val devDeps = pkg.devDependencies ?? {}
349
+ val allKeys = [...Object.keys(deps), ...Object.keys(devDeps)]
350
+
351
+ if allKeys.length == 0:
352
+ console.log()
353
+ info("No dependencies to upgrade")
354
+ console.log()
355
+ return
356
+
357
+ console.log()
358
+ var updated = 0
359
+
360
+ for name in Object.keys(deps):
361
+ val spinner = createSpinner(clr(C.green, name))
362
+ try:
363
+ val info_ = await fetchJson(REGISTRY_URL + "/" + name)
364
+ val latest = info_["dist-tags"]?.latest ?? null
365
+ if not latest:
366
+ spinner.stop(false, clr(C.green, name) + " — could not resolve latest version")
367
+ continue
368
+ val current = deps[name].replace("^", "").replace("~", "")
369
+ if latest == current:
370
+ spinner.stop(true, clr(C.green, name) + clr(C.gray, "@" + current + " — up to date"))
371
+ else:
372
+ spinner.stop(true, clr(C.green, name) + clr(C.gray, "@" + current) + " → " + clr(C.yellow, latest))
373
+ if not isCheck:
374
+ pkg.dependencies[name] = "^" + latest
375
+ updated = updated + 1
376
+ catch(e):
377
+ spinner.stop(false, clr(C.green, name) + " — " + e.message)
378
+
379
+ for name in Object.keys(devDeps):
380
+ val spinner = createSpinner(clr(C.blue, name) + clr(C.gray, " (dev)"))
381
+ try:
382
+ val info_ = await fetchJson(REGISTRY_URL + "/" + name)
383
+ val latest = info_["dist-tags"]?.latest ?? null
384
+ if not latest:
385
+ spinner.stop(false, clr(C.blue, name) + " — could not resolve latest version")
386
+ continue
387
+ val current = devDeps[name].replace("^", "").replace("~", "")
388
+ if latest == current:
389
+ spinner.stop(true, clr(C.blue, name) + clr(C.gray, "@" + current + " — up to date"))
390
+ else:
391
+ spinner.stop(true, clr(C.blue, name) + clr(C.gray, "@" + current) + " → " + clr(C.yellow, latest) + clr(C.gray, " (dev)"))
392
+ if not isCheck:
393
+ pkg.devDependencies[name] = "^" + latest
394
+ updated = updated + 1
395
+ catch(e):
396
+ spinner.stop(false, clr(C.blue, name) + " — " + e.message)
397
+
398
+ console.log()
399
+
400
+ if isCheck:
401
+ ok("Check complete — run " + clr(C.yellow, "flux upgrade") + " to apply upgrades")
402
+ else:
403
+ if updated > 0:
404
+ saveFluxJson(pkg, cwd)
405
+ ok(updated + " package(s) updated in flux.json")
406
+ console.log(" " + clr(C.gray, "Run ") + clr(C.yellow, "flux install") + clr(C.gray, " to install upgraded packages"))
407
+ else:
408
+ ok("All packages are already up to date")
409
+
410
+ console.log()
304
411
 
305
412
  // ── flux publish ──────────────────────────────────────────────
306
413
  export fn cmdPublish(opts):
package/src/self/pkg.js CHANGED
@@ -21,9 +21,11 @@ const { readPackage, writeConfig, loadConfig } = require("./config");
21
21
  const REGISTRY_URL = "https://registry.npmjs.org";
22
22
  const FLUX_PKG_DIR = "flux_modules";
23
23
  const PKG_FILE = "flux.json";
24
- const C = { reset: "\\u001b[0m", bold: "\\u001b[1m", dim: "\\u001b[2m", red: "\\u001b[31m", green: "\\u001b[32m", yellow: "\\u001b[33m", blue: "\\u001b[34m", cyan: "\\u001b[36m", gray: "\\u001b[90m" };
24
+ const C = { reset: "\u001b[0m", bold: "\u001b[1m", dim: "\u001b[2m", red: "\u001b[31m", green: "\u001b[32m", yellow: "\u001b[33m", blue: "\u001b[34m", cyan: "\u001b[36m", gray: "\u001b[90m" };
25
+ const hasColor = ((process.env.COLORTERM || process.env.FORCE_COLOR) || (process.stdout.isTTY && (process.env.TERM != "dumb")));
26
+ const noColor = (process.env.NO_COLOR || !hasColor);
25
27
  function clr(c, s) {
26
- return ((c + s) + C.reset);
28
+ return (noColor ? s : ((c + s) + C.reset));
27
29
  }
28
30
  function ok(msg) {
29
31
  console.log((clr(C.green, "✓ ") + msg));
@@ -34,6 +36,28 @@ function err(msg) {
34
36
  function info(msg) {
35
37
  console.log((clr(C.cyan, " ") + msg));
36
38
  }
39
+ function createSpinner(msg) {
40
+ if (noColor) {
41
+ process.stdout.write(((" " + msg) + " ...\n"));
42
+ function _stop(ok_, done) {
43
+ process.stdout.write(((((" " + (ok_ ? "✓" : "✗")) + " ") + done) + "\n"));
44
+ }
45
+ return { stop: _stop };
46
+ }
47
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
48
+ let fi = 0;
49
+ function tick() {
50
+ process.stdout.write((((("\r " + clr(C.cyan, frames[(fi % frames.length)])) + " ") + msg) + " "));
51
+ fi = (fi + 1);
52
+ }
53
+ const timer = setInterval(tick, 80);
54
+ function stop(ok_, done) {
55
+ clearInterval(timer);
56
+ const icon = (ok_ ? clr(C.green, "✓") : clr(C.red, "✗"));
57
+ process.stdout.write((((("\r " + icon) + " ") + done) + " \n"));
58
+ }
59
+ return { stop };
60
+ }
37
61
  async function fetchJson(url) {
38
62
  const mod = (url.startsWith("https") ? Https : Http);
39
63
  function doRequest(resolve, reject) {
@@ -83,15 +107,17 @@ async function cmdAdd(specs, opts) {
83
107
  const isDev = (opts?.dev ?? false);
84
108
  const cwd = process.cwd();
85
109
  const pkg = ensureFluxJson(cwd);
110
+ let addedCount = 0;
111
+ console.log();
86
112
  for (const spec of specs) {
87
113
  const { name, version } = parsePackageSpec(spec);
88
- console.log((((clr(C.cyan, "\n Adding ") + clr(C.bold, name)) + clr(C.gray, ("@" + version))) + " ..."));
114
+ const spinner = createSpinner((clr(C.bold, name) + clr(C.gray, ("@" + version))));
89
115
  try {
90
116
  const info_ = await fetchJson(((REGISTRY_URL + "/") + name));
91
117
  const resolvedVersion = ((version == "latest") ? (info_["dist-tags"]?.latest ?? "1.0.0") : version);
92
118
  const versionInfo = info_.versions?.[resolvedVersion];
93
119
  if (!versionInfo) {
94
- err(((("Version " + resolvedVersion) + " not found for ") + name));
120
+ spinner.stop(false, ((clr(C.bold, name) + clr(C.gray, ("@" + resolvedVersion))) + " — version not found"));
95
121
  continue;
96
122
  }
97
123
  const depKey = (isDev ? "devDependencies" : "dependencies");
@@ -100,17 +126,19 @@ async function cmdAdd(specs, opts) {
100
126
  }
101
127
  pkg[depKey][name] = ("^" + resolvedVersion);
102
128
  saveFluxJson(pkg, cwd);
103
- const desc = (versionInfo.description ? clr(C.gray, (" — " + versionInfo.description)) : "");
104
- ok(((name + clr(C.green, ("@" + resolvedVersion))) + desc));
105
- info(("Added to " + (isDev ? "devDependencies" : "dependencies")));
129
+ const shortDesc = (versionInfo.description ? clr(C.gray, (" — " + versionInfo.description.slice(0, 55))) : "");
130
+ spinner.stop(true, ((clr(C.bold, name) + clr(C.green, ("@" + resolvedVersion))) + shortDesc));
131
+ addedCount = (addedCount + 1);
106
132
  }
107
133
  catch (e) {
108
- err(((("Failed to fetch " + name) + ": ") + e.message));
134
+ spinner.stop(false, ((clr(C.bold, name) + " ") + e.message));
109
135
  }
110
136
  }
111
137
  console.log();
112
- console.log(((clr(C.gray, " Run ") + clr(C.yellow, "flux install")) + clr(C.gray, " to install packages")));
113
- console.log();
138
+ if ((addedCount > 0)) {
139
+ console.log((((" " + clr(C.gray, "Run ")) + clr(C.yellow, "flux install")) + clr(C.gray, " to install packages")));
140
+ console.log();
141
+ }
114
142
  }
115
143
  module.exports.cmdAdd = cmdAdd;
116
144
  function cmdRemove(names, opts) {
@@ -150,13 +178,19 @@ async function cmdInstall(opts) {
150
178
  const all = { ...deps, ...devDeps };
151
179
  const names = Object.keys(all);
152
180
  if ((names.length == 0)) {
181
+ console.log();
153
182
  info("No dependencies found in flux.json");
183
+ console.log();
154
184
  return;
155
185
  }
156
- console.log(((clr(C.cyan, "\n Installing ") + names.length) + " package(s) into flux_modules/...\n"));
186
+ console.log();
187
+ console.log(((" " + clr(C.bold, (pkg.name ?? "project"))) + clr(C.gray, ((" — " + names.length) + " package(s)"))));
188
+ console.log();
157
189
  for (const name of names) {
158
190
  const spec = all[name];
159
- console.log(((clr(C.gray, " ○ ") + name) + clr(C.gray, ("@" + spec))));
191
+ const isDev_ = Object.prototype.hasOwnProperty.call(devDeps, name);
192
+ const tag = (isDev_ ? clr(C.gray, " dev") : "");
193
+ console.log((((((" " + clr(C.gray, "○")) + " ") + clr((isDev_ ? C.blue : C.green), name)) + clr(C.gray, ("@" + spec))) + tag));
160
194
  }
161
195
  console.log();
162
196
  if (!Fs.existsSync(fluxModDir)) {
@@ -169,27 +203,26 @@ async function cmdInstall(opts) {
169
203
  }
170
204
  const prodNames = Object.keys(deps);
171
205
  const devNames = Object.keys(devDeps);
206
+ const spinner = createSpinner("Installing packages into flux_modules/");
172
207
  try {
173
208
  if ((prodNames.length > 0)) {
174
209
  const prodPkgs = prodNames.map((n) => ((n + "@") + (deps[n] ?? "latest"))).join(" ");
175
210
  const installCmd = ((("npm install --prefix " + fluxModDir) + " ") + prodPkgs);
176
- info(("Running: " + installCmd));
177
- execSync(installCmd, { cwd, stdio: "inherit" });
211
+ execSync(installCmd, { cwd, stdio: "pipe" });
178
212
  }
179
213
  if ((devNames.length > 0)) {
180
214
  const devPkgs = devNames.map((n) => ((n + "@") + (devDeps[n] ?? "latest"))).join(" ");
181
215
  const devCmd = ((("npm install --prefix " + fluxModDir) + " --save-dev ") + devPkgs);
182
- info(("Running: " + devCmd));
183
- execSync(devCmd, { cwd, stdio: "inherit" });
216
+ execSync(devCmd, { cwd, stdio: "pipe" });
184
217
  }
218
+ spinner.stop(true, ((names.length + " package(s) installed into ") + clr(C.gray, "flux_modules/")));
185
219
  console.log();
186
- ok((names.length + " package(s) installed into flux_modules/node_modules/"));
187
- info("Packages resolve automatically when using: flux run, flux bundle");
220
+ console.log(((((" " + clr(C.gray, "Packages resolve automatically when using: ")) + clr(C.yellow, "flux run")) + clr(C.gray, ", ")) + clr(C.yellow, "flux bundle")));
188
221
  }
189
222
  catch (e) {
223
+ spinner.stop(false, ("Install failed: " + e.message));
190
224
  console.log();
191
- err(("Install failed: " + e.message));
192
- info("Try manually: npm install --prefix ./flux_modules <package>");
225
+ console.log(((" " + clr(C.gray, "Try: ")) + clr(C.yellow, "npm install --prefix ./flux_modules <package>")));
193
226
  }
194
227
  console.log();
195
228
  }
@@ -227,65 +260,156 @@ function cmdList(opts) {
227
260
  }
228
261
  module.exports.cmdList = cmdList;
229
262
  async function cmdSearch(query, opts) {
230
- console.log(((clr(C.cyan, "\n Searching for ") + clr(C.bold, (("\"" + query) + "\""))) + " ...\n"));
263
+ console.log();
264
+ const spinner = createSpinner(("Searching for " + clr(C.bold, (("\"" + query) + "\""))));
231
265
  try {
232
- const url = (((REGISTRY_URL + "/-/v1/search?text=") + encodeURIComponent((query + " keywords:flux"))) + "&size=10");
266
+ const url = (((REGISTRY_URL + "/-/v1/search?text=") + encodeURIComponent(query)) + "&size=10");
233
267
  const results = await fetchJson(url);
234
268
  const objects = (results.objects ?? []);
235
269
  if ((objects.length == 0)) {
236
- info(("No packages found for: " + query));
270
+ spinner.stop(false, ("No packages found for: " + query));
237
271
  console.log();
238
272
  return;
239
273
  }
274
+ spinner.stop(true, ((("Found " + objects.length) + " result(s) for ") + clr(C.bold, (("\"" + query) + "\""))));
275
+ console.log();
240
276
  for (const obj of objects) {
241
277
  const p = obj.package;
242
278
  console.log(((" " + clr(C.bold, p.name)) + clr(C.gray, (" v" + p.version))));
243
279
  if (p.description) {
244
280
  console.log((" " + clr(C.gray, p.description)));
245
281
  }
246
- console.log((" " + clr(C.blue, (p.links?.npm ?? ""))));
282
+ if (p.links?.npm) {
283
+ console.log((" " + clr(C.blue, p.links.npm)));
284
+ }
247
285
  console.log();
248
286
  }
249
287
  }
250
288
  catch (e) {
251
- err(("Search failed: " + e.message));
289
+ spinner.stop(false, ("Search failed: " + e.message));
290
+ console.log();
252
291
  }
253
292
  }
254
293
  module.exports.cmdSearch = cmdSearch;
255
294
  async function cmdInfo(name, opts) {
256
- console.log(((clr(C.cyan, "\n Fetching info for ") + clr(C.bold, name)) + " ...\n"));
295
+ console.log();
296
+ const spinner = createSpinner(("Fetching info for " + clr(C.bold, name)));
257
297
  try {
258
298
  const info_ = await fetchJson(((REGISTRY_URL + "/") + name));
259
299
  const latest = (info_["dist-tags"]?.latest ?? "unknown");
260
300
  const ver = (info_.versions?.[latest] ?? { });
261
- console.log((clr(C.bold, (" " + name)) + clr(C.gray, (" v" + latest))));
301
+ spinner.stop(true, (clr(C.bold, name) + clr(C.green, ("@" + latest))));
302
+ console.log();
262
303
  if (ver.description) {
263
- console.log((" " + ver.description));
304
+ console.log((" " + clr(C.gray, ver.description)));
264
305
  }
265
306
  console.log();
266
- console.log((clr(C.gray, " license: ") + (ver.license ?? "unknown")));
267
- console.log((clr(C.gray, " author: ") + ((ver.author?.name ?? ver.author) ?? "unknown")));
307
+ console.log((((" " + clr(C.gray, "license:")) + " ") + (ver.license ?? "unknown")));
308
+ console.log((((" " + clr(C.gray, "author: ")) + " ") + ((ver.author?.name ?? ver.author) ?? "unknown")));
268
309
  const homepage = (ver.homepage ?? info_.homepage);
269
310
  if (homepage) {
270
- console.log((clr(C.gray, " home: ") + clr(C.blue, homepage)));
311
+ console.log((((" " + clr(C.gray, "home: ")) + " ") + clr(C.blue, homepage)));
271
312
  }
272
313
  const repo = (ver.repository?.url ?? info_.repository?.url);
273
314
  if (repo) {
274
- console.log((clr(C.gray, " repo: ") + clr(C.blue, repo.replace("git+", "").replace(".git", ""))));
315
+ console.log((((" " + clr(C.gray, "repo: ")) + " ") + clr(C.blue, repo.replace("git+", "").replace(".git", ""))));
275
316
  }
276
317
  const keywords = (ver.keywords ?? []);
277
318
  if ((keywords.length > 0)) {
278
- console.log((clr(C.gray, " tags: ") + keywords.slice(0, 8).join(", ")));
319
+ console.log((((" " + clr(C.gray, "tags: ")) + " ") + keywords.slice(0, 8).join(", ")));
279
320
  }
280
321
  console.log();
281
- console.log((clr(C.gray, " Install: ") + clr(C.yellow, ("flux add " + name))));
322
+ console.log(((" " + clr(C.gray, "Install: ")) + clr(C.yellow, ("flux add " + name))));
282
323
  console.log();
283
324
  }
284
325
  catch (e) {
285
- err(((("Could not fetch info for " + name) + ": ") + e.message));
326
+ spinner.stop(false, ((("Could not fetch info for " + name) + ": ") + e.message));
327
+ console.log();
286
328
  }
287
329
  }
288
330
  module.exports.cmdInfo = cmdInfo;
331
+ async function cmdUpgrade(opts) {
332
+ const isCheck = (opts?.check ?? false);
333
+ const cwd = process.cwd();
334
+ const pkg = ensureFluxJson(cwd);
335
+ const deps = (pkg.dependencies ?? { });
336
+ const devDeps = (pkg.devDependencies ?? { });
337
+ const allKeys = [...Object.keys(deps), ...Object.keys(devDeps)];
338
+ if ((allKeys.length == 0)) {
339
+ console.log();
340
+ info("No dependencies to upgrade");
341
+ console.log();
342
+ return;
343
+ }
344
+ console.log();
345
+ let updated = 0;
346
+ for (const name of Object.keys(deps)) {
347
+ const spinner = createSpinner(clr(C.green, name));
348
+ try {
349
+ const info_ = await fetchJson(((REGISTRY_URL + "/") + name));
350
+ const latest = (info_["dist-tags"]?.latest ?? null);
351
+ if (!latest) {
352
+ spinner.stop(false, (clr(C.green, name) + " — could not resolve latest version"));
353
+ continue;
354
+ }
355
+ const current = deps[name].replace("^", "").replace("~", "");
356
+ if ((latest == current)) {
357
+ spinner.stop(true, (clr(C.green, name) + clr(C.gray, (("@" + current) + " — up to date"))));
358
+ }
359
+ else {
360
+ spinner.stop(true, (((clr(C.green, name) + clr(C.gray, ("@" + current))) + " → ") + clr(C.yellow, latest)));
361
+ if (!isCheck) {
362
+ pkg.dependencies[name] = ("^" + latest);
363
+ updated = (updated + 1);
364
+ }
365
+ }
366
+ }
367
+ catch (e) {
368
+ spinner.stop(false, ((clr(C.green, name) + " — ") + e.message));
369
+ }
370
+ }
371
+ for (const name of Object.keys(devDeps)) {
372
+ const spinner = createSpinner((clr(C.blue, name) + clr(C.gray, " (dev)")));
373
+ try {
374
+ const info_ = await fetchJson(((REGISTRY_URL + "/") + name));
375
+ const latest = (info_["dist-tags"]?.latest ?? null);
376
+ if (!latest) {
377
+ spinner.stop(false, (clr(C.blue, name) + " — could not resolve latest version"));
378
+ continue;
379
+ }
380
+ const current = devDeps[name].replace("^", "").replace("~", "");
381
+ if ((latest == current)) {
382
+ spinner.stop(true, (clr(C.blue, name) + clr(C.gray, (("@" + current) + " — up to date"))));
383
+ }
384
+ else {
385
+ spinner.stop(true, ((((clr(C.blue, name) + clr(C.gray, ("@" + current))) + " → ") + clr(C.yellow, latest)) + clr(C.gray, " (dev)")));
386
+ if (!isCheck) {
387
+ pkg.devDependencies[name] = ("^" + latest);
388
+ updated = (updated + 1);
389
+ }
390
+ }
391
+ }
392
+ catch (e) {
393
+ spinner.stop(false, ((clr(C.blue, name) + " — ") + e.message));
394
+ }
395
+ }
396
+ console.log();
397
+ if (isCheck) {
398
+ ok((("Check complete — run " + clr(C.yellow, "flux upgrade")) + " to apply upgrades"));
399
+ }
400
+ else {
401
+ if ((updated > 0)) {
402
+ saveFluxJson(pkg, cwd);
403
+ ok((updated + " package(s) updated in flux.json"));
404
+ console.log((((" " + clr(C.gray, "Run ")) + clr(C.yellow, "flux install")) + clr(C.gray, " to install upgraded packages")));
405
+ }
406
+ else {
407
+ ok("All packages are already up to date");
408
+ }
409
+ }
410
+ console.log();
411
+ }
412
+ module.exports.cmdUpgrade = cmdUpgrade;
289
413
  function cmdPublish(opts) {
290
414
  const cwd = process.cwd();
291
415
  const pkg = readPackage(cwd);