refactor(compiler-cli): linker - add Babel plugin, FileLinker and initial PartialLinkers (#39116)
This commit adds the basic building blocks for linking partial declarations. In particular it provides a generic `FileLinker` class that delegates to a set of (not yet implemented) `PartialLinker` classes. The Babel plugin makes use of this `FileLinker` providing concrete classes for `AstHost` and `AstFactory` that work with Babel AST. It can be created with the following code: ```ts const plugin = createEs2015LinkerPlugin({ /* options */ }); ``` PR Close #39116
This commit is contained in:

committed by
Andrew Kushnir

parent
b304bd0535
commit
7e742aea7c
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @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 o from '@angular/compiler/src/output/output_ast';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {TypeScriptAstFactory} from '../../../../src/ngtsc/translator';
|
||||
import {TypeScriptAstHost} from '../../../src/ast/typescript/typescript_ast_host';
|
||||
import {EmitScope} from '../../../src/file_linker/emit_scopes/emit_scope';
|
||||
import {LinkerEnvironment} from '../../../src/file_linker/linker_environment';
|
||||
import {DEFAULT_LINKER_OPTIONS} from '../../../src/file_linker/linker_options';
|
||||
import {generate} from '../helpers';
|
||||
|
||||
describe('EmitScope', () => {
|
||||
describe('translateDefinition()', () => {
|
||||
it('should translate the given output AST into a TExpression', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new EmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
|
||||
const def = emitScope.translateDefinition(o.fn([], [], null, null, 'foo'));
|
||||
expect(generate(def)).toEqual('function foo() { }');
|
||||
});
|
||||
|
||||
it('should use the `ngImport` idenfifier for imports when translating', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new EmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
|
||||
const coreImportRef = new o.ExternalReference('@angular/core', 'foo');
|
||||
const def = emitScope.translateDefinition(o.importExpr(coreImportRef).callMethod('bar', []));
|
||||
expect(generate(def)).toEqual('core.foo.bar()');
|
||||
});
|
||||
|
||||
it('should not emit any shared constants in the replacement expression', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new EmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
|
||||
const constArray = o.literalArr([o.literal('CONST')]);
|
||||
// We have to add the constant twice or it will not create a shared statement
|
||||
emitScope.constantPool.getConstLiteral(constArray);
|
||||
emitScope.constantPool.getConstLiteral(constArray);
|
||||
|
||||
const def = emitScope.translateDefinition(o.fn([], [], null, null, 'foo'));
|
||||
expect(generate(def)).toEqual('function foo() { }');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConstantStatements()', () => {
|
||||
it('should return any constant statements that were added to the `constantPool`', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new EmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
|
||||
const constArray = o.literalArr([o.literal('CONST')]);
|
||||
// We have to add the constant twice or it will not create a shared statement
|
||||
emitScope.constantPool.getConstLiteral(constArray);
|
||||
emitScope.constantPool.getConstLiteral(constArray);
|
||||
|
||||
const statements = emitScope.getConstantStatements();
|
||||
expect(statements.map(generate)).toEqual(['const _c0 = ["CONST"];']);
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @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 o from '@angular/compiler/src/output/output_ast';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {TypeScriptAstFactory} from '../../../../src/ngtsc/translator';
|
||||
import {TypeScriptAstHost} from '../../../src/ast/typescript/typescript_ast_host';
|
||||
import {IifeEmitScope} from '../../../src/file_linker/emit_scopes/iife_emit_scope';
|
||||
import {LinkerEnvironment} from '../../../src/file_linker/linker_environment';
|
||||
import {DEFAULT_LINKER_OPTIONS} from '../../../src/file_linker/linker_options';
|
||||
import {generate} from '../helpers';
|
||||
|
||||
describe('IifeEmitScope', () => {
|
||||
describe('translateDefinition()', () => {
|
||||
it('should translate the given output AST into a TExpression, wrapped in an IIFE', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new IifeEmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
|
||||
const def = emitScope.translateDefinition(o.fn([], [], null, null, 'foo'));
|
||||
expect(generate(def)).toEqual('function () { return function foo() { }; }()');
|
||||
});
|
||||
|
||||
it('should use the `ngImport` idenfifier for imports when translating', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new IifeEmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
|
||||
const coreImportRef = new o.ExternalReference('@angular/core', 'foo');
|
||||
const def = emitScope.translateDefinition(o.importExpr(coreImportRef).callMethod('bar', []));
|
||||
expect(generate(def)).toEqual('function () { return core.foo.bar(); }()');
|
||||
});
|
||||
|
||||
it('should emit any shared constants in the replacement expression IIFE', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new IifeEmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
|
||||
const constArray = o.literalArr([o.literal('CONST')]);
|
||||
// We have to add the constant twice or it will not create a shared statement
|
||||
emitScope.constantPool.getConstLiteral(constArray);
|
||||
emitScope.constantPool.getConstLiteral(constArray);
|
||||
|
||||
const def = emitScope.translateDefinition(o.fn([], [], null, null, 'foo'));
|
||||
expect(generate(def))
|
||||
.toEqual('function () { const _c0 = ["CONST"]; return function foo() { }; }()');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConstantStatements()', () => {
|
||||
it('should throw an error', () => {
|
||||
const factory = new TypeScriptAstFactory();
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), factory, DEFAULT_LINKER_OPTIONS);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const emitScope = new IifeEmitScope<ts.Statement, ts.Expression>(ngImport, linkerEnvironment);
|
||||
expect(() => emitScope.getConstantStatements()).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @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 o from '@angular/compiler/src/output/output_ast';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {TypeScriptAstFactory} from '../../../src/ngtsc/translator';
|
||||
import {AstHost} from '../../src/ast/ast_host';
|
||||
import {TypeScriptAstHost} from '../../src/ast/typescript/typescript_ast_host';
|
||||
import {DeclarationScope} from '../../src/file_linker/declaration_scope';
|
||||
import {FileLinker} from '../../src/file_linker/file_linker';
|
||||
import {LinkerEnvironment} from '../../src/file_linker/linker_environment';
|
||||
import {DEFAULT_LINKER_OPTIONS} from '../../src/file_linker/linker_options';
|
||||
import {PartialDirectiveLinkerVersion1} from '../../src/file_linker/partial_linkers/partial_directive_linker_1';
|
||||
import {generate} from './helpers';
|
||||
|
||||
describe('FileLinker', () => {
|
||||
let factory: TypeScriptAstFactory;
|
||||
beforeEach(() => factory = new TypeScriptAstFactory());
|
||||
|
||||
describe('isPartialDeclaration()', () => {
|
||||
it('should return true if the callee is recognized', () => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
expect(fileLinker.isPartialDeclaration('$ngDeclareDirective')).toBe(true);
|
||||
expect(fileLinker.isPartialDeclaration('$ngDeclareComponent')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the callee is not recognized', () => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
expect(fileLinker.isPartialDeclaration('$foo')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('linkPartialDeclaration()', () => {
|
||||
it('should throw an error if the function name is not recognised', () => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
const version = factory.createLiteral(1);
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const declarationArg = factory.createObjectLiteral([
|
||||
{propertyName: 'version', quoted: false, value: version},
|
||||
{propertyName: 'ngImport', quoted: false, value: ngImport},
|
||||
]);
|
||||
expect(
|
||||
() => fileLinker.linkPartialDeclaration(
|
||||
'foo', [declarationArg], new MockDeclarationScope()))
|
||||
.toThrowError('Unknown partial declaration function foo.');
|
||||
});
|
||||
|
||||
it('should throw an error if the metadata object does not have a `version` property', () => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const declarationArg = factory.createObjectLiteral([
|
||||
{propertyName: 'ngImport', quoted: false, value: ngImport},
|
||||
]);
|
||||
expect(
|
||||
() => fileLinker.linkPartialDeclaration(
|
||||
'$ngDeclareDirective', [declarationArg], new MockDeclarationScope()))
|
||||
.toThrowError(`Expected property 'version' to be present.`);
|
||||
});
|
||||
|
||||
it('should throw an error if the metadata object does not have a `ngImport` property', () => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const declarationArg = factory.createObjectLiteral([
|
||||
{propertyName: 'version', quoted: false, value: ngImport},
|
||||
]);
|
||||
expect(
|
||||
() => fileLinker.linkPartialDeclaration(
|
||||
'$ngDeclareDirective', [declarationArg], new MockDeclarationScope()))
|
||||
.toThrowError(`Expected property 'ngImport' to be present.`);
|
||||
});
|
||||
|
||||
it('should call `linkPartialDeclaration()` on the appropriate partial compiler', () => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
const compileSpy = spyOn(PartialDirectiveLinkerVersion1.prototype, 'linkPartialDeclaration')
|
||||
.and.returnValue(o.literal('compilation result'));
|
||||
|
||||
const ngImport = factory.createIdentifier('core');
|
||||
const version = factory.createLiteral(1);
|
||||
const declarationArg = factory.createObjectLiteral([
|
||||
{propertyName: 'ngImport', quoted: false, value: ngImport},
|
||||
{propertyName: 'version', quoted: false, value: version},
|
||||
]);
|
||||
|
||||
const compilationResult = fileLinker.linkPartialDeclaration(
|
||||
'$ngDeclareDirective', [declarationArg], new MockDeclarationScope());
|
||||
|
||||
expect(compilationResult).toEqual(factory.createLiteral('compilation result'));
|
||||
expect(compileSpy).toHaveBeenCalled();
|
||||
expect(compileSpy.calls.mostRecent().args[3].getNode('ngImport')).toBe(ngImport);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConstantStatements()', () => {
|
||||
it('should capture shared constant values', () => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT'));
|
||||
|
||||
// Here we use the `core` idenfifier for `ngImport` to trigger the use of a shared scope for
|
||||
// constant statements.
|
||||
const declarationArg = factory.createObjectLiteral([
|
||||
{propertyName: 'ngImport', quoted: false, value: factory.createIdentifier('core')},
|
||||
{propertyName: 'version', quoted: false, value: factory.createLiteral(1)},
|
||||
]);
|
||||
|
||||
const replacement = fileLinker.linkPartialDeclaration(
|
||||
'$ngDeclareDirective', [declarationArg], new MockDeclarationScope());
|
||||
expect(generate(replacement)).toEqual('"REPLACEMENT"');
|
||||
|
||||
const results = fileLinker.getConstantStatements();
|
||||
expect(results.length).toEqual(1);
|
||||
const {constantScope, statements} = results[0];
|
||||
expect(constantScope).toBe(MockConstantScopeRef.singleton);
|
||||
expect(statements.map(generate)).toEqual(['const _c0 = [1];']);
|
||||
});
|
||||
|
||||
it('should be no shared constant statements to capture when they are emitted into the replacement IIFE',
|
||||
() => {
|
||||
const {fileLinker} = createFileLinker();
|
||||
spyOnLinkPartialDeclarationWithConstants(o.literal('REPLACEMENT'));
|
||||
|
||||
// Here we use a string literal `"not-a-module"` for `ngImport` to cause constant
|
||||
// statements to be emitted in an IIFE rather than added to the shared constant scope.
|
||||
const declarationArg = factory.createObjectLiteral([
|
||||
{propertyName: 'ngImport', quoted: false, value: factory.createLiteral('not-a-module')},
|
||||
{propertyName: 'version', quoted: false, value: factory.createLiteral(1)},
|
||||
]);
|
||||
|
||||
const replacement = fileLinker.linkPartialDeclaration(
|
||||
'$ngDeclareDirective', [declarationArg], new MockDeclarationScope());
|
||||
expect(generate(replacement))
|
||||
.toEqual('function () { const _c0 = [1]; return "REPLACEMENT"; }()');
|
||||
|
||||
const results = fileLinker.getConstantStatements();
|
||||
expect(results.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
function createFileLinker(): {
|
||||
host: AstHost<ts.Expression>,
|
||||
fileLinker: FileLinker<MockConstantScopeRef, ts.Statement, ts.Expression>
|
||||
} {
|
||||
const linkerEnvironment = LinkerEnvironment.create<ts.Statement, ts.Expression>(
|
||||
new TypeScriptAstHost(), new TypeScriptAstFactory(), DEFAULT_LINKER_OPTIONS);
|
||||
const fileLinker = new FileLinker<MockConstantScopeRef, ts.Statement, ts.Expression>(
|
||||
linkerEnvironment, 'test.js', '// test code');
|
||||
return {host: linkerEnvironment.host, fileLinker};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* This mock implementation of `DeclarationScope` will return a singleton instance of
|
||||
* `MockConstantScopeRef` if the expression is an identifier, or `null` otherwise.
|
||||
*
|
||||
* This way we can simulate whether the constants will be shared or inlined into an IIFE.
|
||||
*/
|
||||
class MockDeclarationScope implements DeclarationScope<MockConstantScopeRef, ts.Expression> {
|
||||
getConstantScopeRef(expression: ts.Expression): MockConstantScopeRef|null {
|
||||
if (ts.isIdentifier(expression)) {
|
||||
return MockConstantScopeRef.singleton;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MockConstantScopeRef {
|
||||
private constructor() {}
|
||||
static singleton = new MockDeclarationScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* Spy on the `PartialDirectiveLinkerVersion1.linkPartialDeclaration()` method, triggering
|
||||
* shared constants to be created.
|
||||
*/
|
||||
function spyOnLinkPartialDeclarationWithConstants(replacement: o.Expression) {
|
||||
let callCount = 0;
|
||||
spyOn(PartialDirectiveLinkerVersion1.prototype, 'linkPartialDeclaration')
|
||||
.and.callFake(((sourceUrl, code, constantPool) => {
|
||||
const constArray = o.literalArr([o.literal(++callCount)]);
|
||||
// We have to add the constant twice or it will not create a shared statement
|
||||
constantPool.getConstLiteral(constArray);
|
||||
constantPool.getConstLiteral(constArray);
|
||||
return replacement;
|
||||
}) as typeof PartialDirectiveLinkerVersion1.prototype.linkPartialDeclaration);
|
||||
}
|
17
packages/compiler-cli/linker/test/file_linker/helpers.ts
Normal file
17
packages/compiler-cli/linker/test/file_linker/helpers.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @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';
|
||||
|
||||
/**
|
||||
* A simple helper to render a TS Node as a string.
|
||||
*/
|
||||
export function generate(node: ts.Node): string {
|
||||
const printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed});
|
||||
const sf = ts.createSourceFile('test.ts', '', ts.ScriptTarget.ES2015, true);
|
||||
return printer.printNode(ts.EmitHint.Unspecified, node, sf);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @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 {PartialComponentLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_component_linker_1';
|
||||
import {PartialDirectiveLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_directive_linker_1';
|
||||
import {PartialLinkerSelector} from '../../../src/file_linker/partial_linkers/partial_linker_selector';
|
||||
|
||||
describe('PartialLinkerSelector', () => {
|
||||
describe('supportsDeclaration()', () => {
|
||||
it('should return true if there is at least one linker that matches the given function name',
|
||||
() => {
|
||||
const selector = new PartialLinkerSelector();
|
||||
expect(selector.supportsDeclaration('$ngDeclareDirective')).toBe(true);
|
||||
expect(selector.supportsDeclaration('$ngDeclareComponent')).toBe(true);
|
||||
expect(selector.supportsDeclaration('$foo')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLinker()', () => {
|
||||
it('should return the linker that matches the name and version number', () => {
|
||||
const selector = new PartialLinkerSelector();
|
||||
expect(selector.getLinker('$ngDeclareDirective', 1))
|
||||
.toBeInstanceOf(PartialDirectiveLinkerVersion1);
|
||||
expect(selector.getLinker('$ngDeclareComponent', 1))
|
||||
.toBeInstanceOf(PartialComponentLinkerVersion1);
|
||||
});
|
||||
|
||||
it('should throw an error if there is no linker that matches the given name or version', () => {
|
||||
const selector = new PartialLinkerSelector();
|
||||
expect(() => selector.getLinker('$foo', 1))
|
||||
.toThrowError('Unknown partial declaration function $foo.');
|
||||
expect(() => selector.getLinker('$ngDeclareDirective', 2))
|
||||
.toThrowError('Unsupported partial declaration version 2 for $ngDeclareDirective.');
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @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 o from '@angular/compiler';
|
||||
import {ImportGenerator, NamedImport, TypeScriptAstFactory} from '@angular/compiler-cli/src/ngtsc/translator';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Translator} from '../../src/file_linker/translator';
|
||||
import {generate} from './helpers';
|
||||
|
||||
describe('Translator', () => {
|
||||
let factory: TypeScriptAstFactory;
|
||||
beforeEach(() => factory = new TypeScriptAstFactory());
|
||||
|
||||
describe('translateExpression()', () => {
|
||||
it('should generate expression specific output', () => {
|
||||
const translator = new Translator<ts.Statement, ts.Expression>(factory);
|
||||
const outputAst = new o.WriteVarExpr('foo', new o.LiteralExpr(42));
|
||||
const translated = translator.translateExpression(outputAst, new MockImportGenerator());
|
||||
expect(generate(translated)).toEqual('(foo = 42)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('translateStatement()', () => {
|
||||
it('should generate statement specific output', () => {
|
||||
const translator = new Translator<ts.Statement, ts.Expression>(factory);
|
||||
const outputAst = new o.ExpressionStatement(new o.WriteVarExpr('foo', new o.LiteralExpr(42)));
|
||||
const translated = translator.translateStatement(outputAst, new MockImportGenerator());
|
||||
expect(generate(translated)).toEqual('foo = 42;');
|
||||
});
|
||||
});
|
||||
class MockImportGenerator implements ImportGenerator<ts.Expression> {
|
||||
generateNamespaceImport(moduleName: string): ts.Expression {
|
||||
return factory.createLiteral(moduleName);
|
||||
}
|
||||
generateNamedImport(moduleName: string, originalSymbol: string): NamedImport<ts.Expression> {
|
||||
return {
|
||||
moduleImport: factory.createLiteral(moduleName),
|
||||
symbol: originalSymbol,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
Reference in New Issue
Block a user