【Svelte】Svelteでrollupの整理をしてみた【rollup.js】

## 概要
[前回](https://qiita.com/hisayuki/items/34de77da216afc0a9e1a)と[前々回](https://qiita.com/hisayuki/items/0ca740968ea34806efa3)でSvelteでTypescript + Pug + Sassを使えるようにしました。
今回はタスクランナー兼、ビルドツールのrollupを最適化していきたいと思います。

## 手順

### 追加パッケージ
- `postcss`でautoprefixerを使用するので追加。
- `.env`ファイルを読み込むようにするので`dotenv`を追加
- 既存のserveを使わずにpluginで行うため、`rollup-plugin-serve`を追加

```sh:console
yarn add -D autoprefixer dotenv rollup-plugin-serve
```

`rollup.config.js`にimportする。

```js:rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import html from 'rollup-plugin-bundle-html';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import serve from 'rollup-plugin-serve';
import dotenv from 'dotenv';
import typescript from 'rollup-plugin-typescript2';
import typescriptCompiler from 'ypescript';
import sveltePreprocessor from 'svelte-preprocess';
import autoprefixer from 'autoprefixer';
```


### buildの出力先をdistに変更
以下のパスを`./dist`変更します

- `output`内の`file: './dist/js/bundle.js'`
- `plugins`内の`!production && livereload('./dist'),` 

```js:rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import html from 'rollup-plugin-bundle-html';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import serve from 'rollup-plugin-serve';
import dotenv from 'dotenv';
import typescript from 'rollup-plugin-typescript2';
import typescriptCompiler from 'ypescript';
import sveltePreprocessor from 'svelte-preprocess';
import autoprefixer from 'autoprefixer';

const production = !process.env.ROLLUP_WATCH;

export default {
	input: 'src/main.ts',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: './dist/js/bundle.js'
	},
	plugins: [
		svelte({
			dev: !production,
			extensions: [".svelte"],
			css: css => {
                css.write('./public/build/bundle.css');
			},
            preprocess: sveltePreprocessor({
                scss: true
            }),
			emitCss: true,
		}),
		resolve({
			browser: true,
			dedupe: ['svelte']
		}),
		postcss({
			extract: true
		}),
		typescript({ typescript: typescriptCompiler }),
		commonjs(),
		!production && serve(),
		!production && livereload('./dist'),
		production && terser()
	],
	watch: {
		clearScreen: false
	}
};

function serve() {
	let started = false;

	return {
		writeBundle() {
			if (!started) {
				started = true;

				require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
					stdio: ['ignore', 'inherit', 'inherit'],
					shell: true
				});
			}
		}
	};
}

```

### svelte
`rollup-plugin-svelte`は既に入ってると思うので、修正方法のみ。
今はbuild後のファイルがすべて`build`ディレクトリに入るので、JSとCSSを分離します。

- `css`の出力先を`public/build`ではなく、`dist/css`ディレクトリにする
- `css `を`false`にする
    - `svelte`でCSSの出力を行わないようにする
    - `emitCss`を`true`にしておくことで、別のプラグインにCSS出力を移譲する。（今回は`postcss`に移譲)



```js:rollup.config.js
		svelte({
			dev: !production,
			css: false
			preprocess: sveltePreprocessor(),
			emitCss: true
		}),
```


### html
`index.html`を`src`ディレクトリから取得して`dist`にコピーするようにします。

```sh:console
yarn add -D rollup-plugin-bundle-html
```

- `template`にコピー元の`index.html`を指定する
- `dest`に出力先のディレクトリを指定する
- `filename`に出力後のファイル名を指定する

```js:rollup.config.js
		html({
			template: "src/index.html",
			dest: "dist",
			filename: "index.html"
		}),
```

`dist`に出力されるHTMLは少し加工されます。

```html:コピー元html
<!DOCTYPE html>
<html lang="jp">
<head>
	<meta charset='utf-8'>
	<meta name='viewport' content='width=device-width,initial-scale=1'>

	<title>Svelte app</title>

	<link rel='icon' type='image/png' href='/favicon.png'>
	<link rel='stylesheet' href='/global.css'>
</head>
<body/>
</html>
```

出力後HTMLには`build`された`bundle.js`と`bundle.css"`の読み込みパスが自動で差し込まれます。
そのため、コピー元htmlからは外しておきます。

```html:distに出力されるhtml
<!DOCTYPE html>
<html lang="jp">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width,initial-scale=1">

	<title>Svelte app</title>

	<link rel="icon" type="image/png" href="/favicon.png">
	<link rel="stylesheet" href="/global.css">
<link rel="stylesheet" href="css/bundle.css">
</head>
<body>

<script type="text/javascript" src="build/bundle.js"></script>
</body></html>

```

### postcss
`svelte`プラグインでやっていたCSSの出力処理を行います。

- `extract`に出力先の`./dist/css/bundle.css`を指定します
- `sourceMap`を`true`にして出力するようにします
- `plugins`に`autoprefixer`を追加します

```js:rollup.config.js
		postcss({
			extract: './dist/css/bundle.css',
			sourceMap: true,
			plugins: [
				autoprefixer()
			]
		}),
```

### resolve＆commonjs

今回は特に指定をしないので、どちらも引数はなしにします。

```js:rollup.config.js
    resolve(),
    commonjs()
```

### DevelopMode

このアタリは好みの問題ですが、`production`を`const production = !process.env.ROLLUP_WATCH;`で判定しているので、これを逆にしました。
変数名的にも`ROLLUP_WATCH`ってしたくなかったので、developかどうかを判定するように変更します。

まず`.env`ファイルを作成します。
その中に`DEVELOP_MODE`といいう環境変数を追加して、trueにします。

```env:.env
DEVELOP_MODE=true
```

次に`rollup.config.js`も修正します。

- `const production = !process.env.ROLLUP_WATCH;`の削除
- `dotenv.config();`の追記
- `svelte()`内の`dev：`を`dev:process.env.DEVELOP_MODE`に変更
- `plugins`部分の切り出し
- `serve()`や`livereload`などのdevelopでのみ動くものをif文で切り出し
- `function serve()`を消して、`rollup-plugin-serve`を適用

一応どこ直したかをピックアップしましたが、このあとの完成版見てもらったほうが早いと思います。

## 完成
最終的に出来上がった`rollup.config.js`がこちら

```js:rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import html from 'rollup-plugin-bundle-html';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import serve from 'rollup-plugin-serve';
import dotenv from 'dotenv';
import typescript from 'rollup-plugin-typescript2';
import typescriptCompiler from 'typescript';
import sveltePreprocessor from 'svelte-preprocess';
import autoprefixer from 'autoprefixer';

dotenv.config();
const plugins = [
	svelte({
		dev: process.env.DEVELOP_MODE,
		css: false,
		preprocess: sveltePreprocessor(),
		emitCss: true
	}),
	html({
		template: "src/index.html",
		dest: "dist",
		filename: "index.html"
	}),
	postcss({
		extract: './dist/css/bundle.css',
		sourceMap: true,
		plugins: [
			autoprefixer()
		]
	}),
	typescript({ typescript: typescriptCompiler }),
	resolve(),
	commonjs()
]

if (process.env.DEVELOP_MODE) {
	plugins.push(
		serve({
			contentBase: './dist',
			open: true
		}),
		livereload({ watch: "./dist" })
	);
} else {
	plugins.push(terser({ sourcemap: true }))
}


export default {
	input: 'src/main.ts',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: './dist/js/bundle.js'
	},
	plugins,
	watch: {
		clearScreen: false
	}
};
```

## まとめ
今回の最適化でrollupが何やってるのか？とか、各pluginになんの引数渡して、何が設定できるのかの理解を深められました。
Webpackやgulpなどもそんなに詳しくないので、よい勉強になったと思います！
また自分の`rollup.config.js`を変更した際には追記すると思います。

## 参考Github
[sveltejs/rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte)
[remaxjs/rollup-plugin-postcss](https://github.com/remaxjs/rollup-plugin-postcss)
[rollup/rollup-plugin-node-resolve](https://github.com/rollup/rollup-plugin-node-resolve)
[thgh/rollup-plugin-serve](https://github.com/thgh/rollup-plugin-serve)