diff --git a/packages/cli/src/command.ts b/packages/cli/src/command.ts index d06fa05..3ecf98c 100644 --- a/packages/cli/src/command.ts +++ b/packages/cli/src/command.ts @@ -2,7 +2,7 @@ * @Author: ahwgs * @Date: 2021-04-02 00:22:55 * @Last Modified by: ahwgs - * @Last Modified time: 2021-05-25 11:43:11 + * @Last Modified time: 2021-05-28 14:31:14 */ import path from 'path' @@ -10,7 +10,9 @@ import program from 'commander' import { getPackageJson, chalk } from '@osdoc-dev/avenger-utils' import { build, create } from '@osdoc-dev/avenger-core' import envinfo from 'envinfo' -import { getBuildArguments } from './common' +import { CreateProjectType } from '@osdoc-dev/avenger-shared' +import { getBuildArguments, getCreateArguments } from './common' + // 注册命令 export const registerCommand = () => { const packageJson = getPackageJson(path.join(__dirname, '..')) @@ -32,9 +34,8 @@ export const registerCommand = () => { .command('create [name]') .description('创建一个新项目') .option('--force', '强制覆盖已存在文件夹') - .action(async (name, options) => { - create({ name, options }) - }) + .option('--template', `创建模版项目,可选项:[ ${Object.keys(CreateProjectType).join(', ')} ]`) + .action(() => create(getCreateArguments())) // debug info program diff --git a/packages/cli/src/common.ts b/packages/cli/src/common.ts index 0723be9..70adb63 100644 --- a/packages/cli/src/common.ts +++ b/packages/cli/src/common.ts @@ -2,11 +2,11 @@ * @Author: ahwgs * @Date: 2021-04-01 00:06:20 * @Last Modified by: ahwgs - * @Last Modified time: 2021-05-14 14:02:18 + * @Last Modified time: 2021-05-28 14:24:24 */ -import { semver, error } from '@osdoc-dev/avenger-utils' +import { semver, error, log } from '@osdoc-dev/avenger-utils' import minimist, { ParsedArgs } from 'minimist' -import { ICliOpt } from '@osdoc-dev/avenger-shared' +import { ICliOpt, ICreateOpt, CreateProjectType } from '@osdoc-dev/avenger-shared' // 检查node版本,避免某些 api 不适配 export const checkNodeVersion = (needVerison: string, packageName: string) => { @@ -34,3 +34,27 @@ export const getBuildArguments = () => { }, } as ICliOpt } + +const getTemplate = (template: any) => { + // 没有输入template + if (!template) return '' + const temps = Object.keys(CreateProjectType).map(v => v) + let ret = template + if (!temps.includes(template)) { + log(`当前 template 不在预设范围内,将默认使用 ${CreateProjectType.basic}`) + ret = CreateProjectType.basic + } + return ret +} + +export const getCreateArguments = () => { + const opt = minimist(process.argv.slice(3)) + const { template, force, _ } = opt as ParsedArgs + return { + name: _[0] || '', + options: { + force: force || false, + template: getTemplate(template), + }, + } as ICreateOpt +} diff --git a/packages/config/src/rollup.ts b/packages/config/src/rollup.ts index 92d23c2..996b349 100644 --- a/packages/config/src/rollup.ts +++ b/packages/config/src/rollup.ts @@ -2,7 +2,7 @@ * @Author: ahwgs * @Date: 2021-04-02 21:35:08 * @Last Modified by: ahwgs - * @Last Modified time: 2021-05-26 23:08:07 + * @Last Modified time: 2021-05-28 11:52:28 */ import path from 'path' import { readFileSync, existsSync } from 'fs' @@ -18,7 +18,7 @@ import { IPackageJson, } from '@osdoc-dev/avenger-shared' import { RollupOptions, OutputOptions } from 'rollup' -import { terser } from 'rollup-plugin-terser' +import { terser, Options } from 'rollup-plugin-terser' import url from '@rollup/plugin-url' import svgr from '@svgr/rollup' import typescript2 from 'rollup-plugin-typescript2' @@ -109,13 +109,14 @@ export const getRollupConfig = (opt: IRollupBuildOpt): RollupOptions[] => { const input = path.join(cwd, entry) // rollup-plugin-terser 插件配置 - const terserOpts = { + const terserOpts: Options = { compress: { pure_getters: true, unsafe: true, unsafe_comps: true, - warnings: false, }, + output: { comments: false }, + ecma: 5, } /** 获取插件配置 */ diff --git a/packages/core/package.json b/packages/core/package.json index 6731758..a440abc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -8,7 +8,8 @@ "main": "./lib/index.js", "typings": "./lib/index.d.ts", "files": [ - "lib" + "lib", + "templates" ], "publishConfig": { "access": "public" @@ -29,13 +30,7 @@ "@osdoc-dev/avenger-config": "^0.1.0", "@osdoc-dev/avenger-shared": "^0.1.0", "@osdoc-dev/avenger-utils": "^0.1.0", - "fs-extra": "^10.0.0", - "lodash": "^4.17.21", "rollup": "^2.45.2", "validate-npm-package-name": "^3.0.0" - }, - "devDependencies": { - "@types/fs-extra": "^9.0.11", - "@types/lodash": "^4.14.168" } } diff --git a/packages/core/src/author.ts b/packages/core/src/author.ts new file mode 100644 index 0000000..2a5b3ef --- /dev/null +++ b/packages/core/src/author.ts @@ -0,0 +1,33 @@ +/* + * @Description: 获取项目作者 + * @Author: ahwgs + * @Date: 2021-05-28 20:17:27 + * @Last Modified by: ahwgs + * @Last Modified time: 2021-05-28 20:19:36 + */ +import { shelljs } from '@osdoc-dev/avenger-utils' + +export const setAuthorName = (author: string) => { + shelljs.exec(`npm config set init-author-name "${author}"`, { silent: true }) +} + +export const getAuthorName = (): string => { + let author = '' + + author = shelljs.exec('npm config get init-author-name', { silent: true }).stdout.trim() + if (author) return author + + author = shelljs.exec('git config --global user.name', { silent: true }).stdout.trim() + if (author) { + setAuthorName(author) + return author + } + + author = shelljs.exec('npm config get init-author-email', { silent: true }).stdout.trim() + if (author) return author + + author = shelljs.exec('git config --global user.email', { silent: true }).stdout.trim() + if (author) return author + + return author +} diff --git a/packages/core/src/create.ts b/packages/core/src/create.ts index 0530990..58046a4 100644 --- a/packages/core/src/create.ts +++ b/packages/core/src/create.ts @@ -3,12 +3,11 @@ * @Author: ahwgs * @Date: 2021-05-13 21:18:50 * @Last Modified by: ahwgs - * @Last Modified time: 2021-05-25 14:31:31 + * @Last Modified time: 2021-05-28 21:51:04 */ import path from 'path' -import fs from 'fs-extra' -import { ICreateOpt, IPackageJsonData, IPackageJson } from '@osdoc-dev/avenger-shared' +import { ICreateOpt, IPackageJsonData, IPackageJson, CreateProjectType } from '@osdoc-dev/avenger-shared' import validateProjectName from 'validate-npm-package-name' import { error, @@ -19,8 +18,11 @@ import { info, inquirer, shelljs, - prettier, + fs, + createFolder, + fileGenerator, } from '@osdoc-dev/avenger-utils' +import { getAuthorName, setAuthorName } from './author' interface IValidateProject { validForNewPackages: boolean @@ -29,198 +31,76 @@ interface IValidateProject { } const UserConfig = { - Eslint_Prettier: 'ESLint / Prettier', Jest: 'Jest', - Commitlint: 'Commitlint', Lerna: 'Lerna', - Avenger: 'Avenger', + CommitLint: 'CommitLint', } -const getCzConfig = () => ({ - types: [ - { - value: 'feat', - name: '✨ feat: 新功能', - }, - { - value: 'fix', - name: '🐛 fix: 修复bug', - }, - { - value: 'refactor', - name: '♻️ refactor: 代码重构(既不是新功能也不是改bug)', - }, - { - value: 'chore', - name: '🎫 chore: 修改流程配置', - }, - { - value: 'docs', - name: '📝 docs: 修改了文档', - }, - { - value: 'test', - name: '✅ test: 更新了测试用例', - }, - { - value: 'style', - name: '💄 style: 修改了样式文件', - }, - { - value: 'perf', - name: '⚡️ perf: 新能优化', - }, - { - value: 'revert', - name: '⏪ revert: 回退提交', - }, - { - value: 'ci', - name: '⏪ ci: 持续集成', - }, - { - value: 'build', - name: '⏪ build: 打包', +const setLintConfig = async (deps: string[], packageJsonData: IPackageJsonData, currentTemplate: string) => { + packageJsonData.husky = { + hooks: { + ...packageJsonData?.husky?.hooks, + 'pre-commit': 'lint-staged', }, - ], - scopes: [], - allowCustomScopes: true, - allowBreakingChanges: ['feat', 'fix'], - subjectLimit: 50, - messages: { - type: '请选择你本次改动的修改类型', - customScope: '\n请明确本次改动的范围(可填):', - subject: '简短描述本次改动:\n', - body: '详细描述本次改动 (可填). 使用 "|" 换行:\n', - breaking: '请列出任何 BREAKING CHANGES (可填):\n', - footer: '请列出本次改动关闭的ISSUE (可填). 比如: #31, #34:\n', - confirmCommit: '你确定提交本次改动吗?', - }, -}) - -const createFolder = (filePath: string) => { - if (!fs.existsSync(filePath)) fs.mkdir(filePath) -} - -const setLintConfig = async ( - choose: string[], - targetDir: string, - deps: string[], - packageJsonData: IPackageJsonData -) => { - if (choose.includes(UserConfig.Eslint_Prettier)) { - const prettierData = prettier.format(` - const prettier = require('@osdoc-dev/eslint-config-preset-prettier') - module.exports = { - ...prettier, - } - `) - - const eslintData = { - extends: '@osdoc-dev/eslint-config-preset-ts', - env: { - node: true, - }, - rules: { - '@typescript-eslint/no-var-requires': 0, - 'brace-style': 0, - 'comma-dangle': 0, - 'arrow-parens': 0, - 'unicorn/prevent-abbreviations': 0, - 'space-before-function-paren': 0, - 'global-require': 0, - 'import/no-dynamic-require': 0, - '@typescript-eslint/indent': 0, - indent: 0, - 'no-await-in-loop': 0, - 'unicorn/no-array-for-each': 0, - }, - } - - // @ts-ignore - if (choose.includes(UserConfig.Jest)) eslintData.env.jest = true - packageJsonData.husky = { - hooks: { - ...packageJsonData.husky.hooks, - 'pre-commit': 'lint-staged', - }, - } + } - packageJsonData['lint-staged'] = { - '*.{js,json,md,tsx,ts}': ['prettier --write', 'git add'], - '*.ts?(x)': ['prettier --write', 'eslint --fix', 'git add'], - } + packageJsonData['lint-staged'] = { + '*.{js,json,md,tsx,ts}': ['prettier --write', 'git add'], + '*.ts?(x)': ['prettier --write', 'eslint --fix', 'git add'], + } - const prettierIgnore = '/dist\r\n/node_modules' - const eslintIgnore = '/dist\r\n/node_modules' + deps.push(...['@osdoc-dev/eslint-config-preset-prettier', 'lint-staged', 'prettier']) - await fs.writeFile(`${targetDir}/.eslintrc.js`, `module.exports = ${JSON.stringify(eslintData, null, 2)}`) - await fs.writeFile(`${targetDir}/.prettierrc.js`, prettierData) - await fs.writeFile(`${targetDir}/.prettierignore`, prettierIgnore) - await fs.writeFile(`${targetDir}/.eslintignore`, eslintIgnore) + if (currentTemplate === CreateProjectType.basic) deps.push(...['@osdoc-dev/eslint-config-preset-ts']) + if (currentTemplate === CreateProjectType.react) deps.push(...['@osdoc-dev/eslint-config-preset-react']) +} - deps.push( - ...['@osdoc-dev/eslint-config-preset-prettier', 'lint-staged', '@osdoc-dev/eslint-config-preset-ts', 'prettier'] - ) - } +const getTemplate = async (template: string) => { + if (template) return { currentTemplate: template } + return inquirer.prompt<{ currentTemplate }>({ + type: 'list', + message: '请选择项目模版:', + name: 'currentTemplate', + choices: Object.keys(CreateProjectType).map(v => v) || [], + }) } +const getUserAuthor = () => + inquirer.prompt<{ name }>({ + type: 'input', + message: '请输入项目作者名:', + name: 'name', + }) + // 获取用户配置 const getUserConfig = async () => inquirer.prompt<{ choose }>([ { name: 'choose', type: 'checkbox', - message: '选择预设配置', + message: '选择拓展预设配置', choices: [ - { name: 'ESLint / Prettier', value: UserConfig.Eslint_Prettier }, { name: 'Jest', value: UserConfig.Jest }, - { name: 'Commitlint', value: UserConfig.Commitlint }, { name: 'Lerna', value: UserConfig.Lerna }, - { name: 'Avenger', value: UserConfig.Avenger }, + { name: 'CommitLint', value: UserConfig.CommitLint }, ], }, ]) -const setJestConfig = async (choose: string[], targetDir: string, deps: string[]) => { - if (choose.includes(UserConfig.Jest)) { - const jestData = { - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/__tests__/**/*.test.ts?(x)'], - testPathIgnorePatterns: ['/dist/', '/node_modules/', '(.*).d.ts'], - } - await fs.writeFile(`${targetDir}/jest.config.js`, `module.exports = ${JSON.stringify(jestData, null, 2)}`) - deps.push(...['jest', 'ts-jest', '@types/jest']) - } -} - -const setAvenger = async (targetDir: string, deps: string[], choose: string[]) => { - if (choose.includes(UserConfig.Avenger)) { - const configData = { - esm: 'rollup', - } - await fs.writeFile(`${targetDir}/.avengerrc.ts`, `export default ${JSON.stringify(configData, null, 2)}`) - deps.push(...['@osdoc-dev/avenger-cli']) - } +const setJestConfig = async (choose: string[], ignores: string[], deps: string[]) => { + if (choose.includes(UserConfig.Jest)) deps.push(...['jest', 'ts-jest', '@types/jest']) + else ignores.push(...['jest.config.js']) } -const setCommitlintConfig = async ( +const setCommitLintConfig = async ( choose: string[], - targetDir: string, deps: string[], - packageJsonData: IPackageJsonData + packageJsonData: IPackageJsonData, + ignores: string[] ) => { - if (choose.includes(UserConfig.Commitlint)) { - const lintData = { extends: ['@commitlint/config-conventional'] } - - await fs.writeFile(`${targetDir}/commitlint.config.js`, `module.exports = ${JSON.stringify(lintData, null, 2)}`) - const czData = await getCzConfig() - - await fs.writeFile(`${targetDir}/.cz-config.js`, `module.exports = ${JSON.stringify(czData, null, 2)}`) - + if (choose.includes(UserConfig.CommitLint)) { packageJsonData.husky = { - ...packageJsonData.husky, + ...packageJsonData?.husky, hooks: { 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS', }, @@ -240,40 +120,33 @@ const setCommitlintConfig = async ( 'conventional-changelog-cli', ] ) + } else { + ignores.push(...['commitlint.config.js', '.cz-config.js']) } } -// 配置ts -const setTypescriptConfig = async (targetDir: string, deps: string[]) => { - const filePath = `${targetDir}/tsconfig.json` - await fs.writeJSON( - filePath, - { - compilerOptions: { - target: 'es5', - module: 'commonjs', - strict: true, - esModuleInterop: true, - skipLibCheck: true, - baseUrl: '.', - noUnusedLocals: true, - noUnusedParameters: true, - noFallthroughCasesInSwitch: true, - outDir: './dist', - }, - }, - { spaces: 2 } - ) - deps.push('typescript') -} - const setPackageJsonFile = async ( currentName: string, choose: string[], targetDir: string, packageJsonData: IPackageJsonData, - deps: string[] + deps: string[], + author: string ) => { + info('初始化package.json...') + const filePath = `${targetDir}/package.json` + + const initJson = { + name: currentName, + version: '0.0.1', + license: 'MIT', + author, + main: 'dist/index.js', + files: ['dist', 'src'], + } + + await fs.writeJSON(filePath, { ...initJson }, { spaces: 2 }) + info('开始安装开发依赖...! ') info(`执行:yarn add ${deps.join(' ')} -D`) @@ -281,9 +154,6 @@ const setPackageJsonFile = async ( info('开发依赖安装完成') - if (choose.includes(UserConfig.Lerna)) shelljs.exec('npx lerna init', { cwd: targetDir }) - - const filePath = `${targetDir}/package.json` const json = (await fs.readJSON(filePath)) as IPackageJson const data: IPackageJsonData = { test: 'jest', @@ -291,15 +161,22 @@ const setPackageJsonFile = async ( build: 'yarn clean && avenger build', 'check-types': 'tsc --noEmit', commit: 'git cz', + // eslint-disable-next-line quotes + lint: "prettier --check '**/*.{js,json,md,tsx,ts}'", } - // eslint-disable-next-line quotes - if (choose.includes(UserConfig.Eslint_Prettier)) data.lint = "prettier --check '**/*.{js,json,md,tsx,ts}'" if (choose.includes(UserConfig.Lerna)) data.bootstrap = 'lerna bootstrap' - if (choose.includes(UserConfig.Commitlint)) + if (choose.includes(UserConfig.CommitLint)) data.changelog = 'conventional-changelog -p angular -i CHANGELOG.md -s -r 0' json.scripts = data await fs.writeJSON(filePath, { ...json, ...packageJsonData }, { spaces: 2 }) + + if (choose.includes(UserConfig.Lerna)) { + info('检测到启动lerna') + await shelljs.exec('npx lerna init', { cwd: targetDir }) + info('lerna 初始化完成') + } + info('项目初始化成功!') info(`请进入项目内进行操作 cd ${currentName} && yarn install`) } @@ -308,17 +185,9 @@ const setLernaConfig = async (choose: string[], deps: string[]) => { if (choose.includes(UserConfig.Lerna)) deps.push(...['lerna']) } -const setReadme = async (currentName: string, targetDir: string) => { - const filePath = `${targetDir}/README.md` - - const data = `## ${currentName}` - - await fs.writeFile(filePath, data) -} - export const create = async (opt: ICreateOpt) => { const { name, options } = opt || {} - const { force } = options || {} + const { force, template } = options || {} const cwd = process.cwd() const inCurrent = !name || name === '.' const currentName = inCurrent ? path.relative('../', cwd) : name @@ -327,7 +196,7 @@ export const create = async (opt: ICreateOpt) => { const result: IValidateProject = await validateProjectName(currentName) if (!result.validForNewPackages) { - error(`Invalid project name: "${name}"`) + error(`无效项目名称: "${name}"`) lodash.forEach(result.errors, (err: any) => { error(`Error: ${err}`) @@ -368,7 +237,7 @@ export const create = async (opt: ICreateOpt) => { }, ]) if (action) { - info(`Removing ${targetDir}...`) + info(`删除中 ${targetDir}...`) await fs.remove(targetDir) } else { process.exit(1) @@ -376,21 +245,47 @@ export const create = async (opt: ICreateOpt) => { } } - const deps = [] as string[] + const deps = ['typescript', '@osdoc-dev/avenger-cli'] as string[] const packageJsonData = {} as IPackageJsonData + const ignores = [] as string[] await createFolder(targetDir) - const { choose } = await getUserConfig() + const { currentTemplate } = await getTemplate(template) - shelljs.exec('yarn init -y', { cwd: targetDir }) + let author = getAuthorName() - await setTypescriptConfig(targetDir, deps) - await setCommitlintConfig(choose, targetDir, deps, packageJsonData) - await setLintConfig(choose, targetDir, deps, packageJsonData) - await setJestConfig(choose, targetDir, deps) + if (!author) { + const { name: authorName } = await getUserAuthor() + author = authorName + setAuthorName(authorName) + } + + const { choose = [] } = await getUserConfig() + + await setCommitLintConfig(choose, deps, packageJsonData, ignores) + await setLintConfig(deps, packageJsonData, currentTemplate) + await setJestConfig(choose, ignores, deps) await setLernaConfig(choose, deps) - await setAvenger(targetDir, deps, choose) - await setReadme(currentName, targetDir) - await setPackageJsonFile(currentName, choose, targetDir, packageJsonData, deps) + // 复制公共模版文件 + await fileGenerator({ + target: targetDir, + source: path.join(__dirname, '../templates/common'), + context: { + year: new Date().getFullYear(), + author, + }, + ignores, + }) + // 复制特定模版文件 + await fileGenerator({ + target: targetDir, + source: path.join(__dirname, `../templates/${currentTemplate}`), + context: { + currentName, + enableJest: choose.includes(UserConfig.Jest), + }, + ignores, + }) + await setPackageJsonFile(currentName, choose, targetDir, packageJsonData, deps, author) } diff --git a/packages/core/templates/basic/.avengerrc.ts b/packages/core/templates/basic/.avengerrc.ts new file mode 100644 index 0000000..2c76087 --- /dev/null +++ b/packages/core/templates/basic/.avengerrc.ts @@ -0,0 +1,4 @@ +export default { + esm: 'rollup', + cjs: 'rollup', +} diff --git a/packages/core/templates/basic/.eslintrc.js.tpl b/packages/core/templates/basic/.eslintrc.js.tpl new file mode 100644 index 0000000..3b2d23c --- /dev/null +++ b/packages/core/templates/basic/.eslintrc.js.tpl @@ -0,0 +1,23 @@ +module.exports = { + extends: '@osdoc-dev/eslint-config-preset-ts', + env: { + node: true, + {{#enableJest}} + jest:true + {{/enableJest}} + }, + rules: { + '@typescript-eslint/no-var-requires': 0, + 'brace-style': 0, + 'comma-dangle': 0, + 'arrow-parens': 0, + 'unicorn/prevent-abbreviations': 0, + 'space-before-function-paren': 0, + 'global-require': 0, + 'import/no-dynamic-require': 0, + '@typescript-eslint/indent': 0, + indent: 0, + 'no-await-in-loop': 0, + 'unicorn/no-array-for-each': 0, + }, +} diff --git a/packages/core/templates/basic/README.md.tpl b/packages/core/templates/basic/README.md.tpl new file mode 100644 index 0000000..6fca980 --- /dev/null +++ b/packages/core/templates/basic/README.md.tpl @@ -0,0 +1,21 @@ +## {{currentName}} + +### 开发 + +- 修改 `src/index.ts` 下文件 + +- 调试 + +```bash + +cd && node ./dist/index.js + +``` + +- 打包 + +```bash + +cd && yarn build + +``` diff --git a/packages/core/templates/basic/src/index.ts b/packages/core/templates/basic/src/index.ts new file mode 100644 index 0000000..ec8fc66 --- /dev/null +++ b/packages/core/templates/basic/src/index.ts @@ -0,0 +1 @@ +console.log('basic') diff --git a/packages/core/templates/basic/tsconfig.json b/packages/core/templates/basic/tsconfig.json new file mode 100644 index 0000000..f108e15 --- /dev/null +++ b/packages/core/templates/basic/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "baseUrl": ".", + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "outDir": "./dist" + } +} diff --git a/packages/core/templates/common/.cz-config.js b/packages/core/templates/common/.cz-config.js new file mode 100644 index 0000000..5f02b73 --- /dev/null +++ b/packages/core/templates/common/.cz-config.js @@ -0,0 +1,61 @@ +module.exports = { + types: [ + { + value: 'feat', + name: '✨ feat: 新功能', + }, + { + value: 'fix', + name: '🐛 fix: 修复bug', + }, + { + value: 'refactor', + name: '♻️ refactor: 代码重构(既不是新功能也不是改bug)', + }, + { + value: 'chore', + name: '🎫 chore: 修改流程配置', + }, + { + value: 'docs', + name: '📝 docs: 修改了文档', + }, + { + value: 'test', + name: '✅ test: 更新了测试用例', + }, + { + value: 'style', + name: '💄 style: 修改了样式文件', + }, + { + value: 'perf', + name: '⚡️ perf: 新能优化', + }, + { + value: 'revert', + name: '⏪ revert: 回退提交', + }, + { + value: 'ci', + name: '⏪ ci: 持续集成', + }, + { + value: 'build', + name: '⏪ build: 打包', + }, + ], + scopes: [], + allowCustomScopes: true, + allowBreakingChanges: ['feat', 'fix'], + subjectLimit: 50, + messages: { + type: '请选择你本次改动的修改类型', + customScope: '\n请明确本次改动的范围(可填):', + subject: '简短描述本次改动:\n', + body: '详细描述本次改动 (可填). 使用 "|" 换行:\n', + breaking: '请列出任何 BREAKING CHANGES (可填):\n', + footer: '请列出本次改动关闭的ISSUE (可填). 比如: #31, #34:\n', + confirmCommit: '你确定提交本次改动吗?', + }, +} diff --git a/packages/core/templates/common/.eslintignore b/packages/core/templates/common/.eslintignore new file mode 100644 index 0000000..db4c6d9 --- /dev/null +++ b/packages/core/templates/common/.eslintignore @@ -0,0 +1,2 @@ +dist +node_modules \ No newline at end of file diff --git a/packages/core/templates/common/.prettierignore b/packages/core/templates/common/.prettierignore new file mode 100644 index 0000000..db4c6d9 --- /dev/null +++ b/packages/core/templates/common/.prettierignore @@ -0,0 +1,2 @@ +dist +node_modules \ No newline at end of file diff --git a/packages/core/templates/common/.prettierrc.js b/packages/core/templates/common/.prettierrc.js new file mode 100644 index 0000000..5cb3c55 --- /dev/null +++ b/packages/core/templates/common/.prettierrc.js @@ -0,0 +1,4 @@ +const prettier = require('@osdoc-dev/eslint-config-preset-prettier') +module.exports = { + ...prettier, +} diff --git a/packages/core/templates/common/LICENSE.tpl b/packages/core/templates/common/LICENSE.tpl new file mode 100644 index 0000000..33f0928 --- /dev/null +++ b/packages/core/templates/common/LICENSE.tpl @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) {{year}} {{author}} + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/core/templates/common/commitlint.config.js b/packages/core/templates/common/commitlint.config.js new file mode 100644 index 0000000..98ee7df --- /dev/null +++ b/packages/core/templates/common/commitlint.config.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], +} diff --git a/packages/core/templates/common/jest.config.js b/packages/core/templates/common/jest.config.js new file mode 100644 index 0000000..7815c06 --- /dev/null +++ b/packages/core/templates/common/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/__tests__/**/*.test.ts?(x)'], + testPathIgnorePatterns: ['/dist/', '/node_modules/', '(.*).d.ts'], +} diff --git a/packages/core/templates/react/.eslintrc.js.tpl b/packages/core/templates/react/.eslintrc.js.tpl new file mode 100644 index 0000000..1bcbbdb --- /dev/null +++ b/packages/core/templates/react/.eslintrc.js.tpl @@ -0,0 +1,23 @@ +module.exports = { + extends: '@osdoc-dev/eslint-config-preset-react', + env: { + node: true, + {{#enableJest}} + jest:true + {{/enableJest}} + }, + rules: { + '@typescript-eslint/no-var-requires': 0, + 'brace-style': 0, + 'comma-dangle': 0, + 'arrow-parens': 0, + 'unicorn/prevent-abbreviations': 0, + 'space-before-function-paren': 0, + 'global-require': 0, + 'import/no-dynamic-require': 0, + '@typescript-eslint/indent': 0, + indent: 0, + 'no-await-in-loop': 0, + 'unicorn/no-array-for-each': 0, + }, +} diff --git a/packages/core/templates/react/LICENSE.tpl b/packages/core/templates/react/LICENSE.tpl new file mode 100644 index 0000000..33f0928 --- /dev/null +++ b/packages/core/templates/react/LICENSE.tpl @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) {{year}} {{author}} + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/core/templates/react/tsconfig.json b/packages/core/templates/react/tsconfig.json new file mode 100644 index 0000000..3ca04bd --- /dev/null +++ b/packages/core/templates/react/tsconfig.json @@ -0,0 +1,17 @@ +{ + "include": ["src", "types"], + "compilerOptions": { + "module": "esnext", + "lib": ["dom", "esnext"], + "importHelpers": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "baseUrl": ".", + "jsx": "react", + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "outDir": "./dist" + } +} diff --git a/packages/shared/src/constants.ts b/packages/shared/src/constants.ts index 9ac80e2..155a8d2 100644 --- a/packages/shared/src/constants.ts +++ b/packages/shared/src/constants.ts @@ -2,7 +2,7 @@ * @Author: ahwgs * @Date: 2021-04-01 00:20:14 * @Last Modified by: ahwgs - * @Last Modified time: 2021-05-07 00:02:19 + * @Last Modified time: 2021-05-28 14:10:40 */ import { IBundleOutTypeMapProps } from './types' @@ -22,3 +22,8 @@ export const BundleOutTypeMap = { cjs: 'cjs', umd: 'umd', } as IBundleOutTypeMapProps + +export const CreateProjectType = { + basic: 'basic', + react: 'react', +} diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index d9b026c..b08e381 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -2,7 +2,7 @@ * @Author: ahwgs * @Date: 2021-04-02 09:42:11 * @Last Modified by: ahwgs - * @Last Modified time: 2021-05-26 23:08:14 + * @Last Modified time: 2021-05-28 14:32:36 */ export type TBundleType = 'rollup' | 'babel' @@ -111,6 +111,7 @@ export interface IPackageJson { export interface ICreateOptions { force?: boolean + template?: string } export interface ICreateOpt { diff --git a/packages/utils/package.json b/packages/utils/package.json index fda28ed..614ba4e 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -25,9 +25,12 @@ }, "dependencies": { "chalk": "^4.1.0", + "fs-extra": "^10.0.0", + "glob": "^7.1.7", "inquirer": "^8.0.0", "joi": "^17.4.0", "lodash": "^4.17.21", + "mustache": "^4.2.0", "prettier": "^2.3.0", "rimraf": "^3.0.2", "rollup": "^2.45.2", @@ -37,9 +40,12 @@ "strip-ansi": "^6.0.0" }, "devDependencies": { + "@types/fs-extra": "^9.0.11", + "@types/glob": "^7.1.3", "@types/inquirer": "^7.3.1", "@types/joi": "^17.2.3", "@types/lodash": "^4.14.168", + "@types/mustache": "^4.1.1", "@types/shelljs": "^0.8.8" } } diff --git a/packages/utils/src/file.ts b/packages/utils/src/file.ts index 4fe9ae4..a79a272 100644 --- a/packages/utils/src/file.ts +++ b/packages/utils/src/file.ts @@ -1,21 +1,26 @@ -import { existsSync, readdirSync } from 'fs' import path from 'path' import shell from 'shelljs' +import { fs } from './index' // 获取文件 export function getExistFile({ cwd, files, returnRelative }) { for (const file of files) { const absFilePath = path.join(cwd, file) - if (existsSync(absFilePath)) return returnRelative ? file : absFilePath + if (fs.existsSync(absFilePath)) return returnRelative ? file : absFilePath } } // 删除文件夹 export function deleteFolder(filePath: string) { - if (existsSync(filePath)) { - const files = readdirSync(filePath) + if (fs.existsSync(filePath)) { + const files = fs.readdirSync(filePath) for (const file of files) { const nextFilePath = `${filePath}/${file}` shell.exec(`rm -rf ${nextFilePath}`) } } } + +// 创建文件夹 +export const createFolder = (filePath: string) => { + if (!fs.existsSync(filePath)) fs.mkdir(filePath) +} diff --git a/packages/utils/src/generator.ts b/packages/utils/src/generator.ts new file mode 100644 index 0000000..f87db4a --- /dev/null +++ b/packages/utils/src/generator.ts @@ -0,0 +1,47 @@ +/* + * 从模版生成文件 + * @Author: ahwgs + * @Date: 2021-05-28 17:00:40 + * @Last Modified by: ahwgs + * @Last Modified time: 2021-05-28 21:14:30 + */ + +import path from 'path' +import { createFolder } from './file' +import { glob, Mustache, fs, log } from './index' + +// 复制tpl 并且转义 +const copyTpl = async (opts: { templatePath: string; target: string; context: object }) => { + const tpl = fs.readFileSync(opts.templatePath, 'utf-8') + const content = Mustache.render(tpl, opts.context) + createFolder(path.dirname(opts.target)) + log(`${opts.target} 写入成功!`) + fs.writeFileSync(opts.target, content, 'utf-8') +} + +export const fileGenerator = (opt: { target: string; source: string; context: Object; ignores: string[] }) => { + const { target, source, context, ignores = [] } = opt || {} + const files = glob.sync('**/*', { + cwd: source, + dot: true, + ignore: ['**/node_modules/**'], + }) + + files.forEach(file => { + if (ignores.includes(file)) return + const absFile = path.join(source, file) + if (fs.statSync(absFile).isDirectory()) return + if (file.endsWith('.tpl')) { + copyTpl({ + templatePath: absFile, + target: path.join(target, file.replace(/\.tpl$/, '')), + context, + }) + } else { + const absTarget = path.join(target, file) + createFolder(path.dirname(absTarget)) + log(`${absTarget} 写入成功!`) + fs.copyFileSync(absFile, absTarget) + } + }) +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index a1d2035..ec66908 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -6,22 +6,24 @@ import rimraf from 'rimraf' import shelljs from 'shelljs' import inquirer from 'inquirer' import prettier from 'prettier' -import { createSchema, validateSchema, validateSchemaSync } from './validate' +import Mustache from 'mustache' +import glob from 'glob' +import fs from 'fs-extra' +export * from './validate' export * from './log' export * from './package' export * from './file' +export * from './generator' -export { - semver, - chalk, - slash, - createSchema, - validateSchema, - validateSchemaSync, - lodash, - rimraf, - shelljs, - inquirer, - prettier, -} +export { semver } +export { chalk } +export { slash } +export { lodash } +export { rimraf } +export { shelljs } +export { inquirer } +export { prettier } +export { Mustache } +export { glob } +export { fs } diff --git a/packages/utils/src/log.ts b/packages/utils/src/log.ts index 8a1efec..0795dbc 100644 --- a/packages/utils/src/log.ts +++ b/packages/utils/src/log.ts @@ -2,7 +2,7 @@ * @Author: ahwgs * @Date: 2021-03-31 23:52:11 * @Last Modified by: ahwgs - * @Last Modified time: 2021-05-14 14:31:06 + * @Last Modified time: 2021-05-28 14:24:54 */ import readline from 'readline' @@ -18,7 +18,7 @@ const format = (label: string, message: string) => const chalkTag = message => chalk.bgBlackBright.white.dim(` ${message} `) export const log = (message = '', tag = null) => { - tag ? console.log(format(chalkTag(tag), message)) : console.log(` 🌀${message}`) + tag ? console.log(format(chalkTag(tag), message)) : console.log(` 🌀 ${message}`) } export const info = (message: string, tag = null) => { diff --git a/tsconfig.json b/tsconfig.json index e79b81f..9cbf795 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,7 @@ "packages/*/node_modules", "packages/*/lib", "packages/*/__tests__", - "examples/*/node_modules" + "examples/*/node_modules", + "packages/core/templates/**/*" ] } diff --git a/yarn.lock b/yarn.lock index bf1e6ac..4b56745 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3065,7 +3065,7 @@ dependencies: "@types/node" "*" -"@types/glob@*": +"@types/glob@*", "@types/glob@^7.1.3": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== @@ -3171,6 +3171,11 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== +"@types/mustache@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.1.1.tgz#fcfa2db0cee6261e66f2437dc2fe71e26c7856b4" + integrity sha512-Sm0NWeLhS2QL7NNGsXvO+Fgp7e3JLHCO6RS3RCnfjAnkw6Y1bsji/AGfISdQZDIR/AeOyzkrxRk9jBkl55zdJw== + "@types/node@*": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.0.tgz#557dd0da4a6dca1407481df3bbacae0cd6f68042" @@ -7309,7 +7314,7 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3: +glob@^7.0.3, glob@^7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -10221,6 +10226,11 @@ multimatch@^5.0.0: arrify "^2.0.1" minimatch "^3.0.4" +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"