Skip to content

Commit dc4c455

Browse files
authored
fix(generator): avoid doing redundant write operations (#6011)
Avoid overwriting the file which is not modified when executing `vue add/invoke` This will reduce the chance of encountering issues like #5939
1 parent d743514 commit dc4c455

File tree

3 files changed

+79
-3
lines changed

3 files changed

+79
-3
lines changed

packages/@vue/cli/__tests__/Generator.spec.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,52 @@ test('api: addConfigTransform transform vue warn', async () => {
933933
})).toBe(true)
934934
})
935935

936+
test('avoid overwriting files that have not been modified', async () => {
937+
const generator = new Generator('/', {
938+
plugins: [
939+
{
940+
id: 'test1',
941+
apply: (api, options) => {
942+
api.render((files, render) => {
943+
files['foo.js'] = render('foo()')
944+
})
945+
}
946+
}
947+
],
948+
files: {
949+
// skip writing to this file
950+
'existFile.js': 'existFile()'
951+
}
952+
})
953+
954+
await generator.generate()
955+
956+
expect(fs.readFileSync('/foo.js', 'utf-8')).toMatch('foo()')
957+
expect(fs.existsSync('/existFile.js')).toBe(false)
958+
})
959+
960+
test('overwrite files that have been modified', async () => {
961+
const generator = new Generator('/', {
962+
plugins: [
963+
{
964+
id: 'test1',
965+
apply: (api, options) => {
966+
api.render((files, render) => {
967+
files['existFile.js'] = render('foo()')
968+
})
969+
}
970+
}
971+
],
972+
files: {
973+
'existFile.js': 'existFile()'
974+
}
975+
})
976+
977+
await generator.generate()
978+
979+
expect(fs.readFileSync('/existFile.js', 'utf-8')).toMatch('foo()')
980+
})
981+
936982
test('extract config files', async () => {
937983
const configs = {
938984
vue: {

packages/@vue/cli/lib/Generator.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,24 @@ const ensureEOL = str => {
7474
return str
7575
}
7676

77+
/**
78+
* Collect created/modified files into set
79+
* @param {Record<string,string|Buffer>} files
80+
* @param {Set<string>} set
81+
*/
82+
const watchFiles = (files, set) => {
83+
return new Proxy(files, {
84+
set (target, key, value, receiver) {
85+
set.add(key)
86+
return Reflect.set(target, key, value, receiver)
87+
},
88+
deleteProperty (target, key) {
89+
set.delete(key)
90+
return Reflect.deleteProperty(target, key)
91+
}
92+
})
93+
}
94+
7795
module.exports = class Generator {
7896
constructor (context, {
7997
pkg = {},
@@ -99,7 +117,11 @@ module.exports = class Generator {
99117
// for conflict resolution
100118
this.depSources = {}
101119
// virtual file tree
102-
this.files = files
120+
this.files = Object.keys(files).length
121+
// when execute `vue add/invoke`, only created/modified files are written to disk
122+
? watchFiles(files, this.filesModifyRecord = new Set())
123+
// all files need to be written to disk
124+
: files
103125
this.fileMiddlewares = []
104126
this.postProcessFilesCbs = []
105127
// exit messages
@@ -177,7 +199,7 @@ module.exports = class Generator {
177199
this.sortPkg()
178200
this.files['package.json'] = JSON.stringify(this.pkg, null, 2) + '\n'
179201
// write/update file tree to disk
180-
await writeFileTree(this.context, this.files, initialFiles)
202+
await writeFileTree(this.context, this.files, initialFiles, this.filesModifyRecord)
181203
}
182204

183205
extractConfigFiles (extractAll, checkExisting) {

packages/@vue/cli/lib/util/writeFileTree.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,22 @@ function deleteRemovedFiles (directory, newFiles, previousFiles) {
1212
}))
1313
}
1414

15-
module.exports = async function writeFileTree (dir, files, previousFiles) {
15+
/**
16+
*
17+
* @param {string} dir
18+
* @param {Record<string,string|Buffer>} files
19+
* @param {Record<string,string|Buffer>} [previousFiles]
20+
* @param {Set<string>} [include]
21+
*/
22+
module.exports = async function writeFileTree (dir, files, previousFiles, include) {
1623
if (process.env.VUE_CLI_SKIP_WRITE) {
1724
return
1825
}
1926
if (previousFiles) {
2027
await deleteRemovedFiles(dir, files, previousFiles)
2128
}
2229
Object.keys(files).forEach((name) => {
30+
if (include && !include.has(name)) return
2331
const filePath = path.join(dir, name)
2432
fs.ensureDirSync(path.dirname(filePath))
2533
fs.writeFileSync(filePath, files[name])

0 commit comments

Comments
 (0)