@akrc/fnpm
Version:
723 lines (701 loc) • 17.5 kB
JavaScript
import consola, { consola as consola$1 } from "consola";
import { commands } from "pm-combo";
import { resolveContext } from "fnpm-context";
import parser from "fnpm-parse";
import { packageDirectory } from "package-directory";
import { x } from "tinyexec";
import { hideBin } from "yargs/helpers";
import colors from "picocolors";
import { readPackage } from "read-pkg";
import * as doctor from "fnpm-doctor";
import { start } from "fnpm-ui";
import { getPort } from "get-port-please";
import gitUrlParse from "git-url-parse";
import open from "open";
//#region package.json
var name = "@akrc/fnpm";
var version = "1.13.0";
var description = "";
var type = "module";
var scripts = {
"build": "tsdown",
"link:fnpm": "pnpm link . -g",
"unlink:fnpm": "pnpm remove @akrc/fnpm -g",
"dev": "tsdown --watch"
};
var keywords = ["fnpm"];
var author = "AkaraChen";
var homepage = "https://github.com/akarachen/fnpm";
var repository = {
"type": "git",
"url": "git+https://github.com/akarachen/fnpm.git"
};
var license = "ISC";
var devDependencies = {
"@akrc/monorepo-tools": "^4.1.0",
"@effect/platform": "^0.84.9",
"@effect/platform-node": "^0.85.11",
"@types/yargs": "^17.0.33",
"effect": "^3.16.5",
"fnpm-test-suite": "workspace:*",
"nanoid": "^5.1.5",
"type-fest": "^4.41.0"
};
var dependencies = {
"consola": "^3.4.2",
"fnpm-context": "workspace:*",
"fnpm-doctor": "workspace:*",
"fnpm-parse": "workspace:*",
"fnpm-ui": "workspace:*",
"get-port-please": "^3.1.2",
"git-url-parse": "^16.1.0",
"open": "^10.1.2",
"package-directory": "^8.1.0",
"picocolors": "^1.1.1",
"pm-combo": "workspace:*",
"read-pkg": "^9.0.1",
"tinyexec": "^1.0.1",
"yargs": "^18.0.0"
};
var bin = {
"fnpm": "./dist/fnpm.js",
"fnpx": "./dist/fnpx.js"
};
var files = ["dist"];
var package_default = {
name,
version,
description,
type,
scripts,
keywords,
author,
homepage,
repository,
license,
devDependencies,
dependencies,
bin,
files
};
//#endregion
//#region src/commands/base.ts
/**
* Base command class that all command classes can extend
*
* @template T The command options type
*/
var BaseCommand = class {
/**
* Context object
*/
ctx;
constructor(ctx) {
this.ctx = ctx;
}
};
var CommandFactory = class {
ctx;
constructor(ctx) {
this.ctx = ctx;
}
create(Command, ctx = this.ctx) {
const original = new Command(ctx);
const command = { ...original };
command.builder = original.builder?.bind(original);
command.handler = original.handler?.bind(original);
return command;
}
};
//#endregion
//#region src/util.ts
function exec(shell, opts = {}) {
const { cwd } = opts;
const [command, ...args] = shell;
return x(command, args, { nodeOptions: {
cwd,
stdio: "inherit"
} });
}
function error(message) {
consola$1.error(message);
process.exit(1);
}
async function getContext(cwd) {
const ctx = await resolveContext(cwd);
const hasWFlag = process.argv[2] === "-w";
if (hasWFlag) process.argv.splice(2, 1);
const args = hideBin(process.argv);
const root = hasWFlag ? ctx.root : await packageDirectory({ cwd }) || cwd;
return {
pm: ctx.pm,
args,
root
};
}
function normalizePackageVersion(input) {
const parsed = parser.parse(input);
return `${parsed.fullName}@${parsed.version || "latest"}`;
}
//#endregion
//#region src/commands/add.ts
var Add = class extends BaseCommand {
command = [
"add [packages..]",
"a",
"install",
"i"
];
describe = "add packages";
builder = (args) => {
return args.positional("packages", {
type: "string",
array: true,
description: "Packages to install"
}).option("save", {
alias: ["d"],
type: "boolean",
description: "Save to dependencies",
conflicts: [
"save-dev",
"save-exact",
"save-peer",
"save-optional"
]
}).option("save-dev", {
alias: ["D"],
type: "boolean",
description: "Save as devDependencies",
conflicts: [
"save",
"save-exact",
"save-peer",
"save-optional"
]
}).option("save-exact", {
alias: ["E", "exact"],
type: "boolean",
description: "Save exact version",
conflicts: [
"save",
"save-dev",
"save-peer",
"save-optional"
]
}).option("save-peer", {
alias: ["P", "peer"],
type: "boolean",
description: "Save as peerDependencies",
conflicts: [
"save",
"save-dev",
"save-exact",
"save-optional"
]
}).option("save-optional", {
alias: ["O", "optional"],
type: "boolean",
description: "Save as optionalDependencies",
conflicts: [
"save",
"save-dev",
"save-exact",
"save-peer"
]
}).option("fixed", {
alias: ["F"],
type: "boolean",
description: "Use fixed version",
default: false
}).option("workspace", {
alias: ["W", "w"],
type: "boolean",
description: "Add packages to workspace root",
default: false
}).option("global", {
alias: ["G", "g"],
type: "boolean",
description: "Install packages globally",
default: false
});
};
async handler(args) {
const { packages, saveOptional = false, saveExact = false, saveDev = false, savePeer = false, fixed = false, workspace = false, global = false, save = true } = args;
consola.info(`Installing packages with ${this.ctx.pm}`);
const options = {
packages,
save,
saveDev,
savePeer,
saveOptional,
exact: saveExact,
global,
fixed,
allowRoot: workspace
};
const command = options.packages ? commands.add.concat(this.ctx.pm, options) : commands.install.concat(this.ctx.pm, options);
await exec(command, { cwd: this.ctx.root });
}
};
var add_default = Add;
//#endregion
//#region src/commands/ci.ts
var CI = class extends BaseCommand {
command = "ci";
describe = "run continuous integration";
builder(args) {
return args;
}
async handler() {
const command = commands.install.concat(this.ctx.pm, { fixed: true });
consola.info(`Running CI with ${this.ctx.pm}`);
await exec(command, { cwd: this.ctx.root });
}
};
var ci_default = CI;
//#endregion
//#region src/commands/config.ts
const verbsMap = {
list: ["list", "ls"],
get: ["get", "g"],
set: ["set", "s"],
delete: [
"delete",
"d",
"rm",
"del",
"remove",
"unset"
]
};
const verbs = Object.values(verbsMap).flat();
var Config = class extends BaseCommand {
command = ["config [verb] [key] [value]", "c"];
describe = "Manage the npm configuration files";
builder = (args) => {
return args.positional("verb", {
type: "string",
description: "Verb to use",
demandOption: false,
default: "list",
choices: verbs
}).positional("key", {
type: "string",
description: "Key to get or set",
demandOption: false
}).positional("value", {
type: "string",
description: "Value to set",
demandOption: false
}).option("global", {
alias: ["G", "g"],
type: "boolean",
description: "Update packages globally"
}).option("json", {
alias: ["j"],
type: "boolean",
description: "Output json"
});
};
async handler(args) {
const { global, json, verb, key, value } = args;
if (!verbs.includes(verb)) error(`Invalid verb ${verb}`);
const command = commands.config.concat(this.ctx.pm, {
verb: Object.entries(verbsMap).find(([_k, v]) => v.includes(verb))[0],
global,
json,
key,
value
});
await exec(command, { cwd: this.ctx.root });
}
};
var config_default = Config;
//#endregion
//#region src/commands/default.ts
var Default = class extends BaseCommand {
command = "*";
describe = "run a script";
builder(args) {
return args;
}
async handler(args) {
if (args._.length === 0) {
consola.info("Installing dependencies");
const shell$1 = commands.install.concat(this.ctx.pm, {});
await exec(shell$1, { cwd: this.ctx.root });
return;
}
const inputs = this.ctx.args;
let pkg;
try {
pkg = await readPackage({ cwd: this.ctx.root });
} catch {
error("Not in a package workspace, you may running fnpm at incorrect place.");
}
const scripts$1 = pkg.scripts || {};
const [script, ...otherArgs] = inputs;
if (script && scripts$1[script]) {
const shell$1 = [
"node",
"--run",
script,
"--",
...otherArgs
];
await exec(shell$1, { cwd: this.ctx.root });
return;
}
const shell = commands.exec.concat(this.ctx.pm, { args: inputs });
consola.info(`Running ${colors.green(shell.join(" "))}`);
await exec(shell, { cwd: this.ctx.root });
}
};
var default_default = Default;
//#endregion
//#region src/commands/dlx.ts
var Dlx = class extends BaseCommand {
command = "dlx";
describe = "download and exec";
builder(args) {
return args;
}
async handler(args) {
const pkg = args._[0] === "dlx" ? args._[1] : args._[0];
const rest = this.ctx.args.slice(this.ctx.args.indexOf(pkg) + 1);
if (!pkg) error("No package specified");
const command = commands.dlx.concat(this.ctx.pm, {
package: normalizePackageVersion(pkg),
args: rest
});
consola.info(`Running ${command.join(" ")}`);
await exec(command);
}
};
var dlx_default = Dlx;
//#endregion
//#region src/commands/doctor.ts
var Doctor = class extends BaseCommand {
command = "doctor";
describe = "diagnose common issues";
builder(args) {
return args;
}
async handler() {
const result = await doctor.scan(this.ctx.root);
if (result.diagnoses.length === 0) consola.success("No issues found");
else result.diagnoses.forEach(doctor.writeToConsole);
}
};
var doctor_default = Doctor;
//#endregion
//#region src/commands/init.ts
var Init = class extends BaseCommand {
command = "init";
describe = "initialize a new project";
builder = (args) => {
return args.option("y", {
type: "boolean",
default: true
});
};
async handler(args) {
const { y } = args;
const command = commands.init.concat(this.ctx.pm, { interactively: !y });
consola.info(`Initializing project with ${this.ctx.pm}`);
await exec(command);
}
};
var init_default = Init;
//#endregion
//#region src/commands/publish.ts
var Publish = class extends BaseCommand {
command = "publish";
describe = "publish package";
builder(args) {
return args;
}
async handler() {
const command = ["npm", "publish"];
await exec(command, { cwd: this.ctx.root });
}
};
var publish_default = Publish;
//#endregion
//#region src/commands/registry.ts
var Registry = class extends BaseCommand {
command = "registry <registry>";
describe = "Manage the npm registry";
builder = (args) => {
return args.positional("registry", {
type: "string",
description: "Registry URL",
demandOption: true
}).option("global", {
alias: ["G", "g"],
type: "boolean",
description: "Install packages globally"
});
};
async handler(args) {
const { global, registry } = args;
if (!URL.canParse(registry)) error(`Invalid registry URL ${registry}`);
const command = commands.config.concat(this.ctx.pm, {
verb: "set",
key: "registry",
value: registry,
global
});
await exec(command, { cwd: this.ctx.root });
}
};
var registry_default = Registry;
//#endregion
//#region src/commands/remove.ts
var Remove = class extends BaseCommand {
command = [
"remove <packages..>",
"rm",
"uninstall",
"un"
];
describe = "remove packages";
builder = (args) => {
return args.positional("packages", {
type: "string",
array: true,
demandOption: true,
description: "Packages to remove"
}).option("save", {
alias: ["S"],
type: "boolean",
description: "Remove from dependencies",
conflicts: [
"save-dev",
"save-peer",
"save-optional",
"global"
]
}).option("save-dev", {
alias: ["D"],
type: "boolean",
description: "Remove from devDependencies",
conflicts: [
"save",
"save-peer",
"save-optional",
"global"
]
}).option("save-peer", {
alias: ["P"],
type: "boolean",
description: "Remove from peerDependencies",
conflicts: [
"save",
"save-dev",
"save-optional",
"global"
]
}).option("save-optional", {
alias: ["O"],
type: "boolean",
description: "Remove from optionalDependencies",
conflicts: [
"save",
"save-dev",
"save-peer",
"global"
]
}).option("global", {
alias: ["G", "g"],
type: "boolean",
description: "Remove packages globally",
conflicts: [
"save",
"save-dev",
"save-peer",
"save-optional"
]
});
};
async handler(args) {
const { packages, saveDev, savePeer, saveOptional, global } = args;
const options = {
packages,
saveDev,
savePeer,
saveOptional,
global
};
const command = commands.remove.concat(this.ctx.pm, options);
consola.info(`Removing packages with ${this.ctx.pm}`);
await exec(command, { cwd: this.ctx.root });
}
};
var remove_default = Remove;
//#endregion
//#region src/commands/scaffold.ts
var Scaffold = class extends BaseCommand {
command = ["scaffold", "sc"];
describe = "Scaffold a new project";
builder(args) {
return args;
}
handler = () => {
exec(commands.create.concat(this.ctx.pm, {
name: "akrc",
args: []
}), { cwd: this.ctx.root });
};
};
var scaffold_default = Scaffold;
//#endregion
//#region src/commands/test.ts
var Test = class extends BaseCommand {
command = ["test", "t"];
describe = "run tests";
builder(args) {
return args;
}
async handler() {
const command = commands.test.concat(this.ctx.pm, { args: this.ctx.args.slice(1) });
consola.info(`Running tests with ${this.ctx.pm}`);
await exec(command, { cwd: this.ctx.root });
}
};
var test_default = Test;
//#endregion
//#region src/commands/ui.ts
var UI = class extends BaseCommand {
command = "ui";
describe = "open the package manager UI";
builder = (args) => {
return args.option("port", {
alias: ["p", "P"],
type: "number",
description: "Port to use"
});
};
async handler(args) {
const port = args.port || await getPort({ port: 13131 });
consola.info(`Starting UI on http://localhost:${port}`);
await start(port, this.ctx.root);
}
};
var ui_default = UI;
//#endregion
//#region src/commands/update.ts
var Update = class extends BaseCommand {
command = ["update [packages..]", "up"];
describe = "update packages";
builder = (args) => {
return args.positional("packages", {
type: "string",
array: true,
description: "Packages to update"
}).option("global", {
alias: ["G", "g"],
type: "boolean",
description: "Update packages globally"
});
};
async handler(args) {
const { packages, global } = args;
const options = {
packages,
global
};
const command = commands.update.concat(this.ctx.pm, options);
await exec(command, { cwd: this.ctx.root });
}
};
var update_default = Update;
//#endregion
//#region src/commands/use.ts
var Use = class extends BaseCommand {
command = "use <pattern>";
describe = "use a different package manager";
builder = (args) => {
return args.positional("pattern", {
type: "string",
description: "Pattern to match package manager",
demandOption: true
});
};
async handler(args) {
const { pattern } = args;
if (pattern === "latest") {
const shell$1 = [
"corepack",
"use",
`${this.ctx.pm}@latest`
];
await exec(shell$1, { cwd: this.ctx.root });
return;
}
const shell = [
"corepack",
"use",
pattern
];
await exec(shell, { cwd: this.ctx.root });
}
};
var use_default = Use;
//#endregion
//#region src/commands/view.ts
var View = class extends BaseCommand {
command = ["view [platform]", "v"];
describe = "View in other platform";
builder = (args) => {
return args.positional("platform", {
choices: ["repo", "npm"],
default: "npm"
});
};
async handler(args) {
const { platform } = args;
const pkgJson = await readPackage({ cwd: this.ctx.root });
switch (platform) {
case "repo": {
if (pkgJson.repository?.type !== "git") error(`The package's repository is hosted on ${pkgJson.repository?.type} which is not supported yet.`);
const url = gitUrlParse(pkgJson.repository.url).toString("https");
open(url);
break;
}
case "npm": {
open(new URL(pkgJson.name, "https://npm.im").href);
break;
}
default: error(`The requested platform ${platform} is not supported`);
}
}
};
var view_default = View;
//#endregion
//#region src/commands/why.ts
var Why = class extends BaseCommand {
command = ["why <query>", "explain"];
describe = "explain why a package is installed";
builder = (args) => {
return args.positional("query", {
type: "string",
description: "Shared to explain",
demandOption: true
});
};
async handler(args) {
const { query } = args;
const command = commands.why.concat(this.ctx.pm, { query });
await exec(command, { cwd: this.ctx.root });
}
};
var why_default = Why;
//#endregion
//#region src/commands/index.ts
function mount(argv, ctx) {
const factory = new CommandFactory(ctx);
return argv.command(factory.create(add_default)).command(factory.create(dlx_default)).command(factory.create(remove_default)).command(factory.create(init_default)).command(factory.create(test_default)).command(factory.create(ci_default)).command(factory.create(doctor_default)).command(factory.create(ui_default)).command(factory.create(default_default)).command(factory.create(use_default)).command(factory.create(update_default)).command(factory.create(publish_default)).command(factory.create(why_default)).command(factory.create(config_default)).command(factory.create(registry_default)).command(factory.create(scaffold_default)).command(factory.create(view_default));
}
//#endregion
export { CommandFactory, dlx_default, getContext, mount, package_default };