() => { testSupport.write('src/index.ts', fileWithGoodContent); // compile angular and produce .ngsummary.json / ngfactory.d.ts files compile(); testSupport.write('src/ok.ts', fileWithGoodContent); testSupport.write('src/error.ts', fileWithStructuralError); // Make sure the ok.ts file is before the error.ts file, // so we added a .ngfactory.ts file for it. const allRootNames = resolveFiles( ['src/ok.ts', 'src/error.ts'].map(fn => path.resolve(testSupport.basePath, fn))); const options = testSupport.createCompilerOptions({ noResolve: true, generateCodeForLibraries: false, }); const host = ng.createCompilerHost({options}); const originalGetSourceFile = host.getSourceFile; host.getSourceFile = (fileName: string, languageVersion: ts.ScriptTarget, onError?: ((message: string) => void) | undefined): ts.SourceFile => { // We should never try to load .ngfactory.ts files if (fileName.match(/\.ngfactory\.ts$/)) { throw new Error(`Non existent ngfactory file: ` + fileName); } return originalGetSourceFile.call(host, fileName, languageVersion, onError); }; const program = ng.createProgram({rootNames: allRootNames, options, host}); const structuralErrors = program.getNgStructuralDiagnostics(); expect(structuralErrors.length).toBe(1); expect(structuralErrors[0].messageText).toContain('Function calls are not supported.'); });
it('should include non-formatted errors (e.g. invalid templateUrl)', () => { testSupport.write('src/index.ts', ` import {Component, NgModule} from '@angular/core'; @Component({ selector: 'my-component', templateUrl: 'template.html', // invalid template url }) export class MyComponent {} @NgModule({ declarations: [MyComponent] }) export class MyModule {} `); const options = testSupport.createCompilerOptions(); const host = ng.createCompilerHost({options}); const program = ng.createProgram({ rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host, }); const structuralErrors = program.getNgStructuralDiagnostics(); expect(structuralErrors.length).toBe(1); expect(structuralErrors[0].messageText).toContain('Couldn\'t resolve resource template.html'); });
it('should report errors for ts and ng errors on emit with noEmitOnError=true', () => { testSupport.writeFiles({ 'src/main.ts': ` import {Component, NgModule} from '@angular/core'; // Ts error let x: string = 1; // Ng error @Component({selector: 'comp', templateUrl: './main.html'}) export class MyComp {} @NgModule({declarations: [MyComp]}) export class MyModule {} `, 'src/main.html': '{{nonExistent}}' }); const options = testSupport.createCompilerOptions({noEmitOnError: true}); const host = ng.createCompilerHost({options}); const program1 = ng.createProgram( {rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host}); const errorDiags = program1.emit().diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error); expect(formatDiagnostics(errorDiags)) .toContain(`src/main.ts(5,13): error TS2322: Type '1' is not assignable to type 'string'.`); expect(formatDiagnostics(errorDiags)) .toContain( `src/main.html(1,1): error TS100: Property 'nonExistent' does not exist on type 'MyComp'.`); });
function createProgram(rootNames: string[], overrideOptions: ng.CompilerOptions = {}) { const options = testSupport.createCompilerOptions(overrideOptions); const host = ng.createCompilerHost({options}); const program = ng.createProgram( {rootNames: rootNames.map(p => path.resolve(testSupport.basePath, p)), options, host}); return {program, options}; }
it('should not throw on structural errors but collect them', () => { testSupport.write('src/index.ts', fileWithStructuralError); const options = testSupport.createCompilerOptions(); const host = ng.createCompilerHost({options}); const program = ng.createProgram( {rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')], options, host}); const structuralErrors = program.getNgStructuralDiagnostics(); expect(structuralErrors.length).toBe(1); expect(structuralErrors[0].messageText).toContain('Function calls are not supported.'); });
it('should not report emit errors with noEmitOnError=false', () => { testSupport.writeFiles({ 'src/main.ts': ` @NgModule() ` }); const options = testSupport.createCompilerOptions({noEmitOnError: false}); const host = ng.createCompilerHost({options}); const program1 = ng.createProgram( {rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host}); expect(program1.emit().diagnostics.length).toBe(0); });
it('should typecheck templates even if skipTemplateCodegen is set', () => { testSupport.writeFiles({ 'src/main.ts': createModuleAndCompSource('main', `{{nonExistent}}`), }); const options = testSupport.createCompilerOptions({skipTemplateCodegen: true}); const host = ng.createCompilerHost({options}); const program = ng.createProgram( {rootNames: [path.resolve(testSupport.basePath, 'src/main.ts')], options, host}); const diags = program.getNgSemanticDiagnostics(); expect(diags.length).toBe(1); expect(diags[0].messageText).toBe(`Property 'nonExistent' does not exist on type 'mainComp'.`); });
function compile(oldProgram?: ng.Program): ng.Program { const options = testSupport.createCompilerOptions(); const rootNames = [path.resolve(testSupport.basePath, 'src/index.ts')]; const program = ng.createProgram({ rootNames: rootNames, options: testSupport.createCompilerOptions(), host: ng.createCompilerHost({options}), oldProgram, }); expectNoDiagnosticsInProgram(options, program); program.emit(); return program; }
function lazyRoutesTest() { const basePath = path.join(__dirname, '../ngtools_src'); const project = path.join(basePath, 'tsconfig-build.json'); const config = readConfiguration(project); const host = ts.createCompilerHost(config.options, true); const program = createProgram({ rootNames: config.rootNames, options: config.options, host, }); config.options.basePath = basePath; config.options.rootDir = basePath; const lazyRoutes = program.listLazyRoutes('app.module#AppModule'); const expectations: {[route: string]: string} = { './lazy.module#LazyModule': 'lazy.module.ts', './feature/feature.module#FeatureModule': 'feature/feature.module.ts', './feature/lazy-feature.module#LazyFeatureModule': 'feature/lazy-feature.module.ts', './feature.module#FeatureModule': 'feature/feature.module.ts', './lazy-feature-nested.module#LazyFeatureNestedModule': 'feature/lazy-feature-nested.module.ts', 'feature2/feature2.module#Feature2Module': 'feature2/feature2.module.ts', './default.module': 'feature2/default.module.ts', 'feature/feature.module#FeatureModule': 'feature/feature.module.ts' }; lazyRoutes.forEach(lazyRoute => { const routeName = lazyRoute.route; // Normalize the module path and the expected module path so that these can be compared // on Windows where path separators are not consistent with TypeScript internal paths. const modulePath = path.normalize(lazyRoute.referencedModule.filePath); const expectedModulePath = path.normalize(path.join(basePath, expectations[routeName])); assert(routeName in expectations, `Found a route that was not expected: "${routeName}".`); assert( modulePath === expectedModulePath, `Route "${routeName}" does not point to the expected absolute path ` + `"${expectedModulePath}". It points to "${modulePath}"`); }); // Verify that all expectations were met. assert.deepEqual( lazyRoutes.map(lazyRoute => lazyRoute.route), Object.keys(expectations), `Expected routes listed to be: \n` + ` ${JSON.stringify(Object.keys(expectations))}\n` + `Actual:\n` + ` ${JSON.stringify(Object.keys(lazyRoutes))}\n`); }
function compileLib(libName: string) { testSupport.writeFiles({ [`${libName}_src/index.ts`]: createModuleAndCompSource(libName), }); const options = testSupport.createCompilerOptions(); const program = ng.createProgram({ rootNames: [path.resolve(testSupport.basePath, `${libName}_src/index.ts`)], options, host: ng.createCompilerHost({options}), }); expectNoDiagnosticsInProgram(options, program); fs.symlinkSync( path.resolve(testSupport.basePath, 'built', `${libName}_src`), path.resolve(testSupport.basePath, 'node_modules', libName)); program.emit({emitFlags: ng.EmitFlags.DTS | ng.EmitFlags.JS | ng.EmitFlags.Metadata}); }