introduce

Typescript-library-starter is an open source typescript development scaffolding tool that helps you quickly initialize a typescript project.

Initialize the project

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

npm install
Copy the code

File Directory Introduction

After installing dependencies, open the project project directory and TypeScript Library Starter generates the following directory structure:

└ ─ CONTRIBUTING. Md ├─ Code-of-conduct. Md ├─ Code-of-Conduct ├─ Package-Lock. json ├─ Package. json ├─ Rollup.config. ts // Rollup Config file ├─ SRC // source directory ├─ test // Test directory ├─ tools // Post to GitHub Pages and post to ├─ └─ tslint.json // TypeScript Lint filesCopy the code

Tool integration

Projects created using TypeScript Library Starter integrate many excellent open source tools:

  • useRollupJSHelp us pack.
  • usePrettierTSLintHelps us format our code and keep it consistent.
  • useTypeDocHelp us automatically generate and deploy documents to GitHub Pages.
  • useJestHelp us do unit testing.
  • useCommitizenHelp us generate normalized submission comments.
  • useSemantic releaseHelps us manage releases and releases.
  • usehuskyHelp us use Git hooks more easily.
  • useConventional changelogHelp us automatically generate a change log from code submission information.

Associate a remote Git repository

Github remote repository is not associated with the repository, so it cannot be submitted to the remote repository.

$git remote add originCopy the code

The address of the warehouse is the newly created warehouse. View the following information:

Then check whether the association is successful:

$ git remote -v
Copy the code

The development of NPM package

Create a new index.ts function in our SRC directory to write the function code. Here we simply write an Add function as the NPM package provided by this release.

// 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
  }
}


Copy the code

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

The demo code

We will use the Node.js Express library to run our demo and use WebPack as the demo building tool.

Depend on the installation

Let’s start by installing some dependency packages that we need to write demo as follows:

npm install -D webpack webpack-dev-middleware webpack-hot-middleware ts-loader  tslint-loader express
Copy the code
  • webpackIs a packaged build tool
  • webpack-dev-middlewarewebpack-hot-middlewareWebpack middleware is 2 express middleware
  • ts-loaderandtslint-loaderIs a typescript-related loader required by WebPack
  • expressNode.js is the server-side framework for Node.js.

Write a WebPack configuration file

Root directory to create webpack configuration file examples/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 examples * we will put different chapters of demo in different subdirectories * under each subdirectory we will create an app.ts * app.ts as the entry file for webpack construction * entries Multiple directories of entry files are collected, and each entry also introduces a file for hot updates * entries are an object with key as 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 to generate target JS with the same 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() ] }Copy the code

Writing a server file

Create server.js file in examples directory:

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`)
})
Copy the code

Write demo code

Start by creating index.html and global.css in the examples directory as global style files for all demos. 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>Copy the code

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 {word-break: break-all; Padding - left: 1.5 em. } a { color: #7f8c8d; text-decoration: none; } a:hover { color: #4fc08d; }Copy the code

Then create basic under examples as the demo directory in this section. Create 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 src="/__build__/basic.js"></script> </body> </html>Copy the code

basic/app.ts

import TsSdk from '.. /.. /src/index.ts' const addResult = TsSdk.add(1, 2) document.querySelector('.result').innerText = addResultCopy the code

Run the demo

Then we add a new NPM script to package.json:

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

It’s almost executednode examples/server.js, will open our server. Then we open up Chrome and visithttp://localhost:9090/Now you can visit our demo

Unit testing


Jest configuration

In package.json, there is the jEST field, which corresponds to the JEST configuration:

"Jest" : {/ / convert ts into js, because practical ts test code "transform" : {" (ts | TSX) ": "Ts-jest"}, // test environment, this is originally node, we can change to jsdom, otherwise can not access such as window object. // indicates that it is a browser-like test environment, and we can use some of the apis in the browser environment. "TestEnvironment ": "jsdom", // The regular expression to test the file. Ts and.spec.ts files in the test directory need to be tested. "TestRegex" : "/ test /. * \ \. (test | spec) \ \. $" (ts), / / modules file extension, when you go into a module did not specify the extension of time, it will try to add these extensions in turn to find you introduce module file. // the.ts module is found first, then.tsx, and finally.js. "ModuleFileExtensions ": ["ts"," TSX ", "js"], // Test coverage threshold setting, if our test coverage does not reach the threshold, the test will fail. // Represents global branch coverage of 90%, method coverage of 95%, line coverage of 95%, and declaration coverage of 95%. "coverageThreshold": { "global": { "branches": 90, "functions": 95, "lines": 95, "statements": 95}}, // Collect test coverage for specified files (even if you don't write tests for those files) with a value of type Glob Patterns. // collects test coverage of JS and TS files in the SRC directory and all of its subdirectories. "collectCoverageFrom": [ "src/*.{js,ts}", "src/**/*.{js,ts}" ], },Copy the code

Writing 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) }) })Copy the code

Run NPM run test to see our test results!

Because of the simplicity of our function, the coverage is easily 100 percent.

⚠️ Note: in the process of writing, the test failed because of the threshold setting of test coverage, not syntax errors. When our test coverage did not reach the threshold, the test would fail. You can lower the threshold as much as you need, or write unit tests as well as you can.

Five, packaging,

We will use Rollup to package our TS-SDK library, which is a well-known compiler packaging tool that is better suited to compiling and packaging some JS libraries than WebPack.

Since we initialize our project with typescript-library-starter, we now have rollup packages and plug-ins installed, so let’s do a little combing through the generated rollup.config.ts.

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 {// indicates the package entry file. Input: 'SRC /${libraryName}. Ts', // represents the output target file, which is an array of objects, 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 in. External: [], // Listen for changes to files, recompile, and only turn it on at compile time --watch works. watch: { include: 'SRC /**',}, // rollup-plugin-typescript2 is used to compile TypeScript files. UseTsconfigDeclarationDir said use tsconfig. DeclarationDir defined in a 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(), ], }Copy the code

Modify the package. The json

If you have changed the libraryName to *** in rollup.config.ts, then you need to make relevant changes in package.json:

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

⚠️ Note: Running NPM run build on the console compiles the output dist directory, where lib is a.js file compiled from a single.ts file. The types directory is the.d.ts declaration file produced after all.ts files are compiled.

***.es5.js is the compiled es mode entry file used in package.json module field, and ***.umd.js is the compiled UMD mode entry file used in package.json main field.

Add 2 NPM scripts:

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

When we run NPM run pub, the prepub script will be executed first. In prepub, we run test:prod and build. The && symbol indicates that subsequent tasks can be executed only after the previous command is successfully executed.

NPM run test: Prod actually runs NPM run lint && NPM run test — –no-cache. Run Lint to verify that our source code and test files comply with the TSLint specification, and then run test to run the test.

NPM run build actually runs TSC –module commonjs, rollup -c rollup.config.ts and TypeDoc –out docs –target ES6 –theme minimal – mode file SRC. Run TSC to compile our TypeScript files, dist/lib and dist/types files, and run rollup to build add.umd. js and add.es. Finally, run TypeDoc to build the documentation for the project.

After running prepub, the pub command is run again, which actually executes sh release.sh, but we don’t have this script right now, so we need to write the deployment script release.sh.

Writing deployment scripts

The sh:

// Deployment scripts are shell scripts, which encapsulate multiple lines of console commands to explain their meaning line by line. // is used to indicate that it is a shell script. #! /usr/bin/env sh // tells the script to exit if the result is not true. Set -e // Press Enter release version: on the console. Echo "Enter release version: "// Reads the value from standard input and assigns it to the $version variable. Read VERSION // Where read-p indicates the prompt. // -n 1 indicates that a maximum of one character can be read in. // -r indicates that backslashes cannot be escaped. Since our read does not specify a variable name, this input read value is assigned to the $REPLY variable by default. read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r // echo outputs null values to jump to a new line, # to indicate comments in shell scripts. Echo # (optional) move to a new line echo # (optional) move to a new line If [[$REPLY =~ ^[Yy]$]] then... . echo "Releasing $VERSION ..." Commit all code changes to the staging area. Git add -a git commit -m "[build] $VERSION" The commit comment is [release] $VERSION. NPM version $version --message "[release] $version" We will publish all the code in the dist directory to NPM because we configured files in package.json to be ["dist"]. # publish npm publish fiCopy the code

Six, publish,

Before publishing, we need to compress the code to make the package smaller. Install a rollup plugin – terser:

npm install rollup-plugin-terser -D
Copy the code

Modify the rollup. Config. Ts

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

Running the deployment script

Next we will run the NPM run pub script deployment, as prompted to directly, you can send packages! Note that NPM must be switched to the original source!

Extension: error handling

  • Execute the package commandtsc --module commonjsCommand, an error will be reported

❓ : exclude node_modules is set in tsconfig.json, TSC is executed in node_modules? ** Reason ❗️ : ** Exclude simply tells Typescript that this is not source code, so don’t translate and check it. However, Typescript still looks for third-party library declarations in node_modules, which can also be configured with the types or typesRoots option.

Solutions ❗️ :

TypeRoots: [// "node_modules/@types"]Copy the code

All in all, we have done our best. All in all, we have done our best

Reference links:

  • www.yuque.com/u188805/ewt…
  • www.tslang.cn/docs/handbo…