Как опубликовать модуль, написанный на ES6, в NPM?
Я собирался опубликовать модуль для NPM, когда подумал о переписывании его в ES6, как в будущем, так и в изучении ES6. Я использовал Babel для перевода на ES5 и запуска тестов. Но я не уверен, как действовать:
- Передаю ли я и публикую папку с результатом/выходом в NPM?
- Включить ли я папку результатов в моем реестре Github?
- Или я поддерживаю 2 репозитория, один с кодом ES6 + gulp script для Github, а другой с переданными результатами + тесты для NPM?
Вкратце: какие шаги я должен предпринять, чтобы опубликовать модуль, написанный на ES6, в NPM, но все же позволяя людям просматривать/форматировать исходный код?
Ответы
Ответ 1
Образец, который я видел до сих пор, заключается в том, чтобы сохранить файлы es6 в каталоге src
и создать ваш материал в npm, чтобы опубликовать его в каталоге lib
.
Вам понадобится файл .npmignore, похожий на .gitignore, но игнорирующий src
вместо lib
.
Ответ 2
Мне нравится ответ Хосе. Я заметил, что несколько модулей уже соответствуют этой схеме. Вот как вы можете легко реализовать его с 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": "node_modules/babel-cli/bin/babel.js src --out-dir lib"
},
"babel": {
"presets": ["es2015"]
}
}
Ответ 3
TL; DR - не раньше ~ октября 2019 года. Команда модулей Node.js Modules Team спросила :
Пожалуйста, не публикуйте никакие пакеты модулей ES, предназначенные для использования Node.js, до [октября 2019 года]
2019 май обновление
С 2015 года, когда был задан этот вопрос, поддержка JavaScript для модулей значительно повзрослела, и мы надеемся, что она станет официально стабильной в октябре 2019 года. Все остальные ответы в настоящее время устарели или слишком сложны. Вот текущая ситуация и лучшие практики.
Поддержка ES6
99% ES6 (он же 2015) поддерживается узлом
с версии 6 . Текущая версия Node - 12. Все вечнозеленые браузеры поддерживают подавляющее большинство функций ES6. ECMAScript теперь находится на версии 2019 , а схема управления версиями теперь предпочитает использовать годы.
ES-модули (также известные как ECMAScript-модули) в браузерах
Все вечнозеленые браузеры
All evergreen browsers поддерживают import
-ing модули ES6 с 2017 года. Динамический импорт Dynamic imports are поддерживается Chrome (+ вилки, такие как Opera и Samsung Интернет) и Safari. Поддержка Firefox запланирована на следующую версию, 67.
Вам больше не нужен Webpack/Rollup/Parcel и т.д. Для загрузки модулей. Они могут все еще быть полезными для других целей, но не обязательны для загрузки вашего кода. Вы можете напрямую импортировать URL-адреса, указывающие на код модулей ES.
ES-модули в узле
Модули ES (файлы .mjs
с import
/export
) поддерживаются начиная с узла v8.5.0, вызывая node
с флагом --experimental-modules
. Node v12, выпущенный в апреле 2019 года, переписал поддержку экспериментальных модулей. Наиболее заметным изменением является то, что расширение файла должно быть указано по умолчанию при импорте:
// 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 команда разработчиков модулей попросила разработчиков не публиковать пакеты модулей ES , предназначенные для использования Node.js, до тех пор, пока не будет найдено решение для использования пакетов через оба require('pkg')
и import 'pkg'
. Вы по-прежнему можете публиковать собственные модули ES, предназначенные для браузеров.
Экосистемная поддержка нативных модулей ES
По состоянию на май 2019 года экосистемная поддержка модулей ES является незрелой. Например, тестовые среды, такие как Jest и Ava, не поддерживают --experimental-modules
. Вам нужно использовать транспортер, а затем выбрать между использованием именованного синтаксиса импорта (import { symbol }
) (который пока не работает с большинством пакетов npm) и синтаксиса импорта по умолчанию (import Package from 'package'
), который работает, но нет, когда Babel анализирует его для пакетов, созданных в TypeScript (graphql-tools, node-infl, faast и т.д.). Однако существует обходной путь, который работает как с --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 года.
Публикация модулей ES6 в npm с обратной совместимостью
Чтобы опубликовать ES-модуль на npmjs.org, чтобы его можно было импортировать напрямую, без Babel или других транспортеров, просто укажите поле main
в вашем package.json
на файл .mjs
, но пропустите расширение:
{
"name": "mjs-example",
"main": "index"
}
Это единственное изменение. Опуская расширение, Node будет сначала искать файл mjs, если он запускается с --experimental-modules. В противном случае он вернется к файлу .js, поэтому ваш существующий процесс переноса для поддержки более старых версий Node будет работать, как и раньше, - просто убедитесь, что Babel указывает на файл .mjs
.
Здесь источник для собственного модуля ES с обратной совместимостью для Node & lt; 8.5.0, который я опубликовал в NPM. Вы можете использовать его прямо сейчас, без Вавилона или чего-либо еще.
Установите модуль:
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...');
Запустите узел (v8.5. 0+) с флагом --experimental-modules:
node --experimental-modules test.mjs
машинопись
Если вы разрабатываете на языке TypeScript, вы можете создавать код ES6 и использовать модули ES6:
tsc index.js --target es6 --modules es2015
Затем вам нужно переименовать вывод *.js
в .mjs
, известную проблему, которая, как мы надеемся, скоро будет устранена, чтобы tsc
мог выводить файлы .mjs
напрямую.
Ответ 4
@Правильно. Нет ничего плохого в публикации ES6/ES2015 в NPM, но это может вызвать проблемы, особенно если человек, использующий ваш пакет, использует Webpack, например, потому что обычно люди игнорируют папку node_modules
при предварительной обработке с помощью babel
по соображениям производительности.
Итак, просто используйте gulp
, grunt
или просто Node.js для сборки папки lib
, которая является ES5.
Здесь my build-lib.js
script, который я храню в ./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).
Ответ 5
Если вы хотите увидеть это в действии в очень простом модуле с открытым исходным кодом Node, то посмотрите nth-day ( который я начал - также другие участники). Посмотрите в файле package.json и на этапе prepublish, который приведет вас к тому, где и как это сделать. Если вы клонируете этот модуль, вы можете запустить его локально и использовать его в качестве шаблона для вас.
Ответ 6
Два критерия пакета NPM заключаются в том, что он может использоваться только с require( 'package' )
и делает что-то программное.
Если вы выполняете эти два требования, вы можете делать все, что пожелаете.
Даже если модуль написан на ES6, если конечный пользователь не должен этого знать, я бы перебросил его для получения максимальной поддержки.
Однако, если, например, koa, ваш модуль требует совместимости с пользователями, использующими функции ES6, то, возможно, два решения пакета будут лучше идея.
Вынос
- Публикуйте столько кода, сколько нужно, чтобы сделать работу
require( 'your-package' )
.
- Если между ES5 и 6 не имеет значения для пользователя, опубликуйте только один пакет. Если это необходимо, перетащите его.
Ответ 7
Главный ключ в package.json
определяет точку входа в пакет после его публикации. Таким образом, вы можете поместить свой вывод Babel куда угодно и просто указать правильный путь в main
ключе.
"main": "./lib/index.js",
Здесь хорошо написана статья о том, как опубликовать пакет npm
https://codeburst.io/publish-your-own-npm-package-ff918698d450
Здесь образец репо вы можете использовать для справки
https://github.com/flexdinesh/npm-module-boilerplate
Ответ 8
Следуя подходу Хосе и Мариуса (с обновлением последней версии Babel в 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 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
...
> [email protected] 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/**"
})
]
};
Наконец, обновите 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
Ответ 9
В зависимости от анатомии вашего модуля, это решение может не работать, но если ваш модуль содержится в одном файле и не имеет зависимостей (не использует импорт), используя следующий шаблон, вы можете освободить свой код как есть. и сможет импортироваться с помощью импорта (модули Browser ES6) и требовать (модули Node CommonJS)
В качестве бонуса, он будет пригоден для импорта с использованием элемента HTML SCRIPT.
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!'
При импорте в браузер...
import {myModule} from './my-module.js';
myModule.helloWorld();
// outputs 'Hello World!'
Или даже если он включен с использованием элемента HTML Script...
<script src="./main.js"></script>
<script>
myModule.helloWorld();
// outputs 'Hello World!'
</script>
Ответ 10
Несколько дополнительных заметок для всех, используя собственные модули непосредственно из github, не просматривая опубликованные модули:
(широко используется). "prepublish" hook ничего не делает для вас.
Лучшая вещь, которую можно сделать (если планируете полагаться на репозитории github, а не на публикацию):
(без добавленной стоимости при попытке чего-либо "предустановить", т.е. babel-cli может отсутствовать)