UNPKG

@coderwyd/eslint-config

Version:
185 lines (176 loc) 6.59 kB
import process from "node:process"; import c from "picocolors"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import fs from "node:fs"; import fsp from "node:fs/promises"; import path from "node:path"; import parse from "parse-gitignore"; import prompts from "prompts"; import { execSync } from "node:child_process"; //#region package.json var version = "4.4.0"; var devDependencies = { "@antfu/ni": "^25.0.0", "@eslint-react/eslint-plugin": "^1.52.2", "@eslint/config-inspector": "^1.1.0", "@types/eslint-config-prettier": "^6.11.3", "@types/node": "^24.0.3", "@types/prompts": "^2.4.9", "@types/yargs": "^17.0.33", "@unocss/eslint-plugin": "^66.2.3", "bumpp": "^10.2.0", "eslint": "^9.29.0", "eslint-plugin-import-lite": "^0.3.0", "eslint-plugin-react-hooks": "6.0.0-rc.1", "eslint-plugin-react-refresh": "^0.4.20", "eslint-plugin-svelte": "^3.9.2", "eslint-plugin-tailwindcss": "^3.18.0", "jiti": "^2.4.2", "nano-staged": "^0.8.0", "simple-git-hooks": "^2.13.0", "svelte": "^5.34.6", "svelte-eslint-parser": "^1.2.0", "tsdown": "^0.12.8", "typescript": "^5.8.3" }; //#endregion //#region src/cli/constants.ts const ARROW = c.cyan("→"); const CHECK = c.green("✔"); const CROSS = c.red("✘"); const WARN = c.yellow("ℹ"); const eslintVersion = devDependencies.eslint; const vscodeSettingsString = ` "editor.formatOnSave": true, // Auto fix "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", "source.organizeImports": "never" }, `; //#endregion //#region src/cli/utils.ts function isGitClean() { try { execSync("git diff-index --quiet HEAD --"); return true; } catch { return false; } } //#endregion //#region src/cli/run.ts async function run(options = {}) { const SKIP_PROMPT = !!process.env.SKIP_PROMPT || options.yes; const SKIP_GIT_CHECK = !!process.env.SKIP_GIT_CHECK; const cwd = process.cwd(); const pathFlatConfig = path.join(cwd, "eslint.config.js"); const pathPackageJSON = path.join(cwd, "package.json"); const pathESLintIngore = path.join(cwd, ".eslintignore"); if (fs.existsSync(pathFlatConfig)) { console.log(c.yellow(`${WARN} eslint.config.js already exists, migration wizard exited.`)); return process.exit(1); } if (!SKIP_GIT_CHECK && !isGitClean()) { const { confirmed } = await prompts({ initial: false, message: "There are uncommitted changes in the current repository, are you sure to continue?", name: "confirmed", type: "confirm" }); if (!confirmed) return process.exit(1); } console.log(c.cyan(`${ARROW} bumping @coderwyd/eslint-config to v${version}`)); const pkgContent = await fsp.readFile(pathPackageJSON, "utf-8"); const pkg = JSON.parse(pkgContent); pkg.devDependencies ??= {}; pkg.devDependencies["@coderwyd/eslint-config"] = `^${version}`; if (!pkg.devDependencies.eslint) pkg.devDependencies.eslint = eslintVersion; await fsp.writeFile(pathPackageJSON, JSON.stringify(pkg, null, 2)); console.log(c.green(`${CHECK} changes wrote to package.json`)); const eslintIgnores = []; if (fs.existsSync(pathESLintIngore)) { console.log(c.cyan(`${ARROW} migrating existing .eslintignore`)); const content = await fsp.readFile(pathESLintIngore, "utf-8"); const parsed = parse(content); const globs = parsed.globs(); for (const glob of globs) if (glob.type === "ignore") eslintIgnores.push(...glob.patterns); else if (glob.type === "unignore") eslintIgnores.push(...glob.patterns.map((pattern) => `!${pattern}`)); } let eslintConfigContent = ""; const coderwydConfig = `${eslintIgnores.length > 0 ? `ignores: ${JSON.stringify(eslintIgnores)}` : ""}`; if (pkg.type === "module") eslintConfigContent = ` import { defineConfig } from '@coderwyd/eslint-config' export default defineConfig({\n${coderwydConfig}\n}) `.trimStart(); else eslintConfigContent = ` const { defineConfig } = require('@coderwyd/eslint-config') module.exports = defineConfig({\n${coderwydConfig}\n}) `.trimStart(); await fsp.writeFile(pathFlatConfig, eslintConfigContent); console.log(c.green(`${CHECK} created eslint.config.js`)); const files = fs.readdirSync(cwd); const legacyConfig = []; files.forEach((file) => { if (/eslint|prettier/.test(file) && !/eslint.config./.test(file)) legacyConfig.push(file); }); if (legacyConfig.length > 0) { console.log(`${WARN} you can now remove those files manually:`); console.log(` ${c.dim(legacyConfig.join(", "))}`); } let promptResult = { updateVscodeSettings: true }; if (!SKIP_PROMPT) try { promptResult = await prompts({ initial: true, message: "Update .vscode/settings.json for better VS Code experience?", name: "updateVscodeSettings", type: "confirm" }, { onCancel: () => { throw new Error(`Cancelled`); } }); } catch (error) { console.log(error.message); return; } if (promptResult?.updateVscodeSettings ?? true) { const dotVscodePath = path.join(cwd, ".vscode"); const settingsPath = path.join(dotVscodePath, "settings.json"); if (!fs.existsSync(dotVscodePath)) await fsp.mkdir(dotVscodePath, { recursive: true }); if (!fs.existsSync(settingsPath)) { await fsp.writeFile(settingsPath, `{${vscodeSettingsString}}\n`, "utf-8"); console.log(c.green(`${CHECK} created .vscode/settings.json`)); } else { let settingsContent = await fsp.readFile(settingsPath, "utf8"); settingsContent = settingsContent.trim().replace(/\s*\}$/, ""); settingsContent += settingsContent.endsWith(",") || settingsContent.endsWith("{") ? "" : ","; settingsContent += `${vscodeSettingsString}}\n`; await fsp.writeFile(settingsPath, settingsContent, "utf-8"); console.log(c.green(`${CHECK} updated .vscode/settings.json`)); } } console.log(c.green(`${CHECK} migration completed`)); console.log(`Now you can update the dependencies and run ${c.blue("eslint . --fix")}\n`); } //#endregion //#region src/cli/index.ts function header() { console.log(`\n${c.green(`@coderwyd/eslint-config `)}${c.dim(`v${version}`)}`); } const instance = yargs(hideBin(process.argv)).scriptName("@coderwyd/eslint-config").usage("").command("*", "Run the initialization or migration", (args) => args.option("yes", { alias: "y", description: "Skip prompts and use default values", type: "boolean" }).help(), async (args) => { header(); console.log(); try { await run(args); } catch (error) { console.error(c.inverse(c.red(" Failed to migrate "))); console.error(c.red(`${CROSS} ${String(error)}`)); process.exit(1); } }).showHelpOnFail(false).alias("h", "help").version("version", version).alias("v", "version"); instance.help().argv; //#endregion