feat(ngcc): support reverting a file written by `FileWriter` (#36626)
This commit adds a `revertFile()` method to `FileWriter`, which can revert a transformed file (and its backup - if any) written by the `FileWriter`. In a subsequent commit, this will be used to allow ngcc to recover when a worker process crashes in the middle of processing a task. PR Close #36626
This commit is contained in:
parent
ff6e93163f
commit
772ccf0d9f
|
@ -6,7 +6,8 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {EntryPointJsonProperty} from '../packages/entry_point';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system';
|
||||
import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {FileToWrite} from '../rendering/utils';
|
||||
|
||||
|
@ -17,4 +18,18 @@ export interface FileWriter {
|
|||
writeBundle(
|
||||
bundle: EntryPointBundle, transformedFiles: FileToWrite[],
|
||||
formatProperties: EntryPointJsonProperty[]): void;
|
||||
|
||||
/**
|
||||
* Revert the changes to an entry-point processed for the specified format-properties by the same
|
||||
* `FileWriter` implementation.
|
||||
*
|
||||
* @param entryPoint The entry-point to revert.
|
||||
* @param transformedFilePaths The original paths of the transformed files. (The transformed files
|
||||
* may be written at the same or a different location, depending on the `FileWriter`
|
||||
* implementation.)
|
||||
* @param formatProperties The format-properties pointing to the entry-point.
|
||||
*/
|
||||
revertBundle(
|
||||
entryPoint: EntryPoint, transformedFilePaths: AbsoluteFsPath[],
|
||||
formatProperties: EntryPointJsonProperty[]): void;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {absoluteFrom, dirname, FileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {absoluteFrom, AbsoluteFsPath, dirname, FileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {EntryPointJsonProperty} from '../packages/entry_point';
|
||||
import {EntryPoint, EntryPointJsonProperty} from '../packages/entry_point';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {FileToWrite} from '../rendering/utils';
|
||||
|
||||
|
@ -29,6 +29,14 @@ export class InPlaceFileWriter implements FileWriter {
|
|||
transformedFiles.forEach(file => this.writeFileAndBackup(file));
|
||||
}
|
||||
|
||||
revertBundle(
|
||||
_entryPoint: EntryPoint, transformedFilePaths: AbsoluteFsPath[],
|
||||
_formatProperties: EntryPointJsonProperty[]): void {
|
||||
for (const filePath of transformedFilePaths) {
|
||||
this.revertFileAndBackup(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
protected writeFileAndBackup(file: FileToWrite): void {
|
||||
this.fs.ensureDir(dirname(file.path));
|
||||
const backPath = absoluteFrom(`${file.path}${NGCC_BACKUP_EXTENSION}`);
|
||||
|
@ -51,4 +59,15 @@ export class InPlaceFileWriter implements FileWriter {
|
|||
this.fs.writeFile(file.path, file.contents);
|
||||
}
|
||||
}
|
||||
|
||||
protected revertFileAndBackup(filePath: AbsoluteFsPath): void {
|
||||
if (this.fs.exists(filePath)) {
|
||||
this.fs.removeFile(filePath);
|
||||
|
||||
const backPath = absoluteFrom(`${filePath}${NGCC_BACKUP_EXTENSION}`);
|
||||
if (this.fs.exists(backPath)) {
|
||||
this.fs.moveFile(backPath, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,27 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
|
|||
this.updatePackageJson(entryPoint, formatProperties, ngccFolder);
|
||||
}
|
||||
|
||||
revertBundle(
|
||||
entryPoint: EntryPoint, transformedFilePaths: AbsoluteFsPath[],
|
||||
formatProperties: EntryPointJsonProperty[]): void {
|
||||
// IMPLEMENTATION NOTE:
|
||||
//
|
||||
// The changes made by `copyBundle()` are not reverted here. The non-transformed copied files
|
||||
// are identical to the original ones and they will be overwritten when re-processing the
|
||||
// entry-point anyway.
|
||||
//
|
||||
// This way, we avoid the overhead of having to inform the master process about all source files
|
||||
// being copied in `copyBundle()`.
|
||||
|
||||
// Revert the transformed files.
|
||||
for (const filePath of transformedFilePaths) {
|
||||
this.revertFile(filePath, entryPoint.package);
|
||||
}
|
||||
|
||||
// Revert any changes to `package.json`.
|
||||
this.revertPackageJson(entryPoint, formatProperties);
|
||||
}
|
||||
|
||||
protected copyBundle(
|
||||
bundle: EntryPointBundle, packagePath: AbsoluteFsPath, ngccFolder: AbsoluteFsPath) {
|
||||
bundle.src.program.getSourceFiles().forEach(sourceFile => {
|
||||
|
@ -71,6 +92,17 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
|
|||
}
|
||||
}
|
||||
|
||||
protected revertFile(filePath: AbsoluteFsPath, packagePath: AbsoluteFsPath): void {
|
||||
if (isDtsPath(filePath.replace(/\.map$/, ''))) {
|
||||
// This is either `.d.ts` or `.d.ts.map` file
|
||||
super.revertFileAndBackup(filePath);
|
||||
} else if (this.fs.exists(filePath)) {
|
||||
const relativePath = relative(packagePath, filePath);
|
||||
const newFilePath = join(packagePath, NGCC_DIRECTORY, relativePath);
|
||||
this.fs.removeFile(newFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
protected updatePackageJson(
|
||||
entryPoint: EntryPoint, formatProperties: EntryPointJsonProperty[],
|
||||
ngccFolder: AbsoluteFsPath) {
|
||||
|
@ -105,4 +137,25 @@ export class NewEntryPointFileWriter extends InPlaceFileWriter {
|
|||
|
||||
update.writeChanges(packageJsonPath, packageJson);
|
||||
}
|
||||
|
||||
protected revertPackageJson(entryPoint: EntryPoint, formatProperties: EntryPointJsonProperty[]) {
|
||||
if (formatProperties.length === 0) {
|
||||
// No format properties need reverting.
|
||||
return;
|
||||
}
|
||||
|
||||
const packageJson = entryPoint.packageJson;
|
||||
const packageJsonPath = join(entryPoint.path, 'package.json');
|
||||
|
||||
// Revert all properties in `package.json` (both in memory and on disk).
|
||||
// Since `updatePackageJson()` only adds properties, it is safe to just remove them (if they
|
||||
// exist).
|
||||
const update = this.pkgJsonUpdater.createUpdate();
|
||||
|
||||
for (const formatProperty of formatProperties) {
|
||||
update.addChange([`${formatProperty}${NGCC_PROPERTY_EXTENSION}`], undefined);
|
||||
}
|
||||
|
||||
update.writeChanges(packageJsonPath, packageJson);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
import {EntryPointBundle} from '../../src/packages/entry_point_bundle';
|
||||
import {InPlaceFileWriter} from '../../src/writing/in_place_file_writer';
|
||||
import {InPlaceFileWriter, NGCC_BACKUP_EXTENSION} from '../../src/writing/in_place_file_writer';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
|
@ -29,77 +30,144 @@ runInEachFileSystem(() => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('should write all the FileInfo to the disk', () => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
fileWriter.writeBundle({} as EntryPointBundle, [
|
||||
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: _('/package/path/folder-1/file-1.js'), contents: 'MODIFIED FILE 1'},
|
||||
{path: _('/package/path/folder-2/file-4.js'), contents: 'MODIFIED FILE 4'},
|
||||
{path: _('/package/path/folder-3/file-5.js'), contents: 'NEW FILE 5'},
|
||||
]);
|
||||
expect(fs.readFile(_('/package/path/top-level.js'))).toEqual('MODIFIED TOP LEVEL');
|
||||
expect(fs.readFile(_('/package/path/folder-1/file-1.js'))).toEqual('MODIFIED FILE 1');
|
||||
expect(fs.readFile(_('/package/path/folder-1/file-2.js'))).toEqual('ORIGINAL FILE 2');
|
||||
expect(fs.readFile(_('/package/path/folder-2/file-3.js'))).toEqual('ORIGINAL FILE 3');
|
||||
expect(fs.readFile(_('/package/path/folder-2/file-4.js'))).toEqual('MODIFIED FILE 4');
|
||||
expect(fs.readFile(_('/package/path/folder-3/file-5.js'))).toEqual('NEW FILE 5');
|
||||
describe('writeBundle()', () => {
|
||||
it('should write all the FileInfo to the disk', () => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
fileWriter.writeBundle({} as EntryPointBundle, [
|
||||
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: _('/package/path/folder-1/file-1.js'), contents: 'MODIFIED FILE 1'},
|
||||
{path: _('/package/path/folder-2/file-4.js'), contents: 'MODIFIED FILE 4'},
|
||||
{path: _('/package/path/folder-3/file-5.js'), contents: 'NEW FILE 5'},
|
||||
]);
|
||||
expect(fs.readFile(_('/package/path/top-level.js'))).toEqual('MODIFIED TOP LEVEL');
|
||||
expect(fs.readFile(_('/package/path/folder-1/file-1.js'))).toEqual('MODIFIED FILE 1');
|
||||
expect(fs.readFile(_('/package/path/folder-1/file-2.js'))).toEqual('ORIGINAL FILE 2');
|
||||
expect(fs.readFile(_('/package/path/folder-2/file-3.js'))).toEqual('ORIGINAL FILE 3');
|
||||
expect(fs.readFile(_('/package/path/folder-2/file-4.js'))).toEqual('MODIFIED FILE 4');
|
||||
expect(fs.readFile(_('/package/path/folder-3/file-5.js'))).toEqual('NEW FILE 5');
|
||||
});
|
||||
|
||||
it('should create backups of all files that previously existed', () => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
fileWriter.writeBundle({} as EntryPointBundle, [
|
||||
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: _('/package/path/folder-1/file-1.js'), contents: 'MODIFIED FILE 1'},
|
||||
{path: _('/package/path/folder-2/file-4.js'), contents: 'MODIFIED FILE 4'},
|
||||
{path: _('/package/path/folder-3/file-5.js'), contents: 'NEW FILE 5'},
|
||||
]);
|
||||
expect(fs.readFile(_('/package/path/top-level.js.__ivy_ngcc_bak')))
|
||||
.toEqual('ORIGINAL TOP LEVEL');
|
||||
expect(fs.readFile(_('/package/path/folder-1/file-1.js.__ivy_ngcc_bak')))
|
||||
.toEqual('ORIGINAL FILE 1');
|
||||
expect(fs.exists(_('/package/path/folder-1/file-2.js.__ivy_ngcc_bak'))).toBe(false);
|
||||
expect(fs.exists(_('/package/path/folder-2/file-3.js.__ivy_ngcc_bak'))).toBe(false);
|
||||
expect(fs.readFile(_('/package/path/folder-2/file-4.js.__ivy_ngcc_bak')))
|
||||
.toEqual('ORIGINAL FILE 4');
|
||||
expect(fs.exists(_('/package/path/folder-3/file-5.js.__ivy_ngcc_bak'))).toBe(false);
|
||||
});
|
||||
|
||||
it('should throw an error if the backup file already exists and errorOnFailedEntryPoint is true',
|
||||
() => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
const absoluteBackupPath = _('/package/path/already-backed-up.js');
|
||||
expect(
|
||||
() => fileWriter.writeBundle(
|
||||
{} as EntryPointBundle,
|
||||
[{path: absoluteBackupPath, contents: 'MODIFIED BACKED UP'}]))
|
||||
.toThrowError(`Tried to overwrite ${
|
||||
absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file, which is disallowed.`);
|
||||
});
|
||||
|
||||
it('should log an error, and skip writing the file, if the backup file already exists and errorOnFailedEntryPoint is false',
|
||||
() => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter =
|
||||
new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ false);
|
||||
const absoluteBackupPath = _('/package/path/already-backed-up.js');
|
||||
fileWriter.writeBundle(
|
||||
{} as EntryPointBundle,
|
||||
[{path: absoluteBackupPath, contents: 'MODIFIED BACKED UP'}]);
|
||||
// Should not have written the new file nor overwritten the backup file.
|
||||
expect(fs.readFile(absoluteBackupPath)).toEqual('ORIGINAL ALREADY BACKED UP');
|
||||
expect(fs.readFile(_(absoluteBackupPath + '.__ivy_ngcc_bak'))).toEqual('BACKED UP');
|
||||
expect(logger.logs.error).toEqual([[
|
||||
`Tried to write ${
|
||||
absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file but it already exists so not writing, nor backing up, ${
|
||||
absoluteBackupPath}.\n` +
|
||||
`This error may be because two or more entry-points overlap and ngcc has been asked to process some files more than once.\n` +
|
||||
`You should check other entry-points in this package and set up a config to ignore any that you are not using.`
|
||||
]]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should create backups of all files that previously existed', () => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
fileWriter.writeBundle({} as EntryPointBundle, [
|
||||
{path: _('/package/path/top-level.js'), contents: 'MODIFIED TOP LEVEL'},
|
||||
{path: _('/package/path/folder-1/file-1.js'), contents: 'MODIFIED FILE 1'},
|
||||
{path: _('/package/path/folder-2/file-4.js'), contents: 'MODIFIED FILE 4'},
|
||||
{path: _('/package/path/folder-3/file-5.js'), contents: 'NEW FILE 5'},
|
||||
]);
|
||||
expect(fs.readFile(_('/package/path/top-level.js.__ivy_ngcc_bak')))
|
||||
.toEqual('ORIGINAL TOP LEVEL');
|
||||
expect(fs.readFile(_('/package/path/folder-1/file-1.js.__ivy_ngcc_bak')))
|
||||
.toEqual('ORIGINAL FILE 1');
|
||||
expect(fs.exists(_('/package/path/folder-1/file-2.js.__ivy_ngcc_bak'))).toBe(false);
|
||||
expect(fs.exists(_('/package/path/folder-2/file-3.js.__ivy_ngcc_bak'))).toBe(false);
|
||||
expect(fs.readFile(_('/package/path/folder-2/file-4.js.__ivy_ngcc_bak')))
|
||||
.toEqual('ORIGINAL FILE 4');
|
||||
expect(fs.exists(_('/package/path/folder-3/file-5.js.__ivy_ngcc_bak'))).toBe(false);
|
||||
describe('revertBundle()', () => {
|
||||
it('should revert the written files (and their backups)', () => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
|
||||
const filePath1 = _('/package/path/folder-1/file-1.js');
|
||||
const filePath2 = _('/package/path/folder-1/file-2.js');
|
||||
const fileBackupPath1 = _(`/package/path/folder-1/file-1.js${NGCC_BACKUP_EXTENSION}`);
|
||||
const fileBackupPath2 = _(`/package/path/folder-1/file-2.js${NGCC_BACKUP_EXTENSION}`);
|
||||
|
||||
fileWriter.writeBundle({} as EntryPointBundle, [
|
||||
{path: filePath1, contents: 'MODIFIED FILE 1'},
|
||||
{path: filePath2, contents: 'MODIFIED FILE 2'},
|
||||
]);
|
||||
expect(fs.readFile(filePath1)).toBe('MODIFIED FILE 1');
|
||||
expect(fs.readFile(filePath2)).toBe('MODIFIED FILE 2');
|
||||
expect(fs.readFile(fileBackupPath1)).toBe('ORIGINAL FILE 1');
|
||||
expect(fs.readFile(fileBackupPath2)).toBe('ORIGINAL FILE 2');
|
||||
|
||||
fileWriter.revertBundle({} as EntryPoint, [filePath1, filePath2], []);
|
||||
expect(fs.readFile(filePath1)).toBe('ORIGINAL FILE 1');
|
||||
expect(fs.readFile(filePath2)).toBe('ORIGINAL FILE 2');
|
||||
expect(fs.exists(fileBackupPath1)).toBeFalse();
|
||||
expect(fs.exists(fileBackupPath2)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should just remove the written files if there is no backup', () => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
|
||||
const filePath = _('/package/path/folder-1/file-1.js');
|
||||
const fileBackupPath = _(`/package/path/folder-1/file-1.js${NGCC_BACKUP_EXTENSION}`);
|
||||
|
||||
fileWriter.writeBundle({} as EntryPointBundle, [
|
||||
{path: filePath, contents: 'MODIFIED FILE 1'},
|
||||
]);
|
||||
fs.removeFile(fileBackupPath);
|
||||
expect(fs.readFile(filePath)).toBe('MODIFIED FILE 1');
|
||||
expect(fs.exists(fileBackupPath)).toBeFalse();
|
||||
|
||||
fileWriter.revertBundle({} as EntryPoint, [filePath], []);
|
||||
expect(fs.exists(filePath)).toBeFalse();
|
||||
expect(fs.exists(fileBackupPath)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should do nothing if the file does not exist', () => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
|
||||
const filePath = _('/package/path/non-existent.js');
|
||||
const fileBackupPath = _(`/package/path/non-existent.js${NGCC_BACKUP_EXTENSION}`);
|
||||
|
||||
fs.writeFile(fileBackupPath, 'BACKUP WITHOUT FILE');
|
||||
fileWriter.revertBundle({} as EntryPoint, [filePath], []);
|
||||
|
||||
expect(fs.exists(filePath)).toBeFalse();
|
||||
expect(fs.readFile(fileBackupPath)).toBe('BACKUP WITHOUT FILE');
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if the backup file already exists and errorOnFailedEntryPoint is true',
|
||||
() => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ true);
|
||||
const absoluteBackupPath = _('/package/path/already-backed-up.js');
|
||||
expect(
|
||||
() => fileWriter.writeBundle(
|
||||
{} as EntryPointBundle,
|
||||
[{path: absoluteBackupPath, contents: 'MODIFIED BACKED UP'}]))
|
||||
.toThrowError(`Tried to overwrite ${
|
||||
absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file, which is disallowed.`);
|
||||
});
|
||||
|
||||
it('should log an error, and skip writing the file, if the backup file already exists and errorOnFailedEntryPoint is false',
|
||||
() => {
|
||||
const fs = getFileSystem();
|
||||
const logger = new MockLogger();
|
||||
const fileWriter = new InPlaceFileWriter(fs, logger, /* errorOnFailedEntryPoint */ false);
|
||||
const absoluteBackupPath = _('/package/path/already-backed-up.js');
|
||||
fileWriter.writeBundle(
|
||||
{} as EntryPointBundle, [{path: absoluteBackupPath, contents: 'MODIFIED BACKED UP'}]);
|
||||
// Should not have written the new file nor overwritten the backup file.
|
||||
expect(fs.readFile(absoluteBackupPath)).toEqual('ORIGINAL ALREADY BACKED UP');
|
||||
expect(fs.readFile(_(absoluteBackupPath + '.__ivy_ngcc_bak'))).toEqual('BACKED UP');
|
||||
expect(logger.logs.error).toEqual([[
|
||||
`Tried to write ${
|
||||
absoluteBackupPath}.__ivy_ngcc_bak with an ngcc back up file but it already exists so not writing, nor backing up, ${
|
||||
absoluteBackupPath}.\n` +
|
||||
`This error may be because two or more entry-points overlap and ngcc has been asked to process some files more than once.\n` +
|
||||
`You should check other entry-points in this package and set up a config to ignore any that you are not using.`
|
||||
]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {absoluteFrom, FileSystem, getFileSystem, join} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||
|
@ -32,7 +32,6 @@ runInEachFileSystem(() => {
|
|||
fs = getFileSystem();
|
||||
logger = new MockLogger();
|
||||
loadTestFiles([
|
||||
|
||||
{
|
||||
name: _('/node_modules/test/package.json'),
|
||||
contents: `
|
||||
|
@ -494,6 +493,142 @@ runInEachFileSystem(() => {
|
|||
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/typings/index.d.ts'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('revertFile()', () => {
|
||||
beforeEach(() => {
|
||||
fileWriter = new NewEntryPointFileWriter(
|
||||
fs, logger, /* errorOnFailedEntryPoint */ true, new DirectPackageJsonUpdater(fs));
|
||||
const config = new NgccConfiguration(fs, _('/'));
|
||||
const result = getEntryPointInfo(
|
||||
fs, config, logger, _('/node_modules/test'), _('/node_modules/test'))!;
|
||||
if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) {
|
||||
return fail(`Expected an entry point but got ${result}`);
|
||||
}
|
||||
entryPoint = result;
|
||||
esm5bundle = makeTestBundle(fs, entryPoint, 'module', 'esm5');
|
||||
});
|
||||
|
||||
it('should remove non-typings files', () => {
|
||||
fileWriter.writeBundle(
|
||||
esm5bundle,
|
||||
[
|
||||
{
|
||||
path: _('/node_modules/test/esm5.js'),
|
||||
contents: 'export function FooTop() {} // MODIFIED',
|
||||
},
|
||||
{
|
||||
path: _('/node_modules/test/esm5.js.map'),
|
||||
contents: 'MODIFIED MAPPING DATA',
|
||||
},
|
||||
// Normally there will be no backup file. Write one here to ensure it is not removed.
|
||||
{
|
||||
path: _('/node_modules/test/esm5.js.__ivy_ngcc_bak'),
|
||||
contents: 'NOT AN ACTUAL BACKUP',
|
||||
},
|
||||
],
|
||||
['module']);
|
||||
|
||||
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/esm5.js'))).toBeTrue();
|
||||
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/esm5.js.map'))).toBeTrue();
|
||||
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/esm5.js.__ivy_ngcc_bak'))).toBeTrue();
|
||||
|
||||
fileWriter.revertBundle(
|
||||
esm5bundle.entryPoint,
|
||||
[_('/node_modules/test/esm5.js'), _('/node_modules/test/esm5.js.map')], []);
|
||||
|
||||
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/esm5.js'))).toBeFalse();
|
||||
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/esm5.js.map'))).toBeFalse();
|
||||
expect(fs.exists(_('/node_modules/test/__ivy_ngcc__/esm5.js.__ivy_ngcc_bak'))).toBeTrue();
|
||||
});
|
||||
|
||||
it('should revert written typings files (and their backups)', () => {
|
||||
fileWriter.writeBundle(
|
||||
esm5bundle,
|
||||
[
|
||||
{
|
||||
path: _('/node_modules/test/index.d.ts'),
|
||||
contents: 'export declare class FooTop {} // MODIFIED'
|
||||
},
|
||||
{
|
||||
path: _('/node_modules/test/index.d.ts.map'),
|
||||
contents: 'MODIFIED MAPPING DATA',
|
||||
},
|
||||
],
|
||||
['module']);
|
||||
|
||||
expect(fs.readFile(_('/node_modules/test/index.d.ts')))
|
||||
.toBe('export declare class FooTop {} // MODIFIED');
|
||||
expect(fs.readFile(_('/node_modules/test/index.d.ts.__ivy_ngcc_bak')))
|
||||
.toBe('export declare class FooTop {}');
|
||||
expect(fs.readFile(_('/node_modules/test/index.d.ts.map'))).toBe('MODIFIED MAPPING DATA');
|
||||
expect(fs.readFile(_('/node_modules/test/index.d.ts.map.__ivy_ngcc_bak')))
|
||||
.toBe('ORIGINAL MAPPING DATA');
|
||||
|
||||
fileWriter.revertBundle(
|
||||
esm5bundle.entryPoint,
|
||||
[_('/node_modules/test/index.d.ts'), _('/node_modules/test/index.d.ts.map')], []);
|
||||
|
||||
expect(fs.readFile(_('/node_modules/test/index.d.ts')))
|
||||
.toBe('export declare class FooTop {}');
|
||||
expect(fs.exists(_('/node_modules/test/index.d.ts.__ivy_ngcc_bak'))).toBeFalse();
|
||||
expect(fs.readFile(_('/node_modules/test/index.d.ts.map'))).toBe('ORIGINAL MAPPING DATA');
|
||||
expect(fs.exists(_('/node_modules/test/index.d.ts.map.__ivy_ngcc_bak'))).toBeFalse();
|
||||
});
|
||||
|
||||
it('should revert changes to `package.json`', () => {
|
||||
const entryPoint = esm5bundle.entryPoint;
|
||||
const packageJsonPath = join(entryPoint.package, 'package.json');
|
||||
|
||||
fileWriter.writeBundle(
|
||||
esm5bundle,
|
||||
[
|
||||
{
|
||||
path: _('/node_modules/test/index.d.ts'),
|
||||
contents: 'export declare class FooTop {} // MODIFIED'
|
||||
},
|
||||
{
|
||||
path: _('/node_modules/test/index.d.ts.map'),
|
||||
contents: 'MODIFIED MAPPING DATA',
|
||||
},
|
||||
],
|
||||
['fesm5', 'module']);
|
||||
const packageJsonFromFile1 = JSON.parse(fs.readFile(packageJsonPath));
|
||||
|
||||
expect(entryPoint.packageJson).toEqual(jasmine.objectContaining({
|
||||
fesm5_ivy_ngcc: '__ivy_ngcc__/esm5.js',
|
||||
fesm5: './esm5.js',
|
||||
module_ivy_ngcc: '__ivy_ngcc__/esm5.js',
|
||||
module: './esm5.js',
|
||||
}));
|
||||
|
||||
expect(packageJsonFromFile1).toEqual(jasmine.objectContaining({
|
||||
fesm5_ivy_ngcc: '__ivy_ngcc__/esm5.js',
|
||||
fesm5: './esm5.js',
|
||||
module_ivy_ngcc: '__ivy_ngcc__/esm5.js',
|
||||
module: './esm5.js',
|
||||
}));
|
||||
|
||||
fileWriter.revertBundle(
|
||||
esm5bundle.entryPoint,
|
||||
[_('/node_modules/test/index.d.ts'), _('/node_modules/test/index.d.ts.map')],
|
||||
['fesm5', 'module']);
|
||||
const packageJsonFromFile2 = JSON.parse(fs.readFile(packageJsonPath));
|
||||
|
||||
expect(entryPoint.packageJson).toEqual(jasmine.objectContaining({
|
||||
fesm5: './esm5.js',
|
||||
module: './esm5.js',
|
||||
}));
|
||||
expect(entryPoint.packageJson.fesm5_ivy_ngcc).toBeUndefined();
|
||||
expect(entryPoint.packageJson.module_ivy_ngcc).toBeUndefined();
|
||||
|
||||
expect(packageJsonFromFile2).toEqual(jasmine.objectContaining({
|
||||
fesm5: './esm5.js',
|
||||
module: './esm5.js',
|
||||
}));
|
||||
expect(packageJsonFromFile2.fesm5_ivy_ngcc).toBeUndefined();
|
||||
expect(packageJsonFromFile2.module_ivy_ngcc).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function makeTestBundle(
|
||||
|
|
Loading…
Reference in New Issue