Open12

Node.js 10がサポート終了したので、Node.js 12以降だとできるようになることをまとめる

azuazu

Node.js LTS

nodejs/Release: Node.js Release Working Group

2021-04-30でNode.js 10.x LTSのサポートが終了しました。
そのため、ライブラリなどではNode.js 12.x以上がサポート対象となります。

Version LTS Date V8 npm NODE_MODULE_VERSION
Node.js 12.22.1 Erbium 2021-04-06 7.8.279.23 6.14.12 72

Node.js 12.x 以降をサポート対象としたときに新たにできるようになることをまとめましょう。

自由に追加、補足コメント入れておいてください。

azuazu

ECMAScript Modules

Added in: 12.7.0

Node.js 12.7.0で--experimental-modulesフラグが外れたため、Node.js 12 LTSではECMAScript Modules(ESM)が利用できるようになります。

これによりライブラリがCommonJS形式(require/exports)ではなく、ESM形式(import/export)で公開できるようになります。そのため、多くのライブラリがESM形式のみでの公開へと切り替わっていきます。

ESMはブラウザ(IE以外)、Node.js、rollup/webpackなどのbundler、TypeScript(exportsフィールドは未対応)/Babel、Jestなど主要なツールがサポートしている状態です。IEの場合は、bundlerなどのツールを経由すれば良いため(またbundlerはESMだと最適化できるため)、ライブラリそのものはESM形式をnpmなどのregistryにpublishするのが適切な状態です。

Node.jsがESMにネイティブ対応する以前も、package.jsonmainフィールドでCommonJS形式とmoduleフィールドでESM形式の両方を公開していました。
しかし、Node.js 12でESMに対応したため、ESM形式のみで十分なケースが増えてきてたのと、ESMは静的に依存関係をパースできるため最適化しやすいなど理由からESMへの移行が進んでいます。

Node.js 12からはpackage.jsonexportsフィールドによって、宣言的にCommonJSとESMの両方へと対応ができるようになっています。
しかし、一部のツールではまだexportsフィールドに対応していないものもあります。

参考:

azuazu

AsyncLocalStorage

Added in: v13.10.0, v12.17.0

async_hooks モジュールのAsyncLocalStorageが利用できるようになります。

非同期処理のコンテキストに紐づくストレージです。
他の言語ならスレッドストレージなどが近い存在です。

AsyncLocalStorageを使うと、Node.jsサーバでリクエストのコンテキストに紐づくログやトレーシングを簡単に扱えるようになります。
リクエスト自体は並列に来るため、そのリクエストを区別して扱うためのIDを管理するインメモリなStorageとして扱えます。

async_hooks モジュール自体はNode 10以前にもあったので、cls-hookedのようなライブラリで似たようなことができました。
express-http-contextもこの async_hooks (cls-hooked経由)を使っています。

ahuahu

fs.rmdirSync(path[, options])

Added in: v12.10.0

再帰的に削除するオプション recursive が入ったため、node -e "fs.rmdirSync('dist',{recursive:true,force:true})" で glob は使えませんが rimraf 相当のことが可能になりました。
なお v14.14.0 で fs.rmSync(path[, options]) などが追加され、v16.0.0 で fs.rmdirSync() などの recursive オプションは非推奨になったため、fs.rmSync() がある環境では node -e "fs.rmSync('dist',{recursive:true,force:true})" としたほうが良さそうです。

azuazu

Readable.from(iterable, [options])

Added in: v12.3.0, v10.17.0

Node.js 10にもbackportされていますが、Iterator/Async IteratorからStreamを作成できるReadable.fromが追加されています。

IteratorやAsync Iteratorを Readable.from(iterator) に渡せるので、
Iteratorである文字列や配列、Generator関数/Async Genrator関数などからStreamを手軽に作成できます。

azuazu

assert.CallTracker

Added in: v14.2.0, v12.19.0

ある関数が呼び出されたかをチェックできる assert.CallTracker が追加されています。
次のようにテスト時に、テストしたい関数が実際に何度呼ばれることを期待するかをチェックできます。

it("should lazy load router has sub path", async () => {
    const app = express();
    const requestTracker = new assert.CallTracker();
    const requestHandler: RequestHandler = requestTracker.calls((_, res) => {
        res.json({
            ok: true
        });
    }, 1); // 1回呼ばれることを期待する
    // /api/status
    app.use(
        "/api",
        lazyLoad(async () => createMockRouter(requestHandler, "/status"))
    );
    return request(app)
        .get("/api/status")
        .set("Accept", "application/json")
        .expect("Content-Type", /json/)
        .expect(200)
        .then((response) => {
            assert.deepStrictEqual(response.body, { ok: true });
            requestTracker.verify(); // 期待された回数呼ばれているならpass
        });
});

参考

teppeisteppeis

ES compatibility

詳細は https://node.green/ を参照

ES2019

  • Object.fromEntries
  • Array.prototype.{flat, flatMap}

ES2020

  • String.prototype.matchAll
  • Promise.allSettled (12.9.0+)
  • globalThis

ES2021

  • numeric separators (12.5.0+)

Stage 3

  • instance class fields (optional private field access を除く)
  • static class fields
azuazu

assert.match(string, regexp[, message])

Added in: v12.16.0

文字列が正規表現にマッチするかをテストできるassert.matchがNode.js v12.16.0で追加されています。

import assert from 'assert/strict';

assert.match('I will fail', /pass/);
// AssertionError [ERR_ASSERTION]: The input did not match the regular ...

assert.match(123, /pass/);
// AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.

assert.match('I will pass', /pass/);
// OK