表題通りの記事です。
これからnode.jsを10->12に更新する方の助けになれば幸いです。
確認環境
- Ubuntu: 18.04.3 LTS
- node.js: 10.16.0、12.14.1
- ビルドツール: gulp (後述しますが4系へのアップデートが必須でした)
やったこと(全体像)
- n をつかってローカル環境のnode.jsを10->12に更新 (参考: nodeとnpmのバージョン更新方法)
-
npm ci
して、出てきたエラーを潰す- firebaseの更新
- node-sassの更新
-
npm run build
して、出てきたエラーを潰す- gulpを3系->4系へ移行
エラー1 (firebaseの更新により解消)
最初はイージーケースから。
事象
npm ci
したところ、大量(体感3分)のログが出力され、最後に以下のエラーが吐かれました。
# これ以前にも大量のエラーログが出る
cc1plus: error: unrecognized command line option ‘-Wno-cast-function-type’ [-Werror]
cc1plus: all warnings being treated as errors
grpc_node.target.mk:188: recipe for target 'Release/obj.target/grpc_node/ext/channel.o' failed
make: *** [Release/obj.target/grpc_node/ext/channel.o] Error 1
# 最後の方に出るmakeのログを見ることで、どのパッケージのビルドに失敗したかが分かる
# (repository-nameでgrepすると早く見つかる)
make: ディレクトリ '/home/user1/repository-name/node_modules/grpc/build' から出ます
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:194:23)
gyp ERR! stack at ChildProcess.emit (events.js:223:5)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12)
gyp ERR! System Linux 4.15.0-74-generic
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "build" "--fallback-to-build" "--library=static_library" "--module=/home/user1/repository-name/node_modules/grpc/src/node/extension_binary/node-v72-linux-x64-glibc/grpc_node.node" "--module_name=grpc_node" "--module_path=/home/user1/repository-name/node_modules/grpc/src/node/extension_binary/node-v72-linux-x64-glibc" "--napi_version=5" "--node_abi_napi=napi" "--napi_build_version=0" "--node_napi_label=node-v72"
gyp ERR! cwd /home/user1/repository-name/node_modules/grpc
gyp ERR! node -v v12.14.1
gyp ERR! node-gyp -v v5.0.5
gyp ERR! not ok
node-pre-gyp ERR! build error
node-pre-gyp ERR! stack Error: Failed to execute '/usr/local/bin/node /usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js build --fallback-to-build --library=static_library --module=/home/user1/repository-name/node_modules/grpc/src/node/extension_binary/node-v72-linux-x64-glibc/grpc_node.node --module_name=grpc_node --module_path=/home/user1/repository-name/node_modules/grpc/src/node/extension_binary/node-v72-linux-x64-glibc --napi_version=5 --node_abi_napi=napi --napi_build_version=0 --node_napi_label=node-v72' (1)
node-pre-gyp ERR! stack at ChildProcess.<anonymous> (/home/user1/repository-name/node_modules/grpc/node_modules/node-pre-gyp/lib/util/compile.js:83:29)
node-pre-gyp ERR! stack at ChildProcess.emit (events.js:223:5)
node-pre-gyp ERR! stack at maybeClose (internal/child_process.js:1021:16)
node-pre-gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:283:5)
node-pre-gyp ERR! System Linux 4.15.0-74-generic
node-pre-gyp ERR! command "/usr/local/bin/node" "/home/user1/repository-name/node_modules/grpc/node_modules/.bin/node-pre-gyp" "install" "--fallback-to-build" "--library=static_library"
node-pre-gyp ERR! cwd /home/user1/repository-name/node_modules/grpc
node-pre-gyp ERR! node -v v12.14.1
node-pre-gyp ERR! node-pre-gyp -v v0.12.0
node-pre-gyp ERR! not ok
調べたこと
ログにあるとおり、grpcのビルドに失敗しています。
(最初は大量のログにとまどいました。どのパッケージがインストールできていないか調べるのが第一歩です)
更新対象リポジトリのpackage.jsonにはgrpcの記述がなく、
npm ls
で依存パッケージを調べたところ、firebaseがgrpcに依存していました。
$ npm ls
# 結果を抜粋
├─┬ firebase@5.5.7
│ ├─┬ @firebase/firestore@0.8.6
│ │ ├─┬ grpc@1.13.1 <- ここでエラー
解決策
執筆時点でfirebaseは7系が最新。単純に古いので、アップデートしたところ解決。
(メジャーバージョン更新による影響も今のところなし)
$ npm install -D firebase
エラー2 (node-sassの更新により解消)
エラー1をちょっとひねったパターン。
事象
npm ci
したところ、大量(体感3分)のログが出力され、最後に以下のエラーが吐かれました。
# これ以前にも大量のエラーログが出る
/home/user1/.node-gyp/12.14.1/include/node/v8.h:3039:5: note: candidate expects 2 arguments, 1 provided
binding.target.mk:129: recipe for target 'Release/obj.target/binding/src/create_string.o' failed
make: *** [Release/obj.target/binding/src/create_string.o] Error 1
# 最後の方に出るmakeのログを見ることで、どのパッケージのビルドに失敗したかが分かる
make: ディレクトリ '/home/user1/repository-name/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/user1/repository-name/node_modules/node-gyp/lib/build.js:262:23)
gyp ERR! stack at ChildProcess.emit (events.js:223:5)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:272:12)
gyp ERR! System Linux 4.15.0-74-generic
gyp ERR! command "/usr/local/bin/node" "/home/user1/repository-name/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
gyp ERR! cwd /home/user1/repository-name/node_modules/node-sass
gyp ERR! node -v v12.14.1
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
Build failed with error code: 1
調べたこと
ログにあるとおり、node-sassのビルドに失敗しています。
とりあえずググったところ、node-sassは4.12.0でnode.jsに12対応したそうです。
更新対象リポジトリのpackage.jsonにはnode-sassの記述がなく、
npm ls
で依存パッケージを調べたところ、gulp-sassがnode-sassの4.11に依存していました。
$ npm ls
# 結果を抜粋
├─┬ gulp-sass@4.0.2
│ ├─┬ node-sass@4.11.0 ←ここでエラー(4.12以降にする必要がある)
私は最初、この段階で「詰んだ」と勘違いしました。
gulp-sassの依存パッケージを変えるには、gulp-sass公式にissueなりPRなりを投げてpackage.jsonを書き換えなければならないと思ったからです。
解決策
明示的にnode-sassをインストールしたところ、nodeがよしなにやってくれました。
$ npm install -D node-sass@4.13.0 # 実行時点での最新バージョン
$ npm ls
# 結果を抜粋
├─┬ gulp-sass@4.0.2
│ ├── node-sass@4.13.0 deduped <- 明示的に4.13を入れたところ、nodeが重複パッケージをまとめてくれた
上記にdeduped(=重複排除)と表示されていますが、
これはnodeが「バージョン違いの同一パッケージを1つに統合した」ことを表しています。
(今回で言うとnode-sassの4.11, 4.13のうち、より新しい4.13に統合された)
この仕様は、今回のようなケースのほかに、
セキュリティ上の脆弱性を持つ孫パッケージ(こういう呼び方があるかは不明)のバージョンを更新したいときにも役立ちます。
(参考: https://cloudpack.media/41572)
$ npm ls
├─┬ lib-nobody-update@1.1.0 # 更新が止まっているライブラリ
│ ├── lib-something@2.2.0 # 2.3.0で脆弱性が修正されたライブラリ
$ npm install -D lib-something@2.3.0
$ npm ls
├─┬ lib-nobody-update@1.1.0
│ ├── lib-something@2.3.0 deduped
Llib-something@2.3.0
今回初めて知りましたが、なかなかに素敵。
エラー3 (gulpを3系->4系へ移行することで解消)
最後はgulpの話です。
事象
npm ci
実行後、npm run build
したところ以下のエラーが発生。
[18:21:08] Requiring external module @babel/register
fs.js:27
const { Math, Object } = primordials;
^
ReferenceError: primordials is not defined
at fs.js:27:26
at req_ (/home/user1/repository-name/node_modules/natives/index.js:143:24)
at Object.req [as require] (/home/user1/repository-name/node_modules/natives/index.js:55:10)
at Object.<anonymous> (/home/user1/repository-name/node_modules/vinyl-fs/node_modules/graceful-fs/fs.js:1:37)
at Module._compile (internal/modules/cjs/loader.js:955:30)
at Module._compile (/home/user1/repository-name/node_modules/pirates/lib/index.js:99:24)
at Module._extensions..js (internal/modules/cjs/loader.js:991:10)
at Object.newLoader [as .js] (/home/user1/repository-name/node_modules/pirates/lib/index.js:104:7)
at Module.load (internal/modules/cjs/loader.js:811:32)
at Function.Module._load (internal/modules/cjs/loader.js:723:14)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! my-package-name@0.0.0 build-gulp: `gulp build`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the my-package-name@0.0.0 build-gulp 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/user1/.npm/_logs/2020-01-15T09_21_13_322Z-debug.log
ERROR: "build-gulp" exited with 1.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! my-package-name@0.0.0 build: `run-p build-gulp build-client-ts`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the my-package-name@0.0.0 build 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/user1/.npm/_logs/2020-01-15T09_21_13_421Z-debug.log
調べたこと
一見して何のエラーか分かりませんでしたが、とりあえずエラーメッセージでググったところ
gulp3系がnode12に未対応なのが原因だと判明。gulp4系は対応済み。
いっそビルド関連をwebpackに置き換えることも考えましたが、
今回は対応速度を重視してgulpを更新する方針にしました。
解決策
以下を参考にgulpを更新しました。
(破壊的変更が多く、そこそこ面倒でした)
- 公式ドキュメント: CommonJS(require)での記載例(英語)
- 公式GitHub: ESModule(import)での記載例(英語)
- 概要を把握しやすかった記事(日本語)
- gulp.task()が非推奨であることに言及した記事(日本語)
要点をかいつまむと、以下のようになります。
- gulp.task()は非推奨になった(この点に触れない記事が多い)
- 後方互換性のため、一応まだ使える
- 代わりに関数をexportするスタイルになった
- タスクの直列実行、並列実行は新関数gulp.series(), gulp.parallel()で制御
// 3系: gulp.task(”タスク名”, タスク用の関数)
gulp.task("buildFrontend", () => { /* フロントエンドのビルド処理 */ });
gulp.task("buildBackend", () => { /* バックエンドのビルド処理 */ });
gulp.task("build", ["buildFrontend", "buildBackend"]); // []は並列実行
// 4系(パターン1): export const タスク名 = タスク用の関数;
export const buildFrontend = () => { /* フロントエンドのビルド処理 */ };
export const buildBackend = () => { /* バックエンドのビルド処理 */ };
export const build = gulp.parallel(buildFrontend, buildBackend);
// 4系(パターン2): 大元のタスクを上に記載する場合はfunctionで書く
export async function build() { await gulp.parallel(buildFrontend, buildBackend) };
export function buildFrontend() { /* フロントエンドのビルド処理 */ };
export function buildBackend() { /* バックエンドのビルド処理 */ };
3系に比べて可読性があがりましたね。
なお、パターン1で大元のタスクを上に持ってくると、エラーになります。
大元のタスクを上に書きたい場合は、パターン2のように書くとうまくいきました。(@shisama さん、指摘ありがとうございました!)
// ダメな例
export const build2 = gulp.parallel(buildFrontend2, buildBackend2);
export const buildFrontend2 = () => { /* フロントエンドのビルド処理 */ };
export const buildBackend2 = () => { /* バックエンドのビルド処理 */ };
// 実行すると以下のエラーが出る
$ npx gulp build
> my-package-name@0.0.0 build /home/duser1/repository-name
> gulp build
/home/duser1/repository-name/gulpfile.js:10
const build = gulp.parallel(buildFrontend2, buildBackend2);
^
ReferenceError: Cannot access 'buildFrontend2' before initialization
at Object.<anonymous> (/home/duser1/repository-name/gulpfile.js:10:29)
at Module._compile (internal/modules/cjs/loader.js:955:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:991:10)
at Module.load (internal/modules/cjs/loader.js:811:32)
at Function.Module._load (internal/modules/cjs/loader.js:723:14)
at Module.require (internal/modules/cjs/loader.js:848:19)
at require (internal/modules/cjs/helpers.js:74:18)
at execute (/home/duser1/repository-name/node_modules/gulp/node_modules/gulp-cli/lib/versioned/^4.0.0/index.js:36:18)
at Liftoff.handleArguments (/home/duser1/repository-name/node_modules/gulp/node_modules/gulp-cli/index.js:201:24)
at Liftoff.execute (/home/duser1/repository-name/node_modules/liftoff/index.js:201:12)
at module.exports (/home/duser1/repository-name/node_modules/flagged-respawn/index.js:51:3)
at Liftoff.<anonymous> (/home/duser1/repository-name/node_modules/liftoff/index.js:191:5)
at /home/duser1/repository-name/node_modules/liftoff/index.js:149:9
at /home/duser1/repository-name/node_modules/v8flags/index.js:138:14
at /home/duser1/repository-name/node_modules/v8flags/index.js:41:14
at /home/duser1/repository-name/node_modules/v8flags/index.js:53:7
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! my-package-name@0.0.0 build: `gulp build`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the my-package-name@0.0.0 build 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/duser1/.npm/_logs/2020-01-15T10_04_26_061Z-debug.log
まとめ
- バージョン更新は思った以上に大変
- こまめな更新が、バージョン更新のコストを下げる
- npm lsでパッケージの依存関係をチェックできる
- nodeにはdedupeという素敵機能がある
- node12でgulp3系は使えない。4系に移行するか、webpackなど他ツールへ移行する
webpackちゃんと使えるようになりたいです。