diff --git a/README.md b/README.md index 261d6c1f..8058b55a 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,9 @@ If you use JS styles without the [`postcss-js`][postcss-js] parser, add the `exe Type: `Boolean|String|Object` Default: `undefined` +Options specified in the config file are combined with options passed to the loader. +Loader options overwrite options from config. + #### Boolean Enables/Disables autoloading config. @@ -264,21 +267,89 @@ module.exports = ({ file, options, env }) => ({ **`webpack.config.js`** ```js -{ - loader: 'postcss-loader', - options: { - ident: 'postcss', - plugins: (loader) => [ - require('postcss-import')({ root: loader.resourcePath }), - require('postcss-preset-env')(), - require('cssnano')() - ] - } -} +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + loader: 'postcss-loader', + options: { + ident: 'postcss', + plugins: (loader) => [ + require('postcss-import')({ root: loader.resourcePath }), + require('postcss-preset-env')(), + require('cssnano')(), + ], + }, + }, + ], + }, +}; ``` > ⚠️ webpack requires an identifier (`ident`) in `options` when `{Function}/require` is used (Complex Options). The `ident` can be freely named as long as it is unique. It's recommended to name it (`ident: 'postcss'`) +**`webpack.config.js`** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + loader: 'postcss-loader', + options: { + ident: 'postcss', + plugins: { + 'postcss-import': {}, + 'postcss-nested': {}, + 'postcss-short': { prefix: 'x' }, + }, + }, + }, + ], + }, +}; +``` + +It is possible to disable the plugin specified in the config. + +**`postcss.config.js`** + +```js +module.exports = { + plugins: { + 'postcss-short': { prefix: 'x' }, + 'postcss-import': {}, + 'postcss-nested': {}, + }, +}; +``` + +**`webpack.config.js`** + +```js +module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + loader: 'postcss-loader', + options: { + ident: 'postcss', + plugins: { + 'postcss-import': {}, + 'postcss-nested': {}, + // Turn off the plugin + 'postcss-short': false, + }, + }, + }, + ], + }, +}; +``` + ### `Syntaxes` | Name | Type | Default | Description | diff --git a/src/index.js b/src/index.js index d694eb01..f07f6e50 100644 --- a/src/index.js +++ b/src/index.js @@ -77,11 +77,13 @@ export default async function loader(content, sourceMap, meta = {}) { let plugins; + const disabledPlugins = []; + try { plugins = [ ...getArrayPlugins(loadedConfig.plugins, file), - ...getArrayPlugins(options.plugins, file), - ]; + ...getArrayPlugins(options.plugins, file, disabledPlugins), + ].filter((i) => !disabledPlugins.includes(i.postcssPlugin)); } catch (error) { this.emitError(error); } diff --git a/src/utils.js b/src/utils.js index 1ec7d0ec..a3f922b6 100644 --- a/src/utils.js +++ b/src/utils.js @@ -165,7 +165,7 @@ function getPlugin(pluginEntry) { return Array.isArray(result) ? result : [result]; } -function getArrayPlugins(plugins, file) { +function getArrayPlugins(plugins, file, disabledPlugins) { if (Array.isArray(plugins)) { return plugins.reduce((accumulator, plugin) => { // eslint-disable-next-line no-param-reassign @@ -180,7 +180,22 @@ function getArrayPlugins(plugins, file) { return []; } - return getArrayPlugins(loadPlugins(plugins, file), file); + const statePlagins = { + enabled: {}, + disabled: disabledPlugins || [], + }; + + Object.entries(plugins).forEach((plugin) => { + const [name, options] = plugin; + + if (options === false) { + statePlagins.disabled.push(name); + } else { + statePlagins.enabled[name] = options; + } + }); + + return getArrayPlugins(loadPlugins(statePlagins.enabled, file), file); } return getPlugin(plugins); diff --git a/test/fixtures/css/plugins.config.js b/test/fixtures/css/plugins.config.js new file mode 100644 index 00000000..47734fec --- /dev/null +++ b/test/fixtures/css/plugins.config.js @@ -0,0 +1,7 @@ +module.exports = { + plugins: { + 'postcss-short': { prefix: 'x' }, + 'postcss-import': {}, + 'postcss-nested': {}, + } +} diff --git a/test/options/__snapshots__/plugins.test.js.snap b/test/options/__snapshots__/plugins.test.js.snap index d2b9cffe..eb29f24c 100644 --- a/test/options/__snapshots__/plugins.test.js.snap +++ b/test/options/__snapshots__/plugins.test.js.snap @@ -1,5 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Options Plugins should disables plugin from config: css 1`] = ` +"a { + -x-border-color: blue blue *; + -x-color: * #fafafa; +} +" +`; + +exports[`Options Plugins should disables plugin from config: errors 1`] = `Array []`; + +exports[`Options Plugins should disables plugin from config: warnings 1`] = `Array []`; + exports[`Options Plugins should emit error on load plugin: errors 1`] = ` Array [ "ModuleBuildError: Module build failed (from \`replaced original path\`): diff --git a/test/options/plugins.test.js b/test/options/plugins.test.js index 9a8516c6..95500404 100644 --- a/test/options/plugins.test.js +++ b/test/options/plugins.test.js @@ -142,6 +142,22 @@ describe('Options Plugins', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); }); + it('should disables plugin from config', async () => { + const compiler = getCompiler('./css/index2.js', { + config: 'test/fixtures/css/plugins.config.js', + plugins: { + 'postcss-short': false, + }, + }); + const stats = await compile(compiler); + + const codeFromBundle = getCodeFromBundle('style2.css', stats); + + expect(codeFromBundle.css).toMatchSnapshot('css'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + it('should emit error on load plugin', async () => { const compiler = getCompiler('./css/index2.js', { plugins: {