Build a React components NPM package and CI/CD with Github Action
Tags: react, npm, github, action
Published: 2020-04-10 22:04:06

Lockdown in NZ creates spare time for me to enjoy something occupied before with chore. Recently, I started to summarize the React framework of SmartERP that a SaaS service I led before, try some new solutions. I would like to demonstrate how to build an NPM (Node.js Package Manager) package and implement CI/CD with Github Action to automate most of the work.

Preparation
1. Create a new public repository "ETSOO/etsoo-react" on GitHub.
2. Change to the target folder, clone the repository into it: "git clone https://github.com/ETSOO/etsoo-react".
3. Install the latest Node.js to your computer(https://nodejs.org/en/).
4. Run 'Node.js command prompt', input command 'npm init' to initialize an NPM package. 'npm init -y' will not ask any question and produce the package.json file with default values. 'npm init --scope=``<your_org_name>``' to create an Org scoped package.
5. 'git add package.json', multiple files are separated by a blank (Boring? learn something from https://stackabuse.com/git-add-all-files-to-a-repo/, 'git add -A'), then 'git commit -m "Message about the commit"' make changes to the local depository, then 'git push origin master' to upload changes to Github. If you made changes on Github, you need to first pull the updates 'git pull origin master' and then push the local version. Beware of any conflicts here. Run 'git stash git pull git stash pop' to keep local updates.
6. Create an account in the NPM registry (https://www.npmjs.com/signup). Enter the command 'npm login', provide account details you just have to complete it. Enter the command 'npm publish' to publish, every time needs to upgrade the "version" under package.json.
7. IDE(Integrated Development Environment): Visual Studio Code, https://code.visualstudio.com/

React & TypeScript:
1. 'npm install -g typescript react react-dom @types/react @types/react-dom' install the minimal set of dependencies required to React and TypeScript.
2. Create a folder 'src', create an 'index.ts' under it. Change package.json, set "main": "lib/index.js" (It's not so perfect to include the ts files directly, with "src/index.ts" will cause parse error, seems tsc will not compile files under node_modules); "types": "lib/index.d.ts", scripts add "build": "tsc".
3. Copy a 'tsconfig.json' from another project to the root and change settings as you want or 'npx tsc --init'. Set "declaration": true, generates corresponding definitions in index.d.ts. "jsx": "react" if include 'tsx' files. "outDir": "./lib" tell the compiler that the 'src' folder will be compiled to javascript in 'lib' folder. Add the folder name to '.gitignore'. Make sure "noEmit": false.
4. If unknown errors occurred, run 'npm install' to check the dependencies and install any missing packages.
5. Test your new NPM module without publishing it (https://medium.com/@the1mills/how-to-test-your-npm-module-without-publishing-it-every-5-minutes-1c4cb4b369be). Run 'npm link' to define a global link. Then go to the project share this package, run 'npm link etsoo-react' to add the global link to the 'node_modules' like it has been published. Run '' to remove the link. Run 'npm unlink --no-save <module_name>' on your project’s directory to remove the local symlink, and run 'npm unlink' on the module’s directory to remove the global symlink. (26/05/2021 update: just install local package with: npm install file:../packagename and the link will be created automatically)

Testings:
1. Run 'npm i jest @types/jest ts-jest -D' to install the testing framework Jest (https://jestjs.io/). Add a folder 'tests' under the root, add a test script file to pass the 'npm test' command.
2. Add "jest": "^25.3.0", under package.json/devDependencies if not exits and run 'npm install'.
3. add   "jest": {
    "testMatch": [ "<rootDir>/tests/**/*.js" ]
  }, to the package.json. Limit Jtest to the folder "__test__" under root.
4. Install vscode-jest
5. npm install -D react-test-renderer
6. npm install -D babel-jest babel-core@^7.0.0-bridge.0 @babel/core @babel/preset-env regenerator-runtime (https://jestjs.io/docs/en/22.x/getting-started.html)
7. npm install --save-dev enzyme enzyme-adapter-react-16 @types/enzyme @types/enzyme-adapter-react-16

tsconfig.json:
```
{
  "compilerOptions": {
    "outDir": "./lib",
    "target": "ES2018",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": false,
    "jsx": "react",
    "declaration": true
  },
  "include": [
    "src"
  ]
}
```
package.json:
```
{
  "name": "etsoo-react",
  "version": "1.0.2",
  "description": "ETSOO React TypeScript components NPM package",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "scripts": {
    "build": "tsc",
    "test": "jest"
  },
  "jest": {
    "testMatch": [ "<rootDir>/tests/**/*.js" ]
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/ETSOO/etsoo-react.git"
  },
  "author": "Garry Xiao",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/ETSOO/etsoo-react/issues"
  },
  "homepage": "https://github.com/ETSOO/etsoo-react#readme",
  "devDependencies": {
    "@types/react": "^16.9.33",
    "@types/react-dom": "^16.9.6",
    "jest": "^25.3.0",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "typescript": "^3.8.3"
  }
}
```
Github Actions:
1. Choose 'Publish Node.js Package' from 'Popular continuous integration workflows' under the Actions tab and create the template YAML file under 'etsoo-react/.github/workflows/'. View https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#about-yaml-syntax-for-workflows for help.
2. Log in NPM, under 'Auth Tokens', create a new one and copy the token, then go to Github repository's settings, secrets, 'add a new secret' named 'GXNpmToken' here.
3. Github desktop (https://desktop.github.com/) is an interesting tool to help you manage depositories.

YAML file content, please view https://github.com/ETSOO/etsoo-react/blob/master/.github/workflows/npmpublish.yml. After you push the changes, the Action will execute and later you will receive a notification email from NPM. That's really exciting!

(2020/7/23) Setting up ESLint to work with TypeScript (https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project)
1. npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D.
2. npx eslint --init, choose a popular styling like airbnb, json format.
3. "comma-dangle": ["error", "never"], "no-console": "off", "arrow-parens": "off", "linebreak-style": "off".
4. npm install prettier -D. Install prettier extension in VSCode. "File" -> "References" -> "Settings" -> search "Format On Save".
5. npm install -D eslint-config-prettier. Create a local configure file '.prettierrc'.
Example project: https://github.com/ETSOO/restclient

If you want to upgrade all dependencies, should be very careful, please follow: https://flaviocopes.com/update-npm-dependencies/
1. npm outdated, give a list.
2. npm install -g npm-check-updates, install the tool.
3. ncu -u, update the version.
4. npm update / npm install

Here is the help link about how to create a template repository and how to use it: https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-template-repository