javascript - Hvordan publisere en modul skrevet i ES6 til NPM?


Jeg var i ferd med å publisere en modul til NPM, da jeg tenkte på å skrive den om i ES6, både for å gjøre den fremtidssikker og for å lære ES6. Jeg har brukt Babel til å transpile til ES5, og kjøre tester. Men jeg er ikke sikker på hvordan jeg skal gå videre:

  1. Skal jeg transpile, og publisere den resulterende /out-mappen til NPM?
  2. Skal jeg inkludere resultatmappen i Github-repoet mitt?
  3. Eller skal jeg opprettholde 2 repos, en med ES6-koden + gulp-skriptet for Github, og en med de transpilerte resultatene + tester for NPM?

Kort sagt: hvilke trinn må jeg ta for å publisere en modul skrevet i ES6 til NPM, samtidig som jeg fortsatt lar folk bla gjennom/forke den originale koden?

Svar #1

Mønsteret jeg har sett så langt er å beholde ES6-filene i en src-mappe og bygge tingene dine i npm's prepublish til lib-mappen.

Du vil trenge en .npmignore-fil, lignende .gitignore, men som ignorerer src i stedet for lib.

Svar #2

Jeg liker José sitt svar. Jeg har lagt merke til at flere moduler allerede følger det mønsteret. Slik kan du enkelt implementere det med Babel6. Jeg installerer babel-cli lokalt slik at byggingen ikke bryter hvis jeg noen gang endrer min globale Babel-versjon.

.npmignore

/src/

.gitignore

/lib/
/node_modules/

Installer 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"]
  }
}

Svar #3

TL;DR - Ikke gjør det, før omtrent oktober 2019. Node.js-modulteamet har spurt:

Vennligst publiser ikke noen ES-modulpakker ment for bruk med Node.js før [oktober 2019]

Oppdatering mai 2019

Siden 2015 da dette spørsmålet ble stilt, har JavaScript-støtten for moduler modnet betydelig, og forhåpentligvis vil den bli offisielt stabil i oktober 2019. Alle andre svar er nå utdaterte eller altfor kompliserte. Her er den nåværende situasjonen og beste praksis.

ES6-støtte

99 % av ES6 (også kjent som 2015) har blitt støttet av Node siden versjon 6. Den nåværende versjonen av Node er 12. Alle moderne nettlesere støtter det store flertallet av ES6-funksjonene. ECMAScript er nå på versjon 2019, og versjonssystemet favoriserer nå bruk av årstall.

ES-moduler (også kjent som ECMAScript-moduler) i nettlesere

Alle moderne nettlesere har støttet import-ering av ES6-moduler siden 2017. Dynamiske imports støttes av Chrome (+ avleggere som Opera og Samsung Internet) og Safari. Støtte i Firefox er planlagt for neste versjon, 67.

Du trenger ikke lenger Webpack/rollup/Parcel osv. for å laste moduler. De kan fortsatt være nyttige for andre formål, men er ikke nødvendige for å laste koden din. Du kan direkte importere URL-er som peker til ES-moduler kode.

ES-moduler i Node

ES-moduler (.mjs-filer med import/export) har blitt støttet siden Node v8.5.0 ved å kjøre node med --experimental-modules-flagget. Node v12, utgitt i april 2019, omskrev støtten for eksperimentelle moduler. Den mest synlige endringen er at filendelsen må spesifiseres som standard når man importerer:

// lib.mjs 

export const hello = 'Hello world!';

// index.mjs:

import { hello } from './lib.mjs';
console.log(hello);

Merk de påkrevde .mjs-utvidelsene gjennomgående. Kjør som:

node --experimental-modules index.mjs

Node 12-utgivelsen er også når Moduler-teamet ba utviklere om ikke å publisere ES-modulpakker ment for bruk av Node.js før en løsning er funnet for å bruke pakker via både require('pkg') og import 'pkg'. Du kan fortsatt publisere native ES-moduler ment for nettlesere.

Økosystemstøtte for native ES-moduler

Per mai 2019 er økosystemstøtten for ES-moduler umoden. For eksempel støtter ikke test-rammeverk som Jest og Ava --experimental-modules. Du må bruke en transpiler, og må deretter velge mellom å bruke navngitt import (import { symbol }) syntaks (som ennå ikke fungerer med de fleste npm-pakker), og standard import-syntaks (import Package from 'package'), som fungerer, men ikke når Babel parser det for pakker skrevet i TypeScript (graphql-tools, node-influx, faast osv.). Det finnes imidlertid en løsning som fungerer både med --experimental-modules og hvis Babel transpilerer koden din, slik at du kan teste det med Jest/Ava/Mocha osv:

import * as ApolloServerM from 'apollo-server'; const ApolloServer = ApolloServerM.default || ApolloServerM;

Muligens stygt, men på denne måten kan du skrive din egen ES-modulkode med import/export og kjøre den med node --experimental-modules, uten transpileringsverktøy. Hvis du har avhengigheter som ennå ikke er ESM-klare, importer dem som ovenfor, og du vil kunne bruke testverktøy og annen verktøy via Babel.

Forrige svar på spørsmålet - husk, ikke gjør dette før Node løser require/import-problemet, forhåpentligvis rundt oktober 2019.

Publisering av ES6-moduler til npm, med bakoverkompatibilitet

For å publisere en ES-modul til npmjs.org slik at den kan importeres direkte, uten Babel eller andre transpileringsverktøy, pek ganske enkelt main-feltet i din package.json til .mjs-filen, men utelat filtypen:

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

Det er den eneste endringen. Ved å utelate filtypen, vil Node først se etter en mjs-fil dersom den kjøres med --experimental-modules. Ellers vil den falle tilbake til .js-filen, så din eksisterende transpileringsprosess for å støtte eldre Node-versjoner vil fungere som før — bare sørg for å peke Babel til .mjs-filene.

Her er kilden for en native ES-modul med bakoverkompatibilitet for Node < 8.5.0 som jeg publiserte til NPM. Du kan bruke den med en gang, uten Babel eller noe annet.

Installer modulen:

npm install local-iso-dt
or, yarn add local-iso-dt

Opprett en testfil test.mjs:

import { localISOdt } from 'local-iso-dt/index.mjs';
console.log(localISOdt(), 'Starting job...');

Kjør node (v8.5.0+) med flagget --experimental-modules:

node --experimental-modules test.mjs

TypeScript

Hvis du utvikler i TypeScript, kan du generere ES6-kode og bruke ES6-moduler:

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

Deretter må du gi nytt navn til *.js-utdataene til .mjs, et kjent problem som forhåpentligvis blir fikset snart, slik at tsc kan generere .mjs-filer direkte.

Svar #4

@Jose har rett. Det er ingenting galt med å publisere ES6/ES2015 til NPM, men det kan forårsake problemer, spesielt hvis personen som bruker pakken din bruker Webpack, for eksempel, fordi folk vanligvis ignorerer node_modules-mappen mens de forhåndsprosesserer med babel av ytelsesårsaker.

Så, bruk bare gulp, grunt eller rett og slett Node.js for å bygge en lib-mappe som er ES5.

Her er mitt build-lib.js-skript, som jeg holder i ./tools/ (ingen gulp eller grunt her):

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

Her er et siste råd: Du må legge til en .npmignore i prosjektet ditt. Hvis npm publish ikke finner denne filen, vil det bruke .gitignore i stedet, noe som vil forårsake problemer fordi normalt vil .gitignore-filen din ekskludere ./lib og inkludere ./src, noe som er nøyaktig det motsatte av det du vil når du publiserer til NPM. .npmignore-filen har i hovedsak samme syntaks som .gitignore (så vidt jeg vet).

Svar #5

Følger José og Marius sin tilnærming, (med oppdatering av Babel sin siste versjon i 2019): Behold de nyeste JavaScript-filene i en src-katalog, og bygg med npms prepublish-script og output til lib-katalogen.

.npmignore

/src

.gitignore

/lib
/node_modules

Installer Babel (versjon 7.5.5 i mitt tilfelle)

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"
    ]
  }
}

Og jeg har src/index.js som bruker piltasten funksjonen:

"use strict";

let NewOneWithParameters = (a, b) => {
  console.log(a + b); // 30
};
NewOneWithParameters(10, 20);

Her er repositoriet på GitHub.

Nå kan du publisere pakken:

npm publish
...
> [email  prepublish .
> babel src -d lib

Successfully compiled 1 file with Babel.
...

Før pakken publiseres til npm, vil du se at lib/index.js har blitt generert, som er transpilert til es5:

"use strict";

var NewOneWithParameters = function NewOneWithParameters(a, b) {
  console.log(a + b); // 30
};

NewOneWithParameters(10, 20);

[Oppdatering for Rollup-bunter]

Som spurt av @kyw, hvordan ville du integrert Rollup-bundleren?

Først, installer rollup og rollup-plugin-babel

npm install -D rollup rollup-plugin-babel

For det andre, opprett rollup.config.js i prosjektets rotmappe

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/**"
    })
  ]
};

Til slutt, oppdater prepublish i package.json

{
  ...
  "scripts": {
    "prepublish": "rollup -c"
  },
  ...
}

Nå kan du kjøre npm publish, og før pakken publiseres til npm, vil du se at lib/index.js har blitt generert, som er transpilt til es5:

'use strict';

var NewOneWithParameters = function NewOneWithParameters(a, b) {
  console.log(a + b); // 30
};

NewOneWithParameters(10, 20);

Merk: forresten, du trenger ikke lenger @babel/cli hvis du bruker Rollup-bundleren. Du kan trygt avinstallere den:

npm uninstall @babel/cli

Relaterte artikler