ScopeIdentifiersTraverser.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import { inject, injectable, } from 'inversify';
  2. import { ServiceIdentifiers } from '../container/ServiceIdentifiers';
  3. import * as eslintScope from 'eslint-scope';
  4. import * as ESTree from 'estree';
  5. import { TNodeWithLexicalScope } from '../types/node/TNodeWithLexicalScope';
  6. import { TScopeIdentifiersTraverserCallback } from '../types/node/TScopeIdentifiersTraverserCallback';
  7. import { IScopeAnalyzer } from '../interfaces/analyzers/scope-analyzer/IScopeAnalyzer';
  8. import { IScopeIdentifiersTraverser } from '../interfaces/node/IScopeIdentifiersTraverser';
  9. import { IScopeIdentifiersTraverserCallbackData } from '../interfaces/node/IScopeIdentifiersTraverserCallbackData';
  10. import { IScopeThroughIdentifiersTraverserCallbackData } from '../interfaces/node/IScopeThroughIdentifiersTraverserCallbackData';
  11. import { NodeGuards } from './NodeGuards';
  12. /**
  13. * Scope traverser
  14. */
  15. @injectable()
  16. export class ScopeIdentifiersTraverser implements IScopeIdentifiersTraverser {
  17. /**
  18. * @type {string}
  19. */
  20. private static readonly argumentsVariableName: string = 'arguments';
  21. /**
  22. * @type {string[]}
  23. */
  24. private static readonly globalScopeNames: string[] = [
  25. 'global',
  26. 'module'
  27. ];
  28. /**
  29. * @type {IScopeAnalyzer}
  30. */
  31. private readonly scopeAnalyzer: IScopeAnalyzer;
  32. /**
  33. * @param {IScopeAnalyzer} scopeAnalyzer
  34. */
  35. public constructor (
  36. @inject(ServiceIdentifiers.IScopeAnalyzer) scopeAnalyzer: IScopeAnalyzer
  37. ) {
  38. this.scopeAnalyzer = scopeAnalyzer;
  39. }
  40. /**
  41. * @param {Program} programNode
  42. * @param {Node | null} parentNode
  43. * @param {TScopeIdentifiersTraverserCallback<IScopeIdentifiersTraverserCallbackData>} callback
  44. */
  45. public traverseScopeIdentifiers (
  46. programNode: ESTree.Program,
  47. parentNode: ESTree.Node | null,
  48. callback: TScopeIdentifiersTraverserCallback<IScopeIdentifiersTraverserCallbackData>
  49. ): void {
  50. this.scopeAnalyzer.analyze(programNode);
  51. const globalScope: eslintScope.Scope = this.scopeAnalyzer.acquireScope(programNode);
  52. this.traverseScopeIdentifiersRecursive(globalScope, globalScope, callback);
  53. }
  54. /**
  55. * @param {Program} programNode
  56. * @param {Node | null} parentNode
  57. * @param {TScopeIdentifiersTraverserCallback<IScopeThroughIdentifiersTraverserCallbackData>} callback
  58. */
  59. public traverseScopeThroughIdentifiers (
  60. programNode: ESTree.Program,
  61. parentNode: ESTree.Node | null,
  62. callback: TScopeIdentifiersTraverserCallback<IScopeThroughIdentifiersTraverserCallbackData>
  63. ): void {
  64. this.scopeAnalyzer.analyze(programNode);
  65. const globalScope: eslintScope.Scope = this.scopeAnalyzer.acquireScope(programNode);
  66. this.traverseScopeThroughIdentifiersRecursive(globalScope, globalScope, callback);
  67. }
  68. /**
  69. * @param {Scope} rootScope
  70. * @param {Scope} currentScope
  71. * @param {TScopeIdentifiersTraverserCallback<IScopeIdentifiersTraverserCallbackData>} callback
  72. */
  73. private traverseScopeIdentifiersRecursive (
  74. rootScope: eslintScope.Scope,
  75. currentScope: eslintScope.Scope,
  76. callback: TScopeIdentifiersTraverserCallback<IScopeIdentifiersTraverserCallbackData>
  77. ): void {
  78. const variableScope: eslintScope.Scope = currentScope.variableScope;
  79. const variableLexicalScopeNode: TNodeWithLexicalScope | null = NodeGuards.isNodeWithBlockLexicalScope(variableScope.block)
  80. ? variableScope.block
  81. : null;
  82. const isGlobalDeclaration: boolean = ScopeIdentifiersTraverser.globalScopeNames.includes(variableScope.type);
  83. if (!variableLexicalScopeNode) {
  84. return;
  85. }
  86. for (const variable of currentScope.variables) {
  87. if (variable.name === ScopeIdentifiersTraverser.argumentsVariableName) {
  88. continue;
  89. }
  90. const isBubblingDeclaration: boolean = variable
  91. .identifiers
  92. .some((identifier: ESTree.Node) =>
  93. identifier.parentNode
  94. && NodeGuards.isPropertyNode(identifier.parentNode)
  95. && identifier.parentNode.shorthand
  96. );
  97. callback({
  98. isGlobalDeclaration,
  99. isBubblingDeclaration,
  100. rootScope,
  101. variable,
  102. variableScope,
  103. variableLexicalScopeNode
  104. });
  105. }
  106. for (const childScope of currentScope.childScopes) {
  107. this.traverseScopeIdentifiersRecursive(rootScope, childScope, callback);
  108. }
  109. }
  110. /**
  111. * @param {Scope} rootScope
  112. * @param {Scope} currentScope
  113. * @param {TScopeIdentifiersTraverserCallback<IScopeThroughIdentifiersTraverserCallbackData>} callback
  114. */
  115. private traverseScopeThroughIdentifiersRecursive (
  116. rootScope: eslintScope.Scope,
  117. currentScope: eslintScope.Scope,
  118. callback: TScopeIdentifiersTraverserCallback<IScopeThroughIdentifiersTraverserCallbackData>
  119. ): void {
  120. const variableScope: eslintScope.Scope = currentScope.variableScope;
  121. const variableLexicalScopeNode: TNodeWithLexicalScope | null = NodeGuards.isNodeWithBlockLexicalScope(variableScope.block)
  122. ? variableScope.block
  123. : null;
  124. if (!variableLexicalScopeNode) {
  125. return;
  126. }
  127. for (const reference of currentScope.through) {
  128. callback({
  129. reference,
  130. variableLexicalScopeNode
  131. });
  132. }
  133. for (const childScope of currentScope.childScopes) {
  134. this.traverseScopeThroughIdentifiersRecursive(rootScope, childScope, callback);
  135. }
  136. }
  137. }