fix(compiler): various squashed fixes for the new ngc

introduce the option `allowEmptyCodegenFiles` to generate all generated files,
even if they are empty.
- also provides the original source files from which the file was generated
  in the write file callback
- needed e.g. for G3 when copying over pinto mod names from the original component
  to all generated files

use `importAs` from flat modules when writing summaries
- i.e. prevents incorrect entries like @angular/common/common in the .ngsummary.json files.

change interaction between ng and ts to prevent race conditions
- before Angular would rely on TS to first read the file for which we generate files,
  and then the generated files. However, this can break easily when we reuse an old program.

don’t generate files for sources that are outside of `rootDir`
(see #19337)
This commit is contained in:
Tobias Bosch
2017-09-21 18:05:07 -07:00
committed by Victor Berchet
parent 13613d4acb
commit a8a9660112
16 changed files with 746 additions and 459 deletions

View File

@ -10,7 +10,7 @@ import * as compiler from '@angular/compiler';
import * as ts from 'typescript';
import {MetadataCollector} from '../../src/metadata/collector';
import {CompilerHost, CompilerOptions} from '../../src/transformers/api';
import {CompilerHost, CompilerOptions, LibrarySummary} from '../../src/transformers/api';
import {TsCompilerAotCompilerTypeCheckHostAdapter, createCompilerHost} from '../../src/transformers/compiler_host';
import {Directory, Entry, MockAotContext, MockCompilerHost} from '../mocks';
@ -21,9 +21,14 @@ const aGeneratedFile = new compiler.GeneratedFile(
const aGeneratedFileText = `var x:any = 1;\n`;
describe('NgCompilerHost', () => {
let codeGenerator: jasmine.Spy;
let codeGenerator: {generateFile: jasmine.Spy; findGeneratedFileNames: jasmine.Spy;};
beforeEach(() => { codeGenerator = jasmine.createSpy('codeGenerator').and.returnValue([]); });
beforeEach(() => {
codeGenerator = {
generateFile: jasmine.createSpy('generateFile').and.returnValue(null),
findGeneratedFileNames: jasmine.createSpy('findGeneratedFileNames').and.returnValue([]),
};
});
function createNgHost({files = {}}: {files?: Directory} = {}): CompilerHost {
const context = new MockAotContext('/tmp/', files);
@ -37,16 +42,16 @@ describe('NgCompilerHost', () => {
moduleResolution: ts.ModuleResolutionKind.NodeJs,
},
ngHost = createNgHost({files}),
summariesFromPreviousCompilations = new Map<string, string>(),
librarySummaries = [],
}: {
files?: Directory,
options?: CompilerOptions,
ngHost?: CompilerHost,
summariesFromPreviousCompilations?: Map<string, string>
librarySummaries?: LibrarySummary[]
} = {}) {
return new TsCompilerAotCompilerTypeCheckHostAdapter(
['/tmp/index.ts'], options, ngHost, new MetadataCollector(), codeGenerator,
summariesFromPreviousCompilations);
librarySummaries);
}
describe('fileNameToModuleName', () => {
@ -180,7 +185,8 @@ describe('NgCompilerHost', () => {
});
it('should generate code when asking for the base name and add it as referencedFiles', () => {
codeGenerator.and.returnValue([aGeneratedFile]);
codeGenerator.findGeneratedFileNames.and.returnValue(['/tmp/src/index.ngfactory.ts']);
codeGenerator.generateFile.and.returnValue(aGeneratedFile);
const host = createHost({
files: {
'tmp': {
@ -201,11 +207,13 @@ describe('NgCompilerHost', () => {
expect(genSf.text).toBe(aGeneratedFileText);
// the codegen should have been cached
expect(codeGenerator).toHaveBeenCalledTimes(1);
expect(codeGenerator.generateFile).toHaveBeenCalledTimes(1);
expect(codeGenerator.findGeneratedFileNames).toHaveBeenCalledTimes(1);
});
it('should generate code when asking for the generated name first', () => {
codeGenerator.and.returnValue([aGeneratedFile]);
codeGenerator.findGeneratedFileNames.and.returnValue(['/tmp/src/index.ngfactory.ts']);
codeGenerator.generateFile.and.returnValue(aGeneratedFile);
const host = createHost({
files: {
'tmp': {
@ -226,10 +234,13 @@ describe('NgCompilerHost', () => {
expect(sf.referencedFiles[1].fileName).toBe('/tmp/src/index.ngfactory.ts');
// the codegen should have been cached
expect(codeGenerator).toHaveBeenCalledTimes(1);
expect(codeGenerator.generateFile).toHaveBeenCalledTimes(1);
expect(codeGenerator.findGeneratedFileNames).toHaveBeenCalledTimes(1);
});
it('should clear old generated references if the original host cached them', () => {
codeGenerator.findGeneratedFileNames.and.returnValue(['/tmp/src/index.ngfactory.ts']);
const ngHost = createNgHost();
const sfText = `
/// <reference path="main.ts"/>
@ -237,8 +248,9 @@ describe('NgCompilerHost', () => {
const sf = ts.createSourceFile('/tmp/src/index.ts', sfText, ts.ScriptTarget.Latest);
ngHost.getSourceFile = () => sf;
codeGenerator.and.returnValue(
[new compiler.GeneratedFile('/tmp/src/index.ts', '/tmp/src/index.ngfactory.ts', [])]);
codeGenerator.findGeneratedFileNames.and.returnValue(['/tmp/src/index.ngfactory.ts']);
codeGenerator.generateFile.and.returnValue(
new compiler.GeneratedFile('/tmp/src/index.ts', '/tmp/src/index.ngfactory.ts', []));
const host1 = createHost({ngHost});
host1.getSourceFile('/tmp/src/index.ts', ts.ScriptTarget.Latest);
@ -246,7 +258,8 @@ describe('NgCompilerHost', () => {
expect(sf.referencedFiles[0].fileName).toBe('main.ts');
expect(sf.referencedFiles[1].fileName).toBe('/tmp/src/index.ngfactory.ts');
codeGenerator.and.returnValue([]);
codeGenerator.findGeneratedFileNames.and.returnValue([]);
codeGenerator.generateFile.and.returnValue(null);
const host2 = createHost({ngHost});
host2.getSourceFile('/tmp/src/index.ts', ts.ScriptTarget.Latest);
@ -257,7 +270,8 @@ describe('NgCompilerHost', () => {
describe('updateSourceFile', () => {
it('should update source files', () => {
codeGenerator.and.returnValue([aGeneratedFile]);
codeGenerator.findGeneratedFileNames.and.returnValue(['/tmp/src/index.ngfactory.ts']);
codeGenerator.generateFile.and.returnValue(aGeneratedFile);
const host = createHost({files: {'tmp': {'src': {'index.ts': ''}}}});
let genSf = host.getSourceFile('/tmp/src/index.ngfactory.ts', ts.ScriptTarget.Latest);
@ -271,11 +285,12 @@ describe('NgCompilerHost', () => {
});
it('should error if the imports changed', () => {
codeGenerator.and.returnValue(
[new compiler.GeneratedFile('/tmp/src/index.ts', '/tmp/src/index.ngfactory.ts', [
new compiler.DeclareVarStmt(
'x', new compiler.ExternalExpr(new compiler.ExternalReference('aModule', 'aName')))
])]);
codeGenerator.findGeneratedFileNames.and.returnValue(['/tmp/src/index.ngfactory.ts']);
codeGenerator.generateFile.and.returnValue(new compiler.GeneratedFile(
'/tmp/src/index.ts', '/tmp/src/index.ngfactory.ts',
[new compiler.DeclareVarStmt(
'x',
new compiler.ExternalExpr(new compiler.ExternalReference('aModule', 'aName')))]));
const host = createHost({files: {'tmp': {'src': {'index.ts': ''}}}});
host.getSourceFile('/tmp/src/index.ngfactory.ts', ts.ScriptTarget.Latest);
@ -292,32 +307,4 @@ describe('NgCompilerHost', () => {
].join('\n'));
});
});
describe('fileExists', () => {
it('should cache calls', () => {
const ngHost = createNgHost({files: {'tmp': {'src': {'index.ts': ``}}}});
spyOn(ngHost, 'fileExists').and.callThrough();
const host = createHost({ngHost});
expect(host.fileExists('/tmp/src/index.ts')).toBe(true);
expect(host.fileExists('/tmp/src/index.ts')).toBe(true);
expect(ngHost.fileExists).toHaveBeenCalledTimes(1);
});
it(`should not derive the existence of generated files baesd on summaries on disc`, () => {
const host = createHost({files: {'tmp': {'lib': {'module.ngsummary.json': ``}}}});
expect(host.fileExists('/tmp/lib/module.ngfactory.ts')).toBe(false);
expect(host.fileExists('/tmp/lib/module.ngfactory.d.ts')).toBe(false);
});
it(`should derive the existence of generated .d.ts files based on the summaries from an old program`,
() => {
const summariesFromPreviousCompilations = new Map<string, string>();
summariesFromPreviousCompilations.set('/tmp/lib/module.ngsummary.json', `{}`);
const host = createHost({summariesFromPreviousCompilations});
expect(host.fileExists('/tmp/lib/module.ngfactory.ts')).toBe(false);
expect(host.fileExists('/tmp/lib/module.ngfactory.d.ts')).toBe(true);
});
});
});