@blitzjs/cli
Version:
Blitz.js CLI
399 lines (398 loc) • 16.7 kB
JavaScript
"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"],
}),
};