如何将 ES6 编写的module发布到 NPM?

IT技术 javascript node.js npm ecmascript-6 babeljs
2021-03-08 22:18:33

我正准备向 NPM 发布一个module,当我考虑在 ES6 中重写它时,既要面向未来,又要学习 ES6。我已经使用 Babel 转译为 ES5,并运行测试。但我不确定如何继续:

  1. 我是否转译并将生成的 /out 文件夹发布到 NPM?
  2. 我是否将结果文件夹包含在我的 Github 存储库中?
  3. 或者我是否维护 2 个存储库,一个带有用于 Github 的 ES6 代码 + gulp 脚本,另一个带有用于 NPM 的转译结果 + 测试?

简而言之:我需要采取哪些步骤才能将用 ES6 编写的module发布到 NPM,同时仍然允许人们浏览/分叉原始代码?

6个回答

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

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

.npmignore您可以使用filespackage.json 中字段代替它可以让你准确地指定,而不是狩猎为你随机文件要发布的文件,希望发布。
2021-04-21 22:18:33
@JamesAkwuh 请注意,您可能希望更改 package.json 中的“start”和“build”命令以使用 babel-cli: 的相对路径./node_modules/babel-cli/bin/babel.js -s inline -d lib -w src这应该确保在部署到新环境时安装不会失败。
2021-04-22 22:18:33
“如果没有 .npmignore 文件,但有 .gitignore 文件,那么 npm 将忽略与 .gitignore 文件匹配的内容。” 官方 npm 文档
2021-04-29 22:18:33
你有示例存储库吗?
2021-05-12 22:18:33
@phazonNinja npm 处理它
2021-05-14 22:18:33

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

.npmignore

/src/

.gitignore

/lib/
/node_modules/

安装巴别塔

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"]
  }
}
@mattecapu 似乎问题prepublish仍然存在。目前我认为手动编译src目录npm publish是要走的路。
2021-04-18 22:18:33
您可以使用prepublishOnly脚本挂钩(请参阅docs.npmjs.com/misc/scripts#prepublish-and-prepare)。请注意,在 npm 的第 5 版中,这应该可以正常工作,但是现在(假设您使用的是 npm v4+)这应该可以工作。
2021-04-18 22:18:33
中的任何命令都scriptsnode_modules/.bin添加到它们中,$PATH并且由于babel-cli安装了二进制文件,node_modules/.bin/babel因此无需按路径引用命令。
2021-04-26 22:18:33
请注意这prepublish是有问题的,因为它可以在安装时运行(github.com/npm/npm/issues/3059),更喜欢更惯用的version脚本钩子(docs.npmjs.com/cli/version
2021-04-27 22:18:33
@FrankNockeprepublishpublish( obv .)之前运行,它将东西推送到 npm(或您配置的任何地方)。所以它用于构建 NPM 包中的内容,即使它没有被签入。
2021-04-27 22:18:33

TL;DR - 不要,直到 2019 年 10 月。Node.jsmodule团队已经

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

2019 年 5 月更新

自 2015 年提出这个问题以来,JavaScript 对module的支持已经显着成熟,有望在 2019 年 10 月正式稳定。所有其他答案现在都已过时或过于复杂。这是当前的情况和最佳实践。

ES6 支持

自版本 6 以来,Node 已支持 99% 的 ES6(又名 2015)Node 当前版本是 12。所有常绿浏览器都支持 ES6 的绝大多数功能。ECMAScript 现在是2019 版本,版本控制方案现在倾向于使用年份。

浏览器中的 ES module(又名 ECMAScript module)

所有常绿浏览器已经支持 import-ing ES6module自2017年动态进口支持通过浏览器(+叉如Opera和三星上网)和Safari。Firefox 支持计划用于下一个版本 67。

您不再需要Webpack/rollup/Parcel 等来加载module。它们可能仍用于其他目的,但不是加载代码所必需的。您可以直接导入指向 ES module代码的 URL。

Node 中的 ES module

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

// 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 版本也是module团队要求开发人员不要发布供 Node.js 使用的ES module包的时间,直到找到通过require('pkg')使用包的解决方案import 'pkg'您仍然可以发布用于浏览器的原生 ES module。

原生 ES module的生态系统支持

截至 2019 年 5 月,对 ES module的生态系统支持尚不成熟。例如,像JestAva这样的测试框架不支持--experimental-modules. 您需要使用转译器,然后必须在使用命名的 import ( import { symbol }) 语法(它还不适用于大多数 npm 包)和默认的导入语法 ( 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;

可以说是丑陋的,但通过这种方式,您可以使用import/编写自己的 ES module代码export并使用 运行它node --experimental-modules,而无需转译器。如果您有尚未准备好 ESM 的依赖项,请按上述方式导入它们,您将能够通过 Babel 使用测试框架和其他工具。


之前对该问题的回答 - 请记住,在 Node 解决 require/import 问题之前不要这样做,希望在 2019 年 10 月左右。

将 ES6 module发布到 npm,具有向后兼容性

要发布的ESmodulenpmjs.org以便它可以直接导入,没有通天或其他transpilers,简单点的main领域在你package.json.mjs文件,但省略扩展名:

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

这是唯一的变化。通过省略扩展名,如果使用 --experimental-modules 运行,Node 将首先查找 mjs 文件。否则它将回退到 .js 文件,因此您现有的支持旧 Node 版本的转译过程将像以前一样工作 - 只需确保将 Babel 指向.mjs文件。

是我发布到 NPM 的具有向后兼容性 Node < 8.5.0 的本机 ES module源代码你现在就可以使用它,不需要 Babel 或其他任何东西。

安装module:

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 module:

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

然后,您需要将*.jsoutput重命名.mjs,这是一个已知问题,有望很快得到修复,因此tsc可以.mjs直接输出文件。

目前,生态系统肯定还不够成熟。发布 v12 的 Node.js 团队特别要求:“在解决此问题之前,请不要发布任何供 Node.js 使用的 ES module包。” 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 月推出官方支持,这是我会认真重新审视的最早时间。
2021-04-22 22:18:33
@thisismydesign:感谢提醒更新这个旧答案!就这样做了。
2021-04-22 22:18:33
说“所有常青浏览器都支持绝大多数 ES6 功能。” 当您查看数据并意识到浏览器中的 es6 支持仅达到所有用户的 80% 左右时,这并没有多大意义
2021-05-01 22:18:33
“所有其他答案现在都已过时或过于复杂。这是目前的情况和最佳实践。” 这似乎非常主观,绝对不能反映 2021 年发布的 npm 包的状态。
2021-05-16 22:18:33

@Jose 是对的。将 ES6/ES2015 发布到 NPM 没有任何问题,但这可能会导致问题,特别是如果使用您的包的人正在使用 Webpack,例如,因为通常人们node_modules在预处理时会babel出于性能原因忽略该文件夹

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

这是我的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文件会 exclude./lib和 include ./src,这与你发布到 NPM 时想要的完全相反。.npmignore文件的语法与.gitignore(AFAIK)基本相同

@thisismydesign:这正是我在上面的评论中所推荐的..?
2021-04-20 22:18:33
.npmignore您可以使用filespackage.json 中字段代替它可以让你准确地指定,而不是狩猎为你随机文件要发布的文件,希望发布。
2021-04-24 22:18:33
我不推荐使用.npmignore,尝试package.jsonfiles,而不是,请参阅:github.com/c-hive/guides/blob/master/js/...
2021-05-08 22:18:33
我的错,没有注意到:)
2021-05-09 22:18:33
这不会打破摇树吗?
2021-05-13 22:18:33

遵循 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

包.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 上的 repo

现在您可以发布包:

$ 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);

[Rollup 打包器的更新]

正如@kyw 所问的,您将如何集成 Rollup 捆绑器?

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

最后,更新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如果您使用的是 Rollup 捆绑器,则不再需要您可以安全地卸载它:

npm uninstall @babel/cli
您将如何集成 Rollup 捆绑器?
2021-04-25 22:18:33
@kyw,关于如何集成 Rollup bundler,请参阅我更新的答案。
2021-04-28 22:18:33
谢谢你!只是为了澄清,将您的功能集成到您可以使用的另一个项目中import NewOneWithParameters from 'latest-js-to-npm';所以在你的代码中你实际上并不需要export
2021-05-10 22:18:33
2019 年 12 月更新 --> github.com/rollup/rollup/blob/...
2021-05-11 22:18:33