background

Recently, when WRITING the business of the company, I found that there are some reusable modules in each project, such as verification, environmental judgment and so on. Every time I start a new project, I have to copy a copy of the code. If some methods need to be updated, it is even more troublesome

Set up the project

The selection

Choose WebPack + TS, of course you can choose other packaging tools such as Glup, Grunt, Rollup, etc

The directory structure

Util ├ ─ ─ the SRC │ ├ ─ ─ env │ │ └ ─ ─ but ts │ └ ─ ─ valid │ └ ─ ─ but ts ├ ─ ─ tsconfig. Json ├ ─ ─ webpack. Config. Js ├ ─ ─ package.jsonCopy the code

Begin

Install dependencies and configuration commands

 "scripts": {
   "build": "rimraf lib && webpack"
 },
 "devDependencies": {
 
    "typescript": "^ 4.2.2." ".// A substitute for ts-loader
    "awesome-typescript-loader": "^ 5.2.1." ".// Webpack configures the import/export file line pipe
    "fs": "^ 0.0.1 ws-security"."glob": "^ 7.1.6." "."path": "^ 0.12.7".// Delete old packaging files before packaging
    "rimraf": "^ 3.0.2." ".// webpack
    "webpack": "4"."webpack-cli": "^ 4.5.0." "
    
     // Compress the package
    "uglifyjs-webpack-plugin": "^ 2.2.0." ",}Copy the code

Write utility functions

src/valid/index.ts

/ * * *@desc Verify whether the domestic mobile phone number is legitimate *@param {String} PhoneNum Mobile phone number *@return {Boolean}* /
const validatePhoneNum = (phoneNum: string): boolean= > {
  const reg = / [0-9] {10} ^ 1 $/;
  return reg.test(phoneNum);
};

export default {
  validatePhoneNum,
};

Copy the code

Webpack configuration

webpack.config.js

const { CheckerPlugin } = require("awesome-typescript-loader");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");

const path = require("path");
const glob = require("glob");
const fs = require("fs");

const ENTRY_REG = path.join(__dirname, "./src/*/**/index.ts");

let entries = {}; / / entrance

glob.sync(ENTRY_REG).forEach((entry) = > {
  const catalogue = path.dirname(entry); // util/valid/index.ts => util/valid
  const fileName = catalogue.split("/").pop(); // util/valid => valid
  entries[fileName] = entry;
});

/* entries:{ valid: 'src/valid.index.ts' env: 'src/env/index.ts' } */

glob.sync(ENTRY_REG).forEach((item) = > console.log(item));
const WEBPACK_CONFIG = {
  mode: "production".entry: {
    ...entries, / / entrance
  },
  output: {
    path: path.resolve(__dirname, "./lib"), // Package directory
    filename: "[name]/index.js".publicPath: "/".libraryTarget: "umd".// Compatible with ES and Node
    umdNamedDefine: true.// libraryTarget uses umD, which must be set
    globalObject: "this".// There are no Windows in compatible Nodes
  },
  // Currently we need to add '.ts' to the resolve.extensions array.
  resolve: {
    extensions: [".ts".".tsx".".js".".jsx"],},module: {
    rules: [{test: /\.ts$/,
        loader: "awesome-typescript-loader",}]},plugins: [new CheckerPlugin()],

  / / compression
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        uglifyOptions: {
          compress: false.mangle: true.output: {
            comments: false,}},sourceMap: false,})]},devtool: "hidden-source-map"};module.exports = WEBPACK_CONFIG;

Copy the code

In a WebPack configuration, the main focus is on the Output section

Tsconfig configuration

tsconfig.json

{
  "compilerOptions": {
    "outDir": "lib".// Output the file name
    "module": "esnext"."target": "es6"."declaration": true.By default, index.d.ts is generated in the same directory as ts files
    "moduleResolution": "node"."allowSyntheticDefaultImports": true."skipLibCheck": true
  },
  "include": ["src"]."exclude": []}Copy the code

Perform packaging

yarn build

The package generated directory is as follows

Util ├ ─ ─ lib │ ├ ─ ─ env │ │ ├ ─ ─ the index, which s │ │ └ ─ ─ index. The js │ └ ─ ─ valid │ ├ ─ ─ the index, which s │ └ ─ ─ index. The js ├ ─ ─ the SRC │ ├ ─ ─ env │ ├─ ├─ ├─ ├.txt ├─ ├.txt ├─ ├.txtCopy the code

test

import valid from ".. /lib/valid";

valid.validatePhoneNum("13999999999") // success
valid.validatePhoneNum(1) // error => validatePhoneNum: (phoneNum: string) => boolean
Copy the code

Unit testing

At this point, the library is ready to be referenced, but we need to make sure that the components/methods in the library are correct before we publish, and we will use JEST for unit testing

Introduction of depend on

yarn add jest @types/jest ts-jest -D
Copy the code

Add the configuration file jest. Config.js to the root directory

module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
};

Copy the code

Write the test file __test__/valid.test.ts

import valid from ".. /src/valid"; test("valid 12345", () => { expect(valid.validatePhoneNum("12345")).toBe(false); }); test("valid 123123123", () => { expect(valid.validatePhoneNum("123123123")).toBe(false); }); test("valid 13429999999", () => { expect(valid.validatePhoneNum("13429999999")).toBe(true); });Copy the code

Run the test

yarn jest
Copy the code

release

Release build

package.json

{
  "name": "util"."version": "0.1.0 from"."description": "v0.1.0"."main": "./lib/main".// It is also possible not to specify the main entry and let the user directly enter the entry of the subdirectory
  "files": [
    "lib"]."scripts": {
    "build": "rimraf lib && webpack"
  },
  "author": "jensonliu"."license": "ISC"."devDependencies": {
    "@types/jest": "^ 26.0.20"."awesome-typescript-loader": "^ 5.2.1." "."fs": "^ 0.0.1 ws-security"."glob": "^ 7.1.6." "."jest": "^ 26.6.3"."path": "^ 0.12.7"."rimraf": "^ 3.0.2." "."ts-jest": "^ 26.5.2"."typescript": "^ 4.2.2." "."uglifyjs-webpack-plugin": "^ 2.2.0." "."webpack": "4"."webpack-cli": "^ 4.5.0." "}}Copy the code

Note: To distinguish devDependencies from dependencies, when you install your NPM package, you will download the package in your dependencies. If you do not need to do this, put the package in devDependencies

Perform the build first

yarn build
Copy the code

And then publish it

npm login
npm publish
Copy the code

Packaged export type

The NPM package doesn’t have to be used in just one environment, it can be used in Both Nodes and browsers, so we need to generate multiple files for developers to import

If developers use WebPack, Webpack will be compatible with the conversion of various module modes, directly using esModule is also no problem

But sometimes we want to use the ESModule directly in the browser, and we need to provide an ESM file like index.esm.js

<script type="module">
  import env from 'module/index.esm.js'
</script>
Copy the code

Also, we need to tell the developer webpack/rollup how to find the entry in node_modules, specified in package.json

  "main": "lib/index.js"."module": "lib/index.esm.js".Copy the code

When importing NPM packages in node_modules using webpack, you first look for the corresponding entry for module in package.json of that package, and then for main if there is none

Esm -webpack-plugin: EsM -webpack-plugin: EsM -webpack-plugin: EsM -webpack-plugin: ESM

Rollup.config. js configuration file reference

import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import typescript from 'rollup-plugin-typescript2'
import pkg from './package.json'

export default [
  // UMD for browser-friendly build
  {
    input: 'src/index.ts'.output: {
      name: 'pkg-umd'.file: pkg.main,
      format: 'umd',},plugins: [resolve(), commonjs(), typescript()],
  },
  {
    input: 'src/index.ts'.output: {
      name: 'pkg-esm'.file: pkg.module, // for es module like <script type="module"> import EventBus from './lib/index.esm.js'</script>
                        // or webpack , cause webpack will find npm package in module field
      format: 'esm',},plugins: [typescript()],
  },
]

Copy the code

Check out juejin.cn/post/684490…

Continuous integration

Speak it pretty full: www.cnblogs.com/gaobw/p/115…

Reference: github.com/liujiapeng/…


👉 github.com/liujiapeng/…