tag:crieit.net,2005:https://crieit.net/tags/webpack-stream/feed 「webpack-stream」の記事 - Crieit Crieitでタグ「webpack-stream」に投稿された最近の記事 2021-01-27T00:00:53+09:00 https://crieit.net/tags/webpack-stream/feed tag:crieit.net,2005:PublicArticle/16653 2021-01-27T00:00:53+09:00 2021-01-27T00:00:53+09:00 https://crieit.net/posts/webpack-dynamically-change-entrypoint-20210121 Webpack (webpack-stream) で エントリーポイントを動的に変更する <p>Scss に続いて JS でも動的に読み込むファイルを切り替えたいと考えました。</p> <h2 id="今回の方法"><a href="#%E4%BB%8A%E5%9B%9E%E3%81%AE%E6%96%B9%E6%B3%95">今回の方法</a></h2> <h3 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h3> <p>サンプルとして、以下のようなディレクトリ構造にしたとします。</p> <pre><code> / ├ dist/ │ └ js/ │ ├ app.min.js // app.js からビルド │ └ parts.min.js // parts/ ディレクトリ下のjsをバンドル └ src/ └ js/ ├ app.js │ └ parts/ ├ parts1.js // 単独 ├ parts2.js // parts3.js に依存 └ parts3.js </code></pre> <ul> <li><code>src/js/app.js</code>: (存在すれば)常に出力、通常の処理 <ul> <li><code>dist/js/app.min.js</code> にビルド</li> </ul></li> <li><code>src/js/parts/</code>ディレクトリ下: フラグによって動的に読み込むファイルを切り替える <ul> <li><code>dist/js/parts.min.js</code> にバンドル</li> </ul></li> </ul> <p>状況的にはこのようにしたいと考えています。</p> <h3 id="webpack.config.js"><a href="#webpack.config.js">webpack.config.js</a></h3> <p>以上を踏まえて <code>webpack.config.js</code> 。</p> <pre><code class="javascript">const webpackTerser = require('terser-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const path = require('path'); const glob = require('glob'); const devFlag = true; // dynamic import test const partsPath = 'src/js/parts'; const flag = { parts1: true, parts2: true }; const entry = () => { const entries = glob .sync( '**/*.js', { cwd: 'src/js', //ignore ignore: [ 'parts/**/*.js', '**/_*.js' ] } ) .map(function (key) { return [key, path.resolve('src/js', key)]; }); // `{ 'app.js': 'PATH/TO/PROJECT/src/js/app.js' }` のようなオブジェクトを entriesObj とする let entriesObj = Object.fromEntries(entries); // 以下、 parts に関する処理 partsCount = 0; // flag の中で true になっている個数をカウント Object.keys(flag).forEach(function (index) { if (flag[index]) { partsCount++; } }); if (partsCount > 0) { // flag の中で true になっている個数が少なくとも1つある場合は、 `parts.js` キーを entriesObj に追加 entriesObj[`parts.js`] = []; // entriesObj の `parts.js` キー に配列で複数のファイル (parts1.js, parts2.js) を指定 Object.keys(flag).forEach(function (index) { if (flag[index]) { entriesObj[`parts.js`].push(path.resolve(partsPath, `${index}.js`)); } }); } return entriesObj; }; const configs = { mode: development, entry: entry(), output: { filename: '[name]', }, devtool: 'inline-source-map', plugins: [ new CleanWebpackPlugin({ cleanOnceBeforeBuildPatterns: [ 'dist/js/**/*.js' ], }), ], optimization: { minimizer: [ new webpackTerser({ extractComments: 'some', terserOptions: { compress: { drop_console: false, }, }, }), ], }, }; module.exports = configs; </code></pre> <ul> <li>エントリーポイントには <code>glob.sync</code> から始まる処理で指定 <ul> <li><code>src/js</code>下 の <code>.js</code> ファイルを <code>{ 'app.js': 'PATH/TO/PROJECT/src/js/app.js' }</code> のようにキーはファイル名、値がファイルへのフルパス、となるようなオブジェクトを作っています <ul> <li><code>ignore</code> で <code>src/js/parts/</code>下 は最初は除外しています</li> <li>本題からは逸れますが <code>_*.js</code> で <code>_</code> 始まりのファイルも <code>ignore</code> 指定しています</li> </ul></li> </ul></li> <li>オブジェクト <code>flag</code> のキーと <code>src/js/parts/</code>下 のファイル名を一致させている、という条件下でフラグの <code>true</code> or <code>false</code> によりエントリーポイントを変化させています <ul> <li>今回の出力は <code>dist/js/parts.min.js</code> でひとまとめにする前提にしています。 <code>app.min.js</code> の中には混ぜません</li> <li>そのため、エントリーポイントを <code>{ 'app.js': [ 'PATH/TO/PROJECT/src/js/app.js', 'PATH/TO/PROJECT/src/js/parts/parts1.js', ... ] }</code> とするのではなく、 <code>{ 'app.js': 'PATH/TO/PROJECT/src/js/app.js', 'parts.js': [ 'PATH/TO/PROJECT/src/js/parts1.js', 'PATH/TO/PROJECT/src/js/parts/parts2.js', ... ] }</code> となるように処理を加えました</li> </ul></li> </ul> <h3 id="Gulpタスク (JavaScriptに関する部分)"><a href="#Gulp%E3%82%BF%E3%82%B9%E3%82%AF+%28JavaScript%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E9%83%A8%E5%88%86%29">Gulpタスク (JavaScriptに関する部分)</a></h3> <pre><code class="javascript">const { dest } = require('gulp'); const plumber = require('gulp-plumber'); const notify = require('gulp-notify'); const rename = require('gulp-rename'); const webpackStream = require('webpack-stream'); const webpackConfig = require('webpack.config'); const jsBuild = () => { return webpackStream(webpackConfig) .pipe(plumber({ errorHandler: notify.onError({ message: 'Error: <%= error.message %>', title: 'jsLibBuild' }) })) .pipe(rename((path) => { path.basename += '.min' path.extname = '.js' })) .pipe(dest('dist/js')); }; module.exports = jsBuild; </code></pre> <p>これでひとまず意図した出力が得られました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sansaisoba/items/921438a19cbf5a31ec53">webpackの基本だけどハマりやすいentryの設定と[name] - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://speakerdeck.com/tomof/webpackgahe-gu-bi-yao-de-he-gu-fen-kariduraifalseka">webpackが何故必要で、 何故分かりづらいのか - Speaker Deck</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kaoru-furusawa/items/fb3f6a3b5023013f3122">webpack + glob でentryファイルを複数指定する方法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://webpack.js.org/concepts/entry-points/#multi-page-application">webpackのentryファイルを複数指定、globパッケージの使い方 - Qiita</a></li> </ul> arm-band