@neutrinojs/library
is a Neutrino preset that supports creating JS libraries for Web or Node.js targets.
- Zero upfront configuration necessary to start developing and building a JavaScript library
- Modern Babel compilation supporting ES modules, async functions, and dynamic imports
- Defaults to UMD-based output for consumption in a variety of environments
- Supports automatically-wired sourcemaps
- Tree-shaking to create smaller bundles
- Automatically marks dependencies as external
- Easily extensible to customize your library as needed
- Node.js v6.10+
- Yarn or npm client
- Neutrino v8
@neutrinojs/library
can be installed via the Yarn or npm clients. Inside your project, make sure
neutrino
and @neutrinojs/library
are development dependencies.
❯ yarn add --dev neutrino @neutrinojs/library
❯ npm install --save-dev neutrino @neutrinojs/library
If you want to have automatically wired sourcemaps added to your project, add source-map-support
:
❯ yarn add source-map-support
❯ npm install --save source-map-support
@neutrinojs/library
follows the standard project layout specified by Neutrino. This
means that by default all library source code should live in a directory named src
in the root of the
project. This includes JavaScript files that would be available to your compiled project.
After installing Neutrino and the Library preset, add a new directory named src
in the root of the project, with
a single JS file named index.js
in it.
❯ mkdir src && touch src/index.js
Edit your src/index.js
file with the following:
// Create a simple logger library that only logs when
// debug mode is enabled, and logs to the appropriate level
export default class Logger {
constructor(level, debug = true) {
this.level = level;
this.debug = debug;
}
log(...args) {
if (this.debug) {
console[level](...args);
}
}
}
Now edit your project's package.json to add commands for building the library.
{
"name": "super-logger",
"scripts": {
"build": "neutrino build"
}
}
When using this library, you must use a .neutrinorc.js
file in your project to specify
the library name. Add this preset to your use
array and define the name
option:
module.exports = {
use: [
['@neutrinojs/library', { name: 'Logger' }]
]
};
You can now build your library!
❯ yarn build
❯ npm start
@neutrinojs/library
builds assets to the build
directory by default when running neutrino build
. Using the
quick start example above as a reference:
❯ yarn build
Build completed in 0.419s
Hash: 89e4fb250fc535920ba4
Version: webpack 3.8.1
Time: 424ms
Asset Size Chunks Chunk Names
Logger.js 4.29 kB 0 [emitted] index
Logger.js.map 3.73 kB 0 [emitted] index
✨ Done in 1.51s.
You should specify a main
property in your package.json pointing to your primary built main entry point. Also when
publishing your project to npm, consider excluding your src
directory by using the files
property to whitelist
build
, or via .npmignore
to blacklist src
.
{
"name": "super-logger",
"main": "build/index.js",
"files": [
"build"
]
}
Your built library can now be consumed with ES imports, CJS require, AMD require, or even script tags:
// ES imports
import Logger from 'super-logger';
// CJS require
const Logger = require('super-logger');
// AMD require
require(['super-logger'], (Logger) => {
// ...
});
<!-- script tags -->
<script src="/path/to/super-logger"></script>
<!--
once published to npm, you can even use a script tag
to point to unpkg
-->
<script src="https://unpkg.com/super-logger"></script>
You can provide custom options and have them merged with this preset's default options to easily affect how this
preset builds. You can modify Library preset settings from .neutrinorc.js
by overriding with an options object. Use
an array pair instead of a string to supply these options in .neutrinorc.js
.
The following shows how you can pass an options object to the Library preset and override its options, showing the defaults where applicable:
module.exports = {
use: [
['@neutrinojs/library', {
// REQUIRED. Sets the name of the library, along with its
// output filename.
name: '<YOUR_LIBRARY_NAME>',
// Compile the library for use in a specific environment.
// Defaults to 'web', but can be switched to 'node' as well
target: 'web',
// Configure how the library will be exposed. Keeping this set
// to 'umd' ensures compatibility with a large number of module
// systems, but you can override if you want to produce a smaller
// bundle targeted to specific module systems like 'commonjs2' (CJS).
libraryTarget: 'umd',
// Override options passed to webpack-node-externals,
// such as whitelisting dependencies for bundling.
externals: {},
polyfills: {
// Enables fast-async polyfill. Set to false to disable
async: true
},
// Remove the contents of the output directory prior to building.
// Set to false to disable cleaning this directory
clean: {
paths: [neutrino.options.output]
},
// Add additional Babel plugins, presets, or env options
babel: {
// Override options for babel-preset-env
presets: [
['babel-preset-env', {
// Passing in browser targets to babel-preset-env will replace them
// instead of merging them when using the 'web' target
targets: {
browsers: [
'last 1 Chrome versions',
'last 1 Firefox versions'
]
}
}]
]
}
}]
]
};
Example: Override the library Babel compilation target to Node.js v8 and commonjs2 module:
module.exports = {
use: [
['@neutrinojs/library', {
name: 'Logger',
target: 'node',
libraryTarget: 'commonjs2',
// Add additional Babel plugins, presets, or env options
babel: {
// Override options for babel-preset-env
presets: [
['babel-preset-env', {
targets: {
node: '8.0'
}
}]
]
}
}]
]
};
To override the build configuration, start with the documentation on customization.
@neutrinojs/library
creates some conventions to make overriding the configuration easier once you are ready to make
changes.
By default Neutrino, and therefore this preset, creates a single main index
entry point to your library, and this
maps to the index.*
file in the src
directory. This means that this preset is optimized toward a single main entry
to your library. Code not imported in the hierarchy of the index
entry will not be output to the bundle. To overcome
this you must either define more mains via options.mains
, import
the code path somewhere along the index
hierarchy, or define multiple configurations in your .neutrinorc.js
.
This preset automatically marks all dependencies as external to your library, meaning that
any dependencies you import will not be bundled with your library. This helps prevent
your library from bloating, but means users of your library will be installing or using
your dependencies as defined in package.json. You can override this in the library preset
options by passing further options to the externals
property. This accepts an options
object format defined by
webpack-node-externals,
to which you can provide a whitelist
value.
The whitelist will override which dependencies are bundled in your library. Any dependency not matched by this whitelist is considered a peer of your library, and will not be bundled.
Example: The following example library redux-example
has the following package.json, marking
redux
and mitt
as dependencies, but only lists mitt
in whitelist. This bundles mitt
along
with the library, so the library consumer does not need to explicitly import/require/script it prior.
The redux
dependency is not in the whitelist, so it will not be bundled, and is considered a peer
of redux-example
.
{
"name": "redux-example",
"version": "1.0.0",
"main": "build/reduxExample.js",
"scripts": {
"build": "neutrino build"
},
"dependencies": {
"mitt": "*",
"redux": "*"
},
"devDependencies": {
"neutrino": "*",
"@neutrinojs/library": "*"
}
}
module.exports = {
use: [
['@neutrinojs/library', {
name: 'reduxExample',
externals: {
whitelist: ['mitt']
}
}]
]
};
// ES imports
import { createStore } from 'redux';
import reduxExample from 'redux-example';
// CJS require
const { createStore } = require('redux');
const reduxExample = require('redux-example');
// AMD require
require(['redux', 'redux-example'], ({ createStore }, reduxExample) => {
// ...
});
<!-- script tags -->
<script src="/path/to/redux"></script>
<script src="/path/to/redux-example"></script>
<script>
const { createStore } = window.redux;
window.reduxExample // ...
</script>
<!--
once published to npm, you can even use a script tag
to point to unpkg
-->
<script src="https://unpkg.com/redux"></script>
<script src="https://unpkg.com/redux-example"></script>
<script>
const { createStore } = window.redux;
window.reduxExample // ...
</script>
The @neutrinojs/library
middleware can be used in conjunction with the
@neutrinojs/fork
middleware to generate multiple library outputs
when building. Follow the instructions to install the fork middleware, and change your .neutrinorc.js
format as follows:
const name = 'Logger';
module.exports = {
use: [
(neutrino) => {
neutrino.on('prebuild', () => neutrino.use('@neutrinojs/clean'));
},
['@neutrinojs/fork', {
configs: {
// Create a named entry for each build type.
// You will most likely want to disable cleaning
// the output directory until prior to building
umd: ['@neutrinojs/library', { name, clean: false }],
commonjs2: ['@neutrinojs/library', { name, clean: false }]
}
}]
]
};
To override the build configuration, start with the documentation on customization.
@neutrinojs/library
creates some conventions to make overriding the configuration easier once you are ready to make
changes.
By default Neutrino, and therefore this preset, creates a single main index
entry point to your library, and this
maps to the index.*
file in the src
directory. This means that this preset is optimized toward a single main entry
to your library. Code not imported in the hierarchy of the index
entry will not be output to the bundle. To overcome
this you must either define more mains via options.mains
, import the code path
somewhere along the index
hierarchy, or define multiple configurations in your .neutrinorc.js
.
The following is a list of rules and their identifiers which can be overridden:
Name | Description | Environments and Commands |
---|---|---|
compile |
Compiles JS files from the src directory using Babel. Contains a single loader named babel |
all |
worker |
Allows importing Web Workers automatically with .worker.* extensions. Contains a single loader named worker . |
all |
The following is a list of plugins and their identifiers which can be overridden:
Note: Some plugins are only available in certain environments. To override them, they should be modified conditionally.
Name | Description | Environments and Commands |
---|---|---|
banner |
Injects source-map-support into the main entry points of your application if detected in dependencies or devDependencies of your package.json. |
Only when source-map-support is installed |
clean |
Clears the contents of build prior to creating a production bundle. |
build command |
minify |
Minifies source code using BabelMinifyWebpackPlugin . From @neutrinojs/minify . |
NODE_ENV production |
module-concat |
Concatenate the scope of all your modules into one closure and allow for your code to have a faster execution time in the browser. | NODE_ENV production |
By following the customization guide and knowing the rule, loader, and plugin IDs above,
you can override and augment the build by by providing a function to your .neutrinorc.js
use array. You can also
make these changes from the Neutrino API in custom middleware.
Example: Allow importing modules with a .esm
extension.
module.exports = {
use: [
['@neutrinojs/library', { /* ... */ }],
(neutrino) => {
neutrino.config.resolve.extensions.add('.esm')
}
]
};
This preset is part of the neutrino-dev repository, a monorepo containing all resources for developing Neutrino and its core presets and middleware. Follow the contributing guide for details.