http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js new file mode 100644 index 0000000..e477a89 --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/ConfigParser.js @@ -0,0 +1,568 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +/* jshint sub:true */ + +var et = require('elementtree'), + xml= require('../util/xml-helpers'), + CordovaError = require('../CordovaError/CordovaError'), + fs = require('fs'), + events = require('../events'); + + +/** Wraps a config.xml file */ +function ConfigParser(path) { + this.path = path; + try { + this.doc = xml.parseElementtreeSync(path); + this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc); + et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0'); + } catch (e) { + console.error('Parsing '+path+' failed'); + throw e; + } + var r = this.doc.getroot(); + if (r.tag !== 'widget') { + throw new CordovaError(path + ' has incorrect root node name (expected "widget", was "' + r.tag + '")'); + } +} + +function getNodeTextSafe(el) { + return el && el.text && el.text.trim(); +} + +function findOrCreate(doc, name) { + var ret = doc.find(name); + if (!ret) { + ret = new et.Element(name); + doc.getroot().append(ret); + } + return ret; +} + +function getCordovaNamespacePrefix(doc){ + var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib); + var prefix = 'cdv'; + for (var j = 0; j < rootAtribs.length; j++ ) { + if(rootAtribs[j].indexOf('xmlns:') === 0 && + doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0'){ + var strings = rootAtribs[j].split(':'); + prefix = strings[1]; + break; + } + } + return prefix; +} + +/** + * Finds the value of an element's attribute + * @param {String} attributeName Name of the attribute to search for + * @param {Array} elems An array of ElementTree nodes + * @return {String} + */ +function findElementAttributeValue(attributeName, elems) { + + elems = Array.isArray(elems) ? elems : [ elems ]; + + var value = elems.filter(function (elem) { + return elem.attrib.name.toLowerCase() === attributeName.toLowerCase(); + }).map(function (filteredElems) { + return filteredElems.attrib.value; + }).pop(); + + return value ? value : ''; +} + +ConfigParser.prototype = { + getAttribute: function(attr) { + return this.doc.getroot().attrib[attr]; + }, + + packageName: function(id) { + return this.getAttribute('id'); + }, + setPackageName: function(id) { + this.doc.getroot().attrib['id'] = id; + }, + android_packageName: function() { + return this.getAttribute('android-packageName'); + }, + android_activityName: function() { + return this.getAttribute('android-activityName'); + }, + ios_CFBundleIdentifier: function() { + return this.getAttribute('ios-CFBundleIdentifier'); + }, + name: function() { + return getNodeTextSafe(this.doc.find('name')); + }, + setName: function(name) { + var el = findOrCreate(this.doc, 'name'); + el.text = name; + }, + description: function() { + return getNodeTextSafe(this.doc.find('description')); + }, + setDescription: function(text) { + var el = findOrCreate(this.doc, 'description'); + el.text = text; + }, + version: function() { + return this.getAttribute('version'); + }, + windows_packageVersion: function() { + return this.getAttribute('windows-packageVersion'); + }, + android_versionCode: function() { + return this.getAttribute('android-versionCode'); + }, + ios_CFBundleVersion: function() { + return this.getAttribute('ios-CFBundleVersion'); + }, + setVersion: function(value) { + this.doc.getroot().attrib['version'] = value; + }, + author: function() { + return getNodeTextSafe(this.doc.find('author')); + }, + getGlobalPreference: function (name) { + return findElementAttributeValue(name, this.doc.findall('preference')); + }, + setGlobalPreference: function (name, value) { + var pref = this.doc.find('preference[@name="' + name + '"]'); + if (!pref) { + pref = new et.Element('preference'); + pref.attrib.name = name; + this.doc.getroot().append(pref); + } + pref.attrib.value = value; + }, + getPlatformPreference: function (name, platform) { + return findElementAttributeValue(name, this.doc.findall('platform[@name=\'' + platform + '\']/preference')); + }, + getPreference: function(name, platform) { + + var platformPreference = ''; + + if (platform) { + platformPreference = this.getPlatformPreference(name, platform); + } + + return platformPreference ? platformPreference : this.getGlobalPreference(name); + + }, + /** + * Returns all resources for the platform specified. + * @param {String} platform The platform. + * @param {string} resourceName Type of static resources to return. + * "icon" and "splash" currently supported. + * @return {Array} Resources for the platform specified. + */ + getStaticResources: function(platform, resourceName) { + var ret = [], + staticResources = []; + if (platform) { // platform specific icons + this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function(elt){ + elt.platform = platform; // mark as platform specific resource + staticResources.push(elt); + }); + } + // root level resources + staticResources = staticResources.concat(this.doc.findall(resourceName)); + // parse resource elements + var that = this; + staticResources.forEach(function (elt) { + var res = {}; + res.src = elt.attrib.src; + res.target = elt.attrib.target || undefined; + res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix+':density'] || elt.attrib['gap:density']; + res.platform = elt.platform || null; // null means icon represents default icon (shared between platforms) + res.width = +elt.attrib.width || undefined; + res.height = +elt.attrib.height || undefined; + + // default icon + if (!res.width && !res.height && !res.density) { + ret.defaultResource = res; + } + ret.push(res); + }); + + /** + * Returns resource with specified width and/or height. + * @param {number} width Width of resource. + * @param {number} height Height of resource. + * @return {Resource} Resource object or null if not found. + */ + ret.getBySize = function(width, height) { + return ret.filter(function(res) { + if (!res.width && !res.height) { + return false; + } + return ((!res.width || (width == res.width)) && + (!res.height || (height == res.height))); + })[0] || null; + }; + + /** + * Returns resource with specified density. + * @param {string} density Density of resource. + * @return {Resource} Resource object or null if not found. + */ + ret.getByDensity = function(density) { + return ret.filter(function(res) { + return res.density == density; + })[0] || null; + }; + + /** Returns default icons */ + ret.getDefault = function() { + return ret.defaultResource; + }; + + return ret; + }, + + /** + * Returns all icons for specific platform. + * @param {string} platform Platform name + * @return {Resource[]} Array of icon objects. + */ + getIcons: function(platform) { + return this.getStaticResources(platform, 'icon'); + }, + + /** + * Returns all splash images for specific platform. + * @param {string} platform Platform name + * @return {Resource[]} Array of Splash objects. + */ + getSplashScreens: function(platform) { + return this.getStaticResources(platform, 'splash'); + }, + + /** + * Returns all resource-files for a specific platform. + * @param {string} platform Platform name + * @return {Resource[]} Array of resource file objects. + */ + getFileResources: function(platform) { + var fileResources = []; + + if (platform) { // platform specific resources + fileResources = this.doc.findall('platform[@name=\'' + platform + '\']/resource-file').map(function(tag) { + return { + platform: platform, + src: tag.attrib.src, + target: tag.attrib.target, + versions: tag.attrib.versions, + deviceTarget: tag.attrib['device-target'], + arch: tag.attrib.arch + }; + }); + } + + return fileResources; + }, + + /** + * Returns all hook scripts for the hook type specified. + * @param {String} hook The hook type. + * @param {Array} platforms Platforms to look for scripts into (root scripts will be included as well). + * @return {Array} Script elements. + */ + getHookScripts: function(hook, platforms) { + var self = this; + var scriptElements = self.doc.findall('./hook'); + + if(platforms) { + platforms.forEach(function (platform) { + scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook')); + }); + } + + function filterScriptByHookType(el) { + return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook; + } + + return scriptElements.filter(filterScriptByHookType); + }, + /** + * Returns a list of plugin (IDs). + * + * This function also returns any plugin's that + * were defined using the legacy <feature> tags. + * @return {string[]} Array of plugin IDs + */ + getPluginIdList: function () { + var plugins = this.doc.findall('plugin'); + var result = plugins.map(function(plugin){ + return plugin.attrib.name; + }); + var features = this.doc.findall('feature'); + features.forEach(function(element ){ + var idTag = element.find('./param[@name="id"]'); + if(idTag){ + result.push(idTag.attrib.value); + } + }); + return result; + }, + getPlugins: function () { + return this.getPluginIdList().map(function (pluginId) { + return this.getPlugin(pluginId); + }, this); + }, + /** + * Adds a plugin element. Does not check for duplicates. + * @name addPlugin + * @function + * @param {object} attributes name and spec are supported + * @param {Array|object} variables name, value or arbitary object + */ + addPlugin: function (attributes, variables) { + if (!attributes && !attributes.name) return; + var el = new et.Element('plugin'); + el.attrib.name = attributes.name; + if (attributes.spec) { + el.attrib.spec = attributes.spec; + } + + // support arbitrary object as variables source + if (variables && typeof variables === 'object' && !Array.isArray(variables)) { + variables = Object.keys(variables) + .map(function (variableName) { + return {name: variableName, value: variables[variableName]}; + }); + } + + if (variables) { + variables.forEach(function (variable) { + el.append(new et.Element('variable', { name: variable.name, value: variable.value })); + }); + } + this.doc.getroot().append(el); + }, + /** + * Retrives the plugin with the given id or null if not found. + * + * This function also returns any plugin's that + * were defined using the legacy <feature> tags. + * @name getPlugin + * @function + * @param {String} id + * @returns {object} plugin including any variables + */ + getPlugin: function(id){ + if(!id){ + return undefined; + } + var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]'); + if (null === pluginElement) { + var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..'); + if(legacyFeature){ + events.emit('log', 'Found deprecated feature entry for ' + id +' in config.xml.'); + return featureToPlugin(legacyFeature); + } + return undefined; + } + var plugin = {}; + + plugin.name = pluginElement.attrib.name; + plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version; + plugin.variables = {}; + var variableElements = pluginElement.findall('variable'); + variableElements.forEach(function(varElement){ + var name = varElement.attrib.name; + var value = varElement.attrib.value; + if(name){ + plugin.variables[name] = value; + } + }); + return plugin; + }, + /** + * Remove the plugin entry with give name (id). + * + * This function also operates on any plugin's that + * were defined using the legacy <feature> tags. + * @name removePlugin + * @function + * @param id name of the plugin + */ + removePlugin: function(id){ + if(id){ + var plugins = this.doc.findall('./plugin/[@name="' + id + '"]') + .concat(this.doc.findall('./feature/param[@name="id"][@value="' + id + '"]/..')); + var children = this.doc.getroot().getchildren(); + plugins.forEach(function (plugin) { + var idx = children.indexOf(plugin); + if (idx > -1) { + children.splice(idx, 1); + } + }); + } + }, + + // Add any element to the root + addElement: function(name, attributes) { + var el = et.Element(name); + for (var a in attributes) { + el.attrib[a] = attributes[a]; + } + this.doc.getroot().append(el); + }, + + /** + * Adds an engine. Does not check for duplicates. + * @param {String} name the engine name + * @param {String} spec engine source location or version (optional) + */ + addEngine: function(name, spec){ + if(!name) return; + var el = et.Element('engine'); + el.attrib.name = name; + if(spec){ + el.attrib.spec = spec; + } + this.doc.getroot().append(el); + }, + /** + * Removes all the engines with given name + * @param {String} name the engine name. + */ + removeEngine: function(name){ + var engines = this.doc.findall('./engine/[@name="' +name+'"]'); + for(var i=0; i < engines.length; i++){ + var children = this.doc.getroot().getchildren(); + var idx = children.indexOf(engines[i]); + if(idx > -1){ + children.splice(idx,1); + } + } + }, + getEngines: function(){ + var engines = this.doc.findall('./engine'); + return engines.map(function(engine){ + var spec = engine.attrib.spec || engine.attrib.version; + return { + 'name': engine.attrib.name, + 'spec': spec ? spec : null + }; + }); + }, + /* Get all the access tags */ + getAccesses: function() { + var accesses = this.doc.findall('./access'); + return accesses.map(function(access){ + var minimum_tls_version = access.attrib['minimum-tls-version']; /* String */ + var requires_forward_secrecy = access.attrib['requires-forward-secrecy']; /* Boolean */ + var requires_certificate_transparency = access.attrib['requires-certificate-transparency']; /* Boolean */ + var allows_arbitrary_loads_in_web_content = access.attrib['allows-arbitrary-loads-in-web-content']; /* Boolean */ + var allows_arbitrary_loads_in_media = access.attrib['allows-arbitrary-loads-in-media']; /* Boolean */ + var allows_local_networking = access.attrib['allows-local-networking']; /* Boolean */ + + return { + 'origin': access.attrib.origin, + 'minimum_tls_version': minimum_tls_version, + 'requires_forward_secrecy' : requires_forward_secrecy, + 'requires_certificate_transparency' : requires_certificate_transparency, + 'allows_arbitrary_loads_in_web_content' : allows_arbitrary_loads_in_web_content, + 'allows_arbitrary_loads_in_media' : allows_arbitrary_loads_in_media, + 'allows_local_networking' : allows_local_networking + }; + }); + }, + /* Get all the allow-navigation tags */ + getAllowNavigations: function() { + var allow_navigations = this.doc.findall('./allow-navigation'); + return allow_navigations.map(function(allow_navigation){ + var minimum_tls_version = allow_navigation.attrib['minimum-tls-version']; /* String */ + var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy']; /* Boolean */ + var requires_certificate_transparency = allow_navigation.attrib['requires-certificate-transparency']; /* Boolean */ + + return { + 'href': allow_navigation.attrib.href, + 'minimum_tls_version': minimum_tls_version, + 'requires_forward_secrecy' : requires_forward_secrecy, + 'requires_certificate_transparency' : requires_certificate_transparency + }; + }); + }, + /* Get all the allow-intent tags */ + getAllowIntents: function() { + var allow_intents = this.doc.findall('./allow-intent'); + return allow_intents.map(function(allow_intent){ + return { + 'href': allow_intent.attrib.href + }; + }); + }, + /* Get all edit-config tags */ + getEditConfigs: function(platform) { + var platform_tag = this.doc.find('./platform[@name="' + platform + '"]'); + var platform_edit_configs = platform_tag ? platform_tag.findall('edit-config') : []; + + var edit_configs = this.doc.findall('edit-config').concat(platform_edit_configs); + + return edit_configs.map(function(tag) { + var editConfig = + { + file : tag.attrib['file'], + target : tag.attrib['target'], + mode : tag.attrib['mode'], + id : 'config.xml', + xmls : tag.getchildren() + }; + return editConfig; + }); + }, + write:function() { + fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8'); + } +}; + +function featureToPlugin(featureElement) { + var plugin = {}; + plugin.variables = []; + var pluginVersion, + pluginSrc; + + var nodes = featureElement.findall('param'); + nodes.forEach(function (element) { + var n = element.attrib.name; + var v = element.attrib.value; + if (n === 'id') { + plugin.name = v; + } else if (n === 'version') { + pluginVersion = v; + } else if (n === 'url' || n === 'installPath') { + pluginSrc = v; + } else { + plugin.variables[n] = v; + } + }); + + var spec = pluginSrc || pluginVersion; + if (spec) { + plugin.spec = spec; + } + + return plugin; +} +module.exports = ConfigParser;
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md new file mode 100644 index 0000000..e5cd1bf --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/ConfigParser/README.md @@ -0,0 +1,86 @@ +<!-- +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# +--> + +# Cordova-Lib + +## ConfigParser + +wraps a valid cordova config.xml file + +### Usage + +### Include the ConfigParser module in a projet + + var ConfigParser = require('cordova-lib').configparser; + +### Create a new ConfigParser + + var config = new ConfigParser('path/to/config/xml/'); + +### Utility Functions + +#### packageName(id) +returns document root 'id' attribute value +#### Usage + + config.packageName: function(id) + +/* + * sets document root element 'id' attribute to @id + * + * @id - new id value + * + */ +#### setPackageName(id) +set document root 'id' attribute to + function(id) { + this.doc.getroot().attrib['id'] = id; + }, + +### + name: function() { + return getNodeTextSafe(this.doc.find('name')); + }, + setName: function(name) { + var el = findOrCreate(this.doc, 'name'); + el.text = name; + }, + +### read the description element + + config.description() + + var text = "New and improved description of App" + setDescription(text) + +### version management + version() + android_versionCode() + ios_CFBundleVersion() + setVersion() + +### read author element + + config.author(); + +### read preference + + config.getPreference(name); http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js new file mode 100644 index 0000000..46e733f --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaCheck.js @@ -0,0 +1,76 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +var fs = require('fs'), + path = require('path'); + +function isRootDir(dir) { + if (fs.existsSync(path.join(dir, 'www'))) { + if (fs.existsSync(path.join(dir, 'config.xml'))) { + // For sure is. + if (fs.existsSync(path.join(dir, 'platforms'))) { + return 2; + } else { + return 1; + } + } + // Might be (or may be under platforms/). + if (fs.existsSync(path.join(dir, 'www', 'config.xml'))) { + return 1; + } + } + return 0; +} + +// Runs up the directory chain looking for a .cordova directory. +// IF it is found we are in a Cordova project. +// Omit argument to use CWD. +function isCordova(dir) { + if (!dir) { + // Prefer PWD over cwd so that symlinked dirs within your PWD work correctly (CB-5687). + var pwd = process.env.PWD; + var cwd = process.cwd(); + if (pwd && pwd != cwd && pwd != 'undefined') { + return isCordova(pwd) || isCordova(cwd); + } + return isCordova(cwd); + } + var bestReturnValueSoFar = false; + for (var i = 0; i < 1000; ++i) { + var result = isRootDir(dir); + if (result === 2) { + return dir; + } + if (result === 1) { + bestReturnValueSoFar = dir; + } + var parentDir = path.normalize(path.join(dir, '..')); + // Detect fs root. + if (parentDir == dir) { + return bestReturnValueSoFar; + } + dir = parentDir; + } + console.error('Hit an unhandled case in CordovaCheck.isCordova'); + return false; +} + +module.exports = { + findProjectRoot : isCordova +}; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js new file mode 100644 index 0000000..7262448 --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaError.js @@ -0,0 +1,91 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +/* jshint proto:true */ + +var EOL = require('os').EOL; + +/** + * A derived exception class. See usage example in cli.js + * Based on: + * stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/8460753#8460753 + * @param {String} message Error message + * @param {Number} [code=0] Error code + * @param {CordovaExternalToolErrorContext} [context] External tool error context object + * @constructor + */ +function CordovaError(message, code, context) { + Error.captureStackTrace(this, this.constructor); + this.name = this.constructor.name; + this.message = message; + this.code = code || CordovaError.UNKNOWN_ERROR; + this.context = context; +} +CordovaError.prototype.__proto__ = Error.prototype; + +// TODO: Extend error codes according the projects specifics +CordovaError.UNKNOWN_ERROR = 0; +CordovaError.EXTERNAL_TOOL_ERROR = 1; + +/** + * Translates instance's error code number into error code name, e.g. 0 -> UNKNOWN_ERROR + * @returns {string} Error code string name + */ +CordovaError.prototype.getErrorCodeName = function() { + for(var key in CordovaError) { + if(CordovaError.hasOwnProperty(key)) { + if(CordovaError[key] === this.code) { + return key; + } + } + } +}; + +/** + * Converts CordovaError instance to string representation + * @param {Boolean} [isVerbose] Set up verbose mode. Used to provide more + * details including information about error code name and context + * @return {String} Stringified error representation + */ +CordovaError.prototype.toString = function(isVerbose) { + var message = '', codePrefix = ''; + + if(this.code !== CordovaError.UNKNOWN_ERROR) { + codePrefix = 'code: ' + this.code + (isVerbose ? (' (' + this.getErrorCodeName() + ')') : '') + ' '; + } + + if(this.code === CordovaError.EXTERNAL_TOOL_ERROR) { + if(typeof this.context !== 'undefined') { + if(isVerbose) { + message = codePrefix + EOL + this.context.toString(isVerbose) + '\n failed with an error: ' + + this.message + EOL + 'Stack trace: ' + this.stack; + } else { + message = codePrefix + '\'' + this.context.toString(isVerbose) + '\' ' + this.message; + } + } else { + message = 'External tool failed with an error: ' + this.message; + } + } else { + message = isVerbose ? codePrefix + this.stack : codePrefix + this.message; + } + + return message; +}; + +module.exports = CordovaError; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js new file mode 100644 index 0000000..ca9a4aa --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js @@ -0,0 +1,48 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +/* jshint proto:true */ + +var path = require('path'); + +/** + * @param {String} cmd Command full path + * @param {String[]} args Command args + * @param {String} [cwd] Command working directory + * @constructor + */ +function CordovaExternalToolErrorContext(cmd, args, cwd) { + this.cmd = cmd; + // Helper field for readability + this.cmdShortName = path.basename(cmd); + this.args = args; + this.cwd = cwd; +} + +CordovaExternalToolErrorContext.prototype.toString = function(isVerbose) { + if(isVerbose) { + return 'External tool \'' + this.cmdShortName + '\'' + + '\nCommand full path: ' + this.cmd + '\nCommand args: ' + this.args + + (typeof this.cwd !== 'undefined' ? '\nCommand cwd: ' + this.cwd : ''); + } + + return this.cmdShortName; +}; + +module.exports = CordovaExternalToolErrorContext; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js new file mode 100644 index 0000000..dc49a4b --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/CordovaLogger.js @@ -0,0 +1,220 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. + */ + +var ansi = require('ansi'); +var EventEmitter = require('events').EventEmitter; +var CordovaError = require('./CordovaError/CordovaError'); +var EOL = require('os').EOL; + +var INSTANCE; + +/** + * @class CordovaLogger + * + * Implements logging facility that anybody could use. Should not be + * instantiated directly, `CordovaLogger.get()` method should be used instead + * to acquire logger instance + */ +function CordovaLogger () { + this.levels = {}; + this.colors = {}; + this.stdout = process.stdout; + this.stderr = process.stderr; + + this.stdoutCursor = ansi(this.stdout); + this.stderrCursor = ansi(this.stderr); + + this.addLevel('verbose', 1000, 'grey'); + this.addLevel('normal' , 2000); + this.addLevel('warn' , 2000, 'yellow'); + this.addLevel('info' , 3000, 'blue'); + this.addLevel('error' , 5000, 'red'); + this.addLevel('results' , 10000); + + this.setLevel('normal'); +} + +/** + * Static method to create new or acquire existing instance. + * + * @return {CordovaLogger} Logger instance + */ +CordovaLogger.get = function () { + return INSTANCE || (INSTANCE = new CordovaLogger()); +}; + +CordovaLogger.VERBOSE = 'verbose'; +CordovaLogger.NORMAL = 'normal'; +CordovaLogger.WARN = 'warn'; +CordovaLogger.INFO = 'info'; +CordovaLogger.ERROR = 'error'; +CordovaLogger.RESULTS = 'results'; + +/** + * Emits log message to process' stdout/stderr depending on message's severity + * and current log level. If severity is less than current logger's level, + * then the message is ignored. + * + * @param {String} logLevel The message's log level. The logger should have + * corresponding level added (via logger.addLevel), otherwise + * `CordovaLogger.NORMAL` level will be used. + * @param {String} message The message, that should be logged to process' + * stdio + * + * @return {CordovaLogger} Current instance, to allow calls chaining. + */ +CordovaLogger.prototype.log = function (logLevel, message) { + // if there is no such logLevel defined, or provided level has + // less severity than active level, then just ignore this call and return + if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel]) + // return instance to allow to chain calls + return this; + + var isVerbose = this.logLevel === 'verbose'; + var cursor = this.stdoutCursor; + + if (message instanceof Error || logLevel === CordovaLogger.ERROR) { + message = formatError(message, isVerbose); + cursor = this.stderrCursor; + } + + var color = this.colors[logLevel]; + if (color) { + cursor.bold().fg[color](); + } + + cursor.write(message).reset().write(EOL); + + return this; +}; + +/** + * Adds a new level to logger instance. This method also creates a shortcut + * method to log events with the level provided (i.e. after adding new level + * 'debug', the method `debug(message)`, equal to logger.log('debug', message), + * will be added to logger instance) + * + * @param {String} level A log level name. The levels with the following + * names added by default to every instance: 'verbose', 'normal', 'warn', + * 'info', 'error', 'results' + * @param {Number} severity A number that represents level's severity. + * @param {String} color A valid color name, that will be used to log + * messages with this level. Any CSS color code or RGB value is allowed + * (according to ansi documentation: + * https://github.com/TooTallNate/ansi.js#features) + * + * @return {CordovaLogger} Current instance, to allow calls chaining. + */ +CordovaLogger.prototype.addLevel = function (level, severity, color) { + + this.levels[level] = severity; + + if (color) { + this.colors[level] = color; + } + + // Define own method with corresponding name + if (!this[level]) { + this[level] = this.log.bind(this, level); + } + + return this; +}; + +/** + * Sets the current logger level to provided value. If logger doesn't have level + * with this name, `CordovaLogger.NORMAL` will be used. + * + * @param {String} logLevel Level name. The level with this name should be + * added to logger before. + * + * @return {CordovaLogger} Current instance, to allow calls chaining. + */ +CordovaLogger.prototype.setLevel = function (logLevel) { + this.logLevel = this.levels[logLevel] ? logLevel : CordovaLogger.NORMAL; + + return this; +}; + +/** + * Adjusts the current logger level according to the passed options. + * + * @param {Object|Array} opts An object or args array with options + * + * @return {CordovaLogger} Current instance, to allow calls chaining. + */ +CordovaLogger.prototype.adjustLevel = function (opts) { + if (opts.verbose || (Array.isArray(opts) && opts.indexOf('--verbose') !== -1)) { + this.setLevel('verbose'); + } else if (opts.silent || (Array.isArray(opts) && opts.indexOf('--silent') !== -1)) { + this.setLevel('error'); + } + + return this; +}; + +/** + * Attaches logger to EventEmitter instance provided. + * + * @param {EventEmitter} eventEmitter An EventEmitter instance to attach + * logger to. + * + * @return {CordovaLogger} Current instance, to allow calls chaining. + */ +CordovaLogger.prototype.subscribe = function (eventEmitter) { + + if (!(eventEmitter instanceof EventEmitter)) + throw new Error('Subscribe method only accepts an EventEmitter instance as argument'); + + eventEmitter.on('verbose', this.verbose) + .on('log', this.normal) + .on('info', this.info) + .on('warn', this.warn) + .on('warning', this.warn) + // Set up event handlers for logging and results emitted as events. + .on('results', this.results); + + return this; +}; + +function formatError(error, isVerbose) { + var message = ''; + + if (error instanceof CordovaError) { + message = error.toString(isVerbose); + } else if (error instanceof Error) { + if (isVerbose) { + message = error.stack; + } else { + message = error.message; + } + } else { + // Plain text error message + message = error; + } + + if (typeof message === 'string' && message.toUpperCase().indexOf('ERROR:') !== 0) { + // Needed for backward compatibility with external tools + message = 'Error: ' + message; + } + + return message; +} + +module.exports = CordovaLogger; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js new file mode 100644 index 0000000..8b6876b --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/FileUpdater.js @@ -0,0 +1,416 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +"use strict"; + +var fs = require("fs"); +var path = require("path"); +var shell = require("shelljs"); +var minimatch = require("minimatch"); + +/** + * Logging callback used in the FileUpdater methods. + * @callback loggingCallback + * @param {string} message A message describing a single file update operation. + */ + +/** + * Updates a target file or directory with a source file or directory. (Directory updates are + * not recursive.) Stats for target and source items must be passed in. This is an internal + * helper function used by other methods in this module. + * + * @param {?string} sourcePath Source file or directory to be used to update the + * destination. If the source is null, then the destination is deleted if it exists. + * @param {?fs.Stats} sourceStats An instance of fs.Stats for the source path, or null if + * the source does not exist. + * @param {string} targetPath Required destination file or directory to be updated. If it does + * not exist, it will be created. + * @param {?fs.Stats} targetStats An instance of fs.Stats for the target path, or null if + * the target does not exist. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, options, log) { + var updated = false; + + var rootDir = (options && options.rootDir) || ""; + var copyAll = (options && options.all) || false; + + var targetFullPath = path.join(rootDir || "", targetPath); + + if (sourceStats) { + var sourceFullPath = path.join(rootDir || "", sourcePath); + + if (targetStats) { + // The target exists. But if the directory status doesn't match the source, delete it. + if (targetStats.isDirectory() && !sourceStats.isDirectory()) { + log("rmdir " + targetPath + " (source is a file)"); + shell.rm("-rf", targetFullPath); + targetStats = null; + updated = true; + } else if (!targetStats.isDirectory() && sourceStats.isDirectory()) { + log("delete " + targetPath + " (source is a directory)"); + shell.rm("-f", targetFullPath); + targetStats = null; + updated = true; + } + } + + if (!targetStats) { + if (sourceStats.isDirectory()) { + // The target directory does not exist, so it should be created. + log("mkdir " + targetPath); + shell.mkdir("-p", targetFullPath); + updated = true; + } else if (sourceStats.isFile()) { + // The target file does not exist, so it should be copied from the source. + log("copy " + sourcePath + " " + targetPath + (copyAll ? "" : " (new file)")); + shell.cp("-f", sourceFullPath, targetFullPath); + updated = true; + } + } else if (sourceStats.isFile() && targetStats.isFile()) { + // The source and target paths both exist and are files. + if (copyAll) { + // The caller specified all files should be copied. + log("copy " + sourcePath + " " + targetPath); + shell.cp("-f", sourceFullPath, targetFullPath); + updated = true; + } else { + // Copy if the source has been modified since it was copied to the target, or if + // the file sizes are different. (The latter catches most cases in which something + // was done to the file after copying.) Comparison is >= rather than > to allow + // for timestamps lacking sub-second precision in some filesystems. + if (sourceStats.mtime.getTime() >= targetStats.mtime.getTime() || + sourceStats.size !== targetStats.size) { + log("copy " + sourcePath + " " + targetPath + " (updated file)"); + shell.cp("-f", sourceFullPath, targetFullPath); + updated = true; + } + } + } + } else if (targetStats) { + // The target exists but the source is null, so the target should be deleted. + if (targetStats.isDirectory()) { + log("rmdir " + targetPath + (copyAll ? "" : " (no source)")); + shell.rm("-rf", targetFullPath); + } else { + log("delete " + targetPath + (copyAll ? "" : " (no source)")); + shell.rm("-f", targetFullPath); + } + updated = true; + } + + return updated; +} + +/** + * Helper for updatePath and updatePaths functions. Queries stats for source and target + * and ensures target directory exists before copying a file. + */ +function updatePathInternal(sourcePath, targetPath, options, log) { + var rootDir = (options && options.rootDir) || ""; + var targetFullPath = path.join(rootDir, targetPath); + var targetStats = fs.existsSync(targetFullPath) ? fs.statSync(targetFullPath) : null; + var sourceStats = null; + + if (sourcePath) { + // A non-null source path was specified. It should exist. + var sourceFullPath = path.join(rootDir, sourcePath); + if (!fs.existsSync(sourceFullPath)) { + throw new Error("Source path does not exist: " + sourcePath); + } + + sourceStats = fs.statSync(sourceFullPath); + + // Create the target's parent directory if it doesn't exist. + var parentDir = path.dirname(targetFullPath); + if (!fs.existsSync(parentDir)) { + shell.mkdir("-p", parentDir); + } + } + + return updatePathWithStats(sourcePath, sourceStats, targetPath, targetStats, options, log); +} + +/** + * Updates a target file or directory with a source file or directory. (Directory updates are + * not recursive.) + * + * @param {?string} sourcePath Source file or directory to be used to update the + * destination. If the source is null, then the destination is deleted if it exists. + * @param {string} targetPath Required destination file or directory to be updated. If it does + * not exist, it will be created. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function updatePath(sourcePath, targetPath, options, log) { + if (sourcePath !== null && typeof sourcePath !== "string") { + throw new Error("A source path (or null) is required."); + } + + if (!targetPath || typeof targetPath !== "string") { + throw new Error("A target path is required."); + } + + log = log || function(message) { }; + + return updatePathInternal(sourcePath, targetPath, options, log); +} + +/** + * Updates files and directories based on a mapping from target paths to source paths. Targets + * with null sources in the map are deleted. + * + * @param {Object} pathMap A dictionary mapping from target paths to source paths. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function updatePaths(pathMap, options, log) { + if (!pathMap || typeof pathMap !== "object" || Array.isArray(pathMap)) { + throw new Error("An object mapping from target paths to source paths is required."); + } + + log = log || function(message) { }; + + var updated = false; + + // Iterate in sorted order to ensure directories are created before files under them. + Object.keys(pathMap).sort().forEach(function (targetPath) { + var sourcePath = pathMap[targetPath]; + updated = updatePathInternal(sourcePath, targetPath, options, log) || updated; + }); + + return updated; +} + +/** + * Updates a target directory with merged files and subdirectories from source directories. + * + * @param {string|string[]} sourceDirs Required source directory or array of source directories + * to be merged into the target. The directories are listed in order of precedence; files in + * directories later in the array supersede files in directories earlier in the array + * (regardless of timestamps). + * @param {string} targetDir Required destination directory to be updated. If it does not exist, + * it will be created. If it exists, newer files from source directories will be copied over, + * and files missing in the source directories will be deleted. + * @param {Object} [options] Optional additional parameters for the update. + * @param {string} [options.rootDir] Optional root directory (such as a project) to which target + * and source path parameters are relative; may be omitted if the paths are absolute. The + * rootDir is always omitted from any logged paths, to make the logs easier to read. + * @param {boolean} [options.all] If true, all files are copied regardless of last-modified times. + * Otherwise, a file is copied if the source's last-modified time is greather than or + * equal to the target's last-modified time, or if the file sizes are different. + * @param {string|string[]} [options.include] Optional glob string or array of glob strings that + * are tested against both target and source relative paths to determine if they are included + * in the merge-and-update. If unspecified, all items are included. + * @param {string|string[]} [options.exclude] Optional glob string or array of glob strings that + * are tested against both target and source relative paths to determine if they are excluded + * from the merge-and-update. Exclusions override inclusions. If unspecified, no items are + * excluded. + * @param {loggingCallback} [log] Optional logging callback that takes a string message + * describing any file operations that are performed. + * @return {boolean} true if any changes were made, or false if the force flag is not set + * and everything was up to date + */ +function mergeAndUpdateDir(sourceDirs, targetDir, options, log) { + if (sourceDirs && typeof sourceDirs === "string") { + sourceDirs = [ sourceDirs ]; + } else if (!Array.isArray(sourceDirs)) { + throw new Error("A source directory path or array of paths is required."); + } + + if (!targetDir || typeof targetDir !== "string") { + throw new Error("A target directory path is required."); + } + + log = log || function(message) { }; + + var rootDir = (options && options.rootDir) || ""; + + var include = (options && options.include) || [ "**" ]; + if (typeof include === "string") { + include = [ include ]; + } else if (!Array.isArray(include)) { + throw new Error("Include parameter must be a glob string or array of glob strings."); + } + + var exclude = (options && options.exclude) || []; + if (typeof exclude === "string") { + exclude = [ exclude ]; + } else if (!Array.isArray(exclude)) { + throw new Error("Exclude parameter must be a glob string or array of glob strings."); + } + + // Scan the files in each of the source directories. + var sourceMaps = sourceDirs.map(function (sourceDir) { + return path.join(rootDir, sourceDir); + }).map(function (sourcePath) { + if (!fs.existsSync(sourcePath)) { + throw new Error("Source directory does not exist: " + sourcePath); + } + return mapDirectory(rootDir, path.relative(rootDir, sourcePath), include, exclude); + }); + + // Scan the files in the target directory, if it exists. + var targetMap = {}; + var targetFullPath = path.join(rootDir, targetDir); + if (fs.existsSync(targetFullPath)) { + targetMap = mapDirectory(rootDir, targetDir, include, exclude); + } + + var pathMap = mergePathMaps(sourceMaps, targetMap, targetDir); + + var updated = false; + + // Iterate in sorted order to ensure directories are created before files under them. + Object.keys(pathMap).sort().forEach(function (subPath) { + var entry = pathMap[subPath]; + updated = updatePathWithStats( + entry.sourcePath, + entry.sourceStats, + entry.targetPath, + entry.targetStats, + options, + log) || updated; + }); + + return updated; +} + +/** + * Creates a dictionary map of all files and directories under a path. + */ +function mapDirectory(rootDir, subDir, include, exclude) { + var dirMap = { "": { subDir: subDir, stats: fs.statSync(path.join(rootDir, subDir)) } }; + mapSubdirectory(rootDir, subDir, "", include, exclude, dirMap); + return dirMap; + + function mapSubdirectory(rootDir, subDir, relativeDir, include, exclude, dirMap) { + var itemMapped = false; + var items = fs.readdirSync(path.join(rootDir, subDir, relativeDir)); + + items.forEach(function(item) { + var relativePath = path.join(relativeDir, item); + if(!matchGlobArray(relativePath, exclude)) { + // Stats obtained here (required at least to know where to recurse in directories) + // are saved for later, where the modified times may also be used. This minimizes + // the number of file I/O operations performed. + var fullPath = path.join(rootDir, subDir, relativePath); + var stats = fs.statSync(fullPath); + + if (stats.isDirectory()) { + // Directories are included if either something under them is included or they + // match an include glob. + if (mapSubdirectory(rootDir, subDir, relativePath, include, exclude, dirMap) || + matchGlobArray(relativePath, include)) { + dirMap[relativePath] = { subDir: subDir, stats: stats }; + itemMapped = true; + } + } else if (stats.isFile()) { + // Files are included only if they match an include glob. + if (matchGlobArray(relativePath, include)) { + dirMap[relativePath] = { subDir: subDir, stats: stats }; + itemMapped = true; + } + } + } + }); + return itemMapped; + } + + function matchGlobArray(path, globs) { + return globs.some(function(elem) { + return minimatch(path, elem, {dot:true}); + }); + } +} + +/** + * Merges together multiple source maps and a target map into a single mapping from + * relative paths to objects with target and source paths and stats. + */ +function mergePathMaps(sourceMaps, targetMap, targetDir) { + // Merge multiple source maps together, along with target path info. + // Entries in later source maps override those in earlier source maps. + // Target stats will be filled in below for targets that exist. + var pathMap = {}; + sourceMaps.forEach(function (sourceMap) { + Object.keys(sourceMap).forEach(function(sourceSubPath){ + var sourceEntry = sourceMap[sourceSubPath]; + pathMap[sourceSubPath] = { + targetPath: path.join(targetDir, sourceSubPath), + targetStats: null, + sourcePath: path.join(sourceEntry.subDir, sourceSubPath), + sourceStats: sourceEntry.stats + }; + }); + }); + + // Fill in target stats for targets that exist, and create entries + // for targets that don't have any corresponding sources. + Object.keys(targetMap).forEach(function(subPath){ + var entry = pathMap[subPath]; + if (entry) { + entry.targetStats = targetMap[subPath].stats; + } else { + pathMap[subPath] = { + targetPath: path.join(targetDir, subPath), + targetStats: targetMap[subPath].stats, + sourcePath: null, + sourceStats: null + }; + } + }); + + return pathMap; +} + +module.exports = { + updatePath: updatePath, + updatePaths: updatePaths, + mergeAndUpdateDir: mergeAndUpdateDir +}; + http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js new file mode 100644 index 0000000..ab94b5f --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PlatformJson.js @@ -0,0 +1,278 @@ +/* + * 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. + * +*/ +/* jshint sub:true */ + +var fs = require('fs'); +var path = require('path'); +var shelljs = require('shelljs'); +var mungeutil = require('./ConfigChanges/munge-util'); +var pluginMappernto = require('cordova-registry-mapper').newToOld; +var pluginMapperotn = require('cordova-registry-mapper').oldToNew; + +function PlatformJson(filePath, platform, root) { + this.filePath = filePath; + this.platform = platform; + this.root = fix_munge(root || {}); +} + +PlatformJson.load = function(plugins_dir, platform) { + var filePath = path.join(plugins_dir, platform + '.json'); + var root = null; + if (fs.existsSync(filePath)) { + root = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + } + return new PlatformJson(filePath, platform, root); +}; + +PlatformJson.prototype.save = function() { + shelljs.mkdir('-p', path.dirname(this.filePath)); + fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 4), 'utf-8'); +}; + +/** + * Indicates whether the specified plugin is installed as a top-level (not as + * dependency to others) + * @method function + * @param {String} pluginId A plugin id to check for. + * @return {Boolean} true if plugin installed as top-level, otherwise false. + */ +PlatformJson.prototype.isPluginTopLevel = function(pluginId) { + var installedPlugins = this.root.installed_plugins; + return installedPlugins[pluginId] || + installedPlugins[pluginMappernto[pluginId]] || + installedPlugins[pluginMapperotn[pluginId]]; +}; + +/** + * Indicates whether the specified plugin is installed as a dependency to other + * plugin. + * @method function + * @param {String} pluginId A plugin id to check for. + * @return {Boolean} true if plugin installed as a dependency, otherwise false. + */ +PlatformJson.prototype.isPluginDependent = function(pluginId) { + var dependentPlugins = this.root.dependent_plugins; + return dependentPlugins[pluginId] || + dependentPlugins[pluginMappernto[pluginId]] || + dependentPlugins[pluginMapperotn[pluginId]]; +}; + +/** + * Indicates whether plugin is installed either as top-level or as dependency. + * @method function + * @param {String} pluginId A plugin id to check for. + * @return {Boolean} true if plugin installed, otherwise false. + */ +PlatformJson.prototype.isPluginInstalled = function(pluginId) { + return this.isPluginTopLevel(pluginId) || + this.isPluginDependent(pluginId); +}; + +PlatformJson.prototype.addPlugin = function(pluginId, variables, isTopLevel) { + var pluginsList = isTopLevel ? + this.root.installed_plugins : + this.root.dependent_plugins; + + pluginsList[pluginId] = variables; + + return this; +}; + +/** + * @chaining + * Generates and adds metadata for provided plugin into associated <platform>.json file + * + * @param {PluginInfo} pluginInfo A pluginInfo instance to add metadata from + * @returns {this} Current PlatformJson instance to allow calls chaining + */ +PlatformJson.prototype.addPluginMetadata = function (pluginInfo) { + + var installedModules = this.root.modules || []; + + var installedPaths = installedModules.map(function (installedModule) { + return installedModule.file; + }); + + var modulesToInstall = pluginInfo.getJsModules(this.platform) + .map(function (module) { + return new ModuleMetadata(pluginInfo.id, module); + }) + .filter(function (metadata) { + // Filter out modules which are already added to metadata + return installedPaths.indexOf(metadata.file) === -1; + }); + + this.root.modules = installedModules.concat(modulesToInstall); + + this.root.plugin_metadata = this.root.plugin_metadata || {}; + this.root.plugin_metadata[pluginInfo.id] = pluginInfo.version; + + return this; +}; + +PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) { + var pluginsList = isTopLevel ? + this.root.installed_plugins : + this.root.dependent_plugins; + + delete pluginsList[pluginId]; + + return this; +}; + +/** + * @chaining + * Removes metadata for provided plugin from associated file + * + * @param {PluginInfo} pluginInfo A PluginInfo instance to which modules' metadata + * we need to remove + * + * @returns {this} Current PlatformJson instance to allow calls chaining + */ +PlatformJson.prototype.removePluginMetadata = function (pluginInfo) { + var modulesToRemove = pluginInfo.getJsModules(this.platform) + .map(function (jsModule) { + return ['plugins', pluginInfo.id, jsModule.src].join('/'); + }); + + var installedModules = this.root.modules || []; + this.root.modules = installedModules + .filter(function (installedModule) { + // Leave only those metadatas which 'file' is not in removed modules + return (modulesToRemove.indexOf(installedModule.file) === -1); + }); + + if (this.root.plugin_metadata) { + delete this.root.plugin_metadata[pluginInfo.id]; + } + + return this; +}; + +PlatformJson.prototype.addInstalledPluginToPrepareQueue = function(pluginDirName, vars, is_top_level, force) { + this.root.prepare_queue.installed.push({'plugin':pluginDirName, 'vars':vars, 'topLevel':is_top_level, 'force':force}); +}; + +PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function(pluginId, is_top_level) { + this.root.prepare_queue.uninstalled.push({'plugin':pluginId, 'id':pluginId, 'topLevel':is_top_level}); +}; + +/** + * Moves plugin, specified by id to top-level plugins. If plugin is top-level + * already, then does nothing. + * @method function + * @param {String} pluginId A plugin id to make top-level. + * @return {PlatformJson} PlatformJson instance. + */ +PlatformJson.prototype.makeTopLevel = function(pluginId) { + var plugin = this.root.dependent_plugins[pluginId]; + if (plugin) { + delete this.root.dependent_plugins[pluginId]; + this.root.installed_plugins[pluginId] = plugin; + } + return this; +}; + +/** + * Generates a metadata for all installed plugins and js modules. The resultant + * string is ready to be written to 'cordova_plugins.js' + * + * @returns {String} cordova_plugins.js contents + */ +PlatformJson.prototype.generateMetadata = function () { + return [ + 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {', + 'module.exports = ' + JSON.stringify(this.root.modules, null, 4) + ';', + 'module.exports.metadata = ', + '// TOP OF METADATA', + JSON.stringify(this.root.plugin_metadata, null, 4) + ';', + '// BOTTOM OF METADATA', + '});' // Close cordova.define. + ].join('\n'); +}; + +/** + * @chaining + * Generates and then saves metadata to specified file. Doesn't check if file exists. + * + * @param {String} destination File metadata will be written to + * @return {PlatformJson} PlatformJson instance + */ +PlatformJson.prototype.generateAndSaveMetadata = function (destination) { + var meta = this.generateMetadata(); + shelljs.mkdir('-p', path.dirname(destination)); + fs.writeFileSync(destination, meta, 'utf-8'); + + return this; +}; + +// convert a munge from the old format ([file][parent][xml] = count) to the current one +function fix_munge(root) { + root.prepare_queue = root.prepare_queue || {installed:[], uninstalled:[]}; + root.config_munge = root.config_munge || {files: {}}; + root.installed_plugins = root.installed_plugins || {}; + root.dependent_plugins = root.dependent_plugins || {}; + + var munge = root.config_munge; + if (!munge.files) { + var new_munge = { files: {} }; + for (var file in munge) { + for (var selector in munge[file]) { + for (var xml_child in munge[file][selector]) { + var val = parseInt(munge[file][selector][xml_child]); + for (var i = 0; i < val; i++) { + mungeutil.deep_add(new_munge, [file, selector, { xml: xml_child, count: val }]); + } + } + } + } + root.config_munge = new_munge; + } + + return root; +} + +/** + * @constructor + * @class ModuleMetadata + * + * Creates a ModuleMetadata object that represents module entry in 'cordova_plugins.js' + * file at run time + * + * @param {String} pluginId Plugin id where this module installed from + * @param (JsModule|Object) jsModule A js-module entry from PluginInfo class to generate metadata for + */ +function ModuleMetadata (pluginId, jsModule) { + + if (!pluginId) throw new TypeError('pluginId argument must be a valid plugin id'); + if (!jsModule.src && !jsModule.name) throw new TypeError('jsModule argument must contain src or/and name properties'); + + this.id = pluginId + '.' + ( jsModule.name || jsModule.src.match(/([^\/]+)\.js/)[1] ); + this.file = ['plugins', pluginId, jsModule.src].join('/'); + this.pluginId = pluginId; + + if (jsModule.clobbers && jsModule.clobbers.length > 0) { + this.clobbers = jsModule.clobbers.map(function(o) { return o.target; }); + } + if (jsModule.merges && jsModule.merges.length > 0) { + this.merges = jsModule.merges.map(function(o) { return o.target; }); + } + if (jsModule.runs) { + this.runs = true; + } +} + +module.exports = PlatformJson; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/07bed560/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js new file mode 100644 index 0000000..44501fa --- /dev/null +++ b/cordova-lib/spec-plugman/projects/android/cordova/node_modules/cordova-common/src/PluginInfo/PluginInfo.js @@ -0,0 +1,425 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +*/ + +/* jshint sub:true, laxcomma:true, laxbreak:true */ + +/* +A class for holidng the information currently stored in plugin.xml +It should also be able to answer questions like whether the plugin +is compatible with a given engine version. + +TODO (kamrik): refactor this to not use sync functions and return promises. +*/ + + +var path = require('path') + , fs = require('fs') + , xml_helpers = require('../util/xml-helpers') + , CordovaError = require('../CordovaError/CordovaError') + ; + +function PluginInfo(dirname) { + var self = this; + + // METHODS + // Defined inside the constructor to avoid the "this" binding problems. + + // <preference> tag + // Example: <preference name="API_KEY" /> + // Used to require a variable to be specified via --variable when installing the plugin. + // returns { key : default | null} + self.getPreferences = getPreferences; + function getPreferences(platform) { + return _getTags(self._et, 'preference', platform, _parsePreference) + .reduce(function (preferences, pref) { + preferences[pref.preference] = pref.default; + return preferences; + }, {}); + } + + function _parsePreference(prefTag) { + var name = prefTag.attrib.name.toUpperCase(); + var def = prefTag.attrib.default || null; + return {preference: name, default: def}; + } + + // <asset> + self.getAssets = getAssets; + function getAssets(platform) { + var assets = _getTags(self._et, 'asset', platform, _parseAsset); + return assets; + } + + function _parseAsset(tag) { + var src = tag.attrib.src; + var target = tag.attrib.target; + + if ( !src || !target) { + var msg = + 'Malformed <asset> tag. Both "src" and "target" attributes' + + 'must be specified in\n' + + self.filepath + ; + throw new Error(msg); + } + + var asset = { + itemType: 'asset', + src: src, + target: target + }; + return asset; + } + + + // <dependency> + // Example: + // <dependency id="com.plugin.id" + // url="https://github.com/myuser/someplugin" + // commit="428931ada3891801" + // subdir="some/path/here" /> + self.getDependencies = getDependencies; + function getDependencies(platform) { + var deps = _getTags( + self._et, + 'dependency', + platform, + _parseDependency + ); + return deps; + } + + function _parseDependency(tag) { + var dep = + { id : tag.attrib.id + , url : tag.attrib.url || '' + , subdir : tag.attrib.subdir || '' + , commit : tag.attrib.commit + }; + + dep.git_ref = dep.commit; + + if ( !dep.id ) { + var msg = + '<dependency> tag is missing id attribute in ' + + self.filepath + ; + throw new CordovaError(msg); + } + return dep; + } + + + // <config-file> tag + self.getConfigFiles = getConfigFiles; + function getConfigFiles(platform) { + var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile); + return configFiles; + } + + function _parseConfigFile(tag) { + var configFile = + { target : tag.attrib['target'] + , parent : tag.attrib['parent'] + , after : tag.attrib['after'] + , xmls : tag.getchildren() + // To support demuxing via versions + , versions : tag.attrib['versions'] + , deviceTarget: tag.attrib['device-target'] + }; + return configFile; + } + + self.getEditConfigs = getEditConfigs; + function getEditConfigs(platform) { + var editConfigs = _getTags(self._et, 'edit-config', platform, _parseEditConfigs); + return editConfigs; + } + + function _parseEditConfigs(tag) { + var editConfig = + { file : tag.attrib['file'] + , target : tag.attrib['target'] + , mode : tag.attrib['mode'] + , xmls : tag.getchildren() + }; + return editConfig; + } + + // <info> tags, both global and within a <platform> + // TODO (kamrik): Do we ever use <info> under <platform>? Example wanted. + self.getInfo = getInfo; + function getInfo(platform) { + var infos = _getTags( + self._et, + 'info', + platform, + function(elem) { return elem.text; } + ); + // Filter out any undefined or empty strings. + infos = infos.filter(Boolean); + return infos; + } + + // <source-file> + // Examples: + // <source-file src="src/ios/someLib.a" framework="true" /> + // <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" /> + self.getSourceFiles = getSourceFiles; + function getSourceFiles(platform) { + var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile); + return sourceFiles; + } + + function _parseSourceFile(tag) { + return { + itemType: 'source-file', + src: tag.attrib.src, + framework: isStrTrue(tag.attrib.framework), + weak: isStrTrue(tag.attrib.weak), + compilerFlags: tag.attrib['compiler-flags'], + targetDir: tag.attrib['target-dir'] + }; + } + + // <header-file> + // Example: + // <header-file src="CDVFoo.h" /> + self.getHeaderFiles = getHeaderFiles; + function getHeaderFiles(platform) { + var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function(tag) { + return { + itemType: 'header-file', + src: tag.attrib.src, + targetDir: tag.attrib['target-dir'] + }; + }); + return headerFiles; + } + + // <resource-file> + // Example: + // <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" device-target="win" arch="x86" versions=">=8.1" /> + self.getResourceFiles = getResourceFiles; + function getResourceFiles(platform) { + var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function(tag) { + return { + itemType: 'resource-file', + src: tag.attrib.src, + target: tag.attrib.target, + versions: tag.attrib.versions, + deviceTarget: tag.attrib['device-target'], + arch: tag.attrib.arch, + reference: tag.attrib.reference + }; + }); + return resourceFiles; + } + + // <lib-file> + // Example: + // <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" /> + self.getLibFiles = getLibFiles; + function getLibFiles(platform) { + var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function(tag) { + return { + itemType: 'lib-file', + src: tag.attrib.src, + arch: tag.attrib.arch, + Include: tag.attrib.Include, + versions: tag.attrib.versions, + deviceTarget: tag.attrib['device-target'] || tag.attrib.target + }; + }); + return libFiles; + } + + // <hook> + // Example: + // <hook type="before_build" src="scripts/beforeBuild.js" /> + self.getHookScripts = getHookScripts; + function getHookScripts(hook, platforms) { + var scriptElements = self._et.findall('./hook'); + + if(platforms) { + platforms.forEach(function (platform) { + scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook')); + }); + } + + function filterScriptByHookType(el) { + return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook; + } + + return scriptElements.filter(filterScriptByHookType); + } + + self.getJsModules = getJsModules; + function getJsModules(platform) { + var modules = _getTags(self._et, 'js-module', platform, _parseJsModule); + return modules; + } + + function _parseJsModule(tag) { + var ret = { + itemType: 'js-module', + name: tag.attrib.name, + src: tag.attrib.src, + clobbers: tag.findall('clobbers').map(function(tag) { return { target: tag.attrib.target }; }), + merges: tag.findall('merges').map(function(tag) { return { target: tag.attrib.target }; }), + runs: tag.findall('runs').length > 0 + }; + + return ret; + } + + self.getEngines = function() { + return self._et.findall('engines/engine').map(function(n) { + return { + name: n.attrib.name, + version: n.attrib.version, + platform: n.attrib.platform, + scriptSrc: n.attrib.scriptSrc + }; + }); + }; + + self.getPlatforms = function() { + return self._et.findall('platform').map(function(n) { + return { name: n.attrib.name }; + }); + }; + + self.getPlatformsArray = function() { + return self._et.findall('platform').map(function(n) { + return n.attrib.name; + }); + }; + self.getFrameworks = function(platform) { + return _getTags(self._et, 'framework', platform, function(el) { + var ret = { + itemType: 'framework', + type: el.attrib.type, + parent: el.attrib.parent, + custom: isStrTrue(el.attrib.custom), + src: el.attrib.src, + spec: el.attrib.spec, + weak: isStrTrue(el.attrib.weak), + versions: el.attrib.versions, + targetDir: el.attrib['target-dir'], + deviceTarget: el.attrib['device-target'] || el.attrib.target, + arch: el.attrib.arch, + implementation: el.attrib.implementation + }; + return ret; + }); + }; + + self.getFilesAndFrameworks = getFilesAndFrameworks; + function getFilesAndFrameworks(platform) { + // Please avoid changing the order of the calls below, files will be + // installed in this order. + var items = [].concat( + self.getSourceFiles(platform), + self.getHeaderFiles(platform), + self.getResourceFiles(platform), + self.getFrameworks(platform), + self.getLibFiles(platform) + ); + return items; + } + ///// End of PluginInfo methods ///// + + + ///// PluginInfo Constructor logic ///// + self.filepath = path.join(dirname, 'plugin.xml'); + if (!fs.existsSync(self.filepath)) { + throw new CordovaError('Cannot find plugin.xml for plugin "' + path.basename(dirname) + '". Please try adding it again.'); + } + + self.dir = dirname; + var et = self._et = xml_helpers.parseElementtreeSync(self.filepath); + var pelem = et.getroot(); + self.id = pelem.attrib.id; + self.version = pelem.attrib.version; + + // Optional fields + self.name = pelem.findtext('name'); + self.description = pelem.findtext('description'); + self.license = pelem.findtext('license'); + self.repo = pelem.findtext('repo'); + self.issue = pelem.findtext('issue'); + self.keywords = pelem.findtext('keywords'); + self.info = pelem.findtext('info'); + if (self.keywords) { + self.keywords = self.keywords.split(',').map( function(s) { return s.trim(); } ); + } + self.getKeywordsAndPlatforms = function () { + var ret = self.keywords || []; + return ret.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray())); + }; +} // End of PluginInfo constructor. + +// Helper function used to prefix every element of an array with cordova- +// Useful when we want to modify platforms to be cordova-platform +function addCordova(someArray) { + var newArray = someArray.map(function(element) { + return 'cordova-' + element; + }); + return newArray; +} + +// Helper function used by most of the getSomething methods of PluginInfo. +// Get all elements of a given name. Both in root and in platform sections +// for the given platform. If transform is given and is a function, it is +// applied to each element. +function _getTags(pelem, tag, platform, transform) { + var platformTag = pelem.find('./platform[@name="' + platform + '"]'); + var tagsInRoot = pelem.findall(tag); + tagsInRoot = tagsInRoot || []; + var tagsInPlatform = platformTag ? platformTag.findall(tag) : []; + var tags = tagsInRoot.concat(tagsInPlatform); + if ( typeof transform === 'function' ) { + tags = tags.map(transform); + } + return tags; +} + +// Same as _getTags() but only looks inside a platform section. +function _getTagsInPlatform(pelem, tag, platform, transform) { + var platformTag = pelem.find('./platform[@name="' + platform + '"]'); + var tags = platformTag ? platformTag.findall(tag) : []; + if ( typeof transform === 'function' ) { + tags = tags.map(transform); + } + return tags; +} + +// Check if x is a string 'true'. +function isStrTrue(x) { + return String(x).toLowerCase() == 'true'; +} + +module.exports = PluginInfo; +// Backwards compat: +PluginInfo.PluginInfo = PluginInfo; +PluginInfo.loadPluginsDir = function(dir) { + var PluginInfoProvider = require('./PluginInfoProvider'); + return new PluginInfoProvider().getAllWithinSearchPath(dir); +}; --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org For additional commands, e-mail: commits-h...@cordova.apache.org