Merge branch 'master' into fix-issue-273

This commit is contained in:
Leo Baschy
2018-06-05 09:26:26 -07:00
22 changed files with 691 additions and 1938 deletions
+2
View File
@@ -11,6 +11,7 @@ export enum ServiceIdentifiers {
Factory__INodeTransformer = 'Factory<INodeTransformer[]>',
Factory__IObfuscationResult = 'Factory<IObfuscationResult>',
Factory__IObfuscatingReplacer = 'Factory<IObfuscatingReplacer>',
Factory__IPropertiesExtractor = 'Factory<IPropertiesExtractor>',
Factory__TControlFlowStorage = 'Factory<TControlFlowStorage>',
IArrayUtils = 'IArrayUtils',
ICalleeDataExtractor = 'ICalleeDataExtractor',
@@ -30,6 +31,7 @@ export enum ServiceIdentifiers {
IOptions = 'IOptions',
IOptionsNormalizer = 'IOptionsNormalizer',
IObfuscatingReplacer = 'IObfuscatingReplacer',
IPropertiesExtractor = 'IPropertiesExtractor',
IRandomGenerator = 'IRandomGenerator',
ISourceCode = 'ISourceCode',
ISourceMapCorrector = 'ISourceMapCorrector',
@@ -30,7 +30,7 @@ export const analyzersModule: interfaces.ContainerModule = new ContainerModule((
.to(ObjectExpressionCalleeDataExtractor)
.whenTargetNamed(CalleeDataExtractor.ObjectExpressionCalleeDataExtractor);
// node transformers factory
// callee data extractor factory
bind<ICalleeDataExtractor>(ServiceIdentifiers.Factory__ICalleeDataExtractor)
.toFactory<ICalleeDataExtractor>(InversifyContainerFacade
.getCacheFactory<CalleeDataExtractor, ICalleeDataExtractor>(
@@ -1,15 +1,20 @@
import { ContainerModule, interfaces } from 'inversify';
import { InversifyContainerFacade } from '../../InversifyContainerFacade';
import { ServiceIdentifiers } from '../../ServiceIdentifiers';
import { INodeTransformer } from '../../../interfaces/node-transformers/INodeTransformer';
import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
import { PropertiesExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
import { AssignmentExpressionPropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/AssignmentExpressionPropertiesExtractor';
import { MemberExpressionTransformer } from '../../../node-transformers/converting-transformers/MemberExpressionTransformer';
import { MethodDefinitionTransformer } from '../../../node-transformers/converting-transformers/MethodDefinitionTransformer';
import { ObjectExpressionKeysTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionKeysTransformer';
import { ObjectExpressionTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionTransformer';
import { TemplateLiteralTransformer } from '../../../node-transformers/converting-transformers/TemplateLiteralTransformer';
import { VariableDeclaratorPropertiesExtractor } from '../../../node-transformers/converting-transformers/properties-extractors/VariableDeclaratorPropertiesExtractor';
export const convertingTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
// converting transformers
@@ -32,4 +37,20 @@ export const convertingTransformersModule: interfaces.ContainerModule = new Cont
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
.to(TemplateLiteralTransformer)
.whenTargetNamed(NodeTransformer.TemplateLiteralTransformer);
// properties extractors
bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
.to(AssignmentExpressionPropertiesExtractor)
.whenTargetNamed(PropertiesExtractor.AssignmentExpressionPropertiesExtractor);
bind<IPropertiesExtractor>(ServiceIdentifiers.IPropertiesExtractor)
.to(VariableDeclaratorPropertiesExtractor)
.whenTargetNamed(PropertiesExtractor.VariableDeclaratorPropertiesExtractor);
// properties extractor factory
bind<IPropertiesExtractor>(ServiceIdentifiers.Factory__IPropertiesExtractor)
.toFactory<IPropertiesExtractor>(InversifyContainerFacade
.getCacheFactory<PropertiesExtractor, IPropertiesExtractor>(
ServiceIdentifiers.IPropertiesExtractor
));
});
@@ -0,0 +1,4 @@
export enum PropertiesExtractor {
AssignmentExpressionPropertiesExtractor = 'AssignmentExpressionPropertiesExtractor',
VariableDeclaratorPropertiesExtractor = 'VariableDeclaratorPropertiesExtractor'
}
@@ -0,0 +1,13 @@
import * as ESTree from 'estree';
export interface IPropertiesExtractor {
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Node} hostNode
* @returns {Node}
*/
extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.Node
): ESTree.Node;
}
@@ -3,74 +3,49 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { TNodeWithScope } from '../../types/node/TNodeWithScope';
import { TPropertiesExtractorFactory } from '../../types/container/node-transformers/TPropertiesExtractorFactory';
import { IOptions } from '../../interfaces/options/IOptions';
import { IPropertiesExtractor } from '../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
import { NodeType } from '../../enums/node/NodeType';
import { PropertiesExtractor } from '../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
import { TransformationStage } from '../../enums/node-transformers/TransformationStage';
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeAppender } from '../../node/NodeAppender';
import { NodeFactory } from '../../node/NodeFactory';
import { NodeGuards } from '../../node/NodeGuards';
import { NodeUtils } from '../../node/NodeUtils';
@injectable()
export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
/**
* @type {Map<ESTree.ObjectExpression, TNodeWithScope>}
* @type {Map<string, PropertiesExtractor>}
*/
private readonly cachedHostScopesMap: Map <ESTree.ObjectExpression, TNodeWithScope> = new Map();
private static readonly propertiesExtractorsMap: Map <string, PropertiesExtractor> = new Map([
[NodeType.AssignmentExpression, PropertiesExtractor.AssignmentExpressionPropertiesExtractor],
[NodeType.VariableDeclarator, PropertiesExtractor.VariableDeclaratorPropertiesExtractor]
]);
/**
* @type {Map<ESTree.ObjectExpression, ESTree.Statement>}
* @type {TPropertiesExtractorFactory}
*/
private readonly cachedHostStatementsMap: Map <ESTree.ObjectExpression, ESTree.Statement> = new Map();
private readonly propertiesExtractorFactory: TPropertiesExtractorFactory;
/**
* @param {TPropertiesExtractorFactory} propertiesExtractorFactory
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.Factory__IPropertiesExtractor)
propertiesExtractorFactory: TPropertiesExtractorFactory,
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
}
/**
* @param {Property} propertyNode
* @returns {string | null}
*/
private static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
if (!propertyNode.key) {
return null;
}
const propertyKeyNode: ESTree.Expression = propertyNode.key;
if (NodeGuards.isLiteralNode(propertyKeyNode) && typeof propertyKeyNode.value === 'string') {
return propertyKeyNode.value;
}
if (NodeGuards.isIdentifierNode(propertyKeyNode)) {
return propertyKeyNode.name;
}
return null;
}
/**
* @param {Node} node
* @returns {propertyValueNode is Pattern}
*/
private static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
return NodeGuards.isObjectPatternNode(node)
|| NodeGuards.isArrayPatternNode(node)
|| NodeGuards.isAssignmentPatternNode(node)
|| NodeGuards.isRestElementNode(node);
this.propertiesExtractorFactory = propertiesExtractorFactory;
}
/**
@@ -116,180 +91,16 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
return objectExpressionNode;
}
if (NodeGuards.isVariableDeclaratorNode(parentNode)) {
return this.transformWithVariableDeclaratorHost(objectExpressionNode, parentNode);
}
const propertiesExtractorName: PropertiesExtractor | undefined = ObjectExpressionKeysTransformer
.propertiesExtractorsMap
.get(parentNode.type);
if (NodeGuards.isAssignmentExpressionNode(parentNode)) {
return this.transformWithAssignmentExpressionHost(objectExpressionNode, parentNode);
}
return objectExpressionNode;
}
/**
* @param {Property[]} properties
* @param {Expression} memberExpressionHostNode
* @returns {[ExpressionStatement[] , number[]]}
*/
private extractPropertiesToExpressionStatements (
properties: ESTree.Property[],
memberExpressionHostNode: ESTree.Expression
): [ESTree.ExpressionStatement[], number[]] {
const propertiesLength: number = properties.length;
const expressionStatements: ESTree.ExpressionStatement[] = [];
const removablePropertyIds: number[] = [];
for (let i: number = 0; i < propertiesLength; i++) {
const property: ESTree.Property = properties[i];
const propertyValue: ESTree.Expression | ESTree.Pattern = property.value;
// invalid property nodes
if (ObjectExpressionKeysTransformer.isProhibitedPattern(propertyValue)) {
continue;
}
/**
* Stage 1: extract property node key names
*/
const propertyKeyName: string | null = ObjectExpressionKeysTransformer.getPropertyNodeKeyName(property);
if (!propertyKeyName) {
continue;
}
/**
* Stage 2: creating new expression statement node with member expression based on removed property
*/
const shouldCreateLiteralNode: boolean = !property.computed
|| (property.computed && !!property.key && NodeGuards.isLiteralNode(property.key));
const memberExpressionProperty: ESTree.Expression = shouldCreateLiteralNode
? NodeFactory.literalNode(propertyKeyName)
: NodeFactory.identifierNode(propertyKeyName);
const memberExpressionNode: ESTree.MemberExpression = NodeFactory
.memberExpressionNode(memberExpressionHostNode, memberExpressionProperty, true);
const expressionStatementNode: ESTree.ExpressionStatement = NodeFactory.expressionStatementNode(
NodeFactory.assignmentExpressionNode('=', memberExpressionNode, propertyValue)
);
/**
* Stage 3: recursively processing nested object expressions
*/
if (NodeGuards.isObjectExpressionNode(property.value)) {
this.transformObjectExpressionNode(property.value, memberExpressionNode);
}
/**
* Stage 4: filling arrays
*/
expressionStatements.push(expressionStatementNode);
removablePropertyIds.push(i);
}
return [expressionStatements, removablePropertyIds];
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {TNodeWithScope}
*/
private getHostScopeNode (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): TNodeWithScope {
if (this.cachedHostScopesMap.has(objectExpressionNode)) {
return <TNodeWithScope>this.cachedHostScopesMap.get(objectExpressionNode);
}
const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(hostStatement);
this.cachedHostScopesMap.set(objectExpressionNode, scopeNode);
return scopeNode;
}
/**
* Returns host statement of object expression node
*
* @param {NodeGuards} objectExpressionNode
* @returns {Node}
*/
private getHostStatement (objectExpressionNode: ESTree.ObjectExpression): ESTree.Statement {
if (this.cachedHostStatementsMap.has(objectExpressionNode)) {
return <ESTree.Statement>this.cachedHostStatementsMap.get(objectExpressionNode);
}
const hostStatement: ESTree.Statement = NodeUtils.getRootStatementOfNode(objectExpressionNode);
this.cachedHostStatementsMap.set(objectExpressionNode, hostStatement);
return hostStatement;
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {VariableDeclarator} hostNode
* @returns {Node}
*/
private transformWithVariableDeclaratorHost (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.VariableDeclarator
): ESTree.Node {
// should pass only Expression nodes as MemberExpression.object value
if (!NodeGuards.isIdentifierNode(hostNode.id)) {
if (!propertiesExtractorName) {
return objectExpressionNode;
}
return this.transformObjectExpressionNode(
objectExpressionNode,
hostNode.id
);
}
const propertiesExtractor: IPropertiesExtractor = this.propertiesExtractorFactory(propertiesExtractorName);
/**
* @param {ObjectExpression} objectExpressionNode
* @param {AssignmentExpression} hostNode
* @returns {Node}
*/
private transformWithAssignmentExpressionHost (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.AssignmentExpression
): ESTree.Node {
const leftNode: ESTree.MemberExpression | ESTree.Pattern = hostNode.left;
// left node shouldn't be as Pattern node
if (ObjectExpressionKeysTransformer.isProhibitedPattern(leftNode)) {
return objectExpressionNode;
}
return this.transformObjectExpressionNode(
objectExpressionNode,
leftNode
);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Expression} memberExpressionHostNode
* @returns {Node}
*/
private transformObjectExpressionNode (
objectExpressionNode: ESTree.ObjectExpression,
memberExpressionHostNode: ESTree.Expression
): ESTree.Node {
const properties: ESTree.Property[] = objectExpressionNode.properties;
const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
.extractPropertiesToExpressionStatements(properties, memberExpressionHostNode);
const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
const scopeNode: TNodeWithScope = this.getHostScopeNode(objectExpressionNode, hostStatement);
objectExpressionNode.properties = properties.filter((property: ESTree.Property, index: number) =>
!removablePropertyIds.includes(index)
);
NodeAppender.insertAfter(scopeNode, expressionStatements, hostStatement);
return objectExpressionNode;
return propertiesExtractor.extract(objectExpressionNode, parentNode);
}
}
@@ -0,0 +1,227 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { TNodeWithScope } from '../../../types/node/TNodeWithScope';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { NodeAppender } from '../../../node/NodeAppender';
import { NodeFactory } from '../../../node/NodeFactory';
import { NodeGuards } from '../../../node/NodeGuards';
import { NodeUtils } from '../../../node/NodeUtils';
@injectable()
export abstract class AbstractPropertiesExtractor implements IPropertiesExtractor {
/**
* @type {Map<ESTree.ObjectExpression, TNodeWithScope>}
*/
protected readonly cachedHostScopesMap: Map <ESTree.ObjectExpression, TNodeWithScope> = new Map();
/**
* @type {Map<ESTree.ObjectExpression, ESTree.Statement>}
*/
protected readonly cachedHostStatementsMap: Map <ESTree.ObjectExpression, ESTree.Statement> = new Map();
/**
* @type {IOptions}
*/
protected readonly options: IOptions;
/**
* @type {IRandomGenerator}
*/
protected readonly randomGenerator: IRandomGenerator;
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
this.randomGenerator = randomGenerator;
this.options = options;
}
/**
* @param {Property} propertyNode
* @returns {string | null}
*/
protected static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
if (!propertyNode.key) {
return null;
}
const propertyKeyNode: ESTree.Expression = propertyNode.key;
if (NodeGuards.isLiteralNode(propertyKeyNode) && typeof propertyKeyNode.value === 'string') {
return propertyKeyNode.value;
}
if (NodeGuards.isIdentifierNode(propertyKeyNode)) {
return propertyKeyNode.name;
}
return null;
}
/**
* @param {Node} node
* @returns {propertyValueNode is Pattern}
*/
protected static isProhibitedPattern (node: ESTree.Node): node is ESTree.Pattern {
return NodeGuards.isObjectPatternNode(node)
|| NodeGuards.isArrayPatternNode(node)
|| NodeGuards.isAssignmentPatternNode(node)
|| NodeGuards.isRestElementNode(node);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Node} hostNode
* @returns {Node}
*/
public abstract extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.Node
): ESTree.Node;
/**
* @param {Property[]} properties
* @param {Expression} memberExpressionHostNode
* @returns {[ExpressionStatement[] , number[]]}
*/
protected extractPropertiesToExpressionStatements (
properties: ESTree.Property[],
memberExpressionHostNode: ESTree.Expression
): [ESTree.ExpressionStatement[], number[]] {
const propertiesLength: number = properties.length;
const expressionStatements: ESTree.ExpressionStatement[] = [];
const removablePropertyIds: number[] = [];
for (let i: number = 0; i < propertiesLength; i++) {
const property: ESTree.Property = properties[i];
const propertyValue: ESTree.Expression | ESTree.Pattern = property.value;
// invalid property nodes
if (AbstractPropertiesExtractor.isProhibitedPattern(propertyValue)) {
continue;
}
/**
* Stage 1: extract property node key names
*/
const propertyKeyName: string | null = AbstractPropertiesExtractor.getPropertyNodeKeyName(property);
if (!propertyKeyName) {
continue;
}
/**
* Stage 2: creating new expression statement node with member expression based on removed property
*/
const shouldCreateLiteralNode: boolean = !property.computed
|| (property.computed && !!property.key && NodeGuards.isLiteralNode(property.key));
const memberExpressionProperty: ESTree.Expression = shouldCreateLiteralNode
? NodeFactory.literalNode(propertyKeyName)
: NodeFactory.identifierNode(propertyKeyName);
const memberExpressionNode: ESTree.MemberExpression = NodeFactory
.memberExpressionNode(memberExpressionHostNode, memberExpressionProperty, true);
const expressionStatementNode: ESTree.ExpressionStatement = NodeFactory.expressionStatementNode(
NodeFactory.assignmentExpressionNode('=', memberExpressionNode, propertyValue)
);
/**
* Stage 3: recursively processing nested object expressions
*/
if (NodeGuards.isObjectExpressionNode(property.value)) {
this.transformObjectExpressionNode(property.value, memberExpressionNode);
}
/**
* Stage 4: filling arrays
*/
expressionStatements.push(expressionStatementNode);
removablePropertyIds.push(i);
}
return [expressionStatements, removablePropertyIds];
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {number[]} removablePropertyIds
*/
protected filterExtractedObjectExpressionProperties (
objectExpressionNode: ESTree.ObjectExpression,
removablePropertyIds: number[]
): void {
objectExpressionNode.properties = objectExpressionNode.properties
.filter((property: ESTree.Property, index: number) => !removablePropertyIds.includes(index));
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Expression} memberExpressionHostNode
* @returns {Node}
*/
protected transformObjectExpressionNode (
objectExpressionNode: ESTree.ObjectExpression,
memberExpressionHostNode: ESTree.Expression
): ESTree.Node {
const properties: ESTree.Property[] = objectExpressionNode.properties;
const [expressionStatements, removablePropertyIds]: [ESTree.ExpressionStatement[], number[]] = this
.extractPropertiesToExpressionStatements(properties, memberExpressionHostNode);
const hostStatement: ESTree.Statement = this.getHostStatement(objectExpressionNode);
const scopeNode: TNodeWithScope = this.getHostScopeNode(objectExpressionNode, hostStatement);
this.filterExtractedObjectExpressionProperties(objectExpressionNode, removablePropertyIds);
NodeAppender.insertAfter(scopeNode, expressionStatements, hostStatement);
return objectExpressionNode;
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Statement} hostStatement
* @returns {TNodeWithScope}
*/
private getHostScopeNode (
objectExpressionNode: ESTree.ObjectExpression,
hostStatement: ESTree.Statement
): TNodeWithScope {
if (this.cachedHostScopesMap.has(objectExpressionNode)) {
return <TNodeWithScope>this.cachedHostScopesMap.get(objectExpressionNode);
}
const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(hostStatement);
this.cachedHostScopesMap.set(objectExpressionNode, scopeNode);
return scopeNode;
}
/**
* Returns host statement of object expression node
*
* @param {NodeGuards} objectExpressionNode
* @returns {Node}
*/
private getHostStatement (objectExpressionNode: ESTree.ObjectExpression): ESTree.Statement {
if (this.cachedHostStatementsMap.has(objectExpressionNode)) {
return <ESTree.Statement>this.cachedHostStatementsMap.get(objectExpressionNode);
}
const hostStatement: ESTree.Statement = NodeUtils.getRootStatementOfNode(objectExpressionNode);
this.cachedHostStatementsMap.set(objectExpressionNode, hostStatement);
return hostStatement;
}
}
@@ -0,0 +1,45 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
@injectable()
export class AssignmentExpressionPropertiesExtractor extends AbstractPropertiesExtractor {
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {AssignmentExpression} hostNode
* @returns {Node}
*/
public extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.AssignmentExpression
): ESTree.Node {
const leftNode: ESTree.MemberExpression | ESTree.Pattern = hostNode.left;
// left node shouldn't be as Pattern node
if (AbstractPropertiesExtractor.isProhibitedPattern(leftNode)) {
return objectExpressionNode;
}
return this.transformObjectExpressionNode(
objectExpressionNode,
leftNode
);
}
}
@@ -0,0 +1,118 @@
import { inject, injectable } from 'inversify';
import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
import * as estraverse from 'estraverse';
import * as ESTree from 'estree';
import { IOptions } from '../../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../../interfaces/utils/IRandomGenerator';
import { AbstractPropertiesExtractor } from './AbstractPropertiesExtractor';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
export class VariableDeclaratorPropertiesExtractor extends AbstractPropertiesExtractor {
/**
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
constructor (
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {VariableDeclarator} hostNode
* @returns {Node}
*/
public extract (
objectExpressionNode: ESTree.ObjectExpression,
hostNode: ESTree.VariableDeclarator
): ESTree.Node {
if (
!NodeGuards.isIdentifierNode(hostNode.id)
|| this.isProhibitedObjectExpressionNode(objectExpressionNode, hostNode.id)
) {
return objectExpressionNode;
}
return this.transformObjectExpressionNode(objectExpressionNode, hostNode.id);
}
/**
* @param {ObjectExpression} objectExpressionNode
* @returns {VariableDeclarator}
*/
private getHostVariableDeclaratorNode (objectExpressionNode: ESTree.ObjectExpression): ESTree.VariableDeclarator | never {
const { parentNode } = objectExpressionNode;
if (!parentNode || !NodeGuards.isVariableDeclaratorNode(parentNode)) {
throw new Error('Cannot get `VariableDeclarator` node for `ObjectExpression` node');
}
return parentNode;
}
/**
* @param {VariableDeclarator} variableDeclaratorNode
* @returns {VariableDeclaration}
*/
private getHostVariableDeclarationNode (variableDeclaratorNode: ESTree.VariableDeclarator): ESTree.VariableDeclaration | never {
const { parentNode } = variableDeclaratorNode;
if (!parentNode || !NodeGuards.isVariableDeclarationNode(parentNode)) {
throw new Error('Cannot get `VariableDeclaration` node for `VariableDeclarator` node');
}
return parentNode;
}
/**
* @param {ObjectExpression} objectExpressionNode
* @param {Identifier} memberExpressionHostNode
* @returns {boolean}
*/
private isProhibitedObjectExpressionNode (
objectExpressionNode: ESTree.ObjectExpression,
memberExpressionHostNode: ESTree.Identifier
): boolean {
const hostVariableDeclarator: ESTree.VariableDeclarator = this.getHostVariableDeclaratorNode(objectExpressionNode);
const hostVariableDeclaration: ESTree.VariableDeclaration = this.getHostVariableDeclarationNode(hostVariableDeclarator);
const { declarations } = hostVariableDeclaration;
const indexOfDeclarator: number = declarations.indexOf(hostVariableDeclarator);
const isLastDeclarator: boolean = indexOfDeclarator === (declarations.length - 1);
// avoid unnecessary checks
if (isLastDeclarator) {
return false;
}
const declaratorsAfterCurrentDeclarator: ESTree.VariableDeclarator[] = declarations.slice(indexOfDeclarator);
let isProhibitedObjectExpressionNode: boolean = false;
// should mark node as prohibited if that node using inside other variable declarators
declaratorsAfterCurrentDeclarator.forEach((variableDeclarator: ESTree.VariableDeclarator) => {
estraverse.traverse(variableDeclarator, {
enter: (node: ESTree.Node): estraverse.VisitorOption | ESTree.Node => {
if (
NodeGuards.isMemberExpressionNode(node)
&& NodeGuards.isIdentifierNode(node.object)
&& node.object.name === memberExpressionHostNode.name
) {
isProhibitedObjectExpressionNode = true;
return estraverse.VisitorOption.Break;
}
return node;
}
});
});
return isProhibitedObjectExpressionNode;
}
}
@@ -0,0 +1,5 @@
import { IPropertiesExtractor } from '../../../interfaces/node-transformers/converting-transformers/properties-extractors/IPropertiesExtractor';
import { PropertiesExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/PropertiesExtractor';
export type TPropertiesExtractorFactory = (propertiesExtractorName: PropertiesExtractor) => IPropertiesExtractor;