-
Notifications
You must be signed in to change notification settings - Fork 234
Support Config.toString() with name hints #53
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many thanks for the PR! I've wanted this feature for ages!
Here's some example output with a project using Neutrino's react + airbnb presets (from master
) + custom overrides:
https://emorley.pastebin.mozilla.org/9085355
Do you have a sense of whether we should call this a breaking change or not? Whilst it's techincally breaking, I can't really believe people we're relying on the previous [object Object]
of the inherited .toString()
.
Also, I wonder if in the future (probably not this PR) we should add mappings to be able to add the imports for the most common plugins/loaders?
src/Config.js
Outdated
if (value && value.__pluginName) { | ||
let match = ( | ||
value.constructor && | ||
value.constructor.toString().match(pluginRE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are your thoughts about using .name
instead of .toString()
, which would mean the regexp could be dropped?
src/Config.js
Outdated
); | ||
// special case for copy-webpack-plugin which uses a non-standard constructor | ||
if (value.__pluginName === 'copy') { | ||
match = [null, `CopyWebpackPlugin`]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried a few variations of this to try and find a generic solution for plugins that do this:
https://github.com/webpack-contrib/copy-webpack-plugin/blob/6205f516f7dd027a93737bf533a3be36d57602d9/src/index.js#L174-L175
I couldn't find any that worked once the plugin was already instantiated, however a this.get('plugin').name
in Plugin.js
worked -- perhaps it might be best to determine the class/function identifier there and pass back along with the name/args?
src/Config.js
Outdated
|
||
if (name) { | ||
return prefix + `new ${name}(${ | ||
value.__pluginArgs.map(arg => stringify(arg)).join(',\n') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This generates plugin output like:
/* config.plugin('copy') */
new CopyWebpackPlugin([
'ui/contribute.json',
'ui/revision.txt',
'ui/robots.txt'
],
{
debug: 'warning'
}),
I played around to see if the indentation could be improved, and came up with this convoluted looking thing:
const args = value.__pluginArgs.length
? `\n${value.__pluginArgs.map(
arg => indent + stringify(arg).split('\n').join('\n' + indent)
).join(',\n')}\n`
: '';
return prefix + `new ${name}(${args})`;
(the intentation stategy taken from: https://github.com/blakeembrey/javascript-stringify/blob/9d2b937ac768d41239ca0088aff90e5ef4e7e113/javascript-stringify.js#L130)
Which gives:
/* config.plugin('copy') */
new CopyWebpackPlugin(
[
'ui/contribute.json',
'ui/revision.txt',
'ui/robots.txt'
],
{
debug: 'warning'
}
),
However I'm open to ideas about whether this is (a) worth it, (b) the right indentation anyway (albeit does match prettier).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I played around to see if the indentation could be improved, and came up with this convoluted looking thing:
Actually, this works and is much cleaner:
// Stringifying the entire args array and discarding the first (`[`) and
// last (`]`) characters is simpler than handling each element separately
// and then having to manually fix the indentation.
const args = stringify(value.__pluginArgs).slice(1, -1);
return prefix + `new ${name}(${args})`;
(if you end up using this, feel free to adjust the code comment to read more clearly)
@@ -27,7 +27,7 @@ module.exports = class extends ChainedMap { | |||
|
|||
plugin(name) { | |||
if (!this.plugins.has(name)) { | |||
this.plugins.set(name, new Plugin(this)); | |||
this.plugins.set(name, new Plugin(this, name)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a test for this one to test/Resolve.js
? :-)
test/Config.js
Outdated
|
||
config | ||
.plugin('gamma') | ||
.use(class TestPlugin {}, ['foo']); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a couple more plugins here, to also test the zero and two argument cases?
src/Config.js
Outdated
return stringify(config, (value, indent, stringify) => { | ||
// shorten long functions | ||
if (!verbose && typeof value === 'function' && value.toString().length > 100) { | ||
return `function () { /* omitted long function */ }`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder whether it might be useful to allow downstream consumers to defineProperties()
on functions they pass, so they can override what's output? (In Neutrino's case for the eslint loader formatter
option, we'd want to make the output be require('eslint/lib/formatters/codeframe')
rather than the function)
src/Config.js
Outdated
match = [null, `CopyWebpackPlugin`]; | ||
} | ||
const name = match[1]; | ||
const prefix = `/* config.plugin('${value.__pluginName}') */\n`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry forgot one last thing... I think it might be good to allow customising the identifier used for the config object. eg default to config
, but allow Neutrino to override to neutrino.config
, which would give: /* neutrino.config.plugin(...) */
.
Should have addressed everything. I don't think anyone would find the original
With these in place we should be able to generate fully usable webpack config! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is amazing! Thank you for the latest changes :-)
@eliperelman do you want to have a quick look before I merge?
const args = stringify(value.__pluginArgs).slice(1, -1); | ||
return prefix + `new ${constructorName}(${args})`; | ||
} else { | ||
return prefix + stringify({ args: value.__pluginArgs || [] }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there known cases where this path is taken, or is it for defensive purposes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly defensive when I found out about the case of copy-webpack-plugin, but this is probably unnecessary as a plugin has to be new-able and thus must have a name.
Ah one last thing - if you have a chance could you add a mention to the README about |
Added readme section. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😍 This in incredible! Thank you so much for your hard work on this one!
Published in v4.7.0. |
I'm not sure this is something worth the extra dependencies of fixing, so not going to file as a new issue for now - but thought worth mentioning it here in case others were similarly confused when trying For plugins that mutate any options objects passed to them as parameters, these mutated options are what will appear in the For example: const CopyWebpackPlugin = require('copy-webpack-plugin');
neutrino.config
.plugin('copy')
.use(CopyWebpackPlugin, [
[ 'foo' ],
{ debug: false }
]); ...results in: /* neutrino.config.plugin('copy') */
new CopyWebpackPlugin(
[
'foo',
],
{
debug: 'warning'
}
), ...where In this specific case cloning the options would be trivial using
|
This take advantage of the new webpack-chain `toString()` added in neutrinojs/webpack-chain#53. The output from `neutrino --inspect` now lists the correct plugin declarations and arguments, annotates plugins/loaders with hints about how to reference them in a custom Neutrino config, and supports using `__expression` to override the stringified output when needed. The usage of `deep-sort-object` has been removed since it breaks the new `toString()` comment annotations, and really if sorted output is considered important, it should be handled by webpack-chain itself. Fixes #328. Refs #96.
As discussed on Twitter :)