本文主要面向新手同学,也欢迎大佬来评论及指导
为了让大家了解项目的每个部分的作用,我会先使用最原始的方式创建项目,从零开始创建一遍,再将其中的一些可以抽离的配置文件改为使用统一的预设配置引入
工具目录
- 开源仓库
- 本文使用gitee,如果不是面向国际的项目,使用gitee,网络问题会比较少
- github操作方式一样,网上资料也很多,想了解的可以自行谷歌,实际上无论你开发用哪个git服务,最终都可以互相同步
- 代码风格检查工具 - eslint
- git相关工具
- 提交信息格式检查工具 commitLint
- 友好的交互式commit命令行工具 commitizen
- CHANGELOG生成工具 conventional-changelog - (在写文章的时候突然发现官方已经推荐使用standard-version作为npm-version和changelog的替代品了😭)
- 构建工具 - gulp
- es版本兼容 - babel
- dts类型描述文件生成 - typescript
- 单元测试工具 - jest
- 基于jsdoc的文档生成工具 - agds-doc
- 这个工具是我封装的,算是夹带私货了吧
1. 新建项目(以node包为例)
- 创建仓库
创建仓库的资料很多,我就不重复了,而且我觉得看文章的大家应该都会~~
- 创建本地项目
仓库创建完成后,可以简单的按照这几个命令完成本地项目的创建
mkdir [your-project] cd [your-project] git init npm init # 按照项目需要完成npm init的命令行交互 git add . git commit -m 'feat: 首次提交' git remote add origin https://gitee.com/[you-gitee-name]/[your-project].git git push -u origin master
2. 创建项目初始配置
- 创建.eidtorconfig文件,统一项目的文本编辑配置
此配置文件在vscode下需要真正生效需要配合
EditorConfig for VS Code
插件使用# .eidtorconfig root = true [*] # 配置适用的文件名 # 你还可以用这种方式配置特定类型的编辑器配置 # ``` # [*.md] # max_line_length = off # trim_trailing_whitespace = false # tab_width = 1 # indent_size = 2 # ``` charset = utf-8 insert_final_newline = false # 是否使文件以一个空白行结尾 trim_trailing_whitespace = true # 是否将行尾空格自动删除 end_of_line = lf #换行符的类型。lf, cr, crlf三种 indent_style = space # 缩进使用tab或者space tab_width = 2 # 缩进为tab时,缩进的宽度 indent_size = 4 # 缩进为space时,缩进的字符数
- 创建eslint配置
- 初始化eslint配置
npx eslint --init
✔ How would you like to use ESLint? · style ✔ What type of modules does your project use? · esm ✔ Which framework does your project use? · none ✔ Does your project use TypeScript? · No / Yes ✔ Where does your code run? · node ✔ How would you like to define a style for your project? · guide ✔ Which style guide do you want to follow? · standard ✔ What format do you want your config file to be in? · JavaScript
- 配置.eslintignore和.gitignore
lib/ node_modules/
- 配置git生命周期检查代码
npm i -D yorkie lint-staged
- 尤大的
yorkie
作为git生命周期脚本配置插件 lint-staged
作为增量文件检查工具(避免lint脚本执行时间过长)
- 尤大的
- 配置
package.json
文件{ + "lint-staged": { + "**/*.{js}": [ + "eslint --fix" + ] + }, + "gitHooks": { + "pre-commit": "lint-staged" + }, }
- 增加json、md文件的格式检查
npm i -D eslint-plugin-jsdoc eslint-plugin-json-format
- 配置.eslintrc.js文件
module.exports = { // 插件注册 + plugins: [ + 'jsdoc', + 'json-format', + ], // 一些默认的配置(降低配置成本) extends: [ 'standard', + 'plugin:jsdoc/recommended', + 'plugin:markdown/recommended', ], + settings: { // 一些基础的json配置 + 'json/sort-package-json': false, + 'json/json-with-comments-files': [], + 'json/ignore-files': [], // 配置jsdoc(vscode仅支持typescript模式,原因懂得都懂~~) + jsdoc: { + mode: 'typescript', + }, + }, rules: { // 一些简单的配置 indent: ['error', 4, { SwitchCase: 1 }], semi: ['error', 'always'], 'comma-dangle': ['error', 'always-multiline'], 'space-before-function-paren': [ 'error', { anonymous: 'always', named: 'never', asyncArrow: 'always' }, ], // jsdoc // 由于jsdoc插件的默认配置过于严格,忽略一些不必要的规则,但是实际上还是有很多规则过于强制,大家也可以自行修改屏蔽 + 'valid-jsdoc': 'off', + 'jsdoc/require-property': 0, + 'jsdoc/require-returns-description': 0, + 'jsdoc/no-undefined-types': 0, }, overrides: [ // 支持md文件内的js及json内容的检查,部分规则需要修改 + { + files: ['**/*.md/*.{js,json}', 'docs/**', 'test/**'], + rules: { + 'no-console': 'off', + 'import/no-unresolved': 'off', + 'no-undef': 'off', + 'no-unused-expressions': 'off', + 'no-unused-vars': 'off', + 'padded-blocks': 'off', + 'eol-last': 'off', }, }, ], };
package.json
lint脚本增加md、json文件检查{ "lint-staged": { + "**/*.{js,json,md}": [ - "**/*.{js}": [ "eslint --fix" ] }, }
- 初始化eslint配置
- git提交信息格式检查
commitlint
- 安装依赖
npm i -D @commitlint/cli @commitlint/config-conventional
- 配置文件
commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'], };
- 将检查脚本加入
package.json
git钩子中{ "gitHooks": { "pre-commit": "lint-staged", + "commit-msg": "commitlint -e $GIT_PARAMS" } }
- 安装依赖
- 命令行交互式填写commit信息
- 安装依赖
npm i -D commitizen cz-conventional-changelog-zh
- 配置
.czrc
// 支持汉化版的交互界面 { "path": "cz-conventional-changelog-zh" }
- 配置
package.json
的gitHooks.prepare-commit-msg
{ "gitHooks": { // agds-gc-has-msg命令可以判断当前commit是否传入-m参数,避免错误调用交互界面 + "prepare-commit-msg": "agds-gc-has-msg && exec < /dev/tty && git cz --hook || true" }, }
- 安装依赖
3. 构建配置
- gulp构建配置
- 安装依赖
npm i -D gulp rimraf gulp-babel @babel/core gulp-typescript typescript merge2 @agds/node-utils
- rimraf用来在每次构建前删除之前的输出
- babel转译js文件,支持一些当前环境未支持的语法
- typescript,生成dts文件,让包的类型声明更加好用,在编辑器中也能有更好的只能提示
- merge2合并ts拆分的Stream流
- @agds/node-utils 一些快捷的node工具类
- 配置构建规则
gulpfile.js
const { series, src, dest, parallel } = require('gulp'); const rimraf = require('rimraf'); const babel = require('gulp-babel'); const ts = require('gulp-typescript'); const merge = require('merge2'); const { FastPath, FastFs } = require('@agds/node-utils'); const path = require('path'); // js项目尽量不要在本地配置[tj]sconfig文件,会影响js库的智能提示和文件跳转 let params = { "compilerOptions": { "target": "ESNext", "module": "commonjs", "allowJs": true, "declaration": true, "allowSyntheticDefaultImports": true, "baseUrl": ".", "moduleResolution": "node", "experimentalDecorators": true, "esModuleInterop": true, "declarationDir": "lib/types", "outDir": "lib" } }; if (FastFs.getPathStatSync(FastPath.getCwdPath('tsconfig.json'))) { params = 'tsconfig.json'; } const tsProject = ts.createProject(params); const _input = FastPath.getCwdPath('src'); const _output = FastPath.getCwdPath('lib'); /** * 清除构建目录 * * @returns {Promise} */ function clean() { return new Promise((resolve) => rimraf(_output, resolve)); }; /** * 构建 * * @returns {merge.Merge2Stream} */ function build() { const tsResult = src(path.join(_input, '**/*.[tj]s')) .pipe(tsProject()); return merge([ tsResult.dts.pipe(dest(path.join(_output, 'types'))), tsResult.js.pipe(babel()).pipe(dest(_output)), ]); } /** * 复制无法构建的文件 * * @returns {NodeJS.ReadWriteStream} */ function cp() { return src(path.join(_input, '**/*.!([tj]s)')).pipe(dest(_output)); } exports.default = series(clean, parallel(build, cp));
- 在
package.json
中添加build脚本{ "scripts": { + "build": "gulp", } }
- 安装依赖
- babel配置
- 安装依赖
npm i -D @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-transform-modules-commonjs @babel/preset-env
- class-properties插件 支持class的属性声明方式
- decorators插件支持可选链语法
a?.b?.[0]?.()
- transform-modules-commonjs 支持将esmodule语法转换为cjs语法
- 配置文件
babel.config.js
module.exports = { presets: [ [ '@babel/preset-env', { targets: { node: '12', }, }, ], ], plugins: [ ['@babel/plugin-proposal-decorators', { legacy: true }], ['@babel/plugin-proposal-class-properties', { loose: false }], ['@babel/plugin-transform-modules-commonjs'], ], };
- eslint配置babel解析
- 安装依赖
npm i -D @babel/eslint-parser
- 修改.eslintrc.js
module.exports = { + parser: '@babel/eslint-parser', + parserOptions: { + ecmaVersion: 12, + babelOptions: { + configFile: './babel.config.js', + }, + }, };
- 安装依赖
- 安装依赖
4. jest配置单测环境
- 下载依赖
npm i -D jest
- 配置
jest.config.js
文件const { FastPath, FastFs } = require('@agds/node-utils'); const pkgPath = FastPath.getCwdPath('package.json'); const path = require('path'); let pkg = {}; if (FastFs.getPathStatSync(pkgPath)) { pkg = require(pkgPath); } const entry = 'src/index.js'; module.exports = { collectCoverage: true, testEnvironment: 'node', roots: [ '<rootDir>/test', ], moduleNameMapper: { [`^${pkg.name}$`]: path.join('<rootDir>', entry), }, coverageThreshold: { global: { // 所有数据都设置100可能很严苛,大家可以根据需要自行更改 statements: 100, branches: 100, functions: 100, lines: 100, }, }, testRegex: 'test/__test__/(.+)\\.(jsx?)$', moduleFileExtensions: ['js', 'jsx', 'json', 'node'], collectCoverageFrom: [ 'src/**/*.{js,jsx}', '!**/node_modules/**', '!**/test/**', ], };
- 新建测试目录
└── test ├── __mock__ // 数据mock目录 └── __test__ // 测试用例目录
package.json
新增test脚本{ "scripts": { "build": "gulp", + "test": "jest" }, }
4. 配置文档自动生成
- 下载依赖
npm i -D @agds/cli-plugin-doc
- 编写配置文件
agds.doc.config.js
/** @type {import('@agds/cli-plugin-doc').RenderOptions} */ module.exports = { // 需使用jsdoc生成文档的文件路径规则 files: ['./src/**/*.js'], // 代码演示文件目录路径规则 codesDir: './test/*', // 代码演示文件路径规则 codesFiles: ['*.js'], // 文档输出路径 output: 'README.md', };
package.json
新增docs脚本{ "scripts": { "test": "jest", + "docs": "agds-doc" }, }
5. 增加changelog配置
默认生成的diff链接格式为github格式,如何修改请自行百度或使用standard-version
- 安装依赖
npm i -D conventional-changelog conventional-changelog-cli
- 在
package.json
中配置脚本{ "scripts": { + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0", } }
到这里,前期项目准备工作就做完了
6. 编写功能代码
这里简单写一个示例
- 在
src/index.js
下添加如下内容/** * a和b相加 * * @param {number} a 数字a * @param {number} b 数字b * @returns {number} a和b相加 */ export function add(a, b) { return a + b; }
- 在
test/__test__/index.js
下编写测试用例import { add } from 'agds-node-template'; import { expect, test } from '@jest/globals'; test('测试1+3', () => { const res = add(1, 3); expect(res).toBe(4); });
- 在命令行中执行
npm run docs
- 你会得到这样的
README.md
-
源码 [your-package-name]是指代你的包名的变量,和version一起自动从
package.json
中获取# [your-package-name] **版本** :[your-package-version] npm库项目的简单模板 ## 快速开始 ### 安装 ```bash npm i [your-package-name] ``` ## 代码演示 ```js import { add } from '[your-package-name]'; import { expect, test } from '@jest/globals'; test('测试1+3', () => { const res = add(1, 3); expect(res).toBe(4); }); ``` ## API文档 <a name="add"></a> ### add(a, b) ⇒ <code>number</code> a和b相加 **性质**: 函数 **返回值**: <code>number</code> - a和b相加 | 参数 | 类型 | 描述 | | --- | --- | --- | | a | <code>number</code> | 数字a | | b | <code>number</code> | 数字b |
-
渲染后的效果
[your-package-name]
版本 :[your-package-version]
npm库项目的简单模板
快速开始
安装
npm i [your-package-name]
代码演示
import { add } from '[your-package-name]'; import { expect, test } from '@jest/globals'; test('测试1+3', () => { const res = add(1, 3); expect(res).toBe(4); });
API文档
add(a, b) ⇒
number
a和b相加
性质: 函数 返回值:
number
- a和b相加参数 类型 描述 a number
数字a b number
数字b
-
6. 基于npm hooks编写脚本代码
在package.json
的scripts字段中新增脚本
{
"scripts": {
+ "docs:postbuild": "npm run docs && (git add . && git commit -m \"docs(readme): 更新文档\" || true)",
+ "commit": "git add . && git commit --no-edit",
+ "postversion": "npm run changelog",
// npm包发布之前先执行test测试用例,确保代码运行正确,再执行build构建代码,最后执行docs:postbuild,生成文档并提交git
+ "prepublishOnly": "npm run test && npm run build && npm run docs:postbuild",
+ "postchangelog": "git add . && git commit -m \"docs(changelog): 更新CHANGELOG\" || true"
}
}
接下来你只需要在每次修改代码后执行以下脚本即可完成发布
npm run commit
npm version patch # 非必选,如果需要修改版本的话最好使用这个方式,他会帮你在git上打上tag,npm version 有很多参数可以选择, npm version -h查看
npm publish # 如果你需要发布npm包的话
git push # 将变更推到git仓库
这个过程中,会自动调用以下功能
- 命令行交互填写message
- 基于git提交记录生成CHANGELOG.md文件
- 执行单测脚本
- 构建源码
- 构建代码文档