UNPKG

@blitzjs/cli

Version:
399 lines (398 loc) 16.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.New = void 0; const tslib_1 = require("tslib"); const env_1 = require("@blitzjs/env"); const generator_1 = require("@blitzjs/generator"); const command_1 = require("@oclif/command"); const chalk_1 = (0, tslib_1.__importDefault)(require("chalk")); const cross_spawn_1 = (0, tslib_1.__importDefault)(require("cross-spawn")); const hasbin_1 = (0, tslib_1.__importDefault)(require("hasbin")); const logging_1 = require("next/dist/server/lib/logging"); const semver_1 = require("semver"); const debug = require("debug")("blitz:new"); const command_2 = require("../command"); const prompt_aborted_1 = require("../errors/prompt-aborted"); const prisma_1 = require("./prisma"); const IS_YARN_INSTALLED = hasbin_1.default.sync("yarn"); const IS_PNPM_INSTALLED = hasbin_1.default.sync("pnpm"); const PREFERABLE_PKG_MANAGER = IS_PNPM_INSTALLED ? "pnpm" : IS_YARN_INSTALLED ? "yarn" : "npm"; const LANGUAGES = ["TypeScript", "JavaScript"]; const DEFAULT_LANG = "TypeScript"; const templates = { full: { path: "app", }, minimal: { path: "minimalapp", skipForms: true, skipDatabase: true, }, }; class New extends command_2.Command { constructor() { super(...arguments); this.pkgManager = PREFERABLE_PKG_MANAGER; this.shouldInstallDeps = true; this.useTs = true; this.template = templates.full; } run() { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { const { args, flags } = this.parse(New); debug("args: ", args); debug("flags: ", flags); const shouldUpgrade = !flags["skip-upgrade"]; if (shouldUpgrade) { const wasUpgraded = yield this.maybeUpgradeGloballyInstalledBlitz(); if (wasUpgraded) { this.rerunButSkipUpgrade(flags, args); return; } } yield this.determineLanguage(flags); yield this.determineTemplate(flags); yield this.determinePkgManagerToInstallDeps(flags); const { pkgManager, shouldInstallDeps, template } = this; try { const destinationRoot = require("path").resolve(args.name); const appName = require("path").basename(destinationRoot); let form; if (!template.skipForms) { form = yield this.determineFormLib(flags); } const { "dry-run": dryRun, "no-git": skipGit } = flags; const requiresManualInstall = dryRun || !shouldInstallDeps; const postInstallSteps = args.name === "." ? [] : [`cd ${args.name}`]; const AppGenerator = require("@blitzjs/generator").AppGenerator; const generatorOpts = { template, destinationRoot, appName, dryRun, useTs: this.useTs, yarn: pkgManager === "yarn", pnpm: pkgManager === "pnpm", form, version: this.config.version, skipInstall: !shouldInstallDeps, skipGit, onPostInstall: () => (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { if (template.skipDatabase) { return; } const spinner = logging_1.log.spinner(logging_1.log.withBrand("Initializing SQLite database")).start(); try { // loadEnvConfig is required in order for DATABASE_URL to be available // don't print info logs from loadEnvConfig for clear output (0, env_1.loadEnvConfig)(process.cwd(), undefined, { error: console.error, info: () => { } }, { ignoreCache: true }); const result = yield (0, prisma_1.runPrisma)(["migrate", "dev", "--name", "Initial migration"], true); if (!result.success) throw new Error(); spinner.succeed(); } catch (error) { spinner.fail(); postInstallSteps.push("blitz prisma migrate dev (when asked, you can name the migration anything)"); } }), }; const generator = new AppGenerator(generatorOpts); this.log("\n" + logging_1.log.withBrand("Hang tight while we set up your new Blitz app!") + "\n"); yield generator.run(); if (requiresManualInstall) { postInstallSteps.push(this.installDepsCmd); postInstallSteps.push("blitz prisma migrate dev (when asked, you can name the migration anything)"); } postInstallSteps.push("blitz dev"); this.log("\n" + logging_1.log.withBrand("Your new Blitz app is ready! Next steps:") + "\n"); postInstallSteps.forEach((step, index) => { this.log(chalk_1.default.yellow(` ${index + 1}. ${step}`)); }); this.log(""); // new line } catch (err) { if (err instanceof prompt_aborted_1.PromptAbortedError) this.exit(0); this.error(err); } }); } // TODO:: there should be some problems with dry run determinePkgManagerToInstallDeps(flags) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { if (flags["skip-install"]) { this.shouldInstallDeps = false; return; } const isPkgManagerSpecifiedAsFlag = flags.npm || flags.pnpm || flags.yarn; if (isPkgManagerSpecifiedAsFlag) { if (flags.npm) { this.pkgManager = "npm"; } else if (flags.pnpm) { if (IS_PNPM_INSTALLED) { this.pkgManager = "pnpm"; } else { this.warn(`Pnpm is not installed. Fallback to ${this.pkgManager}`); } } else if (flags.yarn) { if (IS_YARN_INSTALLED) { this.pkgManager = "yarn"; } else { this.warn(`Yarn is not installed. Fallback to ${this.pkgManager}`); } } } else { const hasPkgManagerChoice = IS_YARN_INSTALLED || IS_PNPM_INSTALLED; if (hasPkgManagerChoice) { const { pkgManager } = (yield this.enquirer.prompt({ type: "select", name: "pkgManager", message: "Install dependencies?", initial: PREFERABLE_PKG_MANAGER, choices: [ { name: "npm", message: "via npm", }, IS_YARN_INSTALLED && { name: "yarn", message: "via yarn", }, IS_PNPM_INSTALLED && { name: "pnpm", message: "via pnpm", }, "skip", ].filter(Boolean), })); if (pkgManager === "skip") { this.shouldInstallDeps = false; } else { this.pkgManager = pkgManager; } } else { const { installDeps } = (yield this.enquirer.prompt({ type: "confirm", name: "installDeps", message: "Install dependencies?", initial: true, })); this.shouldInstallDeps = installDeps; } } }); } determineLanguage(flags) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { if (flags.language) { this.useTs = flags.language === "typescript"; } else { const { language } = (yield this.enquirer.prompt({ type: "select", name: "language", message: "Pick a new project's language", initial: LANGUAGES.indexOf(DEFAULT_LANG), choices: LANGUAGES, })); this.useTs = language === "TypeScript"; } }); } determineFormLib(flags) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { if (flags.form) { switch (flags.form) { case "react-final-form": return "React Final Form"; case "react-hook-form": return "React Hook Form"; case "formik": return "Formik"; } } const formChoices = [ { name: "React Final Form", message: "React Final Form (recommended)" }, { name: "React Hook Form" }, { name: "Formik" }, ]; const promptResult = (yield this.enquirer.prompt({ type: "select", name: "form", message: "Pick a form library (you can switch to something else later if you want)", choices: formChoices, })); return promptResult.form; }); } determineTemplate(flags) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { if (flags.template) { this.template = templates[flags.template]; return; } const choices = [ { name: "full", message: "Full - includes DB and auth (Recommended)" }, { name: "minimal", message: "Minimal — no DB, no auth" }, ]; const { template } = (yield this.enquirer.prompt({ type: "select", name: "template", message: "Pick your new app template", initial: 0, choices, })); this.template = templates[template]; }); } rerunButSkipUpgrade(flags, args) { const flagsArr = Object.keys(flags).reduce((arr, key) => (flags[key] ? [...arr, `--${key}`] : arr), []); cross_spawn_1.default.sync("blitz", ["new", ...Object.values(args), ...flagsArr, "--skip-upgrade"], { stdio: "inherit", }); } maybeUpgradeGloballyInstalledBlitz() { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { const spinner = logging_1.log .spinner(logging_1.log.withBrand("Checking if a new Blitz release is available")) .start(); const latestVersion = (yield (0, generator_1.getLatestVersion)("blitz")).value || this.config.version; if ((0, semver_1.lt)(this.config.version, latestVersion)) { spinner.succeed(logging_1.log.withBrand("A new Blitz release is available")); if (yield this.promptBlitzUpgrade(latestVersion)) { let globalBlitzOwner = this.getGlobalBlitzPkgManagerOwner(); const upgradeOpts = globalBlitzOwner === "yarn" ? ["global", "add", "blitz"] : ["i", "-g", "blitz@latest"]; cross_spawn_1.default.sync(globalBlitzOwner, upgradeOpts, { stdio: "inherit" }); const versionResult = cross_spawn_1.default.sync("blitz", ["--version"], { stdio: "pipe" }); if (versionResult.stdout) { const newVersion = versionResult.stdout.toString().match(/(?<=blitz: )(.*)(?= \(global\))/) || []; if (newVersion[0] && newVersion[0] === latestVersion) { logging_1.log.success(`Upgraded blitz global package to ${newVersion[0]}, running blitz new command...`); return true; } } this.error("Unable to upgrade blitz, please run `blitz new` again and select No to skip the upgrade"); } } else { spinner.succeed(logging_1.log.withBrand("You have the latest Blitz version")); } return false; }); } getGlobalBlitzPkgManagerOwner() { if (IS_PNPM_INSTALLED) { const output = cross_spawn_1.default.sync("pnpm", ["list", "-g", "--depth", "0"], { stdio: "pipe" }); if (output && output.stdout.toString().includes("blitz ")) { return "pnpm"; } } if (IS_YARN_INSTALLED) { const output = cross_spawn_1.default.sync("yarn", ["global", "list"], { stdio: "pipe" }); if (output && output.stdout.toString().includes("blitz@")) { return "yarn"; } } return "npm"; } promptBlitzUpgrade(latestVersion) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function* () { const upgradeChoices = [ { name: "yes", message: `Yes - Upgrade to ${latestVersion}` }, { name: "no", message: `No - Continue with old version (${this.config.version}) - NOT recommended`, }, ]; const promptResult = (yield this.enquirer.prompt({ type: "select", name: "upgrade", message: "Your global blitz version is outdated. Upgrade?", choices: upgradeChoices, })); return promptResult.upgrade === "yes"; }); } get installDepsCmd() { switch (this.pkgManager) { case "yarn": return "yarn"; case "npm": return "npm install"; case "pnpm": return "pnpm install"; default: return "npm install"; } } } exports.New = New; New.description = "Create a new Blitz project"; New.args = [ { name: "name", required: true, description: "name of your new project", }, ]; New.flags = { help: command_1.flags.help({ char: "h" }), language: command_1.flags.string({ description: "Pick your new app language. Options: typescript, javascript.", options: ["typescript", "javascript"], }), npm: command_1.flags.boolean({ description: "Use npm as the package manager", allowNo: true, }), yarn: command_1.flags.boolean({ description: "Use yarn as the package manager", default: false, hidden: !IS_YARN_INSTALLED, allowNo: true, }), pnpm: command_1.flags.boolean({ description: "Use pnpm as the package manager", default: false, hidden: !IS_PNPM_INSTALLED, allowNo: true, }), form: command_1.flags.string({ description: "A form library", options: ["react-final-form", "react-hook-form", "formik"], }), "skip-install": command_1.flags.boolean({ description: "Skip package installation", hidden: true, default: false, allowNo: true, }), "dry-run": command_1.flags.boolean({ char: "d", description: "Show what files will be created without writing them to disk", }), "no-git": command_1.flags.boolean({ description: "Skip git repository creation", default: false, }), "skip-upgrade": command_1.flags.boolean({ description: "Skip blitz upgrade if outdated", default: false, }), template: command_1.flags.string({ description: "Pick your new app template. Options: full, minimal.", options: ["full", "minimal"], }), };