IdentifierReplacer.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { inject, injectable, } from 'inversify';
  2. import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
  3. import * as ESTree from 'estree';
  4. import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
  5. import { TNodeWithLexicalScope } from '../../../types/node/TNodeWithLexicalScope';
  6. import { IIdentifierNamesCacheStorage } from '../../../interfaces/storages/identifier-names-cache/IIdentifierNamesCacheStorage';
  7. import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
  8. import { IIdentifierReplacer } from '../../../interfaces/node-transformers/rename-identifiers-transformers/replacer/IIdentifierReplacer';
  9. import { IOptions } from '../../../interfaces/options/IOptions';
  10. import { NodeFactory } from '../../../node/NodeFactory';
  11. @injectable()
  12. export class IdentifierReplacer implements IIdentifierReplacer {
  13. /**
  14. * @type {IIdentifierNamesCacheStorage}
  15. */
  16. private readonly identifierNamesCacheStorage: IIdentifierNamesCacheStorage;
  17. /**
  18. * @type {IIdentifierNamesGenerator}
  19. */
  20. private readonly identifierNamesGenerator: IIdentifierNamesGenerator;
  21. /**
  22. * @type {Map<TNodeWithLexicalScope, Map<string, string>>}
  23. */
  24. private readonly blockScopesMap: Map<TNodeWithLexicalScope, Map<string, string>> = new Map();
  25. /**
  26. * @type {IOptions}
  27. */
  28. private readonly options: IOptions;
  29. /**
  30. * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
  31. * @param {IIdentifierNamesCacheStorage} identifierNamesCacheStorage
  32. * @param {IOptions} options
  33. */
  34. public constructor (
  35. @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
  36. identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
  37. @inject(ServiceIdentifiers.IIdentifierNamesCacheStorage)
  38. identifierNamesCacheStorage: IIdentifierNamesCacheStorage,
  39. @inject(ServiceIdentifiers.IOptions) options: IOptions
  40. ) {
  41. this.options = options;
  42. this.identifierNamesCacheStorage = identifierNamesCacheStorage;
  43. this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
  44. }
  45. /**
  46. * Store `nodeName` of global identifiers as key in map with random name as value.
  47. * Reserved name will be ignored.
  48. *
  49. * @param {Node} identifierNode
  50. * @param {TNodeWithLexicalScope} lexicalScopeNode
  51. */
  52. public storeGlobalName (identifierNode: ESTree.Identifier, lexicalScopeNode: TNodeWithLexicalScope): void {
  53. const identifierName: string = identifierNode.name;
  54. if (this.isReservedName(identifierName)) {
  55. return;
  56. }
  57. const valueFromIdentifierNamesCache: string | null = this.identifierNamesCacheStorage.get(identifierName) ?? null;
  58. let newIdentifierName: string;
  59. if (valueFromIdentifierNamesCache) {
  60. newIdentifierName = valueFromIdentifierNamesCache;
  61. } else {
  62. newIdentifierName = this.identifierNamesGenerator.generateForGlobalScope();
  63. }
  64. if (!this.blockScopesMap.has(lexicalScopeNode)) {
  65. this.blockScopesMap.set(lexicalScopeNode, new Map());
  66. }
  67. const namesMap: Map<string, string> = <Map<string, string>>this.blockScopesMap.get(lexicalScopeNode);
  68. namesMap.set(identifierName, newIdentifierName);
  69. if (valueFromIdentifierNamesCache !== newIdentifierName) {
  70. this.identifierNamesCacheStorage.set(identifierName, newIdentifierName);
  71. }
  72. }
  73. /**
  74. * Store `nodeName` of local identifier as key in map with random name as value.
  75. * Reserved name will be ignored.
  76. *
  77. * @param {Identifier} identifierNode
  78. * @param {TNodeWithLexicalScope} lexicalScopeNode
  79. */
  80. public storeLocalName (identifierNode: ESTree.Identifier, lexicalScopeNode: TNodeWithLexicalScope): void {
  81. const identifierName: string = identifierNode.name;
  82. if (this.isReservedName(identifierName)) {
  83. return;
  84. }
  85. const newIdentifierName: string = this.identifierNamesGenerator.generateForLexicalScope(lexicalScopeNode);
  86. if (!this.blockScopesMap.has(lexicalScopeNode)) {
  87. this.blockScopesMap.set(lexicalScopeNode, new Map());
  88. }
  89. const namesMap: Map<string, string> = <Map<string, string>>this.blockScopesMap.get(lexicalScopeNode);
  90. namesMap.set(identifierName, newIdentifierName);
  91. }
  92. /**
  93. * @param {Identifier} identifierNode
  94. * @param {TNodeWithLexicalScope} lexicalScopeNode
  95. * @returns {Identifier}
  96. */
  97. public replace (identifierNode: ESTree.Identifier, lexicalScopeNode: TNodeWithLexicalScope): ESTree.Identifier {
  98. let identifierName: string = identifierNode.name;
  99. if (this.blockScopesMap.has(lexicalScopeNode)) {
  100. const namesMap: Map<string, string> = <Map<string, string>>this.blockScopesMap.get(lexicalScopeNode);
  101. if (namesMap.has(identifierName)) {
  102. identifierName = <string>namesMap.get(identifierName);
  103. }
  104. }
  105. return NodeFactory.identifierNode(identifierName);
  106. }
  107. /**
  108. * Preserve `name` to protect it from further using
  109. *
  110. * @param {Identifier} identifierNode
  111. */
  112. public preserveName (identifierNode: ESTree.Identifier): void {
  113. this.identifierNamesGenerator.preserveName(identifierNode.name);
  114. }
  115. /**
  116. * Preserve `name` to protect it from further using
  117. *
  118. * @param {Identifier} identifierNode
  119. * @param {TNodeWithLexicalScope} lexicalScopeNode
  120. */
  121. public preserveNameForLexicalScope (identifierNode: ESTree.Identifier, lexicalScopeNode: TNodeWithLexicalScope): void {
  122. this.identifierNamesGenerator.preserveNameForLexicalScope(identifierNode.name, lexicalScopeNode);
  123. }
  124. /**
  125. * @param {string} name
  126. * @returns {boolean}
  127. */
  128. private isReservedName (name: string): boolean {
  129. if (!this.options.reservedNames.length) {
  130. return false;
  131. }
  132. return this.options.reservedNames
  133. .some((reservedName: string) => {
  134. return new RegExp(reservedName, 'g').exec(name) !== null;
  135. });
  136. }
  137. }