diff --git a/android-static-binding-generator/package.json b/android-static-binding-generator/package.json index 254653eda..fcd080bff 100644 --- a/android-static-binding-generator/package.json +++ b/android-static-binding-generator/package.json @@ -12,7 +12,6 @@ "babel-traverse": "^6.4.5", "babel-types": "^6.4.5", "babylon": "^6.4.5", - "filewalker": "^0.1.2", "lazy": "^1.0.11" }, "devDependencies": { diff --git a/android-static-binding-generator/project/parser/js_parser.js b/android-static-binding-generator/project/parser/js_parser.js index 32b5b62e4..21d7ec328 100644 --- a/android-static-binding-generator/project/parser/js_parser.js +++ b/android-static-binding-generator/project/parser/js_parser.js @@ -194,13 +194,11 @@ function traverseFiles(filesToTraverse) { var filesLength = filesToTraverse.length; for(var i = 0; i < filesLength; i += 1) { - var fp = filesToTraverse[i]; logger.info("Visiting JavaScript file: " + fp); readFile(fp) .then(astFromFileContent) - // .then(writeToFile) .then(visitAst) .then(writeToFile) .catch(exceptionHandler) diff --git a/android-static-binding-generator/project/parser/visitors/es5-visitors.js b/android-static-binding-generator/project/parser/visitors/es5-visitors.js index 7c7405b57..15edf99c1 100644 --- a/android-static-binding-generator/project/parser/visitors/es5-visitors.js +++ b/android-static-binding-generator/project/parser/visitors/es5-visitors.js @@ -1,19 +1,19 @@ var es5_visitors = (function () { - var t = require("babel-types"), + var t = require("babel-types"), - defaultExtendDecoratorName = "JavaProxy", - columnOffset = 1, - FILE_SEPARATOR = "f", - LINE_SEPARATOR = "_l", - COLUMN_SEPARATOR = "_c", - DECLARED_CLASS_SEPARATOR = "__", - TYPESCRIPT_EXTEND_STRING = FILE_SEPARATOR + "rnal_ts_helpers_l47_c38", - customExtendsArr = [], - normalExtendsArr = [], - interfacesArr = [], + defaultExtendDecoratorName = "JavaProxy", + columnOffset = 1, + FILE_SEPARATOR = "f", + LINE_SEPARATOR = "_l", + COLUMN_SEPARATOR = "_c", + DECLARED_CLASS_SEPARATOR = "__", + TYPESCRIPT_EXTEND_STRING = FILE_SEPARATOR + "rnal_ts_helpers_l47_c38", + customExtendsArr = [], + normalExtendsArr = [], + interfacesArr = [], - customExtendsArrGlobal = []; + customExtendsArrGlobal = []; /* ENTRY POINT! * Traverses each passed node with several visitors. @@ -23,401 +23,392 @@ var es5_visitors = (function () { * path - node to visit * config - filename, decorator name ... */ - function es5Visitor(path, config) { - - if (!config.filePath) { - config.filePath = "No file path provided"; - } - - if (path.node.skipMeOnVisit) { - return; - } - - // ES5 Syntax - // anchor is extend (normal extend pattern + custom extend pattern) - if (t.isMemberExpression(path) && path.node.property.name === "extend") { - traverseEs5Extend(path, config); - } - - //anchor is new keyword (interface pattern) - if (t.isNewExpression(path)) { - traverseInterface(path, config); - } - - // // Parsed Typescript to ES5 Syntax (normal extend pattern + custom extend pattern) - // // anchor is __extends - if (t.isIdentifier(path) && path.node.name === "__extends") { - traverseTsExtend(path, config); - } - // Maybe it's not a good idea to expose this scenario because it can be explicitly covered - // //anchor is JavaProxy (optional) - // var customDecoratorName = config.extendDecoratorName === undefined ? defaultExtendDecoratorName : config.extendDecoratorName; - // if(t.isIdentifier(path) && path.node.name === customDecoratorName) { - // if(path.node.skipMeOnVisit) { - // return; - // } - // console.log("enters because there is a java proxy down the way") - // traverseJavaProxyExtend(path, config, customDecoratorName); - // } - - } + function es5Visitor(path, config) { + + if (!config.filePath) { + config.filePath = "No file path provided"; + } + + if (path.node.skipMeOnVisit) { + return; + } + + // ES5 Syntax + // anchor is extend (normal extend pattern + custom extend pattern) + if (t.isMemberExpression(path) && path.node.property.name === "extend") { + traverseEs5Extend(path, config); + } + + //anchor is new keyword (interface pattern) + if (t.isNewExpression(path)) { + traverseInterface(path, config); + } + + // // Parsed Typescript to ES5 Syntax (normal extend pattern + custom extend pattern) + // // anchor is __extends + if (t.isIdentifier(path) && path.node.name === "__extends") { + traverseTsExtend(path, config); + } + // Maybe it's not a good idea to expose this scenario because it can be explicitly covered + // //anchor is JavaProxy (optional) + // var customDecoratorName = config.extendDecoratorName === undefined ? defaultExtendDecoratorName : config.extendDecoratorName; + // if(t.isIdentifier(path) && path.node.name === customDecoratorName) { + // if(path.node.skipMeOnVisit) { + // return; + // } + // console.log("enters because there is a java proxy down the way") + // traverseJavaProxyExtend(path, config, customDecoratorName); + // } + + } /* * Returns the custom extends array generated from visitor */ - es5Visitor.getProxyExtendInfo = function () { - var res = customExtendsArr.slice(); - customExtendsArr = []; - return res; - } + es5Visitor.getProxyExtendInfo = function () { + var res = customExtendsArr.slice(); + customExtendsArr = []; + return res; + } /* * Returns the common extends array generated from visitor */ - es5Visitor.getCommonExtendInfo = function () { - var res = []; - for(var index in normalExtendsArr) { - if(normalExtendsArr[index][0] !== "*") { - res.push(normalExtendsArr[index]); - } - } - - normalExtendsArr = []; - return res; - } + es5Visitor.getCommonExtendInfo = function () { + var res = []; + for (var index in normalExtendsArr) { + if (normalExtendsArr[index][0] !== "*") { + res.push(normalExtendsArr[index]); + } + } + + normalExtendsArr = []; + return res; + } /* * Returns the extended interfaces array generated from visitor */ - es5Visitor.getInterfaceInfo = function () { - var res = interfacesArr.slice(); - interfacesArr = []; - return res; - } + es5Visitor.getInterfaceInfo = function () { + var res = interfacesArr.slice(); + interfacesArr = []; + return res; + } /* * Traverses the typescript extend case (__extends()) * Write results in "normalExtendsArr" or "customExtendsArr". */ - function traverseTsExtend(path, config) { - // information for normal extend (unnamed) - var extendClass; - try { - extendClass = _getArgumentFromNodeAsString(path, 5, config) - } catch (e) { - config.logger.info(e.message) - return; - } - - var overriddenMethodNames = _getOverriddenMethodsTypescript(path, 3); - - var extendParent = _getParrent(path, 1, config); - var declaredClassName = ""; - if (t.isCallExpression(extendParent)) { - declaredClassName = extendParent.node.arguments[0].name; - } - - var decorateNodes = traverseForDecorate(path, config); - - var isDecoratedWithExtend = false, - customExtendDecoratorName, - customExtendDecoratorValue, - implementedInterfaces = []; - - if (decorateNodes) { - for (var i in decorateNodes) { - var currentDecorator = decorateNodes[i]; - if (t.isCallExpression(currentDecorator)) { - // Interfaces/Implements - if (currentDecorator.callee.name === config.interfacesDecoratorName) { - currentDecorator.callee.skipMeOnVisit = true; - - var interfaces = currentDecorator.arguments[0].elements; - - for (var i in interfaces) { - var interfaceName = _getWholeInterfaceNameFromInterfacesNode(interfaces[i]); - implementedInterfaces.push(interfaceName); - } - } - - // JavaProxy - if (currentDecorator.callee.name === config.extendDecoratorName) { - currentDecorator.callee.skipMeOnVisit = true; - - isDecoratedWithExtend = true; - - customExtendDecoratorName = config.extendDecoratorName === undefined ? defaultExtendDecoratorName : config.extendDecoratorName; - customExtendDecoratorValue = currentDecorator.arguments[0].value; - } - } - } - } - - if (isDecoratedWithExtend) { - traverseJavaProxyExtend(customExtendDecoratorValue, config, customExtendDecoratorName, extendClass, overriddenMethodNames, implementedInterfaces); - } else { - var lineToWrite = _generateLineToWrite("", extendClass, overriddenMethodNames, TYPESCRIPT_EXTEND_STRING + DECLARED_CLASS_SEPARATOR + declaredClassName, "", implementedInterfaces); - - if (config.logger) { - config.logger.info(lineToWrite) - } - - normalExtendsArr.push(lineToWrite); - } - } - - /* - * Traverses the passed node to find if there is a __decorate inside it. - * If there is __decorate identifier, then the whole node is treated as a custom extend - * This node is skipped next time because it's already traversed once here. - */ - function traverseToFindDecorate(path, config, extendClass, overriddenMethodNames, implementedInterfaces) { - var iifeRoot = _getParrent(path, 3) - var body = iifeRoot.node.body; - for (var index in body) { - var ci = body[index]; - if (t.isExpressionStatement(ci) && - t.isAssignmentExpression(ci.expression) && - ci.expression.right.callee && - ci.expression.right.callee.name === "__decorate" && - ci.expression.right.arguments && - t.isArrayExpression(ci.expression.right.arguments[0])) { - - for (var i in ci.expression.right.arguments[0].elements) { - var currentDecorator = ci.expression.right.arguments[0].elements[i] - - if (t.isCallExpression(currentDecorator)) { - if (currentDecorator.callee.name === config.extendDecoratorName) { - currentDecorator.callee.skipMeOnVisit = true; - var customDecoratorName = config.extendDecoratorName === undefined ? defaultExtendDecoratorName : config.extendDecoratorName; - traverseJavaProxyExtend(currentDecorator.arguments[0].value, config, customDecoratorName, extendClass, overriddenMethodNames, implementedInterfaces); - return true; - } - } - } - } - } - } - - function traverseForDecorate(path, config) { - var iifeRoot = _getParrent(path, 3) - var body = iifeRoot.node.body; - for (var index in body) { - var ci = body[index]; - if (t.isExpressionStatement(ci) && - t.isAssignmentExpression(ci.expression) && - ci.expression.right.callee && - ci.expression.right.callee.name === "__decorate" && - ci.expression.right.arguments && - t.isArrayExpression(ci.expression.right.arguments[0])) { - // returns the node of the decorate (node.expression.right.callee) - // __decorate([..]) - return ci.expression.right.arguments[0].elements; - } - } - - return null; - } + function traverseTsExtend(path, config) { + // information for normal extend (unnamed) + var extendClass; + try { + extendClass = _getArgumentFromNodeAsString(path, 5, config) + } catch (e) { + config.logger.info(e.message) + return; + } + + var overriddenMethodNames = _getOverriddenMethodsTypescript(path, 3); + + var extendParent = _getParrent(path, 1, config); + var declaredClassName = ""; + if (t.isCallExpression(extendParent)) { + declaredClassName = extendParent.node.arguments[0].name; + } + + var decorateNodes = traverseForDecorate(path, config, 3); + + var isDecoratedWithExtend = false, + customExtendDecoratorName, + customExtendDecoratorValue, + implementedInterfaces = []; + + if (!decorateNodes) { + // 7 -> Takes 7 levels up to get to the scope where the class is declared + decorateNodes = traverseForDecorateSpecial(path, config, 7); + } + + if (decorateNodes) { + for (var i in decorateNodes) { + var currentDecorator = decorateNodes[i]; + if (t.isCallExpression(currentDecorator)) { + // Interfaces/Implements + if (currentDecorator.callee.name === config.interfacesDecoratorName) { + currentDecorator.callee.skipMeOnVisit = true; + + var interfaces = currentDecorator.arguments[0].elements; + + for (var i in interfaces) { + var interfaceName = _getWholeInterfaceNameFromInterfacesNode(interfaces[i]); + implementedInterfaces.push(interfaceName); + } + } + + // JavaProxy + if (currentDecorator.callee.name === config.extendDecoratorName) { + currentDecorator.callee.skipMeOnVisit = true; + + isDecoratedWithExtend = true; + + customExtendDecoratorName = config.extendDecoratorName === undefined ? defaultExtendDecoratorName : config.extendDecoratorName; + customExtendDecoratorValue = currentDecorator.arguments[0].value; + } + } + } + } + + if (isDecoratedWithExtend) { + traverseJavaProxyExtend(customExtendDecoratorValue, config, customExtendDecoratorName, extendClass, overriddenMethodNames, implementedInterfaces); + } else { + var lineToWrite = _generateLineToWrite("", extendClass, overriddenMethodNames, TYPESCRIPT_EXTEND_STRING + DECLARED_CLASS_SEPARATOR + declaredClassName, "", implementedInterfaces); + + if (config.logger) { + config.logger.info(lineToWrite) + } + + normalExtendsArr.push(lineToWrite); + } + } + + function traverseForDecorate(path, config, depth) { + var iifeRoot = _getParrent(path, depth) + var body = iifeRoot.node.body; + for (var index in body) { + var ci = body[index]; + if (isDecorateStatement(ci)) { + // returns the node of the decorate (node.expression.right.callee) + // __decorate([..]) + return ci.expression.right.arguments[0].elements; + } + } + + return null; + } + + function traverseForDecorateSpecial(path, config, depth, classIndex) { + var iifeRoot = _getParrent(path, depth); + + var sibling = iifeRoot.getSibling(iifeRoot.key + 1); + if (ci) { + if (isDecorateStatement(ci)) { + // returns the node of the decorate (node.expression.right.callee) + // __decorate([..]) + return ci.expression.right.arguments[0].elements; + } + } + + return null; + } + + function isDecorateStatement(node) { + return t.isExpressionStatement(node) && + t.isAssignmentExpression(node.expression) && + node.expression.right.callee && + node.expression.right.callee.name === "__decorate" && + node.expression.right.arguments && + t.isArrayExpression(node.expression.right.arguments[0]) + } /* * Traverses the node, which is a "new" expression and find if it's a native interface or not. * Write results in "interfacesArr". */ - function traverseInterface(path, config) { - if (!config.interfaceNames) { - throw "No interface names are provided! You can pass them in config.interfaceNames as an array!" - } - - var o = path.node.callee, - interfaceArr = _getWholeName(o), - foundInterface = false, - interfaceNames = config.interfaceNames - - var currentInterface = interfaceArr.reverse().join("."); - for (var i in interfaceNames) { - var interfaceName = interfaceNames[i].trim(); - if (interfaceName === currentInterface) { - currentInterface = interfaceName; - foundInterface = true; - break; - } - } - - if (foundInterface) { - var arg0 = "", - arg1; - if (path.node.arguments.length === 1) { - arg1 = path.node.arguments[0]; - } - else if (path.node.arguments.length === 2) { - arg0 = path.node.arguments[0]; - arg1 = path.node.arguments[1]; - } - else { - throw { - message: "Not enough or too many arguments passed(" + path.node.arguments.length + ") when trying to extend interface in file: " + config.filePath, - errCode: 1 - } - } - - var isCorrectInterfaceName = _testClassName(arg0.value); - var overriddenInterfaceMethods = _getOverriddenMethods(arg1, config); - var extendInfo = FILE_SEPARATOR + config.filePath + LINE_SEPARATOR + path.node.loc.start.line + COLUMN_SEPARATOR + (path.node.loc.start.column + columnOffset) + DECLARED_CLASS_SEPARATOR + (isCorrectInterfaceName ? arg0.value : ""); - var lineToWrite = _generateLineToWrite("", currentInterface, overriddenInterfaceMethods.join(","), extendInfo, ""); - if (config.logger) { - config.logger.info(lineToWrite) - } - - interfacesArr.push(lineToWrite) - } - } + function traverseInterface(path, config) { + if (!config.interfaceNames) { + throw "No interface names are provided! You can pass them in config.interfaceNames as an array!" + } + + var o = path.node.callee, + interfaceArr = _getWholeName(o), + foundInterface = false, + interfaceNames = config.interfaceNames + + var currentInterface = interfaceArr.reverse().join("."); + for (var i in interfaceNames) { + var interfaceName = interfaceNames[i].trim(); + if (interfaceName === currentInterface) { + currentInterface = interfaceName; + foundInterface = true; + break; + } + } + + if (foundInterface) { + var arg0 = "", + arg1; + if (path.node.arguments.length === 1) { + arg1 = path.node.arguments[0]; + } + else if (path.node.arguments.length === 2) { + arg0 = path.node.arguments[0]; + arg1 = path.node.arguments[1]; + } + else { + throw { + message: "Not enough or too many arguments passed(" + path.node.arguments.length + ") when trying to extend interface in file: " + config.filePath, + errCode: 1 + } + } + + var isCorrectInterfaceName = _testClassName(arg0.value); + var overriddenInterfaceMethods = _getOverriddenMethods(arg1, config); + var extendInfo = FILE_SEPARATOR + config.filePath + LINE_SEPARATOR + path.node.loc.start.line + COLUMN_SEPARATOR + (path.node.loc.start.column + columnOffset) + DECLARED_CLASS_SEPARATOR + (isCorrectInterfaceName ? arg0.value : ""); + var lineToWrite = _generateLineToWrite("", currentInterface, overriddenInterfaceMethods.join(","), extendInfo, ""); + if (config.logger) { + config.logger.info(lineToWrite) + } + + interfacesArr.push(lineToWrite) + } + } /* * Finds the java proxy name from custom class decorator. * Write results in "customExtendsArr" */ - function traverseJavaProxyExtend(path, config, customDecoratorName, extendClass, overriddenMethodNames, implementedInterfaces) { - if (config.logger) { - config.logger.info("\t+in " + customDecoratorName + " anchor"); - } + function traverseJavaProxyExtend(path, config, customDecoratorName, extendClass, overriddenMethodNames, implementedInterfaces) { + if (config.logger) { + config.logger.info("\t+in " + customDecoratorName + " anchor"); + } - var classNameFromDecorator = path;//_getDecoratorArgument(path, config, customDecoratorName); + var classNameFromDecorator = path;//_getDecoratorArgument(path, config, customDecoratorName); - var lineToWrite = _generateLineToWrite(classNameFromDecorator, extendClass, overriddenMethodNames, "", config.fullPathName, implementedInterfaces); - if (config.logger) { - config.logger.info(lineToWrite) - } - addCustomExtend(classNameFromDecorator, config.fullPathName, lineToWrite) - } + var lineToWrite = _generateLineToWrite(classNameFromDecorator, extendClass, overriddenMethodNames, "", config.fullPathName, implementedInterfaces); + if (config.logger) { + config.logger.info(lineToWrite) + } + addCustomExtend(classNameFromDecorator, config.fullPathName, lineToWrite) + } /* * Finds the normal extend name, overridden methods and possibly java proxy name from passed node. * Writes to "customExtendsArr" or "normalExtendsArr". * Left whole for readability. */ - function traverseEs5Extend(path, config) { - var callee = path.parent.callee; - - if (callee) { - var o = callee.object - extendClass = _getWholeName(o); - - var extendArguments = path.parent.arguments; - var arg0, - arg1; - if (extendArguments.length === 1 && t.isObjectExpression(arg0)) { - arg0 = extendArguments[0]; - } - else if (t.isStringLiteral(arg0)) { - - } - - var arg0 = "", - arg1; - if (extendArguments.length) { - // Get implementation object when there is only 1 argument - if (extendArguments.length === 1 && t.isObjectExpression(extendArguments[0])) { - arg1 = extendArguments[0]; - } - // Get the name of the extended class and the implementation object when both arguments are present - else if (extendArguments.length === 2) { - if (t.isStringLiteral(extendArguments[0]) && t.isObjectExpression(extendArguments[1])) { - arg0 = extendArguments[0]; - arg1 = extendArguments[1]; - } - } - else { - // don't throw here, because there can be a valid js extend that has nothing to do with NS - return; - throw { - message: "Not enough or too many arguments passed(" + extendArguments.length + ") when trying to extend class in file: " + config.filePath, - errCode: 1 - } - } - } - else { - // don't throw here, because there can be a valid js extend that has nothing to do with NS - return; - throw { - message: "You need to call the extend with parameters. Example: '...extend(\"a.b.C\", {...overrides...})') in file: " + config.filePath, - errCode: 1 - } - } - - className = arg0.value ? arg0.value : ""; - - // Get all methods from the implementation object - var methodsAndInterfaces = _getOverridenMethodsAndImplementedInterfaces(arg1, config); - var overriddenMethodNames = methodsAndInterfaces[0]; - var implementedInterfaces = methodsAndInterfaces[1]; - - var isCorrectExtendClassName = _testJavaProxyName(className); - var isCorrectClassName = _testClassName(className); - if (className && !isCorrectClassName && !isCorrectExtendClassName) { - throw { - message: "The 'extend' you are trying to make has an invalid name. Example: '...extend(\"a.b.C\", {...overrides...})'), file: " + config.filePath, - errCode: 1 - } - } - - var lineToWrite = ""; - if (isCorrectExtendClassName) { - if (config.logger) { - config.logger.info(lineToWrite) - } - - var classNameFromDecorator = isCorrectExtendClassName ? className : ""; - lineToWrite = _generateLineToWrite(classNameFromDecorator, extendClass.reverse().join("."), overriddenMethodNames, "", config.fullPathName, implementedInterfaces); - addCustomExtend(classNameFromDecorator, config.fullPathName, lineToWrite); - - return; - } - - if (config.logger) { - config.logger.info(lineToWrite) - } - var extendInfo = FILE_SEPARATOR + config.filePath + LINE_SEPARATOR + path.node.property.loc.start.line + COLUMN_SEPARATOR + (path.node.property.loc.start.column + columnOffset) + DECLARED_CLASS_SEPARATOR + className; - lineToWrite = _generateLineToWrite(isCorrectExtendClassName ? className : "", extendClass.reverse().join("."), overriddenMethodNames, extendInfo, "", implementedInterfaces); - normalExtendsArr.push(lineToWrite) - } - else { - // don't throw here, because there can be a valid js extend that has nothing to do with NS - return; - throw { - message: "You need to call the extend '...extend(\"extend_name\", {...overrides...})'), file: " + config.filePath, - errCode: 1 - } - } - } + function traverseEs5Extend(path, config) { + var callee = path.parent.callee; + + if (callee) { + var o = callee.object + extendClass = _getWholeName(o); + + var extendArguments = path.parent.arguments; + var arg0, + arg1; + if (extendArguments.length === 1 && t.isObjectExpression(arg0)) { + arg0 = extendArguments[0]; + } + else if (t.isStringLiteral(arg0)) { + + } + + var arg0 = "", + arg1; + if (extendArguments.length) { + // Get implementation object when there is only 1 argument + if (extendArguments.length === 1 && t.isObjectExpression(extendArguments[0])) { + arg1 = extendArguments[0]; + } + // Get the name of the extended class and the implementation object when both arguments are present + else if (extendArguments.length === 2) { + if (t.isStringLiteral(extendArguments[0]) && t.isObjectExpression(extendArguments[1])) { + arg0 = extendArguments[0]; + arg1 = extendArguments[1]; + } + } + else { + // don't throw here, because there can be a valid js extend that has nothing to do with NS + return; + throw { + message: "Not enough or too many arguments passed(" + extendArguments.length + ") when trying to extend class in file: " + config.filePath, + errCode: 1 + } + } + } + else { + // don't throw here, because there can be a valid js extend that has nothing to do with NS + return; + throw { + message: "You need to call the extend with parameters. Example: '...extend(\"a.b.C\", {...overrides...})') in file: " + config.filePath, + errCode: 1 + } + } + + className = arg0.value ? arg0.value : ""; + + // Get all methods from the implementation object + var methodsAndInterfaces = _getOverridenMethodsAndImplementedInterfaces(arg1, config); + var overriddenMethodNames = methodsAndInterfaces[0]; + var implementedInterfaces = methodsAndInterfaces[1]; + + var isCorrectExtendClassName = _testJavaProxyName(className); + var isCorrectClassName = _testClassName(className); + if (className && !isCorrectClassName && !isCorrectExtendClassName) { + throw { + message: "The 'extend' you are trying to make has an invalid name. Example: '...extend(\"a.b.C\", {...overrides...})'), file: " + config.filePath, + errCode: 1 + } + } + + var lineToWrite = ""; + if (isCorrectExtendClassName) { + if (config.logger) { + config.logger.info(lineToWrite) + } + + var classNameFromDecorator = isCorrectExtendClassName ? className : ""; + lineToWrite = _generateLineToWrite(classNameFromDecorator, extendClass.reverse().join("."), overriddenMethodNames, "", config.fullPathName, implementedInterfaces); + addCustomExtend(classNameFromDecorator, config.fullPathName, lineToWrite); + + return; + } + + if (config.logger) { + config.logger.info(lineToWrite) + } + var extendInfo = FILE_SEPARATOR + config.filePath + LINE_SEPARATOR + path.node.property.loc.start.line + COLUMN_SEPARATOR + (path.node.property.loc.start.column + columnOffset) + DECLARED_CLASS_SEPARATOR + className; + lineToWrite = _generateLineToWrite(isCorrectExtendClassName ? className : "", extendClass.reverse().join("."), overriddenMethodNames, extendInfo, "", implementedInterfaces); + normalExtendsArr.push(lineToWrite) + } + else { + // don't throw here, because there can be a valid js extend that has nothing to do with NS + return; + throw { + message: "You need to call the extend '...extend(\"extend_name\", {...overrides...})'), file: " + config.filePath, + errCode: 1 + } + } + } /* * HELPER METHODS */ - function _getOverriddenMethods(node, config) { - var overriddenMethodNames = []; - if (t.isObjectExpression(node)) { - var objectProperties = node.properties; + function _getOverriddenMethods(node, config) { + var overriddenMethodNames = []; + if (t.isObjectExpression(node)) { + var objectProperties = node.properties; - for (var index in objectProperties) { - overriddenMethodNames.push(objectProperties[index].key.name); - } - } + for (var index in objectProperties) { + overriddenMethodNames.push(objectProperties[index].key.name); + } + } - return overriddenMethodNames; - } + return overriddenMethodNames; + } - // NOTE: It's a near-identical method to _getOverridenMethods for optimisation reasons - // we do not want to check for interfaces while creating an interface - // and likewise, we do not want to iterate twice through the impl. object's properties to read the interfaces - function _getOverridenMethodsAndImplementedInterfaces(node, config) { - var result = []; - var overriddenMethodNames = []; - var implementedInterfaces = []; + // NOTE: It's a near-identical method to _getOverridenMethods for optimisation reasons + // we do not want to check for interfaces while creating an interface + // and likewise, we do not want to iterate twice through the impl. object's properties to read the interfaces + function _getOverridenMethodsAndImplementedInterfaces(node, config) { + var result = []; + var overriddenMethodNames = []; + var implementedInterfaces = []; - var interfacesFound = false; + var interfacesFound = false; - if (t.isObjectExpression(node)) { - var objectProperties = node.properties; + if (t.isObjectExpression(node)) { + var objectProperties = node.properties; /* Iterates through all properties of the implementation object, e.g. @@ -433,209 +424,209 @@ var es5_visitors = (function () { will get 'method1' and 'method3' */ - for (var index in objectProperties) { - // if the user has declared interfaces that he is implementing - if (!interfacesFound - && objectProperties[index].key.name.toLowerCase() === "interfaces" - && t.isArrayExpression(objectProperties[index].value)) { - interfacesFound = true; - var interfaces = objectProperties[index].value.elements; - - for (var i in interfaces) { - var interfaceName = _getWholeInterfaceNameFromInterfacesNode(interfaces[i]); - implementedInterfaces.push(interfaceName); - } - } else { - overriddenMethodNames.push(objectProperties[index].key.name) - } - } - } - - result.push(overriddenMethodNames); - result.push(implementedInterfaces); - - return result; - } - - function _getWholeInterfaceNameFromInterfacesNode(node) { - var interfaceName = ""; - - if (t.isMemberExpression(node)) { - interfaceName += _resolveInterfacePath(node.object); - interfaceName += node.property.name; - } - - return interfaceName; - } - - function _resolveInterfacePath(node) { - var subNode = ""; - - if (t.isMemberExpression(node)) { - if (t.isMemberExpression(node.object)) { - subNode += _resolveInterfacePath(node.object); - subNode += node.property.name + "."; - } else { - subNode += node.object.name + "."; - subNode += node.property.name + "."; - } - } - - return subNode; - } - - function _getWholeName(node) { - var arr = [], - isAndroidInterface = false; - - while (node !== undefined) { - if (!t.isMemberExpression(node)) { - if (isAndroidInterface) { - arr.push(node.name) - } - break; - } - - isAndroidInterface = true; - arr.push(node.property.name) - node = node.object - } - - return arr; - } - - function _getArgumentFromNodeAsString(path, count, config) { - - var extClassArr = []; - var extendedClass = _getParrent(path, count, config); - - if (extendedClass) { - if (t.isCallExpression(extendedClass.node)) { - var o = extendedClass.node.arguments[0]; - } - else { - throw { - message: "Node type is not a call expression. File=" + config.filePath + " line=" + path.node.loc.start.line, - errCode: 1 - } - } - } - - extClassArr = _getWholeName(o); - - return extClassArr.reverse().join("."); - } - - function _getDecoratorArgument(path, config, customDecoratorName) { - if (path.parent && t.isCallExpression(path.parent)) { - - if (path.parent.arguments && path.parent.arguments.length > 0) { - - var classNameFromDecorator = path.parent.arguments[0].value - var isCorrectExtendClassName = _testJavaProxyName(classNameFromDecorator); - if (isCorrectExtendClassName) { - return path.parent.arguments[0].value; - } - else { - throw { - message: "The first argument '" + classNameFromDecorator + "' of the " + customDecoratorName + " decorator is not following the right pattern which is: '[namespace.]ClassName'. Example: '" + customDecoratorName + "(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, - errCode: 1 - } - } - } - else { - throw { - message: "No arguments passed to " + customDecoratorName + " decorator. Example: '" + customDecoratorName + "(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, - errCode: 1 - } - } - } - else { - throw { - message: "Decorator " + customDecoratorName + " must be called with parameters: Example: '" + customDecoratorName + "(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, - errCode: 1 - } - } - return undefined; - } - - function _getOverriddenMethodsTypescript(path, count) { - var overriddenMethods = []; - - var cn = _getParrent(path, count) - - // this pattern follows typescript generated syntax - for (var item in cn.node.body) { - var ci = cn.node.body[item]; - if (t.isExpressionStatement(ci)) { - if (t.isAssignmentExpression(ci.expression)) { - if(ci.expression.left.property) { - overriddenMethods.push(ci.expression.left.property.name) - } - } - } - } - - return overriddenMethods; - } - - function _getParrent(node, numberOfParrents, config) { - if (!node) { - throw { - message: "No parent found for node in file: " + config.filePath, - errCode: 1 - } - } - if (numberOfParrents === 0) { - return node; - } - - return _getParrent(node.parentPath, --numberOfParrents) - } - - function _testJavaProxyName(name) { - if (name) { - return /^((\w+\.)+\w+)$/.test(name) - } - return false; - } - - function _testClassName(name) { - if (name && name != "") { - return /^(\w+)$/.test(name) - } - return false; - } - - function _generateLineToWrite(classNameFromDecorator, extendClass, overriddenMethodNames, extendInfo, filePath, implementedInterfaces) { - var lineToWrite = extendClass + "*" + extendInfo.replace(/[\\/\\-]/g, "_") + "*" + overriddenMethodNames + "*" + classNameFromDecorator + "*" + filePath + "*" + (implementedInterfaces ? implementedInterfaces : ""); - return lineToWrite; - } - - function addCustomExtend(param, extendPath, lineToWrite) { - if (customExtendsArrGlobal.indexOf(param) === -1) { - customExtendsArr.push(lineToWrite) - customExtendsArrGlobal.push(param) - } - else { - console.log("Warning: there already is an extend called " + param + ".") - if (extendPath.indexOf("tns_modules") === -1) { - // app folder will take precedence over tns_modules - console.log("Warning: The static binding generator will generate extend from:" + extendPath + " implementation") - customExtendsArr.push(lineToWrite) - customExtendsArrGlobal.push(param) - } - } - } - - function setLineAndColumn(data) { - TYPESCRIPT_EXTEND_STRING = FILE_SEPARATOR + "rnal_ts_helpers_l"+data.line+"_c"+data.column; - } - - return { - es5Visitor: es5Visitor, - setLineAndColumn: setLineAndColumn - } + for (var index in objectProperties) { + // if the user has declared interfaces that he is implementing + if (!interfacesFound + && objectProperties[index].key.name.toLowerCase() === "interfaces" + && t.isArrayExpression(objectProperties[index].value)) { + interfacesFound = true; + var interfaces = objectProperties[index].value.elements; + + for (var i in interfaces) { + var interfaceName = _getWholeInterfaceNameFromInterfacesNode(interfaces[i]); + implementedInterfaces.push(interfaceName); + } + } else { + overriddenMethodNames.push(objectProperties[index].key.name) + } + } + } + + result.push(overriddenMethodNames); + result.push(implementedInterfaces); + + return result; + } + + function _getWholeInterfaceNameFromInterfacesNode(node) { + var interfaceName = ""; + + if (t.isMemberExpression(node)) { + interfaceName += _resolveInterfacePath(node.object); + interfaceName += node.property.name; + } + + return interfaceName; + } + + function _resolveInterfacePath(node) { + var subNode = ""; + + if (t.isMemberExpression(node)) { + if (t.isMemberExpression(node.object)) { + subNode += _resolveInterfacePath(node.object); + subNode += node.property.name + "."; + } else { + subNode += node.object.name + "."; + subNode += node.property.name + "."; + } + } + + return subNode; + } + + function _getWholeName(node) { + var arr = [], + isAndroidInterface = false; + + while (node !== undefined) { + if (!t.isMemberExpression(node)) { + if (isAndroidInterface) { + arr.push(node.name) + } + break; + } + + isAndroidInterface = true; + arr.push(node.property.name) + node = node.object + } + + return arr; + } + + function _getArgumentFromNodeAsString(path, count, config) { + + var extClassArr = []; + var extendedClass = _getParrent(path, count, config); + + if (extendedClass) { + if (t.isCallExpression(extendedClass.node)) { + var o = extendedClass.node.arguments[0]; + } + else { + throw { + message: "Node type is not a call expression. File=" + config.filePath + " line=" + path.node.loc.start.line, + errCode: 1 + } + } + } + + extClassArr = _getWholeName(o); + + return extClassArr.reverse().join("."); + } + + function _getDecoratorArgument(path, config, customDecoratorName) { + if (path.parent && t.isCallExpression(path.parent)) { + + if (path.parent.arguments && path.parent.arguments.length > 0) { + + var classNameFromDecorator = path.parent.arguments[0].value + var isCorrectExtendClassName = _testJavaProxyName(classNameFromDecorator); + if (isCorrectExtendClassName) { + return path.parent.arguments[0].value; + } + else { + throw { + message: "The first argument '" + classNameFromDecorator + "' of the " + customDecoratorName + " decorator is not following the right pattern which is: '[namespace.]ClassName'. Example: '" + customDecoratorName + "(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, + errCode: 1 + } + } + } + else { + throw { + message: "No arguments passed to " + customDecoratorName + " decorator. Example: '" + customDecoratorName + "(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, + errCode: 1 + } + } + } + else { + throw { + message: "Decorator " + customDecoratorName + " must be called with parameters: Example: '" + customDecoratorName + "(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, + errCode: 1 + } + } + return undefined; + } + + function _getOverriddenMethodsTypescript(path, count) { + var overriddenMethods = []; + + var cn = _getParrent(path, count) + + // this pattern follows typescript generated syntax + for (var item in cn.node.body) { + var ci = cn.node.body[item]; + if (t.isExpressionStatement(ci)) { + if (t.isAssignmentExpression(ci.expression)) { + if (ci.expression.left.property) { + overriddenMethods.push(ci.expression.left.property.name) + } + } + } + } + + return overriddenMethods; + } + + function _getParrent(node, numberOfParrents, config) { + if (!node) { + throw { + message: "No parent found for node in file: " + config.filePath, + errCode: 1 + } + } + if (numberOfParrents === 0) { + return node; + } + + return _getParrent(node.parentPath, --numberOfParrents) + } + + function _testJavaProxyName(name) { + if (name) { + return /^((\w+\.)+\w+)$/.test(name) + } + return false; + } + + function _testClassName(name) { + if (name && name != "") { + return /^(\w+)$/.test(name) + } + return false; + } + + function _generateLineToWrite(classNameFromDecorator, extendClass, overriddenMethodNames, extendInfo, filePath, implementedInterfaces) { + var lineToWrite = extendClass + "*" + extendInfo.replace(/[\\/\\-]/g, "_") + "*" + overriddenMethodNames + "*" + classNameFromDecorator + "*" + filePath + "*" + (implementedInterfaces ? implementedInterfaces : ""); + return lineToWrite; + } + + function addCustomExtend(param, extendPath, lineToWrite) { + if (customExtendsArrGlobal.indexOf(param) === -1) { + customExtendsArr.push(lineToWrite) + customExtendsArrGlobal.push(param) + } + else { + console.log("Warning: there already is an extend called " + param + ".") + if (extendPath.indexOf("tns_modules") === -1) { + // app folder will take precedence over tns_modules + console.log("Warning: The static binding generator will generate extend from:" + extendPath + " implementation") + customExtendsArr.push(lineToWrite) + customExtendsArrGlobal.push(param) + } + } + } + + function setLineAndColumn(data) { + TYPESCRIPT_EXTEND_STRING = FILE_SEPARATOR + "rnal_ts_helpers_l" + data.line + "_c" + data.column; + } + + return { + es5Visitor: es5Visitor, + setLineAndColumn: setLineAndColumn + } })(); module.exports = es5_visitors; diff --git a/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity.android.ts b/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity.android.ts new file mode 100644 index 000000000..eaa12bf24 --- /dev/null +++ b/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity.android.ts @@ -0,0 +1,42 @@ +import { setActivityCallbacks, AndroidActivityCallbacks } from "ui/frame"; + +@JavaProxy("org.nativescript.a.MyCustomActivity") +class Activity extends android.support.v7.app.AppCompatActivity { + private _callbacks: AndroidActivityCallbacks; + + protected onCreate(savedInstanceState: android.os.Bundle): void { + if (!this._callbacks) { + setActivityCallbacks(this); + } + + this._callbacks.onCreate(this, savedInstanceState, super.onCreate); + } + + protected onSaveInstanceState(outState: android.os.Bundle): void { + this._callbacks.onSaveInstanceState(this, outState, super.onSaveInstanceState); + } + + protected onStart(): void { + this._callbacks.onStart(this, super.onStart); + } + + protected onStop(): void { + this._callbacks.onStop(this, super.onStop); + } + + protected onDestroy(): void { + this._callbacks.onDestroy(this, super.onDestroy); + } + + public onBackPressed(): void { + this._callbacks.onBackPressed(this, super.onBackPressed); + } + + public onRequestPermissionsResult(requestCode: number, permissions: Array, grantResults: Array): void { + this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined); + } + + protected onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void { + this._callbacks.onActivityResult(this, requestCode, resultCode, data, super.onActivityResult); + } +} \ No newline at end of file diff --git a/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity_ts2.0.10.android.js b/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity_ts2.0.10.android.js new file mode 100644 index 000000000..6e8dd6f43 --- /dev/null +++ b/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity_ts2.0.10.android.js @@ -0,0 +1,41 @@ +"use strict"; +var frame_1 = require("ui/frame"); +var Activity = (function (_super) { + __extends(Activity, _super); + function Activity() { + _super.apply(this, arguments); + } + Activity.prototype.onCreate = function (savedInstanceState) { + if (!this._callbacks) { + frame_1.setActivityCallbacks(this); + } + this._callbacks.onCreate(this, savedInstanceState, _super.prototype.onCreate); + }; + Activity.prototype.onSaveInstanceState = function (outState) { + this._callbacks.onSaveInstanceState(this, outState, _super.prototype.onSaveInstanceState); + }; + Activity.prototype.onStart = function () { + this._callbacks.onStart(this, _super.prototype.onStart); + }; + Activity.prototype.onStop = function () { + this._callbacks.onStop(this, _super.prototype.onStop); + }; + Activity.prototype.onDestroy = function () { + this._callbacks.onDestroy(this, _super.prototype.onDestroy); + }; + Activity.prototype.onBackPressed = function () { + this._callbacks.onBackPressed(this, _super.prototype.onBackPressed); + }; + Activity.prototype.onRequestPermissionsResult = function (requestCode, permissions, grantResults) { + this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined); + }; + Activity.prototype.onActivityResult = function (requestCode, resultCode, data) { + this._callbacks.onActivityResult(this, requestCode, resultCode, data, _super.prototype.onActivityResult); + }; + Activity = __decorate([ + JavaProxy("org.nativescript.a.MyCustomActivity"), + __metadata('design:paramtypes', []) + ], Activity); + return Activity; +}(android.support.v7.app.AppCompatActivity)); +//# sourceMappingURL=myCustomActivity.android.js.map \ No newline at end of file diff --git a/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity_ts2.1.4.android.js b/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity_ts2.1.4.android.js new file mode 100644 index 000000000..0d093ce70 --- /dev/null +++ b/android-static-binding-generator/tests/cases/decorated_extends_ts/app/myCustomActivity_ts2.1.4.android.js @@ -0,0 +1,41 @@ +"use strict"; +var frame_1 = require("ui/frame"); +var Activity = (function (_super) { + __extends(Activity, _super); + function Activity() { + return _super.apply(this, arguments) || this; + } + Activity.prototype.onCreate = function (savedInstanceState) { + if (!this._callbacks) { + frame_1.setActivityCallbacks(this); + } + this._callbacks.onCreate(this, savedInstanceState, _super.prototype.onCreate); + }; + Activity.prototype.onSaveInstanceState = function (outState) { + this._callbacks.onSaveInstanceState(this, outState, _super.prototype.onSaveInstanceState); + }; + Activity.prototype.onStart = function () { + this._callbacks.onStart(this, _super.prototype.onStart); + }; + Activity.prototype.onStop = function () { + this._callbacks.onStop(this, _super.prototype.onStop); + }; + Activity.prototype.onDestroy = function () { + this._callbacks.onDestroy(this, _super.prototype.onDestroy); + }; + Activity.prototype.onBackPressed = function () { + this._callbacks.onBackPressed(this, _super.prototype.onBackPressed); + }; + Activity.prototype.onRequestPermissionsResult = function (requestCode, permissions, grantResults) { + this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined); + }; + Activity.prototype.onActivityResult = function (requestCode, resultCode, data) { + this._callbacks.onActivityResult(this, requestCode, resultCode, data, _super.prototype.onActivityResult); + }; + return Activity; +}(android.support.v7.app.AppCompatActivity)); +Activity = __decorate([ + JavaProxy("org.nativescript.a.MyCustomActivity"), + __metadata("design:paramtypes", []) +], Activity); +//# sourceMappingURL=myCustomActivity.android.js.map \ No newline at end of file diff --git a/android-static-binding-generator/tests/cases/decorated_extends_ts/internal/ts_helpers.js b/android-static-binding-generator/tests/cases/decorated_extends_ts/internal/ts_helpers.js new file mode 100644 index 000000000..2175f563f --- /dev/null +++ b/android-static-binding-generator/tests/cases/decorated_extends_ts/internal/ts_helpers.js @@ -0,0 +1,124 @@ +(function() { + var __extends_ts = function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); + }; + + var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length; + var r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + + if (typeof global.Reflect === "object" && typeof global.Reflect.decorate === "function") { + r = global.Reflect.decorate(decorators, target, key, desc); + } + else { + for (var i = decorators.length - 1; i >= 0; i--) { + if (d = decorators[i]) { + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + } + } + } + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + + var __native = function(thiz) { + var result = thiz.__proto__; + + for (var prop in thiz) + { + if (thiz.hasOwnProperty(prop)) + { + thiz.__proto__[prop] = thiz[prop]; + delete thiz[prop]; + } + } + + thiz.constructor = undefined; + thiz.__proto__ = undefined; + Object.freeze(thiz); + Object.preventExtensions(thiz) + return result; + }; + + var __extends = function(Child, Parent) { + + if (Parent.extend) { + if (Parent.__isPrototypeImplementationObject) { + throw new Error("Can not extend an already extended native object."); + } + + function extend(child, parent) { + __log("TS extend called"); + if (!child.__extended) { + child.__extended = parent.extend(child.name, child.prototype); + } + + return child.__extended; + }; + + Parent.__activityExtend = function(parent, name, implementationObject) { + __log("__activityExtend called"); + return parent.extend(name, implementationObject); + }; + + Parent.call = function(thiz) { + var Extended = extend(thiz.__proto__.__child, thiz.__proto__.__parent); + if (arguments.length > 1) + { + thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(Array.prototype.slice.call(arguments, 1)))); + } + else + { + thiz.__proto__ = new Extended(); + } + }; + + Parent.apply = function(thiz, args) { + var Extended = extend(thiz.__proto__.__child, thiz.__proto__.__parent); + if (args && args.length > 0) + { + thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(args))); + } + else + { + thiz.__proto__ = new Extended(); + } + }; + } + + __extends_ts(Child, Parent); + + + if (Parent.extend) { + Child.__isPrototypeImplementationObject = true; + Child.__proto__ = Parent; + Child.prototype.__parent = Parent; + Child.prototype.__child = Child; + } + } + + function JavaProxy(className) { + return function (target) { + var extended = target.extend(className, target.prototype) + extended.name = className; + return extended; + }; + } + + function Interfaces(interfacesArr) { + return function (target) { + if(interfacesArr instanceof Array) { + // attach interfaces: [] to the object + target.prototype.interfaces = interfacesArr; + } + } + } + + global.__native = __native; + global.__extends = __extends; + global.__decorate = __decorate; + global.JavaProxy = JavaProxy; + global.Interfaces = Interfaces; +})() \ No newline at end of file diff --git a/android-static-binding-generator/tests/specs/ast-parser-tests.spec.js b/android-static-binding-generator/tests/specs/ast-parser-tests.spec.js index 42cbf0b2a..2b0700caa 100644 --- a/android-static-binding-generator/tests/specs/ast-parser-tests.spec.js +++ b/android-static-binding-generator/tests/specs/ast-parser-tests.spec.js @@ -165,6 +165,40 @@ describe("parser/js_parser tests", function () { }); }); + + it("Generated metadata for bindings should return proper JavaClass name despite emitted TS ES5 code", + function (done) { + let input = path.normalize(prefix + "/decorated_extends_ts/app"), + jsFilesParameter = path.normalize(prefix + "/decorated_extends_ts/jsFiles.txt"), + output = prefix + "/decorated_extends_ts/bindings.txt"; + + const newClassName = "org.nativescript.a.MyCustomActivity"; + + clearOutput(output, jsFilesParameter); + + execGradle(input, output, jsFilesParameter, function (error, stdout, stderr) { + if (error) { + console.error(`exec error: ${error}`); + return; + } + + logExecResult(stdout, stderr) + + let bindingsContent = fs.readFileSync(output, "utf-8").toString().trim().split('\n'); + + for (let line of bindingsContent) { + var lineParts = line.split("*"); + var tsExtendsPart = lineParts[1]; + expect(tsExtendsPart).toBeFalsy(); + + var newClassNamePart = lineParts[3]; + expect(newClassNamePart).toBe(newClassName); + } + + done(); + }); + }); + it("Generate valid metadata for bindings where multiple interfaces are implemented using array", function (done) { let input = path.normalize(prefix + "/extends_with_interfaces/app"), jsFilesParameter = path.normalize(prefix + "/extends_with_interfaces/jsFiles.txt"),