Javascript 如何将 ES6 编写的模块发布到 NPM?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29738381/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to publish a module written in ES6 to NPM?
提问by Traveling Tech Guy
I was about to publish a module to NPM, when I thought about rewriting it in ES6, to both future-proof it, and learn ES6. I've used Babel to transpile to ES5, and run tests. But I'm not sure how to proceed:
我正准备向 NPM 发布一个模块,当我考虑在 ES6 中重写它时,既要面向未来,又要学习 ES6。我已经使用 Babel 转译为 ES5,并运行测试。但我不确定如何继续:
- Do I transpile, and publish the resulting /out folder to NPM?
- Do I include the result folder in my Github repo?
- Or do I maintain 2 repos, one with the ES6 code + gulp script for Github, and one with the transpiled results + tests for NPM?
- 我是否转译并将生成的 /out 文件夹发布到 NPM?
- 我是否将结果文件夹包含在我的 Github 存储库中?
- 或者我是否维护 2 个存储库,一个带有用于 Github 的 ES6 代码 + gulp 脚本,另一个带有用于 NPM 的转译结果 + 测试?
In short: what steps do I need to take to publish a module written in ES6 to NPM, while still allowing people to browse/fork the original code?
简而言之:我需要采取哪些步骤才能将用 ES6 编写的模块发布到 NPM,同时仍然允许人们浏览/分叉原始代码?
采纳答案by José F. Romaniello
The pattern I have seen so far is to keep the es6 files in a srcdirectory and build your stuff in npm's prepublish to the libdirectory.
到目前为止,我看到的模式是将 es6 文件保存在一个src目录中,并在 npm 的prepublish目录中构建您的内容lib。
You will need an .npmignore file, similar to .gitignore but ignoring srcinstead of lib.
您将需要一个 .npmignore 文件,类似于 .gitignore 但忽略src而不是lib.
回答by Marius Craciunoiu
I like José's answer. I've noticed several modules follow that pattern already. Here's how you can easily implement it with Babel6. I install babel-clilocally so the build doesn't break if I ever change my global babel version.
我喜欢何塞的回答。我已经注意到有几个模块已经遵循了这种模式。下面是如何使用 Babel6 轻松实现它。我babel-cli在本地安装,因此如果我更改了全局 babel 版本,构建不会中断。
.npmignore
.npmignore
/src/
.gitignore
.gitignore
/lib/
/node_modules/
Install Babel
安装巴别塔
npm install --save-dev babel-core babel-cli babel-preset-es2015
package.json
包.json
{
"main": "lib/index.js",
"scripts": {
"prepublish": "babel src --out-dir lib"
},
"babel": {
"presets": ["es2015"]
}
}
回答by Dan Dascalescu
TL;DR- Don't, until ~October 2019. The Node.js Modules Teamhas asked:
TL;DR- 不要,直到 2019 年 10 月。Node.js模块团队已经问:
Please do not publish any ES module packages intended for use by Node.js until [October 2019]
请在 [2019 年 10 月] 之前发布任何供 Node.js 使用的 ES 模块包
2019 May update
2019 年 5 月更新
Since 2015 when this question was asked, JavaScript support for modules has matured significantly, and is hopefully going to be officially stable in October 2019. All other answers are now obsolete or overly complicated. Here is the current situation and best practice.
自 2015 年提出这个问题以来,JavaScript 对模块的支持已经显着成熟,有望在 2019 年 10 月正式稳定。所有其他答案现在都已过时或过于复杂。这是当前的情况和最佳实践。
ES6 support
ES6 支持
99% of ES6 (aka 2015) has been supported by Node since version 6. The current version of Node is 12. All evergreen browsers support the vast majority of ES6 features. ECMAScript is now at version 2019, and the versioning scheme now favors using years.
自版本 6 以来,99% 的 ES6(又名 2015)已被 Node 支持。Node 的当前版本是 12。所有常绿浏览器都支持绝大多数 ES6 功能。ECMAScript 现在是2019 版本,版本控制方案现在倾向于使用年份。
ES Modules (aka ECMAScript modules) in browsers
浏览器中的 ES 模块(又名 ECMAScript 模块)
All evergreen browsershave been supportingimport-ing ES6 modules since 2017. Dynamic importsare supportedby Chrome (+ forks like Opera and Samsung Internet) and Safari. Firefox support is slated for the next version, 67.
所有常绿浏览器已经支持import-ing ES6模块自2017年动态进口都支持通过浏览器(+叉如Opera和三星上网)和Safari。Firefox 支持计划用于下一个版本 67。
You no longer needWebpack/rollup/Parcel etc. to load modules. They may be still useful for other purposes, but are not required to load your code. You can directly import URLs pointing to ES modules code.
您不再需要Webpack/rollup/Parcel 等来加载模块。它们可能仍用于其他目的,但不是加载代码所必需的。您可以直接导入指向 ES 模块代码的 URL。
ES modules in Node
Node 中的 ES 模块
ES modules (.mjsfiles with import/export) have been supported since Node v8.5.0 by calling nodewith the --experimental-modulesflag. Node v12, released in April 2019, rewrote the experimental modules support. The most visible change is that the file extension needs to be specified by default when importing:
ES 模块(.mjs带有import/ 的文件export)从 Node v8.5.0 开始通过node使用--experimental-modules标志调用得到支持。2019 年 4 月发布的 Node v12 重写了实验模块支持。最明显的变化是导入时需要默认指定文件扩展名:
// lib.mjs
export const hello = 'Hello world!';
// index.mjs:
import { hello } from './lib.mjs';
console.log(hello);
Note the mandatory .mjsextensions throughout. Run as:
请注意整个强制.mjs扩展。运行为:
node --experimental-modules index.mjs
The Node 12 release is also when the Modules Team askeddevelopers to not publish ES module packages intended for use by Node.jsuntil a solution is found for using packages via both require('pkg')and import 'pkg'. You can still publish native ES modules intended for browsers.
Node 12 版本也是模块团队要求开发人员不要发布供 Node.js 使用的ES 模块包的时间,直到找到通过require('pkg')和使用包的解决方案import 'pkg'。您仍然可以发布用于浏览器的原生 ES 模块。
Ecosystem support of native ES modules
原生 ES 模块的生态系统支持
As of May 2019, ecosystem support for ES Modules is immature. For example, test frameworks like Jestand Avadon't support --experimental-modules. You need to use a transpiler, and must then decide between using the named import (import { symbol }) syntax (which won't work with most npm packages yet), and the default import syntax (import Package from 'package'), which does work, but not when Babel parses itfor packages authored in TypeScript(graphql-tools, node-influx, faast etc.) There is however a workaround that works both with --experimental-modulesand if Babel transpiles your code so you can test it with Jest/Ava/Mocha etc:
截至 2019 年 5 月,对 ES 模块的生态系统支持尚不成熟。例如,像Jest和Ava这样的测试框架不支持--experimental-modules. 您需要使用转译器,然后必须在使用命名的 import ( import { symbol }) 语法(目前还不适用于大多数 npm 包)和默认的 import 语法 ( import Package from 'package')之间做出决定,后者确实有效,但在 Babel 解析它时无效对于在 TypeScript 中编写的包(graphql-tools、node-influx、faast 等),但是有一种解决方法适用于--experimental-modulesBabel 转译您的代码,以便您可以使用 Jest/Ava/Mocha 等对其进行测试:
import * as ApolloServerM from 'apollo-server'; const ApolloServer = ApolloServerM.default || ApolloServerM;
Arguably ugly, but this way you can write your own ES modules code with import/exportand run it with node --experimental-modules, without transpilers. If you have dependencies that aren't ESM-ready yet, import them as above, and you'll be able to use test frameworks and other tooling via Babel.
可以说是丑陋的,但通过这种方式,您可以使用import/编写自己的 ES 模块代码export并使用 运行它node --experimental-modules,而无需转译器。如果您有尚未准备好 ESM 的依赖项,请按上述方式导入它们,您将能够通过 Babel 使用测试框架和其他工具。
Previous answer to the question - remember, don't do this until Node solves the require/import issue, hopefully around October 2019.
该问题的先前答案 - 请记住,在 Node 解决 require/import 问题之前不要这样做,希望在 2019 年 10 月左右。
Publishing ES6 modules to npm, with backwards compatibility
将 ES6 模块发布到 npm,具有向后兼容性
To publish an ES module to npmjs.org so that it can be imported directly, without Babel or other transpilers, simply point the mainfield in your package.jsonto the .mjsfile, but omit the extension:
要发布的ES模块npmjs.org以便它可以直接导入,没有通天或其他transpilers,简单点的main领域在你package.json的.mjs文件,但省略扩展名:
{
"name": "mjs-example",
"main": "index"
}
That's the only change. By omitting the extension, Node will look first for an mjs file if run with --experimental-modules. Otherwise it will fall back to the .js file, so your existing transpilation processto support older Node versions will work as before?—?just make sure to point Babel to the .mjsfile(s).
这是唯一的变化。通过省略扩展名,如果使用 --experimental-modules 运行,Node 将首先查找 mjs 文件。否则它会回退到 .js 文件,所以你现有的支持旧 Node 版本的转译过程会像以前一样工作?—?只需确保将 Babel 指向.mjs文件。
Here's the source for a native ES module with backwards compatibility for Node < 8.5.0that I published to NPM. You can use it right now, without Babel or anything else.
这是我发布到 NPM 的具有向后兼容性 Node < 8.5.0 的本机 ES 模块的源代码。你现在就可以使用它,不需要 Babel 或其他任何东西。
Install the module:
安装模块:
npm install local-iso-dt
# or, yarn add local-iso-dt
Create a test file test.mjs:
创建一个测试文件test.mjs:
import { localISOdt } from 'local-iso-dt/index.mjs';
console.log(localISOdt(), 'Starting job...');
Run node (v8.5.0+) with the --experimental-modules flag:
使用 --experimental-modules 标志运行节点 (v8.5.0+):
node --experimental-modules test.mjs
TypeScript
打字稿
If you develop in TypeScript, you can generate ES6 code and use ES6 modules:
如果你用 TypeScript 开发,你可以生成 ES6 代码并使用 ES6 模块:
tsc index.js --target es6 --modules es2015
Then, you need to rename *.jsoutput to .mjs, a known issuethat will hopefully get fixed soon so tsccan output .mjsfiles directly.
然后,您需要将*.jsoutput重命名为.mjs,这是一个已知问题,有望很快得到修复,因此tsc可以.mjs直接输出文件。
回答by André Pena
@Jose is right. There's nothing wrong with publishing ES6/ES2015 to NPM but that may cause trouble, specially if the person using your package is using Webpack, for instance, because normally people ignore the node_modulesfolder while preprocessing with babelfor performance reasons.
@Jose 是对的。将 ES6/ES2015 发布到 NPM 没有任何问题,但这可能会导致问题,特别是如果使用您的包的人正在使用 Webpack,例如,因为通常人们node_modules在预处理时会babel出于性能原因忽略该文件夹。
So, just use gulp, gruntor simply Node.js to build a libfolder that is ES5.
因此,只需使用gulp,grunt或者简单地使用Node.js 来构建一个libES5 文件夹。
Here's my build-lib.jsscript, which I keep in ./tools/(no gulpor grunthere):
这是我的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));
Here's a last advice: You need to add a .npmignore to your project. If npm publishdoesn't find this file, it will use .gitignoreinstead, which will cause you trouble because normally your .gitignorefile will exclude ./liband include ./src, which is exactly the opposite of what you want when you are publishing to NPM. The .npmignorefile has basically the same syntax of .gitignore(AFAIK).
这是最后一个建议:您需要将 .npmignore 添加到您的项目中。如果npm publish没有找到这个文件,它将.gitignore改为使用,这会给你带来麻烦,因为通常你的.gitignore文件会 exclude./lib和 include ./src,这与你发布到 NPM 时想要的完全相反。该.npmignore文件的语法与.gitignore(AFAIK)基本相同。
回答by Yuci
Following José and Marius's approach, (with update of Babel's latest version in 2019): Keep the latest JavaScript files in a src directory, and build with npm's prepublishscript and output to the lib directory.
遵循 José 和 Marius 的方法,(2019 年更新了 Babel 的最新版本):将最新的 JavaScript 文件保存在 src 目录中,并使用 npm 的prepublish脚本构建并输出到 lib 目录。
.npmignore
.npmignore
/src
.gitignore
.gitignore
/lib
/node_modules
Install Babel(version 7.5.5 in my case)
安装 Babel(在我的例子中是 7.5.5 版)
$ npm install @babel/core @babel/cli @babel/preset-env --save-dev
package.json
包.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"
]
}
}
And I have src/index.jswhich uses the arrow function:
我有src/index.js它使用箭头函数:
"use strict";
let NewOneWithParameters = (a, b) => {
console.log(a + b); // 30
};
NewOneWithParameters(10, 20);
Here is the repo on GitHub.
Now you can publish the package:
现在您可以发布包:
$ npm publish
...
> [email protected] prepublish .
> babel src -d lib
Successfully compiled 1 file with Babel.
...
Before the package is published to npm, you will see that lib/index.jshas been generated, which is transpiled to es5:
在将包发布到 npm 之前,您会看到lib/index.js已经生成,并被转译为 es5:
"use strict";
var NewOneWithParameters = function NewOneWithParameters(a, b) {
console.log(a + b); // 30
};
NewOneWithParameters(10, 20);
[Update for Rollup bundler]
[汇总打包器的更新]
As asked by @kyw, how would you integrate Rollup bundler?
正如@kyw 所问的,您将如何集成 Rollup 捆绑器?
First, install rollupand rollup-plugin-babel
首先,安装rollup并rollup-plugin-babel
npm install -D rollup rollup-plugin-babel
Second, create rollup.config.jsin the project root directory
二、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/**"
})
]
};
Lastly, update prepublishin package.json
最后,更新prepublish中package.json
{
...
"scripts": {
"prepublish": "rollup -c"
},
...
}
Now you can run npm publish, and before the package is published to npm, you will see that lib/index.js has been generated, which is transpiled to es5:
现在可以运行了npm publish,在发布包到 npm 之前,你会看到 lib/index.js 已经生成,并被转译为 es5:
'use strict';
var NewOneWithParameters = function NewOneWithParameters(a, b) {
console.log(a + b); // 30
};
NewOneWithParameters(10, 20);
Note: by the way, you no longer need @babel/cliif you are using the Rollup bundler. You can safely uninstall it:
注意:顺便说一下,@babel/cli如果您使用的是 Rollup 捆绑器,则不再需要。您可以安全地卸载它:
npm uninstall @babel/cli
回答by Guy
If you want to see this in action in a very simple small open source Node module then take a look at nth-day(which I started - also other contributors). Look in the package.json file and at the prepublish step which will lead you to where and how to do this. If you clone that module you can run it locally and use it as a template for yous.
如果您想在一个非常简单的小型开源 Node 模块中看到这一点,那么请查看第 n 天(我开始 - 也是其他贡献者)。查看 package.json 文件和 prepublish 步骤,这将引导您到何处以及如何执行此操作。如果您克隆该模块,您可以在本地运行它并将其用作您的模板。
回答by JoshWillik
The two criteria of an NPM package is that it is usable with nothing more than a require( 'package' )and does something software-ish.
NPM 包的两个标准是它只需要一个就可以使用,require( 'package' )并且可以做一些类似软件的事情。
If you fulfill those two requirements, you can do whatever you wish. Even if the module is written in ES6, if the end user doesn't need to know that, I would transpile it for now to get maximum support.
如果你满足这两个要求,你就可以为所欲为。即使模块是用 ES6 编写的,如果最终用户不需要知道这一点,我会暂时转译它以获得最大的支持。
However, if like koa, your module requires compatibility with users using ES6 features, then perhaps the two package solution would be a better idea.
但是,如果像koa一样,您的模块需要与使用 ES6 功能的用户兼容,那么也许两个包的解决方案会更好。
Takeaway
带走
- Only publish as much code as you need to make
require( 'your-package' )work. - Unless the between ES5 & 6 matters to the user, only publish 1 package. Transpile it if you must.
- 只发布
require( 'your-package' )工作所需的代码。 - 除非 ES5 和 6 之间对用户很重要,否则只发布 1 个包。如果必须,请转译它。
回答by Dinesh Pandiyan
The main key in package.jsondecides the entry point to the package once it's published. So you can put your Babel's output wherever you want and just have to mention the right path in mainkey.
主键package.json决定了包发布后的入口点。所以你可以把你的 Babel 的输出放在任何你想要的地方,只需要在mainkey 中提到正确的路径。
"main": "./lib/index.js",
Here's a well written article on how to publish an npm package
这是一篇关于如何发布 npm 包的好文章
https://codeburst.io/publish-your-own-npm-package-ff918698d450
https://codeburst.io/publish-your-own-npm-package-ff918698d450
Here's a sample repo you can use for reference
这是您可以用作参考的示例存储库
回答by thisismydesign
Node.js 13.2.0+ supports ESM without the experimental flag and there're a few options to publish hybrid (ESM and CommonJS) NPM packages (depending on the level of backward compatibility needed): https://2ality.com/2019/10/hybrid-npm-packages.html
Node.js 13.2.0+ 支持没有实验标志的 ESM,并且有几个选项可以发布混合(ESM 和 CommonJS)NPM 包(取决于所需的向后兼容性级别):https: //2ality.com/2019 /10/hybrid-npm-packages.html
I recommend going the full backward compatibility way to make the usage of your package easier. This could look as follows:
我建议采用完全向后兼容的方式,以便更轻松地使用您的包。这可能如下所示:
The hybrid package has the following files:
混合包具有以下文件:
mypkg/
package.json
esm/
entry.js
commonjs/
package.json
entry.js
mypkg/package.json
mypkg/package.json
{
"type": "module",
"main": "./commonjs/entry.js",
"exports": {
"./esm": "./esm/entry.js"
},
"module": "./esm/entry.js",
···
}
mypkg/commonjs/package.json
{
"type": "commonjs"
}
Importing from CommonJS:
从 CommonJS 导入:
const {x} = require('mypkg');
Importing from ESM:
从 ESM 导入:
import {x} from 'mypkg/esm';
We did an investigation into ESM support in 05.2019and found that a lot of libraries were lacking support (hence the recommendation for backward compatibility):
我们在 2019 年 5 月对 ESM 支持进行了调查,发现很多库都缺乏支持(因此建议向后兼容):
- esmpackage's support doesn't align with Node's which causes issues
- "Builtin require cannot sideload .mjs files." https://github.com/standard-things/esm#loading, https://github.com/standard-things/esm/issues/498#issuecomment-403496745
- "The .mjs file extension should not be the thing developers reach for if they want interop or ease of use. It's available since it's in --experimental-modules but since it's not fully baked I can't commit to any enhancements to it." https://github.com/standard-things/esm/issues/498#issuecomment-403655466
- mocha doesn't have native support for
.mjsfiles- Update 2020-01-13: Mocha released experimental support in
[email protected]
- Update 2020-01-13: Mocha released experimental support in
- Many high-profile projects had issues with
.mjsfiles:
- esm包的支持与导致问题的 Node 不一致
- “内置要求不能旁加载 .mjs 文件。” https://github.com/standard-things/esm#loading, https://github.com/standard-things/esm/issues/498#issuecomment-403496745
- “如果开发人员想要互操作性或易用性,那么 .mjs 文件扩展名不应该是他们想要的东西。它可用,因为它在 --experimental-modules 中,但由于它尚未完全成熟,我无法承诺对其进行任何增强。 ” https://github.com/standard-things/esm/issues/498#issuecomment-403655466
- mocha 没有对
.mjs文件的本地支持- 2020-01-13 更新:Mocha 发布了实验性支持
[email protected]
- 2020-01-13 更新:Mocha 发布了实验性支持
- 许多备受瞩目的项目都存在
.mjs文件问题:
回答by colxi
Deppending on the anatomy of your module, this solution may not work, but if your module is contained inside a single file, and has no dependencies(does not make use of import), using the following pattern you can release your code as it is, and will be able to be imported with import(Browser ES6 Modules) and require(Node CommonJS Modules)
根据模块的结构,此解决方案可能不起作用,但如果您的模块包含在单个文件中,并且没有依赖项(不使用import),则使用以下模式可以按原样发布代码, 并且可以通过import(Browser ES6 Modules) 和require(Node CommonJS Modules)导入
As a bonus, it will be suittable to be imported using a SCRIPT HTML Element.
作为奖励,它适合使用 SCRIPT HTML 元素导入。
main.js:
主.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:
我的模块.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:`
package.json:`
{
"name" : "my-module", // set module name
"main": "main.js", // set entry point
/* ...other package.json stuff here */
}
To use your module, you can now use the regular syntax ...
要使用您的模块,您现在可以使用常规语法...
When imported in NODE...
在NODE 中导入时...
let myModule = require('my-module');
myModule.helloWorld();
// outputs 'Hello World!'
When imported in BROWSER...
在BROWSER 中导入时...
import {myModule} from './my-module.js';
myModule.helloWorld();
// outputs 'Hello World!'
Or even when included using an HTML Script Element...
甚至当使用HTML 脚本元素包含时 ......
<script src="./main.js"></script>
<script>
myModule.helloWorld();
// outputs 'Hello World!'
</script>

