🙆

【React/Typescript】Dockerでマニュアル環境構築 2023

2023/02/12に公開約21,800字

バージョン情報

ホスト環境
- Windows 11
- VScode (拡張機能 Dev Containers)
- PowerShell
- DockerDesktop v4.16.3

Docker環境
- docker image : mcr.microsoft.com/devcontainers/typescript-node:0-18-bullseye
- Debian
- npm 8.19.3
- node v18.13.0

1. VScode + DockerによるNode環境の構築

前準備

この記事では、ホスト環境に下記のアプリケーションが入っていることを前提で話を進めます。

VScode
Dev Containers (VScode 拡張機能)
PowerShell
DockerDesktop
WSL2

VScodeとターミナルを起動してホスト環境のソースコードを保存したいフォルダに、プロジェクトフォルダを作成します。

ターミナル
mkdir test-project

ファイル -> フォルダを開く から作成したtest-projectフォルダを起動してください。

VScodeの左下にあるリモートウインドから、Add Container Configuration file ..選択して
公式イメージからDockerテンプレートをダウンロードします。

今回使うイメージはnode.js & typescriptを選択します。

OSの選択は特に好みがなければ、Default18を選択してください。

Select Featuresは必要に応じて追加してください、この記事では特に必要無いのでチェックせずに次へ進みます。

この時点のフォルダ構成
test-project ── .devcontainer ── devcontainer.json

devcontainer.jsonが作成されたらDockerを起動した際に、自動で拡張機能をインストールするための追記をします。

ココではコードの整形に使うPrettierと、英語のスペルミスを検出してくれるCode Spell Checkerをインストールします。

拡張機能の検索からPrettierを探してから、Add to devcontainer.jsonを選択することで設定へ追加できます。

同じ用に他の機能も追加しましょう。 他に入れたい拡張機能があれば、ここで追加しておいてください。

正しく追加出来ると、devcontainer.jsonがこのような記述になっていると思います。

devcontainer.json
{
	"name": "Node.js & TypeScript",
	"image": "mcr.microsoft.com/devcontainers/typescript-node:0-18",
+	"customizations": {
+	 	"vscode": {
+	 		"extensions": [
+				"esbenp.prettier-vscode",
+				"streetsidesoftware.code-spell-checker"
+			]
+		}
+	}
}

Dockerの起動

続いてDockerを起動してみましょう!

VScode のリモートコンテナを開く から Reopen in Containerを開きます。

結構重いので開くのに時間がかかります。 右下の show log から進行状況の確認ができます。

新しいターミナルを開いて、下記が出ていれば完了です。

ターミナル
node ➜ /workspaces/test-project $ 

次に、VScodeの設定ファイルsettings.jsonを作成します。

ターミナル
mkdir .vscode && touch .vscode/settings.json

.vscodeフォルダ内に作成されたsettings.jsonはワークスペース内で、有効なVScode設定が記述できます。

ちなみにワークスペースとは現在開いている、作業中フォルダのことを指します。

Gitの設定

はじめに.gitの初期化をしましょう。

gitの初期化はコマンドを使わずVScodeからコミットすることをオススメします。

Githubを使っている人はリポジトリも合わせて作成しましょう。

touchコマンドを利用して.gitignoreも合わせて作成しましょう。

.ignoregitでコミットしたくないファイルまたはフォルダを選択するで、指定したフォルダをコミットから除外出来ます。

ココでは、node_moduleのみ記載しておきます。

.gitignore
node_modules
ココまでのフォルダ構成
test-project ─┌─ .devcontainer ─── devcontainer.json
              ├─ .vscode ─── settings.json
              ├─ .gitignore
              └─ .git

2. React インストール

Reactインストール

この記事では、npmを使用していますがyarnでも大丈夫です。

npmの初期化

ターミナル
npm init -y

React DOM のインストール

ターミナル
npm i react react-dom react-router react-router-dom @types/react @types/react-dom @types/react-router @types/react-router-dom

webpack webpack-loaderのインストール

ターミナル
npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader sass css-loader style-loader sass-loader file-loader

webpackの設定

ココではwebpackをインストールします。

webpackとはwebpack-loaderモジュールを集めてゴニョゴニョやってくれるJavascriptモジュールパンドラです。

jscssを結びつけるや、Devサーバーの作成、jsからtsへの変換など、様々なモジュールがあります。

はじめにwebpack.config.jsを作成します。

ターミナル
touch webpack.config.js

詳しい説明は省きますが、ココでやっていることは下記の通りです。

  • tsjsに変換してbuildする設定
  • jscssを結びつける。ついでにsassを使えるようにする設定
  • 画像ファイルを扱えるようにする設定
  • devServerに関する設定
webpack.config.js
const path = require('path');

module.exports = {
	mode: 'development',
	entry: './src/index.tsx',
	output: {
		path: path.join(__dirname, 'dist'),
		filename: 'main.js'
	},
	module: {
		rules: [
			{
				test: /\.(ts|tsx)$/,
				use: [
					{
						loader: 'ts-loader',
						options: {
							configFile: path.resolve(__dirname, 'tsconfig.json')
						}
					}
				]
			},

			{
				test: /\.scss$/,
				use: ['style-loader', 'css-loader', 'sass-loader']
			},

			{
				test: /\.(png|jpg|gif|svg)$/i,
				type: 'asset/resource'
			}
		]
	},
	devServer: {
		static: {
			directory: path.join(__dirname, 'dist')
		},
		port: 3000,
		historyApiFallback: true
	},
	resolve: {
		extensions: ['.ts', '.tsx', '.js', '.json']
	},
	target: 'web'
};

webpackの設定が完了したらpackage.jsondev buildコマンドを追加しましょう。

package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
+    "build": "webpack",
+    "dev": "webpack serve --open"
  }

このコマンドで出来る機能は名前の通りになります。

まだ使いませんが、下記で実行出来ます。

ターミナル
npm run build
npm run dev

tsconfig.jsonの設定

tsconfig.jsonの作成

ターミナル
npx tsc --init

tsconfigの詳しい解説は下記を参考にすることをオススメします。
https://typescriptbook.jp/reference/tsconfig/tsconfig.json-settings

下記の設定は、この記事で作成したプログラムが一応動くコトを確認したものになります。

tsconfig.json
{
	"compilerOptions": {
		/* Visit https://aka.ms/tsconfig to read more about this file */

		/* Projects */
		// "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
		// "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
		// "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
		// "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
		// "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
		// "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

		/* Language and Environment */
		"target": "ES2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
		"lib": [
			"ES2016",
			"DOM"
		] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
		"jsx": "react" /* Specify what JSX code is generated. */,
		// "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
		// "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
		// "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
		// "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
		// "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
		// "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
		// "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
		// "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
		// "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */

		/* Modules */
		"module": "commonJS" /* Specify what module code is generated. */,
		// "rootDir": "./",                                  /* Specify the root folder within your source files. */
		"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
		"baseUrl": "src" /* Specify the base directory to resolve non-relative module names. */,
		// "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
		// "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
		// "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
		// "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
		// "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
		// "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
		"resolveJsonModule": true /* Enable importing .json files. */,
		// "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

		/* JavaScript Support */
		"allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
		// "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
		// "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

		/* Emit */
		// "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
		// "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
		// "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
		// "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
		// "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
		"outDir": "dist" /* Specify an output folder for all emitted files. */,
		// "removeComments": true,                           /* Disable emitting comments. */
		// "noEmit": true 									 /* Disable emitting files from a compilation. */,
		// "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
		// "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
		// "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
		// "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
		// "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
		// "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
		// "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
		// "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
		// "newLine": "crlf",                                /* Set the newline character for emitting files. */
		// "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
		// "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
		// "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
		// "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
		// "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
		// "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

		/* Interop Constraints */
		"isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
		"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
		"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
		// "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
		"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

		/* Type Checking */
		"strict": true /* Enable all strict type-checking options. */,
		// "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
		// "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
		// "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
		// "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
		// "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
		// "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
		// "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
		// "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
		// "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
		// "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
		// "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
		// "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
		"noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
		// "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
		// "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
		// "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
		// "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
		// "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

		/* Completeness */
		// "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
		"skipLibCheck": true /* Skip type checking all .d.ts files. */
	}
}

3. プログラムファイルの作成

ココではsrcの中身やlintの設定をしていきます。

App.tsx index.tsx App.scss の作成

ターミナル
mkdir src && touch src/App.tsx src/index.tsx src/App.scss
App.tsx
import React from "react"
import "./App.scss"

export const App = () => {
  return <div className="App">Hello World!</div>
};
index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { App } from './App';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
	<React.StrictMode>
		<App />
	</React.StrictMode>
);
App.scss
.App {
	font-size: 30px;
	letter-spacing: 0.1rem;
}

index.html の作成

ターミナル
mkdir dist && touch dist/index.html
index.html
<html lang="ja">
	<head>
		<meta charset="UTF-8" />
		<title>Test React App</title>
	</head>
	<body>
		<div id="root"></div>
		<script defer src="main.js"></script>
	</body>
</html>

Typescript 型宣言ファイルの作成

custom.d.tsファイルはTypescriptにて、画像ファイルを使用するための型宣言ファイルです。

ターミナル
touch src/image.d.ts
custom.d.ts
declare module '*.avif' {
	const src: string;
	export default src;
}

declare module '*.bmp' {
	const src: string;
	export default src;
}

declare module '*.gif' {
	const src: string;
	export default src;
}

declare module '*.jpg' {
	const src: string;
	export default src;
}

declare module '*.jpeg' {
	const src: string;
	export default src;
}

declare module '*.png' {
	const src: string;
	export default src;
}

declare module '*.webp' {
	const src: string;
	export default src;
}

declare module '*.svg' {
	const src: string;
	export default src;
}

ESlint Prettier の設定

モジュールのインストール

ターミナル
npm i -D eslint prettier eslint-config-prettier

ESlintの初期化

ターミナル
npx eslint --init

lintの設定は、各々好きなように設定してください。下記は一例です。

ターミナル
?How would you like to use ESLint? ...
> To check syntax, find problems, and enforce code style
? What type of modules does your project use? ..
> JavaScript modules (import/export)
? Which framework does your project use? ...
> React
? Does your project use TypeScript?
> Yes
? Where does your code run? ...  (Press <space> to select, <a> to toggle all,<i> to invert selection)
> Browser
? How would you like to define a style for your project? ... Use a popular style guide
> Answer questions about your style
? What format do you want your config file to be in? ...
> JSON
? What style of indentation do you use? ...
> Tabs
? What quotes do you use for strings? ...
> Single
? What line endings do you use? ...
> Unix
? Do you require semicolons?
> yes
eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
? Would you like to install them now?
> Yes

上記を実行すると.eslintrc.jsが自動作成されます。

.eslintrc.json
module.exports = {
	'env': {
		'browser': true,
		'es2021': true
	},
	'extends': [
		'eslint:recommended',
		'plugin:react/recommended',
		'plugin:@typescript-eslint/recommended'
+        "plugin:react-hooks/recommended",
+   	 "prettier"
	],
	'overrides': [
	],
	'parser': '@typescript-eslint/parser',
	'parserOptions': {
+        "ecmaFeatures": {
+			"jsx": true
+		},
		'ecmaVersion': 'latest',
		'sourceType': 'module'
	},
+    "settings": {
+		"react": {
+			"version": "detect"
+		},
+		"import/resolver": {
+			//importするファイルをjsだけではなく、tsを含むファイルを許可する
+			"node": {
+				"paths": ["src"],
+				"extensions": [".js", ".jsx", ".ts", ".tsx"]
+			}
+		}
+	},
	'plugins': [
		'react',
		'@typescript-eslint'
	],
	'rules': {
		'indent': [
			'error',
			'tab'
		],
		'linebreak-style': [
			'error',
			'unix'
		],
		'quotes': [
			'error',
			'single'
		],
		'semi': [
			'error',
			'always'
		]
	}
};

Prettier 設定ファイルの作成

ターミナル
touch .prettierrc.json
.prettierrc.json
{
	"printWidth": 120,
	"tabWidth": 2,
	"useTabs": true,
	"singleQuote": true,
	"trailingComma": "none",
	"semi": true
}

ESlint Prettier 例外設定ファイルの作成

ターミナル
touch .eslintignore .prettierignore
.eslintignore
webpack.config.js
.prettierignore
# Ignore artifacts:
build
coverage

ESlintの設定によっては大量のlint errorが表示されていると思いますが、コレをPrettierで整形していきます。

まずは最初のほうで作成した.vscode, settings.jsonの内容を変更していきます。

下記のように追記してください。

ココではDefaultformatterprettierに設定し、onSaveつまり上書き保存したときにFormatterを作動させるように設定してあります。

setting.json
{
+	"editor.formatOnSave": true,
+	"editor.defaultFormatter": "esbenp.prettier-vscode"
}

上記を保存してから、エラーが表示されているファイルに対して、すべて上書き保存をかけます。

上書きしてもlint errorが消えない場合は、ctrl + shift + @でVScodeのリロードをかけてみましょう。

それでもlint errorが消えないのは設定が間違っているかもしれません、.eslintrc.jsファイルを見直してみましょう。

ココまでのフォルダ構成
test-project
├── dist
│   └── index.html
├── package.json
├── package-lock.json
├── src
│   ├── App.scss
│   ├── App.tsx
│   ├── image.d.ts
│   └── index.tsx
├── tsconfig.json
├── webpack.config.js
└── node_module

4. build してみる。

build

ターミナル
npm run build

webpack XXX compiled successfully in XXX ms と表示されてたらコンパイル完了です。

devServer

devServerを動かして見ましょう。

ターミナル
npm run dev

ブラウザから http://localhost:3000/ にアクセスすれば下記のように表示されるはずです。

build成功img

これでReact + Typescriptのマニュアルインストールは終了です。お疲れ様でした。

5. Github

https://github.com/MaSTerAK-902/Zenn_Articles.git

GitHubで編集を提案

Discussion

ログインするとコメントできます