Open source guide for front-end projects

Open source guide for front-end projects

Open source guide for front-end projects

This article explains how to build an engineered front-end library, combined with Github Actions , and automatically publish the entire detailed process to Github and NPM .

Example

We often see that popular open source projects like Vue and React have many configuration files. What are they used for? Their Commit and Release records are so standardized, are they based on some kind of agreement?

Stop talking nonsense, take the picture first!

The red icon above is the related engineering configuration, including Linter, Tests, Github Actions, etc., covering the entire process of development, testing, and release.

Related configuration list

Below we start from creating a TypeScript project, complete all engineering configurations step by step, and explain the meaning of each configuration and the pits that are easy to step on.

initialization

In order to avoid compatibility issues, it is recommended to upgrade node to the latest long-term support version first.

First create a repo on Github , pull it down and pass it

npm init -y
initialization. Then create
src
Folder, write
index.ts
.

package.json
After generation, I need to add the following configuration items:

"main": "index.js", + "type": "module", "scripts": { "test": "echo/"Error: no test specified\" && exit 1" }, + "publishConfig": { + "access": "public" + } Copy code

We define the project as an ESM specification, and the front-end community is gradually migrating to the ESM standard. Starting from Node v12.0.0 , as long as you set it

"type": "module"
, Node will treat the entire project as ESM specification, and we can write it directly
import/export
.

publishConfig.access
Indicates that the current project is published to NPM the access level of the , it has
restricted
with
public
Two options,
restricted
Means we publish to NPM is a private package (charged), the default access level is
restricted
, Because we are an open source project so marked as
public
.

Configuration

After creating the project, we start to install the dependencies related to engineering. Because we are a TypeScript project, we also need to install the dependencies of TypeScript.

Typescript

Install TypeScript first, then use

tsc
Named generation
tsconfig.json
.

npm i typescript -D npx tsc --init Copy code

Then we need to add modifications

tsconfig.json
The configuration items are as follows:

{ "compilerOptions" : { /* Basic Options */ "baseUrl" : "." , //Module analysis root path, the default is the directory where tsconfig.json is located "rootDir" : "src" , //Compile and resolve root path, The default is the directory where tsconfig.json is located "target" : "ESNEXT" , //Specify the output ECMAScript version, the default is es5 "module" : "ESNext" , //Specify the output module specification, the default is Commonjs "lib" : [ " ESNext" , "DOM" ], //compile the need to include the API, the default is the default value of target "outDir" : "dist", //Compile output folder path, the default is the same level directory of the source file "sourceMap" :true, //enable sourceMap, the default is false "declaration" : true , //generate .d.ts type files, the default is false "declarationDir" : "dist/types" , //the output directory of .d.ts type files, The default is the outDir directory /* Strict Type-Checking Options */ "strict" : true , //Enable all strict type checking options, the default is true "esModuleInterop" : true , //Realize CommonJS by creating a namespace for imported content Interoperability with ES modules, the default is true "skipLibCheck" : true , //Skip the type check of importing third-party lib declaration files, the default is true "forceConsistentCasingInFileNames" : true ,//Mandatory use consistent capitalization in file names, the default is true "moduleResolution" : "Node" , //Specify which module resolution strategy to use, the default is Classic }, "include" : [ "src" ] //Specify the files to be compiled, by default all .ts, .d.ts, .tsx files in the current directory except exclude } Copy code

For more detailed configuration reference: www.typescriptlang.org/tsconfig

Note, if your project involves

WebWorker API
Needs to be added to
lib
In the field

"lib" : [ "ESNext" , "DOM" , "WebWorker" ], copy the code

Then we add the compiled file path to

package.json
And in
scripts
Add compile commands in.

-"main": "index.js", + "main": "dist/index.js", + "types": "dist/types/index.d.ts" "type": "module", -"scripts": { -"test": "echo/"Error: no test specified\" && exit 1" -}, + "scripts": { + "dev": "tsc --watch", + "build ": "npm run clean && tsc", + "clean": "rm -rf dist" + }, "publishConfig": { "access": "public" } Copy code

types
The configuration item is to specify the type file generated by the compilation, if
compilerOptions.declarationDir
Specified is
dist
, Which is the source code and
.d.ts
Same level, then
types
Can be omitted.

Verify that the configuration takes effect, in

index.ts
Write

const calc = ( a: number , b: number ) => { return a-b } Console .log (Calc ( 1024 , 28 )) copying the code

Execute in the console

npm run build && node dist/index.js duplicated code

Will be at

dist
Generated in the catalog
types/index.d.ts
,
index.js
,
index.js.map
And print
996
.

Eslint & Prettier

Code specifications are inseparable from various

Linter
, The reason for putting these two together is to borrow
Prettier
A sentence from the official website: "Use Prettier to solve code format problems, and use linters to solve code quality problems" .although
eslint
There is also a formatting function, but
prettier
The formatting function is more powerful.

Most of the classmate editors have installed the prettier-vscode and eslint-vscode plug-ins. If your project has only one configuration, because the two partial formatting functions are different, then it will cause a problem, the code They are formatted once by two plug-ins, and solved online

prettier
+
eslint
There are so many conflicting options, and even the whole
rules
The list is posted.

Then here we follow the official recommendation and use the least configuration to solve

prettier
with
eslint
Integration issues.

Eslint

First install

eslint
, And then use
eslint
The command line tool generates the basic configuration.

npm i eslint -D npx eslint --init Copy code

After executing the above command, some options will be prompted, and we will select the configuration that matches our project in turn.

Note, here

eslint
I recommend three mainstream specifications in the community, Airbnb , Standard , and Google . Because of my personal preference, I chose the Standard specification without semicolon .

Generated

.eslintrc.cjs
The file should look like this

module .exports = { env : { browser : true , es2021 : true , node : true }, extends : [ 'standard' ], parser : '@typescript-eslint/parser' , parserOptions : { ecmaVersion : 12 , sourceType : 'module' }, plugins : [ '@typescript-eslint' ], rules : { } } Copy code

Some students may ask, why is the name of the configuration file generated here

.eslintrc.cjs
Instead of
.eslintrc.js
?

Because we define the project as

ESM
,
eslit --init
Will automatically recognize
type
, And generate a compatible configuration file name, if we change it back
.js
End, then run
eslint
An error will be reported. The problem is
eslint
Used internally
require()
Syntax to read the configuration.

Similarly, this question also applies to the configuration of other functions, such as the ones that will be discussed later

Prettier
,
Commitlint
Wait, the configuration file cannot be
xx.js
At the end, it should be changed to other configuration file formats supported by the current library, such as:
.xxrc
,
.xxrc.json
,
.xxrc.yml
.

Verify that the configuration takes effect, modify

index.ts

const calc = (a: number, b: number) => { return a-b } - the console.log (Calc (1024, 28)) +//the console.log (Calc (1024, 28)) copying the code

in

package.json
Add in
lint
command

"scripts": { "dev": "tsc --watch", "build": "npm run clean && tsc", + "lint": "eslint src --ext .js,.ts --cache --fix", "clean": "rm -rf dist" }, Copy code

Then execute in the console

lint
,
eslint
An error message will be prompted, indicating that the verification is effective.

RUN lint NPM # . 1: error. 7 'Calc' IS A Assigned value Never Used NO-But-VARS unused copy the code

Because it is a Typescript project, we also need to add the TypeScrip extension configuration provided by the Standard specification (the same applies to other specifications)

Install eslint-config-standard-with-typescript

npm i eslint-config-standard- with-typescript -D duplicated code

Add and modify

.eslintrc.cjs

module.exports = { env: { browser: true, es2021: true, node: true }, -extends: ['standard'] + extends: ['standard','eslint-config-standard-with-typescript'], parser:'@typescript-eslint/parser', parserOptions: { ecmaVersion: 12, sourceType:'module', + project:'./tsconfig.json' }, plugins: ['@typescript-eslint'], rules: {} } Copy code

Verify that the configuration takes effect

Execute in the console

lint
,
eslint
Two error messages will be prompted, indicating that the verification is effective.

RUN lint NPM # . 1: error. 7 'Calc' IS A Assigned value Never Used NO-But-VARS unused # 1:14 Missing error return type ON function duplicated code

Prettier

Now we follow the recommended method on the official website to put

prettier
R G
eslint
The verification is in progress.

installation

prettier
And initialize the configuration file

npm i prettier -D echo {}> .prettierrc.json Copy code

Then in

.prettierrc.json
Add configuration, here you only need to add the part that conflicts with your selected specification.

{ "semi" : false , //Whether to use semicolon "singleQuote" : true , //Use single quotation marks instead of double quotation marks "trailingComma" : "none" //Use comma end as much as possible for multiple lines } Copy code

For more configuration details, see: prettier.io/docs/en/opt...

Install the two dependencies needed to resolve conflicts

npm i eslint-config-prettier eslint -plugin-prettier -D duplicated code

Add and modify

.eslintrc.cjs
,as follows:

module.exports = { env: { browser: true, es2021: true, node: true, }, -extends: ['standard','eslint-config-standard-with-typescript'], + extends: ['standard','eslint-config-standard-with-typescript','prettier'], parser:'@typescript-eslint/parser', parserOptions: { ecmaVersion: 12, sourceType:'module', project:'./tsconfig.json', }, -plugins: ['@typescript-eslint'], + plugins: ['@typescript-eslint','prettier'], -rules: {}, + rules: { +'prettier/prettier':'error' +} , } Copy code

Then verify that the configuration takes effect, modify

index.ts

-const calc = (a: number, b: number) => { + const calc = (a: number, b: number): number => { return a-b } -//the console.log (Calc (1024, 28)) + the console.log (Calc (1024, 28)) copying the code

Then execute in the console

lint
,Here
prettier
with
eslint
The behavior of has been consistent, if there is no error, then it succeeded.

npm run lint copy the code

We are now done

eslint
with
prettier
Integrated configuration. It has nothing to do with the editor, which means that no matter what editor you use, whether or not you install related plug-ins, it will not affect the effect of code verification.

Husky

Because a project is usually a teamwork, we cannot guarantee that everyone will execute it before submitting the code

lint
Check, so need
git hooks
To automate the verification process, otherwise it is forbidden to submit.

installation

Husky
And generate
.husky
folder

npm i husky -D npx husky install Copy code

Then we need to execute

npm install
Automatically enabled when
husky

if your

npm
Version is greater than or equal to
7.1.0

npm set-script prepare "husky install " copy the code

Otherwise manually

package.json
Add in

"scripts": { "dev": "tsc --watch", "build": "npm run clean && tsc", "lint": "eslint src --ext .js,.ts --cache --fix", "clean": "rm -rf dist", + "prepare": "husky install" }, Copy code

Then add a

lint
hook

npx husky add .husky/pre-commit "npm run lint" copy the code

Is equivalent to manually in

.husky/pre-commit
The file writes the following:

# !/bin/sh . "$(dirname "$0")/_/husky.sh" npm run lint Copy code

Test whether the hook is effective, modify

index.ts

const calc = (a: number, b: number): number => { return a-b } - the console.log (Calc (1024, 28)) +//the console.log (Calc (1024, 28)) copying the code

Then submit a

commit
, It will be executed automatically if the configuration is correct
lint
And prompt 1 error message,
commit
The submission will fail.

git add. git commit -m'test husky' # . 1: error. 7 'Calc' IS A Assigned value But Never Used duplicated code

Commitlint

Why need

Commitlint
, Except in the subsequent generation
changelog
Documents and semantic releases need to be extracted
commit
The information in is also helpful for other students to analyze the code you submitted, so we have to agree
commit
Specifications.

installation

Commitlint

npm i @ commitlint/config-conventional @ commitlint/cli -D duplicated code

Finally

Commitlint
Add to hook

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$ 1"' Copy Code

create

.commitlintrc
And write configuration

{ "extends" : [ "@commitlint/config-conventional" ] } Copy code

Note that the configuration file name used here is

.commitlintrc
Instead of the default
.commitlintrc.js
, See Eslint chapter for details

Test whether the hook is effective, modify

index.ts
To get the code correct

const calc = (a: number, b: number): void => { console.log(a-b) } -//Calc (1024, 28) + Calc (1024, 28) copy the code

Submit a non-compliant

commit
, Submission will fail

git add. git commit -m'add eslint and commitlint' Copy code

Amended to be correct

commit
,Submitted successfully!

git commit -m 'ci: add eslint and commitlint' duplicated code

Angular specification notes:

  • feat : new features
  • fix : fix BUG
  • docs : modify documents, such as README, CHANGELOG, CONTRIBUTE, etc.
  • style : does not change the code logic (only modified spaces, formatting indentation, commas, etc.)
  • refactor : refactoring (neither fix errors nor add functionality)
  • perf : optimization related, such as improving performance and experience
  • test : add tests, including unit tests, integration tests, etc.
  • build : changes to the build system or external dependencies
  • ci : automatic process configuration or script modification
  • chore : non-src and test modification, release version, etc.
  • revert : restore the previous commit

Jest

A good life starts with 100% test coverage.

installation

jest
, And type declaration
@types/jest
, It needs to be executed
ts-node
with
ts-jest

Temporarily fixed here

ts-node
The version is
v9.1.1
, The new version
ts-node@v10.0.0
Will lead to
jest
Report an error, wait for the official fix, see: issues

npm i jest @ types/jest ts-node@9.1.1 ts-jest -D duplicated code

Initialize the configuration file

npx jest --init copy the code

Then modify

jest.config.ts
file

Used IS A PRESET that//AS A Base for Jest apos Configuration -//PRESET: undefined, + PRESET: 'TS-JEST' duplicated code

Add test command to

package.json
in.

"scripts": { "dev": "tsc --watch", "build": "npm run clean && tsc", "lint": "eslint src --ext .js,.ts --cache --fix", "clean": "rm -rf dist", "prepare": "husky install", + "test": "jest" }, Copy code

Create test folder

__tests__
And test files
__tests__/calc.spec.ts

modify

index.ts

const calc = (a: number, b: number): number => { return a-b } -//the console.log (Calc (1024, 28)) + Calc Export default copy the code

Then in

calc.spec.ts
Write test code in

import calc from '../src' test( 'The calculation result should be 996.' , () => { expect(calc( 1024 , 28 )).toBe( 996 ) }) Copy code

Verify that the configuration takes effect

Execute in the console

test
, You will see the result of 100% test coverage.

npm run testCopy code

Finally we give

__tests__
The catalog also adds
lint
check

modify

package.json

"scripts": { "dev": "tsc --watch", "build": "npm run clean && tsc", -"lint": "eslint src --ext .js,.ts --cache --fix", + "lint": "eslint src __tests__ --ext .js,.ts --cache --fix", "clean": "rm -rf dist", "prepare": "husky install", "test": "jest" }, Copy code

Here if we directly execute

npm run lint
Will report an error, prompt
__tests__
Folder is not included in
tsconfig.json
of
include
When we add to
include
After that, the output
dist
It will contain test-related files, which is not the effect we want.

We use

typescript-eslint
The official solution is as follows:

Create a new one

tsconfig.eslint.json
File, write the following:

{ "extends" : "./tsconfig.json" , "include" : [ "**/*.ts" , "**/*.js" ] } Copy code

in

.eslintrc.cjs
Modified in

parserOptions: { ecmaVersion: 12, sourceType:'module', -project:'./tsconfig.json' + project:'./tsconfig.eslint.json' }, Copy code

Then verify whether the configuration is effective, directly submit the test file we added, and the correct submission indicates that the configuration is successful.

git add. git commit -m'test: add unit test' Copy code

Github Actions

We passed

Github Actions
Implement code merge or push to the main branch,
dependabot
Actions such as robot upgrade dependencies will automatically trigger a series of processes such as testing and version release.

Created in the project root directory

.github/workflows
Folder, and then create a new one in it
ci.yml
Files and
cd.yml
file

in

ci.yml
Write in the file:

name: CI ON: Push: branches: - '**' pull_request: branches: - '**' Jobs: Linter: the runs-ON: Ubuntu-Latest Steps: - uses: Actions/V2 Checkout @ - uses: Actions/Node-Setup @ v2 with: node-version: 14 - run: npm ci - run: npm RUN lint Tests: Needs: Linter runs-ON: Ubuntu-Latest Steps: - uses: Actions/Checkout @ v2 - uses: Actions/Setup-the Node @ v2 with: node-version: 14 - run: npm ci - run: npm run test Copy code

The above configuration probably means that monitoring all branches

push
with
pull_request
Action, automatic execution
linter
with
tests
task.

For more usage of GithubActions, please refer to: github.com/features/ac...

Then push the code to verify that the configuration takes effect

git add. git commit -m'ci: use github actions' git push Copy code

At this time, open the Github page of the current project , and then click the Actions menu at the top to see two tasks in progress, one will succeed (test), and the other will fail (release).

The above is only the realization of the automatic code test process, and the following is the realization of the automatic release process.

Need to go to NPM before this register an account on the website (it can be ignored) and create one

package
.

Then create

GH_TOKEN
with
NPM_TOKEN
(Note, do not include any TOKEN information in the code):

Will create two

TOKEN
Add to the Actions secrets of the project :

Github Project Homepage- > Top Settings Menu- > Sidebar Secrets

Then modify

package.json
middle
"Name"
,
"Name"
You are at npm created on
package
The name.

in

cd.yml
Write in the file:

name: CD ON: the Push: branches: - Master pull_request: branches: - Master Jobs: Release: runs-ON: Ubuntu-Latest Steps: - uses: Actions/Checkout @ v2 - uses: Actions/Setup-the Node @ v2 with: the Node-Version : 14 - RUN: NPM CI --ignore-scripts - RUN: NPX Semantic-Release the env: GH_TOKEN: $ {{ secrets.GH_TOKEN }} NPM_TOKEN: $ {{ secrets.NPM_TOKEN }} copy the code

Due to "black life", Github has changed the default branch name of new projects to

"Main"
, See: AND DELINQUENCY , for convenience, called unified behind the main branch

So if your master branch name is

"Main"
, Above
branches
Need to be modified to:

ON: the Push: branches: - main pull_request: branches: - main copy the code

Then install the semantic release dependencies, you need to use

semantic-release
And its plugins:

npm i semantic-release @ semantic- release/changelog @ semantic-release/git -D duplicated code

Create a new configuration file in the project root directory

.releaserc
And write:

{ "branches" : [ "master" ], "plugins" : [ "@semantic-release/commit-analyzer" , "@semantic-release/release-notes-generator" , "@semantic-release/changelog" , " @semantic-release/github" , "@semantic-release/npm" , "@semantic-release/git" ] } Copy code

Same here, if your master branch name is

"Main"
, Above
branches
Need to be modified to:

"branches" : [ "+([0-9])?(.{+([0-9]),x}).x" , "main" ], copy the code

Finally, create a new branch develop branch and submit work content.

git checkout -b develop git add. git commit -m'feat: complete the CI/CD workflow' git push --set-upstream origin develop git push Copy code

Then develop branches merged into the main branch , and submit Note: This submission will trigger test and release version (created automatically

tag
with
changelog
)

git checkout master git merge develop git push Copy code

After completing the above operations, open the Github project home page and the NPM project home page to see a release update record.

Finally switch back to the develop branch and create an automatically updated dependency

workflow
.

in

.github
Create in folder
dependabot.yml
File and write content:

version: 2 updates: # Enable version updates for npm - package-ecosystem: 'npm' # Look for `package.json` and `lock` files in the `root` directory directory: '/' # Check the npm registry for updates Day Every (Weekdays) Schedule: interval The: 'Weekly' duplicated code

Submit and view workflows if all pass, then merged into the main branch and submit this submission does not trigger release.

git pull origin master git add. git commit -m'ci: add dependabot' git push git checkout master git merge develop git push Copy code

Two conditions are required to trigger the release of the version:

  1. Only if
    push
    with
    pull_request
    To the main branch on will trigger release
  2. only
    commit
    Prefixed with
    feat
    ,
    fix
    ,
    perf
    Will be released, otherwise skip.

For more release rules, see: github.com/semantic-re...

How to use SemanticRelease, see: semantic-release.gitbook.io

If you can configure all the above steps correctly and publish successfully, then congratulations! You have a fully automated project, it has: automatic dependency update, testing, release, and automatic generation of version information and other functions.

Complete project example: @resreq/event-hub

Concluding remarks

This article does not involve: component library, Monorepo, Jenkins CI and other configurations, but it can cover most of the front-end project CI/CD process.

Some places are more detailed or even verbose, but I still hope to help everyone! Sprinkle flowers!