Typescript + rollup to quickly build js-sdk

Time:2021-9-19

introduce

typescript-library-starter
It is a scaffolding tool for open source typescript development foundation library, which can help us quickly initialize a typescript project(official website address), interested students can understand.

Initialize project

git clone https://github.com/alexjoverm/typescript-library-starter.git typeScript-sdk-demo
cd typeScript-sdk-demo

npm install

Introduction to file directory

After the dependency installation, open the project directory,TypeScript library starterThe generated directory structure is as follows:

├── CONTRIBUTING.md
├── LICENSE 
├── README.md
├── code-of-conduct.md
├── node_modules
├── package-lock.json
├── package.json
Login - rollup.config.ts // rollup configuration file
- Src // source directory
Test // test directory
Tools // publish to GitHub pages and some configuration script tools published to NPM
: tsconfig.json // typescript compilation configuration file
└ - tslint.json // typescript lint file

Tool integration

useTypeScript library starterThe project created integrates many excellent open source tools:

  • useRollupJSHelp us pack.
  • usePrettierandTSLintHelp us format the code and ensure the consistency of code style.
  • useTypeDocHelp us automatically generate documents and deploy them to GitHub pages.
  • useJestHelp us do unit tests.
  • useCommitizenHelp us generate normalized submission comments.
  • useSemantic releaseHelp us manage releases and releases.
  • usehuskyHelp us use git hooks more easily.
  • useConventional changelogHelp us automatically generate change log through code submission information.

Associated remote git warehouse

Currently, the created code base is not associated with the GitHub remote warehouse, so it cannot be submitted to the remote warehouse. How to associate it?

$git remote add origin warehouse address

The warehouse address is the newly created warehouse, as shown below:
Typescript + rollup to quickly build js-sdk

Next, check whether the association is successful:

$ git remote -v

Development NPM package

Create index.ts in our SRC directory to write the function code. Here, we simply write an add function as the function provided by the NPM package released this time.

// src/index.ts 
export default class TsSdk {
    /**
   * return sum
   * @static
   * @param {number} a 
   *  @param {number} a 
   */
  static add(a: number, b: number): number {
    return a+b
  }
}

However, in the development process, we often need to open a local service to test our code, that is, we need to write a demo. At this time, we need to use webpack.

Demo writing

We will use node.jsexpressLibrary to run our demo, usingwebpackAs a build tool for the demo.

Dependent installation

Let’s install some dependent packages required for writing demo, as follows:

npm install -D webpack webpack-dev-middleware webpack-hot-middleware ts-loader  tslint-loader express
  • webpackPackage build tool
  • webpack-dev-middlewareandwebpack-hot-middlewareTwo express webpack Middleware
  • ts-loaderandtslint-loaderIt is a typescript related loader required by webpack
  • expressIt is the server-side framework of node.js.

    Writing a webpack configuration file

    Create a webpack configuration file under the root directoryexamples/webpack.config.js

    const fs = require('fs')
    const path = require('path')
    const webpack = require('webpack')
    module.exports = {
    mode: 'development',
    /**
     *We will create multiple subdirectories under the examples directory
     *We will put the demos of different chapters in different subdirectories
     *An app.ts will be created under each subdirectory
     *App.ts is used as the entry file for webpack construction
     *Entries collects multiple directory entry files, and each entry also introduces a file for hot update
     *Entries is an object, and key is the directory name
     */
    entry: fs.readdirSync(__dirname).reduce((entries, dir) => {
      const fullDir = path.join(__dirname, dir)
      const entry = path.join(fullDir, 'app.ts')
      if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
        entries[dir] = ['webpack-hot-middleware/client', entry]
      }
      return entries
    }, {}),
    /**
     *Package and generate target JS according to different directory names, and the name is consistent with the directory name
     */
    output: {
      path: path.join(__dirname, '__build__'),
      filename: '[name].js',
      publicPath: '/__build__/'
    },
    module: {
      rules: [
        {
          test: /\.ts$/,
          enforce: 'pre',
          use: [
            {
              loader: 'tslint-loader'
            }
          ]
        },
        {
          test: /\.tsx?$/,
          use: [
            {
              loader: 'ts-loader',
              options: {
                transpileOnly: true
              }
            }
          ]
        }
      ]
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.js']
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoEmitOnErrorsPlugin()
    ]
    }

    Write server file

    Create in the examples directoryserver.jsFile:

    const express = require('express')
    const webpack = require('webpack')
    const webpackDevMiddleware = require('webpack-dev-middleware')
    const webpackHotMiddleware = require('webpack-hot-middleware')
    const WebpackConfig = require('./webpack.config')
    
    const app = express()
    const compiler = webpack(WebpackConfig)
    
    app.use(webpackDevMiddleware(compiler, {
    publicPath: '/__build__/',
    stats: {
      colors: true,
      chunks: false
    }
    }))
    
    app.use(webpackHotMiddleware(compiler))
    
    app.use(express.static(__dirname))
    
    const port = process.env.PORT || 9090
    module.exports = app.listen(port, () => {
    console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`)
    })

    Write demo code

    First, create index.html and global.css in the examples directory as the entry files for all demos and the global style files.
    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>typescript-npm-demo examples</title>
      <link rel="stylesheet" href="/global.css">
    </head>
    <body style="padding: 0 20px">
      <h1>typescript-npm-demo examples</h1>
      <ul>
        <li><a href="basic">Basic</a></li>
      </ul>
    </body>
    </html>

    global.css

    html, body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
    color: #2c3e50;
    }
    ul {
    line-height: 1.5em;
    padding-left: 1.5em;
    }
    a {
    color: #7f8c8d;
    text-decoration: none;
    }
    a:hover {
    color: #4fc08d;
    }

    Then create the basic directory in the examples directory as the demo directory of this chapter, and then create the index.html and app.ts files in this directory

basic/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Basic example</title>
  </head>
  <body>
    <p>add result is equal to: <i class="result"></i></p>
    <script></script>
  </body>
</html>

basic/app.ts

import TsSdk from '../../src/index.ts'

const addResult = TsSdk.add(1, 2)
document.querySelector('.result').innerText = addResult

Run demo

Next, we add an NPM script in package.json:

"dev": "node examples/server.js"
$ npm run dev

It’s equivalent to executionnode examples/server.js, our server will be started.
Then we open the Chrome browser and visithttp://localhost:9090/You can access our demo
Typescript + rollup to quickly build js-sdk

4、 Unit test


Jest configuration

staypackage.json
There is a jest field in the file, corresponding to the jest configuration:

"jest": {
  
  //Convert ts to JS because it is useful to write test code with TS
  "transform": {
    ".(ts|tsx)": "ts-jest"
  },

  //The test environment here was originally node. We can change it to jsdom, otherwise we can't access, such as window objects.
  //Indicates that it is a browser like test environment. We can use some APIs in the browser environment.
  "testEnvironment": "jsdom",

  //The regular expression of the file to test.
  //Indicates that all files with. Test.ts and. Spec.ts in the test directory need to run the test.
  "testRegex": "/test/.*\\.(test|spec)\\.(ts)$",

  //Module file extension. When you import a module without specifying an extension, it will try to add these extensions in turn to find the module file you imported.
  //Indicates that. TS modules are preferred, followed by. TSX, and finally. JS.
  "moduleFileExtensions": [
    "ts",
    "tsx",
    "js"
  ],

  //Threshold setting of test coverage. When our test coverage does not reach the threshold, the test will fail.
  //It means that the global code branch coverage rate should reach 90%, the method coverage rate should reach 95%, the code line coverage rate should reach 95%, and the declaration coverage rate should reach 95%.
  "coverageThreshold": {
    "global": {
      "branches": 90,
      "functions": 95,
      "lines": 95,
      "statements": 95
    }
  },

  //Collect the test coverage of specified files (even if you don't write tests for these files), and its value is glob patterns.
  //Represents the test coverage of collecting JS and TS files in the SRC directory and all its subdirectories.
  "collectCoverageFrom": [
    "src/*.{js,ts}",
    "src/**/*.{js,ts}"
  ],
},

Write unit tests

Create a new index.spec.ts test file in the test directory, and enter our test code below

import TsSdk from "../src/typescript-sdk"

/**
 * TsSdk test
 */
describe('TsSdk test', () => {
    test('should return a number muti value', () => {
      const a = 1
      const b = 3
  
      const result = TsSdk.add(a, b)
  
      expect(result).toBe(4)
    })
  })

Execute NPM run test to view our test results!
Typescript + rollup to quickly build js-sdk

Because our function function is relatively simple, the coverage is very simple, reaching 100%.

⚠️ Note: in the process of writing, if there is no syntax error, the test fails because of the threshold setting of test coverage. When our test coverage fails to reach the threshold, the test will fail. You can lower the threshold according to your needs, or write perfect unit tests as much as possible.

5、 Pack

We will userollupTo package our TS SDK library, which is a very famous compilation and packaging tool. Compared with webpack, it is very suitable for compiling and packaging some JS libraries.

Due to usetypescript-library-starterInitialize our project, we already have itrollupPackage related configurations and installation of related plug-ins. Next, let’s analyze the generatedrollup.config.tsDo a little combing.

rollup.config.ts

import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import sourceMaps from 'rollup-plugin-sourcemaps'
import camelCase from 'lodash.camelcase'
import typescript from 'rollup-plugin-typescript2'
import json from 'rollup-plugin-json'

const pkg = require('./package.json')

const libraryName = 'typescript-sdk'

export default {
  //Represents a package entry file.
  input: `src/${libraryName}.ts`,

  //Represents the output target file. It is an object array. We can specify the output format, such as UMD format, ES mode, etc.
  output: [
    { file: pkg.main, name: camelCase(libraryName), format: 'umd', sourcemap: true },
    { file: pkg.module, format: 'es', sourcemap: true },
  ],
  // Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
 
  //Declare its external dependencies, which can not be packaged.
  external: [],

  //Listen for file changes and recompile. Only when -- watch is turned on during compilation will it take effect.
  watch: {
    include: 'src/**',
  },

  //The plug-in used in the compilation process. Rollup-plugin-typescript2 is used to compile typescript files. Usetsconfigdeclarationdir means to use the declarationdir defined in tsconfig.json file.
  plugins: [
    // Allow json resolution
    json(),
    // Compile TypeScript files
    typescript({ useTsconfigDeclarationDir: true }),
    // Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
    commonjs(),
    // Allow node_modules resolution, so you can use 'external' to control
    // which external modules to include in the bundle
    // https://github.com/rollup/rollup-plugin-node-resolve#usage
    resolve(),

    // Resolve source maps to the original source
    sourceMaps(),
  ],
}

Modify package.json

If alreadyrollup.config.tsThe libraryname is modified to*, so inpackage.jsonYou need to make relevant modifications in the file:

{
  "main": "dist/***.umd.js",
  "module": "dist/***.es5.js",
  "typings": "dist/types/***.d.ts",
}

⚠️ Description: execute on the consolenpm run build, the output dist directory will be compiled, where the Lib directory is the compiled. JS file of a single. TS file. The types directory is the. D.ts declaration file produced by compiling all. TS files.

***.es5.jsIs the entry file of ES mode generated after compilation, which is used in the module field of package.json,***.umd.jsFile is the entry file of UMD mode generated after compilation, which is used in the main field of package.json.

Add 2 NPM scripts:

{
  "prepub": "npm run test:prod && npm run build",
  "pub": "sh release.sh"
}

When we runnpm run pubWhen, the pre pub script will be executed first. In pre pub, we run two scripts: Test: prod and build&& The symbol indicates that the following tasks will not be executed until the previous command is executed successfully.

npm run test:prodIn fact, NPM run lint & & NPM run test —– no cache was run. First run lint to verify whether our source code and test files comply with the tslint specification, and then run test to run the test.

npm run buildIn fact, TSC — module commonjs, rollup – C rollup.config.ts and typedoc — out docs — target ES6 — Theme minimal — mode file SRC are running. First run TSC to compile our typescript files. The files under dist / lib and dist / types are generated by this command, then run rollup to build add.umd.js and add.es.js, and finally run typedoc to build the project documents.

After running prepub, the pub command will be run again. In fact, the SH release.sh command is executed, but we don’t have this script at present. Next, we need to write the deployment script release.sh.

Script deployment

release.sh:

//Deployment scripts are shell scripts, which encapsulate multi line console commands to explain their meaning line by line.

//Used to indicate that it is a shell script.
#!/usr/bin/env sh

//Tell the script to exit if the execution result is not true.
set -e

//Output enter release version: on the console.
echo "Enter release version: "

//Indicates that the value is read from the standard input and assigned to the $version variable.
read VERSION

//Where read - P indicates that a prompt is given
//- N 1 indicates that at most 1 character can be read in as valid
//- R indicates that the escape function of backslash is prohibited. Because our read does not specify a variable name, the default input read value will be assigned to the $reply variable.
read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r

//A null echo output indicates a jump to a new line and # a comment in the shell script.
echo  # (optional) move to a new line

//Represents the process control statement in the shell script. Judge whether $reply is case y. if yes, go to the following then logic.
if [[ $REPLY =~ ^[Yy]$ ]]
then

//Output repeating $version... On the console.
  echo "Releasing $VERSION ..."
  # commit

//Indicates that all code changes are submitted to the staging area.
  git add -A
  git commit -m "[build] $VERSION"

//Modify the version field in package.json to $version, and submit a modification record. The submission comment is [release] $version.
  npm version $VERSION --message "[release] $VERSION"
//Publish the code to the trunk branch
  git push origin master

//To publish the warehouse to NPM, we will publish all the code in the dist directory to NPM, because we configure files in package.json to be ["dist"].
  # publish
  npm publish
fi

6、 Release

Before release, we need to compress the code to make the package smaller.
To install rollup plugin terser:

npm install rollup-plugin-terser -D

modifyrollup.config.ts

//....
import { terser } from "rollup-plugin-terser"
//.....
export default {
  //....
  plugins: [
    //....
    terser()
  ],
}

Run deployment script

Next, we will run the NPM run pub script deployment, and follow the prompts directly to contract!
Note that NPM must be switched to the initial source!

Extensions: error handling

  • Execute the in the package commandtsc --module commonjsCommand, an error will be reported
    Typescript + rollup to quickly build js-sdk

problem: intsconfig.jsonExclude node is already set in_ Modules, TSC is still executed to node_ Modules?
reason ❗ ️:Exclude just tells typescript that this is not the source code. Don’t translate and check it. But typescript still goes to node_ Modules to find the declaration file of the third-party library. This behavior can also be configured through the types or typesrots options.

Typescript + rollup to quickly build js-sdk

terms of settlement ❗ ️:

//Notes
"typeRoots": [
    //   "node_modules/@types"
    ]

Conclusion: it’s done ✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️

Reference link: