diff --git a/circle.yml b/circle.yml index adb198bef..868af9c4e 100644 --- a/circle.yml +++ b/circle.yml @@ -1,7 +1,8 @@ machine: node: version: 4.1.0 - + ruby: + version: 2.1.2 test: override: - echo "Skipping tests for now" diff --git a/dist/src/plugins/touchid.js b/dist/src/plugins/touchid.js index 1e95800df..c0dc1ef84 100644 --- a/dist/src/plugins/touchid.js +++ b/dist/src/plugins/touchid.js @@ -1,3 +1,4 @@ +"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); @@ -50,6 +51,6 @@ var TouchID = (function () { __metadata('design:paramtypes', []) ], TouchID); return TouchID; -})(); +}()); exports.TouchID = TouchID; //# sourceMappingURL=touchid.js.map \ No newline at end of file diff --git a/dist/src/plugins/touchid.js.map b/dist/src/plugins/touchid.js.map index 020aaa14d..a2f8569ca 100644 --- a/dist/src/plugins/touchid.js.map +++ b/dist/src/plugins/touchid.js.map @@ -1 +1 @@ -{"version":3,"file":"touchid.js","sourceRoot":"","sources":["../../../src/plugins/touchid.ts"],"names":["TouchID","TouchID.constructor","TouchID.isAvailable","TouchID.verifyFingerprint","TouchID.verifyFingerprintWithCustomPasswordFallback","TouchID.verifyFingerprintWithCustomPasswordFallbackAndEnterPasswordLabel"],"mappings":";;;;;;;;;AAAA,uBAA8B,UAAU,CAAC,CAAA;AAEzC;IAAAA;IAkBAC,CAACA;IAVCD,6BAAWA,GADXA,cACeE,CAACA;;IAGTF,yBAAiBA,GADxBA,UACyBA,OAAcA,IAAGG,CAACA;IAGpCH,mDAA2CA,GADlDA,UACmDA,OAAcA,IAAGI,CAACA;IAG9DJ,wEAAgEA,GADvEA,UACwEA,OAAcA,EAAEA,kBAAyBA,IAAGK,CAACA;IAVrHL;QAACA,gBAAOA,EAAEA;;;;OACVA,gCAAWA,QAAKA;IAEhBA;QAACA,gBAAOA,EAAEA;;;;OACHA,4BAAiBA,QAAmBA;IAE3CA;QAACA,gBAAOA,EAAEA;;;;OACHA,sDAA2CA,QAAmBA;IAErEA;QAACA,gBAAOA,EAAEA;;;;OACHA,2EAAgEA,QAA8CA;IAjBvHA;QAACA,eAAMA,CAACA;YACNA,IAAIA,EAAEA,SAASA;YACfA,MAAMA,EAAEA,yBAAyBA;YACjCA,SAASA,EAAEA,iBAAiBA;YAC5BA,IAAIA,EAAEA,2DAA2DA;SAClEA,CAACA;;gBAaDA;IAADA,cAACA;AAADA,CAACA,AAlBD,IAkBC;AAZY,eAAO,UAYnB,CAAA"} \ No newline at end of file +{"version":3,"file":"touchid.js","sourceRoot":"","sources":["../../../src/plugins/touchid.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,uBAA8B,UAAU,CAAC,CAAA;AAQzC;IAAA;IAYA,CAAC;IAVQ,6BAAW,GAAlB,cAAsB,CAAC;;IAGhB,yBAAiB,GAAxB,UAAyB,OAAc,IAAG,CAAC;IAGpC,mDAA2C,GAAlD,UAAmD,OAAc,IAAG,CAAC;IAG9D,wEAAgE,GAAvE,UAAwE,OAAc,EAAE,kBAAyB,IAAG,CAAC;IAVrH;QAAC,gBAAO,EAAE;;;;8CAAA;IAGV;QAAC,gBAAO,EAAE;;;;0CAAA;IAGV;QAAC,gBAAO,EAAE;;;;oEAAA;IAGV;QAAC,gBAAO,EAAE;;;;yFAAA;IAhBZ;QAAC,eAAM,CAAC;YACN,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,yBAAyB;YACjC,SAAS,EAAE,iBAAiB;YAC5B,IAAI,EAAE,2DAA2D;SAClE,CAAC;;eAAA;IAaF,cAAC;AAAD,CAAC,AAZD,IAYC;AAZY,eAAO,UAYnB,CAAA"} \ No newline at end of file diff --git a/scripts/docs/typescript-package/index.js b/scripts/docs/typescript-package/index.js old mode 100644 new mode 100755 index d31dc7744..4c7046bc5 --- a/scripts/docs/typescript-package/index.js +++ b/scripts/docs/typescript-package/index.js @@ -1,5 +1,3 @@ -//require('../../tools/transpiler/index.js').init(); - var basePackage = require('dgeni-packages/base'); var Package = require('dgeni').Package; var path = require('canonical-path'); @@ -14,7 +12,8 @@ module.exports = new Package('typescript-parsing', [basePackage]) .factory(require('./services/tsParser/getFileInfo')) .factory(require('./services/tsParser/getExportDocType')) .factory(require('./services/tsParser/getContent')) -.factory(require('./services/tsParser/getDirectiveInfo')) + +.factory(require('./services/convertPrivateClassesToInterfaces')) .factory('EXPORT_DOC_TYPES', function() { return [ @@ -23,6 +22,7 @@ module.exports = new Package('typescript-parsing', [basePackage]) 'function', 'var', 'const', + 'let', 'enum', 'type-alias' ]; @@ -45,16 +45,18 @@ module.exports = new Package('typescript-parsing', [basePackage]) computeIdsProcessor.idTemplates.push({ docTypes: ['member'], idTemplate: '${classDoc.id}.${name}', - getAliases: function(doc) { return [doc.id]; } + getAliases: function(doc) { + return doc.classDoc.aliases.map(function(alias) { return alias + '.' + doc.name; }); + } }); computePathsProcessor.pathTemplates.push({ docTypes: ['member'], - pathTemplate: '${classDoc.path}/${name}', + pathTemplate: '${classDoc.path}#${name}', getOutputPath: function() {} // These docs are not written to their own file, instead they are part of their class doc }); - var MODULES_DOCS_PATH = 'docs'; + var MODULES_DOCS_PATH = 'partials/modules'; computePathsProcessor.pathTemplates.push({ docTypes: ['module'], diff --git a/scripts/docs/typescript-package/mocks/mockPackage.js b/scripts/docs/typescript-package/mocks/mockPackage.js old mode 100644 new mode 100755 diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/ignoreExportsMatching.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/ignoreExportsMatching.ts old mode 100644 new mode 100755 diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/interfaces.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/interfaces.ts old mode 100644 new mode 100755 diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/orderingOfMembers.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/orderingOfMembers.ts old mode 100644 new mode 100755 diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/privateModule.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/privateModule.ts new file mode 100755 index 000000000..d4c6ef610 --- /dev/null +++ b/scripts/docs/typescript-package/mocks/readTypeScriptModules/privateModule.ts @@ -0,0 +1 @@ +export var x = 10; \ No newline at end of file diff --git a/scripts/docs/typescript-package/mocks/readTypeScriptModules/publicModule.ts b/scripts/docs/typescript-package/mocks/readTypeScriptModules/publicModule.ts new file mode 100755 index 000000000..0fbae4ad2 --- /dev/null +++ b/scripts/docs/typescript-package/mocks/readTypeScriptModules/publicModule.ts @@ -0,0 +1,3 @@ +export { x as y} from './privateModule'; + +export abstract class AbstractClass {} \ No newline at end of file diff --git a/scripts/docs/typescript-package/mocks/tsParser/importedSrc.ts b/scripts/docs/typescript-package/mocks/tsParser/importedSrc.ts old mode 100644 new mode 100755 diff --git a/scripts/docs/typescript-package/mocks/tsParser/testSrc.ts b/scripts/docs/typescript-package/mocks/tsParser/testSrc.ts old mode 100644 new mode 100755 diff --git a/scripts/docs/typescript-package/processors/readTypeScriptModules.js b/scripts/docs/typescript-package/processors/readTypeScriptModules.js old mode 100644 new mode 100755 index b8243b982..3739b7b33 --- a/scripts/docs/typescript-package/processors/readTypeScriptModules.js +++ b/scripts/docs/typescript-package/processors/readTypeScriptModules.js @@ -4,8 +4,7 @@ var _ = require('lodash'); var ts = require('typescript'); module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, - getExportDocType, getContent, - getDirectiveInfo, log) { + getExportDocType, getContent, createDocMessage, log) { return { $runAfter: ['files-read'], @@ -61,13 +60,26 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, // If the symbol is an Alias then for most things we want the original resolved symbol var resolvedExport = exportSymbol.resolvedSymbol || exportSymbol; + + // If the resolved symbol contains no declarations then it is invalid + // (probably an abstract class) + // For the moment we are just going to ignore such exports + // TODO: find a way of generating docs for them + if (!resolvedExport.declarations) return; + var exportDoc = createExportDoc(exportSymbol.name, resolvedExport, moduleDoc, basePath, parseInfo.typeChecker); log.debug('>>>> EXPORT: ' + exportDoc.name + ' (' + exportDoc.docType + ') from ' + moduleDoc.id); + // Add this export doc to its module doc + moduleDoc.exports.push(exportDoc); + docs.push(exportDoc); + + exportDoc.members = []; + exportDoc.statics = []; + // Generate docs for each of the export's members if (resolvedExport.flags & ts.SymbolFlags.HasMembers) { - exportDoc.members = []; for(var memberName in resolvedExport.members) { // FIXME(alexeagle): why do generic type params appear in members? if (memberName === 'T') { @@ -92,26 +104,36 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, exportDoc.newMember = memberDoc; } } + } - if (sortClassMembers) { - exportDoc.members.sort(function(a, b) { - if (a.name > b.name) return 1; - if (a.name < b.name) return -1; - return 0; - }); + if (exportDoc.docType === 'enum') { + for(var memberName in resolvedExport.exports) { + log.silly('>>>>>> member: ' + memberName + ' from ' + exportDoc.id + ' in ' + moduleDoc.id); + var memberSymbol = resolvedExport.exports[memberName]; + var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker); + docs.push(memberDoc); + exportDoc.members.push(memberDoc); + } + } else if (resolvedExport.flags & ts.SymbolFlags.HasExports) { + for (var exported in resolvedExport.exports) { + if (exported === 'prototype') continue; + if (hidePrivateMembers && exported.charAt(0) === '_') continue; + var memberSymbol = resolvedExport.exports[exported]; + var memberDoc = createMemberDoc(memberSymbol, exportDoc, basePath, parseInfo.typeChecker); + memberDoc.isStatic = true; + docs.push(memberDoc); + exportDoc.statics.push(memberDoc); } } - if (exportDoc.docType == 'enum') { - exportDoc.members = []; - for (var etype in resolvedExport.exports) { - exportDoc.members.push(etype); - } + if (sortClassMembers) { + exportDoc.members.sort(function(a, b) { + if (a.name > b.name) return 1; + if (a.name < b.name) return -1; + return 0; + }); } - // Add this export doc to its module doc - moduleDoc.exports.push(exportDoc); - docs.push(exportDoc); }); }); } @@ -120,10 +142,12 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, function createModuleDoc(moduleSymbol, basePath) { var id = moduleSymbol.name.replace(/^"|"$/g, ''); + var name = id.split('/').pop(); var moduleDoc = { docType: 'module', + name: name, id: id, - aliases: [id], + aliases: [id, name], moduleTree: moduleSymbol, content: getContent(moduleSymbol), exports: [], @@ -136,6 +160,7 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, function createExportDoc(name, exportSymbol, moduleDoc, basePath, typeChecker) { var typeParamString = ''; var heritageString = ''; + var typeDefinition = ''; exportSymbol.declarations.forEach(function(decl) { var sourceFile = ts.getSourceFileOfNode(decl); @@ -144,6 +169,10 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, typeParamString = '<' + getText(sourceFile, decl.typeParameters) + '>'; } + if (decl.symbol.flags & ts.SymbolFlags.TypeAlias) { + typeDefinition = getText(sourceFile, decl.type); + } + if (decl.heritageClauses) { decl.heritageClauses.forEach(function(heritage) { @@ -173,24 +202,43 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, var exportDoc = { docType: getExportDocType(exportSymbol), + exportSymbol: exportSymbol, name: name, id: moduleDoc.id + '/' + name, typeParams: typeParamString, heritage: heritageString, + decorators: getDecorators(exportSymbol), aliases: aliasNames, moduleDoc: moduleDoc, content: getContent(exportSymbol), fileInfo: getFileInfo(exportSymbol, basePath), - location: getLocation(exportSymbol), - directiveInfo: getDirectiveInfo(exportSymbol) + location: getLocation(exportSymbol) }; + if (exportDoc.docType === 'var' || exportDoc.docType === 'const' || exportDoc.docType === 'let') { + exportDoc.symbolTypeName = exportSymbol.valueDeclaration.type && + exportSymbol.valueDeclaration.type.typeName && + exportSymbol.valueDeclaration.type.typeName.text; + } + + if (exportDoc.docType === 'type-alias') { + exportDoc.returnType = getReturnType(typeChecker, exportSymbol); + } + if(exportSymbol.flags & ts.SymbolFlags.Function) { exportDoc.parameters = getParameters(typeChecker, exportSymbol); } if(exportSymbol.flags & ts.SymbolFlags.Value) { exportDoc.returnType = getReturnType(typeChecker, exportSymbol); } + if (exportSymbol.flags & ts.SymbolFlags.TypeAlias) { + exportDoc.typeDefinition = typeDefinition; + } + + // Compute the original module name from the relative file path + exportDoc.originalModule = exportDoc.fileInfo.relativePath + .replace(new RegExp('\.' + exportDoc.fileInfo.extension + '$'), ''); + return exportDoc; } @@ -199,6 +247,7 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, docType: 'member', classDoc: classDoc, name: memberSymbol.name, + decorators: getDecorators(memberSymbol), content: getContent(memberSymbol), fileInfo: getFileInfo(memberSymbol, basePath), location: getLocation(memberSymbol) @@ -242,6 +291,45 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, return memberDoc; } + + function getDecorators(symbol) { + + var declaration = symbol.valueDeclaration || symbol.declarations[0]; + var sourceFile = ts.getSourceFileOfNode(declaration); + + var decorators = declaration.decorators && declaration.decorators.map(function(decorator) { + decorator = decorator.expression; + return { + name: decorator.expression ? decorator.expression.text : decorator.text, + arguments: decorator.arguments && decorator.arguments.map(function(argument) { + return getText(sourceFile, argument).trim(); + }), + argumentInfo: decorator.arguments && decorator.arguments.map(function(argument) { + return parseArgument(argument); + }), + expression: decorator + }; + }); + return decorators; + } + + function parseProperties(properties) { + var result = {}; + _.forEach(properties, function(property) { + result[property.name.text] = parseArgument(property.initializer); + }); + return result; + } + + function parseArgument(argument) { + if (argument.text) return argument.text; + if (argument.properties) return parseProperties(argument.properties); + if (argument.elements) return argument.elements.map(function(element) { return element.text; }); + var sourceFile = ts.getSourceFileOfNode(argument); + var text = getText(sourceFile, argument).trim(); + return text; + } + function getParameters(typeChecker, symbol) { var declaration = symbol.valueDeclaration || symbol.declarations[0]; var sourceFile = ts.getSourceFileOfNode(declaration); @@ -252,7 +340,11 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, ' at line ' + location.start.line); } return declaration.parameters.map(function(parameter) { - var paramText = getText(sourceFile, parameter.name); + var paramText = ''; + if (parameter.dotDotDotToken) { + paramText += '...'; + } + paramText += getText(sourceFile, parameter.name); if (parameter.questionToken || parameter.initializer) { paramText += '?'; } @@ -260,6 +352,9 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, paramText += ':' + getType(sourceFile, parameter.type); } else { paramText += ': any'; + if (parameter.dotDotDotToken) { + paramText += '[]'; + } } return paramText.trim(); }); @@ -280,6 +375,14 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, var sourceFile = ts.getSourceFileOfNode(declaration); if (declaration.type) { return getType(sourceFile, declaration.type).trim(); + } else if (declaration.initializer) { + // The symbol does not have a "type" but it is being initialized + // so we can deduce the type of from the initializer (mostly). + if (declaration.initializer.expression) { + return declaration.initializer.expression.text.trim(); + } else { + return getType(sourceFile, declaration.initializer).trim(); + } } } @@ -302,8 +405,8 @@ module.exports = function readTypeScriptModules(tsParser, modules, getFileInfo, function getType(sourceFile, type) { var text = getText(sourceFile, type); while (text.indexOf(".") >= 0) { - // Keep namespaced symbols in Rx - if (text.match(/^\s*Rx\./)) break; + // Keep namespaced symbols in RxNext + if (text.match(/^\s*RxNext\./)) break; // handle the case List -> List text = text.replace(/([^.<]*)\.([^>]*)/, "$2"); } diff --git a/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js b/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js old mode 100644 new mode 100755 index 53d959c70..1224acc58 --- a/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js +++ b/scripts/docs/typescript-package/processors/readTypeScriptModules.spec.js @@ -13,6 +13,27 @@ describe('readTypeScriptModules', function() { processor.basePath = path.resolve(__dirname, '../mocks/readTypeScriptModules'); }); + describe('exportDocs', function() { + it('should provide the original module if the export is re-exported', function() { + processor.sourceFiles = [ 'publicModule.ts' ]; + var docs = []; + processor.$process(docs); + + var exportedDoc = docs[1]; + expect(exportedDoc.originalModule).toEqual('privateModule'); + }); + + it('should include exported abstract classes', function() { + processor.sourceFiles = [ 'publicModule.ts' ]; + var docs = []; + processor.$process(docs); + + var exportedDoc = docs[2]; + expect(exportedDoc.name).toEqual('AbstractClass'); + }); + + }); + describe('ignoreExportsMatching', function() { it('should ignore exports that match items in the `ignoreExportsMatching` property', function() { diff --git a/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.js b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.js new file mode 100755 index 000000000..69afdea89 --- /dev/null +++ b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.js @@ -0,0 +1,31 @@ +var _ = require('lodash'); + +module.exports = function convertPrivateClassesToInterfaces() { + return function(exportDocs, addInjectableReference) { + _.forEach(exportDocs, function(exportDoc) { + + // Search for classes with a constructor marked as `@internal` + if (exportDoc.docType === 'class' && exportDoc.constructorDoc && exportDoc.constructorDoc.internal) { + + // Convert this class to an interface with no constructor + exportDoc.docType = 'interface'; + exportDoc.constructorDoc = null; + + if (exportDoc.heritage) { + // convert the heritage since interfaces use `extends` not `implements` + exportDoc.heritage = exportDoc.heritage.replace('implements', 'extends'); + } + + if (addInjectableReference) { + // Add the `declare var SomeClass extends InjectableReference` construct + exportDocs.push({ + docType: 'var', + name: exportDoc.name, + id: exportDoc.id, + returnType: 'InjectableReference' + }); + } + } + }); + }; +}; diff --git a/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.spec.js b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.spec.js new file mode 100755 index 000000000..bc4ed7411 --- /dev/null +++ b/scripts/docs/typescript-package/services/convertPrivateClassesToInterfaces.spec.js @@ -0,0 +1,76 @@ +var mockPackage = require('../mocks/mockPackage'); +var Dgeni = require('dgeni'); +var _ = require('lodash'); + +describe('readTypeScriptModules', function() { + var dgeni, injector, convertPrivateClassesToInterfaces; + + beforeEach(function() { + dgeni = new Dgeni([mockPackage()]); + injector = dgeni.configureInjector(); + convertPrivateClassesToInterfaces = injector.get('convertPrivateClassesToInterfaces'); + }); + + it('should convert private class docs to interface docs', function() { + var docs = [ + { + docType: 'class', + name: 'privateClass', + id: 'privateClass', + constructorDoc: { internal: true } + } + ]; + convertPrivateClassesToInterfaces(docs, false); + expect(docs[0].docType).toEqual('interface'); + }); + + + it('should not touch non-private class docs', function() { + var docs = [ + { + docType: 'class', + name: 'privateClass', + id: 'privateClass', + constructorDoc: { } + } + ]; + convertPrivateClassesToInterfaces(docs, false); + expect(docs[0].docType).toEqual('class'); + }); + + + it('should convert the heritage since interfaces use `extends` not `implements`', function() { + var docs = [ + { + docType: 'class', + name: 'privateClass', + id: 'privateClass', + constructorDoc: { internal: true }, + heritage: 'implements parentInterface' + } + ]; + convertPrivateClassesToInterfaces(docs, false); + expect(docs[0].heritage).toEqual('extends parentInterface'); + }); + + + it('should add new injectable reference types, if specified, to the passed in collection', function() { + var docs = [ + { + docType: 'class', + name: 'privateClass', + id: 'privateClass', + constructorDoc: { internal: true }, + heritage: 'implements parentInterface' + } + ]; + convertPrivateClassesToInterfaces(docs, true); + expect(docs[1]).toEqual({ + docType : 'var', + name : 'privateClass', + id : 'privateClass', + returnType : 'InjectableReference' + }); + }); + +}); diff --git a/scripts/docs/typescript-package/services/modules.js b/scripts/docs/typescript-package/services/modules.js old mode 100644 new mode 100755 diff --git a/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js b/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js old mode 100644 new mode 100755 index 0dc488b0b..9aaa67007 --- a/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js +++ b/scripts/docs/typescript-package/services/tsParser/createCompilerHost.js @@ -57,18 +57,23 @@ module.exports = function createCompilerHost(log) { getNewLine: function() { return ts.sys.newLine; }, - fileExists: function(fileName) { - var resolvedPath = path.resolve(baseDir, fileName); - try { - fs.statSync(resolvedPath); - return true; - } catch (e) { - return false; + fileExists: function (fileName) { + var text, resolvedPath, resolvedPathWithExt; + + // Strip off the extension and resolve relative to the baseDir + baseFilePath = fileName.replace(/\.[^.]+$/, ''); + resolvedPath = path.resolve(baseDir, baseFilePath); + + // Iterate through each possible extension and return the first source file that is actually found + for(var i=0; i