rollup + typescript 打包工具库(参考redux)

1,057 阅读3分钟

初始化

mkdir <projectName>
cd <projectName>
touch index.ts
npm init -y
git init
touch .gitignore

Typescript

# 安装typescript
npm i typescript -D --save
# 初始化typescript配置,生成tsconfig.json
npx tsc -init

tsconfig.json

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Language and Environment */
    "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    "lib": [
      "DOM",
      "ES2017"
    ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,

    /* Modules */
    "module": "ESNext" /* Specify what module code is generated. */,
    "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
    "baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
  
    /* Emit */
    "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
    "sourceMap": true /* Create source map files for emitted JavaScript files. */,
    "declarationDir": "./types" /* Specify the output directory for generated declaration files. */,

    /* Interop Constraints */
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

    /* Type Checking */
    "strict": true /* Enable all strict type-checking options. */,
    "noUnusedLocals": true /* Enable error reporting when a local variables aren't read. */,
    "noUnusedParameters": true /* Raise an error when a function parameter isn't read */,

    /* Completeness */
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Babel

# babel解析核心
npm i @babel/core -D -S
# babel运行时依赖 babel预设置
npm i @babel/plugin-transform-runtime @babel/preset-env -D -S
touch .babelrc.js

.babelrc.js

module.exports = {
  presets: [
    [
      "@babel/env",
      {
        targets: {
          browsers: ["ie >= 11"],
        },
        useBuiltIns: "usage",
        corejs: 3,
        modules: "auto",
        loose: true,
      },
    ],
  ],
};

ESLint

# eslint ts解析器 ts规则插件
npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D -S
touch .eslintrc.js
touch .eslintignore

.eslintrc.js

module.exports = {
  // 规则继承
  extends: ["plugin:@typescript-eslint/recommended"],
  // 解析器
  parser: "@typescript-eslint/parser",
  // ts插件
  plugins: ["@typescript-eslint"],
  // 规则定制
  rules: {
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-expressions": "off",
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        vars: "all", //全部声明包括全局
        args: "all", //一次声明全位置
        ignoreRestSiblings: true, //忽略rest
        argsIgnorePattern: "^_", //通过lint正则
        varsIgnorePattern: "^_", //通过lint正则
      },
    ],
  },
};

.eslintignore

/es
/lib
/node_modules

Prettier

格式化工具,配合eslint

npm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev 
touch .prettierrc

.prettierrc

{
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 80,
  "overrides": [
    {
      "files": ".prettierrc",
      "options": { "parser": "json" }
    }
  ]
}

修改.eslintrc.js

module.exports = {
  extends: [
    'plugin:@typescript-eslint/recommended',
    // 关闭和eslint冲突,配置prettier插件,相关规则预设
    'plugin:prettier/recommended',
  ],
  ...
};

lint-staged/husky

#前置安装husky和lint-staged钩子
npx mrm@2 lint-staged

如果全局环境有安装nvm之类的nodejs管理工具的话,需要修改${project}/.husky/pre-commit文件,以保证husky脚本正确执行

#项目根目录
cd ${project}/.husky

pre-commit

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

export NVM_DIR="$HOME/.nvm/nvm.sh"
. "$(dirname $NVM_DIR)/nvm.sh"

export NVM_DIR="$HOME/.nvm"
a=$(nvm ls | grep 'node')
b=${a#*(-> }
v=${b%%[)| ]*}

export PATH="$NVM_DIR/versions/node/$v/bin:$PATH"

npx lint-staged

Rollup

# 安装rollup 路径解析插件
npm i rollup  @rollup/plugin-node-resolve -D --save
# 安装typescript插件
npm i rollup-plugin-typescript2 -D --save
# 安装babel插件
npm i @rollup/plugin-babel -D --save
# 安装rimraf插件清理产物
npm i rimraf -D --save
# 串行运行方案
npm i npm-run-all -D --save
# 新建rollup配置文件
touch rollup.config.js

rollup.config.js

import nodeResolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';
import typescript from 'rollup-plugin-typescript2';

import pkg from './package.json';
const babelRuntimeVersion = pkg.peerDependencies['@babel/runtime'].replace(
  /^[^0-9]*/,
  '',
);
const extensions = ['.ts'];
const noDeclarationFiles = { compilerOptions: { declaration: false } };
// 生成external配置
const makeExternalPredicate = (externalArr) => {
  if (externalArr.length === 0) {
    return () => false;
  }
  const pattern = new RegExp(`^(${externalArr.join('|')})($|/)`);
  return (id) => pattern.test(id);
};

export default [
  //Commonjs
  {
    input: 'src/index.ts',
    output: {
      file: 'lib/index.js',
      format: 'cjs',
      indent: false,
    },
    external: makeExternalPredicate([
      ...Object.keys(pkg.dependencies || {}),
      ...Object.keys(pkg.peerDependencies || {}),
    ]),
    plugins: [
      nodeResolve({ extensions }),
      typescript({
        useTsconfigDeclarationDir: true,
      }),
      babel({
        extensions,
        plugins: [
          ['@babel/plugin-transform-runtime', { version: babelRuntimeVersion }],
        ],
        babelHelpers: 'runtime',
      }),
    ],
  },
  // ES
  {
    // 入口文件
    input: 'src/index.ts',
    // 输出文件 es 无缩进
    output: { file: 'es/index.js', format: 'es', indent: false },
    // 外部扩展,不打包
    external: makeExternalPredicate([
      ...Object.keys(pkg.dependencies || {}),
      ...Object.keys(pkg.peerDependencies || {}),
    ]),
    // 插件
    // 一些插件重复功能
    // ts路径解析(nodeResolve(extensions)/typescript/babel(extensions))
    // ts转js typescript/babel
    plugins: [
      // rollup路径解析
      nodeResolve({
        extensions,
      }),
      typescript({ tsconfigOverride: noDeclarationFiles }),
      babel({
        // 转译目标文件
        extensions,
        plugins: [
          [
            '@babel/plugin-transform-runtime',
            { version: babelRuntimeVersion, useESModules: true },
          ],
        ],
        //babelHelpers
        //runtime:使用Rollup构建库时常用,它必须与@babel/plugin-transform-runtime结合使用,你还应该指定@babel/runtime作为你的包的依赖。目标是es/cjs时,应该配置@babel/runtime为扩展依赖,也可以按需指定依赖如:@babel/runtime/helpers/get,Rollup将只排除完全匹配字符串的模块。
        //bundled:helpers打包进产物中
        //external:与@babel/plugin-external-helpers结合使用。从全局babelHelpers对象中引用helpers
        //inline:babel默认配置,每个独立文件中插入helpers,会导致严重的代码重复
        babelHelpers: 'runtime',
      }),
    ],
  },
];

package.json

{
  "name": "utils",
  "version": "1.0.0",
  "description": "",
  "main": "lib/index.js",
  "module": "es/index.js",
  "types": "types/index.d.ts",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "clean": "rimraf lib dist es types",
    "build": "rollup -c",
    "lint": "eslint --ext .js,.jsx src/",
    "start": "npm-run-all -s clean build",
    "prepare": "husky install"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.16.0",
    "@babel/plugin-transform-runtime": "^7.16.4",
    "@babel/preset-env": "^7.16.4",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-node-resolve": "^13.0.6",
    "@rollup/plugin-replace": "^3.0.0",
    "@typescript-eslint/eslint-plugin": "^5.5.0",
    "@typescript-eslint/parser": "^5.5.0",
    "eslint": "^8.3.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
    "husky": "^7.0.4",
    "lint-staged": "^12.1.2",
    "npm-run-all": "^4.1.5",
    "prettier": "^2.5.0",
    "rimraf": "^3.0.2",
    "rollup": "^2.60.1",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript2": "^0.31.1",
    "typescript": "^4.5.2"
  },
  "peerDependencies": {
    "@babel/runtime": "^7.16.3"
  },
  "dependencies": {},
  "lint-staged": {
    "*.{js,jsx}": "eslint --cache --fix"
  }
}