ポートフォリオ作ってみる。JamStack 学習目的で。
はじめに
JamStackを学習する目的で、ブログを作ってみる。
環境
"dependencies": {
"@next/font": "^13.2.4",
"microcms-js-sdk": "^2.3.2",
"next": "13.2.4",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/node": "18.15.3",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"eslint": "^8.36.0",
"eslint-config-next": "13.2.4",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"husky": "^8.0.0",
"jest": "^29.5.0",
"lint-staged": "^13.2.0",
"prettier": "^2.8.4",
"sass": "^1.59.3",
"ts-jest": "^29.0.5",
"typescript": "5.0.2"
}
その他
node: 18.15.0
yarn: 1.22.19
hosting は vercel で行う
CSS を選定する過程
CSS Modules
理由
- Next.js はビルトインサポートしているから
- CSS Modules を推している感じはある(解釈)
- CSS をファイルとして出力してくれるというのも評価点
- 描画のために読み込むCSSを最小限にできる(自分がする)
- CSS Modulesの方が、『ロードタイム・ランタイムともに、パフォーマンス面で有利』なよう
デメリットは
- いちいち命名しなくてはならないところ
参考
GPT-4のサポートを検討
例
- 複数のモジュールを渡してドキュメント化
- テスト作成
- 新規モジュール開発
参考
- ESLintやPrettierの設定内容を1つ1つ教えてくれた
環境構築
nextプロジェクトを作成
TypeScript対応のNext.jsアプリケーションのプロジェクトを作成
$ npx create-next-app personal-blog --typescript --no-tailwind --eslint
-
--typescript
: TypeScriptプロジェクトとして初期化 -
--no-tailwind
: Tailwind CSSの設定なしで初期化 -
--eslint
: ESLintの設定の初期化
$ npx create-next-app personal-blog --typescript --no-tailwind --eslint
実行結果
Creating a new Next.js app in /personal-blog/personal-blog.
Using yarn.
Installing dependencies:
- react
- react-dom
- next
yarn add v1.22.19
info No lockfile found.
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved lockfile.
success Saved 16 new dependencies.
info Direct dependencies
├─ next@13.2.4
├─ react-dom@18.2.0
└─ react@18.2.0
info All dependencies
├─ @next/env@13.2.4
├─ @next/swc-darwin-arm64@13.2.4
├─ @swc/helpers@0.4.14
├─ caniuse-lite@1.0.30001466
├─ client-only@0.0.1
├─ js-tokens@4.0.0
├─ nanoid@3.3.4
├─ next@13.2.4
├─ picocolors@1.0.0
├─ postcss@8.4.14
├─ react-dom@18.2.0
├─ react@18.2.0
├─ scheduler@0.23.0
├─ source-map-js@1.0.2
├─ styled-jsx@5.1.1
└─ tslib@2.5.0
✨ Done in 11.24s.
Installing devDependencies:
- eslint
- eslint-config-next
- typescript
- @types/react
- @types/node
- @types/react-dom
yarn add v1.22.19
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved lockfile.
success Saved 185 new dependencies.
info Direct dependencies
├─ @types/node@18.15.3
├─ @types/react-dom@18.0.11
├─ @types/react@18.0.28
├─ eslint-config-next@13.2.4
├─ eslint@8.36.0
└─ typescript@5.0.2
info All dependencies
├─ @babel/runtime@7.21.0
├─ @eslint-community/eslint-utils@4.3.0
├─ @eslint-community/regexpp@4.4.0
├─ @eslint/eslintrc@2.0.1
├─ @eslint/js@8.36.0
├─ @humanwhocodes/config-array@0.11.8
├─ @humanwhocodes/module-importer@1.0.1
├─ @humanwhocodes/object-schema@1.2.1
├─ @next/eslint-plugin-next@13.2.4
├─ @nodelib/fs.scandir@2.1.5
├─ @nodelib/fs.stat@2.0.5
├─ @nodelib/fs.walk@1.2.8
├─ @pkgr/utils@2.3.1
├─ @rushstack/eslint-patch@1.2.0
├─ @types/json5@0.0.29
├─ @types/node@18.15.3
├─ @types/prop-types@15.7.5
├─ @types/react-dom@18.0.11
├─ @types/react@18.0.28
├─ @types/scheduler@0.16.2
├─ @typescript-eslint/parser@5.55.0
├─ @typescript-eslint/scope-manager@5.55.0
├─ @typescript-eslint/typescript-estree@5.55.0
├─ acorn-jsx@5.3.2
├─ acorn@8.8.2
├─ ajv@6.12.6
├─ ansi-regex@5.0.1
├─ ansi-styles@4.3.0
├─ argparse@2.0.1
├─ aria-query@5.1.3
├─ array-buffer-byte-length@1.0.0
├─ array-includes@3.1.6
├─ array-union@2.1.0
├─ array.prototype.flat@1.3.1
├─ array.prototype.tosorted@1.1.1
├─ ast-types-flow@0.0.7
├─ axe-core@4.6.3
├─ axobject-query@3.1.1
├─ balanced-match@1.0.2
├─ brace-expansion@1.1.11
├─ braces@3.0.2
├─ callsites@3.1.0
├─ chalk@4.1.2
├─ color-convert@2.0.1
├─ color-name@1.1.4
├─ concat-map@0.0.1
├─ cross-spawn@7.0.3
├─ csstype@3.1.1
├─ damerau-levenshtein@1.0.8
├─ deep-is@0.1.4
├─ define-lazy-prop@2.0.0
├─ emoji-regex@9.2.2
├─ enhanced-resolve@5.12.0
├─ es-get-iterator@1.1.3
├─ es-set-tostringtag@2.0.1
├─ es-to-primitive@1.2.1
├─ escape-string-regexp@4.0.0
├─ eslint-config-next@13.2.4
├─ eslint-import-resolver-node@0.3.7
├─ eslint-import-resolver-typescript@3.5.3
├─ eslint-module-utils@2.7.4
├─ eslint-plugin-import@2.27.5
├─ eslint-plugin-jsx-a11y@6.7.1
├─ eslint-plugin-react-hooks@4.6.0
├─ eslint-plugin-react@7.32.2
├─ eslint-scope@7.1.1
├─ eslint@8.36.0
├─ esquery@1.5.0
├─ esrecurse@4.3.0
├─ estraverse@5.3.0
├─ fast-deep-equal@3.1.3
├─ fast-glob@3.2.12
├─ fast-json-stable-stringify@2.1.0
├─ fast-levenshtein@2.0.6
├─ fastq@1.15.0
├─ file-entry-cache@6.0.1
├─ fill-range@7.0.1
├─ find-up@5.0.0
├─ flat-cache@3.0.4
├─ flatted@3.2.7
├─ function.prototype.name@1.1.5
├─ get-symbol-description@1.0.0
├─ get-tsconfig@4.4.0
├─ glob-parent@6.0.2
├─ glob@7.1.7
├─ globalthis@1.0.3
├─ globalyzer@0.1.0
├─ globby@13.1.3
├─ globrex@0.1.2
├─ graceful-fs@4.2.11
├─ grapheme-splitter@1.0.4
├─ has-bigints@1.0.2
├─ has-flag@4.0.0
├─ has-proto@1.0.1
├─ import-fresh@3.3.0
├─ imurmurhash@0.1.4
├─ internal-slot@1.0.5
├─ is-bigint@1.0.4
├─ is-boolean-object@1.1.2
├─ is-callable@1.2.7
├─ is-core-module@2.11.0
├─ is-date-object@1.0.5
├─ is-docker@2.2.1
├─ is-extglob@2.1.1
├─ is-map@2.0.2
├─ is-negative-zero@2.0.2
├─ is-number-object@1.0.7
├─ is-number@7.0.0
├─ is-path-inside@3.0.3
├─ is-set@2.0.2
├─ is-symbol@1.0.4
├─ is-weakmap@2.0.1
├─ is-weakref@1.0.2
├─ is-weakset@2.0.2
├─ is-wsl@2.2.0
├─ isexe@2.0.0
├─ js-sdsl@4.3.0
├─ json-schema-traverse@0.4.1
├─ json-stable-stringify-without-jsonify@1.0.1
├─ json5@1.0.2
├─ jsx-ast-utils@3.3.3
├─ language-subtag-registry@0.3.22
├─ language-tags@1.0.5
├─ locate-path@6.0.0
├─ lodash.merge@4.6.2
├─ lru-cache@6.0.0
├─ merge2@1.4.1
├─ micromatch@4.0.5
├─ minimatch@3.1.2
├─ minimist@1.2.8
├─ ms@2.1.2
├─ natural-compare@1.4.0
├─ object-assign@4.1.1
├─ object-inspect@1.12.3
├─ object-is@1.1.5
├─ object.hasown@1.1.2
├─ open@8.4.2
├─ optionator@0.9.1
├─ p-limit@3.1.0
├─ p-locate@5.0.0
├─ parent-module@1.0.1
├─ path-exists@4.0.0
├─ path-key@3.1.1
├─ path-type@4.0.0
├─ picomatch@2.3.1
├─ prop-types@15.8.1
├─ punycode@2.3.0
├─ queue-microtask@1.2.3
├─ react-is@16.13.1
├─ regenerator-runtime@0.13.11
├─ resolve-from@4.0.0
├─ reusify@1.0.4
├─ rimraf@3.0.2
├─ run-parallel@1.2.0
├─ safe-regex-test@1.0.0
├─ shebang-command@2.0.0
├─ shebang-regex@3.0.0
├─ slash@4.0.0
├─ stop-iteration-iterator@1.0.0
├─ string.prototype.matchall@4.0.8
├─ string.prototype.trim@1.2.7
├─ string.prototype.trimend@1.0.6
├─ string.prototype.trimstart@1.0.6
├─ strip-ansi@6.0.1
├─ strip-bom@3.0.0
├─ strip-json-comments@3.1.1
├─ supports-color@7.2.0
├─ synckit@0.8.5
├─ tapable@2.2.1
├─ text-table@0.2.0
├─ tiny-glob@0.2.9
├─ to-regex-range@5.0.1
├─ tsconfig-paths@3.14.2
├─ tsutils@3.21.0
├─ type-check@0.4.0
├─ type-fest@0.20.2
├─ typed-array-length@1.0.4
├─ typescript@5.0.2
├─ unbox-primitive@1.0.2
├─ uri-js@4.4.1
├─ which-collection@1.0.1
├─ which@2.0.2
├─ word-wrap@1.2.3
├─ yallist@4.0.0
└─ yocto-queue@0.1.0
✨ Done in 8.00s.
Success! Created personal-blog at /personal-blog/personal-blog
A new version of `create-next-app` is available!
You can update by running: yarn global add create-next-app
コミット
npx create-next-app
コマンドに --typescript
オプションを指定したので、必要な TypeScript のパッケージがインストールされました。なので以下のコマンドは入力する必要はありません。
$ yarn add --dev typescript @types/react @types/react-dom @types/node
このプロジェクト用のyarnのバージョンを固定
$ yarn set version 1.22.19
コミット
stableバージョンに切り替える方法
$ yarn set version stable
生成された.yarnrc にsave-prefix ""の追加(インストールする package のバージョンを固定する)
このプロジェクト用のnodeのバージョンを固定
nodeのバージョン管理はnvm使用しています。このプロジェクト用のnodeのバージョンを固定する場合、Node.jsのバージョン番号だけを書いたテキストファイル`.nvmrcという名前でプロジェクトルートに置く。
18.15.0
$ nvm use
Found './personal-blog/.nvmrc' with version <18.15.0>
Now using node v18.15.0 (npm v9.5.0)
コミット
tsconfig.json
このファイルはTypeScriptの設定ファイルであり、コンパイラの設定やプロジェクトの構成を指定するものです。
{
"compilerOptions": {
"baseUrl": "src",
"target": "es5",
"module": "esnext",
"jsx": "preserve",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"noEmit": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
},
"exclude": ["node_modules", "deployments", ".next", "out"],
"include": [
"next-env.d.ts",
"globals.d.ts",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.js",
"test/**/*.ts",
"test/**/*.tsx"
]
}
設定内容の詳細
以下に主なオプションを説明します。
- "baseUrl": モジュール名の解決時の基準となるディレクトリを指定します。
- "target": コンパイル後のJavaScriptのバージョンを指定します。
- ここでは、ES5に設定されています。
- "module": このオプションは出力されるJavaScriptがどのようにモジュールを読み込むか指定します。
- ここでは、ESモジュールに設定されています。
- "jsx": JSX をどのような構成の JavaScript に出力するかの指定( ex. preserve, react, react-jsx )
- JSX 構文をそのままにしておくか(preserve)
- React の構文に書き換えるか
- ちなみに、tsc は TypeScript だけじゃなく JSX もコンパイルできる
- react を指定すると、たとえば <div /> が React.createElement("div") のように変換される
- ここでは、"preserve"に設定されています。
- "strict": 厳密な型チェックを有効にしている
- "esModuleInterop": CommonJS / AMD / UMD 形式のモジュールを ES Modules と同様に扱えるようにする
- "skipLibCheck": ライブラリファイルの型チェックをスキップ
- .d.ts の型宣言ファイルに対する型チェックをスキップする
- "forceConsistentCasingInFileNames": インポート時にファイルパス記述の大文字・小文字を区別する
- "lib": コンパイルの際に含める組み込みライブラリを指定する
- (ex.es5,es2015,esnext,dom)
- "allowJs": TypeScriptのコンパイル対象にJavaScriptファイルを含めるかどうかを指定します。
- "noEmit": コンパイル結果を出力しないようにします。
- "moduleResolution": モジュール解決の方針の指定
- ここでは、Node.jsの解決戦略に設定されています。
- "resolveJsonModule": JSON ファイルをインポートしてできたオブジェクトに自動的に型をつける
- "isolatedModules": 各ファイルを個別にコンパイルするための設定です。
excludeとincludeオプションを使用することで、コンパイル対象となるファイルや除外するファイルを指定できます。
コミット
参考
p222
ファイルの整理
不要ファイル削除
$ rm -rf public && rm -rf styles && rm -rf pages/api
コミット
src/ ディレクトリ作成と移動
$ mkdir src/
$ mv pages src/pages/
コミット
現時点のディレクトリ
他のファイルは省略してます。
-├── public
-├── styles
-│── pages
-│ ├── _app.tsx
-│ ├── index.tsx
-│ └── api
-│ └── hello.ts
+├── src
+│ └── pages
+│ ├── _app.tsx
+│ └── index.tsx
_app.tsx の整理
globals.css を削除したので、それをimportしている記述を削除
- import '../styles/globals.css'
import type { AppProps } from "next/app"
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
export default MyApp
コミット
index.tsx の整理
Home.module.css を削除したので、それをimportしている記述を削除など
コミット
pages/index.tsxは色々消します。
変数名もPage
に統一しました。
コミット
ESLint の環境を作る
JavaScript の静的解析のデファクトスタンダード。
ルールに基づいて構文エラーや不具合になりそうな箇所を警告してくれる。
すでにインストール済みのもの
- eslint
これからインストールするもの
-
@typescript-eslint/eslint-plugin
- TypeScript向けのルールを追加するパッケージ
-
@typescript-eslint/parser
- TypeScriptをESLintが理解できるようにパース(プログラムで扱えるようなデータ構造の集合体に変換)する
- TypeScriptはJavaScriptの構文を拡張した言語なので、このパーサーさえ入れておけば、TypeScriptに限らずJavaScriptのこのパーサーひとつで、TypeScriptとJavaScriptのファイルどちらもリントできるようになります
-
eslint-plugin-react
- ESLintのReact用プラグイン
-
eslint-plugin-react-hooks
- React Hooks を使ったとき、適切な記述になるよう用意されたルールを追加する
-
eslint-plugin-import
- import の自動ソートを行う
-
eslint-plugin-simple-import-sort
- import の自動ソートを行う
-
eslint-plugin-jsx-a11y
- Web アクセシビリティに配慮した記述を JSX で行うための各種ルールを追加する
インストール コマンド
$ yarn add --dev \
@typescript-eslint/eslint-plugin \
@typescript-eslint/parser \
eslint-plugin-react \
eslint-plugin-react-hooks \
eslint-plugin-import \
eslint-plugin-simple-import-sort \
eslint-plugin-jsx-a11y
出力結果
yarn add v1.22.19
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved lockfile.
success Saved 11 new dependencies.
info Direct dependencies
├─ @typescript-eslint/eslint-plugin@5.55.0
├─ @typescript-eslint/parser@5.55.0
├─ eslint-plugin-import@2.27.5
├─ eslint-plugin-react@7.32.2
├─ eslint-plugin-simple-import-sort@10.0.0
├─ eslint-plugin-jsx-a11y@6.7.1
├─ eslint-plugin-react-hooks@4.6.0
└─ eslint@8.36.0
info All dependencies
├─ @types/json-schema@7.0.11
├─ @types/semver@7.3.13
├─ @typescript-eslint/eslint-plugin@5.55.0
├─ @typescript-eslint/parser@5.55.0
├─ @typescript-eslint/type-utils@5.55.0
├─ eslint-plugin-import@2.27.5
├─ eslint-plugin-react@7.32.2
├─ eslint-plugin-simple-import-sort@10.0.0
├─ eslint-plugin-jsx-a11y@6.7.1
├─ eslint-plugin-react-hooks@4.6.0
├─ eslint@8.36.0
└─ natural-compare-lite@1.4.0
✨ Done in 4.61s.
コミット
インストール後に出たwarningのmemo
warning Pattern ["@typescript-eslint/parser@^5.55.0"] is trying to unpack in the same destination "/Users/shimizuryousuke/Library/Caches/Yarn/v6/npm-@typescript-eslint-parser-5.55.0-8c96a0b6529708ace1dcfa60f5e6aec0f5ed2262-integrity/node_modules/@typescript-eslint/parser" as pattern ["@typescript-eslint/parser@^5.42.0"]. This could result in non-deterministic behavior, skipping.
warning Pattern ["eslint-plugin-react@^7.32.2"] is trying to unpack in the same destination "/Users/shimizuryousuke/Library/Caches/Yarn/v6/npm-eslint-plugin-react-7.32.2-e71f21c7c265ebce01bcbc9d0955170c55571f10-integrity/node_modules/eslint-plugin-react" as pattern ["eslint-plugin-react@^7.31.7"]. This could result in non-deterministic behavior, skipping.
warning Pattern ["eslint-plugin-import@^2.27.5"] is trying to unpack in the same destination "/Users/shimizuryousuke/Library/Caches/Yarn/v6/npm-eslint-plugin-import-2.27.5-876a6d03f52608a3e5bb439c2550588e51dd6c65-integrity/node_modules/eslint-plugin-import" as pattern ["eslint-plugin-import@^2.26.0"]. This could result in non-deterministic behavior, skipping.
warning Pattern ["eslint-plugin-react-hooks@^4.6.0"] is trying to unpack in the same destination "/Users/shimizuryousuke/Library/Caches/Yarn/v6/npm-eslint-plugin-react-hooks-4.6.0-4c3e697ad95b77e93f8646aaa1630c1ba607edd3-integrity/node_modules/eslint-plugin-react-hooks" as pattern ["eslint-plugin-react-hooks@^4.5.0"]. This could result in non-deterministic behavior, skipping.
warning Pattern ["eslint-plugin-jsx-a11y@^6.7.1"] is trying to unpack in the same destination "/Users/shimizuryousuke/Library/Caches/Yarn/v6/npm-eslint-plugin-jsx-a11y-6.7.1-fca5e02d115f48c9a597a6894d5bcec2f7a76976-integrity/node_modules/eslint-plugin-jsx-a11y" as pattern ["eslint-plugin-jsx-a11y@^6.5.1"]. This could result in non-deterministic behavior, skipping.
今は対応を取らない。困った時用に見返すメモ。
.eslintrc.json の設定値
このESLintの設定ファイル
ESLintの設定
rule は必要になった時に、欲しいものを入れてください。
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "simple-import-sort", "import"],
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"next/core-web-vitals",
],
"env": {
"es6": true,
"browser": true,
"jest": true,
"node": true
},
"rules": {
"react/react-in-jsx-scope": 0,
"react/display-name": 0,
"react/prop-types": 0,
"@typescript-eslint/consistent-type-definitions": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-member-accessibility": 0,
"@typescript-eslint/indent": 0,
"@typescript-eslint/member-delimiter-style": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-use-before-define": 0,
"@typescript-eslint/triple-slash-reference": [
"error",
{ "types": "always" }
],
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }],
"no-console": [2, { "allow": ["warn", "error"] }],
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"import/no-unresolved": "off",
"sort-imports": "off",
"react/self-closing-comp": [
"error",
{
"component": true,
"html": true
}
]
}
}
ちなみに設定の数値はそれぞれ、0="off" 1="warn" 2="error" という意味です。
コミット
このESLintの設定ファイルのrules配下の設定内容は以下のような意味を持ちます。
rules配下
- react/react-in-jsx-scope: 0
- ReactコンポーネントでJSXを使用する際に、Reactが自動的にインポートされるためです。
- Next.jsの場合、このルールは必要ありません。
- この設定では無効化されています。
- react/display-name: 0
- Reactのコンポーネント名を表示するためのルールです。
- react/prop-types: 0
- コンポーネントのprop typesの宣言を確認するためのルールです。
- この設定では無効化されています。
- @typescript-eslint/explicit-function-return-type: 0
- TypeScriptで明示的な関数の戻り値の型宣言が必要かどうかを判断するルールです。
- この設定では無効化されています。
- @typescript-eslint/explicit-member-accessibility: 0
- TypeScriptでメンバーのアクセシビリティを宣言する必要があるかどうかを判断するルールです。
- この設定では無効化されています。
- @typescript-eslint/indent: 0
- TypeScriptのインデントを判断するルールです。
- この設定では無効化されています。
- @typescript-eslint/member-delimiter-style: 0
- クラスのメンバーの区切り文字を判断するルールです。
- この設定では無効化されています。
- @typescript-eslint/no-explicit-any: 0
- any型を使用する際のルールです。
- この設定では無効化されています。
- @typescript-eslint/no-var-requires: 0
- require文を使用する際のルールです。
- この設定では無効化されています。
- @typescript-eslint/no-use-before-define: 0
- 変数や関数が宣言される前に使用されることを防止するルールです。
- この設定では無効化されています。
- @typescript-eslint/triple-slash-reference
- トリプルスラッシュ・ディレクティブの使用を許可するかどうかを定義するルール。
- ここでは eslint-config-standard-with-typescriptが一律禁止にしていたのを、type 属性の場合に限り許可するよう に設定。
- @typescript-eslint/no-unused-vars: [2, {"argsIgnorePattern": "^_"}]
- 未使用の変数を検出するルールです。argsIgnorePatternオプションにより、変数名が_で始まる場合は無視されます。
- no-console: [2, {"allow": ["warn", "error"]}]
- コンソールログの使用を制限するルールです。allowオプションにより、warnやerrorは許可されます。
- simple-import-sort/imports: "error"
- import文をアルファベット順にソートするルールです。
- simple-import-sort/exports: "error"
- export文をアルファベット順にソートするルールです。
- "import/no-unresolved": "off"
- import文で解決できないモジュールを警告しない
- "sort-imports": "off"
- import文の順序を整えない
- "react/self-closing-comp": ["error", { "component": true, "html": true }]
- 自己閉じタグを要求する
- componentで、Reactコンポーネントに自己閉じタグが必要かどうかを設定する
- htmlで、HTML要素に自己閉じタグが必要かどうかを設定する
参考
vscode 使ってたら vscode の設定入れる
.vscode/settings.json
{
"editor.tabSize": 2,
"editor.codeActionsOnSave": {
"source.fixAll": true
},
"css.validate": false
}
この設定は、Visual Studio Codeのエディターでタブ幅を2に設定し、保存時に自動的にコードを修正するように設定しています。また、CSSファイルの構文チェックを無効にしています。具体的には、次のような意味があります。
- editor.tabSize: タブ幅を2に設定する。
- editor.codeActionsOnSave: エディターで保存するときに自動的に修正する。
- source.fixAll: 保存時にコードを自動的に修正する。
- css.validate: CSSファイルの構文チェックを無効にする。
ここまできたら、一度 ESLint の設定が動いているか検証します。
vscode 使ってたら、vscode上で例えば未使用の変数を定義して、no-unused-vars
の指摘が出てくればOKじゃないでしょうか?
また、コマンドでeslintを起動して検証してみましょう。
$ npx eslint 'src/**/*.{js,jsx,ts,tsx}'
チームで開発する場合は必要な vscode 拡張もあれば用意しておとくといいかも
.vscode/extensions.json
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
]
}
この設定ファイルはVisual Studio Codeの拡張機能の推奨設定を指定するためのものです。recommendations プロパティには、使用することが推奨される拡張機能の一覧が記載されています。この例では、 dbaeumer.vscode-eslint と esbenp.prettier-vscode を推奨しています。この設定を使うことで、VS Codeがこれらの拡張機能をユーザーに推奨するようになります。
コミット
これやると、まとめてインストールすることができます。
Prettier の環境を作る
続いてPrettierです。PrettierとEslintではルールの競合が起きる場合があります(参考)。
Prettierのドキュメントにもある通り、eslint-config-prettierを使用することが推奨?言及?されています。
同じような内容ですが、下記にも同じ言及がああります。
理由
p105
参考
そこで、Prettierと競合するEslintのルールを無効化しつつ、Prettierを用いる方法で環境を作っていきましょう。まずはインストール。
必要なパッケージは次の 2 つ
-
prettier
- Prettier 本体
-
eslint-config-prettier
- Prettier と競合する可能性のある ESLint の各種ルールを無効にする共有設定
インストール
$ yarn add -D prettier eslint-config-prettier
コミット
そして.eslintrc.json
を次のように書き換えます。
...
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"next/core-web-vitals",
"prettier"
],
...
extends のところ、一番最後に書いてください。他のルールと競合するルール設定を上書きするので。
参考
{
"printWidth": 120,
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"endOfLine": "auto"
}
Prettierの設定内容
以下のような意味があります。
- printWidth: 行の最大文字数を指定します。この文字数を超えると、改行が挿入されます。
- tabWidth: タブ文字の幅を指定します。
- semi: 文の最後にセミコロンを付けるかどうかを指定します。
- singleQuote: シングルクォート (') を使用するか、ダブルクォート (") を使用するかを指定します。
- trailingComma: オブジェクトや配列の最後の要素の後ろにカンマを付けるかどうかを指定します。
- endOfLine: 改行文字の種類を指定します。autoを指定すると、自動的に検出されます。
ここまでできたら、ESLint で設定してあったルールと Prettier で設定したルールが衝突してないか調べます。
$ yarn eslint-config-prettier 'src/**/*.{js,jsx,ts,tsx}'
出力
yarn run v1.22.19
$ /node_modules/.bin/eslint-config-prettier 'src/**/*.{js,jsx,ts,tsx}'
No rules that are unnecessary or conflict with Prettier were found.
✨ Done in 1.23s.
eslint-config-prettier は不要な設定ルールや Prettier と衝突するルールを検出する CLI ツールを同梱しています。今回、それを実行した結果、衝突してないよって出ました。
ちゃんと設定通り動いていることを確認(VScodeのFormat on save 的なやつ)してコミット。
yarn format
でprettier 実行できる用にしておく。
コミット
参考
Jest の導入
インストールする
$ yarn add -D jest ts-jest @types/jest
実行結果
yarn add v1.22.19
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved lockfile.
success Saved 151 new dependencies.
info Direct dependencies
├─ @types/jest@29.5.0
├─ jest@29.5.0
└─ ts-jest@29.0.5
info All dependencies
├─ @ampproject/remapping@2.2.0
├─ @babel/compat-data@7.21.0
├─ @babel/core@7.21.3
├─ @babel/helper-compilation-targets@7.20.7
├─ @babel/helper-function-name@7.21.0
├─ @babel/helper-hoist-variables@7.18.6
├─ @babel/helper-module-imports@7.18.6
├─ @babel/helper-module-transforms@7.21.2
├─ @babel/helper-simple-access@7.20.2
├─ @babel/helper-string-parser@7.19.4
├─ @babel/helper-validator-identifier@7.19.1
├─ @babel/helper-validator-option@7.21.0
├─ @babel/helpers@7.21.0
├─ @babel/highlight@7.18.6
├─ @babel/parser@7.21.3
├─ @babel/plugin-syntax-async-generators@7.8.4
├─ @babel/plugin-syntax-bigint@7.8.3
├─ @babel/plugin-syntax-class-properties@7.12.13
├─ @babel/plugin-syntax-import-meta@7.10.4
├─ @babel/plugin-syntax-json-strings@7.8.3
├─ @babel/plugin-syntax-jsx@7.18.6
├─ @babel/plugin-syntax-logical-assignment-operators@7.10.4
├─ @babel/plugin-syntax-nullish-coalescing-operator@7.8.3
├─ @babel/plugin-syntax-numeric-separator@7.10.4
├─ @babel/plugin-syntax-object-rest-spread@7.8.3
├─ @babel/plugin-syntax-optional-catch-binding@7.8.3
├─ @babel/plugin-syntax-optional-chaining@7.8.3
├─ @babel/plugin-syntax-top-level-await@7.14.5
├─ @babel/plugin-syntax-typescript@7.20.0
├─ @babel/template@7.20.7
├─ @babel/traverse@7.21.3
├─ @bcoe/v8-coverage@0.2.3
├─ @istanbuljs/load-nyc-config@1.1.0
├─ @jest/globals@29.5.0
├─ @jest/reporters@29.5.0
├─ @jest/source-map@29.4.3
├─ @jest/test-sequencer@29.5.0
├─ @jridgewell/gen-mapping@0.3.2
├─ @jridgewell/resolve-uri@3.1.0
├─ @jridgewell/set-array@1.1.2
├─ @sinclair/typebox@0.25.24
├─ @sinonjs/commons@2.0.0
├─ @sinonjs/fake-timers@10.0.2
├─ @types/babel__generator@7.6.4
├─ @types/babel__template@7.4.1
├─ @types/graceful-fs@4.1.6
├─ @types/istanbul-lib-coverage@2.0.4
├─ @types/istanbul-lib-report@3.0.0
├─ @types/istanbul-reports@3.0.1
├─ @types/jest@29.5.0
├─ @types/prettier@2.7.2
├─ @types/stack-utils@2.0.1
├─ @types/yargs-parser@21.0.0
├─ @types/yargs@17.0.22
├─ anymatch@3.1.3
├─ babel-jest@29.5.0
├─ babel-plugin-jest-hoist@29.5.0
├─ babel-preset-jest@29.5.0
├─ browserslist@4.21.5
├─ bs-logger@0.2.6
├─ bser@2.1.1
├─ buffer-from@1.1.2
├─ camelcase@6.3.0
├─ char-regex@1.0.2
├─ cjs-module-lexer@1.2.2
├─ cliui@8.0.1
├─ co@4.6.0
├─ convert-source-map@1.9.0
├─ dedent@0.7.0
├─ deepmerge@4.3.1
├─ detect-newline@3.1.0
├─ diff-sequences@29.4.3
├─ electron-to-chromium@1.4.333
├─ error-ex@1.3.2
├─ esprima@4.0.1
├─ execa@5.1.1
├─ expect@29.5.0
├─ fb-watchman@2.0.2
├─ find-up@4.1.0
├─ fsevents@2.3.2
├─ gensync@1.0.0-beta.2
├─ get-caller-file@2.0.5
├─ get-package-type@0.1.0
├─ get-stream@6.0.1
├─ html-escaper@2.0.2
├─ human-signals@2.1.0
├─ is-arrayish@0.2.1
├─ is-fullwidth-code-point@3.0.0
├─ is-generator-fn@2.1.0
├─ is-stream@2.0.1
├─ istanbul-lib-instrument@5.2.1
├─ istanbul-lib-source-maps@4.0.1
├─ istanbul-reports@3.1.5
├─ jest-changed-files@29.5.0
├─ jest-circus@29.5.0
├─ jest-cli@29.5.0
├─ jest-docblock@29.4.3
├─ jest-each@29.5.0
├─ jest-leak-detector@29.5.0
├─ jest-pnp-resolver@1.2.3
├─ jest-resolve-dependencies@29.5.0
├─ jest-util@29.5.0
├─ jest@29.5.0
├─ jsesc@2.5.2
├─ json-parse-even-better-errors@2.3.1
├─ json5@2.2.3
├─ kleur@3.0.3
├─ leven@3.1.0
├─ lines-and-columns@1.2.4
├─ locate-path@5.0.0
├─ lodash.memoize@4.1.2
├─ make-dir@3.1.0
├─ make-error@1.3.6
├─ makeerror@1.0.12
├─ mimic-fn@2.1.0
├─ node-int64@0.4.0
├─ node-releases@2.0.10
├─ normalize-path@3.0.0
├─ npm-run-path@4.0.1
├─ onetime@5.1.2
├─ p-locate@4.1.0
├─ p-try@2.2.0
├─ parse-json@5.2.0
├─ pirates@4.0.5
├─ pkg-dir@4.2.0
├─ pretty-format@29.5.0
├─ prompts@2.4.2
├─ pure-rand@6.0.1
├─ require-directory@2.1.1
├─ resolve-cwd@3.0.0
├─ resolve.exports@2.0.1
├─ signal-exit@3.0.7
├─ sisteransi@1.0.5
├─ source-map-support@0.5.13
├─ source-map@0.6.1
├─ sprintf-js@1.0.3
├─ string-width@4.2.3
├─ strip-final-newline@2.0.0
├─ test-exclude@6.0.0
├─ tmpl@1.0.5
├─ to-fast-properties@2.0.0
├─ ts-jest@29.0.5
├─ type-detect@4.0.8
├─ update-browserslist-db@1.0.10
├─ v8-to-istanbul@9.1.0
├─ walker@1.0.8
├─ wrap-ansi@7.0.0
├─ write-file-atomic@4.0.2
├─ y18n@5.0.8
├─ yargs-parser@21.1.1
└─ yargs@17.7.1
✨ Done in 12.04s.
コミット
設定ファイルを作成
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
})
// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const customJestConfig = {
// Add more setup options before each test is run
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
// if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
moduleDirectories: ['node_modules', '<rootDir>/'],
// ↓1行を変更しています: React Testing Libraryはまだ利用しないのでコメントアウト
// testEnvironment: 'jest-environment-jsdom',
}
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)
上記のコードは、Next.jsでテストを実行するためのJestの設定ファイルです。
以下は主なポイントです。
- next-jestモジュールを使用して、Next.jsアプリケーションの next.config.js や .env ファイルを読み込むための設定を行っています。
- customJestConfigには、Jestに渡す追加の設定を記述します。
- moduleDirectoriesは、import文で使用されるモジュールの探索パスを設定しています。
- ここでは node_modules とルートディレクトリ <rootDir>/ を指定しています。
- testEnvironmentはコメントアウトされていて、React Testing Libraryを使用しないために今は無効しています
コミット
テスト実行用のnpmスクリプトを追加
"scripts": {
...
"test": "jest --watch",
"test:ci": "jest",
...
}
コミット
テストコードを書く
足し算の処理とそのテストコードをサンプルとして書きます。
詳細
export const add = (a: number, b: number): number => a + b
import { add } from './add'
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3)
expect(add(10, -5)).toBe(5)
expect(add(0.1, 0.2)).toBeCloseTo(0.3, 5) // 小数の場合はtoBeCloseToを使う
})
テストを実行する
$ yarn test
$ yarn test:ci
ちゃんと動いているか確認できた。
VSCodeの拡張機能を使って検証してもいいでしょう。自分は下記を使っています。
コミット
husky と lint-staged を入れる
以下の機能が動くように環境を作っていきます。具体的には
- git のコミット前にコマンドを実行してくれるhusky
- huskyにはコミットするファイルに対して lint かけてくれるlint-staged
の設定を書きます。
$ yarn add -D husky lint-staged
結果
yarn add v1.22.19
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 🔨 Building fresh packages...
success Saved lockfile.
success Saved 27 new dependencies.
info Direct dependencies
├─ husky@8.0.3
└─ lint-staged@13.2.0
info All dependencies
├─ aggregate-error@3.1.0
├─ clean-stack@2.2.0
├─ cli-cursor@3.1.0
├─ cli-truncate@3.1.0
├─ colorette@2.0.19
├─ commander@10.0.0
├─ eastasianwidth@0.2.0
├─ execa@7.1.1
├─ human-signals@4.3.0
├─ husky@8.0.3
├─ indent-string@4.0.0
├─ is-stream@3.0.0
├─ lilconfig@2.1.0
├─ lint-staged@13.2.0
├─ listr2@5.0.8
├─ log-update@4.0.0
├─ npm-run-path@5.1.0
├─ p-map@4.0.0
├─ pidtree@0.6.0
├─ restore-cursor@3.1.0
├─ rfdc@1.3.0
├─ rxjs@7.8.0
├─ slice-ansi@5.0.0
├─ string-argv@0.3.1
├─ strip-final-newline@3.0.0
├─ through@2.3.8
└─ yaml@2.2.1
✨ Done in 4.77s.
コミット
husky の 初期化と lint-staged の設定
npx husky-init は、プロジェクトで Git Hooks を管理するためのフレームワークである Husky をセットアップするためのコマンドです。これで、Git Hooks 用の準備が整います。
$ npx husky-init
.husky ディレクトリが作成され、pre-commit フックを有効にするためのスクリプトが追加されます。これにより、コミット時に自動的に実行されるスクリプトを追加できます。具体的には package.jsonのscripts内に "prepare": "husky install" が追加されていることが確認できるはずです。
結果
Need to install the following packages:
husky-init@8.0.0
Ok to proceed? (y) y
husky-init updating package.json
setting prepare script to command "husky install"
husky - Git hooks installed
husky - created .husky/pre-commit
please review changes in package.json
lint-staged の設定を package.json に追加します。
...
"lint-staged": {
"*.@(ts|tsx)": [
"yarn lint",
"yarn format"
]
},
...
でしたが、以下に変更。
...
"lint-staged": {
"*.@(ts|tsx)": [
"npx eslint .",
"yarn format"
]
},
...
コミット
変更した理由
起こった問題
下記の問題に近い
私の場合は以下の問題として現れました。
グローバルに適応されるスタイルシートにスタイルを記述し、_app.tsx
からimportした際、lint-staged
(コミット前をgithook)からリンター(Eslint)を動かす際に以下のエラーが出てコミットできなかった。
✖ yarn lint:
error - Failed to load env from .env.production.local Error: ENOTDIR: not a directory, stat '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env.production.local'
at Object.statSync (node:fs:1615:3)
at Module.loadEnvConfig (/Users/shimizuryousuke/dev/personal-blog/node_modules/@next/env/dist/index.js:1:4657)
at Object.loadConfig [as default] (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/server/config.js:48:21)
at nextLint (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/cli/next-lint.js:178:50)
at /Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/bin/next:141:44 {
errno: -20,
syscall: 'stat',
code: 'ENOTDIR',
path: '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env.production.local'
}
error - Failed to load env from .env.local Error: ENOTDIR: not a directory, stat '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env.local'
at Object.statSync (node:fs:1615:3)
at Module.loadEnvConfig (/Users/shimizuryousuke/dev/personal-blog/node_modules/@next/env/dist/index.js:1:4657)
at Object.loadConfig [as default] (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/server/config.js:48:21)
at nextLint (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/cli/next-lint.js:178:50)
at /Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/bin/next:141:44 {
errno: -20,
syscall: 'stat',
code: 'ENOTDIR',
path: '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env.local'
}
error - Failed to load env from .env.production Error: ENOTDIR: not a directory, stat '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env.production'
at Object.statSync (node:fs:1615:3)
at Module.loadEnvConfig (/Users/shimizuryousuke/dev/personal-blog/node_modules/@next/env/dist/index.js:1:4657)
at Object.loadConfig [as default] (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/server/config.js:48:21)
at nextLint (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/cli/next-lint.js:178:50)
at /Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/bin/next:141:44 {
errno: -20,
syscall: 'stat',
code: 'ENOTDIR',
path: '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env.production'
}
error - Failed to load env from .env Error: ENOTDIR: not a directory, stat '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env'
at Object.statSync (node:fs:1615:3)
at Module.loadEnvConfig (/Users/shimizuryousuke/dev/personal-blog/node_modules/@next/env/dist/index.js:1:4657)
at Object.loadConfig [as default] (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/server/config.js:48:21)
at nextLint (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/cli/next-lint.js:178:50)
at /Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/bin/next:141:44 {
errno: -20,
syscall: 'stat',
code: 'ENOTDIR',
path: '/Users/shimizuryousuke/dev/personal-blog/src/pages/_app.tsx/.env'
}
/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/lib/find-pages-dir.js:86
throw new Error("> Couldn't find a `pages` directory. Please create one under the project root");
^
Error: > Couldn't find a `pages` directory. Please create one under the project root
at Object.findPagesDir (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/lib/find-pages-dir.js:86:19)
at nextLint (/Users/shimizuryousuke/dev/personal-blog/node_modules/next/dist/cli/next-lint.js:201:55)
この問題の対応策は、.lintstagedrc.js
を導入するか、package.json
を書き換えるかで、私は後者を選択しました。(原因はよくわかっていません。)
next lint
を使わず、直接 eslint
を立ち上げるように変更しました。
next lint
とは、ESLint によるコードの静的解析を実行する next.js のコマンド
...
"lint-staged": {
"*.@(ts|tsx)": [
// 変更前
"yarn lint",
// 変更後
"npx eslint ."
// 変更後(はこれでもいいかも 要検討)
"npx eslint . --ext .tsx --ext .ts"
"yarn format"
]
},
...
--ext
オプションで拡張子を指定することでLintの対象をにしています。globでやるとひとつもファイルがないとき(ボイラープレート作成直後とか)にエラーになってしまうので、--ext
オプションにするならした方がいいでしょう。
が、今回は--ext
で対象を絞らないようにしました。
理由(暗黙的に lint 対象を制限することの是非)
本当は このファイルも lint されて欲しいのに何故かされなくて混乱する、もしくは気づけなかった、みたいなことが起きそうだから。
なので、Lintから除外するファイルは .eslintignore
に書きつつ、eslint .
でカレントディレクトリ配下のファイル全てを lint 対象にするアプローチにしました。
どのディレクトリが除外されるか明示的になりますし、vscode-eslint が .eslintignore を見て、どのファイルが lint 対象外か認識してくれるので、エディタ上/CLI上での警告に差がなくなるので。
参考
結果
想定通りコミット前にLintが今まで通り効いていることを確認
git のコミット前にコマンドを実行してくれるhuskyの設定を追加
husky の設定の詳細
pre-commit の設定
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged
コミット
CSS Modules の 導入
Next.jsにCSS Modulesを導入します。Next.js は CSS Modulesをビルトインサポートしてるので、大きな設定は不要です。
公式を参考にSassをインストール。
$ yarn add -D sass
コミット
ちゃんとスタイルが適応されるか確認
import type { NextPage } from 'next'
import Head from 'next/head'
import styles from './index.module.scss'
const Page: NextPage = () => {
return (
<div>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
</Head>
<main>
<h1 className={styles.h1}>Hello!</h1>
</main>
</div>
)
}
export default Page
.h1 {
color: red;
}
されてる。
[方針] コンポーネントのスタイルはどこに書くか?
components
ディレクトリと同じ階層に置く
components/Button.tsx
のスタイルはcomponents/Button.module.scss
に書く方針。
[方針] stylesディレクトリ内の構造
styles/
ディレクトリの方針
styles/global.scss
- ここにグローバルに(アプリ全体で)読み込みたいスタイルを書く
-
_app.tsx
でimport styles/global.scss
するだけで読み込み設定は完了 -
公式ドキュメント曰く、Normalize.cssはGlobal Stylesheetの例としている
- [注意] ただ、
_app.js
はどのページでも必ず実行されるため注意が必要 - Global Stylesheetが巨大化すればその分だけ初期ロードは重たくなるだろう
- [注意] ただ、
コミット
styles/layouts
レイアウト用のコンポーネントはlayoutsディレクトリに入れる
styles/variables.scss
使いまわしたい変数をここに入れる
_app.tsxとは
Next.js は pages/_app.tsx を特殊なファイルとして扱い、ここで default export しているコンポーネントを、すべてのページコンポーネントの親コンポーネントとして扱います。 具体的には、上記コード中の Component 部分が各ページのコンポーネントに置き換えられた形で動作します。 結果的に、ここでインポートした CSS が、全ページから参照できることになります。 ちなみに、ここで複数の CSS ファイルをインポートしている場合は、Next.js が Web サイトのビルド時に 1 つの CSS ファイルにマージしてくれます。
参考
CSS Modulesでの変数の管理
CSS変数をグローバルに読み込むSCSSに設定すればOKです。
CSS変数には、主に再利用可能な値を格納するために使用されます。たとえば、カラーコード、フォントサイズ、背景色、z-indexなどの値を保存することができます。これらの変数は、Webページ内の複数の場所で同じ値を使用する場合に特に便利です。変数を使用すると、値を一元的に変更することができ、保守性が向上します。例えば、ページの色を変更したい場合、変数に保存された値を変更するだけで、Webページのすべての場所で新しい色が適用されます。
:root {
--navy: #000082;
--yellow: #f0b400;
}
そして、各module.scssからこれを使って、コンポーネントで読み込みができているか検証してみてください。
.section {
background-color: var(--navy);
}
コミット
メディアクエリ
コミット
参考
今回のCSSをためにしに書いてみた
Next13で盛り込まれた新機能、@next/fonts を導入
GoogleFontsをセルフホスティングして、パフォーマンスを向上させているらしい。
この機能によりブラウザーは GoogleFonts のサーバーから Web フォントをダウンロードするためにアクティブな接続を維持する必要がなくなったみたい。
導入
$ yarn add @next/font
import '../styles/global.scss'
import { Rubik } from '@next/font/google'
import type { AppProps } from 'next/app'
import { Layout } from '@/components/Layout'
export const rubik = Rubik({
preload: false
})
function MyApp({ Component, pageProps }: AppProps) {
return (
<div>
<style jsx global>{`
html,
body {
font-family: ${rubik.style.fontFamily}, 'Hiragino Sans', sans-serif;
}
`}</style>
<Layout>
<Component {...pageProps} />
</Layout>
</div>
)
}
export default MyApp
コミット
Font 関数の argumentsのうち、subsets だけ指定しました。
なぜなら、デフォルトでは preload が true となっており、その場合は subsetsの指定が無いと警告が出たので。
subsetsは Google Fonts API のフォントのサブセットを指定するオプションで、フォントの使用言語に基づいて、特定の文字セットを抽出することができます。
詳しくは
下記のコードを見るとわかりますが、Rubik フォントの文字セットのリストが示されれいます。
リスト内の各要素は文字セットを表す文字列として表現されます。
具体的には、次の文字セットが指定されています。
- 'cyrillic': キリル文字セット(ロシア語、ブルガリア語など)
- 'cyrillic-ext': 拡張キリル文字セット
- 'hebrew': ヘブライ文字セット
- 'latin': ラテン文字セット
- 'latin-ext': 拡張ラテン文字セット
日本語はないですね。
これらのサブセットを必要最小限に抑えることで、フォントの読み込み速度を向上させ、ページのパフォーマンスを改善が期待できます。
ちなみに、型定義がしっかりしており、Rubik の場合は以下のような感じです。
何が指定可能なのか、分かりやすかったです。
export declare function Rubik<T extends CssVariable | undefined = undefined>(options?: {
weight?: '300' | '400' | '500' | '600' | '700' | '800' | '900' | 'variable' | Array<'300' | '400' | '500' | '600' | '700' | '800' | '900'>;
style?: 'normal' | 'italic' | Array<'normal' | 'italic'>;
display?: Display;
variable?: T;
preload?: boolean;
fallback?: string[];
adjustFontFallback?: boolean;
subsets?: Array<'cyrillic' | 'cyrillic-ext' | 'hebrew' | 'latin' | 'latin-ext'>;
}): T extends undefined ? NextFont : NextFontWithVariable;
また、変形フォントを読み込む場合は、フォントのウェイトを指定する必要はないです。
(variable fontsでないフォントファミリーは、必ずこのweightを指定する必要があります)
公式は variable fontsを推奨しています。
他にも必要なプロパティは公式を見てください。
参考
microCMS
microCMSのアカウントを下記URLから作成する
サービスを作成する
環境変数の設定
micro CMS JavaScript SDK をインストール
$ yarn add microcms-js-sdk
API を Next.js で実行する
$ touch .env.local
microCMSの自分のアカウントからコピーした API_KEY
をペーストする
参考
microCMS
microCMSのアカウントを下記URLから作成する
サービスを作成する
環境変数の設定
micro CMS JavaScript SDK をインストール
$ yarn add microcms-js-sdk
API を Next.js で実行する
$ touch .env.local
microCMSの自分のアカウントからコピーした API_KEY
やserviceDomainを.env.local
ペースト
インストールしたmicrocms-js-sdkからAPIをコールする関数を作成
作成したAPIを呼び出しコンテンツを取得する
コミット
参考
Vercelのホスティングは多くの公式を見ればすぐできるのでここには書きません。
超簡素ですが一旦できたので。このスクラップはここでクローズします。
今後の予定
- storybookの導入
- 導入に意味のあるコンポーネントが作れたら
- ディレクトリ構成の決定(現状いい加減なので)
- github action で Eslint + Prettier + Test 実行
- パフォーマンスの自動計測