Webpackでsassとtypescriptの環境を作る

package.json

{
  "name": "front",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --mode development --env development",
    "dev": "webpack-dev-server --mode development --env development",
    "build": "webpack --mode production"
  },
  "keywords": [],
  "author": "hpscript",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.12.13",
    "@babel/preset-env": "^7.12.13",
    "@babel/preset-react": "^7.12.13",
    "babel-loader": "^8.2.2",
    "css-loader": "^5.0.2",
    "mini-css-extract-plugin": "^1.3.6",
    "node-sass": "^5.0.0",
    "optimize-css-assets-webpack-plugin": "^5.0.4",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "sass-loader": "^11.0.1",
    "style-loader": "^2.0.0",
    "ts-loader": "^9.2.6",
    "typescript": "^4.4.3",
    "webpack": "^5.53.0",
    "webpack-cli": "^4.8.0",
    "webpack-dev-server": "^4.2.1"
  },
  "description": ""
}

webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
	entry: {
		anyname: `./src/scss/style.scss`,
		bundle: './src/app.ts'
	},
	output: {
		path: path.join(__dirname,'dist'),
		filename: '[name].js'
	},
	resolve: {
		extensions:['.ts','.js']
	},
	devServer: {
		host: '192.168.34.10',
        port: '8000',
        static: "./dist",
        open: true
	},
	module: {
		rules: [
			{
				test:/\.ts$/,use:'ts-loader'
			},
			{
                test: /\.scss$/,
                use: [
                    { loader: MiniCssExtractPlugin.loader },
                    { loader: 'css-loader' },
                    { loader: 'sass-loader' },
                ],
            }
		]
	},
	plugins: [
        new MiniCssExtractPlugin({ filename: 'css/style.css'}),
    ],
    optimization: {
        minimizer: [new OptimizeCSSAssetsPlugin({})],
    },
}

tsconfig.json

環境構築まではOK^^

[React] WebpackでReactの環境を構築する

$ node -v
v14.15.0
$ npm -v
6.14.8
$ cd sample

### npm
$ npm -y init
$ npm -D install webpack webpack-cli
$ npx webpack -v
webpack 5.21.2
webpack-cli 4.5.0
$ npm -D i sass-loader node-sass style-loader css-loader
$ npm -D i mini-css-extract-plugin optimize-css-assets-webpack-plugin
$ npm i -D webpack-dev-server
$ npm install –save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader
$ npm install –save-dev react react-dom

package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server",
    "dev": "webpack --mode development",
    "build": "webpack --mode production",
    "watch": "webpack --mode development --watch --color --progress"
  },

ディレクトリ構成

webpack.config.js

var webpack = require('webpack');
var path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = (env, argv)=> {

	const IS_DEVELOPMENT = argv.mode === 'development';

	return {
		mode: argv.mode,

		entry: {
			'index': './js/index.js',
		},

		devltool: IS_DEVELOPMENT ? 'source-map': 'none',

		output: {
			filename: '[name].js',
			path: path.resolve(__dirname,'../dist'),
		},

		plugins: [
			new MiniCssExtractPlugin({
				filename: '[name].css',
				path: path.resolve(__dirname,'../dist'),
			})
		],

		resolve: {
			extensions: [".ts"]
		},

		module: {
			rules: [
				{
					test:/\.ts$/,
					use: "ts-loader"
				},
				{
					test:/\.js$/,
					use: [
						{
							loader:"babel-loader",
							options: {
								presets: [
									"@babel/preset-env"
								]
							}
						}
						
					]
				},
				{
					test:/index\.scss$/,
					use: [
						MiniCssExtractPlugin.loader,
						{
							loader:'css-loader',
							options: {
								url: false,
							}
						},
						{
							loader: "postcss-loader",
							options: [
								require("autoprefixer")({
									grid: true,
								})
							]
						}
					]
				}
			]
		},

	}
};

$ webpack –mode development
[webpack-cli] ValidationError: Invalid options object. Mini CSS Extract Plugin has been initialized using an options object that does not match the API schema.
– options has an unknown property ‘path’. These properties are valid:
ん?
なんでや。。

var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
	context:path.join(__dirname, "src"),
	entry: "./js/client.js",
	output: {
		path: __dirname + "/dest/js",
		filename: "client.min.js"
	},
	module: {
		rules: [
			{
				test: /\.css$/i,
	        	use: ['style-loader', 'css-loader'],
			},
			{
			test: /\.jsx?$/,
				exclude: /(node_modules|bower_components)/,
				use: [{
					loader: 'babel-loader',
					options: {
						presets: ['@babel/preset-react', '@babel/preset-env']
					}
				}]
		},
		{
                test: /\.scss$/,
                use: [
                    { loader: MiniCssExtractPlugin.loader },
                    { loader: 'css-loader' },
                    { loader: 'sass-loader' },
                ],
            }
		]
	},
	
	devServer: {
	        contentBase: __dirname + '/src',
	        host: "0.0.0.0"
	},
	plugins: debug ? [] : [
		new webpack.optimize.OccurrenceOrderPlugin(),
		new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false}),
	]
};

$ webpack-cli serve –mode development
ℹ 「wds」: Project is running at http://0.0.0.0:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /home/vagrant/dev/sample/src
ℹ 「wdm」: asset client.min.js 364 KiB [emitted] (name: main)
runtime modules 432 bytes 3 modules
cacheable modules 335 KiB
modules by path ../node_modules/webpack-dev-server/client/ 20.9 KiB 10 modules
modules by path ../node_modules/html-entities/lib/*.js 61 KiB 5 modules
modules by path ../node_modules/url/ 37.4 KiB 3 modules
modules by path ../node_modules/querystring/*.js 4.51 KiB
../node_modules/querystring/index.js 127 bytes [built]
../node_modules/querystring/decode.js 2.34 KiB [built]
../node_modules/querystring/encode.js 2.04 KiB [built]
modules by path ../node_modules/webpack/hot/*.js 1.42 KiB
../node_modules/webpack/hot/emitter.js 75 bytes [built]
../node_modules/webpack/hot/log.js 1.34 KiB [built]
../node_modules/webpack/hot/ sync nonrecursive ^\.\/log$ 170 bytes [built]
webpack 5.21.2 compiled successfully in 1271 ms
ℹ 「wdm」: Compiled successfully.

ん!
とりあえず作り始めるか。

[webpack 5.6.0]webpack-dev-server^3.11.0でMODULE_NOT_FOUND

$ npm run start

> front@1.0.0 start /home/vagrant/dev/front
> webpack-dev-server

internal/modules/cjs/loader.js:883
throw err;
^

Error: Cannot find module ‘webpack-cli/bin/config-yargs’
Require stack:
– /home/vagrant/dev/front/node_modules/webpack-dev-server/bin/webpack-dev-server.js
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
at Function.Module._load (internal/modules/cjs/loader.js:725:27)
at Module.require (internal/modules/cjs/loader.js:952:19)
at require (internal/modules/cjs/helpers.js:88:18)
at Object. (/home/vagrant/dev/front/node_modules/webpack-dev-server/bin/webpack-dev-server.js:65:1)
at Module._compile (internal/modules/cjs/loader.js:1063:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) {
code: ‘MODULE_NOT_FOUND’,
requireStack: [
‘/home/vagrant/dev/front/node_modules/webpack-dev-server/bin/webpack-dev-server.js’
]
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! front@1.0.0 start: `webpack-dev-server`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the front@1.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /home/vagrant/.npm/_logs/2020-11-20T05_45_43_741Z-debug.log

こちらのissueを参考に修正する
https://github.com/webpack/webpack-dev-server/issues/2759

package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack serve --mode development --env development"
  },

$ npm run start

これで上手くいきます。

gulp卒業 -> webpack一本化 [フロント環境]

今までフロント構築時は、sassからcssへの変換およびサーバ起動はgulpでやってたんだけど、webpackで両方とも出来るということがわかったので、gulpは卒業してwebpackに一本化します。

### 前準備
$ node -v
v12.18.3
$ npm -v
6.14.6
$ npm -y init
$ npm -D install webpack webpack-cli
$ npx webpack -v
4.44.0
$ npm -D i sass-loader node-sass style-loader css-loader
$ npm -D i mini-css-extract-plugin optimize-css-assets-webpack-plugin

### webpack-dev-server導入
$ npm i -D webpack-dev-server

package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server"
  },

webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
	entry: {
		anyname: `./src/scss/style.scss`,
		main: `./src/index.js`,
	},
	output: {
		filename: "[name].js",
		path: path.resolve(__dirname, 'dist'),
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.scss$/,
				use: [
					{ loader: MiniCssExtractPlugin.loader },
					{ loader: 'css-loader' },
					{ loader: 'sass-loader' },
				],
			}
		],
	},
	plugins: [
		new MiniCssExtractPlugin({ filename: 'css/style.css'}),
	],
	optimization: {
		minimizer: [new OptimizeCSSAssetsPlugin({})],
	},
}

### サーバ起動
$ npm run start

/dist/index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<link rel="stylesheet" href="css/style.css">
	<script src="main.js"></script>
</head>
<body>
	<h1>hello</h1>
</body>
</html>

/src/style.scssや/src/index.jsを変更すると、ブラウザが自動更新されます。

gulp使ってると、まだgulp触ってるの?みたいな風潮があるからね。。
babel、linterとか他にも色々あるみたいだけど、とりあえず今回はこの環境でフロントを作り始めたいと思います。

webpack4でsassからcssとindex.jsからmain.jsにトランスパイル

$ node -v
v12.18.3
$ npm -v
6.14.6
$ npm -y init
$ npm -D install webpack webpack-cli
$ npx webpack -v
4.44.0
$ npm -D i sass-loader node-sass style-loader css-loader
$ npm -D i mini-css-extract-plugin optimize-css-assets-webpack-plugin

webpack.config.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
	entry: {
		anyname: `./src/scss/style.scss`,
	},
	output: {
		path: path.resolve(__dirname, 'dist'),
	},
	// entry: `./src/index.js`,

	// output: {
	// 	filename: "main.js"
	// },
	module: {
		rules: [
			{
				test: /\.scss$/,
				use: [
					{ loader: MiniCssExtractPlugin.loader },
					{ loader: 'css-loader' },
					{ loader: 'sass-loader' },
				],
			}
		],
	},
	plugins: [
		new MiniCssExtractPlugin({ filename: 'css/style.css'}),
	],
	optimization: {
		minimizer: [new OptimizeCSSAssetsPlugin({})],
	},
}

./src/scss/style.scss

body {
	background-color: aqua;
}

### npx実行
$ npx webpack –mode production

./dest/css/style.css

body{background-color:#0ff}

sassからcssには出来た。

### sassはcssに、jsはdist/main.jsにトランスパイル
sassからcssにする場合と、index.jsからmain.jsにする場合に、一々webpack.config.jsのコメントアウトを外すのは面倒。entryを複数に書いてみる。

module.exports = {
	entry: {
		anyname: `./src/scss/style.scss`,
		main: `./src/index.js`,
	},
	output: {
		filename: "[name].js",
		path: path.resolve(__dirname, 'dist'),
	},
	// 省略
}

こうすると、distフォルダにanyname.jsとmain.jsが作られる。

あとはローカルサーバか。

webpack環境設定: JS内にCSSをバンドルしない場合

– 一般的なヘッダ要素

<link rel="stylesheet" href="style.css">
<script src="main.js"></script>

– webpackの “MiniCssExtractPlugin” を使用する
– バンドルしたjsから、stylesheet部分を別ファイルとして出力できる

– webpack, webpack-cli, webpack-dev-server, style-loader, css-loader, sass, sass-loaderインストール済

### モジュールインストール
$ npm i -D postcss-loader autoprefixer

webpack.config.js

module.exports = {

	mode: "development",
	devtool: "source-map",

	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.scss/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { 
							url: false,
							sourceMap: true,
							importLoaders: 2
						}
					},
					{
						loader: "postcss-loader",
						options: {
							sourceMap: true,
							plugins: [
								require("autoprefixer")({
									grid: true
								})
							]
						}
					},
					{
						loader: "sass-loader",
						options: {
							sourceMap: true
						}
					}
				]
			}
		]
	}
};

webpack環境設定: Sass

– style.scssをmain.jsにバンドルさせる
– webpack, webpack-cli, webpack-dev-server, style-loader, css-loaderインストール済

### Sassモジュールインストール
$ npm i -D sass sass-loader

### webpack.config.js
– scssファイル -> sass-loader -> css-loader -> style-loader

const MODE = "development";
const enabledSourceMap = MODE === "development";

module.exports = {

	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.scss/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { 
							url: false,
							sourceMap: enabledSourceMap,

							// 0 => no loaders(default);
							// 1 => postcss-loader;
							// 2 => postcss-loader, sass-loader;
							importLoaders: 2
						}
					},
					{
						loader: "sass-loader",
						options: {
							sourceMap: enabledSourceMap
						}
					}
				]
			}
		]
	}
};

/src/style.scss

/src/index.js

import "./style.scss";

$ npm run build

webpack環境設定: style.cssの設定

### 前提条件
– node:v12.18.3、npm:6.14.6
– webpack, webpack-cli, webpack-dev-serverインストール済

### Style LoaderとCSS Loaderのインストール
$ npm i -D style-loader css-loader

– 拡張子.cssファイルに対して、useで指定したLoaderが後ろから順番に適用される
– cssファイル -> css-loader -> style-loader -> main.js
webpack.config.js

module.exports = {
	// 省略

	module: {
		rules: [
			{
				test: /\.css/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { url: false }
					}
				]
			}
		]
	}
};

/src/index.js

import "./style.css";

/src/style.css

$ npm run build
-> style.cssが /dist/main.jsにバンドルされる

### ソースマップの出力
– ソースマップとは変換前のコード情報
webpack.config.js

const MODE = "development";
const enabledSourceMap = MODE === "development";

module.exports = {

	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	},

	module: {
		rules: [
			{
				test: /\.css/,
				use: [
					"style-loader",
					{
						loader: "css-loader",
						options: { 
							url: false,
							sourceMap: enabledSourceMap
						}
					}
				]
			}
		]
	}
};

さあ、続いてSassに行きます。

フロント構築時のwebpack環境設定1

$ node -v
v12.18.3
$ npm -v
6.14.6

// package.json生成
$ npm init -y

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

// webpack本体のインストール
$ npm i -D webpack webpack-cli
$ ls
node_modules package-lock.json package.json

### src, distフォルダの作成
$ ls
dist node_modules package-lock.json package.json src

/src/index.js

import { hello } from "./sub";

hello();
[/jqvascript]

/src/sub.js

export function hello(){
	alert("hello method is executed.");
}

### webpackによるbuild
$ npx webpack
Hash: 910d84f6d37763164f2d
Version: webpack 4.43.0
Time: 127ms
Built at: 07/25/2020 12:01:30 AM
Asset Size Chunks Chunk Names
main.js 986 bytes 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 103 bytes {0} [built]
| ./src/index.js 40 bytes [built]
| ./src/sub.js 63 bytes [built]

### package.jsonのカスタマイズ
最低限の記述だけにし、npm run buildで実行できるようにする

{
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12"
  }
}

$ npm run build

### webpack.config.js
– エントリーポイントを指定しなければ自動的に「src/index.js」がエントリーポイントに、出力先を指定しなければ自動的に「dist/main.js」に出力される
– modeを”development”にするとソースマップが出力される
/webpack.config.js

module.exports = {
	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	}
};

### webpackでローカルサーバを起動し、変更時にブラウザリロード
$ npm i -D webpack-dev-server

– package.jsonに”start”を追加する
/package.json

{
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "start": "webpack-dev-server"
  },
  "devDependencies": {
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}

– config.jsでhostとportを指定する
/webpack.config.js

module.exports = {
	entry: `./src/index.js`,

	output: {
		filename: "main.js"
	},

	mode: "development",

	devServer: {
		host: '192.168.33.10',
		port: '8000',
		contentBase: "dist",
		open: true
	}
};

$ npm run start

OK、次は、webpackにsassを入れます。

vagrantでwebpack-dev-server

built-in serverがwebpackにある??

[vagrant@localhost react]$ ./node_modules/webpack-dev-server/bin/webpack-dev-server.js –content-base src –mode development
ℹ 「wds」: Project is running at http://0.0.0.0:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /home/vagrant/react/src
ℹ 「wdm」: Hash: f58893047107846a01ed
Version: webpack 4.41.2
Time: 1464ms
Built at: 2019-11-20 18:36:59
Asset Size Chunks Chunk Names
client.min.js 1.43 MiB main [emitted] main
Entrypoint main = client.min.js
[0] multi ../node_modules/webpack-dev-server/client?http://0.0.0.0:8080 ./js/client.js 40 bytes {main} [built]
[../node_modules/ansi-html/index.js] 4.16 KiB {main} [built]
[../node_modules/html-entities/index.js] 231 bytes {main} [built]
[../node_modules/react-dom/index.js] 1.33 KiB {main} [built]
[../node_modules/react/index.js] 190 bytes {main} [built]
[../node_modules/webpack-dev-server/client/index.js?http://0.0.0.0:8080] ../node_modules/webpack-dev-server/client?http://0.0.0.0:8080 4.29 KiB {main} [built]
[../node_modules/webpack-dev-server/client/overlay.js] 3.51 KiB {main} [built]
[../node_modules/webpack-dev-server/client/socket.js] 1.53 KiB {main} [built]
[../node_modules/webpack-dev-server/client/utils/createSocketUrl.js] 2.89 KiB {main} [built]
[../node_modules/webpack-dev-server/client/utils/log.js] 964 bytes {main} [built]
[../node_modules/webpack-dev-server/client/utils/reloadApp.js] 1.59 KiB {main} [built]
[../node_modules/webpack-dev-server/client/utils/sendMessage.js] 402 bytes {main} [built]
[../node_modules/webpack-dev-server/node_modules/strip-ansi/index.js] 161 bytes {main} [built]
[../node_modules/webpack/hot sync ^\.\/log$] ../node_modules/webpack/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
[./js/client.js] 2.69 KiB {main} [built]
+ 29 hidden modules
ℹ 「wdm」: Compiled successfully.

あれ、vagrantでポートフォワーディングしているipを叩いても、ダメだ。
192.168.34.10:8080
なぜ?

webpack.config.jsに以下を追加

devServer: {
    contentBase: __dirname + '/public',
    host: "0.0.0.0"
	},

phpのビルトインより楽そうですね^^