@@ -30,29 +30,27 @@ const faviconCache = new WeakMap();
3030 * Executes the generator function and caches the result in memory
3131 * The cache will be invalidated after the logo source file was modified
3232 *
33- * @param { import('./options').FaviconWebpackPlugionInternalOptions } faviconOptions
34- * @param { string } context - the compiler.context patth
35- * @param {WebpackCompilation } compilation - the current webpack compilation
33+ * @template TResult
34+ *
35+ * @param {string[] } files
3636 * @param {any } pluginInstance - the plugin instance to use as cache key
37- * @param {(
38- logoSource: Buffer | string,
39- compilation: WebpackCompilation,
40- resolvedPublicPath: string,
41- outputPath: string
42- ) => Promise<FaviconsCompilationResult>
43- } generator
37+ * @param {boolean } useWebpackCache - Support webpack built in cache
38+ * @param {WebpackCompilation } compilation - the current webpack compilation
39+ * @param {string[] } eTags - eTags to verify the string
40+ * @param {(files: { filePath: string, hash: string, content: Buffer }[]) => string } idGenerator
41+ * @param {(files: { filePath: string, hash: string, content: Buffer }[], id: string) => Promise<TResult> } generator
4442 *
45- * @returns {Promise<FaviconsCompilationResult > }
43+ * @returns {Promise<TResult > }
4644 */
4745function runCached (
48- faviconOptions ,
49- context ,
50- compilation ,
46+ files ,
5147 pluginInstance ,
48+ useWebpackCache ,
49+ compilation ,
50+ eTags ,
51+ idGenerator ,
5252 generator
5353) {
54- const { logo } = faviconOptions ;
55-
5654 const latestSnapShot = snapshots . get ( pluginInstance ) ;
5755 const cachedFavicons = latestSnapShot && faviconCache . get ( latestSnapShot ) ;
5856
@@ -64,10 +62,11 @@ function runCached(
6462 faviconCache . delete ( latestSnapShot ) ;
6563
6664 return runCached (
67- faviconOptions ,
68- context ,
69- compilation ,
65+ files ,
7066 pluginInstance ,
67+ compilation ,
68+ idGenerator ,
69+ eTags ,
7170 generator
7271 ) ;
7372 }
@@ -81,21 +80,19 @@ function runCached(
8180 // to find out if the logo was changed
8281 const newSnapShot = createSnapshot (
8382 {
84- fileDependencies : [ logo ] ,
83+ fileDependencies : files ,
8584 contextDependencies : [ ] ,
8685 missingDependencies : [ ]
8786 } ,
8887 compilation
8988 ) ;
9089 snapshots . set ( pluginInstance , newSnapShot ) ;
91-
9290 // Start generating the favicons
93- const faviconsGenerationsPromise = runWithFileCache (
94- faviconOptions ,
95- context ,
96- compilation ,
97- generator
98- ) ;
91+ const faviconsGenerationsPromise = useWebpackCache
92+ ? runWithFileCache ( files , compilation , idGenerator , eTags , generator )
93+ : readFiles ( files , compilation ) . then ( fileContents =>
94+ generator ( fileContents , idGenerator ( fileContents ) )
95+ ) ;
9996
10097 // Store the promise of the favicon compilation in cache
10198 faviconCache . set ( newSnapShot , faviconsGenerationsPromise ) ;
@@ -128,101 +125,68 @@ function createSnapshot(fileDependencies, mainCompilation) {
128125}
129126
130127/**
128+ *
129+ * Use the webpack cache which supports filesystem caching to improve build speed
130+ * See also https://webpack.js.org/configuration/other-options/#cache
131+ * Create one cache for every output target
132+ *
131133 * Executes the generator function and stores it in the webpack file cache
134+ * @template TResult
132135 *
133- * @param {import('./options').FaviconWebpackPlugionInternalOptions } faviconOptions
134- * @param {string } context - the compiler.context patth
136+ * @param {string[] } files - the file pathes to be watched for changes
135137 * @param {WebpackCompilation } compilation - the current webpack compilation
136- * @param {(
137- logoSource: Buffer | string,
138- compilation: WebpackCompilation,
139- resolvedPublicPath: string,
140- outputPath: string
141- ) => Promise<FaviconsCompilationResult>
142- } generator
138+ * @param {(files: { filePath: string, hash: string, content: Buffer }[]) => string } idGenerator
139+ * @param {string[] } eTags - eTags to verify the string
140+ * @param {(files: { filePath: string, hash: string, content: Buffer }[], id: string) => Promise<TResult> } generator
143141 *
144- * @returns {Promise<FaviconsCompilationResult > }
142+ * @returns {Promise<TResult > }
145143 */
146144async function runWithFileCache (
147- faviconOptions ,
148- context ,
145+ files ,
149146 compilation ,
147+ idGenerator ,
148+ eTags ,
150149 generator
151150) {
152- const { logo } = faviconOptions ;
153- const logoSource = await new Promise ( ( resolve , reject ) =>
154- compilation . inputFileSystem . readFile (
155- path . resolve ( context , logo ) ,
156- ( error , fileBuffer ) => {
157- if ( error ) {
158- reject ( error ) ;
159- } else {
160- resolve ( fileBuffer ) ;
161- }
162- }
163- )
164- ) ;
165-
166- const compilationOutputPath =
167- compilation . outputOptions . path === 'auto'
168- ? ''
169- : compilation . outputOptions . path || '' ;
170- /**
171- * the relative output path to the folder where the favicon files should be generated to
172- * it might include tokens like [fullhash] or [contenthash]
173- */
174- const relativeOutputPath = faviconOptions . outputPath
175- ? path . relative (
176- compilationOutputPath ,
177- path . resolve ( compilationOutputPath , faviconOptions . outputPath )
178- )
179- : faviconOptions . prefix ;
180-
181- const logoContentHash = getContentHash ( logoSource ) ;
182- const executeGenerator = ( ) => {
183- const outputPath = replaceContentHash (
184- compilation ,
185- relativeOutputPath ,
186- logoContentHash
187- ) ;
188- const webpackPublicPath =
189- compilation . outputOptions . publicPath === 'auto'
190- ? ''
191- : compilation . outputOptions . publicPath ;
192- const resolvedPublicPath = replaceContentHash (
193- compilation ,
194- resolvePublicPath (
195- compilation ,
196- faviconOptions . publicPath || webpackPublicPath ,
197- faviconOptions . prefix
198- ) ,
199- logoContentHash
200- ) ;
201- return generator ( logoSource , compilation , resolvedPublicPath , outputPath ) ;
202- } ;
203-
204- if ( faviconOptions . cache === false ) {
205- return executeGenerator ( ) ;
206- }
207-
151+ const fileSources = await readFiles ( files , compilation ) ;
208152 const webpackCache = compilation . getCache ( 'favicons-webpack-plugin' ) ;
209153 // Cache invalidation token
210- const eTag = [
211- JSON . stringify ( faviconOptions . publicPath ) ,
212- JSON . stringify ( faviconOptions . mode ) ,
213- // Recompile filesystem cache if the user change the favicon options
214- JSON . stringify ( faviconOptions . favicons ) ,
215- // Recompile filesystem cache if the logo source changes:
216- logoContentHash
217- ] . join ( '\n' ) ;
218-
219- // Use the webpack cache which supports filesystem caching to improve build speed
220- // See also https://webpack.js.org/configuration/other-options/#cache
221- // Create one cache for every output target
222- return webpackCache . providePromise (
223- relativeOutputPath ,
224- eTag ,
225- executeGenerator
154+ const eTag = [ ...eTags , fileSources . map ( ( { hash } ) => hash ) ] . join ( ' ' ) ;
155+ const cacheId = idGenerator ( fileSources ) ;
156+ return webpackCache . providePromise ( cacheId , eTag , ( ) =>
157+ generator ( fileSources , cacheId )
158+ ) ;
159+ }
160+
161+ /**
162+ * readFiles and get content hashes
163+ *
164+ * @param {string[] } files
165+ * @param {WebpackCompilation } compilation
166+ * @returns {Promise<{filePath: string, hash: string, content: Buffer}[]> }
167+ */
168+ function readFiles ( files , compilation ) {
169+ return Promise . all (
170+ files . map ( filePath =>
171+ ! filePath
172+ ? { filePath, hash : '' , content : '' }
173+ : new Promise ( ( resolve , reject ) =>
174+ compilation . inputFileSystem . readFile (
175+ path . resolve ( compilation . compiler . context , filePath ) ,
176+ ( error , fileBuffer ) => {
177+ if ( error ) {
178+ reject ( error ) ;
179+ } else {
180+ resolve ( {
181+ filePath,
182+ hash : getContentHash ( fileBuffer ) ,
183+ content : fileBuffer
184+ } ) ;
185+ }
186+ }
187+ )
188+ )
189+ )
226190 ) ;
227191}
228192
0 commit comments