UNPKG

sober-cli

Version:

Sober learns to set up a simple cli tool

333 lines (297 loc) 10.3 kB
#!/usr/bin/env node // Check node version before requiring/doing anything else // The user may be on a very old node version const chalk = require("chalk"); const semver = require("semver"); const requiredVersion = require("../package.json").engines.node; const didYouMean = require("didyoumean"); // Setting edit distance to 60% of the input string's length didYouMean.threshold = 0.6; function checkNodeVersion(wanted, id) { if (!semver.satisfies(process.version, wanted)) { console.log( chalk.red( "You are using Node " + process.version + ", but this version of " + id + " requires Node " + wanted + ".\nPlease upgrade your Node version." ) ); process.exit(1); } } checkNodeVersion(requiredVersion, "vue-cli"); if (semver.satisfies(process.version, "9.x")) { console.log( chalk.red( `You are using Node ${process.version}.\n` + `Node.js 9.x has already reached end-of-life and will not be supported in future major releases.\n` + `It's strongly recommended to use an active LTS version instead.` ) ); } const fs = require("fs"); const path = require("path"); const slash = require("slash"); const minimist = require("minimist"); // enter debug mode when creating test repo if ( slash(process.cwd()).indexOf("/packages/test") > 0 && (fs.existsSync(path.resolve(process.cwd(), "../@vue")) || fs.existsSync(path.resolve(process.cwd(), "../../@vue"))) ) { process.env.VUE_CLI_DEBUG = true; } const program = require("commander"); const loadCommand = require("../lib/util/loadCommand"); program.version(require("../package").version).usage("<command> [options]"); program .command("create <app-name>") .description("create a new project powered by vue-cli-service") .option( "-p, --preset <presetName>", "Skip prompts and use saved or remote preset" ) .option("-d, --default", "Skip prompts and use default preset") .option( "-i, --inlinePreset <json>", "Skip prompts and use inline JSON string as preset" ) .option( "-m, --packageManager <command>", "Use specified npm client when installing dependencies" ) .option( "-r, --registry <url>", "Use specified npm registry when installing dependencies (only for npm)" ) .option( "-g, --git [message]", "Force git initialization with initial commit message" ) .option("-n, --no-git", "Skip git initialization") .option("-f, --force", "Overwrite target directory if it exists") .option("-c, --clone", "Use git clone when fetching remote preset") .option("-x, --proxy", "Use specified proxy when creating project") .option("-b, --bare", "Scaffold project without beginner instructions") .option("--skipGetStarted", 'Skip displaying "Get started" instructions') .action((name, cmd) => { const options = cleanArgs(cmd); if (minimist(process.argv.slice(3))._.length > 1) { console.log( chalk.yellow( "\n Info: You provided more than one argument. The first one will be used as the app's name, the rest are ignored." ) ); } // --git makes commander to default git to true if (process.argv.includes("-g") || process.argv.includes("--git")) { options.forceGit = true; } require("../lib/create")(name, options); }); program .command("add <plugin> [pluginOptions]") .description( "install a plugin and invoke its generator in an already created project" ) .option( "--registry <url>", "Use specified npm registry when installing dependencies (only for npm)" ) .allowUnknownOption() .action(plugin => { require("../lib/add")(plugin, minimist(process.argv.slice(3))); }); program .command("invoke <plugin> [pluginOptions]") .description("invoke the generator of a plugin in an already created project") .option( "--registry <url>", "Use specified npm registry when installing dependencies (only for npm)" ) .allowUnknownOption() .action(plugin => { require("../lib/invoke")(plugin, minimist(process.argv.slice(3))); }); program .command("inspect [paths...]") .description("inspect the webpack config in a project with vue-cli-service") .option("--mode <mode>") .option("--rule <ruleName>", "inspect a specific module rule") .option("--plugin <pluginName>", "inspect a specific plugin") .option("--rules", "list all module rule names") .option("--plugins", "list all plugin names") .option("-v --verbose", "Show full function definitions in output") .action((paths, cmd) => { require("../lib/inspect")(paths, cleanArgs(cmd)); }); program .command("serve [entry]") .description("serve a .js or .vue file in development mode with zero config") .option("-o, --open", "Open browser") .option("-c, --copy", "Copy local url to clipboard") .action((entry, cmd) => { loadCommand("serve", "@vue/cli-service-global").serve( entry, cleanArgs(cmd) ); }); program .command("build [entry]") .description("build a .js or .vue file in production mode with zero config") .option( "-t, --target <target>", "Build target (app | lib | wc | wc-async, default: app)" ) .option( "-n, --name <name>", "name for lib or web-component mode (default: entry filename)" ) .option("-d, --dest <dir>", "output directory (default: dist)") .action((entry, cmd) => { loadCommand("build", "@vue/cli-service-global").build( entry, cleanArgs(cmd) ); }); program .command("ui") .description("start and open the vue-cli ui") .option( "-H, --host <host>", "Host used for the UI server (default: localhost)" ) .option( "-p, --port <port>", "Port used for the UI server (by default search for available port)" ) .option("-D, --dev", "Run in dev mode") .option("--quiet", `Don't output starting messages`) .option("--headless", `Don't open browser on start and output port`) .action(cmd => { checkNodeVersion(">=8.6", "vue ui"); require("../lib/ui")(cleanArgs(cmd)); }); program .command("init <template> <app-name>") .description( "generate a project from a remote template (legacy API, requires @vue/cli-init)" ) .option("-c, --clone", "Use git clone when fetching remote template") .option("--offline", "Use cached template") .action(() => { loadCommand("init", "@vue/cli-init"); }); program .command("config [value]") .description("inspect and modify the config") .option("-g, --get <path>", "get value from option") .option("-s, --set <path> <value>", "set option value") .option("-d, --delete <path>", "delete option from config") .option("-e, --edit", "open config with default editor") .option("--json", "outputs JSON result only") .action((value, cmd) => { require("../lib/config")(value, cleanArgs(cmd)); }); program .command("upgrade [package-name]") .description("(experimental) upgrade vue cli service / plugins") .option( "-t, --to <version>", "upgrade <package-name> to a version that is not latest" ) .option( "-r, --registry <url>", "Use specified npm registry when installing dependencies" ) .action((packageName, cmd) => { require("../lib/upgrade")(packageName, cleanArgs(cmd)); }); program .command("info") .description("print debugging information about your environment") .action(cmd => { console.log(chalk.bold("\nEnvironment Info:")); require("envinfo") .run( { System: ["OS", "CPU"], Binaries: ["Node", "Yarn", "npm"], Browsers: ["Chrome", "Edge", "Firefox", "Safari"], npmPackages: "/**/{typescript,*vue*,@vue/*/}", npmGlobalPackages: ["@vue/cli"] }, { showNotFound: true, duplicates: true, fullTree: true } ) .then(console.log); }); // output help information on unknown commands program.arguments("<command>").action(cmd => { program.outputHelp(); console.log(` ` + chalk.red(`Unknown command ${chalk.yellow(cmd)}.`)); console.log(); suggestCommands(cmd); }); // add some useful info on help program.on("--help", () => { console.log(); console.log( ` Run ${chalk.cyan( `vue <command> --help` )} for detailed usage of given command.` ); console.log(); }); program.commands.forEach(c => c.on("--help", () => console.log())); // enhance common error messages const enhanceErrorMessages = require("../lib/util/enhanceErrorMessages"); enhanceErrorMessages("missingArgument", argName => { return `Missing required argument ${chalk.yellow(`<${argName}>`)}.`; }); enhanceErrorMessages("unknownOption", optionName => { return `Unknown option ${chalk.yellow(optionName)}.`; }); enhanceErrorMessages("optionMissingArgument", (option, flag) => { return ( `Missing required argument for option ${chalk.yellow(option.flags)}` + (flag ? `, got ${chalk.yellow(flag)}` : ``) ); }); program.parse(process.argv); if (!process.argv.slice(2).length) { program.outputHelp(); } function suggestCommands(cmd) { const availableCommands = program.commands.map(cmd => { return cmd._name; }); const suggestion = didYouMean(cmd, availableCommands); if (suggestion) { console.log(` ` + chalk.red(`Did you mean ${chalk.yellow(suggestion)}?`)); } } function camelize(str) { return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : "")); } // commander passes the Command object itself as options, // extract only actual options into a fresh object. function cleanArgs(cmd) { const args = {}; cmd.options.forEach(o => { const key = camelize(o.long.replace(/^--/, "")); // if an option is not present and Command has a method with the same name // it should not be copied if (typeof cmd[key] !== "function" && typeof cmd[key] !== "undefined") { args[key] = cmd[key]; } }); return args; }