-
-
Notifications
You must be signed in to change notification settings - Fork 388
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: add hook for customizing injected runtime tags #526
base: master
Are you sure you want to change the base?
Conversation
@patriksletmo many thanks for taking a stab at this. I like how simple this change is, but compared to the generic approach I've outlined in #40 I'm afraid it has a few drawbacks from the perspective of our plugin (webpack-subresource-integrity):
With all this in mind, I still think a more generic solution would be preferable. The main issue that would need to be solved first is to figure out where to declare the hook, and I think the best place for it is in Webpack itself, probably on the compilation object. I'm pretty sure I've proposed this a while back on Gitter and @sokra was open to the idea, so maybe it wouldn't take much to put this in place? |
@jscheid Thank you for your feedback! I have thought about those drawbacks as well, and this is the motivation for going with the solution I've chosen:
I agree that this change could probably be more useful with the generic implementation of your general proposal, but I think that the exposure of the hook should remain as is. If a more flexible solution is required in some cases, it might be better to have another hook for that purpose, but perhaps this can be implemented at a later time if needed. I'll update the code accordingly and submit an update to this PR. |
@patriksletmo I see. Thanks for pointing out the compilation hooks getting frozen, I wasn't aware of this. Just thinking out loud, one solution could be to create a new, separate npm package -- something like webpack-injected-tag-hook -- whose sole purpose would be to expose the hook in question and to document the interface contract. This could be used by WSI, MCE and any other/future similar plugins as a point of rendezvous. How would everyone feel about this? As for @michael-ciniawsky's comment about hot reloading, I'm also curious to hear what the requirements are, if we can kill two birds with one stone then I'm all for it. Perhaps he can add some more detail. In the meantime I'd suggest we focus on just the interface between WSI and MCE, which I understand is the problem you're trying to solve, in the interest of keeping the scope manageable. |
I am very positive to a central solution that connects different packages of this kind. It would certainly make the interface between them easier and more uniform, and has the added benefit of not creating unnecessary dependencies. How would the interface for this solution look like? I suppose that the solution you proposed in #40 should hold here as well, by passing a data structure on the following form to all subscribers of the hook. I don't think subscribers would need more specificity than this, but perhaps you have a better understanding of this situation. interface Tag {
tagName: string,
attributes: object
} |
That should work, I can't think of anything else it would need. The next step would then probably be finding out if the maintainers of this plugin (MCE) are happy to add that dependency. There is one potential pitfall with this approach, which is that it requires all plugins to depend on the same version of the package or they will get different hook instances back, similar to this issue. The package description should ask dependees to specify a loose version range like |
Actually, thinking more about it, I have a doubt... webpack itself is injecting tags (for dynamic import/ |
Yeah, it would be interesting to hear a maintainer's take on this. To the best of my understanding, freezing the hooks object means that the hooks object can only be assigned, but never modified. In essence, the following should hold: // Allowed
constructor() {
this.hooks = {...}
}
// Disallowed
Module.hooks.myHook = ... As for the situation with multiple different versions of the same package I think that there is a fairly reasonable solution to that problem. It involves using the
It should be noted that this solution needs consideration to the following limitations:
|
I meant bake it directly into Webpack, but never mind. I think the separate hook package is a good compromise between clean and workable.
That could get messy depending on how exactly two released versions of the package will differ. I think just asking people not to depend on too narrow a version range, and making an effort not to release a new package version too often (if at all) should do the trick. It's going to be an extremely simple package so hopefully there will rarely be the need for a new version, and hopefully never for one with a breaking change. |
@jantimon would be great to hear your thoughts on this as well. |
Hi @jahed @patriksletmo, |
Hey @utlime, no maintainer of this repository seemed interested in the idea so we didn't pursue this issue further. |
@patriksletmo Can you describe case? You can use |
@alexander-akait With an implementation using var MiniCssExtractPlugin = require("mini-css-extract-plugin");
const cssHooks = MiniCssExtractPlugin.getCompilerHooks(this.compilation.compiler);
if (cssHooks) {
cssHooks.customize.tap("MyPlugin", data => {
if (data.tagName === "link") {
const integrityHash = getIntegrityForResource(data); // implementation specific definition
data.attributes.integrity = JSON.stringify(integrityHash);
}
});
} By requiring the hook consumer to provide the required value escaping we make it possible to have plugins interact with other parts of the script, for instance: cssHooks.customize.tap("MyPlugin", data => {
if (data.tagName === "link") {
data.attributes.integrity = "window.integrityData[chunkId]";
}
}); I'm not certain if this last scenario makes perfect sense since it allows for usage of implementation-specific variables that may change without notice, but on the other hand it opens up for more advanced implementations. |
@patriksletmo make sense, can you rebase? |
@alexander-akait Other work got in the way but I've updated the code and rebased against master now. I updated the tests for webpack 5 but couldn't get the tests for webpack 4 to run. Please let me know if you want me to add more tests for the added functionality. |
Let's skip test for v4 (we will drop it in future) and enable this only for webpack v5 |
Enable other plugins to tap into the tag injection mechanism in order to customize the functionality of lazy loaded chunks. Closes webpack-contrib#40.
Alright, I've updated the code so that this feature is not used for webpack v4. |
|
||
// Some attributes cannot be assigned through setAttribute, so we maintain | ||
// a list of attributes that can safely be assigned through dot notation | ||
const safeAttrs = ['href', 'rel', 'type', 'onload', 'onerror']; |
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 think we should change this logic little bit, I think we don't protect attributes, developer can add data-*
attributes, any non valid HTML attribute (why not 😄 ), even onload and onerror can be rewritten
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.
The reason for this logic is that some attributes must be assigned through tag.prop = value
while others must be assigned through tag.setAttribute("prop", value);
. The array above is a bit over-cautious, as I found that only onload
and onerror
needed to be set through dot notation (e.g. tag.onload = ...
), the other attributes defined there can just as easily be set through setAttribute
.
I'm not aware of any sensible way of determining what attributes needs to be set which way, so one proposal here would be to rewrite the above code to say
const safeAttrs = ['onload', 'onerror'];
Perhaps we could also update the wording and the comment to clarify why this is done.
The problem with that solution is that user-defined attributes (other than onload and onerror) that must be assigned through dot notation will not work with that solution. What's your thoughts on this?
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.
Hi @alexander-akait ,
hope you have nice summer weather =)
Could you please prioritise it a bit? Currently it's annoying as we have to use preload/prefetch with SRI, but chrome/safary/ff are not happy to preload with SRI but then load it without SRI.
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.
@utlime need rebase, I think it is stale PR, so feel free to send this code again and we will merge it
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.
@alexander-akait Not at all, I have been waiting for your response to my latest comment
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 think we can write warning in docs about some attributes, they are still internal and should not change, it will simplify logic
} | ||
|
||
// Append dynamic attributes | ||
MiniCssExtractPlugin.getCompilerHooks(compiler).customize.call( |
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.
Also let's rename customize
to setLinkAttributes
, it is more clear
const attributes = { | ||
href: `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId)`, | ||
rel: JSON.stringify('stylesheet'), | ||
onload: 'onLinkComplete', | ||
onerror: 'onLinkComplete', | ||
}; |
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 think we can write warning in docs about some attributes, they are still internal and should not change, it will simplify logic
@alexander-akait I started rebasing and changing the code based on your comments. Are you suggesting that we remove all properties (i.e. attributes that cannot be set through setAttribute) from the above attributes object?
Otherwise I suggest that we change the below declaration to
// Link properties cannot be set using setAttribute, but must instead be set through
// link.property = value. We maintain this list of known properties, so that we can
// combine attributes of both types in the above object.
const linkProperties = ['href', 'rel', 'type', 'onload', 'onerror'];
What do you think?
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 think docs about these attributes will be enough, they are internal and rewriting them is not safe
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.
Okay, so keep the above behavior but add section to docs?
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.
Yes 👍
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.
@alexander-akait Hi there, any plans on pulling this in soon? I followed the discussion and it seemed like you were almost done.
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.
解决了吗?目前也是这个sri问题,需要这个功能
Sorry for delay, really, if somebody still need it feel free to send a new PR /cc @patriksletmo maybe you want to finish it? |
This PR contains a:
Motivation / Use-Case
This contribution includes a hook that can be used to customize the attributes link tags for lazy loaded CSS chunks, and was inspired by #40. As stated in this issue, the new functionality allow plugins such as webpack-subresource-integrity to work properly with injected tags.
Example usage from an external plugin would look something like this:
Breaking Changes
None.
Additional Info
The added functionality is very simple, and gives a greater freedom to other plugins wishing to utilize this functionality. However, this also means that this contribution is quite small. Please let me know if there is anything that can be improved and I will do my best to implement changes in a timely manner.