UNPKG

spfn

Version:

Superfunction CLI - Add SPFN to your Next.js project

294 lines (263 loc) 8.82 kB
import { detectPackageManager, logger } from "./chunk-QH74KQEW.js"; // src/commands/setup.ts import { Command } from "commander"; import { existsSync } from "fs"; import { join } from "path"; import ora from "ora"; import { execa } from "execa"; import fse from "fs-extra"; import chalk from "chalk"; var { ensureDirSync, writeFileSync, readFileSync } = fse; async function setupIcons() { const cwd = process.cwd(); logger.info("Setting up SVGR for SVG icon management...\n"); const packageJsonPath = join(cwd, "package.json"); if (!existsSync(packageJsonPath)) { logger.error("No package.json found. Please run this in a Next.js project."); process.exit(1); } const packageJson = JSON.parse( readFileSync(packageJsonPath, "utf-8") ); const hasNext = packageJson.dependencies?.next || packageJson.devDependencies?.next; if (!hasNext) { logger.error("Next.js not detected in dependencies. This setup is for Next.js projects only."); process.exit(1); } const hasSvgr = packageJson.devDependencies?.["@svgr/webpack"]; if (hasSvgr) { logger.warn("@svgr/webpack is already installed."); logger.info("Skipping installation, but will create directory structure...\n"); } if (!hasSvgr) { const pm = detectPackageManager(cwd); logger.step(`Detected package manager: ${pm}`); const spinner2 = ora("Installing @svgr/webpack...").start(); try { await execa( pm, pm === "npm" ? ["install", "--save-dev", "@svgr/webpack"] : ["add", "-D", "@svgr/webpack"], { cwd } ); spinner2.succeed("@svgr/webpack installed"); } catch (error) { spinner2.fail("Failed to install @svgr/webpack"); logger.error(String(error)); process.exit(1); } } const spinner = ora("Updating next.config...").start(); try { const possibleConfigs = [ "next.config.ts", "next.config.js", "next.config.mjs" ]; let configPath = null; for (const config of possibleConfigs) { const path = join(cwd, config); if (existsSync(path)) { configPath = path; break; } } if (!configPath) { spinner.warn("next.config not found, creating next.config.ts..."); configPath = join(cwd, "next.config.ts"); const newConfig = `import type { NextConfig } from "next"; const nextConfig: NextConfig = { webpack(config) { // SVGR: Import SVG as React components // eslint-disable-next-line @typescript-eslint/no-explicit-any const fileLoaderRule = (config.module.rules as any[]) .find((rule: any) => Array.isArray(rule.oneOf)) ?.oneOf.find((rule: any) => rule.test?.test?.('.svg')); if (fileLoaderRule) { fileLoaderRule.exclude = /\\.svg$/i; } config.module.rules.unshift({ test: /\\.svg$/i, use: ['@svgr/webpack'], }); return config; }, turbopack: { rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js', }, }, }, }; export default nextConfig; `; writeFileSync(configPath, newConfig); spinner.succeed("Created next.config.ts with SVGR support"); } else { let configContent = readFileSync(configPath, "utf-8"); if (configContent.includes("@svgr/webpack")) { spinner.warn("SVGR already configured in next.config"); } else { const hasWebpack = configContent.includes("webpack("); const hasTurbopack = configContent.includes("turbopack:"); if (hasWebpack || hasTurbopack) { spinner.info("Manual update required for next.config"); logger.warn("\nYou need to manually add SVGR configuration to your next.config file."); logger.info("See: https://react-svgr.com/docs/next/"); logger.info("\nAdd this to your next.config:\n"); console.log(chalk.gray(` webpack(config) { const fileLoaderRule = config.module.rules .find(rule => rule.oneOf) ?.oneOf.find(rule => rule.test?.test?.('.svg')); if (fileLoaderRule) { fileLoaderRule.exclude = /\\.svg$/i; } config.module.rules.unshift({ test: /\\.svg$/i, use: ['@svgr/webpack'], }); return config; }, turbopack: { rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js', }, }, }, `)); } else { const webpackConfig = ` webpack(config) { // SVGR: Import SVG as React components // eslint-disable-next-line @typescript-eslint/no-explicit-any const fileLoaderRule = (config.module.rules as any[]) .find((rule: any) => Array.isArray(rule.oneOf)) ?.oneOf.find((rule: any) => rule.test?.test?.('.svg')); if (fileLoaderRule) { fileLoaderRule.exclude = /\\.svg$/i; } config.module.rules.unshift({ test: /\\.svg$/i, use: ['@svgr/webpack'], }); return config; },`; const turbopackConfig = ` turbopack: { rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js', }, }, },`; const emptyConfigPattern = /const\s+\w+:\s*NextConfig\s*=\s*\{\s*\};/; if (emptyConfigPattern.test(configContent)) { configContent = configContent.replace( emptyConfigPattern, `const nextConfig: NextConfig = { ${webpackConfig} ${turbopackConfig} };` ); } else { const configObjectPattern = /(const\s+\w+:\s*NextConfig\s*=\s*\{)([^}]*?)(\};)/s; if (configObjectPattern.test(configContent)) { configContent = configContent.replace( configObjectPattern, (_match, opening, content, closing) => { const trimmedContent = content.trim(); if (trimmedContent) { return `${opening}${content} ${webpackConfig} ${turbopackConfig} ${closing}`; } else { return `${opening} ${webpackConfig} ${turbopackConfig} ${closing}`; } } ); } } writeFileSync(configPath, configContent); spinner.succeed("Added SVGR configuration to next.config"); } } } } catch (error) { spinner.fail("Failed to update next.config"); logger.error(String(error)); } const iconsSpinner = ora("Creating src/assets/icons/ directory...").start(); try { const iconsDir = join(cwd, "src", "assets", "icons"); ensureDirSync(iconsDir); const readmePath = join(iconsDir, "README.md"); const readmeContent = `# Icons This directory manages SVG icons for the project. ## Usage Import SVG files as React components using SVGR: \`\`\`tsx import Logo from '@/assets/icons/logo.svg'; function MyComponent() { return ( <Logo className="size-8 text-gray-900 dark:text-white" /> ); } \`\`\` ## Color Control Use \`fill="currentColor"\` in your SVG files to control colors via Tailwind CSS: \`\`\`tsx // Light mode: gray-900, Dark mode: white <Logo className="size-8 text-gray-900 dark:text-white" /> // Custom colors <Icon className="size-6 text-blue-600" /> \`\`\` ## Adding New Icons 1. Add SVG file to this directory 2. Set \`fill="currentColor"\` for color control (optional) 3. Remove \`width\` and \`height\` attributes for flexible sizing 4. Import and use as React component \`\`\`tsx import NewIcon from '@/assets/icons/new-icon.svg'; \`\`\` ## Configuration - **next.config.ts**: SVGR webpack loader configuration - **Turbopack**: SVG loader rules for fast refresh ## Example SVG Structure \`\`\`xml <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="..." fill="currentColor"/> </svg> \`\`\` Note: Remove \`width\` and \`height\` for flexible sizing with Tailwind utilities. `; writeFileSync(readmePath, readmeContent); iconsSpinner.succeed("Created src/assets/icons/ directory with README.md"); } catch (error) { iconsSpinner.fail("Failed to create directory structure"); logger.error(String(error)); } console.log("\n" + chalk.green.bold("\u2713 SVGR setup completed!\n")); console.log("Next steps:"); console.log(" 1. Add SVG files to " + chalk.cyan("src/assets/icons/")); console.log(" 2. Import them as React components:"); console.log(" " + chalk.gray("import Logo from '@/assets/icons/logo.svg';")); console.log(" 3. Use with Tailwind classes:"); console.log(" " + chalk.gray('<Logo className="size-8 text-gray-900 dark:text-white" />')); console.log("\nDocumentation: " + chalk.cyan("src/assets/icons/README.md")); } var setupCommand = new Command("setup").description("Setup additional features for your project"); setupCommand.command("icons").description("Setup SVGR for SVG icon management").action(setupIcons); export { setupIcons, setupCommand };