Open8
Next.js(app router)+ヘッドレスWordpress+shadcn/uiで作るコーポレートサイト|セットアップ編
- npx create-next-app@latestでnext.js立ち上げ(https://nextjs.org/docs/getting-started/installation)
全てYES
※この時点で、ESlintが入っている。
{
"name": "caen-hp",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "14.2.3"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "^8",
"eslint-config-next": "14.2.3"
}
}
- Gitで管理できるようにする
- Gitでレポジトリを作る
※この時gitignoreも作る。nodeで。 - 下記コマンドを該当レポジトリで打つ。
- Gitでレポジトリを作る
> git init
> git remote add origin https://~(リモートリポジトリのURL).git
> git add .
> git commit -m "first commit"
> git push origin main
※GitHubにpushする時にerror: failed to push some refs と表示されてpushできない時
git push -f origin master
※※error: failed to push some refs to~~ が出たらもう一度git push -f origin masterする
基本的なディレクトリ構造。
├─ app/
├─ components/
│ ├─ elements/
│ │ └─ Ex)Button
│ │ └─ Ex)Button.tsx
│ └─ layouts/
│ └─ Ex)Header
│ └─ Ex)Header.tsx
│ └─ ui/
│ └─ Ex)shadcn/uiで自動インポートされるコンポーネント
├─ features/
│ └─ Ex)/post
│ ├─ api/
│ └─ Ex)getPost.ts
│ ├─ styles/
│ ├─ components/
│ ├─ Ex)Post.tsx
│ └─ Ex)Posts.tsx
│ ├─ hooks/
│ └─ Ex)usePost.ts
│ └─ types/
│ └─ Ex)index.ts
├─ hooks/
├─ styles/
├─ types/
├─ lib/
└─ utils/
tsconfig.jsonでエラーが出ていたので、
"moduleResolution": "bundler", -> "moduleResolution": "node",
に変更
上記の記事を部分的に参考にして、ESlint,Pritter,Stylelint,VScodeの設定を行なっていく。
まずは必要プラグインのインポート。
- Pritter
npm install -D prettier prettier-plugin-tailwindcss eslint-config-prettier
- ESlint
npm install -D eslint-plugin-import eslint-plugin-unused-imports eslint-plugin-simple-import-sort
※eslint-plugin-prettierは非推奨ならしい。https://note.com/eikichi_/n/n89ebfe500fc1 - Stylelint
npm install -D stylelint-scss stylelint-prettier stylelint-config-recommended-scss stylelint-config-recess-order
設定ファイルを作成していく。内容は色々拾ってきただけのもので、ブラッシュアップ可能。
-
tailwind.config.tsは、shadcn/uiを入れなければ変えなくても良さそう
-
stylelint.config.js
stylelint.config.jsの内容
module.exports = {
plugin: ["stylelint-scss"],
extends: [
"stylelint-config-recommended-scss", // scssのための拡張ルール追加
"stylelint-config-recess-order", // 視認性を考慮したcssプロパティの自動ソートを設定
"stylelint-config-prettier", // Prettierとの競合ルールをOFFにする
],
};
- prettier.config.js
prettier.config.jsの内容
module.exports = {
printWidth: 80, // 1行で表示する文字数
tabWidth: 2, // インデントのサイズ
useTabs: false, // インデントにスペースの代わりにタブを使うかどうか
semi: true, // 文の後にセミコロンを付けるかどうか
singleQuote: false, // 文字列をシングルクォートで囲むかどうか(falseだとダブルクォート)
quoteProps: "as-needed", // オブジェクトのプロパティ名をクォートで囲むかどうか
jsxSingleQuote: false, // JSX内のクォートをシングルクォートで囲むかどうか
trailingComma: "es5", // 複数行のときの末尾のカンマを付けるかどうか
bracketSpacing: true, // オブジェクトリテラルの{}内の前後にスペースを入れるかどうか
bracketSameLine: false, // JSX内の要素の閉じタグを最後の行に含んで表示するか
arrowParens: "always", // アロー関数の引数が1つのときにカッコで囲むかどうか
};
- VSCode(Cursol)の設定を統一させるために.vscode/settings.jsonファイルを作成する。
.vscode/settings.jsonの内容
{
// ファイル保存時のフォーマット処理を有効化する
"editor.formatOnSave": true,
// 各ファイルのフォーマッターを指定する
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// Stylelintの対象を設定
"stylelint.validate": ["css", "scss"],
// ESLintの対象を設定
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
// 保存時に実行される各コードアクションを有効化する
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "always",
"source.fixAll.eslint": "always",
"source.organizeImports": "always"
},
// VSCodeデフォルトのLintを無効にする(各Lintのルールで統一する際に設定)
"css.validate": false,
"scss.validate": false,
"javascript.format.enable": false,
"typescript.format.enable": false
}
- .eslintrc.js
.eslintrc.jsの内容
module.exports = {
extends: [
"eslint:recommended",
"next",
"next/core-web-vitals",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"plugin:redos/recommended",
],
plugins: ["simple-import-sort", "import"],
rules: {
"@next/next/no-img-element": "error",
"@next/next/no-page-custom-font": "error",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-explicit-any": "off",
curly: ["error"],
eqeqeq: ["error", "always", { null: "ignore" }],
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"import/no-extraneous-dependencies": ["error"],
"import/no-unresolved": "error",
"no-console": "error",
"no-debugger": "error",
"react-hooks/exhaustive-deps": "warn",
"react-hooks/rules-of-hooks": "error",
"react/jsx-key": [
"error",
{
checkFragmentShorthand: true,
checkKeyMustBeforeSpread: true,
warnOnDuplicates: true,
},
],
"react/jsx-no-bind": [
"error",
{
allowArrowFunctions: true,
allowBind: false,
allowFunctions: false,
ignoreDOMComponents: false,
ignoreRefs: false,
},
],
"react/no-unknown-property": ["error", { ignore: ["custom-element"] }],
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"redos/no-vulnerable": "error",
"simple-import-sort/exports": "error",
"simple-import-sort/imports": "error",
"sort-keys": [
"error",
"asc",
{ caseSensitive: true, minKeys: 2, natural: false },
],
},
// ...
};
shadcn/uiを使いたいので、インポートする。
npx shadcn-ui@latest init
✔ Which style would you like to use? › New York
✔ Which color would you like to use as base color? › Slate
✔ Would you like to use CSS variables for colors? … no / yes
メタデータ設定
src/data/metadata.ts
export const COMPANY_NAME = "????????????";
export const SITE_TITLE = "????????????";
export const SITE_DESCRIPTION = "????????????";
export const SITE_URL = "https://xxxxx.co.jp";
export const SITE_NAME = "????????????";
export const TWITTER_HANDLE = "";
export const OGP_URL = "/ogp/img_ogp.png";
export const metadata = {
title: SITE_TITLE,
description: SITE_DESCRIPTION,
metadataBase: {
url: SITE_URL,
},
keywords: [COMPANY_NAME, "?????", "???????"],
authors: [{ name: COMPANY_NAME, url: SITE_URL }],
openGraph: {
title: SITE_TITLE,
description: SITE_DESCRIPTION,
url: SITE_URL,
siteName: SITE_NAME,
images: [
{
alt: SITE_TITLE,
url: OGP_URL,
width: 1200,
height: 630,
},
],
locale: "ja_JP",
type: "website",
},
twitter: {
card: "summary_large_image",
title: SITE_TITLE,
description: SITE_DESCRIPTION,
site: TWITTER_HANDLE,
creator: TWITTER_HANDLE,
images: [
{
alt: SITE_TITLE,
height: 675,
url: OGP_URL,
width: 1200,
},
],
},
icons: {
icon: "/favicon.ico",
shortcut: "/favicon-16x16.png",
apple: "/apple-touch-icon.png",
},
};
layout.tsx
`import Footer from "@/components/layouts/Footer";
import Header from "@/components/layouts/Header";
import { metadata } from "@/data/metadata";
import { Noto_Sans_JP } from "next/font/google";
import "./globals.css";
const zenKakuGothicNew = Noto_Sans_JP({
subsets: ["latin"],
weight: ["500", "700"], // medium and bold
});
export async function generateMetadata() {
return metadata;
}
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="ja">
<body className={zenKakuGothicNew.className}>
<Header />
{children}
<Footer />
</body>
</html>
);
}