tag:crieit.net,2005:https://crieit.net/tags/gulp/feed
「gulp」の記事 - Crieit
Crieitでタグ「gulp」に投稿された最近の記事
2021-11-28T17:45:22+09:00
https://crieit.net/tags/gulp/feed
tag:crieit.net,2005:PublicArticle/17792
2021-11-28T17:45:22+09:00
2021-11-28T17:45:22+09:00
https://crieit.net/posts/gifsicle-error-gifsicle-pre-build-test-failed-20211128
yarn 時に gifsicle で gifsicle pre-build test failed エラー
<p>数日前から yarn 時に gifsicle で gifsicle pre-build test failed エラーが出るようになってしまったので対処します。</p>
<p>あれこれ試しましたが、結論から言うと <code>imagemin-gifsicle</code> が依存している <code>gifsicle</code> の <code>5.0.0</code> だとダメなので、 <code>resolution</code> で <code>5.2.1</code> を指定すると良さそうです。</p>
<h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2>
<pre><code class="json"> "devDependencies": {
// 略
"gulp-imagemin": "^7.1.0",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-pngquant": "^9.0.2",
"imagemin-gifsicle": "^7.0.0",
"imagemin-svgo": "^9.0.0",
// 略
}
</code></pre>
<p><code>imagemin-gifsicle</code> を使用しているプロジェクトで <code>yarn</code> したところ、以下のエラーで失敗するようになってしまいました。</p>
<pre><code class="bash">warning Error running install script for optional dependency: "PATH\\TO\\PROJECT\\node_modules\\gifsicle: Command failed.
Exit code: 1
Command: node lib/install.js
Arguments:
Directory: PATH\\TO\\PROJECT\\node_modules\\gifsicle
Output:
Response code 404 (Not Found)
gifsicle pre-build test failed
compiling from source
Error: Command failed: C:\\WINDOWS\\system32\\cmd.exe /s /c \"autoreconf -ivf\"
'autoreconf' �́A�����R�}���h�܂��͊O���R�}���h�A
����\\�ȃv���O�����܂��̓o�b�` �t�@�C���Ƃ��ĔF������Ă��܂���B
at PATH\\TO\\PROJECT\\node_modules\\bin-build\\node_modules\\execa\\index.js:231:11
success Saved lockfile.
</code></pre>
<p>バージョンを上げても直後は特に問題なかったような気はするのですが、逐一キャッシュを削除したりしているわけではないので不明。</p>
<p>仕方ないので、色々ググりながら対応してみます。</p>
<h2 id="対処 (失敗したものたち)"><a href="#%E5%AF%BE%E5%87%A6+%28%E5%A4%B1%E6%95%97%E3%81%97%E3%81%9F%E3%82%82%E3%81%AE%E3%81%9F%E3%81%A1%29">対処 (失敗したものたち)</a></h2>
<h3 id="gisicle を足す"><a href="#gisicle+%E3%82%92%E8%B6%B3%E3%81%99">gisicle を足す</a></h3>
<pre><code class="json"> "devDependencies": {
// 略
"gulp-imagemin": "^7.1.0",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-pngquant": "^9.0.2",
"imagemin-gifsicle": "^7.0.0",
"gifsicle": "6.1.0", // 追加
"imagemin-svgo": "^9.0.0",
// 略
}
</code></pre>
<p>変わらず。</p>
<h3 id="逆にいっそ gulp-imagemin 以外全て削除"><a href="#%E9%80%86%E3%81%AB%E3%81%84%E3%81%A3%E3%81%9D+gulp-imagemin+%E4%BB%A5%E5%A4%96%E5%85%A8%E3%81%A6%E5%89%8A%E9%99%A4">逆にいっそ gulp-imagemin 以外全て削除</a></h3>
<pre><code class="json"> "devDependencies": {
// 略
"gulp-imagemin": "^7.1.0",
// 略
}
</code></pre>
<p>変わらず。</p>
<h3 id="コマンドプロンプトを管理者実行"><a href="#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88%E3%82%92%E7%AE%A1%E7%90%86%E8%80%85%E5%AE%9F%E8%A1%8C">コマンドプロンプトを管理者実行</a></h3>
<p><code>gifsicle</code> ありの状態で <code>cmd.exe</code> を管理者として実行→変わらず。</p>
<h3 id="キャッシュ削除"><a href="#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E5%89%8A%E9%99%A4">キャッシュ削除</a></h3>
<pre><code>> yarn cache clean
yarn cache v1.22.11
success Cleared cache.
Done in 450.56s.
</code></pre>
<p>変わらず。</p>
<h3 id="hosts に Github 関係の名前解決を書き込む"><a href="#hosts+%E3%81%AB+Github+%E9%96%A2%E4%BF%82%E3%81%AE%E5%90%8D%E5%89%8D%E8%A7%A3%E6%B1%BA%E3%82%92%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%82%80">hosts に Github 関係の名前解決を書き込む</a></h3>
<pre><code>>ping raw.githubusercontent.com
raw.githubusercontent.com [185.199.108.133]に ping を送信しています 32 バイトのデータ:
185.199.108.133 からの応答: バイト数 =32 時間 =5ms TTL=58
185.199.108.133 からの応答: バイト数 =32 時間 =5ms TTL=58
185.199.108.133 の ping 統計:
パケット数: 送信 = 2、受信 = 2、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
最小 = 5ms、最大 = 5ms、平均 = 5ms
</code></pre>
<p>元々 <code>raw.githubusercontent.com</code> に ping はできていましたが、 <code>hosts</code> に次のように追記してみます。</p>
<pre><code>52.74.223.119 github.com
192.30.253.119 gist.github.com
54.169.195.247 api.github.com
185.199.111.153 assets-cdn.github.com
151.101.76.133 raw.githubusercontent.com
151.101.76.133 gist.githubusercontent.com
151.101.76.133 cloud.githubusercontent.com
151.101.76.133 camo.githubusercontent.com
151.101.76.133 avatars0.githubusercontent.com
151.101.76.133 avatars1.githubusercontent.com
151.101.76.133 avatars2.githubusercontent.com
151.101.76.133 avatars3.githubusercontent.com
151.101.76.133 avatars4.githubusercontent.com
151.101.76.133 avatars5.githubusercontent.com
151.101.76.133 avatars6.githubusercontent.com
151.101.76.133 avatars7.githubusercontent.com
151.101.76.133 avatars8.githubusercontent.com
</code></pre>
<p>変わらず。</p>
<p>なお、ここまで次の issue にあった対処法。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/imagemin/imagemin-gifsicle/issues/33">Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "autoreconf -ivf" · Issue #33 · imagemin/imagemin-gifsicle · GitHub</a></li>
</ul>
<h3 id="ネットワークを変更"><a href="#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%92%E5%A4%89%E6%9B%B4">ネットワークを変更</a></h3>
<p>違う場所に出かけて、ゲートウェイを変更してみました。</p>
<p>が、変わらず。</p>
<h3 id="最小限構成で実験する"><a href="#%E6%9C%80%E5%B0%8F%E9%99%90%E6%A7%8B%E6%88%90%E3%81%A7%E5%AE%9F%E9%A8%93%E3%81%99%E3%82%8B">最小限構成で実験する</a></h3>
<p>長丁場になりそうだったので最小限構成の <code>package.json</code> を作成して検証を続行。</p>
<pre><code>{
"name": "tmp",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"imagemin-gifsicle": "^7.0.0"
}
}
</code></pre>
<p>これで<code>yarn</code>。変わらず。</p>
<p>ちなみに <code>yarn.lock</code> の中身は次のような感じ。</p>
<pre><code>gifsicle@^5.0.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/gifsicle/-/gifsicle-5.3.0.tgz#499713c6f1e89ebbc3630da3a74fdb4697913b4e"
integrity sha512-FJTpgdj1Ow/FITB7SVza5HlzXa+/lqEY0tHQazAJbuAdvyJtkH4wIdsR2K414oaTwRXHFLLF+tYbipj+OpYg+Q==
dependencies:
bin-build "^3.0.0"
bin-wrapper "^4.0.0"
execa "^5.0.0"
imagemin-gifsicle@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz#1a7ab136a144c4678657ba3b6c412f80805d26b0"
integrity sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA==
dependencies:
execa "^1.0.0"
gifsicle "^5.0.0"
is-gif "^3.0.0"
</code></pre>
<p><code>imagemin-gifsicle</code> が依存している <code>gifsicle</code> のバージョンが悪いのではないか、と思い <code>resolutions</code> を追加することにしました。</p>
<h3 id="resolutions を追加"><a href="#resolutions+%E3%82%92%E8%BF%BD%E5%8A%A0">resolutions を追加</a></h3>
<pre><code class="json">{
"name": "tmp",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"imagemin-gifsicle": "^7.0.0"
},
"resolutions": {
"gifsicle": "^6.1.0"
}
}
</code></pre>
<p>すると、 <code>yarn</code> は通りました。</p>
<p>しかし、 6系 は "Use native ESM" なので <code>require</code> を使用している現環境での使用は不可です。惜しい。</p>
<h3 id="resolutions の指定を 5.3.0 にする"><a href="#resolutions+%E3%81%AE%E6%8C%87%E5%AE%9A%E3%82%92+5.3.0+%E3%81%AB%E3%81%99%E3%82%8B">resolutions の指定を 5.3.0 にする</a></h3>
<pre><code>[email protected], gifsicle@^5.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/gifsicle/-/gifsicle-6.1.0.tgz#16194fe7d9420a539709eb065e7245eaf74de5a7"
integrity sha512-tmnKbW2UQzoc/FTg2dq98sUaoGM1/CRQP+HtyeIMNLDlB8ijZbX7TKBCc2s5YJ8oXUZIxdqXH2rKXnCB0/Hg3w==
dependencies:
bin-build "^3.0.0"
bin-wrapper "^4.0.0"
execa "^5.1.1"
</code></pre>
<p><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/gifsicle">gifsicle - npm</a>を確認したところ、11/26現在で <code>6.1.0</code> と並んで "3 days ago" となっている 5系 最新の <code>5.3.0</code> があることが判明。</p>
<pre><code class="json">{
"name": "tmp",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"imagemin-gifsicle": "^7.0.0"
},
"resolutions": {
"gifsicle": "^5.3.0"
}
}
</code></pre>
<p>しかしNG。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/imagemin/gifsicle-bin/issues/134">5.3.0 is error · Issue #134 · imagemin/gifsicle-bin · GitHub</a></li>
</ul>
<blockquote>
<p>yep, this repo's 5.3.0 tag is missing, that makes the downloading process of bin-wrapper failed.</p>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/imagemin/gifsicle-bin/issues/134">5.3.0 is error · Issue #134 · imagemin/gifsicle-bin · GitHub</a></p>
</blockquote>
<p>タグ付けをミスって生まれてしまったバージョンらしいので、これは使えないとのこと。なんて紛らわしい……。</p>
<h2 id="resolutions の指定を 5.2.1 にする"><a href="#resolutions+%E3%81%AE%E6%8C%87%E5%AE%9A%E3%82%92+5.2.1+%E3%81%AB%E3%81%99%E3%82%8B">resolutions の指定を 5.2.1 にする</a></h2>
<pre><code class="json">{
"name": "tmp",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"imagemin-gifsicle": "^7.0.0"
},
"resolutions": {
"gifsicle": "5.2.1"
}
}
</code></pre>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sotarok/items/4ebd4cfedab186355867">package.json のチルダ(~) とキャレット(^) - Qiita</a></li>
</ul>
<p><code>^5.2.1</code> とキャレットを付けると <code>5.2.1 <= version < 6.0.0</code> となってしまい、 <code>5.3.0</code> に上がってしまうので <code>5.2.1</code> とキャレットを外して指定します。</p>
<pre><code>[email protected], gifsicle@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/gifsicle/-/gifsicle-5.2.1.tgz#efadab266a493ef0b4178e34597493349937369e"
integrity sha512-9ewIQQCAnSmkU2DhuWafd1DdsgzAkKqIWnY+023xBLSiK9Az2TDUozWQW+SyRQgFMclbe6RQldUk/49TRO3Aqw==
dependencies:
bin-build "^3.0.0"
bin-wrapper "^4.0.0"
execa "^5.0.0"
imagemin-gifsicle@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/imagemin-gifsicle/-/imagemin-gifsicle-7.0.0.tgz#1a7ab136a144c4678657ba3b6c412f80805d26b0"
integrity sha512-LaP38xhxAwS3W8PFh4y5iQ6feoTSF+dTAXFRUEYQWYst6Xd+9L/iPk34QGgK/VO/objmIlmq9TStGfVY2IcHIA==
dependencies:
execa "^1.0.0"
gifsicle "^5.0.0"
is-gif "^3.0.0"
</code></pre>
<p>これでようやく通りました。処理も無事にできることを確認。</p>
<p>……思ったよりも手間取りました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="gifsicle"><a href="#gifsicle">gifsicle</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/imagemin/imagemin-gifsicle/issues/33">Error: Command failed: C:\WINDOWS\system32\cmd.exe /s /c "autoreconf -ivf" · Issue #33 · imagemin/imagemin-gifsicle · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/imagemin/imagemin-gifsicle/issues/37">Install failed gifsicle@4.0.1 postinstall · Issue #37 · imagemin/imagemin-gifsicle · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/imagemin/gifsicle-bin/issues/102">Command failed: C:\WINDOWS\system32\cmd.exe /s /c "autoreconf -ivf" "autoreconf" · Issue #102 · imagemin/gifsicle-bin · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/facebook/docusaurus/issues/1804">'autoreconf' is not recognized as an internal or external command · Issue #1804 · facebook/docusaurus · GitHub</a>
<ul>
<li><code>taobao.org</code> って中国ですよね……試す気にはなれないですね……。</li>
</ul></li>
<li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/65147246/cannot-find-module-gifsicle-for-loading-image-webpack">Cannot find module 'gifsicle' for loading image -webpack - Stack Overflow</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/gifsicle">gifsicle - npm</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/imagemin/gifsicle-bin/issues/134">5.3.0 is error · Issue #134 · imagemin/gifsicle-bin · GitHub</a></li>
</ul>
<h3 id="package.json"><a href="#package.json">package.json</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sotarok/items/4ebd4cfedab186355867">package.json のチルダ(~) とキャレット(^) - Qiita</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/17604
2021-08-17T23:17:25+09:00
2021-08-17T23:17:25+09:00
https://crieit.net/posts/gulp-imagemin-become-pure-esm-package-20210817
gulp-imagemin で Error [ERR_REQUIRE_ESM]: Must use import to load ES Module と怒られる
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>ある日、 package gardening をして Gulp を走らせたら</p>
<blockquote>
<p>Error [ERR_REQUIRE_ESM]: Must use import to load ES Module</p>
</blockquote>
<p>と怒られてしまいました。</p>
<pre><code class="bash">$ gulp
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: PATH\TO\PROJECT\DIRECTORY\node_modules\gulp-imagemin\index.js
require() of ES modules is not supported.
require() of PATH\TO\PROJECT\DIRECTORY\node_modules\gulp-imagemin\index.js from PATH\TO\PROJECT\DIRECTORY\gulp\tasks\imagemin.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from PATH\TO\PROJECT\DIRECTORY\node_modules\gulp-imagemin\package.json.
</code></pre>
<p>今回は <code>gulp-imagemin</code> のスクリプトの中に起因するようなので、そちらに手を入れるのは避けたいところです。</p>
<p>さて、どうしたものか。</p>
<h2 id="結論"><a href="#%E7%B5%90%E8%AB%96">結論</a></h2>
<p>結論から先にいうと、 <code>gulp-imagemin</code> をダウングレードすることで回避するしかなさそうです。</p>
<p>今回は <code>8.0.0</code> から既存の <code>7.1.0</code> にダウングレードします。</p>
<h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2>
<h3 id=""type": "module""><a href="#%26quot%3Btype%26quot%3B%3A+%26quot%3Bmodule%26quot%3B">"type": "module"</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="http://var.blog.jp/archives/80335431.html">Node.js の ESModules</a></li>
</ul>
<p>検索すると、 <code>package.json</code> にパラメータを追記する方法が出てきます。</p>
<pre><code class="json">{
"engines": {
"node": ">=16.0.0",
"npm": ">=6.14.9"
},
"engineStrict": true,
"prettier": {},
"type": "module" // 追記
}
</code></pre>
<p><code>"type": "module"</code> を追記する方法です。</p>
<p>しかし、この方法を採用する場合、既存の Gulpタスク 全てを CommonJS (<code>require</code>) から ES Modules (<code>import</code>) に書き換える必要があります。</p>
<p>それはそれで既存スクリプトの書き換えが大変なことになってしまうので、この方法は今回は不採用。</p>
<h3 id="公式リポジトリを覗く"><a href="#%E5%85%AC%E5%BC%8F%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E8%A6%97%E3%81%8F">公式リポジトリを覗く</a></h3>
<p>そこで公式リポジトリを覗いてみます。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/356">I have a question · Issue #356 · sindresorhus/gulp-imagemin · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/355">8.0.0 - ESM only? Serious? · Issue #355 · sindresorhus/gulp-imagemin · GitHub</a></li>
</ul>
<p>早速同じ内容の Issues が散見されます。そして回答では「リリースノートを見よ」とのこと。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/releases">Releases · sindresorhus/gulp-imagemin · GitHub</a></li>
</ul>
<blockquote>
<p>This package is now pure ESM. Please <a target="_blank" rel="nofollow noopener" href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">read this</a>.</p>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/releases">Releases · sindresorhus/gulp-imagemin · GitHub</a></p>
</blockquote>
<p>最初からここを見ましょう、という話ではあるのですが。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">Pure ESM package · GitHub</a></li>
</ul>
<p>ご覧の通り、「<code>8.0.0</code> からは ES Modules のパッケージになっていますよー」というアナウンス。</p>
<p>したがって、先の結論の通り CommonJS を使用したければダウングレードせよ、ということになります。是非もなし。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="http://var.blog.jp/archives/80335431.html">Node.js の ESModules</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/tomon9086/scraps/3a1d9d3eed4864">ESMで書かれたライブラリをNextで使うと[ERR_REQUIRE_ESM]で落ちる件について</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/356">I have a question · Issue #356 · sindresorhus/gulp-imagemin · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/issues/355">8.0.0 - ESM only? Serious? · Issue #355 · sindresorhus/gulp-imagemin · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sindresorhus/gulp-imagemin/releases">Releases · sindresorhus/gulp-imagemin · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c">Pure ESM package · GitHub</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/17485
2021-07-05T20:32:23+09:00
2021-07-05T20:32:23+09:00
https://crieit.net/posts/node-sass-adjust-node16-20210705
gulp-sass を Node.js v16 環境に合わせて調整する
<p>Node.js の 16系 を試そうとしたら gulp-sass, node-sass 周りでエラーが発生したので対処をまとめておきます。</p>
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>まず前提として</p>
<ul>
<li>Dart Sass で <code>/</code> を使った徐算が非推奨となった (代わりに <code>math.div</code> を使えとのこと)
<ul>
<li>参考: <a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2021/06/07/dart-sass-warning-slash-as-division/">Dart Sass で DEPRECATION WARNING: Using / for division is deprecated and will be removed in Dart Sass 2.0.0. の警告が表示される - Ewig Leere(Lab2)</a></li>
</ul></li>
<li>上述現象を回避するため、 <code>sass</code> のバージョンを <code>1.32.12</code> で固定していた</li>
</ul>
<p>という事情があります。</p>
<h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2>
<p>この状態で Node.js のバージョンを 16系 に切り替えて <code>yarn</code> 。</p>
<pre><code class="bash">error /home/node/app/node_modules/node-sass: Command failed.
Exit code: 1
Command: node scripts/build.js
Arguments:
Directory: /home/node/app/node_modules/node-sass
Output:
Building: /usr/local/bin/node /home/node/app/node_modules/node-gyp/bin/node-gyp.js rebuild --verbose --libsass_ext= --libsass_cflags= --libsass_ldflags= --libsass_library=
# 略
In file included from /home/node/.node-gyp/16.3.0/include/node/v8.h:30,
from /home/node/.node-gyp/16.3.0/include/node/node.h:63,
from ../../nan/nan.h:56,
from ../src/binding.cpp:1:
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h: In function 'void v8::internal::PerformCastCheck(T*)':
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: error: 'remove_cv_t' is not a member of 'std'
!std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
^~~~~~~~~<del>
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: note: suggested alternative: 'remove_cv'
!std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
^</del>~~~~~~<del>
remove_cv
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: error: 'remove_cv_t' is not a member of 'std'
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:38: note: suggested alternative: 'remove_cv'
!std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
^</del>~~~~~~<del>
remove_cv
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:50: error: template argument 2 is invalid
!std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
^
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:63: error: '::Perform' has not been declared
!std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
^</del>~~<del>
/home/node/.node-gyp/16.3.0/include/node/v8-internal.h:452:63: note: suggested alternative: 'herror'
!std::is_same<Data, std::remove_cv_t<T>>::value>::Perform(data);
^</del>~~<del>
herror
../src/binding.cpp: In function 'Nan::NAN_METHOD_RETURN_TYPE render(Nan::NAN_METHOD_ARGS_TYPE)':
../src/binding.cpp:284:98: warning: cast between incompatible function types from 'void (*)(uv_work_t*)' {aka 'void (*)(uv_work_s*)'} to 'uv_after_work_cb' {aka 'void (*)(uv_work_s*, int)'} [-Wcast-function-type]
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback);
^</del>~~~~~~~<del>
../src/binding.cpp: In function 'Nan::NAN_METHOD_RETURN_TYPE render_file(Nan::NAN_METHOD_ARGS_TYPE)':
../src/binding.cpp:320:98: warning: cast between incompatible function types from 'void (*)(uv_work_t*)' {aka 'void (*)(uv_work_s*)'} to 'uv_after_work_cb' {aka 'void (*)(uv_work_s*, int)'} [-Wcast-function-type]
int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback);
^</del>~~~~~~~<del>
In file included from ../../nan/nan.h:56,
from ../src/binding.cpp:1:
../src/binding.cpp: At global scope:
/home/node/.node-gyp/16.3.0/include/node/node.h:806:43: warning: cast between incompatible function types from 'void (*)(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE)' {aka 'void (*)(v8::Local<v8::Object>)'} to 'node::addon_register_func' {aka 'void (*)(v8::Local<v8::Object>, v8::Local<v8::Value>, void*)'} [-Wcast-function-type]
(node::addon_register_func) (regfunc), \
^
/home/node/.node-gyp/16.3.0/include/node/node.h:840:3: note: in expansion of macro 'NODE_MODULE_X'
NODE_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage)
^</del>~~~~~~~~<del>
../src/binding.cpp:358:1: note: in expansion of macro 'NODE_MODULE'
NODE_MODULE(binding, RegisterModule);
^</del>~~~~~~~~
make: *** [binding.target.mk:133: Release/obj.target/binding/src/binding.o] Error 1
make: Leaving directory '/home/node/app/node_modules/node-sass/build'
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/home/node/app/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack at ChildProcess.emit (node:events:394:28)
gyp ERR! stack at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12)
gyp ERR! System Linux 5.10.25-linuxkit
gyp ERR! command "/usr/local/bin/node" "/home/node/app/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /home/node/app/node_modules/node-sass
gyp ERR! node -v v16.3.0
</code></pre>
<p>……滅茶苦茶怒られました (Docker内のLinux環境)。</p>
<p>ちなみに Windows環境だと <code>yarn</code> 時にはエラーにはなりませんが、 Gulpタスク を走らせた際に同様のエラーメッセージが表示されます。</p>
<p><a href="https://crieit.now.sh/upload_images/68677d73f1123631b69e1a1fa1061c7b60e2edbdef2fa.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/68677d73f1123631b69e1a1fa1061c7b60e2edbdef2fa.jpg?mw=700" alt="Windows環境でのエラーメッセージ" /></a></p>
<h2 id="対処1 (sass, gulp-sass のバージョンを上げる)"><a href="#%E5%AF%BE%E5%87%A61+%28sass%2C+gulp-sass+%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E4%B8%8A%E3%81%92%E3%82%8B%29">対処1 (sass, gulp-sass のバージョンを上げる)</a></h2>
<p>そこでまずは <code>sass</code> のバージョンを上げます。</p>
<p>ちなみに、 <code>1.35.0</code>以降 では <code>quietDeps: true</code> のフラグを追加することで、冒頭の警告を非表示にできるようになったとのことなので、そもそもの問題は回避できるはずです。</p>
<pre><code class="json"> "devDependencies": {
// 略
"gulp-sass": "^4.1.0",
"sass": "1.35.1", // 1.32.12 からアップデート
"fibers": "^5.0.0",
"gulp-autoprefixer": "^7.0.1",
// 略
}
</code></pre>
<p>これで <code>yarn</code> してみましたが、ダメ。</p>
<p>ふと <code>gulp-sass</code> を見てみると、いつの間にか <code>5.0.0</code> がリリースされているではありませんか。</p>
<pre><code class="json"> "devDependencies": {
// 略
"gulp-sass": "^5.0.0", // 4.1.0 からアップデート
"sass": "1.35.1", // 1.32.12 からアップデート
"fibers": "^5.0.0",
"gulp-autoprefixer": "^7.0.1",
// 略
}
</code></pre>
<p>そこで、 <code>gulp-sass</code> も上げます。これで <code>yarn</code> すると正常に完了。</p>
<p>安心して Gulpタスク を走らせると……</p>
<pre><code class="bash">../src/coroutine.cc:134: void* find_thread_id_key(void*): Assertion `thread_id_key != 0x7777' failed.
</code></pre>
<p>…別のエラーで怒られました。</p>
<h2 id="対処2 (fibers を削除)"><a href="#%E5%AF%BE%E5%87%A62+%28fibers+%E3%82%92%E5%89%8A%E9%99%A4%29">対処2 (fibers を削除)</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/67330252/thread-id-key-0x7777-function-find-thread-id-key-file-src-coroutine-cc">node.js - (thread_id_key != 0x7777), function find_thread_id_key, file ../src/coroutine.cc, line 134 - Stack Overflow</a></li>
</ul>
<p>検索すると上述のエラーは <code>fibers</code> が Node.js 16系 に対応していないために発生している模様。</p>
<p>そのため、 <code>fibers</code> を削除します。</p>
<pre><code class="json"> "devDependencies": {
// 略
"gulp-sass": "^5.0.0", // 4.1.0 からアップデート
"sass": "1.35.1", // 1.32.12 からアップデート
"gulp-autoprefixer": "^7.0.1",
// 略
}
</code></pre>
<p>併せてタスクも書き換えます。</p>
<pre><code class="javascript">const sass = require('gulp-sass');
sass.compiler = require('sass');
//const Fiber = require('fibers');
// 略
.pipe(sass({
// fiber: Fiber,
outputStyle: 'compressed',
quietDeps: true // 警告回避のために追加
}).on('error', sass.logError))
// 略
</code></pre>
<p><code>fibers</code> 関係を削除。</p>
<p>これで <code>yarn</code> し直してタスクを再実行。</p>
<pre><code class="bash">Error in plugin "gulp-sass"
Message:
gulp-sass 5 does not have a default Sass compiler; please set one yourself.
Both the `sass` and `node-sass` packages are permitted.
For example, in your gulpfile:
var sass = require('gulp-sass')(require('sass'));
</code></pre>
<p>……別のエラーが発生しました。どうやら <code>gulp-sass</code> の Scssトランスパイラ の指定方法が変わったようです。</p>
<h2 id="対処3 (gulp-sass のScssトランスパイラ指定方法を変更)"><a href="#%E5%AF%BE%E5%87%A63+%28gulp-sass+%E3%81%AEScss%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B9%E3%83%91%E3%82%A4%E3%83%A9%E6%8C%87%E5%AE%9A%E6%96%B9%E6%B3%95%E3%82%92%E5%A4%89%E6%9B%B4%29">対処3 (gulp-sass のScssトランスパイラ指定方法を変更)</a></h2>
<pre><code class="javascript">const sass = require('gulp-sass')(require('sass'));
//const Fiber = require('fibers');
// 略
.pipe(sass({
// fiber: Fiber,
outputStyle: 'compressed',
quietDeps: true // 警告回避のために追加
}).on('error', sass.logError))
// 略
</code></pre>
<p>これでようやくタスクが正常に走るようになりました。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li>Dart Sass の警告回避:
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://sass-lang.com/documentation/js-api#quietdeps">Sass: JavaScript API</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/twbs/bootstrap/issues/34051">Sass deprecation warning: Using `/` for division is deprecated · Issue #34051 · twbs/bootstrap · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sass/dart-sass/issues/1333">support quietDeps flag · Issue #1333 · sass/dart-sass · GitHub</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sass/dart-sass/issues/672">Add a --quiet-upstream flag · Issue #672 · sass/dart-sass · GitHub</a></li>
</ul></li>
<li>fibers: <a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/67330252/thread-id-key-0x7777-function-find-thread-id-key-file-src-coroutine-cc">node.js - (thread_id_key != 0x7777), function find_thread_id_key, file ../src/coroutine.cc, line 134 - Stack Overflow</a></li>
<li>gulp-sass: <a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/gulp-sass">gulp-sass - npm</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/16660
2021-01-28T00:04:36+09:00
2021-01-28T00:04:36+09:00
https://crieit.net/posts/gulp-task-switch-dynamically-20210121
Gulpタスク を動的に切り替える
<p>Gulpタスク も動的に切り替えたい、と考えました。ついでに require も動的にできないか、と。</p>
<h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2>
<p>軽い気持ちで検証したら、これはすんなりできてしまったので結果のコードだけ載せておきます。</p>
<h3 id="gulp/tasks/test.js"><a href="#gulp%2Ftasks%2Ftest.js">gulp/tasks/test.js</a></h3>
<pre><code class="javascript">//testタスク
const test = (done) => {
console.log('hoge');
done();
};
module.exports = test;
</code></pre>
<p>コンソールに何かしらの文字列を出力する適当なタスクを作成します。</p>
<h3 id="gulpfile.js"><a href="#gulpfile.js">gulpfile.js</a></h3>
<pre><code class="javascript">const { series, parallel } = require('gulp');
const task1 = require('./gulp/tasks/task1');
const task2 = require('./gulp/tasks/task2');
// dynamic import test
const taskFlag = false;
let testTask;
if (taskFlag) {
testTask = require('./gulp/tasks/test');
// test
exports.test = testTask;
}
// build
const taskBuild = taskFlag ? parallel(task1, task2, testTask) : parallel(task1, task2);
// default
exports.default = series(taskBuild);
</code></pre>
<p><code>gulpfile.js</code> にこのように書いてみました。</p>
<ul>
<li><code>taskFlag</code> により以下を切り替えます
<ul>
<li><code>gulp/tasks/test.js</code> を <code>require</code> するかどうか</li>
<li><code>exports.test</code> として <code>exports</code> するか</li>
<li>通常のタスク(他の <code>task1</code>, <code>task2</code> のようなタスクと parallel で実行)の配列に加えるか否か</li>
</ul></li>
</ul>
<p>これで実行してみたところ、<code>taskFlag</code> が <code>true</code> の場合も <code>false</code> の場合もエラーにならずに、普通に処理できました。</p>
<p><code>require</code> とか <code>exports</code> は普通に <code>if</code>文 の中に入れられるのですね。</p>
arm-band
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
tag:crieit.net,2005:PublicArticle/16652
2021-01-26T23:55:22+09:00
2021-01-26T23:55:22+09:00
https://crieit.net/posts/gulp-sass-pseudo-dynamic-import-scss-20210120
gulp-sass から Scss を疑似的な動的読み込みを行う
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p>Scss で <code>@import</code>, <code>@use</code>, <code>@forward</code> のような他のファイルを読み込む機能は、 @ディレクティブ 内では使用できないません。そのため、例えば以下のようなことができません。</p>
<pre><code class="scss">$flag: true;
@if ($flag) {
@use "parts1.scss";
}
</code></pre>
<p>あるフラグ変数(ここでは <code>$flag</code> ですが、実際はファイル中ではなく Gulp タスク内で管理)が <code>true</code> のときのみ <code>parts1.scss</code> を読み込む。</p>
<pre><code class="scss">$flag: true;
@if ($flag) {
@use "parts1.scss";
}
@else {
@use "parts2.scss";
}
</code></pre>
<p>あるいは、フラグ変数の値によって読み込む Scss ファイルを切り替える。</p>
<p>この部分について、どうにかできないか(本当にやりたいのは最初の例ですが、代替案として2つ目の例でブランクの <code>parts2.scss</code> をダミーとして用意する方法を考える)、と。</p>
<h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/63803090/sass-dynamic-import-based-on-environment">css - Sass dynamic import based on environment - Stack Overflow</a></li>
</ul>
<p>色々検索した結果、 <code>gulp-sass</code> の <code>includePaths</code> オプションを使えばそれらしいことができそうだ、ということが分かりました。</p>
<p>今回はたまたま自分も <code>gulp-sass</code> を使用している前提で、しかも Scss 本体の変数ではなく Gulp タスク内で管理しているフラグによって、というのが希望だったのでこの形はぴったりでした (この方法でなければ、上記スレの上の方にあるように、フラグをいったん Scss ファイルに変数として出力し、それを読み込むことで伝播させようと考えていました)。</p>
<pre><code class="javascript">const { src, dest } = require('gulp');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const sass = require('gulp-sass');
sass.compiler = require('sass');
const Fiber = require('fibers');
const autoprefixer = require('gulp-autoprefixer');
// dynamic import test
const flag = true;
// scssコンパイルタスク
const scss = () => {
let opt = {
fiber: Fiber,
outputStyle: 'compressed'
};
// dynamic import test
if (flag) {
opt.includePaths = [
`src/scss/parts/enable`
]
}
else {
opt.includePaths = [
`src/scss/parts/disable`
]
}
return src('src/scss/**/*.scss')
.pipe(plumber({
errorHandler: notify.onError({
message: 'Error: <%= error.message %>',
title: 'scss'
})
}))
.pipe(sass(opt).on('error', sass.logError))
.pipe(autoprefixer({
cascade: false
}))
.pipe(dest('dist/css'));
};
module.exports = scss;
</code></pre>
<p>Scss のコンパイルタスクはこのような感じで。</p>
<pre><code>src/
└ scss/
├ .scssファイル やディレクトリ
├ import/
│ └ _index.scss
│
└ parts/
├ disable/
│ └ parts_hoge.scss
└ enable/
└ parts_hoge.scss
</code></pre>
<p>このようなディレクトリ構造で、 <code>src/scss/import/_index.scss</code> から <code>src/scss/parts</code> の中の<code>enable</code> または <code>disable</code> のどちらかの <code>parts_hoge.scss</code> を読み込む、とした場合。</p>
<pre><code class="scss">// dynamic import test
@use "parts_hoge";
</code></pre>
<p><code>src/scss/import/_index.scss</code> の記述はこれだけですが、 Scss のコンパイルタスクに記述されている変数 <code>flag</code> によって読み込む元のディレクトリの候補が <code>src/scss/parts/enable</code> または <code>src/scss/parts/disable</code> のどちらかに切り替わる、ということです。</p>
<p>あとは <code>src/scss/parts/disable/parts_hoge.scss</code> は中身のないブランクのファイルにしておけば、 <code>flag</code> が <code>true</code> の場合のみ css が当たる、ということになります。</p>
<p>出力される css の切り分けはできたので、とりあえずはこの方法でしょうかね。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/63803090/sass-dynamic-import-based-on-environment">css - Sass dynamic import based on environment - Stack Overflow</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/sass">sass - npm</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/sass/node-sass#includepaths">sass/node-sass: Node.js bindings to libsass</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/16236
2020-11-24T23:34:09+09:00
2020-11-24T23:34:09+09:00
https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-resolutions-20201124
続・ Gulp 4 + Webpack 5 を試す (resolutions 使用)
<p>以前、<a href="https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-20201020">Gulp 4 + Webpack 5 を試す ( 未完 / terser-webpack-plugin で TypeError: Cannot read property ‘javascript’ of undefined エラーになる)</a>の記事で Gulp 4 + Webpack 5 の実験をしましたが、その続きです。</p>
<h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2>
<p><a href="https://crieit.net/posts/backstopjs-test-20201122">BackstopJS を試す (Error: Failed to launch the browser process! エラー発生→ puppeteer のバージョンを指定して解決)</a>で <code>package.json</code> に <code>resolutions</code> を記述して Yarn で内部依存パッケージのバージョンを強制的に変更する手法を取りましたが、同様の手法が使えるのではないか、と思った次第です。</p>
<h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2>
<h3 id="package.json"><a href="#package.json">package.json</a></h3>
<p>```json:package.json<br />
// 略<br />
"devDependencies": {<br />
// 略<br />
"webpack": "^5.6.0",<br />
"webpack-stream": "^6.1.1",<br />
"terser-webpack-plugin": "^5.0.3",<br />
// 略<br />
},<br />
"resolutions": {<br />
"webpack": "^5.6.0"<br />
},<br />
// 略</p>
<pre><code><br />上述のように `resolutions` で Webpack のバージョンを指定。
### gulp/tasks/js.js
`gulp/tasks/js.js` は前回のまま。
### webpack.config.js
`webpack.config.js` は source map のための設定を追加した以外はそのままです。
```javascript:webpack.config.js
const _ = require('./gulp/plugin');
const dir = require('./gulp/dir');
const mode = () => {
return process.env.DEV_MODE === 'dev' ? 'development' : 'production';
};
const modeFlag = () => {
return process.env.DEV_MODE === 'dev' ? false : true;
};
const entry = () => {
const entries = _.glob
.sync(
'**/*.js',
{
ignore: [
'_plugins/**'
],
cwd: dir.src.js
}
)
.map(function (key) {
return [key, _.path.resolve(dir.src.js, key)];
});
return Object.fromEntries(entries)
};
const configs = {
mode: mode(),
entry: entry(),
output: {
filename: '[name]'
},
optimization: {
minimizer: [
new _.webpackTerser({
extractComments: 'some',
terserOptions: {
compress: {
drop_console: modeFlag(),
},
},
}),
],
}
};
if (process.env.DEV_MODE === 'dev') { // 追加
configs.devtool = 'inline-source-map';
}
module.exports = configs;
</code></pre>
<p>これで <code>yarn restart</code> などすると</p>
<pre><code class="bash">$ gulp
# 略
[hh:ii:ss] asset app.js 226 KiB [emitted] [minimized] (name: app.js) 1 related asset
webpack 5.6.0 compiled successfully
[hh:ii:ss] Finished 'jsBuild' after 20 s
</code></pre>
<p>動きました!</p>
<hr />
<p>後々は <code>resolutions</code> なしでも動くようにしたいですが、ひとまず動く形になったのでメモしておきます。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h3 id="Yarn の reasolutions"><a href="#Yarn+%E3%81%AE+reasolutions">Yarn の reasolutions</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nju33.com/yarn/resolutions%20%E3%81%A7%E4%BE%9D%E5%AD%98%E3%81%AE%E4%BE%9D%E5%AD%98%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B">yarn: resolutions で依存の依存のバージョンを指定する - nju33</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/16235
2020-11-24T23:28:18+09:00
2022-02-25T22:47:24+09:00
https://crieit.net/posts/gulp4-webpack5-terser-webpack-plugin-error-20201020
Gulp 4 + Webpack 5 を試す ( 未完 / terser-webpack-plugin で TypeError: Cannot read property 'javascript' of undefined エラーになる)
<p>以前、<a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/10/06/browserify-babelify-gulp-transpile-ie/">browserify + babelify + Gulp で IE11対応を試す</a>の記事で browserify + babelify を試しましたが、今回は Webpack に挑んでみます。</p>
<p>ただし、プレーンな Gulp 環境ではなく、 <a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/kiribi_ususama/tree/8a06ef719725236c26337a71bb2b1f9b64ef0900">Ususama</a> で。</p>
<h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2>
<h3 id="package.json"><a href="#package.json">package.json</a></h3>
<pre><code class="json">// 略
"devDependencies": {
// 略
"glob": "7.1.6",
"webpack": "^5.1.3",
"webpack-stream": "^6.1.0",
"terser-webpack-plugin": "^5.0.0",
// 略
}
// 略
</code></pre>
<p><code>gulp-uglify-es</code> と <code>gulp-concat</code> を除き、代わりに <code>webpack</code> 本体と Gulp で Webpack を使用するために必要な <code>webpack-stream</code> 、そして minifier の terser の Webpack 用プラグインである <code>terser-webpack-plugin</code> を追加。</p>
<h3 id="gulp/tasks/js.js"><a href="#gulp%2Ftasks%2Fjs.js">gulp/tasks/js.js</a></h3>
<pre><code class="javascript">const gulp = require('gulp');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const webpack = require('webpack');
const webpackStream = require('webpack-stream');
const rename = require('gulp-rename');
const dir = {
dist: {
js: './dist/js'
}
};
const webpackConfig = require('../../weppack.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(_.gulp.dest(dir.dist.js));
};
module.exports = jsBuild;
</code></pre>
<h3 id="webpack.config.js"><a href="#webpack.config.js">webpack.config.js</a></h3>
<pre><code class="javascript">const webpack = require('webpack');
const webpackTerser = require('terser-webpack-plugin');
const path = require('path');
const glob = require('glob');
const dotenv = require('dotenv').config();
const dir = {
src: {
js: './src/js'
}
};
const mode = () => {
return process.env.DEV_MODE === 'dev' ? 'development' : 'production';
};
const modeFlag = () => {
return process.env.DEV_MODE === 'dev' ? false : true;
};
const entry = () => {
const entries = _.glob
.sync(
'**/*.js',
{
ignore: [
'_plugins/**'
],
cwd: dir.src.js
}
)
.map(function (key) {
return [key, _.path.resolve(dir.src.js, key)];
});
return Object.fromEntries(entries)
};
module.exports = {
mode: mode(),
entry: entry(),
output: {
filename: '[name]'
},
optimization: {
minimizer: [
new _.webpackTerser({
extractComments: 'some',
terserOptions: {
compress: {
drop_console: modeFlag(),
},
},
}),
],
}
};
</code></pre>
<p>いくつかの記事を参考にしながらタスクを組みます。</p>
<p>自分でカスタマイズした部分は以下。</p>
<ul>
<li><code>.env</code> で <code>dev</code>, <code>demo</code>, <code>prod</code> のモードを切り替えているので、その部分を <code>process.env.DEV_MODE</code> で振り分け
<ul>
<li>Webpack の設定の <code>mode</code> と <code>terser-webpack-plugin</code> の <code>drop_console</code> のフラグが関係しています</li>
</ul></li>
<li>複数の <code>.js</code> ファイルをエントリポイントにしたかったのでその部分は<a target="_blank" rel="nofollow noopener" href="https://qiita.com/masato_makino/items/7130bbe408ca929e7f0d">webpackのentryファイルを複数指定、globパッケージの使い方 - Qiita</a>を参考に</li>
<li>最終的なファイル名は <code>XXX.min.js</code> の形にしたかったので <code>gulp-rename</code> を通しました</li>
</ul>
<h2 id="jQuery の扱い"><a href="#jQuery+%E3%81%AE%E6%89%B1%E3%81%84">jQuery の扱い</a></h2>
<p>タスク自体は上記のやり方で走ることが確認できました( <code>DEV_MODE=dev</code> )。</p>
<p>次は現時点ではまだ jQuery を使用しているので、 jQuery をどう読み込ませるかが課題ですが、以下のようにして動作することを確認しました。</p>
<h3 id="app.js"><a href="#app.js">app.js</a></h3>
<pre><code class="javascript">import $ from 'jquery';
import 'jquery.easing/jquery.easing';
$(() => {
/* 処理 */
});
</code></pre>
<h3 id="sitesearch.js"><a href="#sitesearch.js">sitesearch.js</a></h3>
<pre><code class="javascript">import $ from 'jquery';
import List from 'list.js';
//サイト内検索
export default () => {
const options = {
valueNames: ['searchTitle', 'searchText'],
};
const searchList = new List('listSearch', options);
//hits
searchList.on('searchComplete', function (a) {
$('#hits').text(a.matchingItems.length);
});
};
</code></pre>
<p>サイト内検索で <a target="_blank" rel="nofollow noopener" href="https://listjs.com">List.js</a> を使用しているのですが、これについては<a target="_blank" rel="nofollow noopener" href="https://github.com/javve/list.js/issues/559">How am I suppose to import list.js with es6 and webpack ? · Issue #559 · javve/list.js</a>の Issues の方法で解決しました。</p>
<p>ここまでは比較的順調でした。</p>
<p>しかし、間も無く壁に突き当たることになります……。</p>
<h2 id="TypeError: Cannot read property 'javascript' of undefined エラー"><a href="#TypeError%3A+Cannot+read+property+%27javascript%27+of+undefined+%E3%82%A8%E3%83%A9%E3%83%BC">TypeError: Cannot read property 'javascript' of undefined エラー</a></h2>
<p><code>DEV_MODE=dev</code> で動作することは確認できたので、 <code>DEV_MODE=prod</code> に切り替えました。</p>
<p>すると、以下のエラーが出てしまいました。</p>
<pre><code>TypeError: Cannot read property 'javascript' of undefined
at PATH\TO\PROJECT\node_modules\terser-webpack-plugin\dist\index.js:366:38
</code></pre>
<p><code>DEV_MODE=dev</code> に戻すと先ほどと同じように問題なく動作。上記でこのフラグが関係するのは <code>mode</code> と <code>terser-webpack-plugin</code> の <code>drop_console</code> の2箇所なので、そのどちらかだろうとアタリを付けます。</p>
<p>試しに <code>drop_console</code> を常に <code>false</code> としましたが、 <code>DEV_MODE=prod</code> でエラーは再現しました。</p>
<p>となると、 <code>mode</code> の方ということになります。</p>
<p>ここでエラー文で検索すると、以下の Issues を発見。 <code>terser-webpack-plugin</code> 本家のリポジトリです。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li>
</ul>
<p>発生個所も含めてエラー文が同じです。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-709997726">TypeError: Cannot read property 'javascript' of undefined · Issue #335 · webpack-contrib/terser-webpack-plugin</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-710004332">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li>
</ul>
<p>別の方のコメントを見ると、原因は以下の模様。</p>
<ul>
<li>terser-webpack-plugin 5 は Webpack 4 とは互換性がない</li>
<li><code>webpack-stream</code> の内部で使用している Webpack がバージョン 4 系</li>
</ul>
<blockquote>
<p>yep, we are working on it, release with fix will be today/tomorrow</p>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335#issuecomment-710032380">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a> (2020/10/16日 22:04 JST)</p>
</blockquote>
<p>わりとタイムリーなものを踏んでしまったようなので、 fixed されるのを待つ感じですかね……。</p>
<h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2>
<h2 id="Gulp + Webpack"><a href="#Gulp+%2B+Webpack">Gulp + Webpack</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tonkotsuboy_com/items/2d4f3862e6d05dc0bea1">Gulpで始めるwebpack 4入門 - Qiita</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/am10/items/2516fa04def815195ffe">gulp + webpack + babelをつかってみた - Qiita</a></li>
</ul>
<h3 id="webpack-stream"><a href="#webpack-stream">webpack-stream</a></h3>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/webpack-stream">webpack-stream - npm</a></li>
</ul>
<h2 id="Webpack, 複数エントリポイントとoutput"><a href="#Webpack%2C+%E8%A4%87%E6%95%B0%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%81%A8output">Webpack, 複数エントリポイントとoutput</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/masato_makino/items/7130bbe408ca929e7f0d">webpackのentryファイルを複数指定、globパッケージの使い方 - Qiita</a></li>
</ul>
<h2 id="List.js import"><a href="#List.js+import">List.js import</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/javve/list.js/issues/559">How am I suppose to import list.js with es6 and webpack ? · Issue #559 · javve/list.js</a></li>
</ul>
<h2 id="terser-webpack-plugin"><a href="#terser-webpack-plugin">terser-webpack-plugin</a></h2>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/webpack-contrib/terser-webpack-plugin/issues/335">TypeError: Cannot read property 'javascript' of undefined ・ Issue #335 ・ webpack-contrib/terser-webpack-plugin</a></li>
</ul>
arm-band
tag:crieit.net,2005:PublicArticle/14599
2018-11-12T00:32:00+09:00
2018-11-12T00:32:00+09:00
https://crieit.net/posts/Gulp-4-SCSS-Stylelint-Webpack-ESLint-Edge-js-BrowserSync
Gulp 4 で SCSS + Stylelint + Webpack + ESLint + Edge.js + BrowserSync なフロントエンド環境構築
<p>(<a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/gulp-4-frontend-development-starter/">ブログ</a>からのクロス投稿です。)</p>
<p><img src="https://cdn.hypertextcandy.com/posts/gulp-4-frontend-development-starter/EyeCatch.jpg" alt="Eye catch" /></p>
<p>この記事では、Gulp 4でフロントエンドの開発環境を構築する方法を紹介します。Gulp 4をタスクランナーとして、CSSのトランスパイルに SCSS + Stylelint、JavaScript のトランスパイルに Webpack + ESLint、、テンプレートエンジンとして Edge.js、さらにローカルサーバーとして BrowserSync を組み合わせます。</p>
<p>私はどちらかというとサーバサイドな人でフロントエンド専門ではないのですが、モックアップ作ったりで HTML コーディングしたくなって、いまさらながら Gulp で自分なりの環境構築をしたので結果を共有します。</p>
<p>フロントエンドと言っても全面的に React や Vue でゴリゴリアプリケーションを書くというより HTML コーディングの範疇内でのユースケースを想定しています(フレームワークでゴリゴリしたいなら <a target="_blank" rel="nofollow noopener" href="https://github.com/facebook/create-react-app">create-react-app</a> とか <a target="_blank" rel="nofollow noopener" href="https://cli.vuejs.org/">vue-cli</a> とか使うといいです)。</p>
<h2 id="概要"><a href="#%E6%A6%82%E8%A6%81">概要</a></h2>
<p>サンプルコードも含めた設定の全体については<a target="_blank" rel="nofollow noopener" href="https://github.com/MasahiroHarada/gulp-4-starter">こちらのリポジトリ</a>を参照してください。</p>
<h3 id="依存パッケージ"><a href="#%E4%BE%9D%E5%AD%98%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8">依存パッケージ</a></h3>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/MasahiroHarada/gulp-4-starter/blob/master/package.json">https://github.com/MasahiroHarada/gulp-4-starter/blob/master/package.json</a></p>
<p>ちなみに開発環境は Node v10.7.0、npm v6.4.1 です。</p>
<h3 id="ビルドコマンド"><a href="#%E3%83%93%E3%83%AB%E3%83%89%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89">ビルドコマンド</a></h3>
<p>最終的なビルドコマンドは以下の通りです。</p>
<pre><code class="console">// 開発ビルド:監視モードで実行されます。
$ npm run dev
// 本番ビルド:JavaScript が圧縮されます。
$ npm run prod
</code></pre>
<h2 id="ビルドタスク"><a href="#%E3%83%93%E3%83%AB%E3%83%89%E3%82%BF%E3%82%B9%E3%82%AF">ビルドタスク</a></h2>
<h3 id="gulpfile.babel.js"><a href="#gulpfile.babel.js">gulpfile.babel.js</a></h3>
<p>ES Modules(import/export)を使用したかったのでファイル名に <code>.babel</code> をつけて Babel のトランスパイルがかかるようにしました。</p>
<p>gulpfile.babel.js</p>
<pre><code class="js">import { series, parallel, watch } from 'gulp';
import { reload, serve } from './tasks/server';
import { styles } from './tasks/styles';
import { scripts } from './tasks/scripts';
import { templates } from './tasks/templates';
import { images } from './tasks/images';
import { clean } from './tasks/clean';
import {
sass as sassConfig,
scripts as jsConfig,
images as imagesConfig,
templates as templatesConfig
} from './tasks/config';
/**
* ファイルの変更を監視
*/
function watchFiles() {
// Sass
watch(sassConfig.src, series(styles, reload));
// Templates
watch(
[templatesConfig.edges, templatesConfig.data, templatesConfig.helper],
series(templates, reload)
);
// JavaScript
watch(jsConfig.src, series(scripts, reload));
// Images
watch(imagesConfig.src, series(images, reload));
}
/**
* 開発用ビルド
*/
export const dev = series(
clean,
parallel(styles, templates, scripts, images),
serve,
watchFiles
);
/**
* 本番用ビルド
*/
export const build = series(
clean,
parallel(styles, templates, scripts, images)
);
</code></pre>
<p>前述の通り gulpfile を ES2015 以降の構文で書くために Babel の設定ファイルも追加します。</p>
<p>.babelrc</p>
<pre><code class="json">{
"presets": [
[
"@babel/preset-env",
{
"targets": { "node": "current" }
}
]
]
}
</code></pre>
<p>タスクを色々書くと長くなってしまうので、個別のタスクはそれぞれ別ファイルに分け、<code>tasks</code> ディレクトリに格納しています。</p>
<p>それぞれ見ていきましょう。</p>
<h3 id="設定ファイル"><a href="#%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB">設定ファイル</a></h3>
<p>まずは設定ファイルです。パスの設定が入っています。</p>
<p>tasks/config.js</p>
<pre><code class="js">const ASSET_ROOT = 'src';
const DEST_ROOT = 'public';
export const sass = {
src: `${ASSET_ROOT}/sass/**/*.scss`,
dest: `${DEST_ROOT}/styles`
};
export const scripts = {
srcRoot: `${ASSET_ROOT}/js`,
src: `${ASSET_ROOT}/js/**/*.js`,
dest: `${DEST_ROOT}/js`,
babelrc: {
presets: [['@babel/env', { targets: '> 0.25%, not dead' }]]
}
};
export const templates = {
root: `${ASSET_ROOT}/templates`,
edges: `${ASSET_ROOT}/templates/**/*.edge`,
pages: `${ASSET_ROOT}/templates/pages/**/*.edge`,
data: `${ASSET_ROOT}/templates/data.json`,
helper: `${ASSET_ROOT}/templates/helper.js`,
dest: DEST_ROOT
};
export const images = {
src: `${ASSET_ROOT}/images/**/*.*`,
dest: `${DEST_ROOT}/images`
};
export const isProd = process.env.NODE_ENV === 'production';
</code></pre>
<h3 id="BrowserSync"><a href="#BrowserSync">BrowserSync</a></h3>
<p>ファイルの変更を検知して自動でブラウザをリロードさせるため、<a target="_blank" rel="nofollow noopener" href="https://browsersync.io/">BrowserSync</a> のタスクを作成します。設定方法は<a target="_blank" rel="nofollow noopener" href="https://github.com/gulpjs/gulp/blob/4.0/docs/recipes/minimal-browsersync-setup-with-gulp4.md">こちら(Minimal BrowserSync setup with Gulp 4)</a>を参考にしました。</p>
<p>tasks/server.js</p>
<pre><code class="js">import browserSync from 'browser-sync';
const server = browserSync.create();
/**
* 開発用サーバ再起動
*/
export function reload(cb) {
server.reload();
cb();
}
/**
* 開発用サーバ起動
*/
export function serve(cb) {
server.init({
server: {
baseDir: './public'
}
});
cb();
}
</code></pre>
<h3 id="Clean"><a href="#Clean">Clean</a></h3>
<p>ファイルの出力前に出力先の既存ファイルを削除しておくためのタスクです。</p>
<p>tasks/clean.js</p>
<pre><code class="js">import del from 'del';
/**
* 出力先のディレクトリを空にする
*/
export function clean() {
return del(['public']);
}
</code></pre>
<h3 id="スタイル"><a href="#%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB">スタイル</a></h3>
<p>スタイルに関しては2つのタスクを定義しています。</p>
<p>まず1つめはSCSS を CSS に変換するためのタスクです。ソースマップ生成(<a target="_blank" rel="nofollow noopener" href="https://github.com/gulp-sourcemaps/gulp-sourcemaps">gulp-sourcemaps</a>)とファイル圧縮(<a target="_blank" rel="nofollow noopener" href="https://github.com/scniro/gulp-clean-css">gulp-clean-css</a>)も実行しています。</p>
<p>2つめは CSS(今回は SCSS)の構文チェックツール <a target="_blank" rel="nofollow noopener" href="https://stylelint.io/">Stylelint</a> を実行するためのタスクです。<a target="_blank" rel="nofollow noopener" href="https://github.com/olegskl/gulp-stylelint">gulp-stylelint</a> を使用します。またファイルの監視中に毎回すべてのファイルを lint せず変更があったファイルのみチェックするために <a target="_blank" rel="nofollow noopener" href="https://github.com/alexgorbatchev/gulp-changed-in-place">gulp-changed-in-place</a> というプラグインも使用します。</p>
<p>tasks/styles.js</p>
<pre><code class="js">import gulp from 'gulp';
import gulpSass from 'gulp-sass';
import sourcemaps from 'gulp-sourcemaps';
import cleancss from 'gulp-clean-css';
import plumber from 'gulp-plumber';
import gulpStylelint from 'gulp-stylelint';
import changed from 'gulp-changed-in-place';
import { sass as config, isProd } from './config';
/**
* SCSS -> CSS
*/
export function sass() {
return gulp
.src(config.src)
.pipe(plumber())
.pipe(sourcemaps.init())
.pipe(gulpSass().on('error', gulpSass.logError))
.pipe(cleancss())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(config.dest));
}
/**
* Stylelint
*/
export function stylelint() {
return gulp
.src(config.src)
// firstPass は初回実行時に全ファイルを対象とするかどうかのオプション
.pipe(changed({ firstPass: true }))
.pipe(gulpStylelint({
failAfterError: isProd,
reporters: [{ formatter: 'verbose', console: true }],
syntax: 'scss'
}));
}
export const styles = gulp.series(stylelint, sass);
</code></pre>
<p>Stylelint の設定ファイルです。</p>
<p>```json:.stylelintrc<br />
{<br />
"extends": "stylelint-config-standard",<br />
"plugins": ["stylelint-order"],<br />
"rules": {<br />
"order/properties-alphabetical-order": true<br />
}<br />
}</p>
<pre><code><br />### 画像圧縮
[gulp-imagemin](https://github.com/sindresorhus/gulp-imagemin) を使って画像圧縮を行うタスクです。
オプションはお好みでどうぞ。
tasks/images.js
```js
import gulp from 'gulp';
import imagemin from 'gulp-imagemin';
import { images as config } from './config';
export function images() {
return gulp
.src(config.src)
.pipe(imagemin())
.pipe(gulp.dest(config.dest));
}
</code></pre>
<h3 id="Edge.js → HTML"><a href="#Edge.js+%E2%86%92+HTML">Edge.js → HTML</a></h3>
<p>テンプレートエンジン <a target="_blank" rel="nofollow noopener" href="https://edge.adonisjs.com/">Edge.js</a> のファイルを HTML に変換するためのタスクです。</p>
<p>Edge.js について詳しくは<a target="_blank" rel="nofollow noopener" href="https://www.hypertextcandy.com/build-frontend-with-edgejs">こちらの記事</a>もご覧ください。</p>
<p>tasks/templates.js</p>
<pre><code class="js">import gulp from 'gulp';
import edge from 'edge.js';
import tap from 'gulp-tap';
import rename from 'gulp-rename';
import fs from 'fs';
import path from 'path';
import { templates as config } from './config';
/**
* Edge.js -> HTML
*/
export function templates() {
// テンプレートを読み込む
edge.registerViews(path.join(__dirname, `../${config.root}`));
// データファイルを読み込む
const data = fs.existsSync(config.data)
? JSON.parse(fs.readFileSync(config.data, 'utf8'))
: {};
// ヘルパー関数を読み込む
fs.existsSync(config.helpers) && require(`../${config.helper}`);
return gulp
.src(config.pages)
.pipe(
tap(file => {
const contents = edge.renderString(String(file.contents), data);
file.contents = new Buffer(contents);
})
)
.pipe(rename({ extname: '.html' }))
.pipe(gulp.dest(config.dest));
}
</code></pre>
<h3 id="JavaScript"><a href="#JavaScript">JavaScript</a></h3>
<p>タスク <code>esTranspile</code> は <a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/webpack-stream">Webpack</a> により JavaScript のトランスパイル〜バンドルを行います。</p>
<p>また <code>esLint</code> タスクではコードのチェックのために <a target="_blank" rel="nofollow noopener" href="https://eslint.org/">ESLint</a> を実行します。<a target="_blank" rel="nofollow noopener" href="https://github.com/adametry/gulp-eslint">gulp-eslint</a> プラグインを使用します。</p>
<p>tasks/scripts.js</p>
<pre><code class="js">import gulp from 'gulp';
import gulpIf from 'gulp-if';
import plumber from 'gulp-plumber';
import gulpEslint from 'gulp-eslint';
import webpack from 'webpack';
import gulpWebpack from 'webpack-stream';
import changed from 'gulp-changed-in-place';
import { scripts as config, isProd } from './config';
export function esTranspile() {
return gulp
.src(config.src)
.pipe(plumber())
// gulp.watch と一緒に使う場合は第二引数が必須らしい。
// https://www.npmjs.com/package/webpack-stream#usage-with-gulp-watch
.pipe(gulpWebpack(require('../webpack.config.js'), webpack))
.pipe(gulp.dest(config.dest));
}
export function esLint() {
return gulp
.src(config.src)
.pipe(changed({ firstPass: true }))
.pipe(gulpEslint())
.pipe(gulpEslint.format())
.pipe(gulpIf(isProd, gulpEslint.failAfterError()));
}
export const scripts = gulp.series(esLint, esTranspile);
</code></pre>
<p>Webpack の設定ファイルです。</p>
<p>webpack.config.js</p>
<pre><code class="js">import { scripts as config } from './tasks/config';
module.exports = {
mode: process.env.NODE_ENV ? 'production' : 'development',
entry: {
app: `./${config.srcRoot}/app.js`
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
},
output: {
filename: '[name].js',
},
devtool: 'source-map'
}
</code></pre>
<p>複数のファイルを出力したい場合は、以下のように <code>entry</code> に追記すれば OK です。</p>
<pre><code class="js">entry: {
app: `./${config.srcRoot}/app.js`,
foo: `./${config.srcRoot}/foo.js`
},
</code></pre>
<p>こちらはESLint の設定ファイルです。</p>
<p>.eslintrc</p>
<pre><code class="json">{
"extends": ["eslint:recommended"],
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"env": {
"browser": true,
"node": true
}
}
</code></pre>
<h3 id="フォーマッタ"><a href="#%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%82%BF">フォーマッタ</a></h3>
<p>フォーマッタはオマケです。お好みの設定&エディタと連携していただければ。</p>
<h4 id="EditorConfig"><a href="#EditorConfig">EditorConfig</a></h4>
<p>.editorconfig</p>
<pre><code class="ini">root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.{html,edge,scss,css,js,svg}]
indent_style = space
indent_size = 2
</code></pre>
<h4 id="Prettier"><a href="#Prettier">Prettier</a></h4>
<p>.prettierrc</p>
<pre><code class="json">{
"singleQuote": true
}
</code></pre>
<p>以上です。参考になれば幸いです。</p>
Masahiro Harada