# github package && npm repo
## 開發流程
``` mermaid
graph TD;
local_package-->npm_login;
npm_login-->npm_build;
npm_build--->github_repo;
github_repo-->git_action;
git_action-->npm_publish;
npm_publish-->github_package;
npm_publish-->npm_repo;
```
## init npm package
將 npm 初始化
```typescript=
npm init --scope=@my-org
npm init --scope=@my-username
```
### npm_login :
**1. publish to npm repo**
驗證npm publish權限,登入npm 帳號。
**指令:npm login**
**2. publish to github package**
驗證npm publish權限,登入 github repo ower 帳號。
**指令 : npm login --scope=@OWER --registry=https://npm.pkg.github.com**
**注意** : 這時登入的密碼需要透過生成 **Personal access tokens** 當作密碼,以確保發布者的所有權限。
[如何生成Personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
### npm_publish
**npm publish** (發布私有包)
**npm publish --access public** (發布公開包)
**注意 :** **npm publish** 預設是私有狀態,如果 **package.json** 中有設定 **"private": false** 將會報錯
---
### npm_update
如果要將npm update的畫板好記得更新
```typescript
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]
npm publish
```
如果直接 `npm publish`會得到以下的error,原因是如果發布同版本的 pkg ,npm 因為 security policy 關係不能重複上傳相同版本,所以記得更新版本號

### npm_build
在 **package.json** 執行npm script測試打包後的模組結構指令如下。
```json=
//package.json
"scripts": {
"dev": "next dev",
"start": "next start",
"lint": "next lint",
"build-next": "next build",
"build": "rm -rf dist/ && npm run build:esm && npm run build:cjs && npm run build:css",
"build:css": "tailwindcss build styles/globals.css -o public/styles.css --minify && copy-files-from-to",
"build:esm": "tsc --project tsconfig.production.json",
"build:cjs": "tsc --project tsconfig.production.json --module CommonJS --outDir dist/cjs",
"prepublish": "npm run build"
},
"copyFiles": [
{
"from": "public/styles.css",
"to": "dist/main.css"
}
],
```
### 打包規則
* **build:css** :因為專案是用 tailwindcss 開發,所以需要打包 css 檔案,之後透過 copy-files-from-to 套件將打包好的 css 放到 dist 資料夾中。
* **build:esm && build:cjs** : 透過 tsconfig.production.json 打包需要模塊到 dist 資料夾中。
* **copyFiles** :讓 copy-files-from-to 知道哪些哪檔案需要移入 。
#### 打包建議
因為打包套件很像在寫測試他是獨立的專案,所以建議把所有型別放 _TYPES_ 資料夾中去管理,之後引入套件才不會出錯。

**備註 :** 因為專案中也需要做 ts 檢測,所以會分成 tsconfig.production.json 作為打包的設定檔。
### 打包後的結構如下
使用套件時會考慮開發者使用什麼模塊所以會分別打包 **cjs(CommandJS)** 跟 **esm(esModule)** 兩種模塊,以及套件中用到的所有樣式檔案都在 main.css 中 。

每一個 component因為 tsconfig.production.json 設定的關係所以會有
* *.d.ts 型別宣告檔
* 基礎模塊*.js引用入口
* *.js.map 錯誤代碼位置讓之後更新套件方便查錯

### 如何使用套件
> #### step.1 在 _app.tsx 中引用打包好的樣式文件

> #### step.2 根據套件位置引入

---
### git_action
**git action** 是一個 **CI** 工具,讓使用者可以推送到 **github repo** 時自動執行動作,例如推送打包的套件到 **npm repo** 或 **github PKG** ,**github** 稱以上的動作叫 **workflow**,**workflow** 可以在專案中新增以下的資料夾來啟用。

```yml=
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
name: Node.js Package
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: npm test
publish-gpr:
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://npm.pkg.github.com/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
```
更多 **git action** 教學請看以下的連結 :
[教學](https://docs.github.com/en/actions)
---
## 本地測試套件使用情形
``` mermaid
graph TD;
local_package-->npm_link;
local_package-->npm_install;
npm_link-->another_project;
npm_install-->another_project;
```
### 在本地測試套件有兩種方式 :
**1. npm install ~/[PACKAGE_PATH]**
安裝方式跟平時使用 **npm i** 的方式一樣,差別在於要指定套件路徑去安裝。
**2. npm link**
使用npm link 之前要在套件專案下npm link指令,之後到要測試的專案使用 **npm link [@OWER/PACKAGENAME]**
**npm link** 其實就是將專案復用到電腦根目錄的位置,使用npm link 就是讓其他專案與根目錄的.nvm檔案做連結去使用。
> 結構如下 :

---
## package.json
### npm相關套件設定
```json=
{
"name": "@ani1788/danny",
"version": "1.0.0",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"private": false,
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/ani1788/point-trade-platform-ui"
},
"publishConfig": {
"@ani1788:registry": "https://npm.pkg.github.com"
},
"scripts": {
"dev": "next dev",
"start": "next start",
"lint": "next lint",
"build-next": "next build",
"build": "rm -rf dist/ && npm run build:esm && npm run build:cjs && npm run build:css",
"build:css": "tailwindcss build styles/globals.css -o public/styles.css --minify && copy-files-from-to",
"build:esm": "tsc --project tsconfig.production.json",
"build:cjs": "tsc --project tsconfig.production.json --module CommonJS --outDir dist/cjs",
"prepublish": "npm run build"
},
"copyFiles": [
{
"from": "public/styles.css",
"to": "dist/main.css"
}
],
"dependencies": {
"@svgr/webpack": "^6.4.0",
"next": "12.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@types/node": "^18.7.18",
"@types/react": "18.0.20",
"@types/react-dom": "18.0.6",
"autoprefixer": "^10.4.12",
"eslint": "8.23.1",
"eslint-config-next": "12.3.1",
"postcss": "^8.4.17",
"tailwindcss": "^3.1.8",
"typescript": "4.8.3"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
}
```
**name(套件名稱)** : @OWER/YOUR_PROJECT 。
**version** : 套件版本。
**main** :模塊預設進入的路徑。
**files** :npm build 時要包含哪些資料夾。
**repository** :package存放位置。
**prepublish** :這個指令是執行 **npm publish** 時會做的指令,使用情境很像用 **husky** 的 **precommit** 功能,目的在於部署前打包一個 **dist** 資料夾後推送上去。
**publishConfig** : 告訴你 **publish** 的 **repo** 套件擁有者跟套件名稱。
**peerDependencies** : 當使用者下載你發佈的套件後會發現也把開源者使用的 **library** 也一起下載了,這樣會有重複下載其他套件的問題,會造成其他套件版本衝突,這時 **peerDependencies** 就是告訴 **npm** 我下載這個包時只會指定下載哪些使用者需要的 **library**。
---
## tsconfig.production.json
```json=
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": true,
"esModuleInterop": true,
"jsx": "react",
"lib": ["es5", "es2015", "es2016", "dom", "esnext"],
"types": ["node"],
"module": "es2015",
"moduleResolution": "node",
"noImplicitAny": true,
"noUnusedLocals": true,
"outDir": "dist/esm",
"sourceMap": true,
"strict": true,
"target": "es6"
},
"include": ["./components/**/*.ts", "./components/**/*.tsx"]
}
```
**declaration** : 打包時自動生成宣告檔。
**esModuleInterop** : 作用是支持使用import d from 'cjs'的方式引入commonjs包。
**jsx** : jsx檔案要用什麼方式解析。
**sourceMap** : 自動生成sourceMap。
**outDir** : 打包後的路徑。
**include** : 哪些檔案需要經過編譯。
---
## .npmrc( 套件權限資訊 )
```npm=
npm.pkg.github.com/:_authToken=YOUR_TOKEN
@OWER:registry=https://npm.pkg.github.com
```
**_authToken** : **npm login** 密碼
**@OWER** : github repo OWER
### customer npm init
https://blog.greenroots.info/tips-to-customize-npm-init-to-make-it-your-own
### 2023 publish npm
https://www.youtube.com/watch?v=eh89VE3Mk5g&ab_channel=MattPocock
## npm 相關補充
### 查詢沒在使用的套件
```
> npx depcheck
```
### 查詢沒在使用的套件,排除missing
```
npx depcheck --skip-missing
```
### expo 兼容性
查看專案中與expo 47兼容的版本
```
expo update 47
```
### nvm management local node version permanent
```typescript
# this will use latest node version
> nvm alias default node
> nvm use default
# use specific node version
# nvm set default node.js version 16.14.2
> nvm alias default 16.14.2
> nvm use
> node -v
# v16.14.2
```
### 查詢 local 已安裝的 global 套件
```typescript
> npm ls -g
/Users/danny/.nvm/versions/node/v20.0.0/lib
├── @arelstone/traduora-cli@1.1.3
├── @danny101201/commander@1.0.1
├── commander@npm:@danny101201/commander@1.0.1 -> ./../../../../../develpoment/learn/node/commander
├── corepack@0.17.2
├── eas-cli@3.15.0
├── firebase-tools@12.4.3
├── json-server@0.17.3
├── json5@2.2.3
├── lightningcss-cli@1.21.5
├── npm@9.7.1
├── tsx@3.12.7
├── typeorm@0.3.17
└── typescript@4.9.5
```
從資料來看 npm global install 會存放在 `.nvm` 中,並根據 node version 分類套件安裝的node 版本。

### npm bin
```typescript
// package.json
{
"name": "@danny101201/commander",
"version": "1.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"bin": {
"commander_tests2": "index.js"
},
"dependencies": {
"commander": "^10.0.0"
}
}
```
bin 功能是將你定義的 cli 名稱去印射到你想執行的 script,例如底下我想在全局透過呼叫 `commander_tests2` 而去執行 index.js 內容。
在 package.json 中指定 bin 會讓你在 npm i -g 或是 npm link 時,在 `.nvm/versions/node` 底下去保存你在 bin 定義的 file,檔名是 key檔案內容是 value

這邊記得在你想成形的 script 中加 usr 宣告
```typescript
#!/usr/bin/env node
// index.js
```
這樣你就可以透過 cli 方式透過打 `commander_tests2` 去執行 `index.js` 裡面的內容

## 根據 lib 引入方式指定導入什麼 file
建議所有入境都加上副檔名,否則 `nodejs` 會 `import` 錯誤檔案,可能會把 `commjs` 當成 `esModule`
```typescript
// pageage.json
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./presets": {
"import": {
"types": "./dist/presets.d.ts",
"default": "./dist/presets.js"
},
"require": {
"types": "./dist/presets.d.cts",
"default": "./dist/presets.cjs"
}
},
"./package.json": "./package.json"
}
```
## 在 esm 引入 cjs
```typescript
// file.mjs
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const pkg = require('pkg-commonjs');
```
https://medium.com/dcardlab/%E4%B8%80%E7%AF%87%E6%96%87%E6%90%9E%E6%B8%85%E6%A5%9A-node-js-%E6%A8%A1%E7%B5%84%E8%A1%8C%E7%82%BA-%E8%87%AA%E7%94%B1%E9%81%8B%E7%94%A8-commonjs-%E8%88%87-esm-%E6%A8%A1%E7%B5%84-5f1e393276ba
## pnpm update
https://www.youtube.com/watch?v=9AyfXGHu56Y&ab_channel=MayankSrivastava
```typescript
> pnpm --recursive update @typescript-eslint/parser@latest
```
## 檢查可以升級的 package
https://www.freecodecamp.org/chinese/news/how-to-update-npm-dependencies/
```typescript
> pnpm outdated
```
## publish public package
```typescript
// package.json
{
"publishConfig": {
"access": "public"
}
}
```