UNPKG

reablocks-cli

Version:

A CLI for configuring Reablocks in your project.

706 lines (643 loc) 21.2 kB
#!/usr/bin/env node // src/index.ts import path4 from "path"; import fs5 from "fs-extra"; import { Command } from "commander"; // src/utils/get-vite-config.ts import path from "path"; import fs from "fs-extra"; async function getViteConfig() { const viteConfigPath = path.resolve("vite.config.ts"); if (!fs.existsSync(viteConfigPath)) { console.log("\u26A0\uFE0F vite.config.ts not found, skipping Vite configuration update."); return; } let viteConfig = await fs.readFile(viteConfigPath, "utf8"); const tailwindImportRegex = /import tailwindcss from ['"]@tailwindcss\/vite['"]/; if (!tailwindImportRegex.test(viteConfig)) { viteConfig = `import tailwindcss from "@tailwindcss/vite"; ${viteConfig}`; } else { console.log("\u26A0\uFE0F TailwindCSS is already imported, skipping import."); } const pluginsRegex = /(plugins:\s*\[)(\n?)([ \t]*)/s; const tailwindPluginRegex = /tailwindcss\(\)/; if (pluginsRegex.test(viteConfig)) { viteConfig = viteConfig.replace(pluginsRegex, (_, start, newline, indent) => { const actualIndent = indent || " "; if (tailwindPluginRegex.test(viteConfig)) { console.log("\u26A0\uFE0F TailwindCSS plugin is already in the plugins array, skipping addition."); return _; } return `${start}${newline}${actualIndent}tailwindcss(),${newline}${actualIndent}`; }); } else { console.log("\u26A0\uFE0F 'plugins' array not found in vite.config.ts, skipping TailwindCSS plugin addition."); } await fs.writeFile(viteConfigPath, viteConfig, "utf8"); console.log("\u2705 Vite configuration updated with Tailwind CSS."); } // src/utils/logger.ts import chalk from "chalk"; var logger = { error(...args) { console.log(chalk.red(...args)); }, warn(...args) { console.log(chalk.yellow(...args)); }, info(...args) { console.log(chalk.cyan(...args)); }, success(...args) { console.log(chalk.green(...args)); } }; // src/index.ts import prompts from "prompts"; import ora from "ora"; import { execa } from "execa"; // src/utils/get-framework-config-type.ts import fs2 from "fs-extra"; function getFrameworkConfigType(framework) { if (framework === "next") { const filenames = fs2.readdirSync("."); for (const filename of filenames) { if (filename.includes("src")) { return "NEXT_SRC_DIR"; } if (filename.includes("app")) { return "NEXT_APP_DIR"; } } return "NEXT_PAGES_DIR"; } if (framework === "vite") { return "VITE"; } if (framework === "redwood") { return "REDWOOD"; } if (framework === "cra") { return "CRA"; } if (framework === "refine") { return "CRA"; } if (framework === "other") { return "CRA"; } return "NEXT_APP_DIR"; } // src/utils/get-package-manager.ts function getPackageManager() { const userAgent = process.env.npm_config_user_agent; if (!userAgent) { return "npm"; } if (userAgent.startsWith("yarn")) { return "yarn"; } if (userAgent.startsWith("pnpm")) { return "pnpm"; } return "npm"; } // src/utils/update-index-css.ts import path2 from "path"; import fs3 from "fs-extra"; // src/utils/get-tailwind-css.ts function getTailwindCss() { return ` @import 'tailwindcss'; @source '../node_modules/reablocks'; /* Color palette */ :root, :host { --reablocks-theme: dark; /* Primary colors */ --primary: var(--color-blue-500); --primary-active: var(--color-blue-500); --primary-hover: var(--color-blue-400); --primary-inactive: var(--color-blue-200); /* Secondary colors */ --secondary: var(--color-charade); --secondary-active: var(--color-charade); --secondary-hover: var(--color-gray-700); --secondary-inactive: var(--color-gray-600); /* Success colors */ --success: var(--color-green-500); --success-active: var(--color-green-500); --success-hover: var(--color-green-400); --success-background: var(--color-green-950); /* Error colors */ --error: var(--color-red-500); --error-active: var(--color-red-500); --error-hover: var(--color-red-400); --error-background: var(--color-red-950); /* Warning colors */ --warning: var(--color-orange-500); --warning-active: var(--color-orange-500); --warning-hover: var(--color-orange-400); --warning-background: var(--color-orange-950); /* Info colors */ --info: var(--color-blue-500); --info-active: var(--color-blue-500); --info-hover: var(--color-blue-400); --info-background: var(--color-blue-950); /* Panel colors */ --panel: var(--color-black-pearl); --panel-accent: var(--color-charade); /* Surface colors */ --surface: var(--color-charade); --surface-accent: var(--color-blue-500); /* Text colors */ --text-primary: var(--color-athens-gray); --text-secondary: var(--color-waterloo); /* Custom Backgrounds */ --bottom-border-glow: radial-gradient( circle at center, var(--color-anakiwa) 0, blue, transparent 100% ); --button-gradient: linear-gradient( 283deg, #0808a5 0%, rgba(8, 8, 165, 0) 100% ); --button-gradient-hover: linear-gradient( 283deg, #44f 0%, rgba(23, 23, 255, 0.1) 100% ); --button-gradient-focus: linear-gradient( 283deg, #0d0dd2 0%, rgba(23, 23, 255, 0.1) 100% ); .theme-light, &.theme-light, [data-theme='light'], &[data-theme='light'] { --reablocks-theme: light; /* Primary colors */ --primary: var(--color-blue-500); --primary-active: var(--color-blue-500); --primary-hover: var(--color-blue-600); --primary-inactive: var(--color-gray-500); /* Secondary colors */ --secondary: var(--color-blue-200); --secondary-active: var(--color-blue-200); --secondary-hover: var(--color-blue-300); --secondary-inactive: var(--color-waterloo); /* Success colors */ --success: var(--color-green-500); --success-active: var(--color-green-500); --success-hover: var(--color-green-600); --success-background: var(--color-green-100); /* Error colors */ --error: var(--color-red-500); --error-active: var(--color-red-400); --error-hover: var(--color-red-600); --error-background: var(--color-red-100); /* Warning colors */ --warning: var(--color-orange-500); --warning-active: var(--color-orange-500); --warning-hover: var(--color-orange-600); --warning-background: var(--color-orange-100); /* Info colors */ --info: var(--color-blue-500); --info-active: var(--color-blue-500); --info-hover: var(--color-blue-600); --info-background: var(--color-blue-100); /* Panel colors */ --panel: var(--color-white); --panel-accent: var(--color-mystic); /* Surface colors */ --surface: var(--color-gray-300); --surface-accent: var(--color-blue-500); /* Text colors */ --text-primary: var(--color-vulcan); --text-secondary: var(--color-gray-700); } } /* Custom variants */ @custom-variant dark (&:where(.theme-dark, .theme-dark *, [data-theme=dark], [data-theme=dark] *)); @custom-variant light (&:where(.theme-light, .theme-light *, [data-theme=light], [data-theme=light] *)); @custom-variant disabled-within (&:has(input:is(:disabled), textarea:is(:disabled), button:is(:disabled))); /* Define theme tokens */ @theme inline { /* Fonts */ --font-sans: Inter, sans-serif; --font-mono: Fira Code, monospace; /* Font sizes */ --text-xs: 0.625rem; /* 10px */ --text-xs--line-height: 1rem; --text-sm: 0.75rem; /* 12px */ --text-sm--line-height: 1rem; --text-base: 0.875rem; /* 14px */ --text-base--line-height: 1.25rem; --text-lg: 1rem; /* 16px */ --text-lg--line-height: 1.5rem; /* Primary colors */ --color-primary: var(--primary); --color-primary-active: var(--primary-active); --color-primary-hover: var(--primary-hover); --color-primary-inactive: var(--primary-inactive); /* Secondary colors */ --color-secondary: var(--secondary); --color-secondary-active: var(--secondary-active); --color-secondary-hover: var(--secondary-hover); --color-secondary-inactive: var(--secondary-inactive); /* Success colors */ --color-success: var(--success); --color-success-active: var(--success-active); --color-success-hover: var(--success-hover); --color-success-background: var(--success-background); /* Error colors */ --color-error: var(--error); --color-error-active: var(--error-active); --color-error-hover: var(--error-hover); --color-error-background: var(--error-background); /* Warning colors */ --color-warning: var(--warning); --color-warning-active: var(--warning-active); --color-warning-hover: var(--warning-hover); --color-warning-background: var(--warning-background); /* Info colors */ --color-info: var(--info); --color-info-active: var(--info-active); --color-info-hover: var(--info-hover); --color-info-background: var(--info-background); /* Panel colors */ --color-panel: var(--panel); --color-panel-accent: var(--panel-accent); /* Surface colors */ --color-surface: var(--surface); --color-surface-accent: var(--surface-accent); /* Text colors */ --color-text-primary: var(--text-primary); --color-text-secondary: var(--text-secondary); /* Base colors */ --color-white: #ffffff; --color-black: #000000; /* Gray scale */ --color-gray-*: initial; --color-gray-100: #f7f7fa; --color-gray-200: #e6e6f0; --color-gray-300: #c9c9d6; --color-gray-400: #77778c; --color-gray-500: #5c5c73; --color-gray-600: #3d3d4d; --color-gray-700: #242433; --color-gray-800: #1e1e2e; --color-gray-900: #11111f; --color-gray-950: #02020f; /* Magenta */ --color-magenta-*: initial; --color-magenta-100: #fae5f6; --color-magenta-200: #f1bfe9; --color-magenta-300: #e480d3; --color-magenta-400: #d740be; --color-magenta-500: #c900a8; --color-magenta-600: #ab018f; --color-magenta-700: #8c0276; --color-magenta-800: #6e025c; --color-magenta-900: #4f0343; --color-magenta-950: #31042a; /* Pink */ --color-pink-*: initial; --color-pink-100: #fde5f1; --color-pink-200: #f9bfdb; --color-pink-300: #f480b7; --color-pink-400: #ee4094; --color-pink-500: #de006b; --color-pink-600: #bb015a; --color-pink-700: #98014a; --color-pink-800: #740239; --color-pink-900: #510229; --color-pink-950: #2e0318; /* Lime */ --color-lime-*: initial; --color-lime-100: #f4fae5; --color-lime-200: #e3f3bf; --color-lime-300: #c6e880; --color-lime-400: #aadc40; --color-lime-500: #8ed000; --color-lime-600: #78b001; --color-lime-700: #628f01; --color-lime-800: #4c6f02; --color-lime-900: #364e02; --color-lime-950: #202e03; /* Teal */ --color-teal-*: initial; --color-teal-100: #e5fbf9; --color-teal-200: #bff6f0; --color-teal-300: #80ede0; --color-teal-400: #40e5d1; --color-teal-500: #00dcc2; --color-teal-600: #00c2ab; --color-teal-700: #019a88; --color-teal-800: #017365; --color-teal-900: #024b42; --color-teal-950: #02231f; /* Cyan */ --color-cyan-*: initial; --color-cyan-100: #e5f9fe; --color-cyan-200: #bff0fb; --color-cyan-300: #80e2f8; --color-cyan-400: #40d3f4; --color-cyan-500: #00c5f0; --color-cyan-600: #01a7cb; --color-cyan-700: #0289a6; --color-cyan-800: #036b82; --color-cyan-900: #044d5d; --color-cyan-950: #052f38; /* Violet */ --color-violet-*: initial; --color-violet-100: #f0e8fd; --color-violet-200: #dac5f9; --color-violet-300: #b58bf3; --color-violet-400: #9152ee; --color-violet-500: #6c18e8; --color-violet-600: #5b14c5; --color-violet-700: #4b10a1; --color-violet-800: #3a0d7e; --color-violet-900: #2a095b; --color-violet-950: #190537; /* Purple */ --color-purple-*: initial; --color-purple-100: #f3e5fc; --color-purple-200: #e2bff7; --color-purple-300: #c580f0; --color-purple-400: #a840e8; --color-purple-500: #8b00e0; --color-purple-600: #7501bc; --color-purple-700: #5f0298; --color-purple-800: #490274; --color-purple-900: #330350; --color-purple-950: #1d042d; /* Red */ --color-red-*: initial; --color-red-100: #fce5e6; --color-red-200: #f7bfc1; --color-red-300: #f08083; --color-red-400: #e84045; --color-red-500: #e00007; --color-red-600: #b70006; --color-red-700: #8e0005; --color-red-800: #660104; --color-red-900: #3d0103; --color-red-950: #200204; /* Orange */ --color-orange-*: initial; --color-orange-100: #fef3e5; --color-orange-200: #fde1bf; --color-orange-300: #fbc280; --color-orange-400: #f8a340; --color-orange-500: #f68500; --color-orange-600: #cb6e00; --color-orange-700: #9f5701; --color-orange-800: #743f01; --color-orange-900: #482802; --color-orange-950: #251602; /* Yellow */ --color-yellow-*: initial; --color-yellow-100: #fff9e5; --color-yellow-200: #fff1bf; --color-yellow-300: #ffe380; --color-yellow-400: #ffd440; --color-yellow-500: #ffc600; --color-yellow-600: #d2a300; --color-yellow-700: #a58001; --color-yellow-800: #785e01; --color-yellow-900: #4b3b02; --color-yellow-950: #261f03; /* Green */ --color-green-*: initial; --color-green-100: #eef8e9; --color-green-200: #d5efc8; --color-green-300: #aadf91; --color-green-400: #80ce5b; --color-green-500: #55be24; --color-green-600: #469d1d; --color-green-700: #377c16; --color-green-800: #275c10; --color-green-900: #183b09; --color-green-950: #091a02; /* Blue */ --color-blue-*: initial; --color-blue-100: #e7efff; --color-blue-200: #c3d7ff; --color-blue-300: #87aeff; --color-blue-400: #4c86ff; --color-blue-500: #105eff; --color-blue-600: #0d4ed2; --color-blue-700: #0a3da6; --color-blue-800: #082d79; --color-blue-900: #051c4c; --color-blue-950: #041028; /* Named colors */ --color-black-pearl: #02020f; --color-athens-gray: #f7f7fa; --color-mystic: #e6e6f0; --color-vulcan: #11111f; --color-charade: #242433; --color-waterloo: #77778c; --color-anakiwa: #93b6ff; } body { line-height: inherit; } `; } // src/utils/update-index-css.ts async function updateIndexCss(indexCssPath) { const resolvedPath = path2.resolve(indexCssPath); const fileExists = fs3.existsSync(resolvedPath); if (!fileExists) { console.warn(`\u26A0\uFE0F index.css not found at ${resolvedPath}`); return; } let cssContent = await fs3.readFile(resolvedPath, "utf8"); let lines = cssContent.split("\n"); const reablocksThemeExists = lines.some((line) => line.trim().startsWith("--reablocks-theme:")); if (reablocksThemeExists) { console.log("\u26A0\uFE0F TailwindCSS is already imported in index.css. Skipping update."); return; } const tailwindImportRegex = /@import\s+['"]tailwindcss['"];?/; const alreadyImportedTailwindcss = lines.some((line) => tailwindImportRegex.test(line.trim())); if (alreadyImportedTailwindcss) { lines = lines.filter((line) => !tailwindImportRegex.test(line.trim())); } lines = lines.filter( (line) => !line.trim().startsWith("@tailwind base") && !line.trim().startsWith("@tailwind components") && !line.trim().startsWith("@tailwind utilities") ); let lastImportIndex = -1; lines.forEach((line, index) => { if (line.trim().startsWith("@import") || line.trim().startsWith("@use") || line.trim().startsWith("@forward")) { lastImportIndex = index; } }); const newTailwindCss = getTailwindCss(); if (lastImportIndex >= 0) { lines.splice(lastImportIndex + 1, 0, "", newTailwindCss, ""); } else { lines.unshift(newTailwindCss, ""); } await fs3.writeFile(resolvedPath, lines.join("\n"), "utf8"); console.log(`\u2705 Tailwind styles updated in ${resolvedPath}`); } // src/utils/update-post-css-config.ts import path3 from "path"; import fs4 from "fs-extra"; // src/utils/get-postcss-mjs.ts function getPostcssMjs() { return `export default { plugins: { "@tailwindcss/postcss": {}, }, };`; } // src/utils/get-postcss-сjs.ts function getPostcss\u0421js() { return `/* eslint-disable no-undef */ // eslint-disable-next-line no-undef module.exports = { plugins: [require('@tailwindcss/postcss')] }; `; } // src/utils/update-post-css-config.ts var updatePostCssConfig = async (frameworkConfigType) => { const postcssConfigFileCjs = fs4.existsSync("postcss.config.cjs"); const postcssConfigFileMjs = fs4.existsSync("postcss.config.mjs"); const postcssConfigPath = postcssConfigFileCjs ? path3.resolve("postcss.config.cjs") : postcssConfigFileMjs ? path3.resolve("postcss.config.mjs") : null; if (!postcssConfigPath) { console.log("\u26A0\uFE0F No PostCSS config file found, skipping update."); return; } const newConfig = postcssConfigFileCjs ? getPostcss\u0421js() : getPostcssMjs(); await fs4.writeFile(postcssConfigPath, newConfig, "utf8"); console.log("\u2705 PostCSS configuration updated."); }; // src/index.ts function getPackageInfo() { const packageJsonPath = path4.join("package.json"); return fs5.readJSONSync(packageJsonPath); } process.on("SIGINT", () => process.exit(0)); process.on("SIGTERM", () => process.exit(0)); async function main() { const packageInfo = await getPackageInfo(); const packageManager = getPackageManager(); const program = new Command().name("Reablocks CLI").description( packageInfo.description ?? "Configures Reablocks in your project." ).version( packageInfo.version || "1.0.0", "-v, --version", "display the version number" ); program.command("init").description("Configures Reablocks in your project.").option("-y, --yes", "Skip confirmation prompt.").action(async (options) => { logger.warn("This command assumes a React project."); logger.warn( "If you don't have these, follow the manual steps at https://reablocks.dev/docs/getting-started/setup" ); logger.warn(""); const { framework } = await prompts({ type: "select", name: "framework", message: "Which framework are you using?", choices: [ { title: "Next", value: "next" }, { title: "Vite", value: "vite" }, { title: "Refine", value: "refine" }, { title: "Redwood", value: "redwood" }, { title: "Create React App", value: "cra" }, { title: "Other", value: "other" } ] }); const frameworkConfigType = getFrameworkConfigType(framework); if (!options.yes) { const { proceed } = await prompts({ type: "confirm", name: "proceed", message: "Running this command will install dependencies and modify some files. Proceed?", initial: true }); if (!proceed) { process.exit(0); } } const { indexCssPath } = await prompts({ type: "text", name: "indexCssPath", message: "Where is your index.css file located?", initial: "src/app/globals.css" }); const reablocksSpinner = ora(`Installing reablocks...`).start(); await execa(packageManager, [ packageManager === "npm" ? "install" : "add", "reablocks@latest" ]); reablocksSpinner.succeed(); const tailwindcssSpinner = ora(`Installing tailwindcss...`).start(); await execa(packageManager, [ packageManager === "npm" ? "install" : "add", "tailwindcss@4.x", packageManager === "npm" || packageManager === "pnpm" ? "--save-dev" : "--dev" ]); await execa(packageManager, [ packageManager === "npm" ? "install" : "add", "@tailwindcss/postcss@latest", packageManager === "npm" || packageManager === "pnpm" ? "--save-dev" : "--dev" ]); tailwindcssSpinner.succeed(); const postcssSpinner = ora(`Installing postcss...`).start(); await execa(packageManager, [ packageManager === "npm" ? "install" : "add", "postcss@latest", packageManager === "npm" || packageManager === "pnpm" ? "--save-dev" : "--dev" ]); postcssSpinner.succeed(); const cssSpinner = ora( "Inserting css..." ).start(); await updateIndexCss(indexCssPath); cssSpinner.succeed(); const isTypeScriptConfigFile = fs5.existsSync("tailwind.config.ts"); const tailwindConfigFileName = isTypeScriptConfigFile ? "tailwind.config.ts" : "tailwind.config.js"; const tailwindDestination = `./${tailwindConfigFileName}`; const tailwindConfigFileExists = fs5.existsSync(tailwindDestination); if (tailwindConfigFileExists) { const deleteSpinner = ora(`Removing existing ${tailwindConfigFileName}...`).start(); await fs5.remove(tailwindDestination); deleteSpinner.succeed(); } if (framework === "vite") { const viteSpinner = ora( `Configuring ${frameworkConfigType}...` ).start(); await execa(packageManager, [ packageManager === "npm" ? "install" : "add", "@tailwindcss/vite", packageManager === "npm" || packageManager === "pnpm" ? "--save-dev" : "--dev" ]); await getViteConfig(); viteSpinner.succeed(); } const postCssSpinner = ora( `Updating postcss.config.cjs...` ).start(); await updatePostCssConfig(frameworkConfigType); postCssSpinner.succeed(); }); program.parse(); } main(); export { getPackageInfo }; //# sourceMappingURL=index.js.map