8.5 KiB
Advanced Neutrino Customization
No two JavaScript projects are ever the same, and as such there may be times when you will need to make modifications to the way your Neutrino preset is building your project. If you need more customization than can be afforded by augmenting your project's package.json, consider using this advanced configuration guide to modify your build as needed.
Creating a project-specific override
Neutrino configurations are backed by webpack-chain, a library for making modifications to a Webpack configuration using a fluent or chained API. When your project needs more advanced build overrides, you will be interacting with this API in order to perform modifications.
First, we need to create a project-specific override to make these changes. This can either be a JS file or a directory
with an index.js
file. Since Neutrino uses Node.js and Webpack for interacting with presets, it is helpful to
understand that this is a Node.js module. By exporting a middleware function from your module, you will be provided with
a Neutrino instance for modifying the build. Let's create a file called override.js
in the root of our example
project:
// override.js
module.exports = neutrino => {
// ...
};
The signature of this function is what we call "Neutrino middleware". If you're familiar with middleware from the Express/connect world, this works similarly. When using Express middleware, you provide a function to Express which receives arguments to modify a request or response along its lifecycle. There can be a number of middleware functions that Express can load, each one potentially modifying a request or response in succession.
When you customize Neutrino with an override, you export a Neutrino middleware function, except this is typically used to override Neutrino's configuration. Every preset or middleware that Neutrino has loaded follows this same middleware pipeline.
At the moment our custom override isn't doing anything, but it does get us far enough to be able to tell Neutrino
to use it for additional configuration. Modify your package.json and add override.js
as an additional preset.
Note: Neutrino will attempt to load this module relative to the current working directory, which should be the root of your project.
{
"config": {
"use": [
"neutrino-preset-react",
"neutrino-preset-karma",
"override.js"
]
},
"scripts": {
"build": "neutrino build"
}
}
Other than actually changing the build configuration or Neutrino options, that is all the setup necessary for Neutrino to pick up your custom changes.
Overriding Neutrino options
Neutrino has a number of useful options for customizing its behavior, as well as the behavior of presets and middleware.
You can override these options using an object at neutrino.options
.
Important! Some of these options are used by presets and middleware to determine their path locations for building. Making changes to these in advanced configuration will usually mean needing to set Neutrino options before other presets are loaded, and overriding build configuration after the presets have loaded. Consider using simple customization for setting Neutrino options and preset/middleware-specific options.
options.root
Set the base directory which Neutrino middleware and presets operate on. Typically this is the project directory where
the package.json would be located. If the option is not set, Neutrino defaults it to process.cwd()
. If a relative
path is specified, it will be resolved relative to process.cwd()
; absolute paths will be used as-is.
module.exports = neutrino => {
// if not specified, defaults to process.cwd()
neutrino.options.root;
// relative, resolves to process.cwd() + ./website
neutrino.options.root = './website';
// absolute
neutrino.options.root = '/code/website';
};
options.source
Set the directory which contains the application source code. If the option is not set, Neutrino defaults it to src
.
If a relative path is specified, it will be resolved relative to options.root
; absolute paths will be used as-is.
module.exports = neutrino => {
// if not specified, defaults to options.root + src
neutrino.options.source;
// relative, resolves to options.root + ./lib
neutrino.options.source = './lib';
// absolute
neutrino.options.source = '/code/website/lib';
};
options.output
Set the directory which will be the output of built assets. If the option is not set, Neutrino defaults it to build
.
If a relative path is specified, it will be resolved relative to options.root
; absolute paths will be used as-is.
module.exports = neutrino => {
// if not specified, defaults to options.root + build
neutrino.options.output;
// relative, resolves to options.root + ./dist
neutrino.options.output = './dist';
// absolute
neutrino.options.output = '/code/website/dist';
};
options.tests
Set the directory that contains test files. If the option is not set, Neutrino defaults it to test
.
If a relative path is specified, it will be resolved relative to options.root
; absolute paths will be used as-is.
module.exports = neutrino => {
// if not specified, defaults to options.root + test
neutrino.options.tests;
// relative, resolves to options.root + ./testing
neutrino.options.tests = './testing';
// absolute
neutrino.options.tests = '/code/website/testing';
};
options.entry
Set the main entry point for the application. If the option is not set, Neutrino defaults it to index.js
.
If a relative path is specified, it will be resolved relative to options.source
; absolute paths will be used as-is.
module.exports = neutrino => {
// if not specified, defaults to options.source + index.js
neutrino.options.entry;
// relative, resolves to options.source + ./entry.js
neutrino.options.entry = './entry.js';
// absolute
neutrino.options.entry = '/code/website/lib/entry.js';
};
options.node_modules
Set the directory which contains the Node.js modules of the project. If the option is not set, Neutrino defaults it to
node_modules
. If a relative path is specified, it will be resolved relative to options.root
; absolute paths will be
used as-is.
module.exports = neutrino => {
// if not specified, defaults to options.root + node_modules
neutrino.options.node_modules;
// relative, resolves to options.root + ./modules
neutrino.options.node_modules = './modules';
// absolute
neutrino.options.node_modules = '/code/website/modules';
};
Middleware or preset options
Some middleware and presets also have their own custom options. Consult the documentation for the package for specific details on its customization.
Overriding build configuration
The Neutrino instance provided to your custom configurator has a config
property that is an instance of
webpack-chain. We won't go in-depth of all the configuration
possibilities here, but encourage you to check out the documentation for webpack-chain for instruction on your
particular use case. Just know that you can use webpack-chain to modify any part of the underlying Webpack configuration
using its API.
This neutrino.config
is an accumulation of all configuration up to this moment. All Neutrino middleware and presets
interact with and make changes through this config, which is all available to you. For example, if you are using the
presets neutrino-preset-react
and neutrino-preset-karma
, any config set can be extended, manipulated, or removed.
Example: Neutrino's React preset adds .jsx
as a module extension. Let's remove it.
module.exports = neutrino => {
neutrino.config.resolve.extensions.delete('.jsx');
};
Example: Neutrino's Node.js preset uses babel-preset-env
to support Node.js v6.9. Let's change it to support back to
v4.2. This preset has a rule named "compile" and a loader named "babel".
module.exports = neutrino => {
neutrino.config.module
.rule('compile')
.loader('babel', options => {
options.presets[0][1].targets.node = 4.2;
return options;
});
};
Presets can also have their own custom data in addition to the Neutrino config. See your respective preset for details. Again, rather than reiterate the documentation for webpack-chain here, please refer to its documentation for all ways you can modify a config instance to solve your use cases.