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

Convert Stats into Pluggable API #6702

Closed
wants to merge 14 commits into from

Conversation

nveenjain
Copy link
Member

@nveenjain nveenjain commented Mar 7, 2018

What kind of change does this PR introduce?

It is a new feature.

User will now be able to specify something like this and it will totally be accepted:-

options = {
  stats:{
    preset:"none",
    timings:true
    }
}

The above config will only show timings at the time of output, similarly other presets can be modified too.

Moreover, We can add hooks into stats, which will allow plugin authors to set default for stats and they don't need to rely upon webpack's default stats options , they themselves can specify which stats defaults they want and can use those stats. They can also modify the printing of stats(choose which stats options to output). Along with this, we ourselves can let defaults be defined using some presets/plugins instead of setting them from Stats.js file.

Note that plugins only set the defaults, meaning they are to be treated as fallback values, if in case user has supplied the stats options (like in the code example above), the stats which will be shown to user will contain timing regardless of any plugin. The priority(for emitting stats) is:-

User supplied stats option > User supplied Plugins > Presets/defaults

Did you add tests for your changes?
YES

If relevant, link to documentation update:
Will update docs, once this feature is good to go.

Summary
Added preset option support to stats in options object. Now preset can also be passed along with some other options, for user convinience.

Added hooks into stats, and turned stats into pluggable API, Now the defaults are loaded by using plugins in stats, These defaults can also be provided by external plugin authors. Note that this will change defaults only, if user has supplied options himself, then it won't change anything. Also added type checking to options supplied, if the options supplied are not of supported types, then we will fallback to default values. If the default value ( by external plugin) is not supported, then an error will be thrown. This will make stats less prone to unwanted errors. Please not that by default we supply our internal plugin and use it for stats and hence default will not throw errors.

Does this PR introduce a breaking change?
No.

Other information

@nveenjain nveenjain changed the title [WIP] Initial Commit for adding hooks in stats [WIP- Suggestions Required] Initial Commit for adding hooks in stats Mar 8, 2018
@webpack-bot
Copy link
Contributor

The minimum test ratio has been reached. Thanks!

@nveenjain nveenjain changed the title [WIP- Suggestions Required] Initial Commit for adding hooks in stats [Suggestions Required] Initial Commit for adding hooks in stats Mar 10, 2018
@nveenjain
Copy link
Member Author

Also, plugin authors can hook into done of compiler to manipulate the stats which are to be printed. If this PR is merged, then i would love to contribute to docs too for this feature.

Copy link
Member

@sokra sokra left a comment

Choose a reason for hiding this comment

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

That's a good start, but currently the hooks are not very useful.

Try to create a plugin lib/stats/AssetsStatsPlugin.js and move everything related to assets from lib/Stats.js into this plugin. Apply this plugin by default in lib/WebpackOptionsApply.js. The Stats should no longer know about assets.

In the first step you can do this only for toJson and keep toString/jsonToString.

Which hooks do you need? Which problems could occur when moving all other elements too. Is it possible for a user to write custom plugins too?

Can the interpretation of the options be moved to WebpackOptionsApply.js? (Other "options" are interpreted here and plugins are applied conditionally. Can we stick the the same schema for stats options too?)

Copy link
Member

@sokra sokra left a comment

Choose a reason for hiding this comment

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

Maybe calling

compilation.hooks.stats.call(this);

in the Stats construction would allow hooking into the stats, without a breaking change.

This would allow any plugin (added to plugins in configuration`) to hook into the Stats by using:

compiler.hooks.compilation.tap("...", (compilation) => {
  compilation.hooks.stats.tap("...", (stats) => {
    stats...
  });
});

lib/Compiler.js Outdated
@@ -144,7 +143,7 @@ class Compiler extends Tapable {
if (err) return callback(err);

if (this.hooks.shouldEmit.call(compilation) === false) {
const stats = new Stats(compilation);
const stats = compilation.getStats();
Copy link
Member

Choose a reason for hiding this comment

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

If new Stats can no longer be used, it would be a breaking change.

Should we go this way. (Would mean waiting until webpack 5)

Or can be solve this problem in a non-breaking way.

Copy link
Member Author

@nveenjain nveenjain Mar 13, 2018

Choose a reason for hiding this comment

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

new Stats can be used, but i thought it would be better if we just called compilation.getStats() and after this call, we can refer current stats through compilation.stats and modify it accordingly, but we can surely go through non-breaking way if you suggest, Will update it.

@@ -85,7 +85,7 @@ class MultiStats {

const obj = this.toJson(options, true);

return Stats.jsonToString(obj, useColors);
return Stats.jsonToString(obj, useColors); //TODO: convert this to non-static method
Copy link
Member

Choose a reason for hiding this comment

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

Let's ignore this for now and take care of this when finished the other problems.

@nveenjain
Copy link
Member Author

I'll move assets to lib/Stats/AssetsStatsPlugin.js, Only hooks related to showing assets, showing cached Assets, excluding Assets and sorting Assets might be required. Then all the stuff related to assets can be moved to lib/stats/AssetsStatsPlugin.js, i think we can implement the same for modules and chunks too. Regarding interpretation of the options, I am sorry, but i think i might have misunderstood, but if we move interpretation of options to webpackOptionsApply.js, then it means that we rely on options sent by webpack.config.js everytime, however, can't we as developer, ourselves supply options to stats (while using Stats API)? in that case i don't think we'll be able to tap into the options which were not provided in webpack.config.js. I think we need to apply all the stats plugin initially in webpackOptionsApply.js. Sorry, if i misunderstood.

@nveenjain
Copy link
Member Author

nveenjain commented Mar 13, 2018

For e.g. If it is specified in config that assets should not be shown, then i may not register asset plugin( in webpackOptionsApply), but when another plugin author uses stats API, he may want default values of assets to be used, however in that case, due to asset plugin not being registered, expected things will not happen. We can keep current schema of stats options, as the plan is to do this in backwards compatible way. BTW, by moving all things related to assets, do you mean the default options of assets or anything else too?

EDIT:- Got it, will update the PR.

@nveenjain
Copy link
Member Author

nveenjain commented Mar 15, 2018

No idea on how to increase coverage further, sugesstions required 😢, unit test, but how? 😞

@nveenjain nveenjain changed the title [Suggestions Required] Initial Commit for adding hooks in stats Commit for adding hooks in stats Mar 17, 2018
Copy link
Member

@evenstensberg evenstensberg left a comment

Choose a reason for hiding this comment

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

Left some comments, mostly not related to tapable. Let's keep the mocking. Unit test are running correctly, as you've changed them. If implementation is correct, the tests should be running fine, don't you agree? For the all arrays, use optionsForPlugin or something more intuitive and extract each of the .forEach-es into general functions

lib/Stats.js Outdated
"boolean"
);

const showAssets = isTypeOf(options.assets, "boolean")
Copy link
Member

Choose a reason for hiding this comment

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

From diff, looks like isTypeOf could be similar to old impl?

Copy link
Member Author

Choose a reason for hiding this comment

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

With this implementation, I've added type checking too, we can't allow user to pass in any value of any type, if the value supplied by user if not of the type that we require, then we fallback to options.all ( for all boolean values, except for !toString values, will come to that later), if options.all is not set, then we take into account the default values ( which user has supplied either by their custom plugins or we've supplied in webpackOptionsApply), and type check them too, if they are also not of expected type, then we throw an error. However if expected type is not boolean, then we don't fallback to options.all, we directly assert value supplied by plugins.
For the values which are dependent on toString, we first check for current value, whether current value supplied by user is of supported type or not, if not, then we fallback to plugins, if no plugins are setting any value, then we can fallback to toString.

I think type checking the options value supplied should be done. However, if you insist, we can fallback to previous implementation too.

@@ -1152,6 +1362,7 @@ class Stats {
}

static presetToOptions(name) {
// TODO :- DELETE THIS IN UPCOMING VERSION, WE've GOT PLUGINS :)
Copy link
Member

Choose a reason for hiding this comment

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

Bad rebase?

Copy link
Member Author

Choose a reason for hiding this comment

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

😄 No, actulally, it's a static method, and it's used in some test cases individually, https://github.com/webpack/webpack/blob/master/test/Stats.unittest.js#L162 . Couldn't remove it without breaking change. However we no longer need this except if some user specifically want to convert presetToOptions ( which i don't think should happen), so added to TODO to remove it in future version.

Copy link
Member Author

Choose a reason for hiding this comment

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

Now this can be deleted, if suggested.. No function is using it internally, only test-cases are using. Suggestions? @sokra ??

@@ -59,6 +59,8 @@ const NamedChunksPlugin = require("./NamedChunksPlugin");
const DefinePlugin = require("./DefinePlugin");
const SizeLimitsPlugin = require("./performance/SizeLimitsPlugin");

const StatsPlugin = require("./stats/index");
Copy link
Member

Choose a reason for hiding this comment

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

Dunno if you need to include index here, require will pick it up. At least does for us at webpack-cli.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yups, will change it. 😄

Copy link
Member

Choose a reason for hiding this comment

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

Using multiple require would be easier to maintain. Moreover, StatsPlugin suggests that it returns a plugin, which is not the case.

@@ -294,6 +296,31 @@ class WebpackOptionsApply extends OptionsApply {
new ImportPlugin(options.module).apply(compiler);
new SystemPlugin(options.module).apply(compiler);

switch (options.stats) {
Copy link
Member

Choose a reason for hiding this comment

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

Beautiful

@@ -0,0 +1,10 @@
exports = module.exports;
Copy link
Member

Choose a reason for hiding this comment

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

Could be exported like a regular module?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, totally. Will change it.

}
});
const obj = mockStats.toJson();
obj.errors[0].should.be.equal("firstError");
});
});
// ?????????????????????????????????????????????????????????????????????????????????? //
Copy link
Member

Choose a reason for hiding this comment

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

Bad rebase?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, will edit that, sorry. 😃, wanted to ask question related to mocking.

@@ -135,24 +167,18 @@ describe(
},
compiler: {
context: ""
},
hooks: {
stats: new SyncHook(["stats"])
Copy link
Member

Choose a reason for hiding this comment

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

Let's keep mocking it

Copy link
Member Author

Choose a reason for hiding this comment

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

We need hooks, as compilation is required to have hooks property now, as stats constructor will call stats hook on compilation, should, I implement a mock hook call?

@evenstensberg evenstensberg changed the title Commit for adding hooks in stats [WIP] Commit for adding hooks in stats Mar 20, 2018
@nveenjain
Copy link
Member Author

nveenjain commented Apr 2, 2018

@sokra , is there any other improvements for this PR you would like to suggest? or is this PR dismissed?

@alexander-akait
Copy link
Member

@nveenjain what about if i want to get only warnings and errors? What preset i should use?

@alexander-akait
Copy link
Member

alexander-akait commented Apr 2, 2018

Also i think by default should used warnings and errors preset. A lot of problems when people don't see warnings or errors. Example uglify show warnings on mangling and if people don't see this warnings they can create issue and it is not good.

@nveenjain
Copy link
Member Author

@evilebottnawi, i've implemented presets which were supported earlier, no new preset is introduced. For errors however, we used to use error-only preset, which is still supported. By default (when no preset is supplied) we use normal preset, which shows both warnings and errors and the default build info.

However if someone wants only error and warning, then they can pass the following options through config file:-

options.stats = {
  preset:"errrors-only",
  warnings:true
}

This will show only error and warning and nothing else. I'll document it in docs once PR is approved!

@alexander-akait
Copy link
Member

@nveenjain a lot of people just use error-only and they create a lot of issue when don't see warnings.

@nveenjain
Copy link
Member Author

@evilebottnawi , gotcha, but wouldn't preset like debug-report or bug-report be more intuitive for this purpose, I'm ready to introduce another preset fot this purpose in this PR? Or would you like to suggest some other preset. Or should I modify error-only?

@alexander-akait
Copy link
Member

alexander-akait commented Apr 3, 2018

@nveenjain I think better to add error-only, because each warning it is can be potential error. Also it is don't break build other people after update.

@nveenjain
Copy link
Member Author

nveenjain commented Apr 3, 2018

@evilebottnawi, did it, PTAL 😃 and review please.

Copy link
Member

@alexander-akait alexander-akait left a comment

Choose a reason for hiding this comment

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

Great job! Thanks for feedback 👍

@nveenjain
Copy link
Member Author

@sokra @TheLarkInn can we merge this now, or is there anything missing?

@TheLarkInn
Copy link
Member

@nveenjain Could you resolve conflicts and lets run CI again. This is really great work!

@sokra this looks great we should get this in. Is this a SemVer Minor change?

@webpack-bot
Copy link
Contributor

@nveenjain Thanks for your update.

I labeled the Pull Request so reviewers will review it again.

@evilebottnawi Please review the new changes.

@nveenjain
Copy link
Member Author

nveenjain commented May 8, 2018

@TheLarkInn resolved the conflicts, Please review.. Also, I will update the docs as soon as this PR is merged!!

@webpack-bot
Copy link
Contributor

For maintainers only:

  • This need to be documented (issue in webpack/webpack.js.org will be filed when merged)

@sokra
Copy link
Member

sokra commented Jul 10, 2019

done in the next branch

@sokra sokra closed this Jul 10, 2019
@nveenjain nveenjain deleted the experiment/stats branch July 30, 2019 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants