-
-
Notifications
You must be signed in to change notification settings - Fork 210
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 rule to get generated html tags from js #166
base: main
Are you sure you want to change the base?
Conversation
@vonagam are you willing to provide a code example of adding this tags into the head with Next.js? I am attempting to utilize your fork, but have struggled to identify how I might use the array of HTML tags to insert them into Next.js' import Document, { Html, Head, Main, NextScript } from 'next/document'
import favicon from "/images/icon.png";
class MyDocument extends Document {
render() {
return (
<Html>
{ /* How might I add the tags into the Head below? */}
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument |
@jantimon if I work get these tests passing, are you willing to merge this existing |
Here is the example code that can be used to convert an array of favicon html tags to array of react elements without import htmls from './path/to/favicon.svg';
const elements = htmls.map( ( html, index ) => {
const match = html.match( /^<(\S+)\s*(.+?)\s*\/?>$/ );
const tag = match[ 1 ];
const attributes = match[ 2 ].split( /\s(?=\S+?=")/ );
const props = {};
for ( const attribute of attributes ) {
const match = attribute.match( /^(\S+?)="(.+?)"$/ );
const key = match[ 1 ];
const value = match[ 2 ];
props[ key ] = value;
}
const element = React.createElement( tag, { key: index, ...props } );
return element;
} );
export default elements; By the way, tests are passing in current form (reported checks failure has nothing to do with changes in this PR). Rebased on current head and updated tests to use snapshots. |
aef2f1d
to
9ac68f6
Compare
This feature was removed as it is not the intention of this plugin to move the favicons meta data to the client code. However I see your case for next.js. @brunocodutra did already a lot of work to prepare a new major version (probably version favicons-webpack-plugin 3.0) which is probably going to change a little bit how the loader works. Merging your changes would create some merge issues - but I really think we could get it to work somehow - please give me some time to think about it :) |
Rebased on current head. Ping me when you think it is good time to merge this PR and i'll rebase again. |
Hey @vonagam I took the time to prepare the 3.x version: #184 Right now your pull request works as follows:
Wouldn't it be better in your case if it would rather be like this: const tags = require('favicons-webpack-plugin/src/loader!./logo.svg');
// or
import * as tags from 'favicons-webpack-plugin/src/loader!./logo.svg'; which would generate the tags into a map e.g. async function generateFavicons (content) {
// generateFavicons code
}
module.exports.compilations = new Map();
module.exports = (content) => {
const favicons = generateFavicons.call(this, content);
module.exports.compilations.set(this.query, favicons);
return favicons;
} In addition we split the FaviconsWebpackPlugin into two parts:
The the What do you think? |
Hm... so, if i understand your idea correctly, instead of: const logo = '...';
const options = { ... };
const plugin = new FaviconsWebpackPlugin( { logo, ...options } );
const rule = plugin.rule();
const config = {
plugins: [ plugin ],
rules: [ rule ],
}; One will be able to write: const logo = '...';
const options = { ... };
const rule = { include: logo, loader: 'favicons-webpack-plugin/src/loader', options };
const config = {
rules: [ rule ],
}; If so, it can be considered more user friendly for such use cases and i would welcome such development. But i personally will be happy either way as long as i get access to generated tags. |
The favicon dropped the loader approach for performance reasons.. |
But this PR is not dependent on favicon using the loader approach. |
Hey @vonagam you are right - your approach is independent! One problem is the following: https://webpack.js.org/configuration/module/#useentry
Do you have any idea how we could solve it without passing the plugin instance to the loader? |
There are two questions here: First question: How to properly handle stringification if a plugin instance is present in options? Next paragraph answers this:
So const logo = '...';
const options = { ... };
const plugin = new FaviconsWebpackPlugin( { logo, ...options } );
const rule = plugin.rule(); // contains plugin instance and ident in options
const config = {
plugins: [ plugin ],
rules: [ rule ],
}; Second question: How to make it work without passing a plugin instance to the loader? For that to work you will need to allow passing said unique id to a plugin constructor: const logo = '...';
const options = { ... };
const config = {
plugins: [ new FaviconsWebpackPlugin( { logo, id: 'unique-id', ...options } ) ],
rules: [ { include: logo, loader: 'favicons-webpack-plugin/src/loader', options: { id: 'unique-id' } ],
}; |
A plugin is able to add loader rules to the compiler so we could do somethink like: const faviconCompilerId = new WeakMap();
class FaviconWebpackPlugin {
apply( compiler ) {
this.id = faviconCompilerId.get(compiler) || 0;
faviconCompilerId.set(this.id + 1);
// Inject rule into compiler using this.id
// this is untested pseudo code:
compiler.options.module.rules.push( {
include: logo,
resourceQuery: (query) => query.indexOf('faviconTags') > -1,
loader: 'favicons-webpack-plugin/src/loader',
options: { id: this.id }
})
}
} it would allow sth like:
the there are some challenges:
|
Have not thought that anybody will have multiple plugin instances for same favicon file. But if this is something worth supporting, then maybe we can add an option, let's say class FaviconWebpackPlugin {
apply( compiler ) {
if (this.options.tagsName) {
compiler.options.module.rules.push( {
include: this.options.tagsModule,
loader: 'favicons-webpack-plugin/src/loader',
options: { plugin: this, ident: this.options.tagsModule }
})
}
}
} And to import tags: import tags from 'faviconTags'; // with options.tagsModule set to 'faviconTags' |
I just gave it a try and the loader injection works really well - I managed to have typescript support! :) It looks like this: import tags from 'favicons/runtime/tags';
console.log(tags); I although have an idea how we can support multiple logos - the unique key is Now there is only one open question how can the loader get the tags from the plugin without creating a memory leak? |
There is a leak even when you use |
I found a way to inject it: const tagsFilePath = require.resolve('../runtime/tags.js');
compiler.webpack.NormalModule.getCompilationHooks(compilation).loader.tap('FaviconsWebpackPlugin', (loaderContext, normalModule) => {
if (normalModule.resource === tagsFilePath) {
runtimeLoader.contextMap.set(loaderContext, faviconCompilation);
}
}); This allows accessing the data inside the loader: const loader = function () {
const callback = this.async();
const faviconCompilation = contextMap.get(this);
if (!faviconCompilation) {
throw new Error('broken contextMap');
}
faviconCompilation.then(({tags}) => {
callback(null, `export default ${JSON.stringify(tags)}`);
});
} I created also an example inside the import tags from 'favicons-webpack-plugin/runtime/tags';
console.log(tags); which compiles to: (() => {
'use strict';
console.log([
'<link rel="shortcut icon" href="favicon.ico">',
'<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">',
'<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">',
'<link rel="icon" type="image/png" sizes="48x48" href="favicon-48x48.png">',
'<link rel="manifest" href="manifest.json">',
'<meta name="mobile-web-app-capable" content="yes">',
'<meta name="theme-color" content="#fff">',
'<meta name="application-name" content="webapp-example">'
// ....
]);
})(); |
All changes have been pushed to your branch so please take a look :) Open points:
|
What exactly does it mean?
Yeah, i see that you manually prepend path to resulted tags. Same should be done here too. (Should not be hard to do with regexps.)
It is possible to use query param to distinguish them (either some id that person will pass in or something about
Since
Ideally |
hashes are placeholders which allow you to use I added a new commit which:
Typings work too (even in .js files without writing any typings): Right now two things are missing:
|
Okay I added dependency tracking to the loader :) I also adjusted the api a little bit as 99% of the users will probably have only 1 favicons plugin running. import {tags} from 'favicons-webpack-plugin/runtime/tags';
console.log(tags); import {tagsPerPublicPath} from 'favicons-webpack-plugin/runtime/tags';
console.log(tagsPerPublicPath); so all we need now is intensive testing |
Webpack decided to not deprecate @vonagam did you already have time to test the solution? |
Nope, sorry, bussy with other stuff... |
No worries :) |
@vonagam I guess this feature isn't needed anymore - can we close this? |
It is needed, but i at the moment work on non webpack realted things... I assume that i will work with webpack again in a month or so. But, as i understand, everything is already done by you and you just want this to be tested in a field? |
For what it's worth, I would still like to see this feature merged. I appreciate your work on it @vonagam. |
I was doing non-js stuff longer than i anticipated... Anyway, rebased PR on main branch. The only thing that i changed was to add Run |
This issue had no activity for at least half a year. It's subject to automatic issue closing if there is no activity in the next 15 days. |
9a9b961
to
58ee4d9
Compare
Functionality from
webapp-webpack-plugin
which was introduced in this PR but for some reason was not ported back here.My use case: usage with Next.js. Using
html-webpack-plugin
is not an option there, but with this addition it is possible to access html tags generated by the plugin for insertion into a head.