当我考虑在ES6中重写它时,我打算将其发布给NPM,以适应将来的需求并学习ES6。我已经使用Babel移植到ES5,并运行测试。但是我不确定如何进行:
- 我是否要转换并把生成的/ out文件夹发布到NPM?
- 我是否将结果文件夹包含在我的Github存储库中?
- 还是我要维护2个存储库,一个使用Github的ES6代码+ gulp脚本,一个使用转换后的结果+ NPM测试?
简而言之:我需要采取什么步骤将ES6编写的模块发布到NPM,同时仍然允许人们浏览/分叉原始代码?
当我考虑在ES6中重写它时,我打算将其发布给NPM,以适应将来的需求并学习ES6。我已经使用Babel移植到ES5,并运行测试。但是我不确定如何进行:
简而言之:我需要采取什么步骤将ES6编写的模块发布到NPM,同时仍然允许人们浏览/分叉原始代码?
Answers:
到目前为止,我所看到的模式是将es6文件保存在src
目录中,并在npm的预发布目录中构建您的东西lib
。
您将需要一个.npmignore文件,类似于.gitignore,但忽略src
而不是lib
。
./node_modules/babel-cli/bin/babel.js -s inline -d lib -w src
。这应该确保在部署到新环境时安装不会失败。
我喜欢何塞的答案。我注意到已经有几个模块遵循该模式。这是使用Babel6轻松实现它的方法。我babel-cli
在本地安装,因此如果更改全局babel版本,构建不会中断。
.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"]
}
}
scripts
都会node_modules/.bin
添加到它们中,$PATH
并且由于babel-cli
安装了二进制文件,node_modules/.bin/babel
因此无需按路径引用该命令。
prepublish
是有问题的,因为它可以在安装时(运行github.com/npm/npm/issues/3059),喜欢更地道version
脚本挂机(docs.npmjs.com/cli/version)
prepublish
仍然存在。目前,我认为手动编译src
目录npm publish
是必经之路。
prepublishOnly
脚本挂钩(请参阅docs.npmjs.com/misc/scripts#prepublish-and-prepare)。请注意,在npm的第5版中,此功能应可正常运行,但目前(假设您使用的是npm v4 +),此功能应该可以使用。
prepublish
在publish
(obv 。)之前运行,它将内容推送到npm(或任何您配置的位置)。因此,它是建立在NPM包发生的事情,即使它不是在检查。
TL; DR -不,直到日〜10月2019年Node.js的模块组已经要求:
在[2019年10月]之前,请勿发布打算供Node.js使用的任何ES模块软件包。
自2015年提出此问题以来,JavaScript对模块的支持已显着成熟,并有望在2019年10月正式稳定。所有其他答案现在已过时或过于复杂。这是当前的情况和最佳实践。
自版本6开始,Node已支持99%的ES6(aka 2015)。Node的当前版本为12。所有常绿浏览器都支持绝大多数ES6功能。ECMAScript现在的版本为2019,而版本控制方案现在倾向于使用多年。
所有常绿浏览器已经支持 import
-ing ES6模块自2017年动态进口的支持通过浏览器(+叉如Opera和三星上网)和Safari。Firefox的下一个版本是67。
您不再需要 Webpack / rollup / Parcel等来加载模块。它们可能仍可用于其他目的,但不需要加载代码。您可以直接导入指向ES模块代码的URL。
从Node v8.5.0开始,通过使用标志进行调用,已支持ES模块(.mjs
带有import
/的文件export
)。2019年4月发布的Node v12重新编写了实验模块支持。最明显的变化是,导入时默认需要指定文件扩展名:node
--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.js使用的 ES模块软件包之前,Node 12发行,直到找到了通过require('pkg')
和两者使用软件包的解决方案import 'pkg'
。您仍然可以发布供浏览器使用的本机ES模块。
截至2019年5月,对ES模块的生态系统支持尚不成熟。例如,诸如Jest和Ava之类的测试框架不支持--experimental-modules
。您需要使用转译器,然后必须在使用命名的import(import { symbol }
)语法(尚不适用于大多数npm软件包)和默认的import语法(import Package from 'package'
)之间做出决定,该语法有效,但在Babel对其进行解析时无效对于使用TypeScript编写的软件包(graphql工具,node-influx,faast等),但是有一种变通办法可以与--experimental-modules
Babel一起使用,如果Babel可以对您的代码进行编译,则可以使用Jest / Ava / Mocha等对其进行测试:
import * as ApolloServerM from 'apollo-server'; const ApolloServer = ApolloServerM.default || ApolloServerM;
可以说很丑陋,但是通过这种方式,您可以使用import
/ 编写自己的ES模块代码export
然后使用进行运行node --experimental-modules
,而无需编译器。如果您的依赖项尚不支持ESM,请按上述方式导入它们,然后就可以通过Babel使用测试框架和其他工具。
该问题的先前答案-请记住,在Node解决需求/导入问题之前,不要这样做,希望在2019年10月左右。
要发布的ES模块npmjs.org以便它可以直接导入,没有通天或其他transpilers,简单点的main
领域在你package.json
的.mjs
文件,但省略扩展名:
{
"name": "mjs-example",
"main": "index"
}
那是唯一的变化。通过省略扩展名,Node如果与--experimental-modules一起运行,将首先查找mjs文件。否则,它将退回到.js文件,因此您现有的支持较旧Node版本的转译过程将像以前一样工作-只需确保将Babel指向.mjs
文件即可。
这是对节点<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开发,则可以生成ES6代码并使用ES6模块:
tsc index.js --target es6 --modules es2015
然后,您需要将*.js
输出重命名为.mjs
,这是一个已知问题,有望很快得到解决,因此tsc
可以.mjs
直接输出文件。
mjs
文件的依赖项方面存在问题。Node.js计划在2019年10月提供正式支持,这是我最早认真地重新审视这一点。
@乔斯是正确的。将ES6 / ES2015发布到NPM并没有什么问题,但这可能会引起麻烦,特别是如果使用您的软件包的人正在使用Webpack,例如,因为通常人们node_modules
在使用babel
性能原因。
因此,只需使用gulp
,grunt
或仅使用Node.js来构建lib
文件夹是ES5。
这是我的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
(AFAIK)相同。
遵循José和Marius的方法(在2019年更新了Babel的最新版本):将最新的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 Bundle?
一,安装 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/**"
})
]
};
最后,更新 prepublish
中package.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
Node.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');
从ESM导入:
import {x} from 'mypkg/esm';
我们在05.2019中对ESM支持进行了调查,发现许多库缺乏支持(因此建议向后兼容):
.mjs
文件的本机支持
mocha@7.0.0-esm1
.mjs
文件方面存在问题:
NPM软件包的两个标准是,它仅可用于 require( 'package' )
并完成某些类似于软件的工作。
如果满足这两个要求,那么您可以做任何您想做的事情。即使该模块是用ES6编写的,如果最终用户不需要知道这一点,我现在也要对其进行编译以获得最大的支持。
但是,如果像koa一样,您的模块需要与使用ES6功能的用户兼容,那么两个软件包的解决方案可能是一个更好的主意。
require( 'your-package' )
工作。require( 'package' )
工作所需的文件。我将编辑答案以使其更清楚。也就是说,何塞的答案比我自己的答案要好得多。
package.json
一旦发布,包中的主键将决定包的入口点。因此,您可以将Babel的输出放置在任意位置,而只需在main
键中提及正确的路径即可。
"main": "./lib/index.js",
这是一篇有关如何发布npm软件包的书面文章
https://codeburst.io/publish-your-own-npm-package-ff918698d450
这是一个示例仓库,您可以参考
import
可以直接使用而无需Babel或任何其他编译器。
依靠模块的解剖结构,此解决方案可能无法工作,但是如果您的模块包含在单个文件中,并且没有依赖项(不使用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 */
}
要使用您的模块,您现在可以使用常规语法...
当进口的节点?
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>
(广泛使用的)“预发布”挂钩没有做任何事情对您帮助。
一个人可以做的最好的事情(如果计划依靠github仓库,而不是发布的东西):
src
从.npmignore(换句话说:允许它)。如果您没有.npmignore
,请记住:.gitignore
将在安装位置使用的副本,例如ls node_modules/yourProject
所示。babel-cli
是模块中的依赖关系,而不仅仅是devDepenceny,因为您确实是在使用模块的App开发人员计算机上构建消费机器在安装挂钩中执行构建工作,即:
"install": "babel src -d lib -s"
(尝试“预安装”没有附加价值,即可能缺少babel-cli)
npm pack
输出,3)检入build输出。