Skip to content

Commit

Permalink
feat: array syntax for plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed Aug 28, 2020
1 parent 2cd7614 commit d7bc470
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 55 deletions.
105 changes: 75 additions & 30 deletions README.md
Expand Up @@ -38,11 +38,7 @@ npm i -D postcss-loader
```js
module.exports = {
parser: 'sugarss',
plugins: {
'postcss-import': {},
'postcss-preset-env': {},
cssnano: {},
},
plugins: [['postcss-import', {}], ['postcss-preset-env'], 'postcss-short'],
};
```

Expand Down Expand Up @@ -71,7 +67,9 @@ Config lookup starts from `path.dirname(file)` and walks the file tree upwards u
|– package.json
```

After setting up your `postcss.config.js`, add `postcss-loader` to your `webpack.config.js`. You can use it standalone or in conjunction with `css-loader` (recommended). Use it **before** `css-loader` and `style-loader`, but **after** other preprocessor loaders like e.g `sass|less|stylus-loader`, if you use any (since [webpack loaders evaluate right to left/bottom to top](https://webpack.js.org/concepts/loaders/#configuration)).
After setting up your `postcss.config.js`, add `postcss-loader` to your `webpack.config.js`.
You can use it standalone or in conjunction with `css-loader` (recommended).
Use it **before** `css-loader` and `style-loader`, but **after** other preprocessor loaders like e.g `sass|less|stylus-loader`, if you use any (since [webpack loaders evaluate right to left/bottom to top](https://webpack.js.org/concepts/loaders/#configuration)).

**`webpack.config.js`**

Expand Down Expand Up @@ -111,15 +109,15 @@ module.exports = {

<h2 align="center">Options</h2>

| Name | Type | Default | Description |
| :------------------------: | :-------------------------------------------: | :----------------: | :------------------------------------------- |
| [`exec`](#exec) | `{Boolean}` | `undefined` | Enable PostCSS Parser support in `CSS-in-JS` |
| [`parser`](#syntaxes) | `{String\|Object\|Function}` | `undefined` | Set PostCSS Parser |
| [`syntax`](#syntaxes) | `{String\|Object}` | `undefined` | Set PostCSS Syntax |
| [`stringifier`](#syntaxes) | `{String\|Object\|Function}` | `undefined` | Set PostCSS Stringifier |
| [`config`](#config) | `{String\|Object\|Boolean}` | `undefined` | Set `postcss.config.js` config path && `ctx` |
| [`plugins`](#plugins) | `{Function\|Object\|Array<Function\|Object>}` | `[]` | Set PostCSS Plugins |
| [`sourceMap`](#sourcemap) | `{String\|Boolean}` | `compiler.devtool` | Enables/Disables generation of source maps |
| Name | Type | Default | Description |
| :------------------------: | :----------------------------------------------------------: | :----------------: | :------------------------------------------- |
| [`exec`](#exec) | `{Boolean}` | `undefined` | Enable PostCSS Parser support in `CSS-in-JS` |
| [`parser`](#syntaxes) | `{String\|Object\|Function}` | `undefined` | Set PostCSS Parser |
| [`syntax`](#syntaxes) | `{String\|Object}` | `undefined` | Set PostCSS Syntax |
| [`stringifier`](#syntaxes) | `{String\|Object\|Function}` | `undefined` | Set PostCSS Stringifier |
| [`config`](#config) | `{String\|Object\|Boolean}` | `undefined` | Set `postcss.config.js` config path && `ctx` |
| [`plugins`](#plugins) | `{Function\|Object\|Array<String\|Function\|Object\|Array>}` | `[]` | Set PostCSS Plugins |
| [`sourceMap`](#sourcemap) | `{String\|Boolean}` | `compiler.devtool` | Enables/Disables generation of source maps |

### `Exec`

Expand Down Expand Up @@ -261,13 +259,11 @@ Default: `undefined`
```js
module.exports = ({ file, options, env }) => ({
parser: file.extname === '.sss' ? 'sugarss' : false,
plugins: {
'postcss-import': { root: file.dirname },
'postcss-preset-env': options['postcss-preset-env']
? options['postcss-preset-env']
: false,
cssnano: env === 'production' ? options.cssnano : false,
},
plugins: [
// Plugins with options and without
['postcss-import', { root: file.dirname }],
'postcss-preset-env',
],
});
```

Expand Down Expand Up @@ -296,9 +292,56 @@ module.exports = {

### `Plugins`

Type: `Function|Object|Array<Function\|Object>`
Type: `Function|Object|Array<String|Function\|Object|Array>`
Default: `[]`

It is recommended to specify plugins in the format `Array<String\|Array>` or `Function` that returns the same array as shown below.
`Object` format (`{pluginName: pluginOptions}`) is deprecated and will be removed in the next major release.

**`webpack.config.js`**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
loader: 'postcss-loader',
options: {
plugins: [
'postcss-import',
'postcss-nested',
['postcss-short', { prefix: 'x' }],
],
},
},
],
},
};
```

**`webpack.config.js`**

```js
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
loader: 'postcss-loader',
options: {
plugins: (loader) => [
['postcss-import', { root: loader.resourcePath }],
'postcss-nested',
'cssnano',
],
},
},
],
},
};
```

**`webpack.config.js`**

```js
Expand All @@ -321,6 +364,8 @@ module.exports = {
};
```

> ⚠️ The method below for specifying plugins is deprecated.
**`webpack.config.js`**

```js
Expand Down Expand Up @@ -349,11 +394,11 @@ It is possible to disable the plugin specified in the config.

```js
module.exports = {
plugins: {
'postcss-short': { prefix: 'x' },
'postcss-import': {},
'postcss-nested': {},
},
plugins: [
['postcss-short', { prefix: 'x' }],
'postcss-import',
'postcss-nested',
],
};
```

Expand Down Expand Up @@ -662,7 +707,7 @@ module.exports = {
{
loader: 'postcss-loader',
options: {
plugins: [require('postcss-import')(), require('stylelint')()],
plugins: ['postcss-import', 'stylelint'],
},
},
],
Expand All @@ -688,7 +733,7 @@ module.exports = {
{
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')({ ...options })],
plugins: [['autoprefixer', { ...options }]],
},
},
],
Expand Down
4 changes: 2 additions & 2 deletions src/index.js
Expand Up @@ -81,8 +81,8 @@ export default async function loader(content, sourceMap, meta = {}) {

try {
plugins = [
...getArrayPlugins(loadedConfig.plugins, file),
...getArrayPlugins(options.plugins, file, disabledPlugins),
...getArrayPlugins(loadedConfig.plugins, file, false, this),
...getArrayPlugins(options.plugins, file, disabledPlugins, this),
].filter((i) => !disabledPlugins.includes(i.postcssPlugin));
} catch (error) {
this.emitError(error);
Expand Down
49 changes: 43 additions & 6 deletions src/utils.js
Expand Up @@ -160,26 +160,47 @@ function getPlugin(pluginEntry) {
return [];
}

if (pluginEntry.postcssVersion === postcssPkg.version) {
if (isPostcssPlugin(pluginEntry)) {
return [pluginEntry];
}

const result = pluginEntry.call(this, this);
const result = pluginEntry();

return Array.isArray(result) ? result : [result];
}

function getArrayPlugins(plugins, file, disabledPlugins) {
function isPostcssPlugin(plugin) {
return plugin.postcssVersion === postcssPkg.version;
}

function pluginsProcessing(plugins, file, disabledPlugins) {
if (Array.isArray(plugins)) {
return plugins.reduce((accumulator, plugin) => {
let normalizedPlugin = plugin;

if (Array.isArray(plugin)) {
const [name] = plugin;
let [, options] = plugin;

options = options || {};

normalizedPlugin = { [name]: options };
}

if (typeof plugin === 'string') {
normalizedPlugin = { [plugin]: {} };
}

// eslint-disable-next-line no-param-reassign
accumulator = accumulator.concat(getArrayPlugins(plugin));
accumulator = accumulator.concat(
pluginsProcessing(normalizedPlugin, file, disabledPlugins)
);

return accumulator;
}, []);
}

if (typeof plugins === 'object' && typeof plugins !== 'function') {
if (typeof plugins === 'object') {
if (Object.keys(plugins).length === 0) {
return [];
}
Expand All @@ -199,10 +220,26 @@ function getArrayPlugins(plugins, file, disabledPlugins) {
}
});

return getArrayPlugins(loadPlugins(statePlagins.enabled, file), file);
return pluginsProcessing(
loadPlugins(statePlagins.enabled, file),
file,
disabledPlugins
);
}

return getPlugin(plugins);
}

function getArrayPlugins(plugins, file, disabledPlugins, loaderContext) {
if (typeof plugins === 'function') {
if (isPostcssPlugin(plugins)) {
return [plugins];
}

return pluginsProcessing(plugins(loaderContext), file, disabledPlugins);
}

return pluginsProcessing(plugins, file, disabledPlugins);
}

export { exec, loadConfig, getArrayPlugins };
2 changes: 1 addition & 1 deletion test/fixtures/config-autoload/js/object/postcss.config.js
Expand Up @@ -8,7 +8,7 @@ module.exports = function (ctx) {
plugins: {
'postcss-import': {},
'postcss-nested': {},
'cssnano': ctx.env === 'production' ? {} : false
'cssnano': ctx.env === 'production' ? {} : false,
}
}
}
54 changes: 42 additions & 12 deletions test/options/__snapshots__/plugins.test.js.snap
@@ -1,17 +1,5 @@
// 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\`):
Expand All @@ -23,6 +11,48 @@ Loading PostCSS Plugin failed: Cannot find module 'postcss-unresolved' from 'src

exports[`Options Plugins should emit error on load plugin: warnings 1`] = `Array []`;

exports[`Options Plugins should not disables plugin from config: css 1`] = `
"a {
border-top-color: blue;
border-right-color: blue;
border-left-color: blue;
background-color: #fafafa;
}
"
`;

exports[`Options Plugins should not disables plugin from config: errors 1`] = `Array []`;

exports[`Options Plugins should not disables plugin from config: warnings 1`] = `Array []`;

exports[`Options Plugins should work Plugins - { Array } Array + options: css 1`] = `
"a {
border-top-color: blue;
border-right-color: blue;
border-left-color: blue;
background-color: #fafafa;
}
"
`;

exports[`Options Plugins should work Plugins - { Array } Array + options: errors 1`] = `Array []`;

exports[`Options Plugins should work Plugins - { Array } Array + options: warnings 1`] = `Array []`;

exports[`Options Plugins should work Plugins - { Function } Array + options: css 1`] = `
"a {
border-top-color: blue;
border-right-color: blue;
border-left-color: blue;
background-color: #fafafa;
}
"
`;

exports[`Options Plugins should work Plugins - { Function } Array + options: errors 1`] = `Array []`;

exports[`Options Plugins should work Plugins - { Function } Array + options: warnings 1`] = `Array []`;

exports[`Options Plugins should work Plugins - {Array<Object>} + options: css 1`] = `
"a {
border-top-color: blue;
Expand Down
44 changes: 40 additions & 4 deletions test/options/plugins.test.js
Expand Up @@ -121,6 +121,44 @@ describe('Options Plugins', () => {
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('should work Plugins - { Array } Array + options', async () => {
const compiler = getCompiler('./css/index2.js', {
plugins: [
'postcss-import',
['postcss-nested'],
['postcss-short', { prefix: 'x' }],
],
});
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 work Plugins - { Function } Array + options', async () => {
const compiler = getCompiler('./css/index2.js', {
plugins: (loader) => {
expect(loader).toBeDefined();

return [
'postcss-nested',
['postcss-import', { root: loader.resourcePath }],
['postcss-short', { prefix: 'x' }],
];
},
});
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 work Plugins - {Array<Object>} + options', async () => {
const compiler = getCompiler('./css/index2.js', {
// eslint-disable-next-line global-require
Expand All @@ -135,12 +173,10 @@ describe('Options Plugins', () => {
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('should disables plugin from config', async () => {
it('should not disables plugin from config', async () => {
const compiler = getCompiler('./css/index2.js', {
config: 'test/fixtures/config-scope/css/plugins.config.js',
plugins: {
'postcss-short': false,
},
plugins: [['postcss-short', false]],
});
const stats = await compile(compiler);

Expand Down

0 comments on commit d7bc470

Please sign in to comment.