reablocks-cli
Version:
A CLI for configuring Reablocks in your project.
706 lines (643 loc) • 21.2 kB
JavaScript
#!/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