Skip to content

Commit

Permalink
polish the configurePostCss system
Browse files Browse the repository at this point in the history
  • Loading branch information
slorber committed Feb 9, 2021
1 parent 433f1dc commit 10e843c
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 45 deletions.
5 changes: 4 additions & 1 deletion packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ export type AllContent = Record<
>
>;

// TODO improve type (not exposed by postcss-loader)
export type PostCssOptions = Record<string, any> & {plugins: any[]};

export interface Plugin<T, U = unknown> {
name: string;
loadContent?(): Promise<T>;
Expand All @@ -220,7 +223,7 @@ export interface Plugin<T, U = unknown> {
isServer: boolean,
utils: ConfigureWebpackUtils,
): Configuration & {mergeStrategy?: ConfigureWebpackFnMergeStrategy};
configurePostCss?(options: {[name: string]: any}): Configuration;
configurePostCss?(options: PostCssOptions): PostCssOptions;
getThemePath?(): string;
getTypeScriptThemePath?(): string;
getPathsToWatch?(): string[];
Expand Down
117 changes: 98 additions & 19 deletions packages/docusaurus/src/webpack/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ import {
applyConfigureWebpack,
applyConfigurePostCss,
getFileLoaderUtils,
getStyleLoaders,
} from '../utils';
import {
ConfigureWebpackFn,
ConfigureWebpackFnMergeStrategy,
ConfigurePostCssFn,
} from '@docusaurus/types';

describe('extending generated webpack config', () => {
Expand Down Expand Up @@ -157,39 +155,120 @@ describe('getFileLoaderUtils()', () => {

describe('extending PostCSS', () => {
test('user plugin should be appended in PostCSS loader', () => {
let config: Configuration = {
let webpackConfig: Configuration = {
output: {
path: __dirname,
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/,
use: getStyleLoaders(false),
test: 'any',
use: [
{
loader: 'some-loader-1',
options: {},
},
{
loader: 'some-loader-2',
options: {},
},
{
loader: 'postcss-loader-1',
options: {
postcssOptions: {
plugins: [['default-postcss-loader-1-plugin']],
},
},
},
{
loader: 'some-loader-3',
options: {},
},
],
},
{
test: '2nd-test',
use: [
{
loader: 'postcss-loader-2',
options: {
postcssOptions: {
plugins: [['default-postcss-loader-2-plugin']],
},
},
},
],
},
],
},
};
const postCssPlugin = jest.fn(() => {

function createFakePlugin(name: string) {
return [name, {}];
}

// Run multiple times: ensure last run does not override previous runs
webpackConfig = applyConfigurePostCss((postCssOptions) => {
return {
postcssPlugin: 'appended-plugin',
...postCssOptions,
plugins: [
...postCssOptions.plugins,
createFakePlugin('postcss-plugin-1'),
],
};
});
const configurePostCss: ConfigurePostCssFn = (postCssConfig) => {
postCssConfig.plugins.push(postCssPlugin());
return postCssConfig;
};
}, webpackConfig);

config = applyConfigurePostCss(configurePostCss, config);
webpackConfig = applyConfigurePostCss((postCssOptions) => {
return {
...postCssOptions,
plugins: [
createFakePlugin('postcss-plugin-2'),
...postCssOptions.plugins,
],
};
}, webpackConfig);

const postCssLoader = config.module.rules[0].use.slice(-1)[0];
const postCssPlugins = postCssLoader.options.postcssOptions.plugins.map(
(plugin) => {
return plugin.postcssPlugin;
},
webpackConfig = applyConfigurePostCss((postCssOptions) => {
return {
...postCssOptions,
plugins: [
...postCssOptions.plugins,
createFakePlugin('postcss-plugin-3'),
],
};
}, webpackConfig);

// @ts-expect-error: relax type
const postCssLoader1 = webpackConfig.module?.rules[0].use[2];
expect(postCssLoader1.loader).toEqual('postcss-loader-1');

const pluginNames1 = postCssLoader1.options.postcssOptions.plugins.map(
// @ts-expect-error: relax type
(p: unknown) => p[0],
);
expect(pluginNames1).toHaveLength(4);
expect(pluginNames1).toEqual([
'postcss-plugin-2',
'default-postcss-loader-1-plugin',
'postcss-plugin-1',
'postcss-plugin-3',
]);

expect(postCssPlugins).toContain('appended-plugin');
// @ts-expect-error: relax type
const postCssLoader2 = webpackConfig.module?.rules[1].use[0];
expect(postCssLoader2.loader).toEqual('postcss-loader-2');

const pluginNames2 = postCssLoader2.options.postcssOptions.plugins.map(
// @ts-expect-error: relax type
(p: unknown) => p[0],
);
expect(pluginNames2).toHaveLength(4);
expect(pluginNames2).toEqual([
'postcss-plugin-2',
'default-postcss-loader-2-plugin',
'postcss-plugin-1',
'postcss-plugin-3',
]);
});
});
34 changes: 16 additions & 18 deletions packages/docusaurus/src/webpack/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,28 +177,26 @@ export function applyConfigureWebpack(
}

export function applyConfigurePostCss(
configurePostCss: ConfigurePostCssFn,
configurePostCss: NonNullable<ConfigurePostCssFn>,
config: Configuration,
): Configuration {
const isPostCssLoader = (loader) =>
JSON.stringify(loader).includes('postcss-loader');
const postCssLoader = getStyleLoaders(false).find((loader) =>
isPostCssLoader(loader),
) as NewLoader;
const mutatedPostCssOptions = configurePostCss!(
postCssLoader?.options?.postcssOptions,
);
type LocalPostCSSLoader = Loader & {options: {postcssOptions: any}};

config.module?.rules
.filter((rule) => rule.test!.toString().includes('.css'))
.forEach((rule) => {
for (const loader of rule.use as NewLoader[]) {
if (isPostCssLoader(loader)) {
console.log(1, loader);
loader.options!.postcssOptions = mutatedPostCssOptions;
}
function isPostCssLoader(loader: Loader): loader is LocalPostCSSLoader {
// TODO not ideal heuristic but good enough for our usecase?
return !!(loader as any)?.options?.postcssOptions;
}

// Does not handle all edge cases, but good enough for now
config.module?.rules.map((rule) => {
for (const loader of rule.use as NewLoader[]) {
if (isPostCssLoader(loader)) {
loader.options.postcssOptions = configurePostCss(
loader.options.postcssOptions,
);
}
});
}
});

return config;
}
Expand Down
18 changes: 11 additions & 7 deletions website/docs/lifecycle-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,14 @@ Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge

## `configurePostCss(options)`

Modifies [`postcssOptions` of `postcss-loader`](https://webpack.js.org/loaders/postcss-loader/#postcssoptions) during generating client bundle. Should return mutated options.
Modifies [`postcssOptions` of `postcss-loader`](https://webpack.js.org/loaders/postcss-loader/#postcssoptions) during the generation of the client bundle.

Should return the mutated `postcssOptions`.

By default, `postcssOptions` looks like this:

```js
postcssOptions: {
const postcssOptions = {
ident: 'postcss',
plugins: [
require('postcss-preset-env')({
Expand All @@ -363,20 +365,22 @@ postcssOptions: {
stage: 4,
}),
],
},
};
```

Example:

```js {4-11} title="docusaurus-plugin/src/index.js"
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
configurePostCss(options) {
// highlight-start
configurePostCss(postcssOptions) {
// Appends new PostCSS plugin.
options.plugins.push(require('postcss-import'));
return options;
postcssOptions.plugins.push(require('postcss-import'));
return postcssOptions;
},
// highlight-end
};
};
```
Expand Down

0 comments on commit 10e843c

Please sign in to comment.