@silverstripe/webpack-config
v3.0.0-alpha2
Published
SilverStripe config files for modules
Downloads
1,843
Readme
webpack-config
This NPM package provides a shared common webpack configuration used across all Silverstripe modules, this aims to reduce thirdparty module developer fatigue by having a source of truth for configurations and settings used in SilverStripe's webpack.
What this package gives you
For JS
- JavascriptWebpackConfig This class provides the default webpack config most modules will use for transpiling javascript, along with methods for customisation. It automatically includes all of the configuration listed in Advanced below.
Advanced
- externals.js: Provides references to packages that are provided by silverstripe-admin or another core silverstripe module. This will tell your webpack to not include the package in your output file, that it is provided and accessible through a global variable to keep your transpiled bundle smaller.
- modules.js: The common list of loaders for javascript which webpack should use to get a standard output build, such as babel and modernizr.
- plugins.js: Plugins used by webpack, such as:
- A global
Provide
call forjQuery
- The environment variable
process.env.NODE_ENV
to exclude debug functions in production builds - Webpack Bundle Analyzer plugin to aid profiling
- A global
- resolve.js: Provides common ways to resolve a package in your src files, so that you reduce the number of relative path imports.
For CSS
- CssWebpackConfig This class provides the default webpack config most modules will use for transpiling sass to css, along with methods for customisation. It automatically includes all of the configuration listed in Advanced below.
Advanced
- modules.js: The common list of loaders for stylesheets to convert
*.scss
files to a css output file, including exporting images and fonts. - plugins.js: Plugin for webpack to extract the stylesheets into a proper css file.
Usage
The following keys can be used in the PATHS object whenever one is required as a parameter. Note that the default values only apply when using the abstraction classes.
|Key|Description|Required|Default|
|---|---|---|---|
|ROOT|The root path, where your webpack.config.js
file is located|yes|No default - error if missing|
|SRC|The absolute path to your source files|only for advanced usage|`${PATHS.ROOT}/client/src`
|
|DIST|The absolute path to the directory you want to output files to|no|`${PATHS.ROOT}/client/dist`
|
|MODULES|The path (relative to ROOT
, or an absolute path) to your node_modules
folder|only for advanced usage|'node_modules'
|
|THIRDPARTY|The path (relative to ROOT
, or an absolute path) to your thirdparty folder containing copies of packages which wouldn't be available on NPM|no|No default|
Using the abstractions
This library includes JavascriptWebpackConfig
and CssWebpackConfig
classes to abstract some of the webpack configuration, so it's easier to standardise config across all of your Silverstripe modules.
Javascript
To use all of the default configuration for javascript transpilation, instantiate a new JavascriptWebpackConfig
object.
This class's constructor takes a name
string argument (used in the webpack console output and for debugging) and a PATHS
object. It also has a third argument (moduleName
) which is only needed for core and supported Silverstripe modules and should be set to the name of the module (e.g. silverstripe/admin
).
You must set your entry points by passing a valid entry object to the setEntry()
method. This uses the normal syntax for webpack entry
.
Finally, you get the actual webpack config by calling getConfig()
.
Sass
The API for getting a webpack config to transpile sass to css is very similar to geting javascript webpack config. You start by instantiating a new CssWebpackConfig
object.
CssWebpackConfig
takes the same arguments as JavascriptWebpackConfig
(except for moduleName
) - but it also takes an optional filename
argument. The filename
ultimately gets passed to a MiniCssExtractPlugin
. Its default value is "styles/[name].css"
Example
This is a minimal example of using this library to build your webpack configuration. It transpiles client/src/js/main.js
to client/dist/js/main.js
and client/src/styles/main.scss
to client/dist/styles/main.css
.
The css transpilation also includes outputting any referenced fonts to client/dist/fonts/
and any referenced images larger than 10kb to client/dist/images/
.
const Path = require('path');
const { JavascriptWebpackConfig, CssWebpackConfig } = require('@silverstripe/webpack-config');
const PATHS = {
ROOT: Path.resolve(),
};
module.exports = [
new JavascriptWebpackConfig('js', PATHS)
.setEntry({
main: 'js/main.js'
})
.getConfig(),
new CssWebpackConfig('css', PATHS)
.setEntry({
main: 'styles/main.scss'
})
.getConfig(),
];
Customising abstracted configuration
JavascriptWebpackConfig
and CssWebpackConfig
are subclasses of BaseWebpackConfig
, which provides a couple of methods for customising the resulting config.
splitVendor
splitVendor()
uses the SplitChunksPlugin to separate out vendor code into its own file. This method takes two arguments.
The first argument (vendorChunk
) is the name of the chunk to be split out. This is used in the name
portion for output filenames. For example, if the output filename is [name].bundle.js
and vendorChunk
is vendor
, the name of the file will be vendor.bundle.js
. The default value for vendorChunk
is vendor
. Note that this can be the name of one of your entry points, in which case the vendor modules will be included in the same file as the transpiled javascript for that entrypoint.
The second argument (test
) is the regular expression or function that determines which modules are included in this chunk. See the webpack docs for more information. The default value for test
is /[\\/]node_modules[\\/]/
.
mergeConfig
mergeConfig()
allows you to merge your own raw webpack configuration into the configuration created by the abstractions. This will also override any default configuration which uses the same keys.
It takes a single webpack configuration object as an argument.
Additional customisation
Most customisation will be achievable with one of the above two methods - but you might have a really specific use case where you want to dome something that can't be achieved with merging config (e.g. remove or replace one of the default plugins). In that case, you can manipulate the final configuration after calling getConfig()
- since that method gives you the actual webpack configuration object the abstractions produced.
In that case however you may find you are better served by avoiding the abstractions, and building your configuration the advanced way.
Example
This example includes several customisations (explained with comments in the example) of the abstracted configuration. It transpiles js/src/main.js
to js/dist/main.bundle.js
(with a separate vendor bundle in js/dist/vendor.bundle.js
) and css/src/main.scss
to css/dist/main.css
. Fonts referenced in css are output to fonts/
and images to images/
.
Note that in this example we use mergeConfig()
to merge an output object to change the name of the transpiled javascript files, but for css we pass the name into the CssWebpackConfig
constructor. This is because sass to css transpilation is using MiniCssExtractPlugin
, which is in control of the output name of the css. If you try to change the name of css files using output.filename
, you'll get errors (you can still any other output
configuration via mergeConfig()
though).
const Path = require('path');
const webpack = require('webpack');
const { JavascriptWebpackConfig, CssWebpackConfig } = require('@silverstripe/webpack-config');
const PATHS = {
ROOT: Path.resolve(),
SRC: Path.resolve(),
};
const config = [
// Use a different DIST directory for js than is used for css
new JavascriptWebpackConfig('js', { ...PATHS, DIST: `${PATHS.ROOT}/js/dist` })
.setEntry({
main: 'js/src/main.js'
})
// Output the javascript with a different filename schema than the default
.mergeConfig({
output: {
filename: '[name].bundle.js',
},
})
// Split any vendor modules out into a separate `vendor.bundle.js` file
.splitVendor()
.getConfig(),
// Use a different DIST directory for css than is used for js, and output the css with a
// different filename schema than the default
new CssWebpackConfig('css', { ...PATHS, DIST: `${PATHS.ROOT}/css/dist` }, '[name].bundle.css')
.setEntry({
main: 'css/src/main.scss'
})
// Copy some files at the same time as transpiling the css
.mergeConfig({
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: `${PATHS.ROOT}/some-extra-files`,
to: `${PATHS.ROOT}/extra-files-output`
},
],
}),
],
})
.getConfig(),
];
module.exports = config;
Advanced usage
There may be situations where you want to make complex modifications to the default webpack configuration generated by the abstractions provided in this library - or where you want to have your configuration more explicitly declared in your webpack.config.js
file. In those cases, you can bypass the abstractions completely.
Example
This is a minimal example of how to build a webpack configuration array for Silverstripe modules without using the abstraction classes. It produces the exact same configuration (and therefore the same output files) as the example using the abstractions above.
const Path = require('path');
const {
resolveJS,
externalJS,
moduleJS,
pluginJS,
moduleCSS,
pluginCSS,
} = require('@silverstripe/webpack-config');
const ENV = process.env.NODE_ENV;
// All of the keys are required in your PATHS object except DIST and THIRDPARTY
// Be aware that there is no validation for this - you may not get errors if you are missing
// some of this config, but you will likely get unexpected output
const PATHS = {
ROOT: Path.resolve(),
SRC: Path.resolve('client/src'),
DIST: Path.resolve('client/dist'),
MODULES: 'node_modules',
THIRDPARTY: 'thirdparty',
};
module.exports = [
{
name: 'js',
entry: {
main: 'js/main.js'
},
output: {
path: PATHS.DIST,
filename: 'js/[name].js',
},
devtool: (ENV !== 'production') ? 'source-map' : false,
resolve: resolveJS(ENV, PATHS),
externals: externalJS(ENV, PATHS),
module: moduleJS(ENV, PATHS),
plugins: pluginJS(ENV, PATHS),
},
{
name: 'css',
entry: {
main: 'styles/main.scss'
},
// Just like with the abstractions, we don't include output.filename, because the filename
// is handled by MiniCssExtractPlugin
output: {
path: PATHS.DIST,
},
devtool: (ENV !== 'production') ? 'source-map' : false,
module: moduleCSS(ENV, PATHS),
// Pass the filename here, which will get passed down to MiniCssExtractPlugin
plugins: pluginCSS(ENV, PATHS, 'css/[name].css'),
},
];
Customising raw configuration
Because you're dealing with a raw webpack configuration object already, it can be easier to customise than the abstracted config.
For example to add another external module to the externals configuration, merge your externals configuration with externalJS()
(you could also achieve this using mergeConfig()
with the abstractions):
module.exports = [
{
name: 'js',
//...
external: Object.assign({},
externalJS(ENV, PATHS),
{
'components/MyCustomComponent': 'MyCustomComponent',
}
),
},
];
Or to modify the directory for images from images/
to assets/
, you can modify rule.generator.filename
for the appropriate rule in moduleCSS()
(which you cannot achieve using the abstractions without modifying the configuration object after calling getConfig()
):
const cssModules = moduleCSS(ENV, PATHS);
for (let rule of cssModules.rules) {
if (rule.test === '/\.(png|gif|jpe?g|svg)$/') {
rule.generator.filename = 'assets/[name][ext]';
}
}
module.exports = [
{
name: 'css',
//...
module: cssModules,
},
];