如何将用 ES6编写的模块发布到 NPM?

我正准备向 NPM 发布一个模块,这时我考虑在 ES6中重写该模块,以防止将来出现这种情况,并学习 ES6。我已经用巴别塔转换到 ES5,并运行测试。但我不知道该怎么办:

  1. 我是否传送,并将结果/输出文件夹发布到 NPM?
  2. 我是否将结果文件夹包含在我的 Github 回购中?
  3. 或者我维护2个回购协议,一个是用于 Github 的 ES6代码 + Gulp 脚本,另一个是用于 NPM 的转发结果 + 测试?

简而言之: 我需要采取什么步骤来发布用 ES6编写的模块到 NPM,同时仍然允许人们浏览/分叉原始代码?

44800 次浏览

到目前为止,我看到的模式是将 es6文件保存在 src目录中,并将您的内容在 npm 的预发布目录中构建到 lib目录。

您将需要一个. npmignore 文件,类似于. gitignore,但是忽略 src而不是 lib

NPM 软件包的两个标准是,它只需要 require( 'package' )就可以使用,并且可以做一些类似于软件的事情。

如果你满足了这两个要求,你可以做任何你想做的事情。 即使这个模块是用 ES6编写的,如果最终用户不需要知道这一点,我也会暂时转换它以获得最大限度的支持。

但是,如果像 可儿一样,您的模块需要与使用 ES6特性的用户兼容,那么两个包解决方案可能是一个更好的主意。

外卖

  1. 只发布使 require( 'your-package' )工作所需的代码。
  2. 除非 ES5和 ES6之间的问题对用户很重要,否则只发布一个包。如果必须,可以转换它。

@ 何塞是对的。将 ES6/ES2015发布到 NPM 并没有什么错,但是这可能会引起麻烦,特别是如果使用您的包的人正在使用 Webpack,例如,因为通常人们在使用 babel进行预处理时会忽略 node_modules文件夹,这是出于性能原因。

因此,只需使用 gulpgrunt或者简单的 Node.js 来构建一个 lib文件夹,即 ES5。

这是我的 build-lib.js脚本,我保存在 ./tools/中(这里没有 gulpgrunt) :

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(AFAIK)语法基本相同。

我喜欢何塞的回答。我注意到有几个模块已经遵循了这种模式。下面介绍如何使用 Babel6轻松实现它。我在本地安装 babel-cli,所以如果我更改了全局 bel 版本,构建就不会中断。

。 npmno

/src/

。吉蒂诺尔

/lib/
/node_modules/

安装 Babel

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

包裹 Json

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

如果您希望在一个非常简单的开源 Node 模块中看到这种情况,那么可以看一看 第九天(我开始使用它——还有其他贡献者)。查看 package.json 文件并查看预发布步骤,这将引导您找到执行此操作的位置和方法。如果您克隆了这个模块,那么您可以在本地运行它,并将它用作您的模板。

对任何人都有一些额外的注意事项,直接从 github 使用 自己的模块,而不是通过 出版模块:

(广泛使用)“预发布”钩子不为你做 什么都行

人们能做的最好的事情(如果计划依靠 github 回购,而不是发表的东西) :

  • 从。取消列出 src。Npmignore (换句话说: 允许它)。如果您没有 .npmignore,请记住: .gitignore的副本将被用于安装位置,因为 ls node_modules/yourProject将向您显示。
  • 确保 babel-cli是模块中的一个依赖项,而不仅仅是 devDepenceny,因为您确实是在使用模块的消费机器上构建的,也就是在应用程序开发人员的计算机上构建的
  • 安装钩安装钩中执行构建操作,例如:

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

(在尝试任何“预安装”时没有附加值,即 babel-cli 可能丢失)

package.json中的主键决定包发布后的入口点。所以你可以把你的巴别塔的输出任何地方你想要的,只要提到正确的路径在 main键。

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

下面是一篇关于如何发布 npm 包的文章

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

下面是一个可供参考的回购案例

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

不要,直到2019年10月。 Node.js 单元组问道:

请在[2019年10月]之前不要发布任何供 Node.js 使用的 ES 模块包

2019年5月最新情况

自从2015年提出这个问题以来,对模块的 JavaScript 支持已经显著成熟,并且有望在2019年10月正式稳定下来。所有其它答案现在都已过时或过于复杂。下面是目前的情况和最佳实践。

ES6支持

ES6(又名2015)的99% 已经得到 Node 从第六版开始的支持。Node 的当前版本是12。所有常绿浏览器都支持绝大多数 ES6特性。ECMAScript 现在位于 2019年版本,版本控制方案现在倾向于使用年份。

浏览器中的 ES 模块(又名 ECMAScript 模块)

所有常青浏览器 自2017年以来都是 支持 import-ing ES6模块。动态导入是由 Chrome (+ 像 Opera 和三星互联网)和 Safari 的 支持。Firefox 将支持下一个版本67。

您不再需要 需要 Webpack/rollup/Parcel 等来加载模块。它们可能对其他用途仍然有用,但不需要加载代码。您可以直接导入指向 ES 模块代码的 URL。

节点中的 ES 模块

自 Node v8.5.0以来,通过使用 --experimental-modules标志调用 node,ES 模块(带有 import/export.mjs文件)就得到了支持。2019年4月发布的 Node v12重写了对实验模块的支持。最明显的变化是在导入时需要默认指定文件扩展名:

// lib.mjs


export const hello = 'Hello world!';


// index.mjs:


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

请注意整个过程中的强制 .mjs扩展。运行方式如下:

node --experimental-modules index.mjs

节点12版本也是模块团队 问道开发人员在找到通过 require('pkg')import 'pkg'使用包的解决方案之前不发布 ES 模块包 < b > 供 Node.js 使用的时候。您仍然可以发布用于浏览器的本机 ES 模块。

本地 ES 模块的生态系统支持

截至2019年5月,ES 模块的生态系统支持还不成熟。例如,像 开玩笑的艾娃这样的测试框架不支持 --experimental-modules。你需要使用一个传输器,然后必须决定是使用命名导入(import { symbol })语法(这在大多数 npm 包中还不起作用) ,还是默认的导入语法(import Package from 'package') ,它确实起作用,但是 巴别塔分析的时候就不是了用 TypeScript 编写的包起作用(Graphql-tools、节点内流、快速等等)。然而,有一个变通方法既可以用 --experimental-modules,也可以用 Babel 传输你的代码,这样你就可以用 Jest/Ava/Mocha 等来测试它:

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

可以说是丑陋的,但这种方式可以编写自己的 ES 模块代码与 import/export和运行它与 node --experimental-modules,没有发送器。如果您有不符合 ESM 要求的依赖项,那么像上面那样导入它们,您将能够通过 Babel 使用测试框架和其他工具。


先前对这个问题的回答——记住,在 Node 解决了需求/导入问题之前不要这样做,希望在2019年10月左右。

通过向后兼容性将 ES6模块发布到 npm

要将 ES 模块发布到 npmjs.org,以便可以直接导入它,而不需要 Babel 或其他转发器,只需将 package.json中的 main字段指向 .mjs文件,但省略扩展名:

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

这是唯一的改变。通过省略扩展名,Node 将首先查找一个 mjs 文件(如果使用——實验模块运行的话)。否则它将退回到。Js 文件,所以现有的支持旧 Node 版本的 翻译过程翻译过程将像以前一样工作ーー只要确保 Babel 指向 .mjs文件。

这是我发布给 NPM 的 对 Node < 8.5.0具有向后兼容性的本机 ES 模块的源代码。你现在就可以使用它,不需要 Babel 或者其他什么东西。

安装模块:

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

创建一个测试文件 Test MJ:

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

运行节点(v8.5.0 +) ,使用——實验模块标志:

node --experimental-modules test.mjs

打字机

如果使用 TypeScript 开发,则可以生成 ES6代码并使用 ES6模块:

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

然后,需要将 *.js输出重命名为 .mjs,这是一个已知的 问题,希望很快就能修复,这样 tsc就可以直接输出 .mjs文件。

根据模块的结构,这个解决方案可能不起作用,但是如果你的模块是 包含在单个文件中,并且没有依赖项(没有使用 进口) ,使用以下模式,你可以按原样发布代码,并且可以用 进口(Browser ES6 Module)和 要求(Node CommonJS Module)导入代码

另外,它将适合使用 SCRIPTHTML 元素进行导入。

返回文章页面

(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};

Json : ‘

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

要使用模块,现在可以使用常规语法..。

当进口 节点..。

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

当进口 浏览器..。

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

甚至包括使用 HTML 脚本元素..。

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

遵循 José 和 Marius 的方法(2019年更新了 Babel 的最新版本) : 将最新的 JavaScript 文件保存在 src 目录中,使用 npm 的 prepublish脚本构建,并将输出输出到 lib 目录。

。 npmno

/src

。吉蒂诺尔

/lib
/node_modules

安装 Babel (在我的示例中是7.5.5版本)

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

包裹 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?

首先,安装 rolluprollup-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/**"
})
]
};

最后,在 package.json中更新 prepublish

{
...
"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);

注意: 顺便说一下,如果使用 Rollup 捆绑包,则不再需要 @babel/cli。您可以安全地卸载它:

npm uninstall @babel/cli

Js 13.2.0 + 支持无实验标志的 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');

从无害环境管理输入:

import {x} from 'mypkg/esm';

我们做了一个 2019年5月5日有关无害环境管理支援的调查,发现许多图书馆缺乏支持(因此建议使用向下兼容) :