From d360b10ab92ddabacd7c5e6da899f5bdf97a2d05 Mon Sep 17 00:00:00 2001 From: Geoffrey <11151445+g3offrey@users.noreply.github.com> Date: Wed, 15 Dec 2021 22:50:44 +0100 Subject: [PATCH 1/3] fix(cli): avoid write same output when no changes --- src/cli.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cli.js b/src/cli.js index daaea3150ca5..c143b5945e90 100644 --- a/src/cli.js +++ b/src/cli.js @@ -560,6 +560,7 @@ async function build() { let changedContent = [] let configDependencies = [] let contextDependencies = new Set() + let lastBuiltCSS = '' let watcher = null function refreshConfig() { @@ -664,12 +665,20 @@ async function build() { return process.stdout.write(result.css) } + if (result.css === lastBuiltCSS) { + // there is no change on the built CSS + // no need to rewrite the output file + return + } + await Promise.all( [ fs.promises.writeFile(output, result.css, () => true), result.map && fs.writeFile(output + '.map', result.map.toString(), () => true), ].filter(Boolean) ) + + lastBuiltCSS = result.css }) .then(() => { let end = process.hrtime.bigint() From a954031b5f255072987148feac8d7801c311a879 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 4 Jan 2022 15:18:34 +0100 Subject: [PATCH 2/3] generalize outputFile This function will check a cache, it will only write the file if: - The modified timestamps changed since last time we wrote something. This is useful to know if something changed by another tool or manually without diffing the full file. - The contents changed. --- src/cli.js | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/cli.js b/src/cli.js index c143b5945e90..6996e3161a83 100644 --- a/src/cli.js +++ b/src/cli.js @@ -41,6 +41,33 @@ function formatNodes(root) { } } +let outputStates = new Map() +async function outputFile(file, contents) { + if (outputStates.has(file)) { + let { contents: oldContents, stats: oldStats } = outputStates.get(file) + + let stats = await fs.promises.stat(file, { bigint: true }) + + // If the modified timestamp changed then we need to write the file. It + // could be that we end up with the exact same contents, but we already did + // the work internally so just writing it will be faster than comparing + // full output files. + // If the contents changed, then we also have to write the file. + if (stats.mtimeMs === oldStats.mtimeMs && contents === oldContents) { + return // Skip writing the file + } + } + + // Write the file + await fs.promises.writeFile(file, contents, 'utf8') + + // Store latest information in the cache + outputStates.set(file, { + contents, + stats: await fs.promises.stat(file, { bigint: true }), + }) +} + function help({ message, usage, commands, options }) { let indent = 2 @@ -534,6 +561,7 @@ async function build() { if (!output) { return process.stdout.write(result.css) } + return Promise.all( [ fs.promises.writeFile(output, result.css, () => true), @@ -560,7 +588,6 @@ async function build() { let changedContent = [] let configDependencies = [] let contextDependencies = new Set() - let lastBuiltCSS = '' let watcher = null function refreshConfig() { @@ -665,20 +692,12 @@ async function build() { return process.stdout.write(result.css) } - if (result.css === lastBuiltCSS) { - // there is no change on the built CSS - // no need to rewrite the output file - return - } - - await Promise.all( + return Promise.all( [ - fs.promises.writeFile(output, result.css, () => true), - result.map && fs.writeFile(output + '.map', result.map.toString(), () => true), + outputFile(output, result.css), + result.map && outputFile(output + '.map', result.map.toString()), ].filter(Boolean) ) - - lastBuiltCSS = result.css }) .then(() => { let end = process.hrtime.bigint() From df0c31474aa19f2ced9a610767249f15746c64b4 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 4 Jan 2022 16:16:57 +0100 Subject: [PATCH 3/3] further simplify checks Turns out that reading files and comparing them is fairly fast and there is no huge benefit over only using the Stats of the file and keeping track of that information. Thanks @kentcdodds! --- src/cli.js | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/cli.js b/src/cli.js index 6996e3161a83..817aa10259a8 100644 --- a/src/cli.js +++ b/src/cli.js @@ -41,31 +41,13 @@ function formatNodes(root) { } } -let outputStates = new Map() async function outputFile(file, contents) { - if (outputStates.has(file)) { - let { contents: oldContents, stats: oldStats } = outputStates.get(file) - - let stats = await fs.promises.stat(file, { bigint: true }) - - // If the modified timestamp changed then we need to write the file. It - // could be that we end up with the exact same contents, but we already did - // the work internally so just writing it will be faster than comparing - // full output files. - // If the contents changed, then we also have to write the file. - if (stats.mtimeMs === oldStats.mtimeMs && contents === oldContents) { - return // Skip writing the file - } + if (fs.existsSync(file) && (await fs.promises.readFile(file, 'utf8')) === contents) { + return // Skip writing the file } // Write the file await fs.promises.writeFile(file, contents, 'utf8') - - // Store latest information in the cache - outputStates.set(file, { - contents, - stats: await fs.promises.stat(file, { bigint: true }), - }) } function help({ message, usage, commands, options }) {