background

In project development, third-party component libraries, such as Element-UI, Vant and AntDesign, are often used to improve development efficiency. Or some hooks library like vueuse, Ahook.

With the increasing complexity of the system, the open source library cannot meet some special scenarios, and the system will generate a custom tool library. When there is only one system, it is ok for the tool inventory to be managed in the project folder, but multiple projects need to share the code. This management mode will lead to multiple copies of the code, and when bugs occur, they will not be dealt with thoroughly due to code synchronization problems.

To achieve multi-project code sharing, the common code can be isolated into a project and managed in the form of NPM. But as a tool library, we want to be able to support TS, automatic unit testing, ESM support, CommonJS packaging, documentation, standard coding style and automatic version logging.

Since we used vuE-CLI and other scaffolding generation projects, the project has engineering features by default, but if you do not use CLI, build a complete project step by step, exactly what steps are required, the following step by step to explain to you.

Initialization and build scripts

1.1 Create a directory and initialize the project

mkdir xboss-hooks && cd xboss-hooks && npm init -y
Copy the code

Command to create the xboss-hooks directory and initialize the project with NPM init -y, where the -y parameter means to skip the query mode and use the default configuration.

A package.json file will appear in the directory after the successful creation.

// Do not copy the following content, because JSON files do not allow comments, you will make an error copying my file.
{
  "name": "xboss-hooks"./ / project name
  "version": "1.0.0"./ / version number
  "description": "".// Project description
  "main": "index.js".// Project entry file
  "scripts": {           // An executable script, such as NPM run test, executes the following test command
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [].// Keyword, if published to NPM, can be used for search
  "author": ""./ / the author
  "license": "ISC"       // Open source protocol
}

Copy the code

1.2 TS support

Since the tool library needs to be more stable, it is recommended to write in typescript, which also generates type files automatically, providing better code hints for projects that use TS.

TS support is required by relying on the typescript library and compiling the code through the TSC command.

# install dependencies
npm install typescript -D
Copy the code

Package. json adds build and development scripts

  "scripts": {
    "build": "tsc src/* --outDir dist".// SRC /* ts source file --outDir generate file directory
    "dev": "tsc src/* --outDir dist -W"  // -w monitors file changes and automatically compiles files
  },
Copy the code

Run NPM run build to see the generated file in the dist directory, or NPM run dev if you want to save the file for automatic compilation

To test whether the compilation is successful, create a SRC /index.ts file

export const getUser = (name: string) = > {
    return name;
}

console.log(getUser('tom'))
Copy the code

A successful compilation generates dist/index.js

"use strict";
exports.__esModule = true;
exports.getUser = void 0;
var getUser = function (name) {
    return name;
};
exports.getUser = getUser;
console.log((0.exports.getUser)('tom'));

Copy the code

This command converts the ts file to js, but does not execute the file. It can be executed using Node

node dist/index.js
Copy the code

If you run the file manually every time, it will be too tedious. You can use nodemon to listen to the file for automatic re-execution, so that you can see the result on the console by modifying SRC /index.ts.

# Global install dependencies
npm install -g nodemon
Copy the code
Dist /index.js file
nodemon dist/index.js
Copy the code

Note: Remember to keep the NPM run dev command on the other terminal listening for the source code to compile in real time.

1.3 Generating Type Files

Currently, the dist folder only has js files, with no. D. TS type description. The description file mainly gives user friendly type hints for TS projects.

We only need to add the -d parameter at compile time. See the official documentation for more parameters.

The modified package.json is shown as follows

  "scripts": {
    "build": "tsc src/* -d --outDir dist".// Added the description of the type generated by the -d parameter
    "dev": "tsc src/* --outDir dist -W"
  },
Copy the code

After executing the NPM run build again, you see an index.d.ts file in the dist folder

You can add the parameter declarationDir dist/types to the dist/types directory. Your build script appears as follows:

  "scripts": {
    "build": "tsc src/* -d --declarationDir dist/types --outDir dist"."dev": "tsc src/* --outDir dist -W"
  },
Copy the code

1.4 Packaging based on ESM and CJS

In order to adapt to different operating environments, libraries often provide different module management methods. The two most common ones are ESM and CJS, namely ESModule and CommonJS.

The esM provides the browser and CJS provides the NodeJS environment. For more on modularity, read Front-end Modularity: Getting to the bottom of AMD, CMD, ESM and CommonJS.

TSC also provides different modes for compiling parameters. Just add -m commonjs to compile to CJS, add -m es2015 to compile to ESM, and update package.json as follows

  "scripts": {
    "build:esm": "tsc src/* -d --declarationDir dist/types -m es2015 --outDir dist/esm"."build:cjs": "tsc src/* -d --declarationDir dist/types -m commonjs --outDir dist/cjs"."dev": "tsc src/* --outDir dist -W"
  },
Copy the code

Modify the configuration after the build is complete, otherwise publishing to NPM will cause resources not to be found, modify main in package.json, add module and Typings configuration

{
  "main": "dist/cjs/index.js"."module": "dist/esm/index.js"."typings": "dist/types/index.d.ts",}Copy the code

1.5 rollup Optimizes the build process

The above steps complete the TS => JS build, but we also want to make the build process smarter, such as automatically deleting the dist folder before the build, not packing the unused code into a distribution package, code compression, etc.

At ordinary times, webpack may be the most popular, but webpack is more suitable for packaging web applications. Rollup is recommended for component libraries and tool libraries.

Here’s the rollup official description:

Rollup is a JavaScript module wrapper that compiles small pieces of code into large, complex pieces of code, such as libraries or applications.

Rollup uses new standardized formats for code modules that are included in the ES6 version of JavaScript, rather than previous ad-hoc solutions such as CommonJS and AMD. ES6 modules allow you to use the most useful stand-alone functions in your favorite library freely and seamlessly, without having to carry around other unused code in your projects.

Let’s begin the build process prior to the rollup transformation. The dependencies are first installed, where rollup-plugin-delete is used to clear folders and rollup-plugin-typescript2 is used to compile TS.

# install dependencies
npm i rollup rollup-plugin-delete rollup-plugin-typescript2 -D
Copy the code

Create rollup.config.js in the project root directory as follows

import typescript from 'rollup-plugin-typescript2';
import del from 'rollup-plugin-delete';

export default {
  // Import file
  input: 'src/index.ts'.// Output commonJS and ESModule respectively
  output: [{dir: 'dist/cjs/index.js'.format: 'cjs'
    },
    {
      dir: 'dist/esm/index.js'.format: 'esm'}].// Use the del plugin to delete the files in dist
  plugins: [
    del({ targets: 'dist' }),
    // Use the typescript plugin to compile files. The tsconfig parameter can be omitted and the tsconfig.json root directory is read by default
    / / read tsconfig declarationDir useTsconfigDeclarationDir said configuration, if it is false will output level with js file directory
    typescript({ tsconfig: './tsconfig.json'.useTsconfigDeclarationDir: true}})];Copy the code

The tsconfig.json file is used to configure the TS build parameters. Here we only configure the generated type definition file and the storage path

{
    "compilerOptions": {
        "declaration": true."declarationDir": "dist/types",}}Copy the code

Finally, change the run script in package.json to rollup

  "scripts": {
    "dev": "rollup -w -c rollup.config.js"."build": "rollup -c rollup.config.js"
  },
Copy the code

Resources are now generated by executing NPM run build, and the generated resources are automatically tree shaking, i.e. unused code is not packaged.

1.6 Develop an hooks

I am developing in the form of TDD (test driven Development). There are no extensions for TDD. If you are interested, read the TDD Summary principles.

Start by installing the test automation dependency tool

# install dependencies
npm i ts-jest @types/jest -D
Initial configuration
npx ts-jest config:init
Copy the code

Add test scripts to package.json

  "scripts": {
    "build": "jest && rollup -c rollup.config.js"."test": "jest"
  },
Copy the code

Write test cases before writing implementations

// src/useToggle/__test__/index.spec.ts
import { useToggle } from '.. /index';

describe('useToggle'.() = > {
  test('should change state when execute toggle'.() = > {
    // The useToggle method returns an array, the first part of which is a response value, and toggle is a method
    const [state, toggle] = useToggle(false);
    // Expect state.value to be equal to the initialization value
    expect(state.value).toBe(false);
    // After executing the toggle method
    toggle();
    // The expected state is automatically reversed
    expect(state.value).toBe(true);
  });
});
Copy the code

Because the code has not been implemented, all NPM run test use cases fail to be executed, and then the logic is implemented according to the use case requirements. Vue3 dependencies are installed before writing the logic.

# installation vue3
npm i vue@next -D
Copy the code
// src/useToggle/index.ts
import { ref } from 'vue';

export function useToggle(defaultValue) {
  const state = ref(defaultValue);
  const toggle = () = >{ state.value = ! state.value; };return [state, toggle] as const;
}
Copy the code

Then you execute the use case, and the use case is done, because the use case is protected, and your code doesn’t have to worry about someone else changing it

Release tool library

The tool library can be published to the public NPM or set up their own private NPM, if the internal use of the enterprise, it is recommended to set up private NPM, set up the method is relatively simple, the following two methods are introduced.

2.1 Publish the public NPM

First, you need to register your account with NPM, and then run the NPM login command on the console to enter your account password and email address.

Run the NPM publish code. If the code is published successfully, the following log is entered and can be viewed on the official website

Now you can execute the install command to install the library for use in other projects

npm install xboos-hooks
Copy the code

Note: because I already used xboos-hooks package name, you will not commit if you use the same name as me.

2.2 Setting up private NPM

Private NPMS can choose Verdaccio in two steps according to the official tutorial

Install verdaccio globally
npm install -g verdaccio
Start the application after installation
verdaccio
Copy the code

After startup, the default address of the warehouse is http://localhost:4873/. We need to add. NPMRC file to the project and set the following contents:

registry=http://localhost:4873
Copy the code

If you publish now you will get an account error because private NPM also needs to be registered by running the following command and entering the account password and email address

npm adduser --registry http://localhost:4873/
Copy the code

To publish, run NPM publish. You can visit http://localhost:4873/ to check whether the publish is successful

If you want to install the dependency on another project, you can install it by executing the following command, with the Registry parameter telling NPM to go to the private server and get the dependency package

npm install xboss-hooks --registry="http://localhost:4873"
Copy the code

The downside of this approach is that it is not possible for someone else to get the installation package by executing NPM I directly when using your project. A better approach is to use Scope.

NPMRC file in use and add @xboss:registry=http://localhost:4873, NPM i@xboss /hooks install dependencies directly from NPM i@xboss /hooks

To remove the published package, run NPM unpublish@xboss /hooks -f

Three, improve engineering

Now the project can be developed and released normally, but in order to ensure the consistency of the coding style and prevent others from destroying the stability of the warehouse, style detection, automatic unit testing, automatic version management, continuous integration and other means can be added when the project is maintained by multiple people.

3.1 Unified coding style

8, Code style Usually uses Prettier for automatic formatting, prettier is easier to use, install dependencies and create under the project. Prettierrc.js file to define rules, you can also use json files but personally do not recommend because annotations cannot be added to JSON

# installation prettier
npm i prettier -D
Copy the code
// .prettierrc.js
module.exports = {
  printWidth: 80.// The number of characters in a line. If more than 80 characters are added to the line, the default value is 80
  tabWidth: 2.// A TAB represents several Spaces. The default is 80
  useTabs: false.// Whether to use TAB for indentation. The default value is false, indicating that Spaces are used for indentation
  singleQuote: true.// Whether to use single quotation marks for strings. The default is false. Use double quotation marks
  semi: true.// Whether to use semicolons on line bits. Default is true
  trailingComma: 'none'.Tail / / whether or not to use commas, there are three optional value "< none | es5 | all >"
  bracketSpacing: true {foo: bar} {foo: bar}
}

Copy the code

You can then create a.vscode/settings.json file in the project root directory. This configuration only works in the current workspace.

The following configuration declares that ts, JS, json files are formatted using the prettire rule, and editor.formatOnSave means save code is automatically formatted.

{
    "editor.formatOnSave": true."[typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "[json]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"}},Copy the code

Install the prettier plug-in for vscode, write code to see if it automatically formats, and restart vscode if it doesn’t.

3.2 ESLint integrates with Prettier

Prettires only unify code styles, but doesn’t warn against best practices such as variables not being used, constants not being declared with const keywords, etc. Eslint needs to help us because the project is written based on TS, We need to install ESLint, @typescript-eslint/parser, @typescript-eslint/eslint-plugin, eslint-plugin-import

npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-import -D
Copy the code

Next, create the.eslintrc.js configuration check rule

module.exports = {
  parser: '@typescript-eslint/parser'.plugins: ['@typescript-eslint'].extends: ['plugin:@typescript-eslint/recommended']};Copy the code

In addition to TS syntax checking, you can also add an Airbnb-base rule to check es syntax by first installing dependencies and then adding a configuration to extends where the eslint-config- prefix can be omitted

# Install Lint rules for Airbnb
npm i eslint-config-airbnb-base -D
Copy the code
// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser'.plugins: ['@typescript-eslint'].extends: ['airbnb-base'.'plugin:@typescript-eslint/recommended']};Copy the code

If an error message is displayed, the configuration is successful. If no message is displayed, restart VScode

Although there has been an error message, manual repair of the problem will be more troublesome, you can set vscode to save automatic repair

//.vscode/setting.json adds configuration
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
Copy the code

Resolving Configuration Conflicts

Prettier now that ESLint is configured, there would be a conflict between ESLint and prettier, for example, when you change the semi property of.prettierrc.js to false, where semicolons are not added at the end of code, while ESLint requires a semicolon by default, when you go to code and press save, Prettier clears the semicolon, esLint reports a warning.

The official solution to this problem is to use prettier in preference, relying on eslint-config-prettier and then adding prettier to extends. Can read details with Linters

# install dependency NPM I eslint-config-prettier -dCopy the code
// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser'.plugins: ['@typescript-eslint'].The extends extends extends rule extends rule from left to right if Prettier was on the left, his rule would be overwritten by the next rule
  extends: ['airbnb-base'.'plugin:@typescript-eslint/recommended'.'prettier']};Copy the code

3.3 Verification before code submission

Although we have configured the code style tools, we can not ensure that all developers follow the specification development. In order to ensure the standardization of the code warehouse, we can check the code style before entering the warehouse, and the unqualified code is not allowed to submit to the library.

Husky and Lint-staged methods can be used to implement this feature. Husky listens for Git code commits and triggers inspection tasks when code commits are detected. However, entire code base scans can be a waste of resources, so Lint-staged scans only for files in staging are required. The following is the installation and configuration process.

# Install dependent Husky Lint-staged
npm i husky lint-staged -D
Initialize husky
npx husky install
# Add hooks to commit code automatically NPX --no-install Lint-staged
npx husky add .husky/pre-commit "npx --no-install lint-staged"
Copy the code

Following the above command, a.husky folder appears in the project root, and you need to add Lint-staged configuration in package.json, stating that the matching files are formatted with Prettier and esLint, respectively

  "lint-staged": {
    "*.{ts,tsx,js,vue,less}": "prettier --write"."*.{ts,tsx,js,vue}": "eslint --fix"
  },
Copy the code

You can now try modifying the source code and then committing it to the Git repository, which will trigger the script and indicate success when the screenshot below appears

Json script: prepare: “husky install”. This script automatically initialates husky after the user has finished installing the dependencies.

3.4 Specification commit log

Git commit log specifications are commonly used in Angular, which specify different types of commit content. For example, new features require the keyword feat and bug fix. For details, please refer to the official documentation.

We can also use Husky to add commit hook to check whether the commit log complies with the specification when submitting code. The check of the log specification needs to use @commitlint/ CLI and @commitlint/ config-Conventional

# @ commitlint installation/cli
npm i @commitlint/cli @commitlint/config-conventional -D
Create a configuration file from the command line
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
# husky add commit hook
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit $1'
Copy the code

Once configured, you can try to commit the code to the repository, because you will be prompted with the following error because you did not commit according to the specification

Modify the commit log and submit it again

3.5 Version Management

Once we were ready to release, we had to change the version number, write the update notes, tag, generate the release package, and push the NPM.

Because the update iteration of the tool library is relatively frequent, if all the above operations are manually operated, it will not only consume a lot of time, but also may lead to different release standards due to different people’s release. In order to solve this problem, release automation can be realized by using the release-it tool library.

# installation relase - it
npm i release-it -D
Copy the code

Add execution scripts to package.json

  "scripts": {
    "release": "release-it"
  },
Copy the code

Before executing this command, make sure that your repository is bound to the remote repository and the local code has a commit history. Also, since relex-it automatically changes the version and produces a commit, but we used commitlint to limit the commit format, create a. Relex-it. json file in the project root directory. Configure the following

// .release-it.json
{
  "git": {
    "commitMessage": "chore: release v${version}"
  },
  "github": {
    "release": true
  },
  "hooks": {
    "before:init": ["npm run build"]}}Copy the code

After the configuration is complete, execute NPM run release and the following interactive dialog will appear. Fill in the dialog as required.

After publishing, Github produces a release, and the update log is automatically generated based on the commit history

Third, summary

We found that the gap between the implementation of an enterprise application and demo is very large, enterprise applications need to constantly increase engineering and automation tools to solve the problem of repetitive work, standardized writing style and so on.

In the next installment, I will also show you how to design enterprise component libraries and transform Multirepo into Monorepo. If this series of lessons helped you, don’t forget to give a like and leave a comment.

3.1 Project warehouse address

Github.com/zhengguoron…

3.2 Reference Materials

Prettier vs. Linters

Integrating with Linters

Getting Started – Linting your TypeScript Codebase

Contributing to Angular

commitlit-guides-local-setup

release-it#readme

Front end modularity – Thoroughly understand AMD, CMD, ESM and CommonJS