Как опубликовать модуль, написанный на 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, а не на публикацию):

  • unlist src из .npmignore(другими словами: разрешить его). Если у вас нет .npmignore, запомните: копия .gitignore будет использоваться вместо этого в установленном месте, так как ls node_modules/yourProject покажет вам.
  • убедитесь, что babel-cli является зависимым от вашего модуля, а не только devDepenceny, поскольку вы действительно строите на компьютере-потребителе, например, на компьютере разработчиков приложений, который использует ваш модуль.
  • выполните сборку, в установить hook i.e.:

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

(без добавленной стоимости при попытке чего-либо "предустановить", т.е. babel-cli может отсутствовать)