Slide 1

Slide 1 text

CJSとESMとNPMパッケージ CJSとESMとNPMパッケージ (2018/06/29) (2018/06/29) 小田島 太郎 / @shimataro NODE学園 31時限目 NODE学園 31時限目

Slide 2

Slide 2 text

自己紹介 自己紹介 ウェブリオ株式会社所属(京都) サーバサイドエンジニア 趣味は手品 小田島 太郎 shimataro@GitHub odashima.taro@Facebook shimataro999@Twitter

Slide 3

Slide 3 text

この発表について この発表について 対象: 汎用 npmパッケージを開発している人 技術レベル: 中級 npmパッケージやBabelの基礎知識を前提 技術的に濃い話ではありません ↓スライドはこちら↓ https://speakerdeck.com/shimataro https://shimataro.github.io/slides/

Slide 4

Slide 4 text

目次 目次 背景 今回のゴール CJSとESM パッケージの構成 コード生成 まとめ

Slide 5

Slide 5 text

始める前に 始める前に

Slide 6

Slide 6 text

7/5 関西NODE学園 2時限目 7/5 関西NODE学園 2時限目

Slide 7

Slide 7 text

それでは始めます それでは始めます

Slide 8

Slide 8 text

背景 背景

Slide 9

Slide 9 text

背景 背景 JavaScript(ECMAScript)のモジュール require() / module.exports (CommonJS; CJS) import / export (ES Modules; ESM) ネイティブESMはあまり使われていない (BabelでCJSに変 換するのが一般的) でもきっとそのうち普及する

Slide 10

Slide 10 text

今回のゴール 今回のゴール

Slide 11

Slide 11 text

今回のゴール 今回のゴール CJS / ESM / Babelの どれからでも使える パッケージモジ ュールを作りたい ↓ // パッケージ "foo" export default foo; export {bar}; // CJS const {default: foo, bar} = require("foo"); // ESM / Babel import foo, {bar} from "foo";

Slide 12

Slide 12 text

今回のゴール 今回のゴール パッケージの条件 ソースコードは単一 ビルド時に複数ファイルの生成はOK default export / named exports 両方対応 ソースコード内に変なハックは入れない おまじない NG 古い文法 NG 作る側にも使う側にも、極力負担をかけない

Slide 13

Slide 13 text

CJSとESM CJSとESM

Slide 14

Slide 14 text

CJSとESM CJSとESM 拡張子 .mjs はESM それ以外の拡張子はCJS export default foo; export {bar}; module.exports = foo; // または exports.default = foo; exports.bar = bar;

Slide 15

Slide 15 text

CJSとESM CJSとESM 相互運用について Native ES Modules - something almost, but not quite entirely unlike CommonJS by Gil Tayor

Slide 16

Slide 16 text

CJSとESM CJSとESM 1つのファイルで全部対応するのはややこしい .mjs と .js を両方用意する方が確実 最初にESMで書いて、CJS形式にも変換 Babelの出番

Slide 17

Slide 17 text

パッケージの構成 パッケージの構成

Slide 18

Slide 18 text

パッケージの構成 パッケージの構成 package.json 内の main を 拡張子なし で指定 2種類のindexを用意 ./index.js (CJS/Babel用) ./index.mjs (ESM用) http://yosuke- furukawa.hatenablog.com/entry/2016/05/10/111102 { ... "main": "./index", // ".js" はつけない ... }

Slide 19

Slide 19 text

コード生成 コード生成

Slide 20

Slide 20 text

コード生成 コード生成 概要 index.js はこうする index.mjs はこのままでOK exports.default = foo; exports.bar = bar; export default foo; export {bar};

Slide 21

Slide 21 text

コード生成 - CJS コード生成 - CJS index.js は普通にBabelで変換すればOK ↓ export default foo; export {bar}; exports.default = foo; exports.bar = bar;

Slide 22

Slide 22 text

コード生成 - ESM コード生成 - ESM index.mjs は何もしなくてOK? Nodeがサポートしている文法だけを使うならOK デコレータとかstatic propertiesとか使えない 汎用 パッケージなら、ある程度古いバージョンもサポー トしたい せめてLTSくらいは… そのために 古い文法 を使うのは嫌だ というわけで、 やっぱりBabelは使いたい

Slide 23

Slide 23 text

コード生成 - ESM コード生成 - ESM Babel使用時の注意 その1 babel-preset-env のデフォルトはCJS形式 import / export構文を変換しない場合はこうする // .babelrc { "presets": [ ["env", {"modules": false}] ] }

Slide 24

Slide 24 text

コード生成 - ESM コード生成 - ESM Babel使用時の注意 その2 拡張子が自動的に .js になる ESMでは .mjs じゃないとダメ Babel7に --keep-file-extension が登場 拡張子を変更しないオプション 変換元の拡張子も .mjs にする必要あり まだbeta段階

Slide 25

Slide 25 text

コード生成(ESM) コード生成(ESM) 選択肢は2つ Babel単体で解決 Babel7 & --keep-file-extension 他のツールを導入 変換後に拡張子をリネーム 今回は後者(gulp)を採用 Babel6(安定版)でも使える 元の拡張子に制限なし

Slide 26

Slide 26 text

実際に作ってみた 実際に作ってみた

Slide 27

Slide 27 text

実際に作ってみた 実際に作ってみた ESM(.mjs) / Babel CJS https://github.com/shimataro/hell-word npm install @shimataro/hell-word import foo, {bar} from "@shimataro/hell-word"; foo(); // "hell, word" bar(); // "地獄の言葉" const {default: foo, bar} = require("@shimataro/hell-word"); foo(); // "hell, word" bar(); // "地獄の言葉"

Slide 28

Slide 28 text

まとめ まとめ 単一ソースからCJS/ESM/Babel対応のパッケージを作 る方法 まずESMの export 構文で作る CJS用は exports.default に変換→ .js ESM用はそのまま→ .mjs package.json の "main" に拡張子をつけない サンプル作ってみたからよかったら参考にしてね 関西Node学園もよろしく! 手品に興味があったら声をかけてね!

Slide 29

Slide 29 text

ありがとうございました ありがとうございました