fix(ngcc): correctly invalidate cache when moving/removing files/directories (#35106)
One particular scenario where this was causing problems was when the [BackupFileCleaner][1] restored a file (such as a `.d.ts` file) by [moving the backup file][2] to its original location, but the modified content was kept in the cache. [1]: https://github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L54 [2]: https://github.com/angular/angular/blob/4d36b2f6e/packages/compiler-cli/ngcc/src/writing/cleaning/cleaning_strategies.ts#L61 Fixes #35095 PR Close #35106
This commit is contained in:

committed by
Miško Hevery

parent
3c30474417
commit
523c785e8f
@ -72,12 +72,16 @@ export class CachedFileSystem implements FileSystem {
|
||||
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
|
||||
this.delegate.moveFile(from, to);
|
||||
|
||||
this.existsCache.set(from, false);
|
||||
this.existsCache.set(to, true);
|
||||
|
||||
if (this.readFileCache.has(from)) {
|
||||
this.readFileCache.set(to, this.readFileCache.get(from));
|
||||
this.readFileCache.delete(from);
|
||||
} else {
|
||||
this.readFileCache.delete(to);
|
||||
}
|
||||
this.existsCache.set(to, true);
|
||||
}
|
||||
|
||||
ensureDir(path: AbsoluteFsPath): void {
|
||||
@ -90,10 +94,18 @@ export class CachedFileSystem implements FileSystem {
|
||||
|
||||
removeDeep(path: AbsoluteFsPath): void {
|
||||
this.delegate.removeDeep(path);
|
||||
// Clear out all children of this directory from the exists cache.
|
||||
|
||||
// Clear out this directory and all its children from the `exists` cache.
|
||||
for (const p of this.existsCache.keys()) {
|
||||
if (p.startsWith(path)) {
|
||||
this.existsCache.set(path, false);
|
||||
this.existsCache.set(p, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out this directory and all its children from the `readFile` cache.
|
||||
for (const p of this.readFileCache.keys()) {
|
||||
if (p.startsWith(path)) {
|
||||
this.readFileCache.delete(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {CachedFileSystem} from '../src/cached_file_system';
|
||||
import {absoluteFrom, setFileSystem} from '../src/helpers';
|
||||
import {absoluteFrom, join, setFileSystem} from '../src/helpers';
|
||||
import {NodeJSFileSystem} from '../src/node_js_file_system';
|
||||
import {AbsoluteFsPath, FileSystem} from '../src/types';
|
||||
|
||||
@ -236,7 +236,7 @@ describe('CachedFileSystem', () => {
|
||||
expect(readFileSpy).toHaveBeenCalledWith(abcPath);
|
||||
});
|
||||
|
||||
it('should update the `to` "readFile" cache', () => {
|
||||
it('should update the `to` "readFile" cache (if `from` was cached)', () => {
|
||||
spyOn(delegate, 'moveFile');
|
||||
const readFileSpy = spyOn(delegate, 'readFile');
|
||||
|
||||
@ -252,6 +252,24 @@ describe('CachedFileSystem', () => {
|
||||
expect(fs.readFile(xyzPath)).toEqual('abc content');
|
||||
expect(readFileSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should delete the `to` "readFile" cache (if `from` was not cached)', () => {
|
||||
spyOn(delegate, 'moveFile');
|
||||
const readFileSpy = spyOn(delegate, 'readFile');
|
||||
|
||||
// Fill the xyz "readFile" cache
|
||||
readFileSpy.and.returnValue('xyz content');
|
||||
fs.readFile(xyzPath);
|
||||
readFileSpy.calls.reset();
|
||||
|
||||
// Move the file
|
||||
fs.moveFile(abcPath, xyzPath);
|
||||
|
||||
// Show that the cache was not hit for the xyz file
|
||||
readFileSpy.and.returnValue('abc content');
|
||||
expect(fs.readFile(xyzPath)).toBe('abc content');
|
||||
expect(readFileSpy).toHaveBeenCalledWith(xyzPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ensureDir()', () => {
|
||||
@ -281,14 +299,41 @@ describe('CachedFileSystem', () => {
|
||||
});
|
||||
|
||||
it('should update the exists cache', () => {
|
||||
spyOn(delegate, 'writeFile');
|
||||
spyOn(delegate, 'removeDeep');
|
||||
const existsSpy = spyOn(delegate, 'exists').and.returnValue(true);
|
||||
expect(fs.exists(abcPath)).toBe(true);
|
||||
existsSpy.calls.reset();
|
||||
|
||||
// Create a file inside `/a/b/c`.
|
||||
const abcdPath = join(abcPath, 'd');
|
||||
fs.writeFile(abcdPath, 'content');
|
||||
expect(fs.exists(abcdPath)).toBe(true);
|
||||
expect(existsSpy).not.toHaveBeenCalled();
|
||||
|
||||
// Remove the `/a/b/c` directory and ensure it is removed from cache (along with its content).
|
||||
fs.removeDeep(abcPath);
|
||||
expect(fs.exists(abcPath)).toBeFalsy();
|
||||
expect(fs.exists(abcdPath)).toBeFalsy();
|
||||
expect(existsSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update the readFile cache', () => {
|
||||
spyOn(delegate, 'writeFile');
|
||||
spyOn(delegate, 'removeDeep');
|
||||
spyOn(delegate, 'lstat').and.throwError('ENOENT: no such file or directory');
|
||||
const readFileSpy = spyOn(delegate, 'readFile');
|
||||
|
||||
// Create a file inside `/a/b/c`.
|
||||
const abcdPath = join(abcPath, 'd');
|
||||
fs.writeFile(abcdPath, 'content from cache');
|
||||
expect(fs.readFile(abcdPath)).toBe('content from cache');
|
||||
expect(readFileSpy).not.toHaveBeenCalled();
|
||||
|
||||
// Remove the `/a/b/c` directory and ensure it is removed from cache (along with its content).
|
||||
fs.removeDeep(abcPath);
|
||||
expect(() => fs.readFile(abcdPath)).toThrowError('ENOENT: no such file or directory');
|
||||
expect(() => fs.readFile(abcPath)).toThrowError('ENOENT: no such file or directory');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user