mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Merge branch 'master' into fix-issue-273
This commit is contained in:
@@ -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
|
||||
));
|
||||
});
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export enum PropertiesExtractor {
|
||||
AssignmentExpressionPropertiesExtractor = 'AssignmentExpressionPropertiesExtractor',
|
||||
VariableDeclaratorPropertiesExtractor = 'VariableDeclaratorPropertiesExtractor'
|
||||
}
|
||||
+13
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+227
@@ -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;
|
||||
}
|
||||
}
|
||||
+45
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
+118
@@ -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;
|
||||
Reference in New Issue
Block a user