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 plugins to consume webpack stats #4021

Merged
merged 4 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 10 additions & 2 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

// ESLint doesn't understand types dependencies in d.ts
// eslint-disable-next-line import/no-extraneous-dependencies
import {Loader, Configuration} from 'webpack';
import {Loader, Configuration, Stats} from 'webpack';
import {Command} from 'commander';
import {ParsedUrlQueryInput} from 'querystring';
import {MergeStrategy} from 'webpack-merge';
Expand Down Expand Up @@ -178,6 +178,13 @@ export interface Props extends LoadContext, InjectedHtmlTags {
plugins: Plugin<any, unknown>[];
}

/**
* Same as `Props` but also has webpack stats appended.
*/
export interface PropsPostBuild extends Props {
stats: Stats.ToJsonOutput;
}

export interface PluginContentLoadedActions {
addRoute(config: RouteConfig): void;
createData(name: string, data: any): Promise<string>;
Expand Down Expand Up @@ -206,7 +213,7 @@ export interface Plugin<T, U = unknown> {
actions: PluginContentLoadedActions;
}): void;
routesLoaded?(routes: RouteConfig[]): void; // TODO remove soon, deprecated (alpha-60)
postBuild?(props: Props): void;
postBuild?(props: PropsPostBuild): void;
postStart?(props: Props): void;
configureWebpack?(
config: Configuration,
Expand All @@ -224,6 +231,7 @@ export interface Plugin<T, U = unknown> {
postBodyTags?: HtmlTags;
};
getSwizzleComponentList?(): string[];
// TODO before/afterDevServer implementation

// translations
getTranslationFiles?(): Promise<TranslationFiles>;
Expand Down
6 changes: 3 additions & 3 deletions packages/docusaurus/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import chalk = require('chalk');
import chalk from 'chalk';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import fs from 'fs-extra';
import path from 'path';
Expand Down Expand Up @@ -193,7 +193,7 @@ async function buildLocale({
}

// Run webpack to build JS bundle (client) and static html files (server).
await compile([clientConfig, serverConfig]);
const finalCompileResult = await compile([clientConfig, serverConfig]);

// Remove server.bundle.js because it is not needed.
if (
Expand All @@ -215,7 +215,7 @@ async function buildLocale({
if (!plugin.postBuild) {
return;
}
await plugin.postBuild(props);
await plugin.postBuild({...props, stats: finalCompileResult});
}),
);

Expand Down
10 changes: 6 additions & 4 deletions packages/docusaurus/src/webpack/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,22 +198,24 @@ function filterWarnings(
return warnings.filter((warning) => !isWarningFiltered(warning));
}

export function compile(config: Configuration[]): Promise<void> {
export function compile(config: Configuration[]): Promise<Stats.ToJsonOutput> {
return new Promise((resolve, reject) => {
const compiler = webpack(config);
compiler.run((err, stats) => {
if (err) {
reject(new Error(err.toString()));
}
// let plugins consume all the stats
const allStats = stats?.toJson('errors-warnings');
if (stats?.hasErrors()) {
stats.toJson('errors-only').errors.forEach((e) => {
allStats.errors.forEach((e) => {
console.error(e);
});
reject(new Error('Failed to compile with errors.'));
}
if (stats?.hasWarnings()) {
// Custom filtering warnings (see https://github.com/webpack/webpack/issues/7841).
let {warnings} = stats.toJson('errors-warnings');
let warnings = [...allStats.warnings];
Copy link
Collaborator

Choose a reason for hiding this comment

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

are there cases where the warning array is not defined?

Maybe we could replace the condition with if (allStats.warnings?.length > 0), and TS would understand better that the array is there or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is there so the original stats.warnings doesn't get modified.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I see thanks.
It shouldn't be, but won't harm to keep this code


const warningsFilter = ((config[0].stats as Stats.ToJsonOptionsObject)
?.warningsFilter || []) as WarningFilter[];
Expand All @@ -226,7 +228,7 @@ export function compile(config: Configuration[]): Promise<void> {
console.warn(warning);
});
}
resolve();
resolve(allStats);
});
});
}
Expand Down
7 changes: 5 additions & 2 deletions website/docs/lifecycle-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,20 +362,23 @@ type Props = {
postBodyTags: string;
routesPaths: string[];
plugins: Plugin<any>[];
stats: Stats.ToJsonOutput;
};
```

Example:

```js {4-9} title="docusaurus-plugin/src/index.js"
```js {4-11} title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
async postBuild({siteConfig = {}, routesPaths = [], outDir}) {
async postBuild({siteConfig = {}, routesPaths = [], outDir, stats}) {
// Print out to console all the rendered routes.
routesPaths.map((route) => {
console.log(route);
});
// Print out to console all the webpack stats.
console.log(stats);
},
};
};
Expand Down