UNPKG

create-manu-app

Version:

Create a new Next.js project with Tailwind CSS and TypeScript pre-configured. Simplify your project setup process with this scaffolding tool and start building your web application with ease.

369 lines (360 loc) 16.7 kB
#!/usr/bin/env node var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // node_modules/.pnpm/tsup@8.1.0_typescript@5.5.3/node_modules/tsup/assets/cjs_shims.js var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href; var importMetaUrl = /* @__PURE__ */ getImportMetaUrl(); // src/index.ts var import_prompts = require("@clack/prompts"); var import_picocolors = __toESM(require("picocolors")); var import_yargs = __toESM(require("yargs")); var import_helpers = require("yargs/helpers"); // src/commands/create.ts var import_promises3 = require("fs/promises"); var import_path2 = __toESM(require("path")); // src/helpers/installation.ts var import_promises2 = require("fs/promises"); var import_path = __toESM(require("path")); // src/helpers/utils.ts var import_glob = require("glob"); var import_node_child_process = require("child_process"); var import_promises = require("fs/promises"); var import_node_path = __toESM(require("path")); var import_node_url = require("url"); var import_node_util = require("util"); var execAsync = (0, import_node_util.promisify)(import_node_child_process.exec); var getSourcePath = (...folderOrFiles) => import_node_path.default.join(import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl)), ...folderOrFiles); async function replaceFiles(projectName, projectType, template) { const origen = getSourcePath(); const TEMPLATES = `${origen}/templates`; const templatePath = `${TEMPLATES}/${projectType}/${template}`; const destinationPath = import_node_path.default.join(process.cwd(), projectName); try { await (0, import_promises.cp)(`${templatePath}`, destinationPath, { recursive: true }); await (0, import_promises.cp)(`${origen}/always/.vscode`, `${destinationPath}/.vscode`, { recursive: true }); const files = await (0, import_glob.glob)(`**/*`, { nodir: true, cwd: destinationPath, absolute: true, ignore: ["**/node_modules/**"] }); for await (const file of files) { const data = await (0, import_promises.readFile)(file, "utf8"); const draft = data.replace(/{{\s*projectName\s*}}/g, projectName); await (0, import_promises.writeFile)(file, draft, "utf8"); } } catch (err) { console.error(err); } } async function removeFiles(projectName, template) { const projectDirectory = import_node_path.default.join(process.cwd(), projectName); if (template.includes("next")) { try { await (0, import_promises.rm)(`${projectDirectory}/public/file.svg`); await (0, import_promises.rm)(`${projectDirectory}/public/globe.svg`); await (0, import_promises.rm)(`${projectDirectory}/public/next.svg`); await (0, import_promises.rm)(`${projectDirectory}/public/vercel.svg`); await (0, import_promises.rm)(`${projectDirectory}/public/window.svg`); } catch (err) { console.error(err); } } if (template.includes("vite")) { try { await (0, import_promises.rm)(import_node_path.default.join(projectDirectory, ".eslintrc.cjs"), { force: true }); await (0, import_promises.rm)(import_node_path.default.join(projectDirectory, "src", "App.css"), { force: true }); await (0, import_promises.rm)(import_node_path.default.join(projectDirectory, "src", "index.css"), { force: true }); await (0, import_promises.rm)(import_node_path.default.join(projectDirectory, "src", "assets"), { recursive: true, force: true }); await (0, import_promises.rm)(import_node_path.default.join(projectDirectory, "src", "vite.config.js"), { force: true }); } catch (err) { console.error(err); } } } function getPkgManager(args) { if (args["use-pnpm"]) return "pnpm"; if (args["use-yarn"]) return "yarn"; if (args["use-npm"]) return "npm"; const userAgent = process.env.npm_config_user_agent; if (userAgent?.startsWith("yarn")) { return "yarn"; } else if (userAgent?.startsWith("pnpm")) { return "pnpm"; } return "npm"; } var checkIfYarnIsInstalled = async () => { try { await execAsync("yarn --version"); return true; } catch (error) { return false; } }; // src/helpers/installation.ts var installEslintDevDeps = async (pkgManager, projectName, template) => { const cmd = pkgManager.startsWith("n") ? "npm i -D" : `${pkgManager} add -D`; const deps = [ "@typescript-eslint/eslint-plugin", "prettier", "eslint-plugin-import", "eslint-plugin-prettier", "eslint-config-prettier" ]; if (template.includes("vite")) { deps.push("eslint-plugin-react"); } await execAsync(`${cmd} ${deps.join(" ")}`, { cwd: import_path.default.join(process.cwd(), projectName) }); }; var installTailwindAndInit = async (pkgManager, projectName) => { const projectDirectory = import_path.default.join(process.cwd(), projectName); const installCmd = pkgManager.startsWith("n") ? "npm i -D" : `${pkgManager} add -D`; const initCmd = pkgManager.startsWith("n") ? "npx" : `${pkgManager}`; await execAsync(`${installCmd} tailwindcss postcss autoprefixer`, { cwd: projectDirectory }); await execAsync(`${initCmd} tailwindcss init -p`, { cwd: projectDirectory }); }; async function installRainbowKit(projectName, pkgManager) { const projectDirectory = import_path.default.join(process.cwd(), projectName); const cmd = pkgManager === "npm" ? "npm i" : `${pkgManager} add`; await execAsync(`${cmd} @rainbow-me/rainbowkit wagmi ethers@^5`, { cwd: projectDirectory }); } async function installShadcn(projectName, template, pkgManager) { const projectDirectory = import_path.default.join(process.cwd(), projectName); const cmd = pkgManager === "pnpm" ? "pnpm dlx" : "npx"; const installCmd = pkgManager.startsWith("n") ? "npm i" : `${pkgManager} add`; if (template.includes("next")) { await execAsync(`${cmd} shadcn@latest init --yes --defaults --base-color neutral --src-dir`, { cwd: projectDirectory }); } else { await execAsync( `${installCmd} class-variance-authority tailwindcss-animate class-variance-authority clsx tailwind-merge lucide-react`, { cwd: projectDirectory } ); const filesForVite = getSourcePath("always", "shadcn", template); await (0, import_promises2.cp)(`${filesForVite}/tailwind.config.ts`, `${projectDirectory}/tailwind.config.js`, { force: true }); await (0, import_promises2.cp)(`${filesForVite}/globals.css`, `${projectDirectory}/src/globals.css`); await (0, import_promises2.cp)(`${filesForVite}/lib`, `${projectDirectory}/src/lib`, { recursive: true }); } await execAsync(`${cmd} shadcn@latest add --all`, { cwd: projectDirectory }); } // src/commands/create.ts async function createViteReactApp(projectName, pkgManager) { const createCmd = pkgManager.startsWith("n") ? "npx create-vite@latest" : `${pkgManager} create vite`; const cmd = pkgManager.startsWith("n") ? "npm" : `${pkgManager}`; await execAsync(`${createCmd} ${projectName} --template react-swc-ts`, { cwd: process.cwd() }); await execAsync(`${cmd} i`, { cwd: import_path2.default.join(process.cwd(), projectName) }); await execAsync(`${cmd} add -D @types/node`, { cwd: import_path2.default.join(process.cwd(), projectName) }); await installEslintDevDeps(pkgManager, projectName, "vite"); } async function createNextApp(projectName, pkgManager) { const cmd = pkgManager === "npm" ? "npx create-next-app@latest" : `${pkgManager} create next-app`; await execAsync( `${cmd} ${projectName} --ts --eslint --tailwind --src-dir --app --turbopack --import-alias "@/*"` ); await installEslintDevDeps(pkgManager, projectName, "next"); } async function createSE2(projectName) { const projectDirectory = import_path2.default.join(process.cwd(), projectName); await execAsync(`git clone https://github.com/scaffold-eth/scaffold-eth-2.git ${projectName}`, { cwd: projectDirectory }); const origen = getSourcePath("templates", "web3", "se2-w"); await (0, import_promises3.cp)(`${origen}/package.json`, `${projectDirectory}/package.json`); await (0, import_promises3.cp)(`${origen}/start-dev.ps1`, `${projectDirectory}/start-dev.ps1`); const packageJsonDir = import_path2.default.join(projectDirectory, "package.json"); const data = await (0, import_promises3.readFile)(packageJsonDir, "utf8"); const draft = data.replace(/{{\s*projectName\s*}}/g, projectName); await (0, import_promises3.writeFile)(packageJsonDir, draft, "utf8"); await execAsync("yarn install", { cwd: projectDirectory }); } // src/helpers/gitUtils.ts var import_path3 = __toESM(require("path")); async function makeCommit(projectName) { const projectDirectory = import_path3.default.join(process.cwd(), projectName); await execAsync('git commit --amend -m "Initial commit from Create Manu App"', { cwd: projectDirectory }); await execAsync('git add ."', { cwd: projectDirectory }); await execAsync('git commit -m "Initial commit from Create Manu App"', { cwd: projectDirectory }); } // src/index.ts var TEMPLATES_WEB2 = [ { label: "Next.js + ESLint + TypeScript + Shadcn/ui", value: "next-shadcn" }, { label: "Next.js + ESLint + TypeScript + Tailwind", value: "next-tw" }, { label: "React (vite) + ESLint + TypeScript + Shadcn/ui", value: "react-vite-shadcn" } ]; var TEMPLATES_WEB3 = [ { label: "Next.js + ESLint + TypeScript + Shadcn/ui + RainbowKit", value: "next-rainbowkit" }, { label: "Scaffold-Eth-2 Windows: Updated toolkit for building Ethereum dapps with Next.js.", value: "se2-w" } ]; var s = (0, import_prompts.spinner)(); async function main() { const argv = await (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).argv; (0, import_prompts.intro)(import_picocolors.default.bold(import_picocolors.default.white(" Assistant to create a project as Manu would "))); const promptsGroup = await (0, import_prompts.group)( { projectName: () => (0, import_prompts.text)({ message: import_picocolors.default.cyan("TTest What is your project named?"), placeholder: "my-app", validate(value) { if (value.length === 0) return `Name is required!`; if (value.match(/[^a-zA-Z0-9-_]+/g)) return "Project name can only contain letters, numbers, dashes and underscores"; } }), projectType: () => (0, import_prompts.select)({ message: import_picocolors.default.cyan("Select a project type."), initialValue: "web2", options: [ { value: "web2", label: "\u{1F4BB} Web 2" }, { value: "web3", label: "\u{1F310} Web 3" } ] }), template: ({ results: { projectType: projectType2 } }) => (0, import_prompts.select)({ message: import_picocolors.default.cyan("Select a template."), initialValue: projectType2 === "web2" ? TEMPLATES_WEB2[0].value : TEMPLATES_WEB3[0].value, options: projectType2 === "web2" ? TEMPLATES_WEB2 : TEMPLATES_WEB3 }) }, { onCancel: () => { (0, import_prompts.cancel)("Cancelled."); process.exit(0); } } ); const { projectName, projectType, template } = promptsGroup; let pkgManager = getPkgManager(argv); if (template === "se2-w") { if (pkgManager !== "yarn") { const shouldChangePkgManager = await (0, import_prompts.confirm)({ message: import_picocolors.default.bold(import_picocolors.default.italic(import_picocolors.default.yellow("Scaffold-Eth-2 only supports yarn"))) }); if (!shouldChangePkgManager) { (0, import_prompts.outro)(import_picocolors.default.bold(import_picocolors.default.italic(import_picocolors.default.red("Cancelled")))); process.exit(0); } const yarnIsInstalled = await checkIfYarnIsInstalled(); if (!yarnIsInstalled) { (0, import_prompts.outro)(import_picocolors.default.bold(import_picocolors.default.italic(import_picocolors.default.red("Yarn is not installed")))); process.exit(1); } pkgManager = "yarn"; } s.start(import_picocolors.default.yellow("\u{1F680} Creating Scaffold-Eth-2 project")); await createSE2(projectName); s.stop(import_picocolors.default.green("\u{1F680} Successfully Scaffold-Eth-2 project created!")); } if (template.includes("next")) { s.start(import_picocolors.default.yellow("\u{1F680} Creating Next project")); await createNextApp(projectName, pkgManager); s.stop(import_picocolors.default.green("\u{1F680} Successfully Next project created!")); if (template === "next-rainbowkit") { s.start(import_picocolors.default.yellow("\u{1F308} Installing RainbowKit")); await installRainbowKit(projectName, pkgManager); s.stop(import_picocolors.default.green("\u{1F308} RainbowKit successfully installed!")); } if (template === "next-shadcn") { s.start(import_picocolors.default.yellow("\u{1F9E9} Installing Shadcn")); await installShadcn(projectName, template, pkgManager); s.stop(import_picocolors.default.green("\u{1F9E9} Shadcn successfully installed!")); } s.start(import_picocolors.default.yellow("\u{1F4C1} Configuring files")); await replaceFiles(projectName, projectType, template); s.stop(import_picocolors.default.green("\u{1F4C1} Files configured successfully!")); s.start(import_picocolors.default.yellow("\u{1F5D1}\uFE0F Removing unnecessary files")); await removeFiles(projectName, template); s.stop(import_picocolors.default.green("\u{1F5D1}\uFE0F Unnecessary files deleted correctly!")); await makeCommit(projectName); } if (template.includes("vite")) { s.start(import_picocolors.default.yellow("\u{1F680} Creating Vite React project")); await createViteReactApp(projectName, pkgManager); s.stop(import_picocolors.default.green("\u{1F680} Successfully Vite React project created!")); s.start(import_picocolors.default.yellow("\u{1F3A8} Installing Tailwind")); await installTailwindAndInit(pkgManager, projectName); s.stop(import_picocolors.default.green("\u{1F3A8} Tailwind successfully installed!")); s.start(import_picocolors.default.yellow("\u{1F4C1} Configuring files")); await replaceFiles(projectName, projectType, template); s.stop(import_picocolors.default.green("\u{1F4C1} Files configured successfully!")); s.start(import_picocolors.default.yellow("\u{1F9E9} Installing Shadcn")); await installShadcn(projectName, template, pkgManager); s.stop(import_picocolors.default.green("\u{1F9E9} Shadcn successfully installed!")); s.start(import_picocolors.default.yellow("\u{1F5D1}\uFE0F Removing unnecessary files")); await removeFiles(projectName, template); s.stop(import_picocolors.default.green("\u{1F5D1}\uFE0F Unnecessary files deleted correctly!")); } const command = `${pkgManager} ${pkgManager === "npm" ? "run dev" : "dev"}`; const message = ` \u{1F449} To get started, run ${import_picocolors.default.italic( `cd ${projectName}` )} and then ${import_picocolors.default.italic(command)}`; console.log(import_picocolors.default.white(message)); (0, import_prompts.outro)(import_picocolors.default.bold(import_picocolors.default.italic(import_picocolors.default.white(" Let's do great things! \u{1F300} ")))); process.exit(0); } main().catch(console.error); //# sourceMappingURL=index.js.map