Help us understand the problem. What is going on with this article?

node.jsを10->12に更新したときに起きたエラーと対処内容

表題通りの記事です。
これから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を更新しました。
(破壊的変更が多く、そこそこ面倒でした)


要点をかいつまむと、以下のようになります。

  • 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ちゃんと使えるようになりたいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした