diff --git a/libraries/analysis-javascript/lib/target/Target.ts b/libraries/analysis-javascript/lib/target/Target.ts index e8eb5bcf..4bee4b91 100644 --- a/libraries/analysis-javascript/lib/target/Target.ts +++ b/libraries/analysis-javascript/lib/target/Target.ts @@ -15,85 +15,103 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - SubTarget as CoreSubTarget, - Target as CoreTarget, - TargetType, -} from "@syntest/analysis"; +import { SubTarget as CoreSubTarget, + Target as CoreTarget, TargetType } from "@syntest/analysis"; import { VisibilityType } from "./VisibilityType"; -export interface Target extends CoreTarget { +export class TargetGraph implements CoreTarget { path: string; name: string; - subTargets: SubTarget[]; -} + subTargets: Target[]; // deprecated + targetMap: Map; + childTargetMap: Map + parentTargetMap: Map + + constructor() { + this.targetMap = new Map() + this.childTargetMap = new Map() + this.parentTargetMap = new Map() + } + + // TODO check for cycles + + hasTarget(targetId: string) { + return this.targetMap.has(targetId) + } + + addTarget(target: Target, parent?: Target) { + if (this.targetMap.has(target.id)) { + throw new Error('Each target can only exists once!') + } + this.targetMap.set(target.id, target) + this.childTargetMap.set(target.id, []) + if (parent) { + if (!this.targetMap.has(parent.id)) { + throw new Error('parent does not exist! Targets should be added in order') + } + this.parentTargetMap.set(target.id, parent.id) + this.childTargetMap.get(parent.id).push(target.id) + } + } + + getParentId(target: Target | string): undefined | string { + return typeof target === 'string' ? this.parentTargetMap.get(target) : this.parentTargetMap.get(target.id) + } + + getChildrenIds(target: Target | string): undefined | string[] { + return typeof target === 'string' ? this.childTargetMap.get(target) : this.childTargetMap.get(target.id) + } + + getParent(target: Target | string): undefined | Target { + const parentId = typeof target === 'string' ? this.parentTargetMap.get(target) : this.parentTargetMap.get(target.id) + return parentId ? this.targetMap.get(parentId) : undefined + } + + getChildren(target: Target | string): undefined | Target[] { + const childrenIds = typeof target === 'string' ? this.childTargetMap.get(target) : this.childTargetMap.get(target.id) + return childrenIds ? childrenIds.map((id) => this.targetMap.get(id)) : undefined + } +}; -export interface SubTarget extends CoreSubTarget { - type: TargetType; - id: string; -} +export type Target = RootTarget | CoverageTarget +export type RootTarget = ClassTarget | ObjectTarget | FunctionTarget +export type CoverageTarget = LineTarget | BranchTarget | PathTarget -export interface NamedSubTarget extends SubTarget { - name: string; +export type BaseTarget = CoreSubTarget & { + id: string; typeId: string; -} - -export type Exportable = { - exported: boolean; - // maybe scope? - renamedTo?: string; - module?: boolean; - default?: boolean; + exportId: string; }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isExported(target: any): target is Exportable { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return "exported" in target && target.exported === true; -} - -export interface Callable { - isAsync: boolean; -} - -export interface FunctionTarget extends NamedSubTarget, Exportable, Callable { - type: TargetType.FUNCTION; -} - -export interface ClassTarget extends NamedSubTarget, Exportable { +export type ClassTarget = BaseTarget & { type: TargetType.CLASS; -} +}; -export interface MethodTarget extends NamedSubTarget, Callable { - type: TargetType.METHOD; - classId: string; +export type ObjectTarget = BaseTarget & { + type: TargetType.OBJECT; +}; +export type FunctionTarget = BaseTarget & { + type: TargetType.FUNCTION; + isAsync: boolean; + isStatic: boolean; visibility: VisibilityType; - methodType: "constructor" | "method" | "get" | "set"; - isStatic: boolean; -} - -export interface ObjectTarget extends NamedSubTarget, Exportable { - type: TargetType.OBJECT; -} - -export interface ObjectFunctionTarget extends NamedSubTarget, Callable { - type: TargetType.OBJECT_FUNCTION; - objectId: string; -} +}; -export interface PathTarget extends SubTarget { - type: TargetType.PATH; - ids: string[]; +export type LineTarget = { + id: string + type: TargetType.LINE } -export interface BranchTarget extends SubTarget { - type: TargetType.BRANCH; +export type BranchTarget = { + id: string + type: TargetType.BRANCH + } -export interface LineTarget extends SubTarget { - type: TargetType.LINE; - line: number; -} +export type PathTarget = { + id: string + type: TargetType.PATH +} \ No newline at end of file diff --git a/libraries/analysis-javascript/lib/target/TargetFactory.ts b/libraries/analysis-javascript/lib/target/TargetFactory.ts index c9fa9f2f..f9659a26 100644 --- a/libraries/analysis-javascript/lib/target/TargetFactory.ts +++ b/libraries/analysis-javascript/lib/target/TargetFactory.ts @@ -16,22 +16,17 @@ * limitations under the License. */ -import * as path from "node:path"; - import { traverse } from "@babel/core"; import * as t from "@babel/types"; import { TargetFactory as CoreTargetFactory } from "@syntest/analysis"; import { Factory } from "../Factory"; -import { ExportVisitor } from "./export/ExportVisitor"; import { Target } from "./Target"; import { TargetVisitor } from "./TargetVisitor"; /** * TargetFactory for Javascript. - * - * @author Dimitri Stallenberg */ export class TargetFactory extends Factory @@ -44,23 +39,13 @@ export class TargetFactory * @param AST The AST of the target */ extract(filePath: string, AST: t.Node): Target { - // bit sad that we have to do this twice, but we need to know the exports - const exportVisitor = new ExportVisitor(filePath, this.syntaxForgiving); - - traverse(AST, exportVisitor); - - const exports = exportVisitor.exports; - const visitor = new TargetVisitor(filePath, this.syntaxForgiving, exports); + const visitor = new TargetVisitor(filePath, this.syntaxForgiving); traverse(AST, visitor); // we should check wether every export is actually used // TODO - return { - path: filePath, - name: path.basename(filePath), - subTargets: visitor.subTargets, - }; + return visitor._getTargetGraph() } } diff --git a/libraries/analysis-javascript/lib/target/TargetVisitor.ts b/libraries/analysis-javascript/lib/target/TargetVisitor.ts index 0a1ae465..a0371104 100644 --- a/libraries/analysis-javascript/lib/target/TargetVisitor.ts +++ b/libraries/analysis-javascript/lib/target/TargetVisitor.ts @@ -22,863 +22,295 @@ import { TargetType } from "@syntest/analysis"; import { AbstractSyntaxTreeVisitor } from "@syntest/ast-visitor-javascript"; import { getLogger, Logger } from "@syntest/logging"; -import { computedProperty, unsupportedSyntax } from "../utils/diagnostics"; -import { Export } from "./export/Export"; import { - Callable, ClassTarget, - Exportable, FunctionTarget, - MethodTarget, - NamedSubTarget, - ObjectFunctionTarget, ObjectTarget, - SubTarget, + Target, + TargetGraph, } from "./Target"; export class TargetVisitor extends AbstractSyntaxTreeVisitor { protected static override LOGGER: Logger; - private _logOrFail(message: string): undefined { - if (this.syntaxForgiving) { - TargetVisitor.LOGGER.warn(message); - return undefined; - } else { - throw new Error(message); - } - } + // private _logOrFail(message: string): undefined { + // if (this.syntaxForgiving) { + // TargetVisitor.LOGGER.warn(message); + // return undefined; + // } else { + // throw new Error(message); + // } + // } - private _exports: Export[]; + private _targetGraph: TargetGraph; + private _parentStack: Target[] - private _subTargets: SubTarget[]; - - constructor(filePath: string, syntaxForgiving: boolean, exports: Export[]) { + constructor(filePath: string, syntaxForgiving: boolean) { super(filePath, syntaxForgiving); TargetVisitor.LOGGER = getLogger("TargetVisitor"); - this._exports = exports; - this._subTargets = []; + this._targetGraph = new TargetGraph(); } - private _getExport(id: string): Export | undefined { - return this._exports.find((x) => { - return x.id === id; - }); + public _getTargetGraph() { + return this._targetGraph } - private _getTargetNameOfDeclaration( - path: NodePath - ): string { - if (path.node.id === null) { - if (path.parentPath.node.type === "ExportDefaultDeclaration") { - // e.g. export default class {} - // e.g. export default function () {} - return "default"; - } else { - // e.g. class {} - // e.g. function () {} - // Should not be possible - return this._logOrFail( - unsupportedSyntax(path.type, this._getNodeId(path)) - ); - } - } else { - // e.g. class x {} - // e.g. function x() {} - return path.node.id.name; - } + private _currentParent(): Target { + return this._parentStack[this._parentStack.length - 1] } - /** - * Get the target name of an expression - * The variable the expression is assigned to is used as the target name - * @param path - * @returns - */ - private _getTargetNameOfExpression(path: NodePath): string { - // e.g. const x = class A {} - // e.g. const x = function A {} - // e.g. const x = () => {} - // we always use x as the target name instead of A - const parentNode = path.parentPath.node; - switch (parentNode.type) { - case "VariableDeclarator": { - // e.g. const ?? = class {} - // e.g. const ?? = function {} - // e.g. const ?? = () => {} - if (parentNode.id.type === "Identifier") { - // e.g. const x = class {} - // e.g. const x = function {} - // e.g. const x = () => {} - return parentNode.id.name; - } else { - // e.g. const {x} = class {} - // e.g. const {x} = function {} - // e.g. const {x} = () => {} - // Should not be possible - return this._logOrFail( - unsupportedSyntax(path.node.type, this._getNodeId(path)) - ); - } - } - case "AssignmentExpression": { - // e.g. ?? = class {} - // e.g. ?? = function {} - // e.g. ?? = () => {} - const assigned = parentNode.left; - if (assigned.type === "Identifier") { - // could also be memberexpression - // e.g. x = class {} - // e.g. x = function {} - // e.g. x = () => {} - return assigned.name; - } else if (assigned.type === "MemberExpression") { - // e.g. x.? = class {} - // e.g. x.? = function {} - // e.g. x.? = () => {} - if (assigned.computed === true) { - if (assigned.property.type.includes("Literal")) { - // e.g. x["y"] = class {} - // e.g. x["y"] = function {} - // e.g. x["y"] = () => {} - return "value" in assigned.property - ? assigned.property.value.toString() - : "null"; - } else { - // e.g. x[y] = class {} - // e.g. x[y] = function {} - // e.g. x[y] = () => {} - return this._logOrFail( - computedProperty(path.node.type, this._getNodeId(path)) - ); - } - } else if (assigned.property.type === "Identifier") { - // e.g. x.y = class {} - // e.g. x.y = function {} - // e.g. x.y = () => {} - if ( - assigned.property.name === "exports" && - assigned.object.type === "Identifier" && - assigned.object.name === "module" - ) { - // e.g. module.exports = class {} - // e.g. module.exports = function {} - // e.g. module.exports = () => {} - return "id" in parentNode.right - ? parentNode.right.id.name - : "anonymousFunction"; - } - return assigned.property.name; - } else { - // e.g. x.? = class {} - // e.g. x.? = function {} - // e.g. x.? = () => {} - // Should not be possible - return this._logOrFail( - unsupportedSyntax(path.node.type, this._getNodeId(path)) - ); - } - } else { - // e.g. {x} = class {} - // e.g. {x} = function {} - // e.g. {x} = () => {} - // Should not be possible - return this._logOrFail( - unsupportedSyntax(path.node.type, this._getNodeId(path)) - ); - } - } - case "ClassProperty": - // e.g. class A { ? = class {} } - // e.g. class A { ? = function () {} } - // e.g. class A { ? = () => {} } - case "ObjectProperty": { - // e.g. {?: class {}} - // e.g. {?: function {}} - // e.g. {?: () => {}} - if (parentNode.key.type === "Identifier") { - // e.g. class A { x = class {} } - // e.g. class A { x = function () {} } - // e.g. class A { x = () => {} } - - // e.g. {y: class {}} - // e.g. {y: function {}} - // e.g. {y: () => {}} - return parentNode.key.name; - } else if (parentNode.key.type.includes("Literal")) { - // e.g. class A { "x" = class {} } - // e.g. class A { "x" = function () {} } - // e.g. class A { "x" = () => {} } - - // e.g. {1: class {}} - // e.g. {1: function {}} - // e.g. {1: () => {}} - return "value" in parentNode.key - ? parentNode.key.value.toString() - : "null"; - } else { - // e.g. const {x} = class {} - // e.g. const {x} = function {} - // e.g. const {x} = () => {} - - // e.g. {?: class {}} - // e.g. {?: function {}} - // e.g. {?: () => {}} - // Should not be possible - return this._logOrFail( - unsupportedSyntax(path.node.type, this._getNodeId(path)) - ); - } - } - case "ReturnStatement": - // e.g. return class {} - // e.g. return function () {} - // e.g. return () => {} - case "ArrowFunctionExpression": - // e.g. () => class {} - // e.g. () => function () {} - // e.g. () => () => {} - case "NewExpression": - // e.g. new Class(class {}) // dont think this one is possible but unsure - // e.g. new Class(function () {}) - // e.g. new Class(() => {}) - case "CallExpression": { - // e.g. function(class {}) // dont think this one is possible but unsure - // e.g. function(function () {}) - // e.g. function(() => {}) - return "id" in path.node && path.node.id && "name" in path.node.id - ? path.node.id.name - : "anonymous"; - } - case "ConditionalExpression": { - // e.g. c ? class {} : b - // e.g. c ? function () {} : b - // e.g. c ? () => {} : b - return this._getTargetNameOfExpression(path.parentPath); - } - case "LogicalExpression": { - // e.g. c || class {} - // e.g. c || function () {} - // e.g. c || () => {} - return this._getTargetNameOfExpression(path.parentPath); - } - case "ExportDefaultDeclaration": { - // e.g. export default class {} - // e.g. export default function () {} - // e.g. export default () => {} - return "default"; - } - default: { - // e.g. class {} - // e.g. function () {} - // e.g. () => {} - // Should not be possible - return this._logOrFail( - unsupportedSyntax(parentNode.type, this._getNodeId(path)) - ); - } - } - } - public FunctionDeclaration: (path: NodePath) => void = - (path) => { - // e.g. function x() {} - const targetName = this._getTargetNameOfDeclaration(path); - const id = this._getNodeId(path); - const export_ = this._getExport(id); - - this._extractFromFunction( - path, - id, - id, - targetName, - export_, - false, - false - ); - - path.skip(); - }; - - public ClassDeclaration: (path: NodePath) => void = ( - path - ) => { - // e.g. class A {} - const targetName = this._getTargetNameOfDeclaration(path); - const id = this._getNodeId(path); - const export_ = this._getExport(id); - - this._extractFromClass(path, id, id, targetName, export_); - - path.skip(); - }; - - public FunctionExpression: (path: NodePath) => void = ( - path - ) => { - // only thing left where these can be found is: - // call(function () {}) - const targetName = this._getTargetNameOfExpression(path); - - if (!targetName) { - return; - } - - const id = this._getNodeId(path); - const export_ = this._getExport(id); - - this._extractFromFunction(path, id, id, targetName, export_, false, false); - - path.skip(); - }; - - public ClassExpression: (path: NodePath) => void = ( - path - ) => { - // only thing left where these can be found is: - // call(class {}) - const targetName = this._getTargetNameOfExpression(path); - - if (!targetName) { - return; - } - - const id = this._getNodeId(path); - const export_ = this._getExport(id); + public Function = { + enter: (path: NodePath) => { + const currentParent = this._currentParent() - this._extractFromClass(path, id, id, targetName, export_); - - path.skip(); - }; - - public ArrowFunctionExpression: ( - path: NodePath - ) => void = (path) => { - // only thing left where these can be found is: - // call(() => {}) - const targetName = this._getTargetNameOfExpression(path); + const id = this._getNodeId(path); - if (!targetName) { - return; + const newTarget: FunctionTarget = { + id: id, + typeId: id, + exportId: id, + type: TargetType.FUNCTION, + isAsync: path.node.async, + isStatic: path.isClassMethod() || path.isClassProperty() ? path.node.static : false, + methodType: path.isClassMethod() ? path.node.kind : "method", + visibility: path.isClassMethod() && path.node.access ? path.node.access : "public", + } + this._targetGraph.addTarget(newTarget, currentParent) + this._parentStack.push(newTarget) + }, + exit: (_path: NodePath) => { + // TODO check if it is me + this._parentStack.pop() } + } - // TODO is there a difference if the parent is a variable declarator? - - const id = this._getNodeId(path); - const export_ = this._getExport(id); - - this._extractFromFunction(path, id, id, targetName, export_, false, false); + public Class = { + enter: (path: NodePath) => { + const currentParent = this._currentParent() - path.skip(); - }; + const id = this._getNodeId(path); - public VariableDeclarator: (path: NodePath) => void = ( - path - ) => { - if (!path.has("init")) { - path.skip(); - return; - } - const idPath = >path.get("id"); - const init = path.get("init"); - - const targetName = idPath.node.name; - const id = this._getNodeId(path); - const typeId = this._getNodeId(init); - const export_ = this._getExport(id); - - if (init.isFunction()) { - this._extractFromFunction( - init, - id, - typeId, - targetName, - export_, - false, - false - ); - } else if (init.isClass()) { - this._extractFromClass(init, id, typeId, targetName, export_); - } else if (init.isObjectExpression()) { - this._extractFromObjectExpression(init, id, typeId, targetName, export_); - } else { - // TODO + const newTarget: ClassTarget = { + id: id, + typeId: id, + exportId: id, + type: TargetType.CLASS + } + this._targetGraph.addTarget(newTarget, currentParent) + this._parentStack.push(newTarget) + }, + exit: (path: NodePath) => { + // TODO check if it is me + this._parentStack.pop() } + } - path.skip(); - }; - - public AssignmentExpression: ( - path: NodePath - ) => void = (path) => { - const left = path.get("left"); - const right = path.get("right"); - - if ( - !right.isFunction() && - !right.isClass() && - !right.isObjectExpression() - ) { - return; - } + public ObjectExpression = { + enter: (path: NodePath) => { + const currentParent = this._currentParent() - const targetName = this._getTargetNameOfExpression(right); - if (!targetName) { - return; - } - let isObject = false; - let isMethod = false; - let objectId: string; - - let id: string = this._getBindingId(left); - if (left.isMemberExpression()) { - const object = left.get("object"); - const property = left.get("property"); - - if (property.isIdentifier() && left.node.computed) { - path.skip(); - - this._logOrFail(computedProperty(left.type, this._getNodeId(path))); - return; - } else if (!left.get("property").isIdentifier() && !left.node.computed) { - // we also dont support a.f() = ? - // or equivalent - path.skip(); - return; - } + const id = this._getNodeId(path); - if (object.isIdentifier()) { - // x.? = ? - // x['?'] = ? - if ( - object.node.name === "exports" || - (object.node.name === "module" && - property.isIdentifier() && - property.node.name === "exports") - ) { - // exports.? = ? - // module.exports = ? - isObject = false; - id = this._getBindingId(right); - } else { - isObject = true; - objectId = this._getBindingId(object); - // find object - const objectTarget = this._subTargets.find( - (value) => value.id === objectId && value.type === TargetType.OBJECT - ); - - if (!objectTarget) { - const export_ = this._getExport(objectId); - // create one if it does not exist - const objectTarget: ObjectTarget = { - id: objectId, - typeId: objectId, - name: object.node.name, - type: TargetType.OBJECT, - exported: !!export_, - default: export_ ? export_.default : false, - module: export_ ? export_.module : false, - }; - this._subTargets.push(objectTarget); - } - } - } else if (object.isMemberExpression()) { - // ?.?.? = ? - const subObject = object.get("object"); - const subProperty = object.get("property"); - // what about module.exports.x - if ( - subObject.isIdentifier() && - subProperty.isIdentifier() && - subProperty.node.name === "prototype" - ) { - // x.prototype.? = ? - objectId = this._getBindingId(subObject); - const objectTarget = ( - this._subTargets.find((value) => value.id === objectId) - ); - - const newTargetClass: ClassTarget = { - id: objectTarget.id, - type: TargetType.CLASS, - name: objectTarget.name, - typeId: objectTarget.id, - exported: objectTarget.exported, - renamedTo: objectTarget.renamedTo, - module: objectTarget.module, - default: objectTarget.default, - }; - - // replace original target by prototype class - this._subTargets[this._subTargets.indexOf(objectTarget)] = - newTargetClass; - - const constructorTarget: MethodTarget = { - id: objectTarget.id, - type: TargetType.METHOD, - name: objectTarget.name, - typeId: objectTarget.id, - methodType: "constructor", - classId: objectTarget.id, - visibility: "public", - isStatic: false, - isAsync: - "isAsync" in objectTarget - ? (objectTarget).isAsync - : false, - }; - - this._subTargets.push(constructorTarget); - - isMethod = true; - } - } else { - path.skip(); - return; + const newTarget: ObjectTarget = { + id: id, + typeId: id, + exportId: id, + type: TargetType.OBJECT } + this._targetGraph.addTarget(newTarget, currentParent) + this._parentStack.push(newTarget) + }, + exit: (_path: NodePath) => { + // TODO check if it is me + this._parentStack.pop() } + } - const typeId = this._getNodeId(right); - const export_ = this._getExport(isObject ? objectId : id); - - if (right.isFunction()) { - this._extractFromFunction( - right, - id, - typeId, - targetName, - export_, - isObject, - isMethod, - objectId - ); - } else if (right.isClass()) { - this._extractFromClass(right, id, typeId, targetName, export_); - } else if (right.isObjectExpression()) { - this._extractFromObjectExpression(right, id, typeId, targetName, export_); - } else { - // TODO - } + public ObjectPattern = { + enter: (path: NodePath) => { + const currentParent = this._currentParent() - path.skip(); - }; - - private _extractFromFunction( - path: NodePath, - functionId: string, - typeId: string, - functionName: string, - export_: Export | undefined, - isObjectFunction: boolean, - isMethod: boolean, - superId?: string - ) { - let target: FunctionTarget | ObjectFunctionTarget | MethodTarget; - - if (isObjectFunction && isMethod) { - throw new Error("Cannot be method and object function"); - } + const id = this._getNodeId(path); - if (isObjectFunction) { - if (!superId) { - throw new Error( - "if it is an object function the object id should be given" - ); + const newTarget: ObjectTarget = { + id: id, + typeId: id, + exportId: id, + type: TargetType.OBJECT } - target = { - id: functionId, - typeId: typeId, - objectId: superId, - name: functionName, - type: TargetType.OBJECT_FUNCTION, - isAsync: path.node.async, - }; - } else if (isMethod) { - if (!superId) { - throw new Error( - "if it is an object function the object id should be given" - ); - } - target = { - id: functionId, - typeId: typeId, - classId: superId, - name: functionName, - type: TargetType.METHOD, - isAsync: path.node.async, - methodType: path.isClassMethod() ? path.node.kind : "method", - visibility: - path.isClassMethod() && path.node.access - ? path.node.access - : "public", - isStatic: - path.isClassMethod() || path.isClassProperty() - ? path.node.static - : false, - }; - } else { - target = { - id: functionId, - typeId: typeId, - name: functionName, - type: TargetType.FUNCTION, - exported: !!export_, - default: export_ ? export_.default : false, - module: export_ ? export_.module : false, - isAsync: path.node.async, - }; + this._targetGraph.addTarget(newTarget, currentParent) + this._parentStack.push(newTarget) + }, + exit: (_path: NodePath) => { + // TODO check if it is me + this._parentStack.pop() } + } - this._subTargets.push(target); - - const body = path.get("body"); + public AssignmentExpression = { + enter: (path: NodePath) => { + const currentParent = this._currentParent() - if (Array.isArray(body)) { - throw new TypeError("weird function body"); - } else { - body.visit(); - } - } + const id = this._getNodeId(path); - private _extractFromObjectExpression( - path: NodePath, - objectId: string, - typeId: string, - objectName: string, - export_?: Export - ) { - const target: ObjectTarget = { - id: objectId, - typeId: typeId, - name: objectName, - type: TargetType.OBJECT, - exported: !!export_, - default: export_ ? export_.default : false, - module: export_ ? export_.module : false, - }; - - this._subTargets.push(target); - - // loop over object properties - for (const property of path.get("properties")) { - if (property.isObjectMethod()) { - if (property.node.computed) { - // e.g. class A { ?() {} } - // unsupported - // not possible i think - this._logOrFail( - computedProperty(property.type, this._getNodeId(property)) - ); - continue; - } - const key = property.get("key"); - if (key.isIdentifier()) { - const targetName = key.node.name; - - const id = this._getNodeId(property); - this._extractFromFunction( - property, - id, - id, - targetName, - undefined, - true, - false, - objectId - ); - } else if (key.isLiteral()) { - const targetName = "value" in key ? String(key.value) : "null"; - - const id = this._getNodeId(property); - this._extractFromFunction( - property, - id, - id, - targetName, - undefined, - true, - false, - objectId - ); - } else { - // not possible i think - this._logOrFail( - unsupportedSyntax(property.node.type, this._getNodeId(property)) - ); - continue; - } - } else if (property.isObjectProperty()) { - const key = property.get("key"); - const value = property.get("value"); - - if (value) { - const id = this._getNodeId(property); - let targetName: string; - if (key.isIdentifier()) { - targetName = key.node.name; - } else if ( - key.isStringLiteral() || - key.isBooleanLiteral() || - key.isNumericLiteral() || - key.isBigIntLiteral() - ) { - targetName = String(key.node.value); - } - - if (value.isFunction()) { - this._extractFromFunction( - value, - id, - id, - targetName, - undefined, - true, - false, - objectId - ); - } else if (value.isClass()) { - this._extractFromClass(value, id, id, targetName); - } else if (value.isObjectExpression()) { - this._extractFromObjectExpression(value, id, id, targetName); - } else { - // TODO - } - } - } else if (property.isSpreadElement()) { - // TODO - // extract the spread element + const newTarget: ObjectTarget = { + id: id, + typeId: id, + exportId: id, + type: TargetType.OBJECT } + this._targetGraph.addTarget(newTarget, currentParent) + this._parentStack.push(newTarget) + }, + exit: (path: NodePath) => { + // TODO check if it is me + this._parentStack.pop() } } - private _extractFromClass( - path: NodePath, - classId: string, - typeId: string, - className: string, - export_?: Export | undefined - ): void { - const target: ClassTarget = { - id: classId, - typeId: typeId, - name: className, - type: TargetType.CLASS, - exported: !!export_, - default: export_ ? export_.default : false, - module: export_ ? export_.module : false, - }; - - this._subTargets.push(target); - - const body = >path.get("body"); - for (const classBodyAttribute of body.get("body")) { - if (classBodyAttribute.isClassMethod()) { - if (classBodyAttribute.node.key.type !== "Identifier") { - // e.g. class A { ?() {} } - // unsupported - // not possible i think - this._logOrFail( - unsupportedSyntax( - classBodyAttribute.node.type, - this._getNodeId(classBodyAttribute) - ) - ); - continue; - } - - const targetName = classBodyAttribute.node.key.name; - - const id = this._getNodeId(classBodyAttribute); - - this._extractFromFunction( - classBodyAttribute, - id, - id, - targetName, - undefined, - false, - true, - classId - ); - } else if (classBodyAttribute.isClassProperty()) { - const key = classBodyAttribute.get("key"); - const value = classBodyAttribute.get("value"); - - if (value) { - const id = this._getNodeId(classBodyAttribute); - let targetName: string; - if (key.isIdentifier()) { - targetName = key.node.name; - } else if ( - key.isStringLiteral() || - key.isBooleanLiteral() || - key.isNumericLiteral() || - key.isBigIntLiteral() - ) { - targetName = String(key.node.value); - } - - if (value.isFunction()) { - this._extractFromFunction( - value, - id, - id, - targetName, - undefined, - false, - true, - classId - ); - } else if (value.isClass()) { - this._extractFromClass(value, id, id, targetName); - } else if (value.isObjectExpression()) { - this._extractFromObjectExpression(value, id, id, targetName); - } else { - // TODO - } - } - } else { - return this._logOrFail( - unsupportedSyntax(body.node.type, this._getNodeId(classBodyAttribute)) - ); - } - } - } - get subTargets(): SubTarget[] { - return this._subTargets - .reverse() - .filter((subTarget, index, self) => { - if (!("name" in subTarget)) { - // paths/branches/lines are always unique - return true; - } - - // filter duplicates because of redefinitions - // e.g. let a = 1; a = 2; - // this would result in two subtargets with the same name "a" - // but we only want the last one - return ( - index === - self.findIndex((t) => { - return ( - "name" in t && - t.id === subTarget.id && - t.type === subTarget.type && - t.name === subTarget.name && - (t.type === TargetType.METHOD - ? (t).methodType === - (subTarget).methodType && - (t).isStatic === - (subTarget).isStatic && - (t).classId === - (subTarget).classId - : true) - ); - }) - ); - }) - .reverse(); - } + // public AssignmentExpression: ( + // path: NodePath + // ) => void = (path) => { + // const left = path.get("left"); + // const right = path.get("right"); + + // const targetName = this._getTargetNameOfExpression(right); + // if (!targetName) { + // return; + // } + // let isObject = false; + // let isMethod = false; + // let superId: string; + + // let id: string; + + // if (left.isIdentifier()) { + // // x = ? + // id = this._getBindingId(left); + // } else { + // // ? = ? + // id = this._getBindingId(right); + // } + + // if (left.isMemberExpression()) { + // const object = left.get("object"); + // const property = left.get("property"); + + // if (property.isIdentifier() && left.node.computed) { + // path.skip(); + // this._logOrFail(computedProperty(left.type, this._getNodeId(path))); + // return; + // } else if (!property.isIdentifier() && !left.node.computed) { + // // we also dont support a.f() = ? + // // or equivalent + // path.skip(); + // this._logOrFail(unsupportedSyntax(left.type, this._getNodeId(path))); + // return; + // } + + // if (object.isIdentifier()) { + // // x.? = ? + // // x['?'] = ? + // if ( + // object.node.name === "exports" || + // (object.node.name === "module" && + // property.isIdentifier() && + // property.node.name === "exports") + // ) { + // // exports.? = ? + // // module.exports = ? + // isObject = false; + // id = this._getBindingId(right); + // } else if ( + // (property.isIdentifier() && property.node.name === "prototype") || + // (property.isStringLiteral() && property.node.value === "prototype") + // ) { + // // x.prototype = ? + // // x['prototype'] = ? + // isObject = true; + // superId = this._getBindingId(object); + // const typeId = this._getBindingId(right); + + // this._findAndReplaceOrCreateClass( + // superId, + // typeId, + // superId, + // object.node.name + // ); + // isMethod = true; + + // // // find object + // // this._findOrCreateObject(superId, typeId, superId, object.node.name) + + // const prototypeId = this._getBindingId(right); + + // this._setEqual(superId, prototypeId); + // } else { + // // x.x = ? + // isObject = true; + // superId = this._getBindingId(object); + // // find object + // this._findOrCreateObject(superId, superId, superId, object.node.name); + // } + // } else if (object.isMemberExpression()) { + // // ?.?.? = ? + // const subObject = object.get("object"); + // const subProperty = object.get("property"); + // // what about module.exports.x + // if ( + // subObject.isIdentifier() && + // ((subProperty.isIdentifier() && + // subProperty.node.name === "prototype") || + // (subProperty.isStringLiteral() && + // subProperty.node.value === "prototype")) + // ) { + // // x.prototype.? = ? + // // x['prototype'].? = ? + // superId = this._getBindingId(subObject); + + // this._findAndReplaceOrCreateClass( + // superId, + // superId, + // superId, + // subObject.node.name + // ); + // isMethod = true; + // } + // } else { + // path.skip(); + // return; + // } + // } + + // const typeId = this._getNodeId(right); + // const export_ = this._getExport(isObject || isMethod ? superId : id); + + // if (right.isFunction()) { + // this._extractFromFunction( + // right, + // id, + // typeId, + // targetName, + // export_, + // isObject, + // isMethod, + // superId + // ); + // } else if (right.isClass()) { + // this._extractFromClass(right, id, typeId, targetName, export_); + // } else if (right.isObjectExpression()) { + // this._findOrCreateObject(id, typeId, isObject ? superId : id, targetName); + // this._extractFromObjectExpression(right, id); + // } else if (right.isIdentifier()) { + // this._setEqual(id, this._getBindingId(right)); + // } else { + // // TODO + // } + + // path.skip(); + // }; + } diff --git a/libraries/analysis-javascript/lib/target/VisibilityType.ts b/libraries/analysis-javascript/lib/target/VisibilityType.ts index 6090501c..8ea92e33 100644 --- a/libraries/analysis-javascript/lib/target/VisibilityType.ts +++ b/libraries/analysis-javascript/lib/target/VisibilityType.ts @@ -18,7 +18,5 @@ /** * Visibility Types. - * - * @author Dimitri Stallenberg */ export type VisibilityType = "public" | "private" | "protected"; diff --git a/libraries/analysis-javascript/lib/target/export/ExpressionStatement.ts b/libraries/analysis-javascript/lib/target/export/ExpressionStatement.ts index 1b0289c0..3e5d64c1 100644 --- a/libraries/analysis-javascript/lib/target/export/ExpressionStatement.ts +++ b/libraries/analysis-javascript/lib/target/export/ExpressionStatement.ts @@ -103,7 +103,6 @@ export function extractExportsFromRightAssignmentExpression( }); } - console.log(exports); return exports; } diff --git a/libraries/analysis-javascript/package.json b/libraries/analysis-javascript/package.json index 12f6f836..605d74d6 100644 --- a/libraries/analysis-javascript/package.json +++ b/libraries/analysis-javascript/package.json @@ -40,7 +40,7 @@ "format:check": "prettier --config ../../.prettierrc.json --ignore-path ../../.prettierignore --check .", "lint": "eslint --config ../../.eslintrc.json --ignore-path ../../.eslintignore .", "lint:fix": "eslint --config ../../.eslintrc.json --ignore-path ../../.eslintignore . --fix", - "test": "mocha --config ../../.mocharc.json", + "test": "mocha --config ../../.mocharc.json -g 'TargetVisitor'", "test:coverage": "nyc --reporter=text --reporter=html --reporter=lcov mocha --config ../../.mocharc.json", "test:coverage:ci": "nyc --reporter=lcovonly mocha --config ../../.mocharc.json --reporter json --reporter-option output=test-results.json", "test:watch": "mocha --config ../../.mocharc.json --watch" diff --git a/libraries/analysis-javascript/test/target/PrototypeTargetVisitor.test.ts b/libraries/analysis-javascript/test/target/PrototypeTargetVisitor.test.ts new file mode 100644 index 00000000..b036b5df --- /dev/null +++ b/libraries/analysis-javascript/test/target/PrototypeTargetVisitor.test.ts @@ -0,0 +1,180 @@ +/* + * Copyright 2020-2023 Delft University of Technology and SynTest contributors + * + * This file is part of SynTest Framework - SynTest JavaScript. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { traverse } from "@babel/core"; +import { TargetType } from "@syntest/analysis"; +import * as chai from "chai"; + +import { AbstractSyntaxTreeFactory } from "../../lib/ast/AbstractSyntaxTreeFactory"; +import { ExportVisitor } from "../../lib/target/export/ExportVisitor"; +import { + ClassTarget, + MethodTarget, + ObjectFunctionTarget, + ObjectTarget, + SubTarget, +} from "../../lib/target/Target"; +import { TargetVisitor } from "../../lib/target/TargetVisitor"; + +const expect = chai.expect; + +function targetHelper(source: string) { + const generator = new AbstractSyntaxTreeFactory(); + const ast = generator.convert("", source); + + const exportVisitor = new ExportVisitor("", true); + traverse(ast, exportVisitor); + const exports = exportVisitor.exports; + + const visitor = new TargetVisitor("", true, exports); + traverse(ast, visitor); + + return visitor.subTargets; +} +// function checkFunction( +// target: SubTarget, +// name: string, +// exported: boolean, +// isAsync: boolean +// ): void { +// expect(target.type).to.equal(TargetType.FUNCTION); + +// const functionTarget = target; + +// expect(functionTarget.name).to.equal(name); +// expect(functionTarget.exported).to.equal(exported); +// expect(functionTarget.isAsync).to.equal(isAsync); +// } + +function checkObject(target: SubTarget, name: string, exported: boolean): void { + expect(target.type).to.equal(TargetType.OBJECT); + + const objectTarget = target; + + expect(objectTarget.name).to.equal(name); + expect(objectTarget.exported).to.equal(exported); +} + +function checkObjectFunction( + target: SubTarget, + name: string, + objectId: string, + isAsync: boolean +): void { + expect(target.type).to.equal(TargetType.OBJECT_FUNCTION); + + const functionTarget = target; + + expect(functionTarget.name).to.equal(name); + expect(functionTarget.objectId).to.equal(objectId); + expect(functionTarget.isAsync).to.equal(isAsync); +} + +function checkClass(target: SubTarget, name: string, exported: boolean): void { + expect(target.type).to.equal(TargetType.CLASS); + + const classTarget = target; + + expect(classTarget.name).to.equal(name); + expect(classTarget.exported).to.equal(exported); +} + +function checkClassMethod( + target: SubTarget, + name: string, + classId: string, + methodType: string, + visibility: string, + isStatic: boolean, + isAsync: boolean +): void { + expect(target.type).to.equal(TargetType.METHOD); + + const methodTarget = target; + + expect(methodTarget.name).to.equal(name); + expect(methodTarget.classId).to.equal(classId); + expect(methodTarget.methodType).to.equal(methodType); + expect(methodTarget.visibility).to.equal(visibility); + expect(methodTarget.isStatic).to.equal(isStatic); + expect(methodTarget.isAsync).to.equal(isAsync); +} + +describe("Prototyped TargetVisitor test", () => { + it("Test 1", () => { + const source = ` + const x = {} + x.prototype.y = function () {} + x.prototype.z = function () {} + + module.exports = x + `; + + const targets = targetHelper(source); + + expect(targets.length).to.equal(3); + + checkClass(targets[0], "x", true); + checkClassMethod( + targets[1], + "y", + targets[0].id, + "method", + "public", + false, + false + ); + checkClassMethod( + targets[2], + "z", + targets[0].id, + "method", + "public", + false, + false + ); + }); + + it("Test 2", () => { + const source = ` + const x = {} + const y = {} + y.f = function () {} + x.prototype = y + + module.exports = x + `; + + const targets = targetHelper(source); + + expect(targets.length).to.equal(4); + + checkClass(targets[0], "x", true); + checkObject(targets[1], "y", false); + + checkObjectFunction(targets[2], "f", targets[1].id, false); + checkClassMethod( + targets[3], + "f", + targets[0].id, + "method", + "public", + false, + false + ); + }); +}); diff --git a/libraries/analysis-javascript/test/target/TargetVisitor.test.ts b/libraries/analysis-javascript/test/target/TargetVisitor.test.ts index 5f1d52a4..e132112a 100644 --- a/libraries/analysis-javascript/test/target/TargetVisitor.test.ts +++ b/libraries/analysis-javascript/test/target/TargetVisitor.test.ts @@ -24,10 +24,8 @@ import { ExportVisitor } from "../../lib/target/export/ExportVisitor"; import { ClassTarget, FunctionTarget, - MethodTarget, - ObjectFunctionTarget, ObjectTarget, - SubTarget, + Target, } from "../../lib/target/Target"; import { TargetVisitor } from "../../lib/target/TargetVisitor"; @@ -37,83 +35,35 @@ function targetHelper(source: string) { const generator = new AbstractSyntaxTreeFactory(); const ast = generator.convert("", source); - const exportVisitor = new ExportVisitor("", true); - traverse(ast, exportVisitor); - const exports = exportVisitor.exports; - - const visitor = new TargetVisitor("", true, exports); + const visitor = new TargetVisitor("", true); traverse(ast, visitor); - return visitor.subTargets; + return visitor._getTargetGraph(); } function checkFunction( - target: SubTarget, - name: string, - exported: boolean, - isAsync: boolean + target: Target, + isAsync: boolean, + methodType: string, + visibility: string, + isStatic: boolean ): void { expect(target.type).to.equal(TargetType.FUNCTION); const functionTarget = target; - expect(functionTarget.name).to.equal(name); - expect(functionTarget.exported).to.equal(exported); expect(functionTarget.isAsync).to.equal(isAsync); + expect(functionTarget.methodType).to.equal(methodType); + expect(functionTarget.visibility).to.equal(visibility); + expect(functionTarget.isStatic).to.equal(isStatic); } -function checkObject(target: SubTarget, name: string, exported: boolean): void { +function checkObject(target: Target): void { expect(target.type).to.equal(TargetType.OBJECT); - - const objectTarget = target; - - expect(objectTarget.name).to.equal(name); - expect(objectTarget.exported).to.equal(exported); -} - -function checkObjectFunction( - target: SubTarget, - name: string, - objectId: string, - isAsync: boolean -): void { - expect(target.type).to.equal(TargetType.OBJECT_FUNCTION); - - const functionTarget = target; - - expect(functionTarget.name).to.equal(name); - expect(functionTarget.objectId).to.equal(objectId); - expect(functionTarget.isAsync).to.equal(isAsync); } -function checkClass(target: SubTarget, name: string, exported: boolean): void { +function checkClass(target: Target): void { expect(target.type).to.equal(TargetType.CLASS); - - const classTarget = target; - - expect(classTarget.name).to.equal(name); - expect(classTarget.exported).to.equal(exported); -} - -function checkClassMethod( - target: SubTarget, - name: string, - classId: string, - methodType: string, - visibility: string, - isStatic: boolean, - isAsync: boolean -): void { - expect(target.type).to.equal(TargetType.METHOD); - - const methodTarget = target; - - expect(methodTarget.name).to.equal(name); - expect(methodTarget.classId).to.equal(classId); - expect(methodTarget.methodType).to.equal(methodType); - expect(methodTarget.visibility).to.equal(visibility); - expect(methodTarget.isStatic).to.equal(isStatic); - expect(methodTarget.isAsync).to.equal(isAsync); } describe("TargetVisitor test", () => { @@ -125,13 +75,14 @@ describe("TargetVisitor test", () => { export { name1 } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(3); - checkFunction(targets[0], "name1", true, false); - checkFunction(targets[1], "name2", false, true); - checkFunction(targets[2], "name3", false, true); + checkFunction(targets[0], false, 'method', 'public', true); + checkFunction(targets[1], true, 'method', 'public', true); + checkFunction(targets[2], true, 'method', 'public', true); }); it("FunctionExpression: functions overwritten", () => { @@ -141,7 +92,8 @@ describe("TargetVisitor test", () => { export { name1 } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(1); checkFunction(targets[0], "name1", true, true); @@ -159,7 +111,8 @@ describe("TargetVisitor test", () => { export { name1 } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(1); @@ -173,7 +126,8 @@ describe("TargetVisitor test", () => { export { name1 } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(2); @@ -187,7 +141,8 @@ describe("TargetVisitor test", () => { export { x } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(1); @@ -200,7 +155,8 @@ describe("TargetVisitor test", () => { export { name1 } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(1); @@ -227,7 +183,8 @@ describe("TargetVisitor test", () => { } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(8); @@ -302,7 +259,8 @@ describe("TargetVisitor test", () => { const x = () => {} `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(1); @@ -316,7 +274,8 @@ describe("TargetVisitor test", () => { } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(2); @@ -339,7 +298,8 @@ describe("TargetVisitor test", () => { } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(2); @@ -362,7 +322,8 @@ describe("TargetVisitor test", () => { } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(2); @@ -387,7 +348,8 @@ describe("TargetVisitor test", () => { } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(3); @@ -413,7 +375,8 @@ describe("TargetVisitor test", () => { } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(3); @@ -440,7 +403,8 @@ describe("TargetVisitor test", () => { exports = obj `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(3); @@ -462,7 +426,8 @@ describe("TargetVisitor test", () => { const x = {} x[y] = function name1() {} `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(1); }); @@ -473,7 +438,8 @@ describe("TargetVisitor test", () => { x.y = function name1() {} `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(2); @@ -488,7 +454,8 @@ describe("TargetVisitor test", () => { x['z'] = async () => {} `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(3); @@ -504,7 +471,8 @@ describe("TargetVisitor test", () => { export { x } `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(2); @@ -519,7 +487,8 @@ describe("TargetVisitor test", () => { module.exports = x `; - const targets = targetHelper(source); + const targetGraph = targetHelper(source); + const targets = [...targetGraph.targetMap.values()] expect(targets.length).to.equal(2); diff --git a/libraries/search-javascript/lib/testbuilding/JavaScriptDecoder.ts b/libraries/search-javascript/lib/testbuilding/JavaScriptDecoder.ts index 998605a9..bf4213c5 100644 --- a/libraries/search-javascript/lib/testbuilding/JavaScriptDecoder.ts +++ b/libraries/search-javascript/lib/testbuilding/JavaScriptDecoder.ts @@ -74,10 +74,6 @@ export class JavaScriptDecoder implements Decoder { decodings = decodings.slice(0, index); } - if (decodings.length === 0) { - throw new Error("No statements in test case after error reduction"); - } - const metaCommentBlock = this.generateMetaComments(testCase); const testLines: string[] = this.generateTestLines( @@ -125,7 +121,6 @@ export class JavaScriptDecoder implements Decoder { const lines = [ "// Imports", - "require = require('esm')(module)", ...imports, gatherAssertionData ? assertionFunction : "", `describe('SynTest Test Suite', function() {`, diff --git a/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts b/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts index ba41d8b1..b84305b7 100644 --- a/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts +++ b/libraries/search-javascript/lib/testcase/execution/JavaScriptRunner.ts @@ -128,6 +128,7 @@ export class JavaScriptRunner implements EncodingRunner { childProcess.send({ message: "run", silent: this.silenceTestOutput, + esm: true, paths: paths, timeout: this.testTimeout, }); diff --git a/libraries/search-javascript/lib/testcase/execution/TestExecutor.ts b/libraries/search-javascript/lib/testcase/execution/TestExecutor.ts index c5b9eedc..93506d40 100644 --- a/libraries/search-javascript/lib/testcase/execution/TestExecutor.ts +++ b/libraries/search-javascript/lib/testcase/execution/TestExecutor.ts @@ -34,6 +34,7 @@ export type Message = RunMessage | DoneMessage; export type RunMessage = { message: "run"; silent: boolean; + esm: boolean; paths: string[]; timeout: number; }; @@ -71,11 +72,16 @@ process.on("message", async (data: Message) => { throw new TypeError("Invalid data received from child process"); } if (data.message === "run") { - await runMocha(data.silent, data.paths, data.timeout); + await runMocha(data.silent, data.esm, data.paths, data.timeout); } }); -async function runMocha(silent: boolean, paths: string[], timeout: number) { +async function runMocha( + silent: boolean, + esm: boolean, + paths: string[], + timeout: number +) { const argv: Mocha.MochaOptions = ({ reporter: silent ? SilentMochaReporter : undefined, // diff: false, @@ -90,13 +96,16 @@ async function runMocha(silent: boolean, paths: string[], timeout: number) { }); const mocha = new Mocha(argv); // require('ts-node/register') - // eslint-disable-next-line unicorn/prefer-module - require("regenerator-runtime/runtime"); - // eslint-disable-next-line unicorn/prefer-module, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-var-requires - require("@babel/register")({ + + if (esm) { // eslint-disable-next-line unicorn/prefer-module - presets: [require.resolve("@babel/preset-env")], - }); + require("regenerator-runtime/runtime"); + // eslint-disable-next-line unicorn/prefer-module, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-var-requires + require("@babel/register")({ + // eslint-disable-next-line unicorn/prefer-module + presets: [require.resolve("@babel/preset-env")], + }); + } for (const _path of paths) { // eslint-disable-next-line unicorn/prefer-module @@ -126,7 +135,7 @@ async function runMocha(silent: boolean, paths: string[], timeout: number) { return { status: status, error: - status === JavaScriptExecutionStatus.FAILED + test && test.err ? { name: test.err.name, message: test.err.message, diff --git a/tools/javascript/lib/JavaScriptLauncher.ts b/tools/javascript/lib/JavaScriptLauncher.ts index 2c7b343a..a714ca0d 100644 --- a/tools/javascript/lib/JavaScriptLauncher.ts +++ b/tools/javascript/lib/JavaScriptLauncher.ts @@ -550,7 +550,6 @@ export class JavaScriptLauncher extends Launcher { finalEncodings = new Map( [...newArchives.entries()].map(([target, archive]) => { - console.log("archive size", archive.size); return [target, archive.getEncodings()]; }) ); @@ -744,6 +743,7 @@ export class JavaScriptLauncher extends Launcher { .getActionableTargets() .filter((target) => isExported(target)); + console.log(currentSubject.getActionableTargets()); if (rootTargets.length === 0) { JavaScriptLauncher.LOGGER.info( `No actionable exported root targets found for ${target.name} in ${target.path}`