Як опублікувати модуль, написаний на ES6 в NPM?


144

Я збирався опублікувати модуль до NPM, коли думав про перезапис його в ES6, щоб це було підтверджено майбутнім, і вивчити ES6. Я використовував Babel для транспіляції в ES5 та проведення тестів. Але я не впевнений, як діяти:

  1. Чи потрібно перекладати та публікувати отриману папку / вихід у NPM?
  2. Чи включати папку результатів у своє репортаж Github?
  3. Або я підтримую 2 репости, один із кодом ES6 + скрипт gulp для Github, а другий із перекладеними результатами + тести для NPM?

Якщо коротко: які дії потрібно зробити, щоб опублікувати модуль, написаний на ES6 в NPM, при цьому все ще дозволяти людям переглядати / розщедрити оригінальний код?


Я останнім часом борюся з цим рішенням. Я бачу відповідь, яку ви відзначили як правильну Хосе як консенсус.
талі

Ось моя відповідь 2018 року , враховуючи прогрес у підтримці модулів з 2015 року.
Дан Даскалеску,

1
Я хотів би, якби я міг зробити навпаки. Використовуйте модуль ES для імпорту модуля NPM, але це єдині результати, які я отримую.
SeanMC

Відповіді:


81

Я вже бачив шаблон - зберігати файли es6 у srcкаталозі та створювати свої речі в попередній публікації npm до libкаталогу.

Вам знадобиться .npmignore файл, схожий на .gitignore, але ігноруючи srcзамість lib.


4
Чи є у вас сховище прикладів?
Ахмед Аббас

2
@JamesAkwuh Зауважте, що ви, ймовірно, захочете змінити команди "start" і "build" в package.json, щоб використовувати відносний шлях babel-cli : ./node_modules/babel-cli/bin/babel.js -s inline -d lib -w src. Це повинно забезпечити, щоб установки не вийшли з ладу під час розгортання в нових середовищах.
phazonNinja

2
@phazonNinja npm обробляє це
Джеймс Аквух

4
"Якщо немає файлу .npmignore, але є файл .gitignore, то npm буде ігнорувати матеріал, який відповідає файлу .gitignore." Офіційні документи npm
Франк Нокк

10
Замість цього .npmignoreви можете використовувати filesполе в package.json . Це дозволяє точно вказати файли, які ви хочете опублікувати, замість того, щоб шукати випадкові файли, які ви не бажаєте публікувати.
Дан Даскалеску

76

Мені подобається відповідь Хосе. Я помітив, що декілька модулів вже слідують цій схемі. Ось як ви можете легко реалізувати це за допомогою 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"]
  }
}

29
Будь-які команди у scriptsзаповіті node_modules/.binдодані до їх, $PATHа оскільки babel-cliвстановлює двійковий node_modules/.bin/babelкод, немає необхідності посилатися на команду по шляху.
Сукіма

3
Зауважте, що prepublishце проблематично, оскільки воно може працювати під час встановлення ( github.com/npm/npm/isissue/3059 ), надайте перевагу більш ідіоматичному versionгачку сценарію ( docs.npmjs.com/cli/version )
mattecapu

@mattecapu, здається, проблема з проблемою prepublishвсе ще існує. На даний момент я думаю вручну скласти srcкаталог і npm publishце шлях.
sonlexqt

1
Ви можете використовувати prepublishOnlyгачок сценарію (див. Docs.npmjs.com/misc/scripts#prepublish-and-prepare ). Зауважте, що у версії 5 npm це має функціонувати як очікувалося, але поки що (припускаючи, що ви використовуєте npm v4 +) це має працювати.
Алекс Манн

1
@FrankNocke prepublish працює до publish(obv.), Який виштовхує матеріал до npm (або куди б ви не налаштували). Тож це для побудови того, що йде в пакеті NPM, навіть якщо він не зареєстрований.
Саймон Бучан

42

TL; DR - Не до ~ жовтня 2019 року Node.js модулі Team НЕ запитав :

Будь ласка, не публікуйте жодних пакетів модулів ES, призначених для використання Node.js до [жовтня 2019 року]

2019 травень оновлення

З 2015 року, коли було задано це запитання, підтримка модулів JavaScript значно визріла, і, сподіваємось, офіційно буде стабільною у жовтні 2019 року. Усі інші відповіді застаріли або надмірно ускладнюються. Ось поточна ситуація та найкраща практика.

Підтримка ES6

99% ES6 (він же 2015) підтримується Node з версії 6 . Поточна версія Node складає 12. Усі вічнозелені браузери підтримують переважну більшість функцій ES6. ECMAScript зараз на версії 2019 року , а схема версій тепер надає перевагу використанню років.

Модулі ES (він же модулі ECMAScript) у браузерах

Все вічнозелені браузери були підтримувати import -ву модулі ES6 оскільки 2017. Динамічні імпорту будуть підтримуватися з допомогою Chrome (+ вила , як Opera і Samsung Інтернет) і Safari. Підтримка Firefox призначена для наступної версії 67.

Вам більше не потрібен Webpack / rollup / посилка тощо для завантаження модулів. Вони можуть бути корисні для інших цілей, але не потрібно завантажувати ваш код. Ви можете безпосередньо імпортувати URL-адреси, що вказують на код модулів ES.

Модулі ES в Node

ES модулі ( .mjsфайли з import/ export) підтримуються з Node 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 }синтаксису import ( ) (який ще не працює з більшістю пакетів npm) та синтаксисом імпорту за замовчуванням ( import Package from 'package'), який працює, але не тоді, коли Babel аналізує його для пакетів, автором яких є TypeScript (graphql-інструменти, приплив вузлів, швидкість і т. д.) Однак існує рішення, яке працює і з, --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 так , що він може бути імпортований безпосередньо, без Бабеля та інших transpilers, просто вказати mainполе в package.jsonв .mjsфайл, але опустити розширення:

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

Це єдина зміна. Опустивши розширення, Node спочатку шукатиме файл mjs, якщо він працює з --experimental-модулями. В іншому випадку він повернеться до файлу .js, тож ваш існуючий процес транспіляції для підтримки старих версій вузла буде працювати, як і раніше - просто переконайтеся, що наведіть Babel на .mjsфайл (и).

Ось джерело для рідного модуля ES із сумісністю для зворотного для Node <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-module:

node --experimental-modules test.mjs

TypeScript

Якщо ви розробляєте TypeScript, ви можете генерувати код ES6 та використовувати модулі ES6:

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

Тоді вам потрібно перейменувати *.jsвихід на .mjsвідому проблему, яка, сподіваємось, незабаром виправиться, тому tscзможе виводити .mjsфайли безпосередньо.


3
Кажучи: "Усі вічнозелені браузери підтримують переважну більшість функцій ES6". це не означає багато, коли ви дивитесь на дані та розумієте, що підтримка es6 у браузерах досягає лише приблизно 80% усіх користувачів.
Педро Педроса

3
В даний час екосистема, безумовно, для цього недостатньо зріла. Команда Node.js з випуском v12 спеціально попросила: "Будь ласка, не публікуйте жодних пакетів модулів ES, призначених для використання Node.js, поки це не буде вирішено." 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 року, що є найбільш раннім, і я серйозно перегляну це.
цедизайндизайн

3
@thisismydesign: дякую за нагадування про оновлення цієї старої відповіді! Щойно зробили так.
Дан Даскалеску

17

@Jose має рацію. Немає нічого поганого в публікації 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).


1
Замість цього .npmignoreви можете використовувати filesполе в package.json . Це дозволяє точно вказати файли, які ви хочете опублікувати, замість того, щоб шукати випадкові файли, які ви не бажаєте публікувати.
Дан Даскалеску

Не хотіли цього розбиття деревом?
Джо

Я не рекомендую використовувати .npmignore, спробуйте package.json«и filesзамість цього, див: github.com/c-hive/guides/blob/master/js / ...
thisismydesign

@thisismydesign: саме це я і рекомендував у своєму коментарі вище ..?
Дан Даскалеску

Моє погано, не помітили :)
thisismydesign

8

Після підходу Хосе і Маріуса (з оновленням останньої версії Babel в 2019 році): Зберігайте останні файли JavaScript у каталозі src та створюйте prepublishсценарій npm та виводите його в каталог 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і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

Як би ви інтегрували пакет зшивання?
kyw

1
@kyw, про те, як інтегрувати пакет пакету, див. мою оновлену відповідь.
Юйци

Оновлення грудня 2019 року -> github.com/rollup/rollup/blob/…
a.barbieri

6

Якщо ви хочете побачити це в дії в дуже простому невеликому модулі Node з відкритим кодом, тоді подивіться на n-й день (який я почав - також інші учасники). Подивіться у файл package.json та на крок перед публікацією, який призведе вас до того, де і як це зробити. Якщо ви клонуєте цей модуль, ви можете запустити його локально і використовувати його як шаблон для себе.


4

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';

Ми провели розслідування щодо підтримки ESM у 05.2019 р. І виявили, що для багатьох бібліотек бракує підтримки (звідси рекомендація щодо зворотної сумісності):


Я не можу імпортувати глобально встановлені модулі ES6 у папку node_modules (надається npm root -g). Невже ми не повинні цього зробити? Я справді розгублений. Я знаю, що посилання npm може вирішити проблему, прив’язавши модуль до моєї локальної папки node_modules, але я хочу знати, чому імпорт глобальних модулів вузлів не підтримується.
Йоаким Л. Крістіансен

Відповідаючи на себе, я думаю, що його ніколи не підтримають: github.com/nodejs/node-eps/blob/master/… Хоча це дійсно дурне рішення, його легко підтримати ...
Йоаким Л. Крістіансен

3

Два критерії пакету NPM полягають у тому, що він може бути використаний лише з a require( 'package' ) і робить щось із програмного забезпечення.

Якщо ви виконаєте ці дві вимоги, ви можете робити все, що завгодно. Навіть якщо модуль написаний на ES6, якщо кінцевому користувачеві цього не потрібно знати, я би зараз його переклав, щоб отримати максимальну підтримку.

Однак, якщо, наприклад, koa , ваш модуль вимагає сумісності з користувачами, що використовують функції ES6, то, можливо, два пакетні рішення будуть кращою ідеєю.

Винос

  1. Публікуйте лише стільки коду, скільки потрібно для require( 'your-package' )роботи.
  2. Якщо між ES5 і 6 питання не стосується користувача, опублікуйте лише 1 пакет. Перекладіть, якщо потрібно.

1
Схоже, це не відповідає на питання. Я думаю, що ОП намагається розібратися, як структурувати їх репортаж у Github і що публікувати в NPM, і все, що ти сказав, - це те, що вони можуть робити все, що завгодно. ОП хоче конкретних рекомендацій щодо належної практики у цій ситуації.
jfriend00

@ jfriend00 Я не згоден. Я рекомендував йому перекладати та публікувати лише ті файли, які потрібні для require( 'package' )роботи. Я відредагую свою відповідь, щоб зробити це більш зрозумілим. Однак, відповідь Хосе набагато краща, ніж моя власна.
JoshWillik

Відповідь Хосе дуже хороша, але я вдячний за те, що явно окреслював хороші правила, коли / навіщо використовувати один проти двох пакетів.
Джордан Грей

3

Основний ключ у 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


Цей пост ухиляється від того, що ви можете (і повинні) публікувати модулі NPM, автором яких є ES6 та importздатні безпосередньо, не вимагаючи Babel або будь-яких інших транспіляторів.
Дан Даскалеску

0

Залежно від анатомії вашого модуля це рішення може не працювати, але якщо ваш модуль є міститься в одному файлі і не має залежностей (не використовує імпорт ), використовуючи наступний зразок, ви можете випустити свій код таким, який він є , і їх можна буде імпортувати при імпорті (Модулі браузера 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 */
    }

Щоб використовувати модуль, тепер ви можете використовувати звичайний синтаксис ...

При імпорті в NODE ...

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

При імпорті в BROWSER ...

    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>

-1

Кілька додаткових приміток для всіх, хто використовує власні модулі безпосередньо з github, не переглядаючи опубліковані модулі:

( Широко використовуваний ) гачок перед публікацією нічого не робить для вас.

Найкраще, що можна зробити (якщо планується покладатися на репозитори Github, а не опубліковані речі):

  • unlist srcвід .npmignore (іншими словами: дозволити). Якщо у вас немає .npmignore, пам’ятайте: Копію.gitignore замість цього буде встановлена у встановленому місці якls node_modules/yourProject покаже вам.
  • переконайтесь, babel-cli це надійність у вашому модулі, а не лише devDepenceny, оскільки ви справді будуєте на споживчій машині, також на комп'ютері розробників додатків, який використовує ваш модуль
  • виконайте збірку, у гачку встановлення, тобто:

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

(жодної додаткової цінності при спробі чогось "встановити", тобто babel-cli може бути відсутнім)


3
Компіляція при встановленні дуже груба. Будь ласка, не робіть цього - час встановлення npm досить погано! Для внутрішнього коду, де ви хочете уникнути використання сховища пакетів npm, ви можете: 1) використовувати монорепо, 2) Завантажувати та залежати від npm packвиводу, 3) перевіряти вихід у збірці.
Саймон Бучан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.