diff --git a/example/runtime/src/app.js b/example/runtime/src/app.js
index 85ec2fe0..e6473e78 100644
--- a/example/runtime/src/app.js
+++ b/example/runtime/src/app.js
@@ -1,3 +1,3 @@
-import tags from 'favicons-webpack-plugin/runtime/tags';
+import {tags} from 'favicons-webpack-plugin/runtime/tags';
console.log(tags);
\ No newline at end of file
diff --git a/runtime/tags.js b/runtime/tags.js
index b79fdff8..fd4873c3 100644
--- a/runtime/tags.js
+++ b/runtime/tags.js
@@ -3,9 +3,22 @@
//
// This is only a placeholder file for typescript and eslint
//
-// The real content for this file will be generated by runtime-loader.js
+// The real content for this file will be generated by favicons-webpack-plugin/src/runtime-loader.js
//
+/**
+ * All tags from all favicons compilations inside a flat array
+ *
+ * @type {string[]}
+ *
+ * @example
+```js
+[ '' ]
+```
+ */
+var tags = [];
+module.exports.tags = tags;
+
/**
* All tags from all favicons compilations
*
@@ -16,5 +29,5 @@
{ 'assets/': [ '' ] }
```
*/
-var tags = {};
-module.exports = tags;
\ No newline at end of file
+var tagsPerPublicPath = {};
+module.exports.tagsPerPublicPath = tagsPerPublicPath;
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 4ff3530e..54c88b1e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,5 +1,4 @@
// @ts-check
-
const assert = require('assert');
const parse5 = require('parse5');
const path = require('path');
@@ -9,7 +8,21 @@ const url = require('url');
const { resolvePublicPath, replaceContentHash } = require('./hash');
const { webpackLogger } = require('./logger');
const runtimeLoader = require('./runtime-loader');
-const attachedCompilers = new WeakSet();
+
+/**
+ * @type {WeakSet}
+ * Static set to track if a compiler has already a FaviconWebpackPlugin instance attached
+ */
+const attachedWebpackCompilers = new WeakSet();
+
+/**
+ * @typedef {{
+ tags: import('./html-tags').HtmlTagObject[],
+ publicPath: string,
+ assets: Array<{name: string, contents: import('webpack').sources.RawSource }>,
+ dependencies: string[],
+ }} FaviconCompilationResult
+ */
class FaviconsWebpackPlugin {
/**
@@ -36,11 +49,28 @@ class FaviconsWebpackPlugin {
*/
apply(compiler) {
compiler.hooks.initialize.tap('FaviconsWebpackPlugin', () => {
+ if (!attachedWebpackCompilers.has(compiler)) {
+ attachedWebpackCompilers.add(compiler);
+ this.hookIntoCompilerOnce(compiler);
+ }
this.hookIntoCompiler(compiler);
});
}
/**
+ * This hook is only executed once per compiler no matter how many
+ * FaviconsWebpackPlugins will be attached
+ *
+ * @param {import('webpack').Compiler} compiler
+ */
+ hookIntoCompilerOnce(compiler) {
+ // Add one loader to add support for `import { tags ] from 'favicons-webpack-plugin/runtime/tags'`
+ compiler.options.module.rules.push(runtimeLoader.moduleRuleConfig);
+ }
+
+ /**
+ * This hook is executed once per FaviconsWebpackPlugin instance
+ *
* @param {import('webpack').Compiler} compiler
*/
hookIntoCompiler(compiler) {
@@ -48,7 +78,7 @@ class FaviconsWebpackPlugin {
const Compilation = webpack.Compilation;
const NormalModule = webpack.NormalModule;
const oracle = new Oracle(compiler.context);
- /** @type {WeakMap}>>} */
+ /** @type {WeakMap>}>>} */
const faviconCompilations = new WeakMap();
{
@@ -69,12 +99,6 @@ class FaviconsWebpackPlugin {
});
}
- // Add one loader to add support for `import tags from 'favicons-webpack-plugin/runtime/tags'`
- if (!attachedCompilers.has(compiler)) {
- attachedCompilers.add(compiler);
- compiler.options.module.rules.push(runtimeLoader.moduleRuleConfig);
- }
-
if (this.options.logo === undefined) {
const defaultLogo = path.resolve(compiler.context, 'logo.png');
try {
@@ -132,7 +156,7 @@ class FaviconsWebpackPlugin {
);
// Inject favicons information into runtime tags
- // to allow `import tags from 'favicons-webpack-plugin/runtime/tags'`
+ // to allow `import { tags ] from 'favicons-webpack-plugin/runtime/tags'`
const tagsFilePath = require.resolve('../runtime/tags.js');
const normalModuleHooks = NormalModule.getCompilationHooks(compilation);
normalModuleHooks.loader.tap(
@@ -292,8 +316,9 @@ class FaviconsWebpackPlugin {
* @param {Buffer | string} baseManifest - the content of the file from options.manifest
* @param {import('webpack').Compilation} compilation
* @param {string} outputPath
+ * @returns {Promise}
*/
- generateFavicons(logo, baseManifest, compilation, outputPath) {
+ async generateFavicons(logo, baseManifest, compilation, outputPath) {
const resolvedPublicPath = getResolvedPublicPath(
logo.hash,
compilation,
@@ -305,6 +330,9 @@ class FaviconsWebpackPlugin {
? JSON.parse(baseManifest.toString() || '{}')
: this.options.manifest || {};
+ // File dependencies
+ const dependencies = (this.options.manifest === 'string' ? [this.options.manifest] : []).concat(this.options.logo);
+
switch (this.getCurrentCompilationMode(compilation.compiler)) {
case 'light':
if (!this.options.mode) {
@@ -312,25 +340,25 @@ class FaviconsWebpackPlugin {
'generate only a single favicon for fast compilation time in development mode. This behaviour can be changed by setting the favicon mode option.'
);
}
-
- return this.generateFaviconsLight(
+ const lightFaviconsCompilation = await this.generateFaviconsLight(
logo.content,
parsedBaseManifest,
compilation,
resolvedPublicPath,
- outputPath
+ outputPath,
);
+ return {...lightFaviconsCompilation, dependencies};
case 'webapp':
default:
webpackLogger(compilation).log('generate favicons');
-
- return this.generateFaviconsWebapp(
+ const webappFaviconsCompilation = await this.generateFaviconsWebapp(
logo.content,
parsedBaseManifest,
compilation,
resolvedPublicPath,
- outputPath
+ outputPath,
);
+ return {...webappFaviconsCompilation, dependencies};
}
}
diff --git a/src/runtime-loader.js b/src/runtime-loader.js
index d6d9fd4d..290caa6f 100644
--- a/src/runtime-loader.js
+++ b/src/runtime-loader.js
@@ -1,37 +1,39 @@
/// @ts-check
const htmlTagObjectToString = require('./html-tags').htmlTagObjectToString;
+/**
+ * Config used for the webpack config
+ */
+const moduleRuleConfig = Object.freeze({
+ test: require.resolve('../runtime/tags.js'),
+ use: 'favicons-webpack-plugin/src/runtime-loader'
+});
+
+/**
+ * @typedef {{
+ tags: import('./html-tags').HtmlTagObject[],
+ publicPath: string,
+ assets: Array<{name: string, contents: import('webpack').sources.RawSource }>,
+ dependencies: string[],
+ }} FaviconCompilationResult
+ */
+
/**
* The contextMap is a bridge which receives data from the
* favicons-webpack-plugin during the NormalModule loader phase
* see ./index.js
*
* @type {
- WeakMap;
- }>>>
+ WeakMap>>
}
*/
const contextMap = new WeakMap();
-/**
- * Config used for the webpack config
- */
-const moduleRuleConfig = Object.freeze({
- test: require.resolve('../runtime/tags.js'),
- use: 'favicons-webpack-plugin/src/runtime-loader'
-});
-
/**
* the main loader is only a placeholder which will have no effect
* as the pitch function returns
*
- * @this {{ async: () => ((err: Error | null, result: string) => void)}}
+ * @this {{ addDependency: (dependency: string) => void }}
*/
const loader = async function faviconsTagLoader() {
const faviconCompilationPromisses = contextMap.get(this);
@@ -41,6 +43,12 @@ const loader = async function faviconsTagLoader() {
const faviconCompilations = await Promise.all(faviconCompilationPromisses);
+ faviconCompilations.forEach(({dependencies}) => {
+ dependencies.forEach((dependency) => {
+ this.addDependency(dependency);
+ });
+ });
+
const tagsOfFaviconCompilations = faviconCompilations.map(
faviconCompilation => {
const { tags, publicPath } = faviconCompilation;
@@ -74,7 +82,20 @@ const loader = async function faviconsTagLoader() {
{}
);
- return `export default ${JSON.stringify(tagsByPublicPath)}`;
+ // create a flat array as it will be easier to use for all users
+ // who use only a single FaviconWebpackPlugin instance
+ const allTags = tagsOfFaviconCompilations.reduce(
+ (result, [key, value]) => {
+ result.push(...value);
+ return result;
+ },
+ /** @type {string[]} */([])
+ );
+
+ return `// Generated by favicons-webpack-plugin/runtime-loader
+export const tags = ${JSON.stringify(allTags)};
+export const tagsPerPublicPath = ${JSON.stringify(tagsByPublicPath)};
+`;
};
module.exports = Object.assign(loader, {