@tommy-ca/lazypi 0.10.3 → 0.10.4
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/bin/lazypi.mjs +51 -4
- package/package.json +1 -1
package/bin/lazypi.mjs
CHANGED
|
@@ -185,7 +185,7 @@ function matchesSelector(pkg, selectors) {
|
|
|
185
185
|
return selectors.some((name) => name === pkg.category || name === pkg.id);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
function resolveSelection(flags) {
|
|
188
|
+
export function resolveSelection(flags) {
|
|
189
189
|
if (flags.only) {
|
|
190
190
|
if (!validateSelectors(flags.only, "--only")) return null;
|
|
191
191
|
return new Set(PACKAGES.filter((p) => matchesSelector(p, flags.only)).map((p) => p.id));
|
|
@@ -253,9 +253,47 @@ export function buildSpawnOptions(options = {}, _platformName = platform()) {
|
|
|
253
253
|
return { ...options };
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
function isWindowsCmdOrBat(resolved, platformName = platform()) {
|
|
257
|
+
return platformName === "win32" && /\.(cmd|bat)$/i.test(String(resolved));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const CMD_QUOTE_RE = /[\s&|<>^%!"()]/;
|
|
261
|
+
|
|
262
|
+
export function quoteCmdArg(value, { always = false } = {}) {
|
|
263
|
+
const text = String(value);
|
|
264
|
+
if (!always && text !== "" && !CMD_QUOTE_RE.test(text)) return text;
|
|
265
|
+
return `"${text.replaceAll("%", "%%").replaceAll('"', '""')}"`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function windowsComSpec(env = process.env) {
|
|
269
|
+
const comspec = env.ComSpec;
|
|
270
|
+
if (comspec && /(^|[\\/])cmd\.exe$/i.test(comspec)) return comspec;
|
|
271
|
+
const root = env.SystemRoot || "C:\\Windows";
|
|
272
|
+
return `${root}\\System32\\cmd.exe`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function pickWindowsWhereHit(stdout) {
|
|
276
|
+
const lines = String(stdout ?? "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
277
|
+
return lines.find((line) => /\.(cmd|bat|exe|com)$/i.test(line)) ?? lines[0] ?? null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function windowsSpawnArgv(resolved, args = [], platformName = platform()) {
|
|
281
|
+
if (isWindowsCmdOrBat(resolved, platformName)) {
|
|
282
|
+
return {
|
|
283
|
+
command: windowsComSpec(),
|
|
284
|
+
args: ["/d", "/s", "/c", "call", quoteCmdArg(resolved, { always: true }), ...args.map((arg) => quoteCmdArg(arg))],
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return { command: resolved, args: [...args] };
|
|
288
|
+
}
|
|
289
|
+
|
|
256
290
|
function spawnCommand(command, args = [], options = {}) {
|
|
257
291
|
const resolved = command === "pi" || command === "npm" ? commandPath(command) ?? command : command;
|
|
258
|
-
|
|
292
|
+
const spawned = windowsSpawnArgv(resolved, args);
|
|
293
|
+
const spawnOptions = isWindowsCmdOrBat(resolved)
|
|
294
|
+
? { ...buildSpawnOptions(options), shell: false, windowsVerbatimArguments: true }
|
|
295
|
+
: buildSpawnOptions(options);
|
|
296
|
+
return spawnSync(spawned.command, spawned.args, spawnOptions);
|
|
259
297
|
}
|
|
260
298
|
|
|
261
299
|
function hasCmd(name) {
|
|
@@ -355,6 +393,7 @@ function commandPath(name) {
|
|
|
355
393
|
const command = platform() === "win32" ? "where" : "which";
|
|
356
394
|
const probe = spawnSync(command, [name], buildSpawnOptions({ encoding: "utf8" }));
|
|
357
395
|
if (probe.status !== 0) return null;
|
|
396
|
+
if (platform() === "win32") return pickWindowsWhereHit(probe.stdout);
|
|
358
397
|
const first = String(probe.stdout ?? "").split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
359
398
|
return first || null;
|
|
360
399
|
}
|
|
@@ -766,6 +805,11 @@ function cmdStatus(flags) {
|
|
|
766
805
|
);
|
|
767
806
|
|
|
768
807
|
printHeader(`Installed from LazyPi catalog (${installed.length}/${PACKAGES.length}):`);
|
|
808
|
+
const coreTotal = PACKAGES.filter((pkg) => pkg.category === "core").length;
|
|
809
|
+
const optionalTotal = PACKAGES.filter((pkg) => pkg.category === "optional").length;
|
|
810
|
+
const coreInstalled = installed.filter((pkg) => pkg.category === "core").length;
|
|
811
|
+
const optionalInstalled = installed.filter((pkg) => pkg.category === "optional").length;
|
|
812
|
+
console.log(dim(` core ${coreInstalled}/${coreTotal}, optional ${optionalInstalled}/${optionalTotal}`));
|
|
769
813
|
if (installed.length === 0) console.log(dim(" none"));
|
|
770
814
|
for (const pkg of installed) {
|
|
771
815
|
const installedSource = [...sources].find((src) => sourcesMatch(pkg.source, src)) ?? pkg.source;
|
|
@@ -822,7 +866,7 @@ function cmdDoctor(flags) {
|
|
|
822
866
|
let problems = 0;
|
|
823
867
|
let warnings = 0;
|
|
824
868
|
const pass = (msg) => console.log(` ${green("✓")} ${msg}`);
|
|
825
|
-
const warn = (msg, { fatal =
|
|
869
|
+
const warn = (msg, { fatal = false } = {}) => {
|
|
826
870
|
console.log(` ${yellow("!")} ${msg}`);
|
|
827
871
|
if (fatal) problems++;
|
|
828
872
|
else warnings++;
|
|
@@ -860,7 +904,10 @@ function cmdDoctor(flags) {
|
|
|
860
904
|
else if (error) fail(`${path} is not valid JSON — ${error}`);
|
|
861
905
|
else {
|
|
862
906
|
pass(`${path} is readable`);
|
|
863
|
-
const unpinnedGit = [...sources].filter((src) =>
|
|
907
|
+
const unpinnedGit = [...sources].filter((src) => {
|
|
908
|
+
if (!/^git:github\.com\/[^/@]+\/[^@\s]+$/.test(src)) return false;
|
|
909
|
+
return !PACKAGES.some((pkg) => sourcesMatch(pkg.source, src) || isLegacySourceForPackage(pkg, src));
|
|
910
|
+
});
|
|
864
911
|
for (const src of unpinnedGit) warn(`${src} is an unpinned git head — pin it to a commit for reproducible installs`, { fatal: false });
|
|
865
912
|
}
|
|
866
913
|
|