feat(compiler): read and write .ngsummary.json
files
When compiling libraries, this feature extracts the minimal information from the directives/pipes/modules of the library into `.ngsummary.json` files, so that applications that use this library only need to be recompiled if one of the summary files change, but not on every change of the libraries (e.g. one of the templates). Only works if individual codegen for libraries is enabled, see the `generateCodeForLibraries: false` option. Closes #12787
This commit is contained in:

committed by
Alex Rickabaugh

parent
9ab401f4d3
commit
614a35d539
@ -694,7 +694,7 @@ describe('StaticReflector', () => {
|
||||
|
||||
});
|
||||
|
||||
class MockStaticReflectorHost implements StaticReflectorHost {
|
||||
export class MockStaticReflectorHost implements StaticReflectorHost {
|
||||
private collector = new MetadataCollector();
|
||||
|
||||
constructor(private data: {[key: string]: any}) {}
|
||||
|
93
modules/@angular/compiler/test/aot/summary_resolver_spec.ts
Normal file
93
modules/@angular/compiler/test/aot/summary_resolver_spec.ts
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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 {AotSummaryResolver, AotSummaryResolverHost, CompileTypeSummary, StaticReflector, StaticReflectorHost, StaticSymbol} from '@angular/compiler';
|
||||
import * as path from 'path';
|
||||
|
||||
import {MockStaticReflectorHost} from './static_reflector_spec';
|
||||
|
||||
const EXT = /\.ts$|.d.ts$/;
|
||||
|
||||
export function main() {
|
||||
describe('AotSummaryResolver', () => {
|
||||
let resolver: AotSummaryResolver;
|
||||
let staticReflector: StaticReflector;
|
||||
|
||||
function init(summaries: {[filePath: string]: string} = {}) {
|
||||
// Note: We don't give the static reflector metadata files,
|
||||
// so that we can test that we can deserialize summary files
|
||||
// without reading metadata files. This is important
|
||||
// as summary files can contain references to files of transitive compilation
|
||||
// dependencies, and we don't want to read their metadata files.
|
||||
staticReflector = new StaticReflector(new MockStaticReflectorHost({}));
|
||||
const host = new MockAotSummaryResolverHost(summaries);
|
||||
resolver = new AotSummaryResolver(host, staticReflector, {excludeFilePattern: /\.d\.ts$/});
|
||||
}
|
||||
|
||||
it('should add .ngsummary.json to the filename', () => {
|
||||
init();
|
||||
expect(resolver.serializeSummaries('a.ts', []).genFileUrl).toBe('a.ngsummary.json');
|
||||
expect(resolver.serializeSummaries('a.d.ts', []).genFileUrl).toBe('a.ngsummary.json');
|
||||
expect(resolver.serializeSummaries('a.js', []).genFileUrl).toBe('a.ngsummary.json');
|
||||
});
|
||||
|
||||
it('should serialize plain data', () => {
|
||||
init();
|
||||
const data = <any>[{a: 'b'}];
|
||||
expect(JSON.parse(resolver.serializeSummaries('someSourceFile', data).source)).toEqual(data);
|
||||
});
|
||||
|
||||
it('should serialize summary for .ts files and deserialize based on .d.ts files', () => {
|
||||
init();
|
||||
const serializedData = resolver.serializeSummaries(
|
||||
'/tmp/some_class.ts', [{
|
||||
isSummary: true,
|
||||
type: {
|
||||
reference: staticReflector.getStaticSymbol('/tmp/some_class.ts', 'SomeClass'),
|
||||
diDeps: [],
|
||||
lifecycleHooks: []
|
||||
}
|
||||
}]);
|
||||
|
||||
// Note: this creates a new staticReflector!
|
||||
init({[serializedData.genFileUrl]: serializedData.source});
|
||||
|
||||
expect(resolver.resolveSummary(
|
||||
staticReflector.getStaticSymbol('/tmp/some_class.d.ts', 'SomeClass')))
|
||||
.toEqual({
|
||||
isSummary: true,
|
||||
type: {
|
||||
reference: staticReflector.getStaticSymbol('/tmp/some_class.d.ts', 'SomeClass'),
|
||||
diDeps: [],
|
||||
lifecycleHooks: []
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockAotSummaryResolverHost implements AotSummaryResolverHost {
|
||||
constructor(private summaries: {[fileName: string]: string}) {}
|
||||
|
||||
loadSummary(filePath: string): string {
|
||||
const result = this.summaries[filePath];
|
||||
if (!result) {
|
||||
throw new Error(`Could not find summary for ${filePath}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fileNameToModuleName(fileName: string): string {
|
||||
return './' + path.basename(fileName).replace(EXT, '');
|
||||
}
|
||||
|
||||
getOutputFileName(sourceFileName: string): string {
|
||||
return sourceFileName.replace(EXT, '') + '.d.ts';
|
||||
}
|
||||
}
|
@ -84,7 +84,7 @@ export function main() {
|
||||
}
|
||||
|
||||
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).loading.then(() => {
|
||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
|
||||
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
||||
expect(meta.selector).toEqual('someSelector');
|
||||
expect(meta.template.styleUrls).toEqual(['someStyleUrl']);
|
||||
@ -94,29 +94,6 @@ export function main() {
|
||||
resourceLoader.flush();
|
||||
})));
|
||||
|
||||
it('should wait for external resources of imported modules',
|
||||
async(inject(
|
||||
[CompileMetadataResolver, ResourceLoader],
|
||||
(resolver: CompileMetadataResolver, resourceLoader: MockResourceLoader) => {
|
||||
@NgModule({
|
||||
declarations: [ComponentWithExternalResources],
|
||||
exports: [ComponentWithExternalResources]
|
||||
})
|
||||
class SomeImportedModule {
|
||||
}
|
||||
|
||||
@NgModule({imports: [SomeImportedModule]})
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
resourceLoader.when('someTemplateUrl', 'someTemplate');
|
||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).loading.then(() => {
|
||||
const meta = resolver.getDirectiveMetadata(ComponentWithExternalResources);
|
||||
expect(meta.selector).toEqual('someSelector');
|
||||
});
|
||||
resourceLoader.flush();
|
||||
})));
|
||||
|
||||
it('should use `./` as base url for templates during runtime compilation if no moduleId is given',
|
||||
async(inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||
@Component({selector: 'someComponent', templateUrl: 'someUrl'})
|
||||
@ -128,7 +105,7 @@ export function main() {
|
||||
class SomeModule {
|
||||
}
|
||||
|
||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).loading.then(() => {
|
||||
resolver.loadNgModuleDirectiveAndPipeMetadata(SomeModule, false).then(() => {
|
||||
const value: string =
|
||||
resolver.getDirectiveMetadata(ComponentWithoutModuleId).template.templateUrl;
|
||||
const expectedEndValue = './someUrl';
|
||||
@ -329,7 +306,7 @@ export function main() {
|
||||
class MyModule {
|
||||
}
|
||||
|
||||
const modMeta = resolver.loadNgModuleDirectiveAndPipeMetadata(MyModule, true).ngModule;
|
||||
const modMeta = resolver.getNgModuleMetadata(MyModule);
|
||||
expect(modMeta.declaredDirectives.length).toBe(1);
|
||||
expect(modMeta.declaredDirectives[0].reference).toBe(MyComp);
|
||||
}));
|
||||
|
Reference in New Issue
Block a user