diff --git a/packages/f2elint/package.json b/packages/f2elint/package.json index de3c8e6..a138468 100644 --- a/packages/f2elint/package.json +++ b/packages/f2elint/package.json @@ -44,6 +44,7 @@ "bin": "dist/f2elint.js", "files": [ "dist", + "templates", "CHANGELOG.md", "LICENSE", "README.md" @@ -66,6 +67,7 @@ "@babel/core": "^7.23.9", "@babel/eslint-parser": "^7.23.10", "@babel/preset-react": "^7.23.3", + "@clack/prompts": "^0.7.0", "@commitlint/cli": "^16.3.0", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", @@ -91,6 +93,7 @@ "fs-extra": "^9.1.0", "glob": "^7.2.3", "husky": "^3.1.0", + "init-roll": "^1.0.10", "inquirer": "^7.3.3", "is-docker": "^2.2.1", "lodash": "^4.17.21", diff --git a/packages/f2elint/src/actions/init.ts b/packages/f2elint/src/actions/init.ts deleted file mode 100644 index fe7dfa0..0000000 --- a/packages/f2elint/src/actions/init.ts +++ /dev/null @@ -1,167 +0,0 @@ -import path from 'path'; -import fs from 'fs-extra'; -import inquirer from 'inquirer'; -import spawn from 'cross-spawn'; -import update from './update'; -import npmType from '../utils/npm-type'; -import log from '../utils/log'; -import conflictResolve from '../utils/conflict-resolve'; -import generateTemplate from '../utils/generate-template'; -import { PROJECT_TYPES, PKG_NAME } from '../utils/constants'; -import type { InitOptions, PKG } from '../types'; - -let step = 0; - -/** - * 选择项目语言和框架 - */ -const chooseEslintType = async (): Promise => { - const { type } = await inquirer.prompt({ - type: 'list', - name: 'type', - message: `Step ${++step}. 请选择项目的语言(JS/TS)和框架(React/Vue)类型:`, - choices: PROJECT_TYPES, - }); - - return type; -}; - -/** - * 选择是否启用 stylelint - * @param defaultValue - */ -const chooseEnableStylelint = async (defaultValue: boolean): Promise => { - const { enable } = await inquirer.prompt({ - type: 'confirm', - name: 'enable', - message: `Step ${++step}. 是否需要使用 stylelint(若没有样式文件则不需要):`, - default: defaultValue, - }); - - return enable; -}; - -/** - * 选择是否启用 markdownlint - */ -const chooseEnableMarkdownLint = async (): Promise => { - const { enable } = await inquirer.prompt({ - type: 'confirm', - name: 'enable', - message: `Step ${++step}. 是否需要使用 markdownlint(若没有 Markdown 文件则不需要):`, - default: true, - }); - - return enable; -}; - -/** - * 选择是否启用 prettier - */ -const chooseEnablePrettier = async (): Promise => { - const { enable } = await inquirer.prompt({ - type: 'confirm', - name: 'enable', - message: `Step ${++step}. 是否需要使用 Prettier 格式化代码:`, - default: true, - }); - - return enable; -}; - -export default async (options: InitOptions) => { - const cwd = options.cwd || process.cwd(); - const isTest = process.env.NODE_ENV === 'test'; - const checkVersionUpdate = options.checkVersionUpdate || false; - const disableNpmInstall = options.disableNpmInstall || false; - const config: Record = {}; - const pkgPath = path.resolve(cwd, 'package.json'); - let pkg: PKG = fs.readJSONSync(pkgPath); - - // 版本检查 - if (!isTest && checkVersionUpdate) { - await update(false); - } - - // 初始化 `enableESLint`,默认为 true,无需让用户选择 - if (typeof options.enableESLint === 'boolean') { - config.enableESLint = options.enableESLint; - } else { - config.enableESLint = true; - } - - // 初始化 `eslintType` - if (options.eslintType && PROJECT_TYPES.find((choice) => choice.value === options.eslintType)) { - config.eslintType = options.eslintType; - } else { - config.eslintType = await chooseEslintType(); - } - - // 初始化 `enableStylelint` - if (typeof options.enableStylelint === 'boolean') { - config.enableStylelint = options.enableStylelint; - } else { - config.enableStylelint = await chooseEnableStylelint(!/node/.test(config.eslintType)); - } - - // 初始化 `enableMarkdownlint` - if (typeof options.enableMarkdownlint === 'boolean') { - config.enableMarkdownlint = options.enableMarkdownlint; - } else { - config.enableMarkdownlint = await chooseEnableMarkdownLint(); - } - - // 初始化 `enablePrettier` - if (typeof options.enablePrettier === 'boolean') { - config.enablePrettier = options.enablePrettier; - } else { - config.enablePrettier = await chooseEnablePrettier(); - } - - if (!isTest) { - log.info(`Step ${++step}. 检查并处理项目中可能存在的依赖和配置冲突`); - pkg = await conflictResolve(cwd, options.rewriteConfig); - log.success(`Step ${step}. 已完成项目依赖和配置冲突检查处理 :D`); - - if(!disableNpmInstall){ - log.info(`Step ${++step}. 安装依赖`); - const npm = await npmType; - spawn.sync( - npm, - ['i', '-D', PKG_NAME], - { stdio: 'inherit', cwd }, - ); - log.success(`Step ${step}. 安装依赖成功 :D`); - } - } - - // 更新 pkg.json - pkg = fs.readJSONSync(pkgPath); - // 在 `package.json` 中写入 `scripts` - if (!pkg.scripts) { - pkg.scripts = {}; - } - if (!pkg.scripts[`${PKG_NAME}-scan`]) { - pkg.scripts[`${PKG_NAME}-scan`] = `${PKG_NAME} scan`; - } - if (!pkg.scripts[`${PKG_NAME}-fix`]) { - pkg.scripts[`${PKG_NAME}-fix`] = `${PKG_NAME} fix`; - } - - // 配置 commit 卡点 - log.info(`Step ${++step}. 配置 git commit 卡点`); - if (!pkg.husky) pkg.husky = {}; - if (!pkg.husky.hooks) pkg.husky.hooks = {}; - pkg.husky.hooks['pre-commit'] = `${PKG_NAME} commit-file-scan`; - pkg.husky.hooks['commit-msg'] = `${PKG_NAME} commit-msg-scan`; - fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2)); - log.success(`Step ${step}. 配置 git commit 卡点成功 :D`); - - log.info(`Step ${++step}. 写入配置文件`); - generateTemplate(cwd, config); - log.success(`Step ${step}. 写入配置文件成功 :D`); - - // 完成信息 - const logs = [`${PKG_NAME} 初始化完成 :D`].join('\r\n'); - log.success(logs); -}; diff --git a/packages/f2elint/src/actions/scan.ts b/packages/f2elint/src/actions/scan.ts deleted file mode 100644 index 5c4947b..0000000 --- a/packages/f2elint/src/actions/scan.ts +++ /dev/null @@ -1,66 +0,0 @@ -import fs from 'fs-extra'; -import path from 'path'; -import { doESLint, doMarkdownlint, doPrettier, doStylelint } from '../lints'; -import type { Config, PKG, ScanOptions, ScanReport, ScanResult } from '../types'; -import { PKG_NAME } from '../utils/constants'; - -export default async (options: ScanOptions): Promise => { - const { cwd, fix, outputReport, config: scanConfig } = options; - - const readConfigFile = (pth: string): any => { - const localPath = path.resolve(cwd, pth); - return fs.existsSync(localPath) ? require(localPath) : {}; - }; - const pkg: PKG = readConfigFile('package.json'); - const config: Config = scanConfig || readConfigFile(`${PKG_NAME}.config.js`); - const runErrors: Error[] = []; - let results: ScanResult[] = []; - - // prettier - if (fix && config.enablePrettier !== false) { - await doPrettier(options); - } - - // eslint - if (config.enableESLint !== false) { - try { - const eslintResults = await doESLint({ ...options, pkg, config }); - results = results.concat(eslintResults); - } catch (e) { - runErrors.push(e); - } - } - - // stylelint - if (config.enableStylelint !== false) { - try { - const stylelintResults = await doStylelint({ ...options, pkg, config }); - results = results.concat(stylelintResults); - } catch (e) { - runErrors.push(e); - } - } - - // markdown - if (config.enableMarkdownlint !== false) { - try { - const markdownlintResults = await doMarkdownlint({ ...options, pkg, config }); - results = results.concat(markdownlintResults); - } catch (e) { - runErrors.push(e); - } - } - - // 生成报告报告文件 - if (outputReport) { - const reportPath = path.resolve(process.cwd(), `./${PKG_NAME}-report.json`); - fs.outputFile(reportPath, JSON.stringify(results, null, 2), () => {}); - } - - return { - results, - errorCount: results.reduce((count, { errorCount }) => count + errorCount, 0), - warningCount: results.reduce((count, { warningCount }) => count + warningCount, 0), - runErrors, - }; -}; diff --git a/packages/f2elint/src/actions/update.ts b/packages/f2elint/src/actions/update.ts deleted file mode 100644 index 65eeb68..0000000 --- a/packages/f2elint/src/actions/update.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { execSync } from 'child_process'; -import ora from 'ora'; -import log from '../utils/log'; -import npmType from '../utils/npm-type'; -import { PKG_NAME, PKG_VERSION } from '../utils/constants'; - -/** - * 检查最新版本号 - */ -const checkLatestVersion = async (): Promise => { - const npm = await npmType; - const latestVersion = execSync(`${npm} view ${PKG_NAME} version`).toString('utf-8').trim(); - - if (PKG_VERSION === latestVersion) return null; - - const compareArr = PKG_VERSION.split('.').map(Number); - const beComparedArr = latestVersion.split('.').map(Number); - - // 依次比较版本号每一位大小 - for (let i = 0; i < compareArr.length; i++) { - if (compareArr[i] > beComparedArr[i]) { - return null; - } else if (compareArr[i] < beComparedArr[i]) { - return latestVersion; - } - } -}; - -/** - * 检查包的版本 - * @param install - 自动安装最新包 - */ -export default async (install = true) => { - const checking = ora(`[${PKG_NAME}] 正在检查最新版本...`); - checking.start(); - - try { - const npm = await npmType; - const latestVersion = await checkLatestVersion(); - checking.stop(); - - if (latestVersion && install) { - const update = ora(`[${PKG_NAME}] 存在新版本,将升级至 ${latestVersion}`); - - update.start(); - - execSync(`${npm} i -g ${PKG_NAME}`); - - update.stop(); - } else if (latestVersion) { - log.warn( - `最新版本为 ${latestVersion},本地版本为 ${PKG_VERSION},请尽快升级到最新版本。\n你可以执行 ${npm} install -g ${PKG_NAME}@latest 来安装此版本\n`, - ); - } else if (install) { - log.info(`当前没有可用的更新`); - } - } catch (e) { - checking.stop(); - log.error(e); - } -}; diff --git a/packages/f2elint/src/config/_editorconfig.ejs b/packages/f2elint/src/config/_editorconfig.ejs deleted file mode 100644 index 3192996..0000000 --- a/packages/f2elint/src/config/_editorconfig.ejs +++ /dev/null @@ -1,13 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -quote_type = single - -[*.md] -trim_trailing_whitespace = false diff --git a/packages/f2elint/src/config/_eslintignore.ejs b/packages/f2elint/src/config/_eslintignore.ejs deleted file mode 100644 index 473803f..0000000 --- a/packages/f2elint/src/config/_eslintignore.ejs +++ /dev/null @@ -1,9 +0,0 @@ -build/ -coverage/ -dist/ -es/ -lib/ -node_modules/ -**/*.min.js -**/*-min.js -**/*.bundle.js diff --git a/packages/f2elint/src/config/_eslintrc.js.ejs b/packages/f2elint/src/config/_eslintrc.js.ejs deleted file mode 100644 index a64c7ed..0000000 --- a/packages/f2elint/src/config/_eslintrc.js.ejs +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - extends: [ - '<%= eslintType === 'index' ? 'eslint-config-ali' : `eslint-config-ali/${eslintType}` %>', - <%_ if (enablePrettier) { _%> - 'prettier', - <%_ } _%> - ], -}; diff --git a/packages/f2elint/src/config/_markdownlint.json.ejs b/packages/f2elint/src/config/_markdownlint.json.ejs deleted file mode 100644 index 078265c..0000000 --- a/packages/f2elint/src/config/_markdownlint.json.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%_ if (enableMarkdownlint) { _%> -{ - "extends": "markdownlint-config-ali" -} -<%_ } _%> diff --git a/packages/f2elint/src/config/_markdownlintignore.ejs b/packages/f2elint/src/config/_markdownlintignore.ejs deleted file mode 100644 index dd94ad2..0000000 --- a/packages/f2elint/src/config/_markdownlintignore.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%_ if (enableMarkdownlint) { _%> -<%_ markdownLintIgnores.forEach((txt) => { _%> -<%= txt %> -<% }) %> -<%_ } _%> diff --git a/packages/f2elint/src/config/_prettierrc.js.ejs b/packages/f2elint/src/config/_prettierrc.js.ejs deleted file mode 100644 index 9353f63..0000000 --- a/packages/f2elint/src/config/_prettierrc.js.ejs +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - printWidth: 100, - tabWidth: 2, - semi: true, - singleQuote: true, - trailingComma: 'all', - arrowParens: 'always', -}; diff --git a/packages/f2elint/src/config/_stylelintignore.ejs b/packages/f2elint/src/config/_stylelintignore.ejs deleted file mode 100644 index 7b425fd..0000000 --- a/packages/f2elint/src/config/_stylelintignore.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%_ if (enableStylelint) { _%> -<%_ stylelintIgnores.forEach((txt) => { _%> -<%= txt %> -<% }) %> -<%_ } _%> diff --git a/packages/f2elint/src/config/_stylelintrc.js.ejs b/packages/f2elint/src/config/_stylelintrc.js.ejs deleted file mode 100644 index 1a790f9..0000000 --- a/packages/f2elint/src/config/_stylelintrc.js.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%_ if (enableStylelint) { _%> -module.exports = { - extends: 'stylelint-config-ali', -}; -<%_ } _%> diff --git a/packages/f2elint/src/config/_vscode/extensions.json.ejs b/packages/f2elint/src/config/_vscode/extensions.json.ejs deleted file mode 100644 index 4170a59..0000000 --- a/packages/f2elint/src/config/_vscode/extensions.json.ejs +++ /dev/null @@ -1,7 +0,0 @@ -{ - "recommendations": [ - "dbaeumer.vscode-eslint", - "stylelint.vscode-stylelint", - "esbenp.prettier-vscode" - ] -} diff --git a/packages/f2elint/src/config/_vscode/settings.json.ejs b/packages/f2elint/src/config/_vscode/settings.json.ejs deleted file mode 100644 index afd7853..0000000 --- a/packages/f2elint/src/config/_vscode/settings.json.ejs +++ /dev/null @@ -1,54 +0,0 @@ -{ - <%_ if (enableESLint) { _%> - "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"], - <%_ } _%> - <%_ if (enableStylelint) { _%> - "stylelint.validate": [<%- stylelintExt.map((ext) => `"${ext.replace(/^\./, '')}"`).join(',') %>], - <%_ } _%> - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true<%= enableStylelint || enableMarkdownlint ? ',' : ''%> - <%_ if (enableStylelint) { _%> - "source.fixAll.stylelint": true<%= enableMarkdownlint ? ',' : ''%> - <%_ } _%> - <%_ if (enableMarkdownlint) { _%> - "source.fixAll.markdownlint": true - <%_ } _%> - }, - <%_ if (enablePrettier) { _%> - "editor.defaultFormatter": "esbenp.prettier-vscode", - "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[javascriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[vue]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[css]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[less]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[scss]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[html]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[jsonc]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - <%_ } _%> - "editor.formatOnSave": <%= enablePrettier ? 'true' : 'false' %> -} diff --git a/packages/f2elint/src/config/commitlint.config.js.ejs b/packages/f2elint/src/config/commitlint.config.js.ejs deleted file mode 100644 index 52f3b75..0000000 --- a/packages/f2elint/src/config/commitlint.config.js.ejs +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ['ali'], -}; diff --git a/packages/f2elint/src/config/f2elint.config.js.ejs b/packages/f2elint/src/config/f2elint.config.js.ejs deleted file mode 100644 index 09fd525..0000000 --- a/packages/f2elint/src/config/f2elint.config.js.ejs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - enableESLint: <%= enableESLint %>, - enableStylelint: <%= enableStylelint %>, - enableMarkdownlint: <%= enableMarkdownlint %>, - enablePrettier: <%= enablePrettier %>, -}; diff --git a/packages/f2elint/src/f2elint.ts b/packages/f2elint/src/f2elint.ts old mode 100644 new mode 100755 index e26d779..0678add --- a/packages/f2elint/src/f2elint.ts +++ b/packages/f2elint/src/f2elint.ts @@ -1,168 +1,136 @@ #!/usr/bin/env node -import path from 'path'; -import fs from 'fs-extra'; -import { execSync } from 'child_process'; -import { program } from 'commander'; -import spawn from 'cross-spawn'; -import ora from 'ora'; -import glob from 'glob'; -import init from './actions/init'; -import update from './actions/update'; -import scan from './actions/scan'; -import printReport from './utils/print-report'; -import { getCommitFiles, getAmendFiles } from './utils/git'; -import generateTemplate from './utils/generate-template'; -import npmType from './utils/npm-type'; -import log from './utils/log'; -import { PKG_NAME, PKG_VERSION } from './utils/constants'; - -const cwd = process.cwd(); - -/** - * 若无 node_modules,则帮用户 install(否则会找不到 config) - */ -const installDepsIfThereNo = async () => { - const lintConfigFiles = [].concat( - glob.sync('.eslintrc?(.@(js|yaml|yml|json))', { cwd }), - glob.sync('.stylelintrc?(.@(js|yaml|yml|json))', { cwd }), - glob.sync('.markdownlint(.@(yaml|yml|json))', { cwd }), - ); - const nodeModulesPath = path.resolve(cwd, 'node_modules'); - - if (!fs.existsSync(nodeModulesPath) && lintConfigFiles.length > 0) { - const npm = await npmType; - log.info(`使用项目 Lint 配置,检测到项目未安装依赖,将进行安装(执行 ${npm} install)`); - execSync(`cd ${cwd} && ${npm} i`); - } -}; - -program - .version(PKG_VERSION) - .description( - `${PKG_NAME} 是《阿里巴巴前端规约》的配套 Lint 工具,提供简单的 CLI 和 Node.js API,让项目能够一键接入、一键扫描、一键修复、一键升级,并为项目配置 git commit 卡点,降低项目实施规约的成本`, - ); - -program - .command('init') - .description('一键接入:为项目初始化规约工具和配置,可以根据项目类型和需求进行定制') - .option('--vscode', '写入.vscode/setting.json配置') - .action(async (cmd) => { - if (cmd.vscode) { - const configPath = path.resolve(cwd, `${PKG_NAME}.config.js`); - generateTemplate(cwd, require(configPath), true); - } else { - await init({ - cwd, - checkVersionUpdate: true, - }); + +import { cancel, confirm, intro, isCancel, outro, select, spinner, text } from '@clack/prompts'; +import chalk from 'chalk'; +import { Command } from 'commander'; +import { readFileSync } from 'fs'; +import { dirname, join, resolve } from 'path'; +import { fileURLToPath } from 'url'; +import { f2elint } from '.'; +import { TemplateType } from './types'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8')); + +if (process.argv.length > 2) { + // Non-interactive + + const program = new Command('f2elint'); + + program + .argument('[project]', '项目位置') + .option('--template