2017-09-13 19:55:42 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. 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 {isMetadataGlobalReferenceExpression} from '../../src/metadata/schema';
|
|
|
|
import {Symbols} from '../../src/metadata/symbols';
|
|
|
|
|
2020-04-07 15:43:43 -04:00
|
|
|
import {Directory, expectNoDiagnostics, Host} from './typescript.mocks';
|
2017-09-13 19:55:42 -04:00
|
|
|
|
|
|
|
describe('Symbols', () => {
|
|
|
|
let symbols: Symbols;
|
|
|
|
const someValue = 'some-value';
|
|
|
|
|
|
|
|
beforeEach(() => symbols = new Symbols(null as any as ts.SourceFile));
|
|
|
|
|
|
|
|
it('should be able to add a symbol', () => symbols.define('someSymbol', someValue));
|
|
|
|
|
|
|
|
beforeEach(() => symbols.define('someSymbol', someValue));
|
|
|
|
|
|
|
|
it('should be able to `has` a symbol', () => expect(symbols.has('someSymbol')).toBeTruthy());
|
|
|
|
it('should be able to `get` a symbol value',
|
|
|
|
() => expect(symbols.resolve('someSymbol')).toBe(someValue));
|
|
|
|
it('should be able to `get` a symbol value',
|
|
|
|
() => expect(symbols.resolve('someSymbol')).toBe(someValue));
|
|
|
|
it('should be able to determine symbol is missing',
|
|
|
|
() => expect(symbols.has('missingSymbol')).toBeFalsy());
|
|
|
|
it('should return undefined from `get` for a missing symbol',
|
|
|
|
() => expect(symbols.resolve('missingSymbol')).toBeUndefined());
|
|
|
|
|
|
|
|
let host: ts.LanguageServiceHost;
|
|
|
|
let service: ts.LanguageService;
|
|
|
|
let program: ts.Program;
|
|
|
|
let expressions: ts.SourceFile;
|
|
|
|
let imports: ts.SourceFile;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
host = new Host(FILES, ['consts.ts', 'expressions.ts', 'imports.ts']);
|
|
|
|
service = ts.createLanguageService(host);
|
2020-04-07 15:43:43 -04:00
|
|
|
program = service.getProgram()!;
|
|
|
|
expressions = program.getSourceFile('expressions.ts')!;
|
|
|
|
imports = program.getSourceFile('imports.ts')!;
|
2017-09-13 19:55:42 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should not have syntax errors in the test sources', () => {
|
|
|
|
expectNoDiagnostics(service.getCompilerOptionsDiagnostics());
|
|
|
|
for (const sourceFile of program.getSourceFiles()) {
|
|
|
|
expectNoDiagnostics(service.getSyntacticDiagnostics(sourceFile.fileName));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to find the source files', () => {
|
|
|
|
expect(expressions).toBeDefined();
|
|
|
|
expect(imports).toBeDefined();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to create symbols for a source file', () => {
|
|
|
|
const symbols = new Symbols(expressions);
|
|
|
|
expect(symbols).toBeDefined();
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should be able to find symbols in expression', () => {
|
|
|
|
const symbols = new Symbols(expressions);
|
|
|
|
expect(symbols.has('someName')).toBeTruthy();
|
|
|
|
expect(symbols.resolve('someName'))
|
|
|
|
.toEqual({__symbolic: 'reference', module: './consts', name: 'someName'});
|
|
|
|
expect(symbols.has('someBool')).toBeTruthy();
|
|
|
|
expect(symbols.resolve('someBool'))
|
|
|
|
.toEqual({__symbolic: 'reference', module: './consts', name: 'someBool'});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to detect a * import', () => {
|
|
|
|
const symbols = new Symbols(imports);
|
|
|
|
expect(symbols.resolve('b')).toEqual({__symbolic: 'reference', module: 'b'});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to detect importing a default export', () => {
|
|
|
|
const symbols = new Symbols(imports);
|
|
|
|
expect(symbols.resolve('d')).toEqual({__symbolic: 'reference', module: 'd', default: true});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to import a renamed symbol', () => {
|
|
|
|
const symbols = new Symbols(imports);
|
|
|
|
expect(symbols.resolve('g')).toEqual({__symbolic: 'reference', name: 'f', module: 'f'});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should be able to resolve any symbol in core global scope', () => {
|
2017-12-22 12:36:47 -05:00
|
|
|
const core = (program.getSourceFiles() as ts.SourceFile[])
|
|
|
|
.find(source => source.fileName.endsWith('lib.d.ts'));
|
2017-09-13 19:55:42 -04:00
|
|
|
expect(core).toBeDefined();
|
|
|
|
const visit = (node: ts.Node): boolean => {
|
|
|
|
switch (node.kind) {
|
|
|
|
case ts.SyntaxKind.VariableStatement:
|
|
|
|
case ts.SyntaxKind.VariableDeclarationList:
|
|
|
|
return !!ts.forEachChild(node, visit);
|
|
|
|
case ts.SyntaxKind.VariableDeclaration:
|
|
|
|
const variableDeclaration = <ts.VariableDeclaration>node;
|
|
|
|
const nameNode = <ts.Identifier>variableDeclaration.name;
|
|
|
|
const name = nameNode.text;
|
|
|
|
const result = symbols.resolve(name);
|
|
|
|
expect(isMetadataGlobalReferenceExpression(result) && result.name).toEqual(name);
|
|
|
|
|
|
|
|
// Ignore everything after Float64Array as it is IE specific.
|
|
|
|
return name === 'Float64Array';
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
2020-04-07 15:43:43 -04:00
|
|
|
ts.forEachChild(core!, visit);
|
2017-09-13 19:55:42 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const FILES: Directory = {
|
|
|
|
'consts.ts': `
|
|
|
|
export var someName = 'some-name';
|
|
|
|
export var someBool = true;
|
|
|
|
export var one = 1;
|
|
|
|
export var two = 2;
|
|
|
|
`,
|
|
|
|
'expressions.ts': `
|
|
|
|
import {someName, someBool, one, two} from './consts';
|
|
|
|
`,
|
|
|
|
'imports.ts': `
|
|
|
|
import * as b from 'b';
|
|
|
|
import 'c';
|
|
|
|
import d from 'd';
|
|
|
|
import {f as g} from 'f';
|
|
|
|
`
|
|
|
|
};
|