Why we need a library of functions:

Suppose we have some general functions in the project, such as: parse URL, copy object, we use a JS file to save these general functions, use the method of export to use, he is a function library.

The pain point is that if multiple projects use the library, we need to consider using and maintaining optimizations, otherwise we will add or modify the library and multiple projects will need to be manually synchronized

So our library needs to use NPM for version management, so that the library can better play its capabilities


A library of functions should be available

  • NPM version management

  • Support on demand import

  • Provides a variety of module export methods

  • Passing unit tests

  • The document

    And then we need ts syntax, plus

  • Support for TS syntax

    For team maintenance convenience, added

  • Automated build templates


Select the packaging tool to build

We can choose one of the packaging tools to start with: rollup or Webpack

Build using rollup

Rollup Chinese document

  • Rollup is installed globally
npm install rollup --global
Copy the code
  • Build the project directory

    The catalog looks something like this:

    Steps to build:

  1. Create a new SRC /main.js file that will expose only the index module

     export * from './components/index'
    Copy the code
  2. Create package.json and add the required dependencies, including rollup, typescript, and rollup’s two plug-ins /plugin-babel, plugin-typescript.

    {" name ":" ts - util - a rollup ", "version" : "1.0.0", "scripts" : {" build ":" a rollup - c} ", "main" : "Dist /index.js", // NPM library entry "dependencies": {}, "devDependencies": {"@rollup/plugin-babel": "^ 5.2.1," "@ rollup/plugin - the typescript" : "^ 6.0.0", "a rollup" : "^ 2.32.0", "a rollup - plugin - json" : "^ 4.0.0", "tslib" : "^2.0.3", "typescript": "^4.0.3"}}Copy the code
  3. Added the rollup.config.js configuration file

      // rollup.config.js
      import json from 'rollup-plugin-json'
      import typescript from '@rollup/plugin-typescript'
      import pkg from './package.json'
      export default {
        input: 'src/main.ts',
        output: [
          {
            name: pkg.name,
            file: pkg.main,
            format: 'umd'
          }
        ],
        plugins: [
          json(),
          typescript({lib: ["es5", "es6", "dom"], target: "es5"})
        ]
      };
    Copy the code
  4. SRC/Components adds a module function isMobile. Ts, which uses the re to determine the phone number. We use the ts file suffix

    export default function isMobile(v: any): boolean {
      return /^1[0-9][0-9]\d{8}$/.test(v)
    }
    Copy the code

    It is then imported into index.ts and exposed uniformly

    import isMobile from './isMobile'
    export {
        isMobile
    }
    Copy the code
  5. Run the package command rollup -c or the NPM run build we defined, and the packaged file will appear in Dist.

    Dist/index. Js:

    (function (global, factory) { typeof exports === 'object' && typeof module ! == 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis ! == 'undefined' ? globalThis : global || self, factory(global['ts-util-rollup'] = {})); }(this, (function (exports) { 'use strict'; function isMobile(v) { return /^1[0-9][0-9]\d{8}$/.test(v); } exports.isMobile = isMobile; Object.defineProperty(exports, '__esModule', { value: true }); })));Copy the code
  6. validation

    Put the packaged dist/index.js file in a project and import the isMobile function for validation

    import {isMobile} from './index.js' ... Console. log(' Correct mobile number ', isMobile('13680988189'))Copy the code


Build with WebPack

  • Install Webpack & Webpack – CLI
NPM install --global webpack NPM install webpack webpack-cli --save-dev // This tool is used to run webpack on the command lineCopy the code
  • Build the project directory

  1. The files in SRC remain the same

  2. New packjson. Js

    {" name ":" ts - util - webpack ", "version" : "1.0.0", "description" : ""," main ":" index. Js ", "scripts" : {" build ": "Webpack}", "keywords" : [], "author" : ""," license ":" ISC ", "devDependencies" : {" ts - loader ":" ^ 8.0.11 ", "typescript" : "^ 4.1.2 webpack", ""," ^ 5.8.0 ", "webpack - cli" : "^ 4.2.0"}}Copy the code
  3. New webpack. Config. Js

    const path = require('path'); Module. exports = {entry: './ SRC /main.ts', // exports: {filename: 'index.umd.js', path: Path. resolve(__dirname, 'dist'), library: 'tsUtil', libraryTarget: 'umd'Copy the code
  • Add support for typescript

    Install tsloader

    npm install --save-dev typescript ts-loader
    Copy the code

    Create the ts.config.js file

    {
      "compilerOptions": {
        "outDir": "./dist/",
        "noImplicitAny": true,
        "module": "es6",
        "target": "es5",
        "jsx": "react",
        "allowJs": true
      }
    }
    Copy the code

    Update webapck.config.js to add TS support

    const path = require('path'); module.exports = { entry: './src/main.ts', output: { filename: 'index.umd.js', path: Path. resolve(__dirname, 'dist'), library: 'tsUtil', libraryTarget: 'umd'}, // use tsloader to resolve ts file module: {rules: [ { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ } ] }, resolve: { extensions: [ '.tsx', '.ts', '.js' ] }, }Copy the code
  • Packaging validation

    Run NPX webapck to package, and we can see that we have packaged the file we want: dist/index.umd.js, same validation method

    import {isMobile} from './index.umd.js' ... Console. log(' Correct mobile number ', isMobile('13680988189'))Copy the code

A choice between two packaging tools

Webpack began with the challenge of building complex single-page applications (spAs).

  • Resolution of the codeSolve the problem of single-page application loading the whole program slowly, decompose the application into manageable blocks, load on demand, in the interactive web pages we need fast response to improve greatly.
  • Static Resource ManagementImages and CSS can be imported into your application and can be used as another node in the dependency graph

Rollup uses ES2015 module design to efficiently build modules that can be directly applied by other JS libraries, which undoubtedly meets the requirements of function libraries.

  • He puts all the code in one place and executes it all at once,Generates cleaner code and starts faster
  • Provide single function, lighter than WebPack

There are a lot of libraries in the community that are packaged in WebPack, there are a lot of applications that are packaged in rollup, and currently in our production environment we use Rollup to package function libraries and WebPack to package applications


Published to the NPM

  • runnpm initInitialization, it’ll let you fill in the content, just use the default

  • runnpm loginLog in. If you don’t have one, go to the NPM website

  • runnpm publishRelease note here to use NPM official source, can not use Taobao source

    The version was rejected because I already committed version 1.0.0, which means I can’t publish the same version, just change the version in packjson.js and re-publish it.

  • Install validation in the project:


Provide more module specifications

The built-in javascript is not support modular, but in the community gives the solution that is the module specification, for your output dist files, library users may not and you are using the same module specification, if you want to library users can choose you need flexible module, that is about to do to the format of the output optimization

Configure several major module specifications, users can choose their own

output: [
   {
     name: pkg.name,
     file: pkg.main,
     format: 'umd'
   },
   {
     file: pkg.common,
     format: 'cjs'
   },
   {
     file: pkg.module,
     format: 'es'
   }
],
Copy the code

Here we configure three modules, with different suffixes, so that we can see three JS files in the dist directory, which correspond to the three module specification package code

  • Commonjs module

    • useThe require and exports(Module. exports) how references and exports interact
    • Synchronous loading
    • A file is a module

    Index.mon. Js:

     Object.defineProperty(exports, '__esModule', { value: true });
     
     function isMobile(v) {
         return /^1[0-9][0-9]\d{8}$/.test(v);
     }
    
     exports.isMobile = isMobile;
    Copy the code

    And his invocation specification:

    Const util = require('ts-util ') // util.isMobile('123');Copy the code

    CommonJs is a node default module specification that can be passedwebpackloading


  • Es module

    • Es6 language level implementationModule mechanism, future module standard
    • The import and exportModule interaction for export Default
    • Limited browser support

    Index.module.js:

    function isMobile(v) {
      return /^1[0-9][0-9]\d{8}$/.test(v);
    }
    
    export { isMobile };
    
    Copy the code

    And his invocation specification:

    Import * as util from 'ts-util'; / /... util.isMobile('123');Copy the code

    Es modulewebpackandrollupCan be loaded


  • Umd module
    • compatiblegeneralmodel
    • Check whether reuse is supportedcommonjsoramdorMount to global.

    Code packaged after configurationindex.umd.js:

    (function (global, factory) { typeof exports === 'object' && typeof module ! == 'undefined' ? Factory (exports) : // Does commonJS Typeof define === 'function' && define.amd support? Define (['exports'], factory) : // amd // global = typeof globalThis! == 'undefined' ? globalThis : global || self, factory(global['ts-util-rollup'] = {})); }(this, (function (exports) { 'use strict'; function isMobile(v) { return /^1[0-9][0-9]\d{8}$/.test(v); } exports.isMobile = isMobile; Object.defineProperty(exports, '__esModule', { value: true }); })));Copy the code

Adding unit tests

Function libraries must be unit tested, which is rigorous, and must be fully covered

The recommended test library is Jest, which is used by many open source projects

  • The installationjestandts-jest
npm i jest --save-dev
npm i ts-jest --save-dev
Copy the code
  • Create a newjest.config.js
module.exports = { transform: { '^.+\\.tsx? $': 'ts-jest' // ts file uses ts-jest}}Copy the code
  • Writing test files:isMobile.test.ts
Import isMobile from './isMobile' test('15919316514 returns true', () => {expect(isMobile('15919316514')).tobe (true)}) test('159193 returns false', () => {expect(isMobile('159193')).tobe (false)}) test('15919312312312312 returns false', () => {expect(isMobile('15919312312312312')).tobe (false)}) test( () => { expect(isMobile('15919312312312312')).toBe(false) })Copy the code
  • Run jest

    You can see that we passed the test


Automated build

Now if we were to add a new function what would we do?

You need to create a new function function ts file first, and then create a new jEST test file, etc. If the file level is relatively deep, as the function library becomes richer, the depth is inevitable, this process will become tedious, and you need to explain how to create a new document, very troublesome

We can useNode file OperationsAutomation of this tedious process is mainly utilizedNode file system

  • Let’s adjust the file structure to make it more reasonable. Each function has a folder wrapped around it, where the test files are stored

  • Create an Add.js node run file in the build folder

    The add. Js:

    Const fs = require('fs') const path = require('path') const readline = require('readline') // terminate const exit = function (log) {console.error(log) process.exit(1)} // Module path const MODULES_PATH = path.join(__dirname, '.. / SRC /components') const MODULES = process.argv[2] if (! MODULES) {exit(' please fill in the module name ')} // Output path const OUT_PATH = path.join(MODULES_PATH, MODULES) // File path const TEST_OUT_PATH = path-. join(OUT_PATH, '__tests__') // Test case file path const isExists = fs.existssync (OUT_PATH) if (isExists) {exit(' ${MODULES} directory already exists ')} Fs.mkdirsync (OUT_PATH) console.log(' create module directory ', OUT_PATH) fs.mkdirsync (TEST_OUT_PATH) console.log(' create test directory ', TEST_OUT_PATH) // Function template const addFileTemp = 'export default function ${MODULES} (val:any) {return val}' // Test case template const testTemp = ` import ${MODULES} from '.. / index 'the describe (' ${MODULES}' () = > {it (' results', () = > {expect (${MODULES} (' a ')). The place (' a ')})}) ` / / file path const FILE_PATH = path. Join (OUT_PATH, '/index.ts') const TEST_PATH = path.join(TEST_OUT_PATH,' ${MODULES}.test.ts') // Write to fs.writefilesync (FILE_PATH, AddFileTemp) fs.writefilesync (TEST_PATH, testTemp) console.log(' create new module successfully ', 'path: SRC /components/${MODULES}')Copy the code

    / SRC /components/ creates a folder with the same function name. It contains the function js file, the test file, and the basic template

    Function template:

    `
      export default function ${MODULES} (val:any) {
        return val
      }
      `
    Copy the code

    Test file template:

    ` import ${MODULES} from '.. The describe/index '(' ${MODULES}' () = > {it (' results', () = > {expect (${MODULES} (' a ')). The place (' a ')})}) `Copy the code
  • Then we add the script command to package.json, which executes the add.js file when we use NPM run add

    "add": "node ./build/add.js"
    Copy the code
  • Run NPM run add cloneDeep to add a deep-copy function

    The console will say

  • Let’s go back to the Components folder and see that cloneDeep has been generated

    The structure also meets our requirements, so we can focus on writing function code, which is very convenient for multi-party collaboration


Document generation

If it is maintained by the company team, then the documentation is very necessary, and HERE I don’t bother to write by hand, so I use an automatically generated APidOC

  • Write comments in your code

    /** * @api {function} is series isMobile * @apiname isMobile * @APIVersion 0.1.0 * @apidescription Mobile phone number is correct * @apigroup is series * @apiParam {any} v Mobile phone number * @apisuccess (return value) {Boolean} val Export default function isMobile(v: any): boolean { return /^1[0-9][0-9]\d{8}$/.test(v) }Copy the code
  • A new command

    "doc": "apidoc -i src/modules/ -o doc/ -t apidoc/template/",
    Copy the code

    -i SRC /modules/ indicates that apidoc is needed to find the generated source file

    -o doc/ indicates the generated directory output

    -t apidoc/template/ is the template source file, which can be omitted for deployment purposes.

Reference:

Webpack and rollup

CommonJs specification