Update the license headers throughout the repository to reference Google LLC rather than Google Inc, for the required license headers. PR Close #37205
		
			
				
	
	
		
			197 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google LLC 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 * as ts from 'typescript';
 | |
| 
 | |
| import {isClassMetadata, MetadataCollector} from '../../src/metadata/index';
 | |
| import {getInlineResourcesTransformFactory, InlineResourcesMetadataTransformer} from '../../src/transformers/inline_resources';
 | |
| import {MetadataCache} from '../../src/transformers/metadata_cache';
 | |
| import {MockAotContext, MockCompilerHost} from '../mocks';
 | |
| 
 | |
| describe('inline resources transformer', () => {
 | |
|   describe('decorator input', () => {
 | |
|     describe('should not touch unrecognized decorators', () => {
 | |
|       it('Not from @angular/core', () => {
 | |
|         expect(convert(`declare const Component: Function;
 | |
|           @Component({templateUrl: './thing.html'}) class Foo {}`))
 | |
|             .toContain('templateUrl');
 | |
|       });
 | |
|       it('missing @ sign', () => {
 | |
|         expect(convert(`import {Component} from '@angular/core';
 | |
|           Component({templateUrl: './thing.html'}) class Foo {}`))
 | |
|             .toContain('templateUrl');
 | |
|       });
 | |
|       it('too many arguments to @Component', () => {
 | |
|         expect(convert(`import {Component} from '@angular/core';
 | |
|           @Component(1, {templateUrl: './thing.html'}) class Foo {}`))
 | |
|             .toContain('templateUrl');
 | |
|       });
 | |
|       it('wrong argument type to @Component', () => {
 | |
|         expect(convert(`import {Component} from '@angular/core';
 | |
|           @Component([{templateUrl: './thing.html'}]) class Foo {}`))
 | |
|             .toContain('templateUrl');
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('should replace templateUrl', () => {
 | |
|       const actual = convert(`import {Component} from '@angular/core';
 | |
|         @Component({
 | |
|           templateUrl: './thing.html',
 | |
| 	        otherProp: 3,
 | |
| 	      }) export class Foo {}`);
 | |
|       expect(actual).not.toContain('templateUrl:');
 | |
|       expect(actual.replace(/\s+/g, ' '))
 | |
|           .toContain(
 | |
|               'Foo = __decorate([ core_1.Component({ template: "Some template", otherProp: 3 }) ], Foo)');
 | |
|     });
 | |
|     it('should allow different quotes', () => {
 | |
|       const actual = convert(`import {Component} from '@angular/core';
 | |
|         @Component({"templateUrl": \`./thing.html\`}) export class Foo {}`);
 | |
|       expect(actual).not.toContain('templateUrl:');
 | |
|       expect(actual).toContain('{ template: "Some template" }');
 | |
|     });
 | |
|     it('should replace styleUrls', () => {
 | |
|       const actual = convert(`import {Component} from '@angular/core';
 | |
|         @Component({
 | |
|           styleUrls: ['./thing1.css', './thing2.css'],
 | |
|         })
 | |
|         export class Foo {}`);
 | |
|       expect(actual).not.toContain('styleUrls:');
 | |
|       expect(actual).toContain('styles: [".some_style {}", ".some_other_style {}"]');
 | |
|     });
 | |
|     it('should preserve existing styles', () => {
 | |
|       const actual = convert(`import {Component} from '@angular/core';
 | |
|         @Component({
 | |
|           styles: ['h1 { color: blue }'],
 | |
|           styleUrls: ['./thing1.css'],
 | |
|         })
 | |
|         export class Foo {}`);
 | |
|       expect(actual).not.toContain('styleUrls:');
 | |
|       expect(actual).toContain(`styles: ['h1 { color: blue }', ".some_style {}"]`);
 | |
|     });
 | |
|     it('should handle empty styleUrls', () => {
 | |
|       const actual = convert(`import {Component} from '@angular/core';
 | |
|         @Component({styleUrls: [], styles: []}) export class Foo {}`);
 | |
|       expect(actual).not.toContain('styleUrls:');
 | |
|       expect(actual).not.toContain('styles:');
 | |
|     });
 | |
|   });
 | |
|   describe('annotation input', () => {
 | |
|     it('should replace templateUrl', () => {
 | |
|       const actual = convert(`import {Component} from '@angular/core';
 | |
|       declare const NotComponent: Function;
 | |
| 
 | |
|       export class Foo {
 | |
|         static decorators: {type: Function, args?: any[]}[] = [
 | |
|           {
 | |
|             type: NotComponent,
 | |
|             args: [],
 | |
|           },{
 | |
|             type: Component,
 | |
|             args: [{
 | |
|               templateUrl: './thing.html'
 | |
|           }],
 | |
|         }];
 | |
|       }
 | |
|     `);
 | |
|       expect(actual).not.toContain('templateUrl:');
 | |
|       expect(actual.replace(/\s+/g, ' '))
 | |
|           .toMatch(
 | |
|               /Foo\.decorators = [{ .*type: core_1\.Component, args: [{ template: "Some template" }]/);
 | |
|     });
 | |
|     it('should replace styleUrls', () => {
 | |
|       const actual = convert(`import {Component} from '@angular/core';
 | |
|       declare const NotComponent: Function;
 | |
| 
 | |
|       export class Foo {
 | |
|         static decorators: {type: Function, args?: any[]}[] = [{
 | |
|           type: Component,
 | |
|           args: [{
 | |
|             styleUrls: ['./thing1.css', './thing2.css'],
 | |
|           }],
 | |
|         }];
 | |
|       }
 | |
|     `);
 | |
|       expect(actual).not.toContain('styleUrls:');
 | |
|       expect(actual.replace(/\s+/g, ' '))
 | |
|           .toMatch(
 | |
|               /Foo\.decorators = [{ .*type: core_1\.Component, args: [{ style: "Some template" }]/);
 | |
|     });
 | |
|   });
 | |
| });
 | |
| 
 | |
| describe('metadata transformer', () => {
 | |
|   it('should transform decorators', () => {
 | |
|     const source = `import {Component} from '@angular/core';
 | |
|       @Component({
 | |
|         templateUrl: './thing.html',
 | |
|         styleUrls: ['./thing1.css', './thing2.css'],
 | |
|         styles: ['h1 { color: red }'],
 | |
|       })
 | |
|       export class Foo {}
 | |
|     `;
 | |
|     const sourceFile = ts.createSourceFile(
 | |
|         'someFile.ts', source, ts.ScriptTarget.Latest, /* setParentNodes */ true);
 | |
|     const cache = new MetadataCache(
 | |
|         new MetadataCollector(), /* strict */ true,
 | |
|         [new InlineResourcesMetadataTransformer(
 | |
|             {loadResource, resourceNameToFileName: (u: string) => u})]);
 | |
|     const metadata = cache.getMetadata(sourceFile);
 | |
|     expect(metadata).toBeDefined('Expected metadata from test source file');
 | |
|     if (metadata) {
 | |
|       const classData = metadata.metadata['Foo'];
 | |
|       expect(classData && isClassMetadata(classData))
 | |
|           .toBeDefined(`Expected metadata to contain data for Foo`);
 | |
|       if (classData && isClassMetadata(classData)) {
 | |
|         expect(JSON.stringify(classData)).not.toContain('templateUrl');
 | |
|         expect(JSON.stringify(classData)).toContain('"template":"Some template"');
 | |
|         expect(JSON.stringify(classData)).not.toContain('styleUrls');
 | |
|         expect(JSON.stringify(classData))
 | |
|             .toContain('"styles":["h1 { color: red }",".some_style {}",".some_other_style {}"]');
 | |
|       }
 | |
|     }
 | |
|   });
 | |
| });
 | |
| 
 | |
| function loadResource(path: string): Promise<string>|string {
 | |
|   if (path === './thing.html') return 'Some template';
 | |
|   if (path === './thing1.css') return '.some_style {}';
 | |
|   if (path === './thing2.css') return '.some_other_style {}';
 | |
|   throw new Error('No fake data for path ' + path);
 | |
| }
 | |
| 
 | |
| function convert(source: string) {
 | |
|   const baseFileName = 'someFile';
 | |
|   const moduleName = '/' + baseFileName;
 | |
|   const fileName = moduleName + '.ts';
 | |
|   const context = new MockAotContext('/', {[baseFileName + '.ts']: source});
 | |
|   const host = new MockCompilerHost(context);
 | |
| 
 | |
|   const sourceFile =
 | |
|       ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, /* setParentNodes */ true);
 | |
|   const program = ts.createProgram(
 | |
|       [fileName], {
 | |
|         module: ts.ModuleKind.CommonJS,
 | |
|         target: ts.ScriptTarget.ES2017,
 | |
|       },
 | |
|       host);
 | |
|   const moduleSourceFile = program.getSourceFile(fileName);
 | |
|   const transformers: ts.CustomTransformers = {
 | |
|     before: [getInlineResourcesTransformFactory(
 | |
|         program, {loadResource, resourceNameToFileName: (u: string) => u})]
 | |
|   };
 | |
|   let result = '';
 | |
|   const emitResult = program.emit(
 | |
|       moduleSourceFile, (emittedFileName, data, writeByteOrderMark, onError, sourceFiles) => {
 | |
|         if (fileName.startsWith(moduleName)) {
 | |
|           result = data;
 | |
|         }
 | |
|       }, undefined, undefined, transformers);
 | |
|   return result;
 | |
| }
 |