diff --git a/test/cli/samples/wait-for-bundle-input-object/_config.js b/test/cli/samples/wait-for-bundle-input-object/_config.js index 981f3ba47a9..4bda6e35d5c 100644 --- a/test/cli/samples/wait-for-bundle-input-object/_config.js +++ b/test/cli/samples/wait-for-bundle-input-object/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { atomicWriteFileSync } = require('../../../utils'); let second; let third; @@ -17,9 +18,9 @@ module.exports = { }, abortOnStderr(data) { if (data.includes('waiting for input second')) { - fs.writeFileSync(second, "export default 'second'"); + atomicWriteFileSync(second, "export default 'second'"); } else if (data.includes('waiting for input third')) { - fs.writeFileSync(third, "export default 'third'"); + atomicWriteFileSync(third, "export default 'third'"); } } }; diff --git a/test/cli/samples/wait-for-bundle-input/_config.js b/test/cli/samples/wait-for-bundle-input/_config.js index 045097c443b..64f197395b4 100644 --- a/test/cli/samples/wait-for-bundle-input/_config.js +++ b/test/cli/samples/wait-for-bundle-input/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { atomicWriteFileSync } = require('../../../utils'); let mainFile; @@ -15,7 +16,7 @@ module.exports = { abortOnStderr(data) { if (data.includes('waiting for input main.js')) { // wait longer than one polling interval - setTimeout(() => fs.writeFileSync(mainFile, 'export default 42;'), 600); + setTimeout(() => atomicWriteFileSync(mainFile, 'export default 42;'), 600); } } }; diff --git a/test/cli/samples/watch/bundle-error/_config.js b/test/cli/samples/watch/bundle-error/_config.js index 7b101a1bc14..2f38a2e2407 100644 --- a/test/cli/samples/watch/bundle-error/_config.js +++ b/test/cli/samples/watch/bundle-error/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { atomicWriteFileSync } = require('../../../../utils'); let mainFile; @@ -16,7 +17,7 @@ module.exports = { }, abortOnStderr(data) { if (data.includes('Error: Unexpected token')) { - setTimeout(() => fs.writeFileSync(mainFile, 'export default 42;'), 500); + setTimeout(() => atomicWriteFileSync(mainFile, 'export default 42;'), 500); return false; } if (data.includes('created _actual')) { diff --git a/test/cli/samples/watch/watch-config-early-update/_config.js b/test/cli/samples/watch/watch-config-early-update/_config.js index c13c2690ab6..cd26f5c990c 100644 --- a/test/cli/samples/watch/watch-config-early-update/_config.js +++ b/test/cli/samples/watch/watch-config-early-update/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { atomicWriteFileSync } = require('../../../../utils'); let configFile; @@ -34,7 +35,7 @@ module.exports = { }, abortOnStderr(data) { if (data === 'initial\n') { - fs.writeFileSync( + atomicWriteFileSync.writeFileSync( configFile, ` console.error('updated'); diff --git a/test/cli/samples/watch/watch-config-error/_config.js b/test/cli/samples/watch/watch-config-error/_config.js index caa161f4052..149901b3e4e 100644 --- a/test/cli/samples/watch/watch-config-error/_config.js +++ b/test/cli/samples/watch/watch-config-error/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { atomicWriteFileSync } = require('../../../../utils'); let configFile; @@ -25,12 +26,12 @@ module.exports = { }, abortOnStderr(data) { if (data.includes(`created _actual${path.sep}main1.js`)) { - fs.writeFileSync(configFile, 'throw new Error("Config contains errors");'); + atomicWriteFileSync(configFile, 'throw new Error("Config contains errors");'); return false; } if (data.includes('Config contains errors')) { setTimeout(() => { - fs.writeFileSync( + atomicWriteFileSync( configFile, 'export default {\n' + '\tinput: "main.js",\n' + diff --git a/test/cli/samples/watch/watch-config-initial-error/_config.js b/test/cli/samples/watch/watch-config-initial-error/_config.js index 9fd2eb4def9..d2ecd6532ec 100644 --- a/test/cli/samples/watch/watch-config-initial-error/_config.js +++ b/test/cli/samples/watch/watch-config-initial-error/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { atomicWriteFileSync } = require('../../../../utils'); let configFile; @@ -16,7 +17,7 @@ module.exports = { async abortOnStderr(data) { if (data.includes('Config contains initial errors')) { await new Promise(resolve => setTimeout(resolve, 100)); - fs.writeFileSync( + atomicWriteFileSync( configFile, 'export default {\n' + '\tinput: "main.js",\n' + diff --git a/test/cli/samples/watch/watch-config-no-update/_config.js b/test/cli/samples/watch/watch-config-no-update/_config.js index 635d77d42d9..7bc29a79ab1 100644 --- a/test/cli/samples/watch/watch-config-no-update/_config.js +++ b/test/cli/samples/watch/watch-config-no-update/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const { atomicWriteFileSync } = require('../../../../utils'); let configFile; const configContent = @@ -23,7 +24,7 @@ module.exports = { }, abortOnStderr(data) { if (data.includes('created _actual/main.js')) { - fs.writeFileSync(configFile, configContent); + atomicWriteFileSync(configFile, configContent); return new Promise(resolve => setTimeout(() => resolve(true), 500)); } }, diff --git a/test/utils.js b/test/utils.js index beb2c60d97b..23aecff9d3a 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,4 +1,5 @@ const assert = require('assert'); +const fs = require('fs'); const path = require('path'); const fixturify = require('fixturify'); const sander = require('sander'); @@ -14,6 +15,7 @@ exports.runTestSuiteWithSamples = runTestSuiteWithSamples; exports.assertDirectoriesAreEqual = assertDirectoriesAreEqual; exports.assertFilesAreEqual = assertFilesAreEqual; exports.assertIncludes = assertIncludes; +exports.atomicWriteFileSync = atomicWriteFileSync; function normaliseError(error) { delete error.stack; @@ -220,3 +222,12 @@ function assertIncludes(actual, expected) { throw err; } } + +// Workaround a race condition in fs.writeFileSync that temporarily creates +// an empty file for a brief moment which may be read by rollup watch - even +// if the content being overwritten is identical. +function atomicWriteFileSync(filePath, contents) { + const stagingPath = filePath + '_'; + fs.writeFileSync(stagingPath, contents); + fs.renameSync(stagingPath, filePath); +}