smartui-migration-tool
Version:
Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI
874 lines • 28.3 kB
JavaScript
"use strict";
/**
* Multi-Language AST Parser Framework
* Phase 1: Advanced AST Parser Infrastructure
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiLanguageASTParser = void 0;
const fs_1 = require("fs");
const babel = __importStar(require("@babel/parser"));
const babelTraverse = __importStar(require("@babel/traverse"));
class MultiLanguageASTParser {
constructor() {
this.parsers = new Map();
this.initializeParsers();
}
initializeParsers() {
// JavaScript/TypeScript Parser (Babel)
this.parsers.set('javascript', new BabelASTParser('javascript'));
this.parsers.set('typescript', new BabelASTParser('typescript'));
// Python Parser (Custom implementation)
this.parsers.set('python', new PythonASTParser());
// Java Parser (Custom implementation)
this.parsers.set('java', new JavaASTParser());
// C# Parser (Custom implementation)
this.parsers.set('csharp', new CSharpASTParser());
}
async parseFile(filePath, config) {
try {
const code = await fs_1.promises.readFile(filePath, 'utf-8');
return this.parse(code, config);
}
catch (error) {
return {
success: false,
errors: [{
message: `Failed to read file: ${error instanceof Error ? error.message : 'Unknown error'}`,
code: 'FILE_READ_ERROR',
position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
severity: 'error'
}],
warnings: [],
metadata: {
language: config.language,
framework: config.framework,
platform: config.platform,
complexity: 0,
nodeCount: 0,
importCount: 0,
functionCount: 0,
classCount: 0,
testCount: 0,
visualTestCount: 0,
parseTime: 0,
memoryUsage: 0
}
};
}
}
parse(code, config) {
const startTime = Date.now();
const startMemory = process.memoryUsage().heapUsed;
try {
const parser = this.parsers.get(config.language);
if (!parser) {
throw new Error(`Unsupported language: ${config.language}`);
}
const result = parser.parse(code, config);
const endTime = Date.now();
const endMemory = process.memoryUsage().heapUsed;
result.metadata.parseTime = endTime - startTime;
result.metadata.memoryUsage = endMemory - startMemory;
return result;
}
catch (error) {
const endTime = Date.now();
return {
success: false,
errors: [{
message: `Parse error: ${error instanceof Error ? error.message : 'Unknown error'}`,
code: 'PARSE_ERROR',
position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
severity: 'error'
}],
warnings: [],
metadata: {
language: config.language,
framework: config.framework,
platform: config.platform,
complexity: 0,
nodeCount: 0,
importCount: 0,
functionCount: 0,
classCount: 0,
testCount: 0,
visualTestCount: 0,
parseTime: endTime - startTime,
memoryUsage: 0
}
};
}
}
analyze(ast) {
const parser = this.parsers.get(ast.language);
if (!parser) {
throw new Error(`Unsupported language: ${ast.language}`);
}
return parser.analyze(ast);
}
transform(ast, transformations) {
const parser = this.parsers.get(ast.language);
if (!parser) {
throw new Error(`Unsupported language: ${ast.language}`);
}
return parser.transform(ast, transformations);
}
generate(ast) {
const parser = this.parsers.get(ast.language);
if (!parser) {
throw new Error(`Unsupported language: ${ast.language}`);
}
return parser.generate(ast);
}
}
exports.MultiLanguageASTParser = MultiLanguageASTParser;
/**
* Babel-based AST Parser for JavaScript/TypeScript
*/
class BabelASTParser {
constructor(language) {
this.language = language;
}
parse(code, config) {
try {
const babelConfig = {
sourceType: config.sourceType || 'module',
allowImportExportEverywhere: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,
allowUndeclaredExports: true,
plugins: this.getBabelPlugins(config),
tokens: true,
ranges: true,
createParenthesizedExpressions: true,
errorRecovery: true,
strictMode: config.strictMode || false,
attachComment: config.includeComments || false
};
const ast = babel.parse(code, babelConfig);
const universalAST = this.convertBabelToUniversal(ast, code, config);
return {
success: true,
ast: universalAST,
errors: [],
warnings: [],
metadata: this.calculateMetadata(universalAST, config)
};
}
catch (error) {
return {
success: false,
errors: [{
message: `Babel parse error: ${error instanceof Error ? error.message : 'Unknown error'}`,
code: 'BABEL_PARSE_ERROR',
position: this.extractErrorPosition(error),
severity: 'error'
}],
warnings: [],
metadata: this.getEmptyMetadata(config)
};
}
}
getBabelPlugins(config) {
const plugins = [
'jsx',
'decorators-legacy',
'classProperties',
'objectRestSpread',
'functionBind',
'exportDefaultFrom',
'exportNamespaceFrom',
'dynamicImport',
'nullishCoalescingOperator',
'optionalChaining',
'asyncGenerators',
'functionSent',
'throwExpressions',
'topLevelAwait'
];
if (config.language === 'typescript') {
plugins.push('typescript');
}
if (config.experimentalFeatures) {
plugins.push('pipelineOperator', 'doExpressions', 'partialApplication');
}
return plugins;
}
convertBabelToUniversal(babelAST, code, config) {
const universalAST = {
id: 'root',
type: 'program',
language: this.language,
framework: config.framework,
platform: config.platform,
position: this.extractPosition(babelAST.loc),
metadata: this.createMetadata(babelAST, config),
children: [],
raw: code
};
this.traverseBabelAST(babelAST, universalAST, code);
return universalAST;
}
traverseBabelAST(babelNode, universalNode, code) {
if (!babelNode || typeof babelNode !== 'object')
return;
babelTraverse.default(babelNode, {
enter: (path) => {
const node = path.node;
const universalChild = this.convertBabelNode(node, code, universalNode);
if (universalChild) {
universalNode.children?.push(universalChild);
}
}
});
}
convertBabelNode(babelNode, code, parent) {
const nodeType = this.mapBabelNodeType(babelNode.type);
if (!nodeType)
return null;
return {
id: this.generateNodeId(babelNode),
type: nodeType,
language: this.language,
position: this.extractPosition(babelNode.loc),
metadata: this.createNodeMetadata(babelNode),
parent,
raw: this.extractRawCode(babelNode, code),
children: []
};
}
mapBabelNodeType(babelType) {
const typeMap = {
'ImportDeclaration': 'import',
'ExportNamedDeclaration': 'export',
'ExportDefaultDeclaration': 'export',
'ExportAllDeclaration': 'export',
'ClassDeclaration': 'class',
'FunctionDeclaration': 'function',
'ArrowFunctionExpression': 'arrow-function',
'MethodDefinition': 'method',
'VariableDeclaration': 'variable',
'VariableDeclarator': 'variable',
'Decorator': 'decorator',
'TSInterfaceDeclaration': 'interface',
'TSTypeAliasDeclaration': 'type',
'TSEnumDeclaration': 'enum',
'TSModuleDeclaration': 'namespace',
'ExpressionStatement': 'statement',
'CallExpression': 'call',
'MemberExpression': 'member',
'AssignmentExpression': 'assignment',
'ConditionalExpression': 'conditional',
'ForStatement': 'loop',
'WhileStatement': 'loop',
'DoWhileStatement': 'loop',
'ForInStatement': 'loop',
'ForOfStatement': 'loop',
'TryStatement': 'try-catch',
'JSXElement': 'jsx-element',
'TemplateLiteral': 'template',
'StringLiteral': 'string',
'NumericLiteral': 'number',
'BooleanLiteral': 'boolean',
'ArrayExpression': 'array',
'ObjectExpression': 'object',
'AsyncFunctionExpression': 'async-function',
'GeneratorFunction': 'generator',
'ObjectPattern': 'destructuring',
'ArrayPattern': 'destructuring',
'SpreadElement': 'spread',
'RestElement': 'rest',
'OptionalMemberExpression': 'optional',
'NullishCoalescingExpression': 'nullish-coalescing',
'LogicalExpression': 'logical',
'BinaryExpression': 'binary',
'UnaryExpression': 'unary',
'UpdateExpression': 'update',
'NewExpression': 'new',
'ThisExpression': 'this',
'Super': 'super',
'YieldExpression': 'yield',
'AwaitExpression': 'await',
'ReturnStatement': 'return',
'ThrowStatement': 'throw',
'BreakStatement': 'break',
'ContinueStatement': 'continue',
'DebuggerStatement': 'debugger',
'WithStatement': 'with',
'SwitchStatement': 'switch',
'SwitchCase': 'case',
'LabeledStatement': 'label',
'BlockStatement': 'block',
'Program': 'program',
'File': 'file',
'Identifier': 'identifier',
'Literal': 'literal'
};
return typeMap[babelType] || 'unknown';
}
extractPosition(loc) {
if (!loc) {
return {
start: { line: 0, column: 0, offset: 0 },
end: { line: 0, column: 0, offset: 0 }
};
}
return {
start: {
line: loc.start.line,
column: loc.start.column,
offset: loc.start.index || 0
},
end: {
line: loc.end.line,
column: loc.end.column,
offset: loc.end.index || 0
}
};
}
createMetadata(node, config) {
return {
confidence: 1.0,
context: [],
dependencies: [],
frameworkHints: config.framework ? [config.framework] : [],
platformHints: config.platform ? [config.platform] : [],
complexity: 'low',
transformationPriority: 1
};
}
createNodeMetadata(node) {
return {
confidence: 0.9,
context: [],
dependencies: [],
frameworkHints: [],
platformHints: [],
complexity: 'low',
transformationPriority: 1
};
}
generateNodeId(node) {
return `${node.type}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
extractRawCode(node, code) {
if (node.start !== undefined && node.end !== undefined) {
return code.slice(node.start, node.end);
}
return '';
}
extractErrorPosition(error) {
if (error.loc) {
return this.extractPosition(error.loc);
}
return {
start: { line: 0, column: 0, offset: 0 },
end: { line: 0, column: 0, offset: 0 }
};
}
calculateMetadata(ast, config) {
const analysis = this.analyze(ast);
return {
language: config.language,
framework: config.framework,
platform: config.platform,
complexity: analysis.complexity,
nodeCount: analysis.nodes.length,
importCount: analysis.imports.length,
functionCount: analysis.functions.length,
classCount: analysis.classes.length,
testCount: analysis.testFiles.length,
visualTestCount: analysis.visualTests.length,
parseTime: 0,
memoryUsage: 0
};
}
getEmptyMetadata(config) {
return {
language: config.language,
framework: config.framework,
platform: config.platform,
complexity: 0,
nodeCount: 0,
importCount: 0,
functionCount: 0,
classCount: 0,
testCount: 0,
visualTestCount: 0,
parseTime: 0,
memoryUsage: 0
};
}
analyze(ast) {
const nodes = [];
const imports = [];
const exports = [];
const functions = [];
const classes = [];
const variables = [];
const decorators = [];
const annotations = [];
const visualTests = [];
const testFiles = [];
const dependencies = [];
const frameworks = [];
const platforms = [];
this.traverseUniversalAST(ast, (node) => {
nodes.push(node);
switch (node.type) {
case 'import':
imports.push(node);
break;
case 'export':
exports.push(node);
break;
case 'function':
case 'arrow-function':
case 'async-function':
case 'generator':
functions.push(node);
break;
case 'class':
classes.push(node);
break;
case 'variable':
variables.push(node);
break;
case 'decorator':
decorators.push(node);
break;
case 'annotation':
annotations.push(node);
break;
}
// Detect visual testing patterns
if (this.isVisualTestNode(node)) {
visualTests.push(node);
}
// Detect test files
if (this.isTestFile(node)) {
testFiles.push(node);
}
});
return {
nodes,
imports,
exports,
functions,
classes,
variables,
decorators,
annotations,
visualTests,
testFiles,
dependencies,
frameworks,
platforms,
complexity: this.calculateComplexity(nodes),
maintainability: this.calculateMaintainability(nodes),
testability: this.calculateTestability(nodes)
};
}
traverseUniversalAST(ast, callback) {
callback(ast);
if (ast.children) {
ast.children.forEach(child => this.traverseUniversalAST(child, callback));
}
}
isVisualTestNode(node) {
const visualTestPatterns = [
/percy/i,
/applitools/i,
/eyes/i,
/sauce/i,
/visual/i,
/screenshot/i,
/snapshot/i
];
return visualTestPatterns.some(pattern => pattern.test(node.raw));
}
isTestFile(node) {
const testPatterns = [
/test/i,
/spec/i,
/\.test\./i,
/\.spec\./i,
/describe/i,
/it\(/i,
/expect\(/i
];
return testPatterns.some(pattern => pattern.test(node.raw));
}
calculateComplexity(nodes) {
let complexity = 0;
nodes.forEach(node => {
switch (node.type) {
case 'loop':
case 'conditional':
case 'try-catch':
case 'switch':
complexity += 2;
break;
case 'function':
case 'class':
complexity += 1;
break;
}
});
return complexity;
}
calculateMaintainability(nodes) {
const totalNodes = nodes.length;
const complexNodes = nodes.filter(node => ['loop', 'conditional', 'try-catch', 'switch'].includes(node.type)).length;
return Math.max(0, 100 - (complexNodes / totalNodes) * 100);
}
calculateTestability(nodes) {
const functions = nodes.filter(node => ['function', 'arrow-function', 'async-function', 'generator'].includes(node.type)).length;
const testFiles = nodes.filter(node => this.isTestFile(node)).length;
return Math.min(100, (testFiles / Math.max(1, functions)) * 100);
}
transform(ast, transformations) {
// Implementation for AST transformations
return ast;
}
generate(ast) {
// Implementation for code generation
return ast.raw;
}
validate(code) {
try {
const babelConfig = {
sourceType: 'module',
allowImportExportEverywhere: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,
allowUndeclaredExports: true,
plugins: this.getBabelPlugins({
language: this.language,
includeComments: true,
includeWhitespace: false,
strictMode: false,
experimentalFeatures: true,
sourceType: 'module'
}),
errorRecovery: false
};
babel.parse(code, babelConfig);
return true;
}
catch (error) {
return false;
}
}
}
/**
* Python AST Parser (Custom Implementation)
*/
class PythonASTParser {
constructor() {
this.language = 'python';
}
parse(code, config) {
// Python AST parsing implementation
return {
success: true,
ast: this.createUniversalAST(code, config),
errors: [],
warnings: [],
metadata: this.getEmptyMetadata(config)
};
}
analyze(ast) {
return this.getEmptyAnalysis();
}
transform(ast, transformations) {
return ast;
}
generate(ast) {
return ast.raw;
}
validate(code) {
// Simple validation for Python code
try {
// Basic Python syntax validation
return code.trim().length > 0 && !code.includes('SyntaxError');
}
catch (error) {
return false;
}
}
createUniversalAST(code, config) {
return {
id: 'python_root',
type: 'program',
language: 'python',
framework: config.framework,
platform: config.platform,
position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
metadata: {
confidence: 1.0,
context: [],
dependencies: [],
frameworkHints: [],
platformHints: [],
complexity: 'low',
transformationPriority: 1
},
raw: code
};
}
getEmptyMetadata(config) {
return {
language: config.language,
framework: config.framework,
platform: config.platform,
complexity: 0,
nodeCount: 0,
importCount: 0,
functionCount: 0,
classCount: 0,
testCount: 0,
visualTestCount: 0,
parseTime: 0,
memoryUsage: 0
};
}
getEmptyAnalysis() {
return {
nodes: [],
imports: [],
exports: [],
functions: [],
classes: [],
variables: [],
decorators: [],
annotations: [],
visualTests: [],
testFiles: [],
dependencies: [],
frameworks: [],
platforms: [],
complexity: 0,
maintainability: 0,
testability: 0
};
}
}
/**
* Java AST Parser (Custom Implementation)
*/
class JavaASTParser {
constructor() {
this.language = 'java';
}
parse(code, config) {
// Java AST parsing implementation
return {
success: true,
ast: this.createUniversalAST(code, config),
errors: [],
warnings: [],
metadata: this.getEmptyMetadata(config)
};
}
analyze(ast) {
return this.getEmptyAnalysis();
}
transform(ast, transformations) {
return ast;
}
generate(ast) {
return ast.raw;
}
validate(code) {
// Simple validation for Java code
try {
return code.trim().length > 0 && code.includes('class') && !code.includes('SyntaxError');
}
catch (error) {
return false;
}
}
createUniversalAST(code, config) {
return {
id: 'java_root',
type: 'program',
language: 'java',
framework: config.framework,
platform: config.platform,
position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
metadata: {
confidence: 1.0,
context: [],
dependencies: [],
frameworkHints: [],
platformHints: [],
complexity: 'low',
transformationPriority: 1
},
raw: code
};
}
getEmptyMetadata(config) {
return {
language: config.language,
framework: config.framework,
platform: config.platform,
complexity: 0,
nodeCount: 0,
importCount: 0,
functionCount: 0,
classCount: 0,
testCount: 0,
visualTestCount: 0,
parseTime: 0,
memoryUsage: 0
};
}
getEmptyAnalysis() {
return {
nodes: [],
imports: [],
exports: [],
functions: [],
classes: [],
variables: [],
decorators: [],
annotations: [],
visualTests: [],
testFiles: [],
dependencies: [],
frameworks: [],
platforms: [],
complexity: 0,
maintainability: 0,
testability: 0
};
}
}
/**
* C# AST Parser (Custom Implementation)
*/
class CSharpASTParser {
constructor() {
this.language = 'csharp';
}
parse(code, config) {
// C# AST parsing implementation
return {
success: true,
ast: this.createUniversalAST(code, config),
errors: [],
warnings: [],
metadata: this.getEmptyMetadata(config)
};
}
analyze(ast) {
return this.getEmptyAnalysis();
}
transform(ast, transformations) {
return ast;
}
generate(ast) {
return ast.raw;
}
validate(code) {
// Simple validation for C# code
try {
return code.trim().length > 0 && code.includes('using') && !code.includes('SyntaxError');
}
catch (error) {
return false;
}
}
createUniversalAST(code, config) {
return {
id: 'csharp_root',
type: 'program',
language: 'csharp',
framework: config.framework,
platform: config.platform,
position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
metadata: {
confidence: 1.0,
context: [],
dependencies: [],
frameworkHints: [],
platformHints: [],
complexity: 'low',
transformationPriority: 1
},
raw: code
};
}
getEmptyMetadata(config) {
return {
language: config.language,
framework: config.framework,
platform: config.platform,
complexity: 0,
nodeCount: 0,
importCount: 0,
functionCount: 0,
classCount: 0,
testCount: 0,
visualTestCount: 0,
parseTime: 0,
memoryUsage: 0
};
}
getEmptyAnalysis() {
return {
nodes: [],
imports: [],
exports: [],
functions: [],
classes: [],
variables: [],
decorators: [],
annotations: [],
visualTests: [],
testFiles: [],
dependencies: [],
frameworks: [],
platforms: [],
complexity: 0,
maintainability: 0,
testability: 0
};
}
}
//# sourceMappingURL=ASTParser.js.map