├── .gitignore ├── src ├── template │ ├── .husky │ │ ├── .gitignore │ │ ├── pre-commit │ │ └── commit-msg │ ├── .prettierignore │ ├── .eslintignore │ ├── code │ │ └── vue-ts │ │ │ ├── assets │ │ │ └── logo.png │ │ │ ├── main.ts │ │ │ ├── store │ │ │ ├── index.ts │ │ │ └── login.ts │ │ │ ├── env.d.ts │ │ │ ├── route │ │ │ └── index.ts │ │ │ ├── components │ │ │ ├── My.vue │ │ │ └── HelloWorld.vue │ │ │ └── App.vue │ ├── .prettierrc │ ├── commitlint.config.js │ └── language-template │ │ └── eslint-vue-ts │ │ └── .eslintrc.js ├── core │ ├── startupProjectRun.ts │ ├── gitInitRun.ts │ ├── replaceTplRun.ts │ ├── index.ts │ ├── eslintRun.ts │ ├── prettierRun.ts │ ├── viteRun.ts │ └── gitHooksRun.ts ├── utils │ ├── plugins.ts │ └── index.ts ├── types │ └── index.d.ts └── index.ts ├── entry └── index.js ├── .idea ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── modules.xml └── vite-eslint-cli.iml ├── example ├── remove.js └── index.js ├── .prettierrc ├── package.json ├── .pnpm-debug.log ├── README.md ├── tsconfig.json └── pnpm-lock.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /dist 3 | -------------------------------------------------------------------------------- /src/template/.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /src/template/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /entry/index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | require('../dist/index') 3 | -------------------------------------------------------------------------------- /src/template/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /src/template/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.css 4 | *.jpg 5 | *.jpeg 6 | *.png 7 | *.gif 8 | *.d.ts 9 | -------------------------------------------------------------------------------- /src/template/.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a572251465/vite-eslint-cli/HEAD/src/template/code/vue-ts/assets/logo.png -------------------------------------------------------------------------------- /example/remove.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { removeSync } = require('fs-extra') 3 | 4 | removeSync(path.resolve(__dirname, 'a')) 5 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | const cp = require('child_process') 2 | 3 | cp.exec('npm --version', (error, output) => { 4 | console.log(error, output) 5 | }) 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "jsxBracketSameLine": true, 6 | "printWidth": 80 7 | } 8 | -------------------------------------------------------------------------------- /src/template/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": false, 3 | "semi": false, 4 | "tabWidth": 2, 5 | "trailingComma": "none", 6 | "useTabs": false, 7 | "endOfLine": "auto" 8 | } 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue" 2 | 3 | import App from "./App.vue" 4 | import store from "./store/index" 5 | import router from "./route" 6 | 7 | createApp(App).use(store).use(router).mount("#app") 8 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from "pinia" 2 | import piniaPluginPersist from "pinia-plugin-persist" 3 | 4 | const store = createPinia() 5 | store.use(piniaPluginPersist) 6 | 7 | export default store 8 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /src/template/commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'type-enum': [ 5 | 2, 6 | 'always', 7 | [ 8 | 'build', 9 | 'chore', 10 | 'ci', 11 | 'feat', 12 | 'docs', 13 | 'feature', 14 | 'fix', 15 | 'perf', 16 | 'refactor', 17 | 'revert', 18 | 'style', 19 | 'test' 20 | ] 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/route/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router" 2 | 3 | import HelloWorld from "../components/HelloWorld.vue" 4 | import My from "../components/My.vue" 5 | 6 | // 配置路由 7 | const routes: RouteRecordRaw[] = [ 8 | { path: "/", component: My }, 9 | { path: "/hello", component: HelloWorld } 10 | ] 11 | 12 | // 创建router 路由 13 | const router = createRouter({ 14 | history: createWebHistory(), 15 | routes 16 | }) 17 | 18 | export default router 19 | -------------------------------------------------------------------------------- /.idea/vite-eslint-cli.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/components/My.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /src/core/startupProjectRun.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description 表示启动项目 4 | */ 5 | import { IExecOptions } from '../types' 6 | import { runCommand } from '../utils' 7 | 8 | class StartupProjectRun { 9 | /** 10 | * @author lihh 11 | * @description 项目入口 12 | * @param options 13 | */ 14 | async apply(options: IExecOptions) { 15 | const { tool, projectPath, callback } = options 16 | 17 | await runCommand(tool, ['dev'], { cwd: projectPath }) 18 | callback && callback() 19 | } 20 | } 21 | 22 | export default new StartupProjectRun() 23 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/store/login.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia" 2 | 3 | const useLoginInfoStore = defineStore("loginInfoStore", { 4 | state: () => { 5 | return { 6 | // github name 7 | name: "lihh", 8 | // github 地址 9 | address: "https://github.com/a572251465" 10 | } 11 | }, 12 | 13 | persist: { 14 | enabled: true, 15 | strategies: [ 16 | { 17 | storage: localStorage, 18 | // 此处设置缓存方式 按字段缓存 19 | paths: ["address"] 20 | } 21 | ] 22 | } 23 | }) 24 | 25 | export { useLoginInfoStore } 26 | -------------------------------------------------------------------------------- /src/core/gitInitRun.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description 进行git初始化 4 | */ 5 | import { IExecOptions } from '../types' 6 | import { runCommand, successLog } from '../utils' 7 | 8 | class GitInitRun { 9 | /** 10 | * @author lihh 11 | * @description 表示程序运行的入口 12 | * @param options 表示传递的参数 13 | */ 14 | async apply(options: IExecOptions) { 15 | const { projectPath, callback } = options 16 | 17 | // 执行初始命令 18 | await runCommand('git', ['init'], { cwd: projectPath }) 19 | 20 | successLog(`3. git init successfully`) 21 | callback && callback() 22 | } 23 | } 24 | 25 | export default new GitInitRun() 26 | -------------------------------------------------------------------------------- /src/core/replaceTplRun.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description 进行模板替换 4 | */ 5 | import { IExecOptions } from '../types' 6 | import { copyFile, joinPath, removeFileAndDir } from '../utils' 7 | 8 | class ReplaceTplRun { 9 | async apply(options: IExecOptions) { 10 | const { tpl, callback, projectPath } = options 11 | 12 | const basePath = joinPath(__dirname, `./template/code/${tpl}`) 13 | // 删除原来src下内容 14 | removeFileAndDir(joinPath(projectPath, 'src')) 15 | // 进行文件复制 16 | copyFile(basePath, joinPath(projectPath, 'src')) 17 | 18 | callback && callback() 19 | } 20 | } 21 | 22 | export default new ReplaceTplRun() 23 | -------------------------------------------------------------------------------- /src/template/language-template/eslint-vue-ts/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | es2021: true, 6 | node: true 7 | }, 8 | 9 | extends: [ 10 | 'plugin:vue/vue3-recommended', 11 | 'eslint:recommended', 12 | '@vue/typescript/recommended' 13 | ], 14 | parserOptions: { 15 | parser: '@typescript-eslint/parser', 16 | ecmaVersion: 2021 17 | }, 18 | rules: { 19 | 'no-unused-vars': 'off', 20 | 'vue/no-unused-vars': 'off', 21 | '@typescript-eslint/no-unused-vars': 'off' 22 | }, 23 | globals: { 24 | defineProps: 'readonly', 25 | defineEmits: 'readonly' 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/plugins.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description 可以选择的插件市场 将来是要做版本匹配的 4 | */ 5 | 6 | const allPlugins: Record = { 7 | 'eslint-vue-ts': 'eslint, eslint-plugin-vue, @typescript-eslint/parser, @typescript-eslint/eslint-plugin, @vue/eslint-config-typescript', 8 | 'prettier-vue-ts': 'prettier, eslint-plugin-prettier, @vue/eslint-config-prettier' 9 | } 10 | 11 | /** 12 | * @author lihh 13 | * @description 用来匹配版本是否存在 14 | * @param plugins 需要匹配的插件 15 | */ 16 | const matchingVersion = (plugins: string[]) => plugins 17 | 18 | /** 19 | * @author lihh 20 | * @description 根据key 获取对应的插件 21 | * @param key 22 | */ 23 | const getDependentPlugins = (key: string): string[] => { 24 | const values = allPlugins[key] || '' 25 | const plugins = values === '' ? [] : matchingVersion(values.split(', ')) 26 | return plugins 27 | } 28 | 29 | 30 | export default getDependentPlugins 31 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 21 | 46 | -------------------------------------------------------------------------------- /src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface IPackageInfo { 2 | name: string, 3 | version: string, 4 | description: string, 5 | author: string 6 | } 7 | 8 | export interface ICmdOptions { 9 | y?: boolean, 10 | template?: string 11 | } 12 | 13 | export interface ICommanderOptions { 14 | keyword: string, 15 | description: string 16 | } 17 | 18 | export interface IExecOptions { 19 | tpl: string, 20 | projectName: string, 21 | tool: string, 22 | isPinia: boolean, 23 | isVueRouter: boolean, 24 | rootPath: string, 25 | projectPath: string 26 | callback?: (...argv: any) => any 27 | } 28 | 29 | export interface ISpawnOptions { 30 | cwd?: string 31 | env?: object 32 | argv0?: string 33 | stdio?: string 34 | detach?: boolean 35 | uid?: number 36 | gid?: number 37 | shell?: boolean | string 38 | timeout?: number 39 | } 40 | 41 | export interface IEslintrc { 42 | root?: boolean, 43 | env?: Record 44 | extends?: string[] 45 | } 46 | -------------------------------------------------------------------------------- /src/template/code/vue-ts/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 36 | 37 | 54 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | import { IExecOptions } from '../types' 2 | import viteRun from './viteRun' 3 | import gitInitRun from './gitInitRun' 4 | import { successLog } from '../utils' 5 | import eslintRun from './eslintRun' 6 | import prettierRun from './prettierRun' 7 | import gitHooksRun from './gitHooksRun' 8 | import startupProjectRun from './startupProjectRun' 9 | import replaceTplRun from './replaceTplRun' 10 | 11 | const execStacks = [viteRun, gitInitRun, eslintRun, prettierRun, gitHooksRun, replaceTplRun] 12 | /** 13 | * @author lihh 14 | * @description 开始执行命令 15 | * @param options 通过shell 以及入口 收集的参数 16 | */ 17 | const run = async (options: IExecOptions) => { 18 | const len = execStacks.length 19 | 20 | // 表示成功的回调 21 | async function success() { 22 | successLog(`end: Project initialization succeeded`) 23 | 24 | // 启动后置钩子 25 | await startupProjectRun.apply(options) 26 | } 27 | 28 | async function next(index: number) { 29 | const instance = execStacks[index] 30 | await instance.apply({ 31 | ...options, callback: async (flags?: string) => { 32 | const currentIndex = index + 1 33 | // 如果flags存在值 表示cli中途中断 34 | if (currentIndex === len || (flags && flags === 'end')) { 35 | await success() 36 | } else { 37 | await next(currentIndex) 38 | } 39 | } 40 | }) 41 | } 42 | 43 | await next(0) 44 | } 45 | 46 | export default run 47 | -------------------------------------------------------------------------------- /src/core/eslintRun.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description 需要执行的eslint命令处理 4 | */ 5 | import { IExecOptions } from '../types' 6 | import getDependentPlugins from '../utils/plugins' 7 | import { 8 | copyFile, 9 | joinPath, 10 | resolvePath, 11 | runCommand, 12 | successLog, 13 | warningLog 14 | } from '../utils' 15 | 16 | class EslintRun { 17 | /** 18 | * @author lihh 19 | * @description 命令执行的入口 20 | * @param options 通过外界以及shell收集的参数 21 | */ 22 | async apply(options: IExecOptions) { 23 | const { tpl, callback, tool, projectPath } = options 24 | const flags = `eslint-${tpl}` 25 | 26 | // 获取可以执行的模板 27 | const plugins = getDependentPlugins(flags) 28 | if (plugins.length === 0) { 29 | warningLog( 30 | `该脚手架暂时不支持<${flags}>, 后续逐渐支持,请持续关注. 但是vite创建的项目已经可以使用` 31 | ) 32 | callback && callback('end') 33 | return 34 | } 35 | 36 | // 执行command命令 37 | await runCommand( 38 | tool, 39 | [tool === 'yarn' ? 'add' : 'install'].concat(plugins).concat(['-D']), 40 | { cwd: projectPath } 41 | ) 42 | // 复制配置文件 43 | const basePath = resolvePath('./template') 44 | 45 | // 开始复制文件 46 | copyFile( 47 | joinPath(basePath, '.eslintignore'), 48 | joinPath(projectPath, '.eslintignore') 49 | ) 50 | copyFile(joinPath(basePath, `language-template/${flags}`), projectPath) 51 | 52 | successLog('4. eslint configuration succeeded') 53 | callback && callback() 54 | } 55 | } 56 | 57 | export default new EslintRun() 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-eslint-cli", 3 | "version": "1.0.1", 4 | "description": "基于vite + vue3/ vite + react等, 实现eslint prettier部署,内置vite vue3运行方式,执行代码构建以及eslint部署一体. 内置(pinia,vue-router等)", 5 | "bin": { 6 | "vite-eslint-cli": "entry/index.js" 7 | }, 8 | "scripts": { 9 | "build": "rollup -c build/rollup.config.js" 10 | }, 11 | "files": [ 12 | "dist", 13 | "entry" 14 | ], 15 | "keywords": [ 16 | "rollup", 17 | "vue3", 18 | "react", 19 | "eslint", 20 | "commitlint", 21 | "prettier" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/a572251465/vite-eslint-cli.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/a572251465/vite-eslint-cli/issues" 29 | }, 30 | "homepage": "https://github.com/a572251465/vite-eslint-cli", 31 | "author": "lihaohao", 32 | "license": "MIT", 33 | "devDependencies": { 34 | "@rollup/plugin-commonjs": "^21.0.2", 35 | "@rollup/plugin-json": "^4.1.0", 36 | "@rollup/plugin-node-resolve": "^13.1.3", 37 | "@types/commander": "^2.12.2", 38 | "@types/node": "^17.0.21", 39 | "@types/prompts": "^2.0.14", 40 | "colors": "^1.4.0", 41 | "commander": "^9.0.0", 42 | "eslint": "^8.10.0", 43 | "fs-extra": "^10.0.1", 44 | "prettier": "^2.5.1", 45 | "prompts": "^2.4.2", 46 | "rollup": "^2.69.0", 47 | "rollup-plugin-copy": "^3.4.0", 48 | "rollup-plugin-delete": "^2.0.0", 49 | "rollup-plugin-typescript2": "^0.31.2", 50 | "tslib": "^2.3.1", 51 | "typescript": "^4.6.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.pnpm-debug.log: -------------------------------------------------------------------------------- 1 | { 2 | "0 debug pnpm:scope": { 3 | "selected": 1 4 | }, 5 | "1 debug pnpm:package-manifest": { 6 | "initial": { 7 | "name": "vite-eslint-cli", 8 | "version": "1.0.0", 9 | "description": "", 10 | "main": "index.js", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC" 17 | }, 18 | "prefix": "F:\\my-project\\vite-eslint-cli" 19 | }, 20 | "2 debug pnpm:context": { 21 | "currentLockfileExists": false, 22 | "storeDir": "F:\\.pnpm-store\\v3", 23 | "virtualStoreDir": "F:\\my-project\\vite-eslint-cli\\node_modules\\.pnpm" 24 | }, 25 | "3 debug pnpm:stage": { 26 | "prefix": "F:\\my-project\\vite-eslint-cli", 27 | "stage": "resolution_started" 28 | }, 29 | "4 error pnpm": { 30 | "code": "ERR_PNPM_FETCH_404", 31 | "hint": "perttier is not in the npm registry, or you have no permission to fetch it.\n\nNo authorization header was set for the request.", 32 | "request": { 33 | "url": "https://registry.npm.taobao.org/perttier" 34 | }, 35 | "response": { 36 | "size": 0 37 | }, 38 | "pkgName": "perttier", 39 | "pkgsStack": [], 40 | "err": { 41 | "name": "pnpm", 42 | "message": "GET https://registry.npm.taobao.org/perttier: Not Found - 404", 43 | "code": "ERR_PNPM_FETCH_404", 44 | "stack": "pnpm: GET https://registry.npm.taobao.org/perttier: Not Found - 404\n at RetryOperation._fn (C:\\Users\\Lenovo\\AppData\\Roaming\\npm\\node_modules\\pnpm\\dist\\pnpm.cjs:85680:19)\n at processTicksAndRejections (internal/process/task_queues.js:95:5)" 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Vite logo 4 | 5 |

6 |
7 | 8 | # vite-eslint-cli 9 | 10 | > 基于vite + vue3/ vite + react等 实现eslint prettier部署,内置vite vue3运行方式。执行代码构建以及eslint部署一体 11 | 12 | - 💡 基于vite 实现了项目以及规范一体化部署 13 | - ⚡️可以从0 到 1快速部署 14 | - 🛠️ 内置实现vue3/ ts/ eslint/ prettier/ commitlint/ husky/ pinia持久化/ vue-router 15 | - 📦 可以快速实现从代码以及规范的搭建 16 | 17 | ## 与vite vue3的不同 18 | 19 | - 使用vite 创建vue3 + ts项目只会出现一个简单模板, 需要手动`npm install`, 以及配置基本插件以及规范,例如:pinia, eslint等 20 | - vite-eslint-cli 能做什么??? 21 | - 💡 内置vite 创建方式,和单独使用vite没有什么不同 22 | - ⚡️ 内置了eslint规范,例如:prettier, eslint, commitlint, husky 23 | - 🛠️ 提供了vue3 的全家桶配置。例如: pinia, pinia-plugin-persist, vue-router 24 | - 📦 提供了pinia 持久化方案 25 | - 🔩 内置了vue-router以及pinia 案例 26 | - 🔑 可以用--template 参数 来替代vite --template参数 27 | 28 | ## 支持范围 29 | 30 | | 功能 | 是否支持 | 31 | | ------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | 32 | | vue3 + ts | 支持 | 33 | | vue3 + js | 待支持 | 34 | | react + ts | 待支持 | 35 | | react + js | 待支持 | 36 | 37 | ## 使用 38 | 39 | > $ npx vite-eslint-cli myapp 40 | >
41 | > $ npx vite-eslint-cli myapp --template vue-ts 42 | 43 | ## 注意 44 | 45 | - 默认版本就是vue3 + ts 46 | 47 | ## QA 48 | 49 | - 如果你是高手 50 | - 使用插件可以免去你配置规范的步骤,快速开发业务代码 51 | - 如果你是新手 52 | - 让你从0 到1快速实现项目,内置“全家桶” 案例能够快速学习 53 | -------------------------------------------------------------------------------- /src/core/prettierRun.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description 表示prettier处理 4 | */ 5 | import { IEslintrc, IExecOptions } from '../types' 6 | import getDependentPlugins from '../utils/plugins' 7 | import { copyFile, joinPath, resolvePath, runCommand, successLog, warningLog } from '../utils' 8 | 9 | const fs = require('fs') 10 | 11 | class PrettierRun { 12 | /** 13 | * @author lihh 14 | * @description 表示功能插件的运行入口 15 | * @param options 参数 16 | */ 17 | async apply(options: IExecOptions) { 18 | const { projectPath, callback, tpl, tool } = options 19 | const flags = `prettier-${tpl}` 20 | 21 | // 获取插件 22 | const plugins = getDependentPlugins(flags) 23 | if (plugins.length === 0) { 24 | warningLog(`该脚手架暂时不支持<${flags}>, 后续逐渐支持,请持续关注. 但是vite创建的项目已经可以使用`) 25 | callback && callback('end') 26 | return 27 | } 28 | 29 | // 开始注册插件 30 | await runCommand(tool, [tool === 'yarn' ? 'add' : 'install'].concat(plugins).concat(['-D']), { cwd: projectPath }) 31 | 32 | // 开始复制文件 33 | const basePath = resolvePath('./template') 34 | const fileArr = ['.prettierrc', '.prettierignore'] 35 | fileArr.forEach(fileName => { 36 | copyFile(joinPath(basePath, fileName), joinPath(projectPath, fileName)) 37 | }) 38 | 39 | // 开始读取.eslintrc.js 进行文件修改 40 | const fileDir = joinPath(projectPath, '.eslintrc.js') 41 | const eslintrcConfig = require(fileDir) as IEslintrc 42 | eslintrcConfig.extends!.push('prettier') 43 | if (tpl.startsWith('vue')) eslintrcConfig.extends!.push('@vue/eslint-config-prettier') 44 | // 开始写文件 45 | fs.writeFileSync(fileDir, `module.exports = ${JSON.stringify(eslintrcConfig, null, ' ')}`, { encoding: 'utf-8' }) 46 | 47 | successLog('5. prettier configuration succeeded') 48 | callback && callback() 49 | } 50 | } 51 | 52 | export default new PrettierRun() 53 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description 执行commander入口 4 | */ 5 | 6 | import { Command } from 'commander' 7 | import { getCommanderOptions, getConfigFile } from './utils' 8 | import { ICmdOptions, IExecOptions } from './types' 9 | import prompts from 'prompts' 10 | import run from './core' 11 | 12 | const path = require('path') 13 | 14 | /** 15 | * @author lihh 16 | * @description 表示闻讯函数 17 | * @param tpl 表示模板 18 | */ 19 | const promptHandle = async (tpl: string) => { 20 | let baseOptions = [{ 21 | type: 'select', 22 | name: 'tool', 23 | message: 'please select a running tool', 24 | choices: ['npm', 'yarn', 'pnpm'].map(item => ({ title: item, value: item })) 25 | }] as prompts.PromptObject[] 26 | 27 | const res = await prompts(baseOptions) 28 | 29 | return { ...res, isPinia: true, isVueRouter: true } 30 | } 31 | 32 | const program = new Command() 33 | // 获取package 文件配置信息 34 | const configInfo = getConfigFile() 35 | // 获取commander options 配置信息 36 | const commanderOptions = getCommanderOptions() 37 | 38 | program 39 | .name(configInfo.name) 40 | .description(configInfo.description) 41 | .version(configInfo.version) 42 | 43 | program.argument('', 'Please enter the project name ') 44 | 45 | commanderOptions.forEach(item => { 46 | program.option(item.keyword, item.description) 47 | }) 48 | 49 | program.action(async (projectName: string) => { 50 | const params = program.opts() as ICmdOptions 51 | 52 | // 是否快速创建 53 | const isY = params.y || false 54 | // 表示使用模板 55 | const tpl = isY ? 'vue-ts' : params.template || 'vue-ts' 56 | 57 | // 选择执行工具 58 | const tool = await promptHandle(tpl) 59 | const rootPath = process.cwd() 60 | const projectPath = path.resolve(rootPath, projectName) 61 | const options = { tpl, ...tool, rootPath, projectName, projectPath } as IExecOptions 62 | 63 | // 开始运行命令 64 | await run(options) 65 | }) 66 | 67 | program.parse() 68 | -------------------------------------------------------------------------------- /src/core/viteRun.ts: -------------------------------------------------------------------------------- 1 | import { IExecOptions } from '../types' 2 | import { exec, runCommand, successLog } from '../utils' 3 | 4 | /** 5 | * @author lihh 6 | * @description 执行vite处理 7 | */ 8 | class ViteRun { 9 | async apply(options: IExecOptions) { 10 | const { tpl, tool, projectName, rootPath, callback, projectPath } = options 11 | 12 | // 表示npm版本 13 | let npmVersion = '' 14 | if (tool === 'npm') { 15 | try { 16 | npmVersion = await exec('npm --version') 17 | } catch (e) { 18 | console.log(e) 19 | process.exit(1) 20 | } 21 | } 22 | 23 | // 表示命令参数 24 | const commandOptions = [] 25 | // 1. 初始化命令 26 | if (tool === 'npm') { 27 | commandOptions.push('init', 'vite@latest') 28 | } else { 29 | commandOptions.push('create', 'vite') 30 | } 31 | // 2. 添加项目名称 32 | commandOptions.push(projectName) 33 | // 3. 添加模板名称 34 | if (tool === 'pnpm' || (tool === 'npm' && npmVersion.startsWith('7'))) { 35 | commandOptions.push('--') 36 | } 37 | 38 | // 4. 添加结尾内容 39 | commandOptions.push('--template', tpl) 40 | 41 | // 执行命令 42 | await runCommand(tool, commandOptions, { cwd: rootPath }) 43 | 44 | // 表示其余的插件 45 | const otherPlugins = [] 46 | if (options.isPinia) otherPlugins.push('pinia', 'pinia-plugin-persist') 47 | if (options.isVueRouter) otherPlugins.push('vue-router') 48 | if (otherPlugins.length > 0) { 49 | await runCommand( 50 | tool, 51 | [tool === 'yarn' ? 'add' : 'install'].concat( 52 | otherPlugins.concat(['-S']) 53 | ), 54 | { cwd: projectPath } 55 | ) 56 | } 57 | 58 | successLog(`1. Vite generated project<${projectName}> successfully `) 59 | 60 | // 进行依赖注册 61 | await runCommand(tool, ['install'], { cwd: projectPath }) 62 | successLog( 63 | `2. project<${projectName}> dependencies installed successfully ` 64 | ) 65 | 66 | callback && callback() 67 | } 68 | } 69 | 70 | export default new ViteRun() 71 | -------------------------------------------------------------------------------- /src/core/gitHooksRun.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author lihh 3 | * @description git hook执行过程 4 | */ 5 | import { IExecOptions } from '../types' 6 | import { copyFile, joinPath, resolvePath, runCommand, successLog } from '../utils' 7 | 8 | const fs = require('fs') 9 | 10 | class GitHooksRun { 11 | /** 12 | * @author lihh 13 | * @description git hook项目入口 14 | * @param options 15 | */ 16 | async apply(options: IExecOptions) { 17 | const { projectPath, tool, callback } = options 18 | 19 | // 开始注册执行插件 20 | await runCommand('npx', ['husky', 'install'], { cwd: projectPath }) 21 | await runCommand(tool, [tool === 'yarn' ? 'add' : 'install', 'husky', 'lint-staged', '-D'], { cwd: projectPath }) 22 | 23 | // 读取package配置文件 修改内容 24 | try { 25 | const packagePath = joinPath(projectPath, 'package.json') 26 | const packageInfo = require(packagePath) 27 | const scripts = (packageInfo.scripts || (packageInfo.scripts = {})) 28 | scripts.lint = 'eslint --ext .ts,.tsx,.vue,.js,.jsx src/** --no-error-on-unmatched-pattern --quiet' 29 | scripts['lint:fix'] = 'eslint --ext .ts,.tsx,.vue,.js,.jsx src/** --no-error-on-unmatched-pattern --quiet --fix' 30 | scripts.prepare = 'husky install' 31 | 32 | packageInfo['lint-staged'] = { 33 | '*.{ts,vue,tsx,js,jsx}': 'eslint --cache --fix' 34 | } 35 | // 开始重新写文件 36 | fs.writeFileSync(packagePath, JSON.stringify(packageInfo, null, ' ')) 37 | 38 | // 安装commitlint插件 39 | await runCommand(tool, [tool === 'yarn' ? 'add' : 'install', '@commitlint/cli', '@commitlint/config-conventional', '-D'], { cwd: projectPath }) 40 | 41 | // 开始复制commitlint文件 42 | const basePath = resolvePath('./template') 43 | copyFile(joinPath(basePath, 'commitlint.config.js'), joinPath(projectPath, 'commitlint.config.js')) 44 | copyFile(joinPath(basePath, '.husky'), joinPath(projectPath, '.husky')) 45 | 46 | successLog('6. commitlint configuration succeeded') 47 | callback && callback() 48 | } catch (e) { 49 | console.log(e) 50 | process.exit(1) 51 | } 52 | } 53 | } 54 | 55 | export default new GitHooksRun() 56 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 18 | 19 | 28 | 29 | 32 | 33 | 34 | 38 | 39 | 40 | 47 | 48 | 55 | 56 | 63 | 64 | 69 | 70 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { green, yellow } from 'colors' 2 | 3 | const path = require('path') 4 | const fs = require('fs') 5 | const cp = require('child_process') 6 | import { ICommanderOptions, IPackageInfo, ISpawnOptions } from '../types' 7 | import { copySync, moveSync, removeSync } from 'fs-extra' 8 | 9 | /** 10 | * @author lihh 11 | * @description 获取当前文件的配置文件 12 | */ 13 | const getConfigFile = (): IPackageInfo => { 14 | const readPath = path.resolve(__dirname, '../package.json') 15 | const content = fs.readFileSync(readPath, 'utf-8') 16 | return JSON.parse(content) as IPackageInfo 17 | } 18 | 19 | /** 20 | * @author lihh 21 | * @description 返回一个commander描述信息 22 | */ 23 | const getCommanderOptions = (): ICommanderOptions[] => { 24 | return [ 25 | { 26 | keyword: '-tpl, --template