Closed hypernova7 closed 4 years ago
Hi Eddy, I managed to solve this problem!
By creating a micro plugin using hooks.
It was required to use webapp-webpack-plugin, this fork favicons-webpack-plugin with implemented support for the hook webappWebpackPluginBeforeEmit, but, unfortunately, without options mode
and devMode
.
Jan, Bruno, if you are reading this, please give the opportunity to inject it into a custom template from the box or add support for hooks to FaviconsWebpackPlugin or the devMode
option in the fork. Bad that you have to use crutches =(
The crutch itself:
(set below new HtmlWebpackPlugin({ ... })
and below new WebappWebpackPlugin({ ... })
)
new (class WebappTransponderWebpackPlugin {
apply(compiler) {
let iconsTags = [];
compiler.hooks.make.tap("WebappTransponderWebpackPlugin", (compilation) => {
compilation.hooks.webappWebpackPluginBeforeEmit.tapAsync("WebappTransponderWebpackPlugin", (data, cb) => {
iconsTags = data.tags.map((tag) => {
return {
tagName: tag.slice(1, tag.indexOf(' ')),
tagAttributes: tag.slice(tag.indexOf(' ') + 1, -2).split('" ').map((attr) => {
attr = attr.split('="');
return { attrName: attr[0], attrValue: attr[1] };
})
};
});
return cb(null, data);
});
compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync('WebappTransponderWebpackPlugin', (data, cb) => {
data.assets.favicons = (data.assets.favicons || []).concat(iconsTags);
return cb(null, data);
});
});
}
})()
Add in your .ejs template:
<% if (Array.isArray(htmlWebpackPlugin.files.favicons)) {
for (var item of htmlWebpackPlugin.files.favicons) { %>
<<%= item.tagName %><% for (var item of item.tagAttributes) { %> <%= item.attrName %>="<%= item.attrValue %>"<% } %>><%
}
} %>
Only works with webapp-webpack-plugin use new WebappWebpackPlugin ({ otions... })
.
Explanations:
Why are html lincs parsed? The first reason, for greater total flexibility, is the ability to calculate the functional by adding modifications to the inserted html tags.
The second reason, I tried to get link icon tags in the form of an object with parameters, from compilation.hooks.webappWebpackPluginBeforeEmit.tap...
, but there only files names and files themselves are the Buffer
object, everything else: tag name, path, media data, etc. only in the form of already assembled html.
This is my first experience in creating the use of webpack hooks and in general the first experience of writing a plugin for webpack, so I could not do everything absolutely right, but everything works!
For example, I have a suspicion that I am transferring incorrect names to hooks
-> tap
, although in this case it does not affect anything.
Hi Eddy, I managed to solve this problem!
By creating a micro plugin using hooks.
It was required to use WebappWebpackPlugin this fork FaviconsWebpackPlugin with implemented support for the hook webappWebpackPluginBeforeEmit but unfortunately with the options
mode
anddevMode
remaining. Jan, Bruno if you are reading this, please give the opportunity to inject it into a custom template from the box or add support for hooks to FaviconsWebpackPlugin or thedevMode
option in the fork. And then you have to use crutches =(The crutch itself:
new (class FaviconsTransponderWebpackPlugin { apply(compiler) { let lincIcons = []; compiler.hooks.make.tap("FaviconsTransponderWebpackPlugin", (compilation) => { compilation.hooks.webappWebpackPluginBeforeEmit.tapAsync("FaviconsTransponderWebpackPlugin", (data, cb) => { lincIcons = data.tags.map((tag) => { let tagName = tag.split(' ')[0].slice(1); tag = tag.slice(tagName.length + 1).slice(2, -1); let tagAttributes = tag.split('" ').reduce((attrs, attr) => { attr = attr.split('="'); attrs[attr[0]] = attr[1]; return attrs; }, {}); return tagAttributes; }); return cb(null, data); }); }); compiler.hooks.make.tap('FaviconsTransponderWebpackPlugin', (compilation) => { compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync('FaviconsTransponderWebpackPlugin', (data, cb) => { data.assets.icon = (data.assets.icon || []).concat(lincIcons); return cb(null, data); }); }); } })()
In your .ejs template:
<%if (Array.isArray(htmlWebpackPlugin.files.icon)) { for (let icon of htmlWebpackPlugin.files.icon) { %> <link<% for (key in icon) { %> <%= key %>="<%= icon[key] %>"<% } %>><% } } %>
Only works with WebappWebpackPlugin use
new WebappWebpackPlugin ({ otions... })
.Explanations:
Why are html lincs parsed? The first reason, for greater total flexibility, is the ability to calculate the functional by adding modifications to the inserted html tags. The second reason, I tried to get link icon tags in the form of an object with parameters, but from
compilation.hooks.webappWebpackPluginBeforeEmit.tapAsync
only html and something came in the formatBuffer
, I could not figure out whyBuffer
and what would be kept in it.This is my first experience in creating the use of webpack hooks and in general the first experience of writing a plugin for webpack, so I could not do everything absolutely right, but everything works! For example, I have a suspicion that I am transferring incorrect names to hooks tap, although in this case it does not affect anything.
@CoderAmigo Not what i was looking for
And what is wrong?
@CoderAmigo the properties of the meta tags, would be within the link tags, and would result in an incorrect html and would affect the SEO of the page.
@hypernova7 No, they will not do. Check it out.
The correct thing would be to pass the html that returns favicons-webpack-plungin to an object of html-webpack-plugin.
Something like that:
new HtmlWebpackPlugin({
htmlFavicons: Object // Object returned from FaviconsWebpackPlugin with html output
})
ejs template
<%- htmlWebpackPlugin.options.htmlFavicons %>
html returned
<meta name="..." content="...">
<link rel="..." href="..." type="...">
<!-- etc -->
Apparently your .ejs template is a "children's" - a simple variation.
Transmit assembled finished HTML is not true! Look at how advanced templates work. For example html-webpack-template look at index.ejs In principle, implementing it so that the ready-made html is sent to the html-webpack-plugin will not be a serious mistake, but it will not be flexible.
<% - htmlWebpackPlugin.options.htmlFavicons %>
You suggested putting the data received from the favicon plugin into htmlWebpackPlugin.options
in options, this is absolutely not right.
And now, with regard to the final result of the assembly, the solution that I gave initially leads to the result of the assembly that you are talking about: meta tags separately, link tags separately and in no way intersect, neither attributes nor how! And in what sequence they will be placed during assembly will depend on the template, just add to the right place:
<% if (Array.isArray(htmlWebpackPlugin.files.favicons)) {
for (var item of htmlWebpackPlugin.files.favicons) { %>
<<%= item.tagName %><% for (var item of item.tagAttributes) { %> <%= item.attrName %>="<%= item.attrValue %>"<% } %>><%
}
} %>
In general, to do exactly as you want is a very simple task, just compile the html not in the template but of my WebappTransponderWebpackPlugin
Just change here:
data.assets.favicons = (data.assets.favicons || []).concat(iconsTags);
And put the assembled html wherever you want. For example: data.assets.faviconsHtml
will be available in the template like this: <%= htmlWebpackPlugin.files.faviconsHtml %>
. But this is not right!
Or remove the html parsing from the code and transfer it in its original form, the original data is in data.tags
6 line in WebappTransponderWebpackPlugin
.
@CoderAmigo Thanks, I understood. And excuse me, I'm new to this for webpack plugins xD
@CoderAmigo And thanks for the code, I'll use it if it doesn't bother you.
@hypernova7 Thanks, I will be glad if my code will help in your work.
@hypernova7 It seems I initially did not quite understand what you were talking about.
I wrote down all the tags, substituting the name of the link
tag, I initially did not notice that the favicon plugin generates not only link
tags but also meta
tags.
And it turned out what you were possibly talking about, link
tags with meta
tags attributes were generated, you originally talk to me about this?
So I fixed it! + made a small but cool code refactoring)
...Updated the code in my posts.
@CoderAmigo If that's why I told you that, I was going to generate a badly formed HTML hahahaha
@CoderAmigo Thank you for modifying the code you showed me before. I will close this post
@hypernova7 The problem is not properly resolved. You can open this issue.
@CoderAmigo It is done
Cast summon spell, @brunocodutra, =)
@CoderAmigo Your code doesn't work anymore (not with 1.0.2 and not with 3.x version). What I'm trying to do is to get all the tags and save them to a file (to be manually injected by PHP). Previously I could tap into "webappWebpackPluginBeforeEmit" hook and get all the tags, but this is not possible anymore as "webappWebpackPluginBeforeEmit" hook doesn't exist enymore.
Have you managed to fix this?
Here is an example which is using
https://github.com/jantimon/favicons-webpack-plugin/tree/master/example/no-inject
Your <head>
might look like this:
Here is an example which is using
- html-webpack-plugin 4.x
- favicons-webpack-plugin 3.x
https://github.com/jantimon/favicons-webpack-plugin/tree/master/example/no-inject
Your
<head>
might look like this:
Yes, this is the route I chose to do it, but it does add other tags (like css links or the head tag) that are not needed. You can create an empty .ejs file with this inside:
<%= htmlWebpackPlugin.tags.headTags.filter((tag) => tag.toString().indexOf('.css"') === -1).join('') %>
This will output all tags without .css files. This works ok as a quick hack
You can also tell the html webpack to exclude all chunks if you don't want it to provide any js or css files.
@ioulian I assume that you have mixed up favicons-webpack-plugin and webapp-webpack-plugin. Current actual stable version webapp-webpack-plugin is 2.7.1
And I believe that @jantimon perhaps in the favicons-webpack-plugin 3.x version, realized the possibility of solving the problem under discussion in a more correct and concise way.
new FaviconsWebpackPlugin({
logo: 'logo.svg',
inject: htmlPlugin => {
// Your code that implements adding additional data for the .ejs template, here
return false
}
})
But all this will still look like a hack, but not a beautiful concise solution. But I like the option with the ability to use own code in the inject
function, but then all the necessary data should go to the function. Or maybe they already go, but I did not find information about this in the documentation for the favicons-webpack-plugin.
Let me explain for what purposes such customization tools may be required. For example, in some projects I use a fairly large, expansible, customizable template similar to the following this. And I need the ability to manage the data used in template.
We merged webapp back into the 2.x version of the favicons-webpack-plugin
You can disable any html-webpack-plugin if you set favicons:false
or provide a custom handler function.
This is the code which handles that logic:
However I am not sure if your question is related to this issue.
Here is an example which is using
* [html-webpack-plugin 4.x](https://www.npmjs.com/package/html-webpack-plugin/v/4.0.0-beta.11) * favicons-webpack-plugin 3.x
https://github.com/jantimon/favicons-webpack-plugin/tree/master/example/no-inject
Your
<head>
might look like this:
This is what I was referring to, thank you very much. (Sorry for replying so late, I mistakenly disabled notifications for this issue and just realized)
Thanks for the update :)
For example:
Output: