Node.js プロジェクトを立ち上げる手順¶
Warning
途中に出てくるテンプレートと、この記事の内容が異なるので注意。
環境¶
- Windows 10 22H2 (Build 22621.2428)
- Node.js v20.9.0
- npm v9.5.1
- pnpm v8.8.0
- typescript
- @types/node
- ts-node
- ts-node-dev
- prettier
- eslint
- eslint-config-standard
- eslint-config-prettier
- eslint-plugin-import
- eslint-plugin-n
- eslint-plugin-promise
- eslint-plugin-unicorn
- yarn-run-all
- @typescript-eslint/parser
- @typescript-eslint/eslint-plugin
- @vercel/ncc
- @book000/node-utils
package.json の作成¶
name
: プロジェクトの名前。大文字を含んではならない。version
: プロジェクトバージョンdescription
: プロジェクトの説明main
: エントリポイントのファイルパスhomepage
: Web サイトへの URLrepository
: GitHub への Git URLbugs
: バグトラッカーへの URLauthor
: 作者名。メールアドレスを入れる場合はXXXXX <[email protected]>
のようにする。license
: プロジェクトに適用するライセンス。MIT
などprivate
: true の場合、パッケージの公開を制限する
yarn init
を使ってもよいし、そのまま package.json
を作成して作ってもよい。
❯ yarn init
yarn init v1.22.19
question name (test):
question version (1.0.0):
question description:
question entry point (index.js):
question repository url:
question author:
question license (MIT):
question private:
success Saved package.json
Done in 5.07s.
https://garafu.blogspot.com/2016/11/packagejson-specification-ja.html
Node.js バージョンの指定¶
fnm などの Node.js バージョン管理ツールを利用している場合、.node-version
などでプロジェクトで使用するバージョンを決定しておいたほうが安心。
node -v > .node-version
また、fnm などを利用していない人向けに package.json
で対象の Node.js バージョンを明記しておく。
以下の bash スクリプトで設定可能。
# bash (Windowsの場合はWSLなど)で実行すること
# .node-version ファイルを読み取り、改行と `v` を消す
# package.json を上書きはできないので、テンポラリファイルをつくる
jq --rawfile version .node-version '.engines.node |= ($version | gsub("[v\\r\\n]"; ""))' package.json > package.json.tmp
mv package.json.tmp package.json
# .node-version のバージョン固定になるので、18.x とか範囲指定にするといいかも。
.gitignore を追加する¶
wget -O .gitignore https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
Commit
git commit -am "feat: init"
GitHub Actions による Node.js CI¶
book000/templates の nodejs-ci.yml
を利用して、CI ワークフローを導入する。
mkdir -p .github/workflows ; wget -O .github/workflows/nodejs-ci.yml https://raw.githubusercontent.com/book000/templates/master/workflows/nodejs-ci.yml
必要に応じ、with
オプションの disabled-jobs
で実行しないスクリプト(depcheck
など)を無効化すること。
無効化する必要がないなら with
オプションを削除すること。
Commit
git commit -am "ci: add node.js ci"
TypeScript 環境の構築¶
まず、typescript
パッケージを依存関係に追加する。
yarn add -D -E typescript @types/node
そのあと、tsconfig.json
を書く。
tsconfig.json サンプルと説明
{
"ts-node": { "files": true },
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"moduleResolution": "Node",
"lib": ["ESNext", "ESNext.AsyncIterable"],
"outDir": "./dist",
"removeComments": true,
"esModuleInterop": true,
"allowJs": false,
"incremental": true,
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"experimentalDecorators": true,
"baseUrl": ".",
"newLine": "LF",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*"]
}
ts-node
: のちに追加するts-node
でcompilerOptions.paths
を機能させるためのおまじない?compilerOptions
target
: コンパイル後の成果物をどの ECMAScript バージョンを対象とする。とりあえずes2020
module
: モジュールの読み込み方法moduleResolution
: モジュールの解決方法lib
: Proposal な機能の追加導入outDir
: コンパイル後の出力先removeComments
: コンパイル時にコメントを消すesModuleInterop
:import * as xxx from "xxx"
をimport xxx from "xxx"
と書けるようにするallowJs
:.js
と.jsx
をコンパイル対象とするかincremental
: 差分コンパイルを有効sourceMap
:.map
ファイルを生成するかdeclaration
:export
した型定義の.d.ts
を生成するかdeclarationMap
:export
した型定義のd.ts.map
を生成するかstrict
: 以下を有効にするnoImplicitAny
:any
になりうる値をエラーとするnoImplicitThis
:this
がany
型になりうる場合をエラーとするalwaysStrict
:"use strict";
を入れるstrictBindCallApply
:bind
,call
,apply
の使用時に厳密な型チェックを行うstrictNullChecks
:null
になりうる値に対してメソッドなどの呼び出しをエラーとするstrictFunctionTypes
: より厳密な関数型のチェックを行う?strictPropertyInitialization
: クラスのインスタンス変数の初期化を宣言時かコンストラクタで行っていない場合にエラーとする
noUnusedLocals
: 宣言しているのに使っていない変数をエラーとするnoUnusedParameters
: 定義しているのに使っていない関数パラメータをエラーとするnoImplicitReturns
: いずれかの条件でreturn
していない場合にエラーとするnoFallthroughCasesInSwitch
:switch
でbreak
やreturn
が抜けている場合にエラーとするnoUncheckedIndexedAccess
: 配列の要素やプロパティアクセスでundefined
になりうる場合にエラーとするbaseUrl
: プロジェクトのルートパスを指定するnewLine
: ファイルの改行コードを指定するpaths
: 長いパスを短くするために@/...
をマップする
include
: コンパイル対象を指定する
https://typescriptbook.jp/reference/tsconfig/tsconfig.json-settings
https://qiita.com/ryokkkke/items/390647a7c26933940470
最後に、コンパイルして実行のプロセスを 1 コマンドで行う ts-node
を導入する。
yarn add -D -E ts-node ts-node-dev
package.json
の scripts
に以下を追加する。
{
"scripts": {
"start": "ts-node -r tsconfig-paths/register ./src/main.ts",
"dev": "ts-node-dev --poll -r tsconfig-paths/register ./src/main.ts"
}
}
Commit
git commit -am "feat: add typescript"
Lint / Formatter ツールの導入¶
Lint / Formatter として、ESLint と Prettier をいれる。
Prettier と ESLint はベストプラクティスが変わりに変わっているので、何が適切かわからない。
とりあえず参考記事に従って、ESLint に Linter・Prettier に Formatter と責務を分けて設定することにする。
...と思っていたけど、微妙に気に入らない点がいくつか出てきたので Lint と Formatter を両方 ESLint に変える方向性を模索したいと思う。
まずはパッケージを追加。
yarn add -D -E prettier eslint eslint-config-standard eslint-config-prettier eslint-plugin-import eslint-plugin-n eslint-plugin-promise eslint-plugin-unicorn yarn-run-all @typescript-eslint/parser @typescript-eslint/eslint-plugin
Prettier¶
先に、Prettier の設定から行う。.prettierrc.yml
に以下の設定を書き込む。
(ファイル名は Prettier が認識するものならなんでも良いのだが、個人的な好みで Prettier と ESLint の設定ファイルは YAML を使っている)
printWidth: 80
tabWidth: 2
useTabs: false
semi: false
quoteProps: 'as-needed'
singleQuote: true
jsxSingleQuote: true
trailingComma: 'none'
bracketSpacing: true
bracketSameLine: true
arrowParens: 'always'
endOfLine: lf
printWidth
: 行長さを 80 文字に制限するtabWidth
: インデントをスペース 2 つにするuseTabs
: インデントにタブを使うか。false なのでスペースを使うsemi
: 末尾のセミコロンをなくすquoteProps
: プロパティ指定時、クオートするsingleQuote
: シングルクオートを利用するjsxSingleQuote
: JSX でシングルクオートを利用するtrailingComma
: 末尾のカンマを消すbracketSpacing
:{ hoge: true, piyo: false }
のように角括弧の内側にスペースをいれるbracketSameLine
: HTML で>
をその行の最後に置くarrowParens
: アロー関数の引数で括弧を必ず入れるendOfLine
: ファイルの改行コードを LF にする
https://prettier.io/docs/en/options.html
ESLint¶
次に、ESLint の設定をする。.eslintrc.yml
に以下の設定を書き込む。
env:
es2020: true
node: true
extends:
- standard
- plugin:@typescript-eslint/recommended
- plugin:unicorn/recommended
- prettier
parser: '@typescript-eslint/parser'
parserOptions:
ecmaVersion: latest
sourceType: module
project: ./tsconfig.json
plugins:
- '@typescript-eslint'
rules:
'@typescript-eslint/no-explicit-any': off
'@typescript-eslint/ban-ts-comment': off
'unicorn/prefer-top-level-await': off
'unicorn/no-null': off
'no-console': error
extends
: 必ずprettier
は最後にすることrules
@typescript-eslint/no-explicit-any
: 明示的なany
を使えるようにする@typescript-eslint/ban-ts-comment
:@ts-ignore
を使えるようにするunicorn/prefer-top-level-await
: トップレベルの await を優先しなくするunicorn/no-null
: null を制限することをやめるno-console
:console.log
などをエラーとする(あとで winston を導入するため)
.eslintignore
に除外するファイル・ディレクトリを記述する。
dist
output
node_modules
data
logs
https://hisa-tech.site/bestpractice-eslint-and-prettier/
scripts に lint と lintfix コマンドを追加する¶
package.json
の scripts
に以下を追加する。
{
"scripts": {
"lint": "run-p -c lint:prettier lint:eslint lint:tsc",
"lint:prettier": "prettier --check src",
"lint:eslint": "eslint . --ext ts,tsx",
"lint:tsc": "tsc",
"fix": "run-s fix:prettier fix:eslint",
"fix:eslint": "eslint . --ext ts,tsx --fix",
"fix:prettier": "prettier --write src"
}
}
Commit
git commit -am "feat: add linter"
ncc を使って 1 ファイルにコンパイル¶
@vercel/ncc を使って、1 ファイル + α にコンパイルする。
yarn add -D -E @vercel/ncc
package.json
の scripts
に以下を追加する。
{
"scripts": {
"package": "run-s clean compile packing",
"packing": "ncc build ./dist/main.js -o output/ -m",
"compile": "tsc -p .",
"clean": "rimraf dist output"
}
}
Commit
git commit -am "feat: add ncc"
winston でロギング¶
yarn add -D -E @book000/node-utils
その後、ESLint のルールに no-console: error
を、(Dockerfile に ENV LOG_DIR /data/logs
を)追加する。
Commit
git commit -am "feat: winston logging"
renovate¶
以下を renovate.json
に書き込む。
{
"extends": [
"config:base"
],
"ignorePresets": [
":prHourlyLimit2"
],
"timezone": "Asia/Tokyo",
"dependencyDashboard": false,
"automerge": true,
"branchConcurrentLimit": 0
}
ここまでのテンプレート¶
irm https://raw.githubusercontent.com/book000/memo/main/docs/programming/nodejs/template/all.ps1 | iex
設定ファイル用の JSON Schema を生成¶
設定ファイルの JSON Schema を TypeScript の型定義から生成する。
yarn add -D -E typescript-json-schema
どのファイルでもよいので、以下のような設定ファイルの型定義を作る。必ず export
すること。
export interface Configuration {
username: string
password: string
}
package.json
の scripts
に以下を追加する。
{
"scripts": {
"generate-schema": "typescript-json-schema --required tsconfig.json Configuration -o schema/Configuration.json",
}
}
設定ファイルには以下のように JSON Schema を定義できる。
{
"$schema": "../schema/Configuration.json"
}
Commit
git commit -am "feat: add config schema"
Docker で動作¶
book000/templates の node-ncc-app.Dockerfile
と docker.yml
を使う。
wget -O Dockerfile https://raw.githubusercontent.com/book000/templates/master/dockerfiles/node-ncc-app.Dockerfile
mkdir -p .github/workflows ; wget -O .github/workflows/docker.yml https://raw.githubusercontent.com/book000/templates/master/workflows/docker.yml
- winston を導入している場合、
ENV LOG_DIR /data/logs
をDockerfile
に追加すること -
docker.yml
のwith
にて、targets
を変更すること
Commit
git commit -am "feat: docker"