@antfu/ni
Version:
Use the right package manager
268 lines (261 loc) • 7.68 kB
JavaScript
import { a as __toESM } from "./chunk-CMuxRQOh.mjs";
import { A as require_prompts, E as limitText, S as CLI_TEMP_DIR, d as parseNr, i as runCli, k as writeFileSafe } from "./src-DinnZ_Bc.mjs";
import { t as getPackageJSON } from "./fs-ClsKNes2.mjs";
import { existsSync, promises } from "node:fs";
import { dirname, resolve } from "node:path";
import process from "node:process";
import { Fzf, byLengthAsc } from "fzf";
import { globSync } from "tinyglobby";
//#region src/monorepo.ts
var import_prompts = /* @__PURE__ */ __toESM(require_prompts(), 1);
const IGNORE_PATHS = [
"**/node_modules/**",
"**/dist/**",
"**/public/**",
"**/fixture/**",
"**/fixtures/**"
];
function findPackages(ctx) {
const { cwd = process.cwd() } = ctx ?? {};
const packagePath = resolve(cwd, "package.json");
if (!existsSync(packagePath)) return [];
const pkgs = globSync("**/package.json", {
ignore: IGNORE_PATHS,
cwd,
onlyFiles: true,
dot: false,
expandDirectories: false
});
if (pkgs.length <= 1) return [packagePath];
return pkgs;
}
async function promptSelectPackage(ctx, command) {
const cwd = ctx?.cwd ?? process.cwd();
const packagePaths = findPackages(ctx);
if (packagePaths.length <= 1) return ctx;
const blank = " ".repeat(process.stdout?.columns || 80);
let choices = packagePaths.map((item) => {
const filePath = resolve(cwd, item);
const dir = dirname(filePath);
const pkg = getPackageJSON({
...ctx,
cwd: dir,
programmatic: true
});
return {
title: pkg.name ?? item,
value: dir,
description: `${pkg.description ?? filePath}${blank}`,
scripts: pkg.scripts
};
});
if (command) choices = choices.filter((c) => c.scripts?.[command]);
if (!choices.length) return ctx;
if (choices.length === 1) return {
...ctx,
cwd: choices[0].value
};
const fzf = new Fzf(choices, {
selector: (item) => `${item.title} ${item.description}`,
casing: "case-insensitive",
tiebreakers: [byLengthAsc]
});
let res;
try {
const { pkg } = await (0, import_prompts.default)({
name: "pkg",
message: "select a package",
type: "autocomplete",
choices,
async suggest(input, choices) {
if (!input) return choices;
return fzf.find(input).map((r) => choices.find((c) => c.value === r.item.value));
}
});
if (!pkg) throw new Error("No package selected");
res = pkg;
} catch (error) {
if (!ctx?.programmatic) process.exit(1);
throw error;
}
return {
...ctx,
cwd: res
};
}
//#endregion
//#region src/package.ts
async function readWorkspaceScripts(ctx, args) {
const index = args.findIndex((i) => i === "-p");
let command = "";
if (index !== -1) command = args[index + 1];
const context = await promptSelectPackage(ctx, command);
if (ctx && context?.cwd) ctx.cwd = context.cwd;
const scripts = readPackageScripts(context);
const cmdIndex = scripts.findIndex((i) => i.key === command);
if (command && cmdIndex !== -1) return [scripts[cmdIndex]];
return scripts;
}
function readPackageScripts(ctx) {
const pkg = getPackageJSON(ctx);
const rawScripts = pkg.scripts || {};
const scriptsInfo = pkg["scripts-info"] || {};
const scripts = Object.entries(rawScripts).filter((i) => !i[0].startsWith("?")).map(([key, cmd]) => ({
key,
cmd,
description: scriptsInfo[key] || rawScripts[`?${key}`] || cmd
}));
if (scripts.length === 0 && !ctx?.programmatic) console.warn("No scripts found in package.json");
return scripts;
}
//#endregion
//#region src/completion.ts
const rawBashCompletionScript = `
###-begin-nr-completion-###
if type complete &>/dev/null; then
_nr_completion() {
local words
local cur
local cword
_get_comp_words_by_ref -n =: cur words cword
IFS=$'\\n'
COMPREPLY=($(COMP_CWORD=$cword COMP_LINE=$cur nr --completion \${words[@]}))
}
complete -F _nr_completion nr
fi
###-end-nr-completion-###
`.trim();
const rawZshCompletionScript = `
#compdef nr
_nr_completion() {
local -a completions
completions=("\${(f)$(nr --completion $words[2,-1])}")
compadd -a completions
}
_nr_completion
`.trim();
const rawFishCompletionScript = `
function _nr_completion
set -l tokens (commandline -xpc)
if test (count $tokens) -ge 1
set tokens $tokens[2..-1]
end
nr --completion $tokens 2>/dev/null
end
complete -c nr -f -a '(_nr_completion)' -d 'package.json scripts'
`.trim();
function getCompletionSuggestions(args, ctx) {
return new Fzf(readPackageScripts(ctx), {
selector: (item) => item.key,
casing: "case-insensitive",
tiebreakers: [byLengthAsc]
}).find(args[1] || "").map((r) => r.item.key);
}
//#endregion
//#region src/storage.ts
let storage;
const storagePath = resolve(CLI_TEMP_DIR, "_storage.json");
async function load(fn) {
if (!storage) storage = existsSync(storagePath) ? JSON.parse(await promises.readFile(storagePath, "utf-8") || "{}") || {} : {};
if (fn) {
if (await fn(storage)) await dump();
}
return storage;
}
async function dump() {
if (storage) await writeFileSafe(storagePath, JSON.stringify(storage));
}
//#endregion
//#region src/commands/nr.ts
runCli(async (agent, args, ctx) => {
const storage = await load();
const promptSelectScript = async (raw) => {
const terminalColumns = process.stdout?.columns || 80;
const last = storage.lastRunCommand;
const choices = raw.reduce((acc, { key, description }) => {
const item = {
title: key,
value: key,
description: limitText(description, terminalColumns - 15)
};
if (last && key === last) return [item, ...acc];
return [...acc, item];
}, []);
const fzf = new Fzf(raw, {
selector: (item) => `${item.key} ${item.description}`,
casing: "case-insensitive",
tiebreakers: [byLengthAsc]
});
let isExited = false;
try {
const { fn } = await (0, import_prompts.default)({
name: "fn",
message: "script to run",
type: "autocomplete",
choices,
async suggest(input, choices) {
if (!input) return choices;
return fzf.find(input).map((r) => choices.find((c) => c.value === r.item.key));
},
onState(state) {
if (state.exited) isExited = true;
}
});
if (!fn || isExited) process.exit(1);
args.push(fn);
} catch {
process.exit(1);
}
};
if (args[0] === "--completion") {
const compLine = process.env.COMP_LINE;
const rawCompCword = process.env.COMP_CWORD;
if (compLine !== void 0 && rawCompCword !== void 0) {
const compCword = Number.parseInt(rawCompCword, 10);
const compWords = args.slice(1);
if (compCword === 1) {
const suggestions = getCompletionSuggestions(compWords, ctx);
console.log(suggestions.join("\n"));
}
} else {
const suggestions = getCompletionSuggestions(args, ctx);
console.log(suggestions.join("\n"));
}
return;
}
if (args[0] === "-p") {
const raw = await readWorkspaceScripts(ctx, args);
if (raw.length > 1) await promptSelectScript(raw);
}
if (args[0] === "-") {
if (!storage.lastRunCommand) {
if (!ctx?.programmatic) {
console.error("No last command found");
process.exit(1);
}
throw new Error("No last command found");
}
args[0] = storage.lastRunCommand;
}
if (args.length === 0 && !ctx?.programmatic) await promptSelectScript(readPackageScripts(ctx));
if (storage.lastRunCommand !== args[0]) {
storage.lastRunCommand = args[0];
dump();
}
return parseNr(agent, args, ctx);
}, { onBeforeCommand: (args, ctx) => {
if (args[0] === "--completion-zsh") {
console.log(rawZshCompletionScript);
return ctx.exit();
}
if (args[0] === "--completion-bash") {
console.log(rawBashCompletionScript);
return ctx.exit();
}
if (args[0] === "--completion-fish") {
console.log(rawFishCompletionScript);
return ctx.exit();
}
} });
//#endregion
export {};