117 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			117 lines
		
	
	
		
			3.9 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 {parse} from '@babel/parser';
							 | 
						||
| 
								 | 
							
								import traverse, {NodePath} from '@babel/traverse';
							 | 
						||
| 
								 | 
							
								import * as t from '@babel/types';
							 | 
						||
| 
								 | 
							
								import {BabelDeclarationScope} from '../src/babel_declaration_scope';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								describe('BabelDeclarationScope', () => {
							 | 
						||
| 
								 | 
							
								  describe('getConstantScopeRef()', () => {
							 | 
						||
| 
								 | 
							
								    it('should return a path to the ES module where the expression was imported', () => {
							 | 
						||
| 
								 | 
							
								      const ast = parse(
							 | 
						||
| 
								 | 
							
								          [
							 | 
						||
| 
								 | 
							
								            'import * as core from \'@angular/core\';',
							 | 
						||
| 
								 | 
							
								            'function foo() {',
							 | 
						||
| 
								 | 
							
								            '  var TEST = core;',
							 | 
						||
| 
								 | 
							
								            '}',
							 | 
						||
| 
								 | 
							
								          ].join('\n'),
							 | 
						||
| 
								 | 
							
								          {sourceType: 'module'});
							 | 
						||
| 
								 | 
							
								      const nodePath = findVarDeclaration(ast, 'TEST');
							 | 
						||
| 
								 | 
							
								      const scope = new BabelDeclarationScope(nodePath.scope);
							 | 
						||
| 
								 | 
							
								      const constantScope = scope.getConstantScopeRef(nodePath.get('init').node);
							 | 
						||
| 
								 | 
							
								      expect(constantScope).not.toBe(null);
							 | 
						||
| 
								 | 
							
								      expect(constantScope!.node).toBe(ast.program);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    it('should return a path to the ES Module where the expression is declared', () => {
							 | 
						||
| 
								 | 
							
								      const ast = parse(
							 | 
						||
| 
								 | 
							
								          [
							 | 
						||
| 
								 | 
							
								            'var core;',
							 | 
						||
| 
								 | 
							
								            'export function foo() {',
							 | 
						||
| 
								 | 
							
								            '  var TEST = core;',
							 | 
						||
| 
								 | 
							
								            '}',
							 | 
						||
| 
								 | 
							
								          ].join('\n'),
							 | 
						||
| 
								 | 
							
								          {sourceType: 'module'});
							 | 
						||
| 
								 | 
							
								      const nodePath = findVarDeclaration(ast, 'TEST');
							 | 
						||
| 
								 | 
							
								      const scope = new BabelDeclarationScope(nodePath.scope);
							 | 
						||
| 
								 | 
							
								      const constantScope = scope.getConstantScopeRef(nodePath.get('init').node);
							 | 
						||
| 
								 | 
							
								      expect(constantScope).not.toBe(null);
							 | 
						||
| 
								 | 
							
								      expect(constantScope!.node).toBe(ast.program);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    it('should return null if the file is not an ES module', () => {
							 | 
						||
| 
								 | 
							
								      const ast = parse(
							 | 
						||
| 
								 | 
							
								          [
							 | 
						||
| 
								 | 
							
								            'var core;',
							 | 
						||
| 
								 | 
							
								            'function foo() {',
							 | 
						||
| 
								 | 
							
								            '  var TEST = core;',
							 | 
						||
| 
								 | 
							
								            '}',
							 | 
						||
| 
								 | 
							
								          ].join('\n'),
							 | 
						||
| 
								 | 
							
								          {sourceType: 'script'});
							 | 
						||
| 
								 | 
							
								      const nodePath = findVarDeclaration(ast, 'TEST');
							 | 
						||
| 
								 | 
							
								      const scope = new BabelDeclarationScope(nodePath.scope);
							 | 
						||
| 
								 | 
							
								      const constantScope = scope.getConstantScopeRef(nodePath.get('init').node);
							 | 
						||
| 
								 | 
							
								      expect(constantScope).toBe(null);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    it('should return the IIFE factory function where the expression is a parameter', () => {
							 | 
						||
| 
								 | 
							
								      const ast = parse(
							 | 
						||
| 
								 | 
							
								          [
							 | 
						||
| 
								 | 
							
								            'var core;',
							 | 
						||
| 
								 | 
							
								            '(function(core) {',
							 | 
						||
| 
								 | 
							
								            '  var BLOCK = \'block\';',
							 | 
						||
| 
								 | 
							
								            '  function foo() {',
							 | 
						||
| 
								 | 
							
								            '    var TEST = core;',
							 | 
						||
| 
								 | 
							
								            '  }',
							 | 
						||
| 
								 | 
							
								            '})(core);',
							 | 
						||
| 
								 | 
							
								          ].join('\n'),
							 | 
						||
| 
								 | 
							
								          {sourceType: 'script'});
							 | 
						||
| 
								 | 
							
								      const nodePath = findVarDeclaration(ast, 'TEST');
							 | 
						||
| 
								 | 
							
								      const fnPath = findFirstFunction(ast);
							 | 
						||
| 
								 | 
							
								      const scope = new BabelDeclarationScope(nodePath.scope);
							 | 
						||
| 
								 | 
							
								      const constantScope = scope.getConstantScopeRef(nodePath.get('init').node);
							 | 
						||
| 
								 | 
							
								      expect(constantScope).not.toBe(null);
							 | 
						||
| 
								 | 
							
								      expect(constantScope!.isFunction()).toBe(true);
							 | 
						||
| 
								 | 
							
								      expect(constantScope!.node).toEqual(fnPath.node);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function findVarDeclaration(
							 | 
						||
| 
								 | 
							
								    file: t.File, varName: string): NodePath<t.VariableDeclarator&{init: t.Expression}> {
							 | 
						||
| 
								 | 
							
								  let varDecl: NodePath<t.VariableDeclarator>|undefined = undefined;
							 | 
						||
| 
								 | 
							
								  traverse(file, {
							 | 
						||
| 
								 | 
							
								    VariableDeclarator: (path) => {
							 | 
						||
| 
								 | 
							
								      const id = path.get('id');
							 | 
						||
| 
								 | 
							
								      if (id.isIdentifier() && id.node.name === varName && path.get('init') !== null) {
							 | 
						||
| 
								 | 
							
								        varDecl = path;
							 | 
						||
| 
								 | 
							
								        path.stop();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  if (varDecl === undefined) {
							 | 
						||
| 
								 | 
							
								    throw new Error(`TEST BUG: expected to find variable declaration for ${varName}.`);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return varDecl;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function findFirstFunction(file: t.File): NodePath<t.Function> {
							 | 
						||
| 
								 | 
							
								  let fn: NodePath<t.Function>|undefined = undefined;
							 | 
						||
| 
								 | 
							
								  traverse(file, {
							 | 
						||
| 
								 | 
							
								    Function: (path) => {
							 | 
						||
| 
								 | 
							
								      fn = path;
							 | 
						||
| 
								 | 
							
								      path.stop();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  if (fn === undefined) {
							 | 
						||
| 
								 | 
							
								    throw new Error(`TEST BUG: expected to find a function.`);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return fn;
							 | 
						||
| 
								 | 
							
								}
							 |