SharePoint Framework–extending build pipeline with custom configurations

Sometimes you need to adjust SPFx build pipeline a bit, in order to add your own webpack loaders or modify configuration for some tasks. This can be done in different ways depending on your needs. Let’s try to take a closer look at the options available.   Below is a diagram showing common config flow with extensibility points:

gulp-spfx-pipeline

There are two places where you can put your customizations – under custom task’s config adjustments or using configuration file under config/[task name].json during loadCustomConfigs() method.

For example I want to extend sass task with some additional options. You can do that directly from your gulpfile.js.

Here is defaults for the sass task:

{
	preamble: '/* tslint:disable */',
	postamble: '/* tslint:enable */',
	sassMatch: [
		'src/**/*.scss'
	],
	useCSSModules: false,
	dropCssFiles: false,
	warnOnNonCSSModules: false
}

There three methods available to change the config before build initialization: 

setConfig – shallow merges config settings into the task config

This method is handy if you want completely overwrite same config settings, but leave untouchable others. For example, instead of *.scss I want to match *.sass. You need to open your gulpfile.js and put below code before build.initialize(gulp) :

build.sass.setConfig({
    sassMatch: [
        'src/**/*.sass'
    ]
});

Your build variable contains references to all tasks in the build. Before build initializing you can call different tasks’ methods in order to configure them. setConfig is one of that methods.

After the executing the task your new sass task config becomes:

{
	preamble: '/* tslint:disable */',
	postamble: '/* tslint:enable */',
	sassMatch: [
		'src/**/*.sass'
	],
	useCSSModules: false,
	dropCssFiles: false,
	warnOnNonCSSModules: false
}

You see, `sassMatch` param completely replaced with our own version.

mergeConfig – deep merges config settings into task config

This method merges your config with existing one. Now let’s say I want to add .sass extension to those which processed by sass task. So I need to:

build.sass.mergeConfig({
    sassMatch: [
        'src/**/*.sass', 'src/**/*.scss'
    ]
});

In that case your config will be:

{
	preamble: '/* tslint:disable */',
	postamble: '/* tslint:enable */',
	sassMatch: [
		'src/**/*.sass', 'src/**/*.scss'
	],
	useCSSModules: false,
	dropCssFiles: false,
	warnOnNonCSSModules: false
}

mergeConfig is very similar to setConfig, the difference visible only if you are merging complicated configs with deep nesting.

replaceConfig – replaces task config settings with new settings

If I want to completely replace config with only one sassMatch attribute, I can do:

build.sass.replaceConfig({
    sassMatch: [
        'src/**/*.sass'
    ]
});

Resulting config:

{
	sassMatch: [
		'src/**/*.sass'
	],
	dropCssFiles: false,
	warnOnNonCSSModules: false
}

Did you notice that two extra params? They are added on the setupSharedConfig() stage (see diagram at the beginning). If you want to change this settings as well you need to use another approach for config modifications – task’s config file.

Load custom config from file

In your project, under config folder create sass.json.

If you are using VSCode it’s possible to enable intellisense for this json file, because every config file has it’s own json schema. Under VSCode click File –> Presences –> Workspace settings, add below json to your settings:


"json.schemas": [
        {
            "fileMatch": [
                "/config/sass.json"
            ],
            "url": "./node_modules/@microsoft/sp-build-web/lib/schemas/sass.schema.json"
        }
    ]

Now when typing your schema you will get handy tooltips:

sass config intellisense

These settings will be added to resulting config for the task using merge (works exactly the same as mergeConfig described above) at the very last preparation stage of the build called loadCustomConfigs(). That’s the last chance when you can modify config for the task.

The latter approach is considered as best option unless you have some advanced logic which requires modifying gulpfile.js (read below about webpack).

Notes regarding webpack task configuration

By default inside build pipeline webpack uses configPath option with path to node_modules\@microsoft\gulp-core-build-webpack\webpack.config.js . When this option is used, all others are ignored and all attempts to modify with .json files will be failed. The only working option I found is modifying configure-webpack task in gulpfile.js with help of merge-webpack

Here is full code showing how you can adjust webpack settings (added vue.js loader):

const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
var merge = require('webpack-merge');

build.configureWebpack.setConfig({
    additionalConfiguration: function (config) {
        var vueConfig = {
            module: {
                loaders: [
                    {
                        test: /\.vue$/,
                        loader: 'vue-loader'
                    }
                ]
            },
            vue: {
                esModule: true
            }
        };

        return merge(config, vueConfig);
    }
});

build.initialize(gulp);

Conclusion

You have some options for modifying tasks configs. For simple scenarios use configuration files under config/[task name].json, for advanced like webpack configurations use gulpfile.js and [set\merge\replace]Config method.