fast-npm-meta 1.2.1 → 1.4.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/dist/cli.mjs +879 -0
- package/package.json +8 -3
- package/skills/fast-npm-meta/SKILL.md +92 -0
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process$1 from "node:process";
|
|
3
|
+
|
|
4
|
+
//#region ../node_modules/.pnpm/cac@7.0.0/node_modules/cac/dist/index.js
|
|
5
|
+
function toArr(any) {
|
|
6
|
+
return any == null ? [] : Array.isArray(any) ? any : [any];
|
|
7
|
+
}
|
|
8
|
+
function toVal(out, key, val, opts) {
|
|
9
|
+
var x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || val === true ? "" : String(val) : typeof val === "boolean" ? val : !!~opts.boolean.indexOf(key) ? val === "false" ? false : val === "true" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;
|
|
10
|
+
out[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
|
|
11
|
+
}
|
|
12
|
+
function lib_default(args, opts) {
|
|
13
|
+
args = args || [];
|
|
14
|
+
opts = opts || {};
|
|
15
|
+
var k, arr, arg, name, val, out = { _: [] };
|
|
16
|
+
var i = 0, j = 0, idx = 0, len = args.length;
|
|
17
|
+
const alibi = opts.alias !== void 0;
|
|
18
|
+
const strict = opts.unknown !== void 0;
|
|
19
|
+
const defaults = opts.default !== void 0;
|
|
20
|
+
opts.alias = opts.alias || {};
|
|
21
|
+
opts.string = toArr(opts.string);
|
|
22
|
+
opts.boolean = toArr(opts.boolean);
|
|
23
|
+
if (alibi) for (k in opts.alias) {
|
|
24
|
+
arr = opts.alias[k] = toArr(opts.alias[k]);
|
|
25
|
+
for (i = 0; i < arr.length; i++) (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
|
|
26
|
+
}
|
|
27
|
+
for (i = opts.boolean.length; i-- > 0;) {
|
|
28
|
+
arr = opts.alias[opts.boolean[i]] || [];
|
|
29
|
+
for (j = arr.length; j-- > 0;) opts.boolean.push(arr[j]);
|
|
30
|
+
}
|
|
31
|
+
for (i = opts.string.length; i-- > 0;) {
|
|
32
|
+
arr = opts.alias[opts.string[i]] || [];
|
|
33
|
+
for (j = arr.length; j-- > 0;) opts.string.push(arr[j]);
|
|
34
|
+
}
|
|
35
|
+
if (defaults) for (k in opts.default) {
|
|
36
|
+
name = typeof opts.default[k];
|
|
37
|
+
arr = opts.alias[k] = opts.alias[k] || [];
|
|
38
|
+
if (opts[name] !== void 0) {
|
|
39
|
+
opts[name].push(k);
|
|
40
|
+
for (i = 0; i < arr.length; i++) opts[name].push(arr[i]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const keys = strict ? Object.keys(opts.alias) : [];
|
|
44
|
+
for (i = 0; i < len; i++) {
|
|
45
|
+
arg = args[i];
|
|
46
|
+
if (arg === "--") {
|
|
47
|
+
out._ = out._.concat(args.slice(++i));
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
for (j = 0; j < arg.length; j++) if (arg.charCodeAt(j) !== 45) break;
|
|
51
|
+
if (j === 0) out._.push(arg);
|
|
52
|
+
else if (arg.substring(j, j + 3) === "no-") {
|
|
53
|
+
name = arg.substring(j + 3);
|
|
54
|
+
if (strict && !~keys.indexOf(name)) return opts.unknown(arg);
|
|
55
|
+
out[name] = false;
|
|
56
|
+
} else {
|
|
57
|
+
for (idx = j + 1; idx < arg.length; idx++) if (arg.charCodeAt(idx) === 61) break;
|
|
58
|
+
name = arg.substring(j, idx);
|
|
59
|
+
val = arg.substring(++idx) || i + 1 === len || ("" + args[i + 1]).charCodeAt(0) === 45 || args[++i];
|
|
60
|
+
arr = j === 2 ? [name] : name;
|
|
61
|
+
for (idx = 0; idx < arr.length; idx++) {
|
|
62
|
+
name = arr[idx];
|
|
63
|
+
if (strict && !~keys.indexOf(name)) return opts.unknown("-".repeat(j) + name);
|
|
64
|
+
toVal(out, name, idx + 1 < arr.length || val, opts);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (defaults) {
|
|
69
|
+
for (k in opts.default) if (out[k] === void 0) out[k] = opts.default[k];
|
|
70
|
+
}
|
|
71
|
+
if (alibi) for (k in out) {
|
|
72
|
+
arr = opts.alias[k] || [];
|
|
73
|
+
while (arr.length > 0) out[arr.shift()] = out[k];
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function removeBrackets(v) {
|
|
78
|
+
return v.replace(/[<[].+/, "").trim();
|
|
79
|
+
}
|
|
80
|
+
function findAllBrackets(v) {
|
|
81
|
+
const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;
|
|
82
|
+
const SQUARE_BRACKET_RE_GLOBAL = /\[([^\]]+)\]/g;
|
|
83
|
+
const res = [];
|
|
84
|
+
const parse = (match) => {
|
|
85
|
+
let variadic = false;
|
|
86
|
+
let value = match[1];
|
|
87
|
+
if (value.startsWith("...")) {
|
|
88
|
+
value = value.slice(3);
|
|
89
|
+
variadic = true;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
required: match[0].startsWith("<"),
|
|
93
|
+
value,
|
|
94
|
+
variadic
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
let angledMatch;
|
|
98
|
+
while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(angledMatch));
|
|
99
|
+
let squareMatch;
|
|
100
|
+
while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(squareMatch));
|
|
101
|
+
return res;
|
|
102
|
+
}
|
|
103
|
+
function getMriOptions(options) {
|
|
104
|
+
const result = {
|
|
105
|
+
alias: {},
|
|
106
|
+
boolean: []
|
|
107
|
+
};
|
|
108
|
+
for (const [index, option] of options.entries()) {
|
|
109
|
+
if (option.names.length > 1) result.alias[option.names[0]] = option.names.slice(1);
|
|
110
|
+
if (option.isBoolean) if (option.negated) {
|
|
111
|
+
if (!options.some((o, i) => {
|
|
112
|
+
return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === "boolean";
|
|
113
|
+
})) result.boolean.push(option.names[0]);
|
|
114
|
+
} else result.boolean.push(option.names[0]);
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
function findLongest(arr) {
|
|
119
|
+
return arr.sort((a, b) => {
|
|
120
|
+
return a.length > b.length ? -1 : 1;
|
|
121
|
+
})[0];
|
|
122
|
+
}
|
|
123
|
+
function padRight(str, length) {
|
|
124
|
+
return str.length >= length ? str : `${str}${" ".repeat(length - str.length)}`;
|
|
125
|
+
}
|
|
126
|
+
function camelcase(input) {
|
|
127
|
+
return input.replaceAll(/([a-z])-([a-z])/g, (_, p1, p2) => {
|
|
128
|
+
return p1 + p2.toUpperCase();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function setDotProp(obj, keys, val) {
|
|
132
|
+
let current = obj;
|
|
133
|
+
for (let i = 0; i < keys.length; i++) {
|
|
134
|
+
const key = keys[i];
|
|
135
|
+
if (i === keys.length - 1) {
|
|
136
|
+
current[key] = val;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (current[key] == null) {
|
|
140
|
+
const nextKeyIsArrayIndex = +keys[i + 1] > -1;
|
|
141
|
+
current[key] = nextKeyIsArrayIndex ? [] : {};
|
|
142
|
+
}
|
|
143
|
+
current = current[key];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function setByType(obj, transforms) {
|
|
147
|
+
for (const key of Object.keys(transforms)) {
|
|
148
|
+
const transform = transforms[key];
|
|
149
|
+
if (transform.shouldTransform) {
|
|
150
|
+
obj[key] = [obj[key]].flat();
|
|
151
|
+
if (typeof transform.transformFunction === "function") obj[key] = obj[key].map(transform.transformFunction);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function getFileName(input) {
|
|
156
|
+
const m = /([^\\/]+)$/.exec(input);
|
|
157
|
+
return m ? m[1] : "";
|
|
158
|
+
}
|
|
159
|
+
function camelcaseOptionName(name) {
|
|
160
|
+
return name.split(".").map((v, i) => {
|
|
161
|
+
return i === 0 ? camelcase(v) : v;
|
|
162
|
+
}).join(".");
|
|
163
|
+
}
|
|
164
|
+
var CACError = class extends Error {
|
|
165
|
+
constructor(message) {
|
|
166
|
+
super(message);
|
|
167
|
+
this.name = "CACError";
|
|
168
|
+
if (typeof Error.captureStackTrace !== "function") this.stack = new Error(message).stack;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
var Option = class {
|
|
172
|
+
rawName;
|
|
173
|
+
description;
|
|
174
|
+
/** Option name */
|
|
175
|
+
name;
|
|
176
|
+
/** Option name and aliases */
|
|
177
|
+
names;
|
|
178
|
+
isBoolean;
|
|
179
|
+
required;
|
|
180
|
+
config;
|
|
181
|
+
negated;
|
|
182
|
+
constructor(rawName, description, config) {
|
|
183
|
+
this.rawName = rawName;
|
|
184
|
+
this.description = description;
|
|
185
|
+
this.config = Object.assign({}, config);
|
|
186
|
+
rawName = rawName.replaceAll(".*", "");
|
|
187
|
+
this.negated = false;
|
|
188
|
+
this.names = removeBrackets(rawName).split(",").map((v) => {
|
|
189
|
+
let name = v.trim().replace(/^-{1,2}/, "");
|
|
190
|
+
if (name.startsWith("no-")) {
|
|
191
|
+
this.negated = true;
|
|
192
|
+
name = name.replace(/^no-/, "");
|
|
193
|
+
}
|
|
194
|
+
return camelcaseOptionName(name);
|
|
195
|
+
}).sort((a, b) => a.length > b.length ? 1 : -1);
|
|
196
|
+
this.name = this.names.at(-1);
|
|
197
|
+
if (this.negated && this.config.default == null) this.config.default = true;
|
|
198
|
+
if (rawName.includes("<")) this.required = true;
|
|
199
|
+
else if (rawName.includes("[")) this.required = false;
|
|
200
|
+
else this.isBoolean = true;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
let runtimeProcessArgs;
|
|
204
|
+
let runtimeInfo;
|
|
205
|
+
if (typeof process !== "undefined") {
|
|
206
|
+
let runtimeName;
|
|
207
|
+
if (typeof Deno !== "undefined" && typeof Deno.version?.deno === "string") runtimeName = "deno";
|
|
208
|
+
else if (typeof Bun !== "undefined" && typeof Bun.version === "string") runtimeName = "bun";
|
|
209
|
+
else runtimeName = "node";
|
|
210
|
+
runtimeInfo = `${process.platform}-${process.arch} ${runtimeName}-${process.version}`;
|
|
211
|
+
runtimeProcessArgs = process.argv;
|
|
212
|
+
} else if (typeof navigator === "undefined") runtimeInfo = `unknown`;
|
|
213
|
+
else runtimeInfo = `${navigator.platform} ${navigator.userAgent}`;
|
|
214
|
+
var Command = class {
|
|
215
|
+
rawName;
|
|
216
|
+
description;
|
|
217
|
+
config;
|
|
218
|
+
cli;
|
|
219
|
+
options;
|
|
220
|
+
aliasNames;
|
|
221
|
+
name;
|
|
222
|
+
args;
|
|
223
|
+
commandAction;
|
|
224
|
+
usageText;
|
|
225
|
+
versionNumber;
|
|
226
|
+
examples;
|
|
227
|
+
helpCallback;
|
|
228
|
+
globalCommand;
|
|
229
|
+
constructor(rawName, description, config = {}, cli) {
|
|
230
|
+
this.rawName = rawName;
|
|
231
|
+
this.description = description;
|
|
232
|
+
this.config = config;
|
|
233
|
+
this.cli = cli;
|
|
234
|
+
this.options = [];
|
|
235
|
+
this.aliasNames = [];
|
|
236
|
+
this.name = removeBrackets(rawName);
|
|
237
|
+
this.args = findAllBrackets(rawName);
|
|
238
|
+
this.examples = [];
|
|
239
|
+
}
|
|
240
|
+
usage(text) {
|
|
241
|
+
this.usageText = text;
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
allowUnknownOptions() {
|
|
245
|
+
this.config.allowUnknownOptions = true;
|
|
246
|
+
return this;
|
|
247
|
+
}
|
|
248
|
+
ignoreOptionDefaultValue() {
|
|
249
|
+
this.config.ignoreOptionDefaultValue = true;
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
version(version, customFlags = "-v, --version") {
|
|
253
|
+
this.versionNumber = version;
|
|
254
|
+
this.option(customFlags, "Display version number");
|
|
255
|
+
return this;
|
|
256
|
+
}
|
|
257
|
+
example(example) {
|
|
258
|
+
this.examples.push(example);
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Add a option for this command
|
|
263
|
+
* @param rawName Raw option name(s)
|
|
264
|
+
* @param description Option description
|
|
265
|
+
* @param config Option config
|
|
266
|
+
*/
|
|
267
|
+
option(rawName, description, config) {
|
|
268
|
+
const option = new Option(rawName, description, config);
|
|
269
|
+
this.options.push(option);
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
alias(name) {
|
|
273
|
+
this.aliasNames.push(name);
|
|
274
|
+
return this;
|
|
275
|
+
}
|
|
276
|
+
action(callback) {
|
|
277
|
+
this.commandAction = callback;
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check if a command name is matched by this command
|
|
282
|
+
* @param name Command name
|
|
283
|
+
*/
|
|
284
|
+
isMatched(name) {
|
|
285
|
+
return this.name === name || this.aliasNames.includes(name);
|
|
286
|
+
}
|
|
287
|
+
get isDefaultCommand() {
|
|
288
|
+
return this.name === "" || this.aliasNames.includes("!");
|
|
289
|
+
}
|
|
290
|
+
get isGlobalCommand() {
|
|
291
|
+
return this instanceof GlobalCommand;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Check if an option is registered in this command
|
|
295
|
+
* @param name Option name
|
|
296
|
+
*/
|
|
297
|
+
hasOption(name) {
|
|
298
|
+
name = name.split(".")[0];
|
|
299
|
+
return this.options.find((option) => {
|
|
300
|
+
return option.names.includes(name);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
outputHelp() {
|
|
304
|
+
const { name, commands } = this.cli;
|
|
305
|
+
const { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand;
|
|
306
|
+
let sections = [{ body: `${name}${versionNumber ? `/${versionNumber}` : ""}` }];
|
|
307
|
+
sections.push({
|
|
308
|
+
title: "Usage",
|
|
309
|
+
body: ` $ ${name} ${this.usageText || this.rawName}`
|
|
310
|
+
});
|
|
311
|
+
if ((this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0) {
|
|
312
|
+
const longestCommandName = findLongest(commands.map((command) => command.rawName));
|
|
313
|
+
sections.push({
|
|
314
|
+
title: "Commands",
|
|
315
|
+
body: commands.map((command) => {
|
|
316
|
+
return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
|
|
317
|
+
}).join("\n")
|
|
318
|
+
}, {
|
|
319
|
+
title: `For more info, run any command with the \`--help\` flag`,
|
|
320
|
+
body: commands.map((command) => ` $ ${name}${command.name === "" ? "" : ` ${command.name}`} --help`).join("\n")
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];
|
|
324
|
+
if (!this.isGlobalCommand && !this.isDefaultCommand) options = options.filter((option) => option.name !== "version");
|
|
325
|
+
if (options.length > 0) {
|
|
326
|
+
const longestOptionName = findLongest(options.map((option) => option.rawName));
|
|
327
|
+
sections.push({
|
|
328
|
+
title: "Options",
|
|
329
|
+
body: options.map((option) => {
|
|
330
|
+
return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? "" : `(default: ${option.config.default})`}`;
|
|
331
|
+
}).join("\n")
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
if (this.examples.length > 0) sections.push({
|
|
335
|
+
title: "Examples",
|
|
336
|
+
body: this.examples.map((example) => {
|
|
337
|
+
if (typeof example === "function") return example(name);
|
|
338
|
+
return example;
|
|
339
|
+
}).join("\n")
|
|
340
|
+
});
|
|
341
|
+
if (helpCallback) sections = helpCallback(sections) || sections;
|
|
342
|
+
console.info(sections.map((section) => {
|
|
343
|
+
return section.title ? `${section.title}:\n${section.body}` : section.body;
|
|
344
|
+
}).join("\n\n"));
|
|
345
|
+
}
|
|
346
|
+
outputVersion() {
|
|
347
|
+
const { name } = this.cli;
|
|
348
|
+
const { versionNumber } = this.cli.globalCommand;
|
|
349
|
+
if (versionNumber) console.info(`${name}/${versionNumber} ${runtimeInfo}`);
|
|
350
|
+
}
|
|
351
|
+
checkRequiredArgs() {
|
|
352
|
+
const minimalArgsCount = this.args.filter((arg) => arg.required).length;
|
|
353
|
+
if (this.cli.args.length < minimalArgsCount) throw new CACError(`missing required args for command \`${this.rawName}\``);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Check if the parsed options contain any unknown options
|
|
357
|
+
*
|
|
358
|
+
* Exit and output error when true
|
|
359
|
+
*/
|
|
360
|
+
checkUnknownOptions() {
|
|
361
|
+
const { options, globalCommand } = this.cli;
|
|
362
|
+
if (!this.config.allowUnknownOptions) {
|
|
363
|
+
for (const name of Object.keys(options)) if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Check if the required string-type options exist
|
|
368
|
+
*/
|
|
369
|
+
checkOptionValue() {
|
|
370
|
+
const { options: parsedOptions, globalCommand } = this.cli;
|
|
371
|
+
const options = [...globalCommand.options, ...this.options];
|
|
372
|
+
for (const option of options) {
|
|
373
|
+
const value = parsedOptions[option.name.split(".")[0]];
|
|
374
|
+
if (option.required) {
|
|
375
|
+
const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));
|
|
376
|
+
if (value === true || value === false && !hasNegated) throw new CACError(`option \`${option.rawName}\` value is missing`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Check if the number of args is more than expected
|
|
382
|
+
*/
|
|
383
|
+
checkUnusedArgs() {
|
|
384
|
+
const maximumArgsCount = this.args.some((arg) => arg.variadic) ? Infinity : this.args.length;
|
|
385
|
+
if (maximumArgsCount < this.cli.args.length) throw new CACError(`Unused args: ${this.cli.args.slice(maximumArgsCount).map((arg) => `\`${arg}\``).join(", ")}`);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
var GlobalCommand = class extends Command {
|
|
389
|
+
constructor(cli) {
|
|
390
|
+
super("@@global@@", "", {}, cli);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
var CAC = class extends EventTarget {
|
|
394
|
+
/** The program name to display in help and version message */
|
|
395
|
+
name;
|
|
396
|
+
commands;
|
|
397
|
+
globalCommand;
|
|
398
|
+
matchedCommand;
|
|
399
|
+
matchedCommandName;
|
|
400
|
+
/**
|
|
401
|
+
* Raw CLI arguments
|
|
402
|
+
*/
|
|
403
|
+
rawArgs;
|
|
404
|
+
/**
|
|
405
|
+
* Parsed CLI arguments
|
|
406
|
+
*/
|
|
407
|
+
args;
|
|
408
|
+
/**
|
|
409
|
+
* Parsed CLI options, camelCased
|
|
410
|
+
*/
|
|
411
|
+
options;
|
|
412
|
+
showHelpOnExit;
|
|
413
|
+
showVersionOnExit;
|
|
414
|
+
/**
|
|
415
|
+
* @param name The program name to display in help and version message
|
|
416
|
+
*/
|
|
417
|
+
constructor(name = "") {
|
|
418
|
+
super();
|
|
419
|
+
this.name = name;
|
|
420
|
+
this.commands = [];
|
|
421
|
+
this.rawArgs = [];
|
|
422
|
+
this.args = [];
|
|
423
|
+
this.options = {};
|
|
424
|
+
this.globalCommand = new GlobalCommand(this);
|
|
425
|
+
this.globalCommand.usage("<command> [options]");
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Add a global usage text.
|
|
429
|
+
*
|
|
430
|
+
* This is not used by sub-commands.
|
|
431
|
+
*/
|
|
432
|
+
usage(text) {
|
|
433
|
+
this.globalCommand.usage(text);
|
|
434
|
+
return this;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Add a sub-command
|
|
438
|
+
*/
|
|
439
|
+
command(rawName, description, config) {
|
|
440
|
+
const command = new Command(rawName, description || "", config, this);
|
|
441
|
+
command.globalCommand = this.globalCommand;
|
|
442
|
+
this.commands.push(command);
|
|
443
|
+
return command;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Add a global CLI option.
|
|
447
|
+
*
|
|
448
|
+
* Which is also applied to sub-commands.
|
|
449
|
+
*/
|
|
450
|
+
option(rawName, description, config) {
|
|
451
|
+
this.globalCommand.option(rawName, description, config);
|
|
452
|
+
return this;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Show help message when `-h, --help` flags appear.
|
|
456
|
+
*
|
|
457
|
+
*/
|
|
458
|
+
help(callback) {
|
|
459
|
+
this.globalCommand.option("-h, --help", "Display this message");
|
|
460
|
+
this.globalCommand.helpCallback = callback;
|
|
461
|
+
this.showHelpOnExit = true;
|
|
462
|
+
return this;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Show version number when `-v, --version` flags appear.
|
|
466
|
+
*
|
|
467
|
+
*/
|
|
468
|
+
version(version, customFlags = "-v, --version") {
|
|
469
|
+
this.globalCommand.version(version, customFlags);
|
|
470
|
+
this.showVersionOnExit = true;
|
|
471
|
+
return this;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Add a global example.
|
|
475
|
+
*
|
|
476
|
+
* This example added here will not be used by sub-commands.
|
|
477
|
+
*/
|
|
478
|
+
example(example) {
|
|
479
|
+
this.globalCommand.example(example);
|
|
480
|
+
return this;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Output the corresponding help message
|
|
484
|
+
* When a sub-command is matched, output the help message for the command
|
|
485
|
+
* Otherwise output the global one.
|
|
486
|
+
*
|
|
487
|
+
*/
|
|
488
|
+
outputHelp() {
|
|
489
|
+
if (this.matchedCommand) this.matchedCommand.outputHelp();
|
|
490
|
+
else this.globalCommand.outputHelp();
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Output the version number.
|
|
494
|
+
*
|
|
495
|
+
*/
|
|
496
|
+
outputVersion() {
|
|
497
|
+
this.globalCommand.outputVersion();
|
|
498
|
+
}
|
|
499
|
+
setParsedInfo({ args, options }, matchedCommand, matchedCommandName) {
|
|
500
|
+
this.args = args;
|
|
501
|
+
this.options = options;
|
|
502
|
+
if (matchedCommand) this.matchedCommand = matchedCommand;
|
|
503
|
+
if (matchedCommandName) this.matchedCommandName = matchedCommandName;
|
|
504
|
+
return this;
|
|
505
|
+
}
|
|
506
|
+
unsetMatchedCommand() {
|
|
507
|
+
this.matchedCommand = void 0;
|
|
508
|
+
this.matchedCommandName = void 0;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Parse argv
|
|
512
|
+
*/
|
|
513
|
+
parse(argv, { run = true } = {}) {
|
|
514
|
+
if (!argv) {
|
|
515
|
+
if (!runtimeProcessArgs) throw new Error("No argv provided and runtime process argv is not available.");
|
|
516
|
+
argv = runtimeProcessArgs;
|
|
517
|
+
}
|
|
518
|
+
this.rawArgs = argv;
|
|
519
|
+
if (!this.name) this.name = argv[1] ? getFileName(argv[1]) : "cli";
|
|
520
|
+
let shouldParse = true;
|
|
521
|
+
for (const command of this.commands) {
|
|
522
|
+
const parsed = this.mri(argv.slice(2), command);
|
|
523
|
+
const commandName = parsed.args[0];
|
|
524
|
+
if (command.isMatched(commandName)) {
|
|
525
|
+
shouldParse = false;
|
|
526
|
+
const parsedInfo = {
|
|
527
|
+
...parsed,
|
|
528
|
+
args: parsed.args.slice(1)
|
|
529
|
+
};
|
|
530
|
+
this.setParsedInfo(parsedInfo, command, commandName);
|
|
531
|
+
this.dispatchEvent(new CustomEvent(`command:${commandName}`, { detail: command }));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (shouldParse) {
|
|
535
|
+
for (const command of this.commands) if (command.isDefaultCommand) {
|
|
536
|
+
shouldParse = false;
|
|
537
|
+
const parsed = this.mri(argv.slice(2), command);
|
|
538
|
+
this.setParsedInfo(parsed, command);
|
|
539
|
+
this.dispatchEvent(new CustomEvent("command:!", { detail: command }));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (shouldParse) {
|
|
543
|
+
const parsed = this.mri(argv.slice(2));
|
|
544
|
+
this.setParsedInfo(parsed);
|
|
545
|
+
}
|
|
546
|
+
if (this.options.help && this.showHelpOnExit) {
|
|
547
|
+
this.outputHelp();
|
|
548
|
+
run = false;
|
|
549
|
+
this.unsetMatchedCommand();
|
|
550
|
+
}
|
|
551
|
+
if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {
|
|
552
|
+
this.outputVersion();
|
|
553
|
+
run = false;
|
|
554
|
+
this.unsetMatchedCommand();
|
|
555
|
+
}
|
|
556
|
+
const parsedArgv = {
|
|
557
|
+
args: this.args,
|
|
558
|
+
options: this.options
|
|
559
|
+
};
|
|
560
|
+
if (run) this.runMatchedCommand();
|
|
561
|
+
if (!this.matchedCommand && this.args[0]) this.dispatchEvent(new CustomEvent("command:*", { detail: this.args[0] }));
|
|
562
|
+
return parsedArgv;
|
|
563
|
+
}
|
|
564
|
+
mri(argv, command) {
|
|
565
|
+
const cliOptions = [...this.globalCommand.options, ...command ? command.options : []];
|
|
566
|
+
const mriOptions = getMriOptions(cliOptions);
|
|
567
|
+
let argsAfterDoubleDashes = [];
|
|
568
|
+
const doubleDashesIndex = argv.indexOf("--");
|
|
569
|
+
if (doubleDashesIndex !== -1) {
|
|
570
|
+
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
|
|
571
|
+
argv = argv.slice(0, doubleDashesIndex);
|
|
572
|
+
}
|
|
573
|
+
let parsed = lib_default(argv, mriOptions);
|
|
574
|
+
parsed = Object.keys(parsed).reduce((res, name) => {
|
|
575
|
+
return {
|
|
576
|
+
...res,
|
|
577
|
+
[camelcaseOptionName(name)]: parsed[name]
|
|
578
|
+
};
|
|
579
|
+
}, { _: [] });
|
|
580
|
+
const args = parsed._;
|
|
581
|
+
const options = { "--": argsAfterDoubleDashes };
|
|
582
|
+
const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;
|
|
583
|
+
const transforms = Object.create(null);
|
|
584
|
+
for (const cliOption of cliOptions) {
|
|
585
|
+
if (!ignoreDefault && cliOption.config.default !== void 0) for (const name of cliOption.names) options[name] = cliOption.config.default;
|
|
586
|
+
if (Array.isArray(cliOption.config.type) && transforms[cliOption.name] === void 0) {
|
|
587
|
+
transforms[cliOption.name] = Object.create(null);
|
|
588
|
+
transforms[cliOption.name].shouldTransform = true;
|
|
589
|
+
transforms[cliOption.name].transformFunction = cliOption.config.type[0];
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
for (const key of Object.keys(parsed)) if (key !== "_") {
|
|
593
|
+
setDotProp(options, key.split("."), parsed[key]);
|
|
594
|
+
setByType(options, transforms);
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
args,
|
|
598
|
+
options
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
runMatchedCommand() {
|
|
602
|
+
const { args, options, matchedCommand: command } = this;
|
|
603
|
+
if (!command || !command.commandAction) return;
|
|
604
|
+
command.checkUnknownOptions();
|
|
605
|
+
command.checkOptionValue();
|
|
606
|
+
command.checkRequiredArgs();
|
|
607
|
+
command.checkUnusedArgs();
|
|
608
|
+
const actionArgs = [];
|
|
609
|
+
command.args.forEach((arg, index) => {
|
|
610
|
+
if (arg.variadic) actionArgs.push(args.slice(index));
|
|
611
|
+
else actionArgs.push(args[index]);
|
|
612
|
+
});
|
|
613
|
+
actionArgs.push(options);
|
|
614
|
+
return command.commandAction.apply(this, actionArgs);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
/**
|
|
618
|
+
* @param name The program name to display in help and version message
|
|
619
|
+
*/
|
|
620
|
+
const cac = (name = "") => new CAC(name);
|
|
621
|
+
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region package.json
|
|
624
|
+
var version = "1.4.0";
|
|
625
|
+
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region ../node_modules/.pnpm/is-network-error@1.3.0/node_modules/is-network-error/index.js
|
|
628
|
+
const objectToString = Object.prototype.toString;
|
|
629
|
+
const isError = (value) => objectToString.call(value) === "[object Error]";
|
|
630
|
+
const errorMessages = new Set([
|
|
631
|
+
"network error",
|
|
632
|
+
"Failed to fetch",
|
|
633
|
+
"NetworkError when attempting to fetch resource.",
|
|
634
|
+
"The Internet connection appears to be offline.",
|
|
635
|
+
"Network request failed",
|
|
636
|
+
"fetch failed",
|
|
637
|
+
"terminated",
|
|
638
|
+
" A network error occurred.",
|
|
639
|
+
"Network connection lost"
|
|
640
|
+
]);
|
|
641
|
+
function isNetworkError(error) {
|
|
642
|
+
if (!(error && isError(error) && error.name === "TypeError" && typeof error.message === "string")) return false;
|
|
643
|
+
const { message, stack } = error;
|
|
644
|
+
if (message === "Load failed") return stack === void 0 || "__sentry_captured__" in error;
|
|
645
|
+
if (message.startsWith("error sending request for url")) return true;
|
|
646
|
+
return errorMessages.has(message);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
//#endregion
|
|
650
|
+
//#region ../node_modules/.pnpm/p-retry@7.1.1/node_modules/p-retry/index.js
|
|
651
|
+
function validateRetries(retries) {
|
|
652
|
+
if (typeof retries === "number") {
|
|
653
|
+
if (retries < 0) throw new TypeError("Expected `retries` to be a non-negative number.");
|
|
654
|
+
if (Number.isNaN(retries)) throw new TypeError("Expected `retries` to be a valid number or Infinity, got NaN.");
|
|
655
|
+
} else if (retries !== void 0) throw new TypeError("Expected `retries` to be a number or Infinity.");
|
|
656
|
+
}
|
|
657
|
+
function validateNumberOption(name, value, { min = 0, allowInfinity = false } = {}) {
|
|
658
|
+
if (value === void 0) return;
|
|
659
|
+
if (typeof value !== "number" || Number.isNaN(value)) throw new TypeError(`Expected \`${name}\` to be a number${allowInfinity ? " or Infinity" : ""}.`);
|
|
660
|
+
if (!allowInfinity && !Number.isFinite(value)) throw new TypeError(`Expected \`${name}\` to be a finite number.`);
|
|
661
|
+
if (value < min) throw new TypeError(`Expected \`${name}\` to be \u2265 ${min}.`);
|
|
662
|
+
}
|
|
663
|
+
var AbortError = class extends Error {
|
|
664
|
+
constructor(message) {
|
|
665
|
+
super();
|
|
666
|
+
if (message instanceof Error) {
|
|
667
|
+
this.originalError = message;
|
|
668
|
+
({message} = message);
|
|
669
|
+
} else {
|
|
670
|
+
this.originalError = new Error(message);
|
|
671
|
+
this.originalError.stack = this.stack;
|
|
672
|
+
}
|
|
673
|
+
this.name = "AbortError";
|
|
674
|
+
this.message = message;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
function calculateDelay(retriesConsumed, options) {
|
|
678
|
+
const attempt = Math.max(1, retriesConsumed + 1);
|
|
679
|
+
const random = options.randomize ? Math.random() + 1 : 1;
|
|
680
|
+
let timeout = Math.round(random * options.minTimeout * options.factor ** (attempt - 1));
|
|
681
|
+
timeout = Math.min(timeout, options.maxTimeout);
|
|
682
|
+
return timeout;
|
|
683
|
+
}
|
|
684
|
+
function calculateRemainingTime(start, max) {
|
|
685
|
+
if (!Number.isFinite(max)) return max;
|
|
686
|
+
return max - (performance.now() - start);
|
|
687
|
+
}
|
|
688
|
+
async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTime, options }) {
|
|
689
|
+
const normalizedError = error instanceof Error ? error : /* @__PURE__ */ new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`);
|
|
690
|
+
if (normalizedError instanceof AbortError) throw normalizedError.originalError;
|
|
691
|
+
const retriesLeft = Number.isFinite(options.retries) ? Math.max(0, options.retries - retriesConsumed) : options.retries;
|
|
692
|
+
const maxRetryTime = options.maxRetryTime ?? Number.POSITIVE_INFINITY;
|
|
693
|
+
const context = Object.freeze({
|
|
694
|
+
error: normalizedError,
|
|
695
|
+
attemptNumber,
|
|
696
|
+
retriesLeft,
|
|
697
|
+
retriesConsumed
|
|
698
|
+
});
|
|
699
|
+
await options.onFailedAttempt(context);
|
|
700
|
+
if (calculateRemainingTime(startTime, maxRetryTime) <= 0) throw normalizedError;
|
|
701
|
+
const consumeRetry = await options.shouldConsumeRetry(context);
|
|
702
|
+
const remainingTime = calculateRemainingTime(startTime, maxRetryTime);
|
|
703
|
+
if (remainingTime <= 0 || retriesLeft <= 0) throw normalizedError;
|
|
704
|
+
if (normalizedError instanceof TypeError && !isNetworkError(normalizedError)) {
|
|
705
|
+
if (consumeRetry) throw normalizedError;
|
|
706
|
+
options.signal?.throwIfAborted();
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
if (!await options.shouldRetry(context)) throw normalizedError;
|
|
710
|
+
if (!consumeRetry) {
|
|
711
|
+
options.signal?.throwIfAborted();
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
const delayTime = calculateDelay(retriesConsumed, options);
|
|
715
|
+
const finalDelay = Math.min(delayTime, remainingTime);
|
|
716
|
+
options.signal?.throwIfAborted();
|
|
717
|
+
if (finalDelay > 0) await new Promise((resolve, reject) => {
|
|
718
|
+
const onAbort = () => {
|
|
719
|
+
clearTimeout(timeoutToken);
|
|
720
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
721
|
+
reject(options.signal.reason);
|
|
722
|
+
};
|
|
723
|
+
const timeoutToken = setTimeout(() => {
|
|
724
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
725
|
+
resolve();
|
|
726
|
+
}, finalDelay);
|
|
727
|
+
if (options.unref) timeoutToken.unref?.();
|
|
728
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
729
|
+
});
|
|
730
|
+
options.signal?.throwIfAborted();
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
async function pRetry(input, options = {}) {
|
|
734
|
+
options = { ...options };
|
|
735
|
+
validateRetries(options.retries);
|
|
736
|
+
if (Object.hasOwn(options, "forever")) throw new Error("The `forever` option is no longer supported. For many use-cases, you can set `retries: Infinity` instead.");
|
|
737
|
+
options.retries ??= 10;
|
|
738
|
+
options.factor ??= 2;
|
|
739
|
+
options.minTimeout ??= 1e3;
|
|
740
|
+
options.maxTimeout ??= Number.POSITIVE_INFINITY;
|
|
741
|
+
options.maxRetryTime ??= Number.POSITIVE_INFINITY;
|
|
742
|
+
options.randomize ??= false;
|
|
743
|
+
options.onFailedAttempt ??= () => {};
|
|
744
|
+
options.shouldRetry ??= () => true;
|
|
745
|
+
options.shouldConsumeRetry ??= () => true;
|
|
746
|
+
validateNumberOption("factor", options.factor, {
|
|
747
|
+
min: 0,
|
|
748
|
+
allowInfinity: false
|
|
749
|
+
});
|
|
750
|
+
validateNumberOption("minTimeout", options.minTimeout, {
|
|
751
|
+
min: 0,
|
|
752
|
+
allowInfinity: false
|
|
753
|
+
});
|
|
754
|
+
validateNumberOption("maxTimeout", options.maxTimeout, {
|
|
755
|
+
min: 0,
|
|
756
|
+
allowInfinity: true
|
|
757
|
+
});
|
|
758
|
+
validateNumberOption("maxRetryTime", options.maxRetryTime, {
|
|
759
|
+
min: 0,
|
|
760
|
+
allowInfinity: true
|
|
761
|
+
});
|
|
762
|
+
if (!(options.factor > 0)) options.factor = 1;
|
|
763
|
+
options.signal?.throwIfAborted();
|
|
764
|
+
let attemptNumber = 0;
|
|
765
|
+
let retriesConsumed = 0;
|
|
766
|
+
const startTime = performance.now();
|
|
767
|
+
while (Number.isFinite(options.retries) ? retriesConsumed <= options.retries : true) {
|
|
768
|
+
attemptNumber++;
|
|
769
|
+
try {
|
|
770
|
+
options.signal?.throwIfAborted();
|
|
771
|
+
const result = await input(attemptNumber);
|
|
772
|
+
options.signal?.throwIfAborted();
|
|
773
|
+
return result;
|
|
774
|
+
} catch (error) {
|
|
775
|
+
if (await onAttemptFailure({
|
|
776
|
+
error,
|
|
777
|
+
attemptNumber,
|
|
778
|
+
retriesConsumed,
|
|
779
|
+
startTime,
|
|
780
|
+
options
|
|
781
|
+
})) retriesConsumed++;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
throw new Error("Retry attempts exhausted without throwing an error.");
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
//#endregion
|
|
788
|
+
//#region src/api.ts
|
|
789
|
+
const defaultRetryOptions = {
|
|
790
|
+
retries: 5,
|
|
791
|
+
factor: 2,
|
|
792
|
+
minTimeout: 1e3,
|
|
793
|
+
maxTimeout: Infinity,
|
|
794
|
+
randomize: false
|
|
795
|
+
};
|
|
796
|
+
const defaultOptions = { apiEndpoint: "https://npm.antfu.dev/" };
|
|
797
|
+
async function getLatestVersionBatch(packages, options = {}) {
|
|
798
|
+
const { apiEndpoint = defaultOptions.apiEndpoint, fetch: fetchApi = fetch, throw: throwError = true, retry = defaultRetryOptions } = options;
|
|
799
|
+
let query = [
|
|
800
|
+
options.force ? "force=true" : "",
|
|
801
|
+
options.metadata ? "metadata=true" : "",
|
|
802
|
+
throwError ? "" : "throw=false"
|
|
803
|
+
].filter(Boolean).join("&");
|
|
804
|
+
if (query) query = `?${query}`;
|
|
805
|
+
const fetchFn = () => fetchApi(new URL(packages.join("+") + query, apiEndpoint)).then((r) => r.json());
|
|
806
|
+
const retryOptions = typeof retry === "number" ? {
|
|
807
|
+
...defaultRetryOptions,
|
|
808
|
+
retries: retry
|
|
809
|
+
} : retry;
|
|
810
|
+
const list = toArray(await (retryOptions === false ? fetchFn() : pRetry(fetchFn, retryOptions)));
|
|
811
|
+
return throwError ? throwErrorObject(list) : list;
|
|
812
|
+
}
|
|
813
|
+
async function getVersionsBatch(packages, options = {}) {
|
|
814
|
+
const { apiEndpoint = defaultOptions.apiEndpoint, fetch: fetchApi = fetch, throw: throwError = true, retry = defaultRetryOptions } = options;
|
|
815
|
+
let query = [
|
|
816
|
+
options.force ? "force=true" : "",
|
|
817
|
+
options.loose ? "loose=true" : "",
|
|
818
|
+
options.metadata ? "metadata=true" : "",
|
|
819
|
+
options.after ? `after=${encodeURIComponent(options.after)}` : "",
|
|
820
|
+
throwError ? "" : "throw=false"
|
|
821
|
+
].filter(Boolean).join("&");
|
|
822
|
+
if (query) query = `?${query}`;
|
|
823
|
+
const fetchFn = () => fetchApi(new URL(`/versions/${packages.join("+")}${query}`, apiEndpoint)).then((r) => r.json());
|
|
824
|
+
const list = toArray(await (retry === false ? fetchFn() : pRetry(fetchFn, typeof retry === "number" ? {
|
|
825
|
+
...defaultRetryOptions,
|
|
826
|
+
retries: retry
|
|
827
|
+
} : retry)));
|
|
828
|
+
return throwError ? throwErrorObject(list) : list;
|
|
829
|
+
}
|
|
830
|
+
function throwErrorObject(data) {
|
|
831
|
+
for (const item of toArray(data)) if (item && "error" in item) throw new Error(item.message || item.error);
|
|
832
|
+
return data;
|
|
833
|
+
}
|
|
834
|
+
function toArray(data) {
|
|
835
|
+
if (Array.isArray(data)) return data;
|
|
836
|
+
return [data];
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
//#endregion
|
|
840
|
+
//#region src/cli.ts
|
|
841
|
+
const cli = cac("fast-npm-meta");
|
|
842
|
+
cli.command("version <...pkgs>", "Get the latest version of one or more packages").option("--json", "Output as JSON").option("--force", "Bypass cache and get the latest data").option("--metadata", "Include version metadata (engines, deprecated, etc.)").option("--api-endpoint <url>", "API endpoint URL").action(async (pkgs, options) => {
|
|
843
|
+
try {
|
|
844
|
+
const results = await getLatestVersionBatch(pkgs, {
|
|
845
|
+
force: options.force,
|
|
846
|
+
metadata: options.metadata,
|
|
847
|
+
apiEndpoint: options.apiEndpoint
|
|
848
|
+
});
|
|
849
|
+
if (options.json) {
|
|
850
|
+
const output = results.length === 1 ? results[0] : results;
|
|
851
|
+
console.log(JSON.stringify(output, null, 2));
|
|
852
|
+
} else for (const result of results) console.log(result.version);
|
|
853
|
+
} catch (e) {
|
|
854
|
+
console.error(e.message);
|
|
855
|
+
process$1.exit(1);
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
cli.command("full <...pkgs>", "Get full package metadata (versions list, dist-tags, etc.)").option("--force", "Bypass cache and get the latest data").option("--loose", "Include all versions that are newer than the specified version").option("--metadata", "Include per-version metadata (time, engines, deprecated, etc.)").option("--after <date>", "Only return versions published after this ISO date-time").option("--api-endpoint <url>", "API endpoint URL").action(async (pkgs, options) => {
|
|
859
|
+
try {
|
|
860
|
+
const results = await getVersionsBatch(pkgs, {
|
|
861
|
+
force: options.force,
|
|
862
|
+
loose: options.loose,
|
|
863
|
+
metadata: options.metadata,
|
|
864
|
+
after: options.after,
|
|
865
|
+
apiEndpoint: options.apiEndpoint
|
|
866
|
+
});
|
|
867
|
+
const output = results.length === 1 ? results[0] : results;
|
|
868
|
+
console.log(JSON.stringify(output, null, 2));
|
|
869
|
+
} catch (e) {
|
|
870
|
+
console.error(e.message);
|
|
871
|
+
process$1.exit(1);
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
cli.help();
|
|
875
|
+
cli.version(version);
|
|
876
|
+
cli.parse();
|
|
877
|
+
|
|
878
|
+
//#endregion
|
|
879
|
+
export { };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-npm-meta",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.0",
|
|
5
5
|
"description": "Get npm package metadata",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -19,12 +19,17 @@
|
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
21
|
"types": "./dist/index.d.mts",
|
|
22
|
+
"bin": {
|
|
23
|
+
"fast-npm-meta": "./dist/cli.mjs"
|
|
24
|
+
},
|
|
22
25
|
"files": [
|
|
23
|
-
"dist"
|
|
26
|
+
"dist",
|
|
27
|
+
"skills"
|
|
24
28
|
],
|
|
25
29
|
"devDependencies": {
|
|
30
|
+
"cac": "^7.0.0",
|
|
26
31
|
"p-retry": "^7.1.1",
|
|
27
|
-
"tsdown": "^0.20.
|
|
32
|
+
"tsdown": "^0.20.3"
|
|
28
33
|
},
|
|
29
34
|
"scripts": {
|
|
30
35
|
"build": "tsdown",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fast-npm-meta
|
|
3
|
+
description: Get npm package metadata
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# fast-npm-meta CLI
|
|
8
|
+
|
|
9
|
+
When querying npm package versions or metadata, **prefer `fast-npm-meta` over `npm view`**.
|
|
10
|
+
|
|
11
|
+
`npm view` fetches the full registry manifest (often 4+ MB of JSON per package). `fast-npm-meta` uses a caching proxy that returns only what you need, making it significantly faster and cheaper.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
Use `fast-npm-meta` whenever you need to:
|
|
16
|
+
- Look up the latest version of a package
|
|
17
|
+
- Check what versions of a package exist
|
|
18
|
+
- Inspect dist-tags, publish dates, or engine requirements
|
|
19
|
+
- Resolve a version range to a specific version
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Get the latest version (plain text)
|
|
24
|
+
|
|
25
|
+
One version per line — suitable for scripting.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
fast-npm-meta version vite
|
|
29
|
+
# 7.3.1
|
|
30
|
+
fast-npm-meta version vite@8
|
|
31
|
+
# 8.0.0
|
|
32
|
+
fast-npm-meta version "nuxt@^3.5"
|
|
33
|
+
# 3.5.22
|
|
34
|
+
|
|
35
|
+
fast-npm-meta version vite nuxt vue
|
|
36
|
+
# 7.3.1
|
|
37
|
+
# 4.3.1
|
|
38
|
+
# 3.5.29
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Get version as JSON
|
|
42
|
+
|
|
43
|
+
Single package returns an object; multiple packages return an array.
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
fast-npm-meta version vite --json
|
|
47
|
+
fast-npm-meta version vite nuxt vue --json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Add `--metadata` to also get engines, deprecated, and provenance fields:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
fast-npm-meta version vite --json --metadata
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Get full package metadata (all versions, dist-tags)
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
fast-npm-meta full vite
|
|
60
|
+
fast-npm-meta full vite nuxt vue # returns an array
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Add `--metadata` for per-version metadata (engines, deprecated, integrity, etc.):
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
fast-npm-meta full vite --metadata
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Filter to versions published after a date:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
fast-npm-meta full vite --after 2025-01-01T00:00:00Z
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Do NOT use these instead
|
|
76
|
+
|
|
77
|
+
| Avoid | Use instead |
|
|
78
|
+
|-------|-------------|
|
|
79
|
+
| `npm view vite version` | `fast-npm-meta version vite` |
|
|
80
|
+
| `npm view vite versions --json` | `fast-npm-meta full vite` |
|
|
81
|
+
| `npm view vite dist-tags --json` | `fast-npm-meta full vite` |
|
|
82
|
+
|
|
83
|
+
## Flags reference
|
|
84
|
+
|
|
85
|
+
| Flag | Commands | Description |
|
|
86
|
+
|------|----------|-------------|
|
|
87
|
+
| `--json` | `version` | Output as JSON instead of plain text |
|
|
88
|
+
| `--force` | both | Bypass cache, fetch fresh data |
|
|
89
|
+
| `--metadata` | both | Include per-version metadata |
|
|
90
|
+
| `--loose` | `full` | Include all versions newer than the specifier |
|
|
91
|
+
| `--after <date>` | `full` | Only versions published after this ISO date |
|
|
92
|
+
| `--api-endpoint <url>` | both | Custom API endpoint |
|