RenamePropertiesReplacer.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /* eslint-disable no-console */
  2. import { inject, injectable, } from 'inversify';
  3. import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
  4. import * as ESTree from 'estree';
  5. import { TIdentifierNamesGeneratorFactory } from '../../../types/container/generators/TIdentifierNamesGeneratorFactory';
  6. import { IIdentifierNamesGenerator } from '../../../interfaces/generators/identifier-names-generators/IIdentifierNamesGenerator';
  7. import { IRenamePropertiesReplacer } from '../../../interfaces/node-transformers/rename-properties-transformers/replacer/IRenamePropertiesReplacer';
  8. import { IOptions } from '../../../interfaces/options/IOptions';
  9. // eslint-disable-next-line import/no-internal-modules
  10. import ReservedDomProperties from './ReservedDomProperties.json';
  11. import { NodeGuards } from '../../../node/NodeGuards';
  12. import { NodeFactory } from '../../../node/NodeFactory';
  13. @injectable()
  14. export class RenamePropertiesReplacer implements IRenamePropertiesReplacer {
  15. /**
  16. * Properties list taken from `UglifyJS` and `terser`
  17. * https://github.com/mishoo/UglifyJS/blob/master/tools/domprops.json
  18. * https://github.com/terser/terser/blob/master/tools/domprops.js
  19. * Copyright 2012-2018 (c) Mihai Bazon <[email protected]>
  20. *
  21. * @type {Set<string>}
  22. */
  23. private static readonly reservedDomPropertiesList: Set<string> = new Set(ReservedDomProperties);
  24. /**
  25. * @type {IIdentifierNamesGenerator}
  26. */
  27. private readonly identifierNamesGenerator: IIdentifierNamesGenerator;
  28. /**
  29. * @type {Map<string, string>}
  30. * @private
  31. */
  32. private readonly propertyNamesMap: Map<string, string> = new Map();
  33. /**
  34. * @type {IOptions}
  35. */
  36. private readonly options: IOptions;
  37. /**
  38. * @param {TIdentifierNamesGeneratorFactory} identifierNamesGeneratorFactory
  39. * @param {IOptions} options
  40. */
  41. public constructor (
  42. @inject(ServiceIdentifiers.Factory__IIdentifierNamesGenerator)
  43. identifierNamesGeneratorFactory: TIdentifierNamesGeneratorFactory,
  44. @inject(ServiceIdentifiers.IOptions) options: IOptions
  45. ) {
  46. this.identifierNamesGenerator = identifierNamesGeneratorFactory(options);
  47. this.options = options;
  48. }
  49. /**
  50. * @param {ESTree.Identifier | ESTree.Literal} node
  51. * @returns {ESTree.Identifier | ESTree.Literal}
  52. */
  53. public replace (node: ESTree.Identifier | ESTree.Literal): ESTree.Identifier | ESTree.Literal {
  54. if (NodeGuards.isIdentifierNode(node)) {
  55. return NodeFactory.identifierNode(
  56. this.replacePropertyName(node.name)
  57. );
  58. }
  59. if (NodeGuards.isLiteralNode(node) && typeof node.value === 'string') {
  60. return NodeFactory.literalNode(
  61. this.replacePropertyName(node.value)
  62. );
  63. }
  64. return node;
  65. }
  66. /**
  67. * @param {string} propertyName
  68. * @returns {string}
  69. * @private
  70. */
  71. private replacePropertyName (propertyName: string): string {
  72. if (this.isReservedName(propertyName)) {
  73. return propertyName;
  74. }
  75. let renamedPropertyName: string | null = this.propertyNamesMap.get(propertyName) ?? null;
  76. if (renamedPropertyName !== null) {
  77. return renamedPropertyName;
  78. }
  79. renamedPropertyName = this.identifierNamesGenerator.generateNext();
  80. this.propertyNamesMap.set(propertyName, renamedPropertyName);
  81. return renamedPropertyName;
  82. }
  83. /**
  84. * @param {string} name
  85. * @returns {boolean}
  86. */
  87. private isReservedName (name: string): boolean {
  88. return this.isReservedOptionName(name)
  89. || this.isReservedDomPropertyName(name);
  90. }
  91. /**
  92. * @param {string} name
  93. * @returns {boolean}
  94. */
  95. private isReservedOptionName (name: string): boolean {
  96. if (!this.options.reservedNames.length) {
  97. return false;
  98. }
  99. return this.options.reservedNames
  100. .some((reservedName: string) => {
  101. return new RegExp(reservedName, 'g').exec(name) !== null;
  102. });
  103. }
  104. /**
  105. * @param {string} name
  106. * @returns {boolean}
  107. */
  108. private isReservedDomPropertyName (name: string): boolean {
  109. return RenamePropertiesReplacer.reservedDomPropertiesList.has(name);
  110. }
  111. }