ES6で記述されたモジュールをNPMに公開する方法は?


144

モジュールをNPMに公開しようとしていたとき、ES6でモジュールを書き直そうと思ったとき、将来を見据え、ES6を学びました。私はBabelを使用してES5にトランスパイルし、テストを実行しました。しかし、私はどのように進めるかわかりません:

  1. トランスパイルして、結果の/ outフォルダーをNPMに公開しますか?
  2. 結果フォルダーをGithubリポジトリに含めますか?
  3. または、2つのリポジトリを維持します。1つはGithubのES6コード+ gulpスクリプト、もう1つは変換された結果+ NPMのテストです。

つまり、ES6で作成されたモジュールをNPMに公開する一方で、元のコードを参照/フォークできるようにするために、どのような手順を踏む必要がありますか?


私はこの決定に最近苦労しています。ホセが正解としてマークした回答もコンセンサスになっています。
タルフ2015年


1
逆のことができればいいのですが。ESモジュールを使用してNPMモジュールをインポートしますが、これが唯一の結果です。
SeanMC

回答:


81

これまでに見たパターンは、es6ファイルをsrcディレクトリに保持し、npmのディレクトリへの事前発行で自分のものを構築することlibです。

.gitignoreに似ていますが、src代わりに無視する.npmignoreファイルが必要になりますlib


4
サンプルリポジトリはありますか?
Ahmed Abbas

2
@JamesAkwuh package.jsonの「start」および「build」コマンドを変更して、babel-cli:の相対パスを使用することをお勧めします./node_modules/babel-cli/bin/babel.js -s inline -d lib -w src。これにより、新しい環境に展開するときにインストールが失敗しないことが保証されます。
phazonNinja 2017

2
@phazonNinja npmが対応
James Akwuh

4
「.npmignoreファイルはないが.gitignoreファイルはある場合、npmは.gitignoreファイルに一致するものを無視します。」公式npm文書
フランクノッケ2017年

10
代わりに、package.json.npmignorefilesフィールドを使用できます。公開したくないランダムなファイルを探す代わりに、公開したいファイルを正確に指定できます。
Dan Dascalescu 2017

76

ホセの答えが好きです。すでにいくつかのモジュールがそのパターンに従っていることに気づきました。これがBabel6で簡単に実装できる方法です。babel-cliローカルにインストールするので、グローバルバベルバージョンを変更してもビルドは中断しません。

.npmignore

/src/

.gitignore

/lib/
/node_modules/

Babelをインストールする

npm install --save-dev babel-core babel-cli babel-preset-es2015

package.json

{
  "main": "lib/index.js",
  "scripts": {
    "prepublish": "babel src --out-dir lib"
  },
  "babel": {
    "presets": ["es2015"]
  }
}

29
にコマンドscriptsnode_modules/.bin追加され、バイナリがインストールされるため$PATH、パスでコマンドを参照する必要はありません。babel-clinode_modules/.bin/babel
スキマ

3
prepublishインストール時に実行される可能性があるため、問題があることに注意してください(github.com/npm/npm/issues/3059)、より慣用的なversionスクリプトフック(docs.npmjs.com/cli/version)を使用してください
mattecapu

@mattecapuにprepublishはまだ問題があるようです。現時点では、手動でsrcディレクトリをコンパイルすると思いますnpm publish
sonlexqt 2017

1
prepublishOnlyスクリプトフックを使用できます(docs.npmjs.com/misc/scripts#prepublish-and-prepareを参照)。npmのバージョン5では、これは期待どおりに機能するはずですが、現時点では(npm v4 +を使用していると想定して)、これは機能するはずです。
Alex Mann

1
@FrankNocke prepublishpublish(obv。)の前に実行され、npm(または設定した場所)にデータをプッシュします。だから、それがチェックインいない場合でも、NPMパッケージに何が起こるのか構築するためだ。
サイモン・バカン

42

TL; DR -ない、〜10月2019年ザ・Node.jsのまで、モジュールのチームが尋ねました

[2019年10月]まで、Node.jsでの使用を目的としたESモジュールパッケージは公開しないでください。

2019年5月更新

2015年にこの質問が寄せられて以来、モジュールのJavaScriptサポートは大幅に成熟し、2019年10月に正式に安定する予定です。他のすべての回答は廃止されたか、過度に複雑になっています。現在の状況とベストプラクティスを次に示します。

ES6サポート

ES6の99%(別名2015)は、バージョン6以降、Nodeでサポートされています。Nodeの現在のバージョンは12です。すべての常緑樹ブラウザは、ES6機能の大部分をサポートしています。ECMAScriptのバージョンは2019になり、バージョン管理スキームでは年を使用するようになりました。

ブラウザのESモジュール(別名ECMAScriptモジュール)

すべての常緑樹ブラウザーは2017年以降ES6モジュールをサポートし ていimportます。動的インポートはChrome(+ OperaやSamsung Internetなどのフォーク)とSafariでサポートされています。Firefoxのサポートは次のバージョン、67で予定されています。

あなたはもはや必要モジュールをロードするためのWebPACK /ロールアップ/小包など。これらは他の目的にも役立ちますが、コードをロードする必要はありません。ESモジュールのコードを指すURLを直接インポートできます。

ノードのESモジュール

ESモジュール(/を含む.mjsファイル)は、フラグを指定して呼び出すことにより、Node v8.5.0以降でサポートされています。2019年4月にリリースされたNode v12は、実験的なモジュールのサポートを書き直しました。最も目に見える変更は、インポート時にデフォルトでファイル拡張子を指定する必要があることです。importexportnode--experimental-modules

// lib.mjs 

export const hello = 'Hello world!';

// index.mjs:

import { hello } from './lib.mjs';
console.log(hello);

.mjs全体で必須の拡張子に注意してください。次として実行:

node --experimental-modules index.mjs

Node 12リリースはまた、モジュールチーム開発者に、およびの両方を介してパッケージを使用するためのソリューションが見つかるまで、Node.jsによる使用を目的としたESモジュールパッケージを公開しないように依頼したときでもrequire('pkg')ありimport 'pkg'ます。ブラウザ向けのネイティブESモジュールは引き続き公開できます。

ネイティブESモジュールのエコシステムサポート

2019年5月の時点で、ESモジュールのエコシステムサポートは未成熟です。たとえば、JestAvaなどのテストフレームワークはをサポートしていません--experimental-modules。あなたはtranspilerを使用する必要があり、その後、名前のインポート(使用しての間で決定しなければなりませんimport { symbol })構文(まだほとんどNPMのパッケージとなりますない作品)、およびデフォルトのインポート構文(import Package from 'package'仕事をする)、しかし、バベルは、それを解析していないときTypeScriptで作成されたパッケージ(graphql-tools、node-influx、faastなど)の場合。ただし、--experimental-modulesJave / Ava / Mochaなどでテストできるように、Babelがコードをトランスパイルする場合に機能する回避策があります。

import * as ApolloServerM from 'apollo-server'; const ApolloServer = ApolloServerM.default || ApolloServerM;

間違いなく醜いですが、この方法では、トランスパイラなしでimport/ exportを使用して独自のESモジュールコードを記述し、で実行node --experimental-modulesできます。ESMにまだ対応していない依存関係がある場合は、上記のようにインポートすると、Babelを介してテストフレームワークやその他のツールを使用できるようになります。


以前の質問への回答-覚えておいてください、できれば2019年10月頃にNodeがrequire / import問題を解決するまで、これを行わないでください。

下位互換性を備えたES6モジュールのnpmへの公開

ESモジュールをnpmjs.orgに公開して、Babelや他のトランスパイラーなしで直接インポートできるようmainにするpackage.jsonには、フィールドを.mjsファイルに指定するだけですが、拡張子は省略します。

{
  "name": "mjs-example",
  "main": "index"
}

それが唯一の変更です。拡張機能を省略すると、Nodeは--experimental-modulesで実行された場合、最初にmjsファイルを探します。それ以外の場合は.jsファイルにフォールバックするため、古いノードバージョンをサポートするための既存の変換プロセスは以前と同様に.mjs機能します。必ず、Babelがファイルを指すようにしてください。

ここだノード<8.5.0のための後方互換性を持つネイティブESモジュールのソース私はNPMに発行されていること。Babelなどを使わなくても、今すぐ使用できます。

モジュールをインストールします。

npm install local-iso-dt
# or, yarn add local-iso-dt

テストファイルtest.mjsを作成します。

import { localISOdt } from 'local-iso-dt/index.mjs';
console.log(localISOdt(), 'Starting job...');

--experimental-modulesフラグを指定してノード(v8.5.0 +)を実行します。

node --experimental-modules test.mjs

TypeScript

TypeScriptで開発する場合は、ES6コードを生成してES6モジュールを使用できます。

tsc index.js --target es6 --modules es2015

次に、*.js出力の名前をに変更する必要があります。これは.mjs既知の問題であり、すぐに修正され、ファイルを直接tsc出力できるようになることが期待されてい.mjsます。


3
「すべての常緑樹ブラウザは、ES6機能の大部分をサポートしています。」データを見て、ブラウザーでのes6サポートが全ユーザーの約80%にしか到達しないことを理解しても、それほど意味がありません。
ペドロペドロサ

3
現在、エコシステムはこれに対して十分に成熟していません。Node.jsチームは、v12のリリースについて、「これが解決するまで、Node.jsでの使用を目的としたESモジュールパッケージを公開しないでください」と具体的に尋ねました。2ality.com/2019/04/nodejs-esm-impl.html#es-modules-on-npm Mochaは.mjsファイルをネイティブでサポートしていません。多くのライブラリ(create-react-app、react-apollo、graphql-jsなど)には、mjsファイルを含む依存関係に関する問題がありました。Node.jsは、2019年10月に公式サポートを展開する予定です。これは、私がこれを真剣に再検討する最も早い時期です。
thisismydesign

3
@thisismydesign:この古い答えを更新するリマインダーをありがとう!ちょうどそうしました。
Dan Dascalescu、

17

@ホセは正しいです。ES6 / ES2015をNPMに公開することに問題はありませんが、特にパッケージを使用しているユーザーがWebpackを使用している場合は特に問題が発生する可能性がありnode_modulesますbabel

したがって、単にgulpgruntまたは単にNode.jsを使用して、libES5であるフォルダーを作成します。

ここにbuild-lib.js私が保持しているスクリプトがあります./tools/(いいえgulpまたはgruntここにあります):

var rimraf = require('rimraf-promise');
var colors = require('colors');
var exec = require('child-process-promise').exec;

console.log('building lib'.green);

rimraf('./lib')
    .then(function (error) {
        let babelCli = 'babel --optional es7.objectRestSpread ./src --out-dir ./lib';
        return exec(babelCli).fail(function (error) {
            console.log(colors.red(error))
        });
    }).then(() => console.log('lib built'.green));

最後のアドバイスです。プロジェクトに.npmignoreを追加する必要がありますnpm publishこのファイルが見つからない場合は、.gitignore代わりに使用されます。通常、.gitignoreファイルはを除外./libしてインクルードしますが./src、これはNPMに公開するときに希望するものとは正反対です。.npmignoreファイルは、基本的には同じ構文は.gitignore(私の知る限り)を。


1
代わりに、package.json.npmignorefilesフィールドを使用できます。公開したくないランダムなファイルを探す代わりに、公開したいファイルを正確に指定できます。
Dan Dascalescu 2017

この休憩ツリーを振っていませんか?
Joe

私は使用することはお勧めしません.npmignore、試してみるpackage.jsonfiles代わりに、以下を参照してくださいgithub.com/c-hive/guides/blob/master/js/...
thisismydesign

@thisismydesign:それはまさに私が上記のコメントで推奨していたものです..?
Dan Dascalescu、

私の悪いことに気づかなかった:)
thisismydesign '20年

8

ホセとマリウスのアプローチに従い(2019年にバベルの最新バージョンを更新)、最新のJavaScriptファイルをsrcディレクトリに保持し、npmのprepublishスクリプトでビルドしてlibディレクトリに出力します。

.npmignore

/src

.gitignore

/lib
/node_modules

Babelをインストールする(私の場合はバージョン7.5.5)

$ npm install @babel/core @babel/cli @babel/preset-env --save-dev

package.json

{
  "name": "latest-js-to-npm",
  "version": "1.0.0",
  "description": "Keep the latest JavaScript files in a src directory and build with npm's prepublish script and output to the lib directory.",
  "main": "lib/index.js",
  "scripts": {
    "prepublish": "babel src -d lib"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.5.5",
    "@babel/core": "^7.5.5",
    "@babel/preset-env": "^7.5.5"
  },
  "babel": {
    "presets": [
      "@babel/preset-env"
    ]
  }
}

そして、私はsrc/index.js矢印関数を使用しています:

"use strict";

let NewOneWithParameters = (a, b) => {
  console.log(a + b); // 30
};
NewOneWithParameters(10, 20);

これがGitHubのリポジトリです。

これでパッケージを公開できます:

$ npm publish
...
> latest-js-to-npm@1.0.0 prepublish .
> babel src -d lib

Successfully compiled 1 file with Babel.
...

パッケージがnpmに公開される前にlib/index.js、それが生成され、es5にトランスパイルされていることがわかります。

"use strict";

var NewOneWithParameters = function NewOneWithParameters(a, b) {
  console.log(a + b); // 30
};

NewOneWithParameters(10, 20);

[ロールアップバンドラーの更新]

@kywが尋ねたように、Rollup bundlerをどのように統合しますか?

まず、インストールrollupしてrollup-plugin-babel

npm install -D rollup rollup-plugin-babel

次に、rollup.config.jsプロジェクトのルートディレクトリに作成します

import babel from "rollup-plugin-babel";

export default {
  input: "./src/index.js",
  output: {
    file: "./lib/index.js",
    format: "cjs",
    name: "bundle"
  },
  plugins: [
    babel({
      exclude: "node_modules/**"
    })
  ]
};

最後に、更新prepublishpackage.json

{
  ...
  "scripts": {
    "prepublish": "rollup -c"
  },
  ...
}

これで、を実行できますnpm publish。パッケージがnpmに公開される前に、lib / index.jsが生成され、es5に変換されていることがわかります。

'use strict';

var NewOneWithParameters = function NewOneWithParameters(a, b) {
  console.log(a + b); // 30
};

NewOneWithParameters(10, 20);

注:ちなみに、@babel/cliロールアップバンドラーを使用している場合は必要ありません。安全にアンインストールできます:

npm uninstall @babel/cli

Rollup bundlerをどのように統合しますか?
kyw

1
@ kyw、Rollup bundlerの統合方法については、私の最新の回答を参照してください。
Yuci


6

非常にシンプルな小さなオープンソースのNodeモジュールでこれを実行したい場合は、n日目(私が始めた-他の貢献者も)を見てください。package.jsonファイルと、これを実行する場所と方法を示す事前公開ステップを確認します。そのモジュールを複製すると、ローカルで実行して、テンプレートとして使用できます。


4

Node.js 13.2.0+は、experimentalフラグなしでESMをサポートし、ハイブリッド(ESMおよびCommonJS)NPMパッケージを公開するためのいくつかのオプションがあります(必要な下位互換性のレベルに応じて):https : //2ality.com/2019 /10/hybrid-npm-packages.html

パッケージの使用を簡単にするために、完全な下位互換性のある方法を使用することをお勧めします。これは次のようになります。

ハイブリッドパッケージには次のファイルがあります。

mypkg/
  package.json
  esm/
    entry.js
  commonjs/
    package.json
    entry.js

mypkg/package.json

{
  "type": "module",
  "main": "./commonjs/entry.js",
  "exports": {
    "./esm": "./esm/entry.js"
  },
  "module": "./esm/entry.js",
  ···
}

mypkg/commonjs/package.json

{
  "type": "commonjs"
}

CommonJSからのインポート:

const {x} = require('mypkg');

ESMからのインポート:

import {x} from 'mypkg/esm';

2019年5月にESMサポートを調査しところ、多くのライブラリにサポートが不足していることがわかりました(そのため、下位互換性のための推奨事項です)。


グローバルにインストールされたES6モジュールをnode_modulesフォルダー(npm root -gによって提供される)にインポートできないようです。私たちは本当にそれを行うことができないはずですか?私は本当に混乱しています。npm linkがモジュールをローカルのnode_modulesフォルダーにリンクすることで問題を解決できることはわかっていますが、グローバルノードモジュールのインポートがサポートされていない理由を知りたいです。
Joakim L. Christiansen

自分自身に答えて、私はそれをサポートすることはありません推測:github.com/nodejs/node-eps/blob/master/... ...、それはしかし、本当に愚かな意思決定の支援には簡単だろう
ヨアキムL.クリスティアンセン

3

NPMパッケージの2つの基準は、A以外では何も使用できず、require( 'package' )ソフトウェアのような何かを行うことです。

これら2つの要件を満たしている場合は、何でも好きなことができます。モジュールがES6で記述されている場合でも、エンドユーザーがそれを知る必要がない場合は、最大のサポートを得るために、今のところそれを変換します。

ただし、koaのように、モジュールにES6機能を使用するユーザーとの互換性が必要な場合は、おそらく2つのパッケージソリューションの方が適しています。

取り除く

  1. require( 'your-package' )作業に必要なだけのコードを公開してください。
  2. ES5とES6の間がユーザーにとって重要でない限り、1つのパッケージのみを公開します。必要に応じてトランスパイルします。

1
これは質問に答えていないようです。OPはGithubリポジトリをどのように構成し、NPMに何を公開するかを理解しようとしていると思います。あなたが言ったことは、彼らが望むことは何でもできるということです。OPは、この状況の適切な実践に関する特定の推奨事項を求めています。
jfriend00

@ jfriend00同意しない。私は彼にトランスパイルし、require( 'package' )動作するために必要なファイルのみを公開することを勧めました。これをより明確にするために、回答を編集します。そうは言っても、ホセの答えは私自身の答えよりもはるかに優れています。
JoshWillik 2015

ホセの答えは非常に良いですが、1つと2つのパッケージをいつどのように使用するかについての大まかな経験則を明確に概説しているので、この1つに感謝します。
ジョーダングレイ

3

のメインキーはpackage.json、パッケージが公開された後のパッケージへのエントリポイントを決定します。したがって、バベルの出力を好きな場所に置くことができ、mainキーの正しいパスに言及する必要があります。

"main": "./lib/index.js",

これは、npmパッケージを公開する方法に関するよく書かれた記事です

https://codeburst.io/publish-your-own-npm-package-ff918698d450

参照用に使用できるサンプルリポジトリは次のとおりです

https://github.com/flexdinesh/npm-module-boilerplate


その投稿では、ES6作成され、import Babelや他のトランスパイラーを必要とせずに直接実行できるNPMモジュールにパブリッシュできる(そしてすべき)という事実を省略しています。
Dan Dascalescu 2018

0

モジュールの構造に依存して、このソリューションは機能しない可能性がありますが、モジュールが単一のファイル内に含まれ、依存関係がない場合importを使用しない)、次のパターンを使用して、コードをそのままリリースできます、およびインポート(ブラウザES6モジュール)でインポートでき、必要(ノードCommonJSモジュール)

おまけとして、SCRIPT HTML要素を使用してインポートするのが適しています。

main.js

(function(){
    'use strict';
    const myModule = {
        helloWorld : function(){ console.log('Hello World!' )} 
    };

    // if running in NODE export module using NODEJS syntax
    if(typeof module !== 'undefined') module.exports = myModule ;
    // if running in Browser, set as a global variable.
    else window.myModule = myModule ;
})()

my-module.js

    // import main.js (it will declare your Object in the global scope)
    import './main.js';
    // get a copy of your module object reference
    let _myModule = window.myModule;
    // delete the the reference from the global object
    delete window.myModule;
    // export it!
    export {_myModule as myModule};

package.json: `

    {
        "name" : "my-module", // set module name
        "main": "main.js",  // set entry point
        /* ...other package.json stuff here */
    }

モジュールを使用するには、通常の構文を使用できます...

NODEにインポートすると...

    let myModule = require('my-module');
    myModule.helloWorld();
    // outputs 'Hello World!'

BROWSERにインポートする ...

    import {myModule} from './my-module.js';
    myModule.helloWorld();
    // outputs 'Hello World!'

または、HTMLスクリプト要素を使用して含まれている場合でも ...

<script src="./main.js"></script>
<script>
     myModule.helloWorld();
    // outputs 'Hello World!'
</script>

-1

公開されたモジュールを経由せずに、githubから直接独自のモジュールを使用する場合の注意点:

広く使用されている)「prepublish」フックは何もしていません。

できる最善のこと(公開されたものではなくgithubリポジトリに依存する場合):

  • .npmignore srcからリストを外します(つまり、許可します)。がない場合は、次.npmignoreの点.gitignoreに注意してls node_modules/yourProjectください。インストールされた場所では、代わりにのコピーが使用されます。
  • 確かに、babel-cliあなたはモジュールを使用しているアプリ開発者のコ​​ンピューターで別名消費マシン上に実際にビルドしているので、単なるdevDepencenyではなく、モジュールの依存関係です。
  • インストールフックでビルドを行います。

    "install": "babel src -d lib -s"

(「プレインストール」を試みることで付加価値はありません。つまり、babel-cliが欠落している可能性があります)


3
インストール時にコンパイルするのはとても失礼です。これを行わないでください-npmのインストール時間は不十分です。npmパッケージリポジトリの使用を避けたい内部コードの場合、1)monorepoを使用します。2)アップロードしてnpm pack出力に依存します。3)ビルド出力をチェックインします。
Simon Buchan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.