Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v2): allow extend PostCSS config #4185

Merged
merged 2 commits into from
Feb 9, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export interface Plugin<T, U = unknown> {
isServer: boolean,
utils: ConfigureWebpackUtils,
): Configuration & {mergeStrategy?: ConfigureWebpackFnMergeStrategy};
configurePostCss?(options: {[name: string]: any}): Configuration;
getThemePath?(): string;
getTypeScriptThemePath?(): string;
getPathsToWatch?(): string[];
Expand Down Expand Up @@ -253,6 +254,7 @@ export interface Plugin<T, U = unknown> {

export type ConfigureWebpackFn = Plugin<unknown>['configureWebpack'];
export type ConfigureWebpackFnMergeStrategy = Record<string, MergeStrategy>;
export type ConfigurePostCssFn = Plugin<unknown>['configurePostCss'];

export type PluginOptions = {id?: string} & Record<string, unknown>;

Expand Down
37 changes: 22 additions & 15 deletions packages/docusaurus/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import {handleBrokenLinks} from '../server/brokenLinks';
import {BuildCLIOptions, Props} from '@docusaurus/types';
import createClientConfig from '../webpack/client';
import createServerConfig from '../webpack/server';
import {compile, applyConfigureWebpack} from '../webpack/utils';
import {
compile,
applyConfigureWebpack,
applyConfigurePostCss,
} from '../webpack/utils';
import CleanWebpackPlugin from '../webpack/plugins/CleanWebpackPlugin';
import {loadI18n} from '../server/i18n';
import {mapAsyncSequencial} from '@docusaurus/utils';
Expand Down Expand Up @@ -166,24 +170,27 @@ async function buildLocale({
});
}

// Plugin Lifecycle - configureWebpack.
// Plugin Lifecycle - configureWebpack and configurePostCss.
plugins.forEach((plugin) => {
const {configureWebpack} = plugin;
if (!configureWebpack) {
return;
const {configureWebpack, configurePostCss} = plugin;

if (configurePostCss) {
clientConfig = applyConfigurePostCss(configurePostCss, clientConfig);
}

clientConfig = applyConfigureWebpack(
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
clientConfig,
false,
);
if (configureWebpack) {
clientConfig = applyConfigureWebpack(
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
clientConfig,
false,
);

serverConfig = applyConfigureWebpack(
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
serverConfig,
true,
);
serverConfig = applyConfigureWebpack(
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
serverConfig,
true,
);
}
});

// Make sure generated client-manifest is cleaned first so we don't reuse
Expand Down
27 changes: 17 additions & 10 deletions packages/docusaurus/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import {load} from '../server';
import {StartCLIOptions} from '@docusaurus/types';
import {CONFIG_FILE_NAME, STATIC_DIR_NAME} from '../constants';
import createClientConfig from '../webpack/client';
import {applyConfigureWebpack, getHttpsConfig} from '../webpack/utils';
import {
applyConfigureWebpack,
applyConfigurePostCss,
getHttpsConfig,
} from '../webpack/utils';
import {getCLIOptionHost, getCLIOptionPort} from './commandUtils';
import {getTranslationsLocaleDirPath} from '../server/translations/translations';

Expand Down Expand Up @@ -134,18 +138,21 @@ export default async function start(
],
});

// Plugin Lifecycle - configureWebpack.
// Plugin Lifecycle - configureWebpack and configurePostCss.
plugins.forEach((plugin) => {
const {configureWebpack} = plugin;
if (!configureWebpack) {
return;
const {configureWebpack, configurePostCss} = plugin;

if (configurePostCss) {
config = applyConfigurePostCss(configurePostCss, config);
}

config = applyConfigureWebpack(
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
config,
false,
);
if (configureWebpack) {
config = applyConfigureWebpack(
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
config,
false,
);
}
});

// https://webpack.js.org/configuration/dev-server
Expand Down
47 changes: 46 additions & 1 deletion packages/docusaurus/src/webpack/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ import {
} from 'webpack';
import path from 'path';

import {applyConfigureWebpack, getFileLoaderUtils} from '../utils';
import {
applyConfigureWebpack,
applyConfigurePostCss,
getFileLoaderUtils,
getStyleLoaders,
} from '../utils';
import {
ConfigureWebpackFn,
ConfigureWebpackFnMergeStrategy,
ConfigurePostCssFn,
} from '@docusaurus/types';

describe('extending generated webpack config', () => {
Expand Down Expand Up @@ -148,3 +154,42 @@ describe('getFileLoaderUtils()', () => {
);
});
});

describe('extending PostCSS', () => {
test('user plugin should be appended in PostCSS loader', () => {
let config: Configuration = {
output: {
path: __dirname,
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/,
use: getStyleLoaders(false),
},
],
},
};
const postCssPlugin = jest.fn(() => {
return {
postcssPlugin: 'appended-plugin',
};
});
const configurePostCss: ConfigurePostCssFn = (postCssConfig) => {
postCssConfig.plugins.push(postCssPlugin());
return postCssConfig;
};

config = applyConfigurePostCss(configurePostCss, config);

const postCssLoader = config.module.rules[0].use.slice(-1)[0];
const postCssPlugins = postCssLoader.options.postcssOptions.plugins.map(
(plugin) => {
return plugin.postcssPlugin;
},
);

expect(postCssPlugins).toContain('appended-plugin');
});
});
30 changes: 29 additions & 1 deletion packages/docusaurus/src/webpack/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import merge from 'webpack-merge';
import webpack, {
Configuration,
Loader,
NewLoader,
Plugin,
RuleSetRule,
Stats,
Expand All @@ -23,7 +24,7 @@ import path from 'path';
import crypto from 'crypto';
import chalk from 'chalk';
import {TransformOptions} from '@babel/core';
import {ConfigureWebpackFn} from '@docusaurus/types';
import {ConfigureWebpackFn, ConfigurePostCssFn} from '@docusaurus/types';
import CssNanoPreset from '@docusaurus/cssnano-preset';
import {version as cacheLoaderVersion} from 'cache-loader/package.json';
import {BABEL_CONFIG_FILE_NAME, STATIC_ASSETS_DIR_NAME} from '../constants';
Expand Down Expand Up @@ -175,6 +176,33 @@ export function applyConfigureWebpack(
return config;
}

export function applyConfigurePostCss(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was wondering if we need a new lifecycle for this case?

Maybe that should be part of configureWebpack? (as the post css loader is part of the webpack config?)

No strong opinions for now, just thinking out loud

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, PostCSS plugins can be added via configureWebpack hook, but for this we need to duplicate a lot Docusaurus core code related to webpack, for example from this comment:

image

(Since this hook should return a valid webpack config, which will be merged with the default config).

Thus, configurePostCss is a handy and useful shortcut that you can use to quickly add the required PostCSS plugins. Or do you know a better way to achieve this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @lex111 , understand, and it will be useful for the upcoming tailwind theme too

What I see is that Gatsby does something similar with a plugin: https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-postcss/src/gatsby-node.js

And next does not really have a plugin system, but allow a local postcss.config.js and will support tailwind postcss enhancements in core soon: vercel/next.js#20030.

That does not look viable for our usecase so the most convenient is indeed a new lifecycle as themes can enhance the existing config.

Going to finish your implementation and merge, I suspect there is a little bug for which I want to have a test, and will also try to make the loader/option lookup more robust

configurePostCss: 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,
);

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;
}
}
});

return config;
}

// See https://webpack.js.org/configuration/stats/#statswarningsfilter
// @slorber: note sure why we have to re-implement this logic
// just know that legacy had this only partially implemented, so completed it
Expand Down
35 changes: 35 additions & 0 deletions website/docs/lifecycle-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,41 @@ module.exports = function (context, options) {

Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details.

## `configurePostCss(options)`

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

By default, `postcssOptions` looks like this:

```js
postcssOptions: {
ident: 'postcss',
plugins: [
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 4,
}),
],
},
```

Example:

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

## `postBuild(props)`

Called when a (production) build finishes.
Expand Down