Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
require(ESM)とECMAScript仕様
Search
uhyo
April 25, 2024
Technology
4
980
require(ESM)とECMAScript仕様
Meguro.es #27 @ oRo
uhyo
April 25, 2024
Tweet
Share
More Decks by uhyo
See All by uhyo
TypeScript Quiz (Encraft #12 Frontend Quiz Night)
uhyo
7
1.4k
Shadow DOMとCSSの現状
uhyo
11
6.2k
TypeScriptってどんな言語? 言語そのものを知る面白さ
uhyo
16
8.3k
App Router時代のデータ取得アーキテクチャ
uhyo
45
14k
ステート管理を超えるRecoil運用の考え方
uhyo
15
11k
ついに来る!TypeScript5.0の新機能
uhyo
21
15k
TypeScript 4.7と型レベルプログラミング
uhyo
6
5.3k
TypeScriptを振り回せ!
uhyo
28
8.9k
2020年代、使う技術はどう選ぶ?
uhyo
22
12k
Other Decks in Technology
See All in Technology
Cloud Service Mesh に触れ合う
phaya72
1
230
地理空間データ可視化・解析・活用ソリューション Pacific Spatial Solutions (PSS)
pacificspatialsolutions
0
330
MixIT 2024 - Pulumi : Gérer son infra avec son langage de programmation préféré
ju_hnny5
1
120
MLOpsの「壁」を乗り越える、LINEヤフーの Data Quality as Code
lycorptech_jp
PRO
8
620
Documentação de Produtos: Artefatos essenciais na prática
rigolon
1
120
How to do well in consulting–Balkan Ruby 2024
irinanazarova
0
140
ExaDB-D dbaascli で出来ること
oracle4engineer
PRO
0
2.1k
VSCodeの拡張機能を作っている話
ebarakazuhiro
1
810
IPUT App Dev. Co. -Overview 2024/4
iputapp
0
130
エンジニア候補者向け資料2024.04.24.pdf
macloud
0
3.4k
チームでロジカルシンキングに改めて向き合っている話 〜学習環境と実践⽅法〜
sansantech
PRO
3
3.3k
ルーターでプレゼンする
puhitaku
1
3.3k
Featured
See All Featured
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
155
14k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
352
28k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
33
6k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
188
16k
GitHub's CSS Performance
jonrohan
1025
450k
How to name files
jennybc
65
93k
Designing with Data
zakiwarfel
96
4.8k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
14
1.5k
Designing the Hi-DPI Web
ddemaree
276
33k
A better future with KSS
kneath
231
16k
4 Signs Your Business is Dying
shpigford
176
21k
Git: the NoSQL Database
bkeepers
PRO
423
63k
Transcript
require(ESM)とECMAScript仕様 2024-04-25 Meguro.es #27 @ oRo
発表者紹介 uhyo 株式会社カオナビ フロントエンドエンジニア 普段はTypeScriptとかReactをやっている。 好きな⾔語機能はジェネレータ関数。
JavaScriptの最近のニュース Node.jsが require(ESM) のサポートを追加。 require先がtop-level awaitを含まない ESモジュール (sync ESM) であれば、
CJSファイルからrequireできる。
This Talk require(ESM) って、ECMAScript仕様の視点で ⾒るとどういうことなんだっけ? を解説します
CJSとかESMの復習
ScriptとModule ECMAScript仕様上、JavaScriptプログラムは 2種類に分類される。 •Script: 昔からあるやつ •Module: import/exportが使えるやつ 両者は構⽂レベルで異なる。 (importなどをScriptで使うのは構⽂エラー)
ScriptとModule ScriptかModuleかは外的要因によって決まる (ことが多い) HTML <script> // Scriptとしてパースされる </script> <script type=“module”>
// Moduleとしてパースされる </script> Node.js foo.cjs // Scriptとしてパースされる bar.mjs // Moduleとしてパースされる
ESモジュールグラフ import/exportでつながるモジュールグラフが形成 される。
モジュールの種類 モジュールはJavaScriptファイルとは限らない。 .js .js .mjs .js .js .js .html .json
.wasm .css
モジュールの種類 ECMAScriptでは3種類に分類される。 (下は上の部分クラス) (Abstract) Module Record … 全てのモジュール Cyclic Module
Record … 他のモジュールをimport可能 Source Text Module Record … JavaScriptで書かれた モジュール ※ 厳密に⾔えばAbstract Module Recordも他のモジュールに依存可能だが、ES仕様で扱う依存グラフには表れない
モジュールの種類 .js .js .mjs .js .js .js .html .json .wasm
.css
Node.jsのCJSとESMの関係は? CommonJSのモジュールはECMAScriptモジュー ルではない。ECMAScript側から⾒たらどういう 扱いなのか? .mjs .cjs 特にこうやってESMからCJSをimportする場合
Node.jsのCJSとESMの関係は? 多分CommonJSモジュールはESMから⾒たら Abstract Module Recordに相当する。 .mjs .cjs
モジュールの種類と仕様 (Abstract) Module Record … 全部ホスト依存 Cyclic Module Record …
モジュールグラフの探索周り は仕様で明記 Source Text Module Record … 全部仕様で明記 この仕様により、JS以外のモジュールを扱うことができ る。JS以外のモジュールの挙動は、⼀部または全部が ホスト依存となる。 ※ ESモジュールのホストの例: Node.js、HTML仕様、webpackなど
Node.jsのCJS Abstract Module Recordの挙動は、仕様で 決められた制約を守ればホスト環境の⾃由。 つまり、.cjsから何がexportされるのかとか、 同期的に実⾏されるといった詳細はNode.jsが ⾃由に決められる。 .mjs .cjs
本題
require(ESM)の仕様上の解釈 require(“./module.mjs”)のようにすると、 module.mjsが同期的に実⾏される。 このことをES仕様に沿って記述できるか? ES仕様と⽭盾する処理にはならないのか? が気になる
既存の類例 ⾮モジュールからモジュールを実⾏する機能と して、import()がすでに存在する。 この構⽂であれば、ScriptからでもModuleを 実⾏できる。もちろんNode.jsのCJSからでも。 import(“module”).then(mod => …) “module”をモジュール解決して実⾏し、モジュール名前空間オ ブジェクトに解決されるPromiseを返す
import()の仕様レベルの挙動
import()の仕様レベルの挙動 import(“module”) HostLoadImportedModule … モジュール解決してModule Recordを取得 module.LoadRequestedModules() … 依存モジュール読み込み module.Link()
… Environment Recordを初期化 module.Evaluate() … モジュールグラフに従って実⾏ ざっくりこの4段階でモジュールを実⾏できる。
HTML仕様の例 HTML仕様でも、モジュールを実⾏することはEvaluate()で表現 (LoadRequestedModulesとLinkは別のところで呼んでる)
ES仕様におけるモジュールの実⾏ module.Evaluate()を呼び出すことでモジュールが実 ⾏される。Evaluateには次の制約がある。 •Promiseを返す。モジュールの実⾏が完了すると fulfillする • Evaluateを呼ぶ前にLinkの呼び出しが成功して いる必要がある
ES仕様におけるモジュールの実⾏ module.Link()はモジュールの実⾏の準備をする。 Linkには次の制約がある。 • Linkを呼ぶ前にLoadRequestedModulesの 呼び出しが成功している必要がある
ES仕様におけるモジュールの実⾏ module.LoadRequestedModules()はモジュールの 依存先を全部読み込む。Promiseを返す。 LoadRequestedModulesはホスト定義の HostLoadImportedModuleを利⽤して実際の 読み込みを⾏う。 (HostLoadImportedModule⾃体は同期的にも⾮同期的にも 動作可能と定義されている)
ES仕様におけるモジュールの実⾏ 仕様上の制約として、この順に実⾏する必要がある。 HostLoadImportedModule ↑利⽤ module.LoadRequestedModules() ↑前提 module.Link() ↑前提 module.Evaluate()
ES仕様におけるモジュールの実⾏ require(ESM)の処理をES仕様で記述する場合も制約を守る必要がある。 HostLoadImportedModule ↑利⽤ module.LoadRequestedModules() ↑前提 module.Link() ↑前提 module.Evaluate()
ES仕様におけるモジュールの実⾏ HostLoadImportedModule 同期実⾏可能 ↑利⽤ module.LoadRequestedModules() Promiseを返す ↑前提 module.Link() 同期実⾏ ↑前提
module.Evaluate() Promiseを返す
ES仕様におけるモジュールの実⾏ HostLoadImportedModule 同期実⾏可能 ↑利⽤ module.LoadRequestedModules() Promiseを返す ↑前提 module.Link() 同期実⾏ ↑前提
module.Evaluate() Promiseを返す
Evaluate()の同期実⾏ 実はsync ESMのみをサポートする前例が Service Workerに存在する。 モジュールをService Workerとして登録する場合、 TLAを含むモジュールを登録することはできない。
Service Workerの仕様を⾒る 登録されたモジュールを実⾏するところの仕様
Service Workerの仕様を⾒る module.Evaluate()の結果のPromise 最初から解決済
Sync ESMのEvaluate() 実はES仕様では、TLAを持たないSource Text Module RecordのEvaluate()は、最初から 解決済みのPromiseを返すことが保証されている。 (=TLAを含まないモジュールグラフは仕様上同期的に 実⾏できる) Evaluate()が返したPromiseをもらった瞬間に
捨てればOK。
ES仕様におけるモジュールの実⾏ HostLoadImportedModule 同期実⾏可能 ↑利⽤ module.LoadRequestedModules() Promiseを返す ↑前提 module.Link() 同期実⾏ ↑前提
module.Evaluate() Promiseを返す
LoadRequestedModules()の定義を⾒る
ES仕様におけるモジュールの実⾏ 詳細は省くが、HostLoadImportedModule()が同期的に動く 環境であれば、 LoadRequestedModules()が返すPromiseも 最初から解決されている。 こちらも同期的に実⾏可能。 (返ってきたPromiseはやはり捨てる)
補⾜: エラー処理について どちらも返ってきたPromiseを捨てることで同期的に 処理可能だが、エラーの場合の対応は必要。 そこは処理系特権でPromiseの中⾝を同期的に確認し、 エラー処理を⾏う。
まとめ
まとめ Node.jsが新たに実装したrequire(ESM)は、 実装されてもECMAScript仕様とは⽭盾しないことが 分かった。 (仕様上Promiseの中⾝を同期的に⾒るというずるが必要になるが、 多分実装でカバーできる) 安⼼して使おう!