Skip to content

Commit

Permalink
feat: add manifest option
Browse files Browse the repository at this point in the history
  • Loading branch information
jantimon committed Jan 23, 2021
1 parent 7293186 commit e2928c4
Show file tree
Hide file tree
Showing 37 changed files with 1,227 additions and 167 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,22 @@ If the webpack mode is set to `production` the favicons mode will use `webapp`.

This behaviour can be adjusted by setting the favicon `mode` and `devMode` options.

### Custom manifests

The manifest options allows to overwrite values of the generated manifest.json with own values

```javascript
const FaviconsWebpackPlugin = require('favicons-webpack-plugin')

plugins: [
new FaviconsWebpackPlugin({
logo: './src/logo.png',
mode: 'webapp',
manifest: './src/manigest.json'
})
]
```

## Compatibility

favicons-webpack-plugin 2.x is compatible with html-webpack-plugin 3.x
Expand Down
10 changes: 10 additions & 0 deletions example/custom-manifest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "webapp-example",
"version": "1.0.0",
"description": "Demo of webpapp webpack plugin",
"scripts": {
},
"keywords": [],
"author": "",
"license": "MIT"
}
Empty file.
Binary file added example/custom-manifest/src/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions example/custom-manifest/src/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions example/custom-manifest/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Home</title>
</head>

<body>
</body>

</html>
11 changes: 11 additions & 0 deletions example/custom-manifest/src/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "FaviconsDemo",
"short_name": "FaviconsDemo",
"description": "Just a demo",
"dir": "auto",
"lang": "en",
"display": "standalone",
"background_color": "#fff",
"theme_color": "#fff",
"orientation": null
}
32 changes: 32 additions & 0 deletions example/custom-manifest/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const FaviconsWebpackPlugin = require('../../src/');

const webpack = require('webpack');

module.exports = (env, args) => {
return {
context: __dirname,
entry: './src/app.js',
output: {
path: resolve(__dirname, 'public'),
filename: 'app.js',
},
cache: {
type: 'filesystem',
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html',
}),
new FaviconsWebpackPlugin({
logo: './src/favicon.png',
manifest: './src/manifest.json',
mode: 'webapp'

}),
],
stats: "errors-only"
};
}
186 changes: 75 additions & 111 deletions src/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,27 @@ const faviconCache = new WeakMap();
* Executes the generator function and caches the result in memory
* The cache will be invalidated after the logo source file was modified
*
* @param {import('./options').FaviconWebpackPlugionInternalOptions} faviconOptions
* @param {string} context - the compiler.context patth
* @param {WebpackCompilation} compilation - the current webpack compilation
* @template TResult
*
* @param {string[]} files
* @param {any} pluginInstance - the plugin instance to use as cache key
* @param {(
logoSource: Buffer | string,
compilation: WebpackCompilation,
resolvedPublicPath: string,
outputPath: string
) => Promise<FaviconsCompilationResult>
} generator
* @param {boolean} useWebpackCache - Support webpack built in cache
* @param {WebpackCompilation} compilation - the current webpack compilation
* @param {string[]} eTags - eTags to verify the string
* @param {(files: { filePath: string, hash: string, content: Buffer }[]) => string} idGenerator
* @param {(files: { filePath: string, hash: string, content: Buffer }[], id: string) => Promise<TResult>} generator
*
* @returns {Promise<FaviconsCompilationResult>}
* @returns {Promise<TResult>}
*/
function runCached(
faviconOptions,
context,
compilation,
files,
pluginInstance,
useWebpackCache,
compilation,
eTags,
idGenerator,
generator
) {
const { logo } = faviconOptions;

const latestSnapShot = snapshots.get(pluginInstance);
const cachedFavicons = latestSnapShot && faviconCache.get(latestSnapShot);

Expand All @@ -64,10 +62,11 @@ function runCached(
faviconCache.delete(latestSnapShot);

return runCached(
faviconOptions,
context,
compilation,
files,
pluginInstance,
compilation,
idGenerator,
eTags,
generator
);
}
Expand All @@ -81,21 +80,19 @@ function runCached(
// to find out if the logo was changed
const newSnapShot = createSnapshot(
{
fileDependencies: [logo],
fileDependencies: files,
contextDependencies: [],
missingDependencies: []
},
compilation
);
snapshots.set(pluginInstance, newSnapShot);

// Start generating the favicons
const faviconsGenerationsPromise = runWithFileCache(
faviconOptions,
context,
compilation,
generator
);
const faviconsGenerationsPromise = useWebpackCache
? runWithFileCache(files, compilation, idGenerator, eTags, generator)
: readFiles(files, compilation).then(fileContents =>
generator(fileContents, idGenerator(fileContents))
);

// Store the promise of the favicon compilation in cache
faviconCache.set(newSnapShot, faviconsGenerationsPromise);
Expand Down Expand Up @@ -128,101 +125,68 @@ function createSnapshot(fileDependencies, mainCompilation) {
}

/**
*
* Use the webpack cache which supports filesystem caching to improve build speed
* See also https://webpack.js.org/configuration/other-options/#cache
* Create one cache for every output target
*
* Executes the generator function and stores it in the webpack file cache
* @template TResult
*
* @param {import('./options').FaviconWebpackPlugionInternalOptions} faviconOptions
* @param {string} context - the compiler.context patth
* @param {string[]} files - the file pathes to be watched for changes
* @param {WebpackCompilation} compilation - the current webpack compilation
* @param {(
logoSource: Buffer | string,
compilation: WebpackCompilation,
resolvedPublicPath: string,
outputPath: string
) => Promise<FaviconsCompilationResult>
} generator
* @param {(files: { filePath: string, hash: string, content: Buffer }[]) => string} idGenerator
* @param {string[]} eTags - eTags to verify the string
* @param {(files: { filePath: string, hash: string, content: Buffer }[], id: string) => Promise<TResult>} generator
*
* @returns {Promise<FaviconsCompilationResult>}
* @returns {Promise<TResult>}
*/
async function runWithFileCache(
faviconOptions,
context,
files,
compilation,
idGenerator,
eTags,
generator
) {
const { logo } = faviconOptions;
const logoSource = await new Promise((resolve, reject) =>
compilation.inputFileSystem.readFile(
path.resolve(context, logo),
(error, fileBuffer) => {
if (error) {
reject(error);
} else {
resolve(fileBuffer);
}
}
)
);

const compilationOutputPath =
compilation.outputOptions.path === 'auto'
? ''
: compilation.outputOptions.path || '';
/**
* the relative output path to the folder where the favicon files should be generated to
* it might include tokens like [fullhash] or [contenthash]
*/
const relativeOutputPath = faviconOptions.outputPath
? path.relative(
compilationOutputPath,
path.resolve(compilationOutputPath, faviconOptions.outputPath)
)
: faviconOptions.prefix;

const logoContentHash = getContentHash(logoSource);
const executeGenerator = () => {
const outputPath = replaceContentHash(
compilation,
relativeOutputPath,
logoContentHash
);
const webpackPublicPath =
compilation.outputOptions.publicPath === 'auto'
? ''
: compilation.outputOptions.publicPath;
const resolvedPublicPath = replaceContentHash(
compilation,
resolvePublicPath(
compilation,
faviconOptions.publicPath || webpackPublicPath,
faviconOptions.prefix
),
logoContentHash
);
return generator(logoSource, compilation, resolvedPublicPath, outputPath);
};

if (faviconOptions.cache === false) {
return executeGenerator();
}

const fileSources = await readFiles(files, compilation);
const webpackCache = compilation.getCache('favicons-webpack-plugin');
// Cache invalidation token
const eTag = [
JSON.stringify(faviconOptions.publicPath),
JSON.stringify(faviconOptions.mode),
// Recompile filesystem cache if the user change the favicon options
JSON.stringify(faviconOptions.favicons),
// Recompile filesystem cache if the logo source changes:
logoContentHash
].join('\n');

// Use the webpack cache which supports filesystem caching to improve build speed
// See also https://webpack.js.org/configuration/other-options/#cache
// Create one cache for every output target
return webpackCache.providePromise(
relativeOutputPath,
eTag,
executeGenerator
const eTag = [...eTags, fileSources.map(({ hash }) => hash)].join(' ');
const cacheId = idGenerator(fileSources);
return webpackCache.providePromise(cacheId, eTag, () =>
generator(fileSources, cacheId)
);
}

/**
* readFiles and get content hashes
*
* @param {string[]} files
* @param {WebpackCompilation} compilation
* @returns {Promise<{filePath: string, hash: string, content: Buffer}[]>}
*/
function readFiles(files, compilation) {
return Promise.all(
files.map(filePath =>
!filePath
? { filePath, hash: '', content: '' }
: new Promise((resolve, reject) =>
compilation.inputFileSystem.readFile(
path.resolve(compilation.compiler.context, filePath),
(error, fileBuffer) => {
if (error) {
reject(error);
} else {
resolve({
filePath,
hash: getContentHash(fileBuffer),
content: fileBuffer
});
}
}
)
)
)
);
}

Expand Down
Loading

0 comments on commit e2928c4

Please sign in to comment.