UNPKG

@shlroland/lint-cli

Version:
382 lines (363 loc) โ€ข 11.7 kB
import { AnswerContext, cjsConfigFactory, deleteFile, ensureConfig, esmConfigFactory, initGit, initHusky, install, isGitRepository, isHuskyInstalled, onCancel, shouldInitGitPrompt, shouldOverridePrompt, sleep, willInstallList, writeFile } from "./chunk-7K4SGG5N.js"; // src/cli.ts import process2 from "node:process"; import restoreCursor from "restore-cursor"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; // package.json var version = "0.5.1"; // src/prompts.ts import * as p4 from "@clack/prompts"; import c3 from "picocolors"; // src/answer/index.ts import * as p from "@clack/prompts"; // src/answer/abstract/answer.ts var AbstractAnswer = class { static toolName; async configGuard() { const configs = this.config.pendingConfigs; for (const config2 of configs) { if (await config2.checkConfigFileExisted()) { const shouldOverride = await shouldOverridePrompt(this.answerName); if (shouldOverride) { config2.overrideFile = async () => { await deleteFile(config2.configFilePath); }; this.config.addPendingConfig(config2); } else { this.config.removePendingConfig(config2); } } } } get pendingPackages() { return this.installer.pendingPackages; } get pendingConfigs() { const result = []; for (const config2 of this.config.pendingConfigs) { result.push({ configFilePath: config2.configFilePath, configFileContent: config2.configFileContent, overrideFile: config2.overrideFile }); } return result; } }; // src/answer/abstract/config.ts var Config = class { #pendingConfigs = /* @__PURE__ */ new Map(); constructor(defaultConfigs) { for (const config2 of defaultConfigs) { this.#pendingConfigs.set(config2.configFileName, config2); } } addPendingConfig(config2) { this.#pendingConfigs.set(config2.configFileName, config2); } removePendingConfig(config2) { this.#pendingConfigs.delete(config2.configFileName); } clearPendingConfigs() { this.#pendingConfigs.clear(); } get pendingConfigs() { return this.#pendingConfigs.values(); } }; // src/answer/abstract/config-option.ts import fs from "node:fs"; import path from "node:path"; import process from "node:process"; var ConfigOption = class { context = AnswerContext.instance; async overrideFile() { return Promise.resolve(); } }; var CommonConfigOption = class extends ConfigOption { configFileName; configFilePath; configFileContent; constructor(options) { super(); this.configFileName = options.configFileName; this.configFilePath = path.join(this.context.cwd, this.configFileName); this.configFileContent = options.content; } async checkConfigFileExisted() { const filePath = path.resolve(process.cwd(), this.configFilePath); const exists = await fs.promises.access(filePath).then(() => true).catch(() => false); return exists; } }; var CosmiConfigOption = class extends ConfigOption { configFileName; configFilePath; configFileContent; esmImportConfigContent; esmExportConfigContent; cjsImportConfigContent; cjsExportConfigContent; checkConfigNames; constructor(options) { super(); this.configFileName = options.configFileName; this.esmImportConfigContent = options.esmImportConfigContent; this.esmExportConfigContent = options.esmExportConfigContent; this.cjsImportConfigContent = options.cjsImportConfigContent; this.cjsExportConfigContent = options.cjsExportConfigContent; this.checkConfigNames = options.checkConfigNames; const { content, path: path2 } = this.configFileContentFactory(); this.configFileContent = content; this.configFilePath = path2; } configFileContentFactory() { const content = this.context.moduleType === "module" ? esmConfigFactory(this.esmExportConfigContent, this.esmImportConfigContent) : cjsConfigFactory(this.cjsExportConfigContent, this.cjsImportConfigContent); return { content, path: path.join(this.context.cwd, this.configFileName) }; } async checkConfigFileExisted() { const checkConfigNames = this.checkConfigNames; for (const configName of checkConfigNames) { const config2 = await ensureConfig(configName); if (config2) { return true; } } return false; } }; // src/answer/abstract/install.ts var Installer = class extends Set { get pendingPackages() { return Array.from(this.values()); } }; // src/answer/commitlint.ts var CommitlintAnswer = class extends AbstractAnswer { static toolName = "commitlint"; answerName = "commitlint"; installer = new Installer(["czg", "@commitlint/cli", "@shlroland/cz-config"]); config = new Config([ new CosmiConfigOption({ configFileName: "commitlint.config.js", esmImportConfigContent: ``, esmExportConfigContent: `{ extends: ['@shlroland/cz-config/commitlint'] }`, cjsImportConfigContent: ``, cjsExportConfigContent: `{ extends: ['@shlroland/cz-config/commitlint'] }`, checkConfigNames: ["commitlint"] }) ]); }; // src/answer/eslint.ts var EslintAnswer = class extends AbstractAnswer { static toolName = "eslint"; answerName = "eslint"; installer = new Installer(["eslint", "@shlroland/eslint-config", "eslint-plugin-format"]); config = new Config( [ new CosmiConfigOption({ configFileName: "eslint.config.js", esmImportConfigContent: 'import { shlroland } from "@shlroland/eslint-config"', esmExportConfigContent: "shlroland()", cjsImportConfigContent: 'const { shlroland } = require("@shlroland/eslint-config")', cjsExportConfigContent: "shlroland()", checkConfigNames: ["eslint"] }) ] ); }; // ../husky/lib/index.js var config = { hooks: { "commit-msg": "npx --no -- commitlint --edit $1", "pre-commit": "lint-staged \n tsc --noEmit" } }; var husky_default = config; // src/answer/husky.ts var HuskyAnswer = class extends AbstractAnswer { static toolName = "husky"; context = AnswerContext.instance; answerName = "husky"; installer = new Installer(["husky"]); config = new Config([ new CommonConfigOption({ configFileName: ".husky/pre-commit", content: husky_default.hooks["pre-commit"] }), new CommonConfigOption({ configFileName: ".husky/commit-msg", content: husky_default.hooks["commit-msg"] }) ]); async configGuard() { const hasGit = await isGitRepository(this.context.cwd); if (!hasGit) { const shouldInitGit = await shouldInitGitPrompt(); if (!shouldInitGit) { this.config.clearPendingConfigs(); return; } await initGit(); } const hasHusky = await isHuskyInstalled(this.context.cwd); if (!hasHusky) { await initHusky(); } const configs = this.config.pendingConfigs; for (const config2 of configs) { if (await config2.checkConfigFileExisted()) { const shouldOverride = await shouldOverridePrompt(config2.configFileName); if (shouldOverride) { config2.overrideFile = async () => { await deleteFile(config2.configFilePath); }; this.config.addPendingConfig(config2); } else { this.config.removePendingConfig(config2); } } } } }; // src/answer/lint-staged.ts var LintStagedAnswer = class extends AbstractAnswer { static toolName = "lint-staged"; answerName = "lint-staged"; installer = new Installer(["lint-staged", "@shlroland/lint-staged"]); config = new Config([ new CosmiConfigOption({ configFileName: "lint-staged.config.js", esmImportConfigContent: 'import lintStaged from "@shlroland/lint-staged"', esmExportConfigContent: "lintStaged", cjsImportConfigContent: 'const lintStaged = require("@shlroland/lint-staged")', cjsExportConfigContent: "lintStaged", checkConfigNames: ["lint-staged", "lintstaged"] }) ]); }; // src/answer/index.ts var answers = [ new EslintAnswer(), new LintStagedAnswer(), new CommitlintAnswer(), new HuskyAnswer() ]; var toSelectAnswers = /* @__PURE__ */ new Map([ [EslintAnswer.toolName, EslintAnswer], [LintStagedAnswer.toolName, LintStagedAnswer], [CommitlintAnswer.toolName, CommitlintAnswer], [HuskyAnswer.toolName, HuskyAnswer] ]); async function getAnswers() { const answerKeys = Array.from(toSelectAnswers.keys()); const options = answerKeys.map((answer) => ({ value: answer })); const selectedAnswers = await p.multiselect({ message: "Please select lint tools to install:", options, initialValues: answerKeys }); if (p.isCancel(selectedAnswers)) { onCancel(); return []; } return selectedAnswers.map((answer) => { const AnswerCls = toSelectAnswers.get(answer); if (!AnswerCls) { throw new Error(`Answer ${answer} not found`); } return new AnswerCls(); }); } // src/tasks/config.ts import * as p2 from "@clack/prompts"; import c from "picocolors"; async function configTask(selectedAnswers) { p2.log.step(c.cyan("๐Ÿ› ๏ธ Config lint tools step")); for (const answer of selectedAnswers) { await answer.configGuard(); } const pendingConfigs = selectedAnswers.map((answer) => answer.pendingConfigs).flat(); for (const { configFilePath, configFileContent, overrideFile } of pendingConfigs) { await overrideFile(); await writeFile(configFilePath, configFileContent); } p2.log.step(c.green("๐Ÿฅณ Config lint tools successfully.")); } // src/tasks/install.ts import * as p3 from "@clack/prompts"; import c2 from "picocolors"; async function installTask(selectedAnswers) { p3.log.step(c2.cyan("๐Ÿ“ฆ Install lint tools step")); const pendingPkgs = selectedAnswers.map((answer) => answer.pendingPackages).flat(); willInstallList(pendingPkgs); await install(pendingPkgs); p3.log.step(c2.green("๐Ÿ˜‹ Install lint tools successfully.")); } // src/prompts.ts async function commandInstallPrompt() { console.clear(); await sleep(500); p4.intro(c3.bgGreen(c3.greenBright(`๐Ÿงบ This command will install lint tools only but not config them.`))); await sleep(500); const selectedAnswers = await getAnswers(); await installTask(selectedAnswers); p4.outro(c3.green("๐ŸŽ‰ All done!")); } async function commandConfigPrompt() { console.clear(); await sleep(500); p4.intro(c3.bgBlack(c3.bgBlackBright(`๐Ÿงฐ This command will config lint tools only but not install them.`))); await sleep(500); const selectedAnswers = await getAnswers(); await configTask(selectedAnswers); p4.outro(c3.green("๐ŸŽ‰ All done!")); } async function commandInitPrompt() { await sleep(500); p4.intro(c3.bgMagenta(c3.magentaBright(`๐Ÿš€ This command will install lint tools and config them2.`))); await sleep(500); const selectedAnswers = await getAnswers(); await installTask(selectedAnswers); await configTask(selectedAnswers); await sleep(500); p4.outro(c3.magentaBright("๐ŸŽ‰ All done!")); } // src/cli.ts yargs(hideBin(process2.argv)).scriptName("shlroland-lint").command("install", "install lint tools", (yargs2) => yargs2, () => commandInstallPrompt()).command("config", "config lint tool", (yargs2) => yargs2, () => commandConfigPrompt()).command( ["$0", "$0 init"], "cli tool to setup lint tool", (yargs2) => yargs2, async () => { await commandInitPrompt(); } ).alias("v", "version").version(version).help("h", "help info").showHelpOnFail(false).argv; process2.stdin.on("keypress", (str, key) => { if (key.ctrl && key.name === "c") process2.exit(); }); restoreCursor();