Update the license headers throughout the repository to reference Google LLC rather than Google Inc, for the required license headers. PR Close #37205
		
			
				
	
	
		
			165 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			5.6 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 {PartialModule} from '@angular/compiler';
 | 
						|
import * as o from '@angular/compiler/src/output/output_ast';
 | 
						|
import * as ts from 'typescript';
 | 
						|
 | 
						|
import {getAngularClassTransformerFactory} from '../../src/transformers/r3_transform';
 | 
						|
import {Directory, MockAotContext, MockCompilerHost} from '../mocks';
 | 
						|
 | 
						|
const someGenFilePath = '/somePackage/someGenFile';
 | 
						|
const someGenFileName = someGenFilePath + '.ts';
 | 
						|
 | 
						|
describe('r3_transform_spec', () => {
 | 
						|
  let context: MockAotContext;
 | 
						|
  let host: MockCompilerHost;
 | 
						|
 | 
						|
  beforeEach(() => {
 | 
						|
    context = new MockAotContext('/', FILES);
 | 
						|
    host = new MockCompilerHost(context);
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to generate a simple identity function', () => {
 | 
						|
    expect(emitStaticMethod(new o.ReturnStatement(o.variable('v')), ['v']))
 | 
						|
        .toContain('static someMethod(v) { return v; }');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to generate a static field declaration', () => {
 | 
						|
    expect(emitStaticField(o.literal(10))).toContain('SomeClass.someField = 10');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to import a symbol', () => {
 | 
						|
    expect(emitStaticMethod(new o.ReturnStatement(
 | 
						|
               o.importExpr(new o.ExternalReference('@angular/core', 'Component')))))
 | 
						|
        .toContain('static someMethod() { return i0.Component; } }');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should be able to modify multiple classes in the same module', () => {
 | 
						|
    const result = emit(getAngularClassTransformerFactory(
 | 
						|
        [{
 | 
						|
          fileName: someGenFileName,
 | 
						|
          statements: [
 | 
						|
            classMethod(new o.ReturnStatement(o.variable('v')), ['v'], 'someMethod', 'SomeClass'),
 | 
						|
            classMethod(
 | 
						|
                new o.ReturnStatement(o.variable('v')), ['v'], 'someOtherMethod', 'SomeOtherClass')
 | 
						|
          ]
 | 
						|
        }],
 | 
						|
        false));
 | 
						|
    expect(result).toContain('static someMethod(v) { return v; }');
 | 
						|
    expect(result).toContain('static someOtherMethod(v) { return v; }');
 | 
						|
  });
 | 
						|
 | 
						|
  it('should insert imports after existing imports', () => {
 | 
						|
    context = context.override({
 | 
						|
      somePackage: {
 | 
						|
        'someGenFile.ts': `
 | 
						|
        import {Component} from '@angular/core';
 | 
						|
 | 
						|
        @Component({selector: 'some-class', template: 'hello!'})
 | 
						|
        export class SomeClass {}
 | 
						|
 | 
						|
        export class SomeOtherClass {}
 | 
						|
      `
 | 
						|
      }
 | 
						|
    });
 | 
						|
    host = new MockCompilerHost(context);
 | 
						|
 | 
						|
    expect(emitStaticMethod(new o.ReturnStatement(
 | 
						|
               o.importExpr(new o.ExternalReference('@angular/core', 'Component')))))
 | 
						|
        .toContain('const core_1 = require("@angular/core"); const i0 = require("@angular/core");');
 | 
						|
  });
 | 
						|
 | 
						|
  function emit(factory: ts.TransformerFactory<ts.SourceFile>): string {
 | 
						|
    let result: string = '';
 | 
						|
    const program = ts.createProgram(
 | 
						|
        [someGenFileName], {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2017}, host);
 | 
						|
    const moduleSourceFile = program.getSourceFile(someGenFileName);
 | 
						|
    const transformers: ts.CustomTransformers = {before: [factory]};
 | 
						|
    const emitResult = program.emit(
 | 
						|
        moduleSourceFile, (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
 | 
						|
          if (fileName.startsWith(someGenFilePath)) {
 | 
						|
            result = data;
 | 
						|
          }
 | 
						|
        }, undefined, undefined, transformers);
 | 
						|
    return normalizeResult(result);
 | 
						|
  }
 | 
						|
 | 
						|
  function emitStaticMethod(
 | 
						|
      stmt: o.Statement|o.Statement[], parameters: string[] = [], methodName: string = 'someMethod',
 | 
						|
      className: string = 'SomeClass'): string {
 | 
						|
    const module: PartialModule = {
 | 
						|
      fileName: someGenFileName,
 | 
						|
      statements: [classMethod(stmt, parameters, methodName, className)]
 | 
						|
    };
 | 
						|
    return emit(getAngularClassTransformerFactory([module], false));
 | 
						|
  }
 | 
						|
 | 
						|
  function emitStaticField(
 | 
						|
      initializer: o.Expression, fieldName: string = 'someField',
 | 
						|
      className: string = 'SomeClass'): string {
 | 
						|
    const module: PartialModule = {
 | 
						|
      fileName: someGenFileName,
 | 
						|
      statements: [classField(initializer, fieldName, className)]
 | 
						|
    };
 | 
						|
    return emit(getAngularClassTransformerFactory([module], false));
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
const FILES: Directory = {
 | 
						|
  somePackage: {
 | 
						|
    'someGenFile.ts': `
 | 
						|
 | 
						|
  export class SomeClass {}
 | 
						|
 | 
						|
  export class SomeOtherClass {}
 | 
						|
`
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
function classMethod(
 | 
						|
    stmt: o.Statement|o.Statement[], parameters: string[] = [], methodName: string = 'someMethod',
 | 
						|
    className: string = 'SomeClass'): o.ClassStmt {
 | 
						|
  const statements = Array.isArray(stmt) ? stmt : [stmt];
 | 
						|
  return new o.ClassStmt(
 | 
						|
      /* name */ className,
 | 
						|
      /* parent */ null,
 | 
						|
      /* fields */[],
 | 
						|
      /* getters */[],
 | 
						|
      /* constructorMethod */ new o.ClassMethod(null, [], []),
 | 
						|
      /* methods */[new o.ClassMethod(
 | 
						|
          methodName, parameters.map(name => new o.FnParam(name)), statements, null,
 | 
						|
          [o.StmtModifier.Static])]);
 | 
						|
}
 | 
						|
 | 
						|
function classField(
 | 
						|
    initializer: o.Expression, fieldName: string = 'someField',
 | 
						|
    className: string = 'SomeClass'): o.ClassStmt {
 | 
						|
  return new o.ClassStmt(
 | 
						|
      /* name */ className,
 | 
						|
      /* parent */ null,
 | 
						|
      /* fields */[new o.ClassField(fieldName, null, [o.StmtModifier.Static], initializer)],
 | 
						|
      /* getters */[],
 | 
						|
      /* constructorMethod */ new o.ClassMethod(null, [], []),
 | 
						|
      /* methods */[]);
 | 
						|
}
 | 
						|
 | 
						|
function normalizeResult(result: string): string {
 | 
						|
  // Remove TypeScript prefixes
 | 
						|
  // Remove new lines
 | 
						|
  // Squish adjacent spaces
 | 
						|
  // Remove prefix and postfix spaces
 | 
						|
  return result.replace('"use strict";', ' ')
 | 
						|
      .replace('exports.__esModule = true;', ' ')
 | 
						|
      .replace('Object.defineProperty(exports, "__esModule", { value: true });', ' ')
 | 
						|
      .replace(/\n/g, ' ')
 | 
						|
      .replace(/ +/g, ' ')
 | 
						|
      .replace(/^ /g, '')
 | 
						|
      .replace(/ $/g, '');
 | 
						|
}
 |