From 05ff6c09ca92ac28c0b32d9b986205cb053ae503 Mon Sep 17 00:00:00 2001 From: Trotyl Date: Wed, 29 Nov 2017 15:27:16 +0800 Subject: [PATCH] fix(compiler): make tsx file aot compatible fixes #20555 --- .../src/transformers/compiler_host.ts | 6 ++-- .../compiler-cli/src/transformers/program.ts | 2 +- .../test/transformers/compiler_host_spec.ts | 16 +++++++++ .../test/transformers/program_spec.ts | 16 +++++++++ packages/compiler/src/aot/compiler.ts | 6 ++-- packages/compiler/src/aot/util.ts | 6 +++- packages/compiler/test/aot/compiler_spec.ts | 36 +++++++++++++++++++ 7 files changed, 80 insertions(+), 8 deletions(-) diff --git a/packages/compiler-cli/src/transformers/compiler_host.ts b/packages/compiler-cli/src/transformers/compiler_host.ts index 560768556b..d095ffda54 100644 --- a/packages/compiler-cli/src/transformers/compiler_host.ts +++ b/packages/compiler-cli/src/transformers/compiler_host.ts @@ -326,7 +326,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos return {generate: false}; } const [, base, genSuffix, suffix] = genMatch; - if (suffix !== 'ts') { + if (suffix !== 'ts' && suffix !== 'tsx') { return {generate: false}; } let baseFileName: string|undefined; @@ -337,9 +337,9 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos } } else { // Note: on-the-fly generated files always have a `.ts` suffix, - // but the file from which we generated it can be a `.ts`/ `.d.ts` + // but the file from which we generated it can be a `.ts`/ `.tsx`/ `.d.ts` // (see options.generateCodeForLibraries). - baseFileName = [`${base}.ts`, `${base}.d.ts`].find( + baseFileName = [`${base}.ts`, `${base}.tsx`, `${base}.d.ts`].find( baseFileName => this.isSourceFile(baseFileName) && this.originalFileExists(baseFileName)); if (!baseFileName) { return {generate: false}; diff --git a/packages/compiler-cli/src/transformers/program.ts b/packages/compiler-cli/src/transformers/program.ts index 5ee63237d6..39ffc15fcc 100644 --- a/packages/compiler-cli/src/transformers/program.ts +++ b/packages/compiler-cli/src/transformers/program.ts @@ -337,7 +337,7 @@ class AngularCompilerProgram implements Program { metadataJsonCount++; const metadata = this.metadataCache.getMetadata(sf); const metadataText = JSON.stringify([metadata]); - const outFileName = srcToOutPath(sf.fileName.replace(/\.ts$/, '.metadata.json')); + const outFileName = srcToOutPath(sf.fileName.replace(/\.tsx?$/, '.metadata.json')); this.writeFile(outFileName, metadataText, false, undefined, undefined, [sf]); } }); diff --git a/packages/compiler-cli/test/transformers/compiler_host_spec.ts b/packages/compiler-cli/test/transformers/compiler_host_spec.ts index caf7d4ab29..98b5285005 100644 --- a/packages/compiler-cli/test/transformers/compiler_host_spec.ts +++ b/packages/compiler-cli/test/transformers/compiler_host_spec.ts @@ -285,6 +285,22 @@ describe('NgCompilerHost', () => { expect(sf.referencedFiles.length).toBe(1); expect(sf.referencedFiles[0].fileName).toBe('main.ts'); }); + + it('should generate for tsx files', () => { + codeGenerator.findGeneratedFileNames.and.returnValue(['/tmp/src/index.ngfactory.ts']); + codeGenerator.generateFile.and.returnValue(aGeneratedFile); + const host = createHost({files: {'tmp': {'src': {'index.tsx': ``}}}}); + + const genSf = host.getSourceFile('/tmp/src/index.ngfactory.ts', ts.ScriptTarget.Latest); + expect(genSf.text).toBe(aGeneratedFileText); + + const sf = host.getSourceFile('/tmp/src/index.tsx', ts.ScriptTarget.Latest); + expect(sf.referencedFiles[0].fileName).toBe('/tmp/src/index.ngfactory.ts'); + + // the codegen should have been cached + expect(codeGenerator.generateFile).toHaveBeenCalledTimes(1); + expect(codeGenerator.findGeneratedFileNames).toHaveBeenCalledTimes(1); + }); }); describe('updateSourceFile', () => { diff --git a/packages/compiler-cli/test/transformers/program_spec.ts b/packages/compiler-cli/test/transformers/program_spec.ts index fa387a7098..6b52e110ba 100644 --- a/packages/compiler-cli/test/transformers/program_spec.ts +++ b/packages/compiler-cli/test/transformers/program_spec.ts @@ -391,6 +391,22 @@ describe('ng program', () => { testSupport.shouldExist('built/src/main.ngfactory.d.ts'); }); + it('should work with tsx files', () => { + // create a temporary ts program to get the list of all files from angular... + testSupport.writeFiles({ + 'src/main.tsx': createModuleAndCompSource('main'), + }); + const allRootNames = resolveFiles([path.resolve(testSupport.basePath, 'src/main.tsx')]); + + const program = compile(undefined, {jsx: ts.JsxEmit.React}, allRootNames); + + testSupport.shouldExist('built/src/main.js'); + testSupport.shouldExist('built/src/main.d.ts'); + testSupport.shouldExist('built/src/main.ngfactory.js'); + testSupport.shouldExist('built/src/main.ngfactory.d.ts'); + testSupport.shouldExist('built/src/main.ngsummary.json'); + }); + it('should emit also empty generated files depending on the options', () => { testSupport.writeFiles({ 'src/main.ts': ` diff --git a/packages/compiler/src/aot/compiler.ts b/packages/compiler/src/aot/compiler.ts index 499c66e966..15496acec8 100644 --- a/packages/compiler/src/aot/compiler.ts +++ b/packages/compiler/src/aot/compiler.ts @@ -34,7 +34,7 @@ import {StaticReflector} from './static_reflector'; import {StaticSymbol} from './static_symbol'; import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver'; import {createForJitStub, serializeSummaries} from './summary_serializer'; -import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName, summaryForJitFileName, summaryForJitName} from './util'; +import {ngfactoryFilePath, normalizeGenFileSuffix, splitTypescriptSuffix, summaryFileName, summaryForJitFileName, summaryForJitName} from './util'; enum StubEmitFlags { Basic = 1 << 0, @@ -103,7 +103,7 @@ export class AotCompiler { genFileNames.push(summaryForJitFileName(file.fileName, true)); } } - const fileSuffix = splitTypescriptSuffix(file.fileName, true)[1]; + const fileSuffix = normalizeGenFileSuffix(splitTypescriptSuffix(file.fileName, true)[1]); file.directives.forEach((dirSymbol) => { const compMeta = this._metadataResolver.getNonNormalizedDirectiveMetadata(dirSymbol) !.metadata; @@ -317,7 +317,7 @@ export class AotCompiler { srcFileUrl: string, ngModuleByPipeOrDirective: Map, directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: CompileNgModuleMetadata[], injectables: StaticSymbol[]): GeneratedFile[] { - const fileSuffix = splitTypescriptSuffix(srcFileUrl, true)[1]; + const fileSuffix = normalizeGenFileSuffix(splitTypescriptSuffix(srcFileUrl, true)[1]); const generatedFiles: GeneratedFile[] = []; const outputCtx = this._createOutputContext(ngfactoryFilePath(srcFileUrl, true)); diff --git a/packages/compiler/src/aot/util.ts b/packages/compiler/src/aot/util.ts index 6fd75eba8c..568dd57ad0 100644 --- a/packages/compiler/src/aot/util.ts +++ b/packages/compiler/src/aot/util.ts @@ -13,7 +13,7 @@ const JIT_SUMMARY_NAME = /NgSummary$/; export function ngfactoryFilePath(filePath: string, forceSourceFile = false): string { const urlWithSuffix = splitTypescriptSuffix(filePath, forceSourceFile); - return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`; + return `${urlWithSuffix[0]}.ngfactory${normalizeGenFileSuffix(urlWithSuffix[1])}`; } export function stripGeneratedFileSuffix(filePath: string): string { @@ -38,6 +38,10 @@ export function splitTypescriptSuffix(path: string, forceSourceFile = false): st return [path, '']; } +export function normalizeGenFileSuffix(srcFileSuffix: string): string { + return srcFileSuffix === '.tsx' ? '.ts' : srcFileSuffix; +} + export function summaryFileName(fileName: string): string { const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, ''); return `${fileNameWithoutSuffix}.ngsummary.json`; diff --git a/packages/compiler/test/aot/compiler_spec.ts b/packages/compiler/test/aot/compiler_spec.ts index 82de7df5ce..788d01a4e3 100644 --- a/packages/compiler/test/aot/compiler_spec.ts +++ b/packages/compiler/test/aot/compiler_spec.ts @@ -905,6 +905,14 @@ describe('compiler (bundled Angular)', () => { expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); }); + + it('should support tsx', () => { + const tsOptions = {jsx: ts.JsxEmit.React}; + const {genFiles} = + compile([QUICKSTART_TSX, angularFiles], /* options */ undefined, tsOptions); + expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); + expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined(); + }); }); describe('Bundled library', () => { @@ -978,6 +986,34 @@ const QUICKSTART: MockDirectory = { } }; +const QUICKSTART_TSX: MockDirectory = { + quickstart: { + app: { + // #20555 + 'app.component.tsx': ` + import {Component} from '@angular/core'; + + @Component({ + template: '

Hello {{name}}

' + }) + export class AppComponent { + name = 'Angular'; + } + `, + 'app.module.ts': ` + import { NgModule } from '@angular/core'; + import { AppComponent } from './app.component'; + + @NgModule({ + declarations: [ AppComponent ], + bootstrap: [ AppComponent ] + }) + export class AppModule { } + ` + } + } +}; + const LIBRARY: MockDirectory = { bolder: { 'public-api.ts': `