You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2013/04/22 20:59:29 UTC
[03/15] git commit: hugeass refactor
hugeass refactor
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/5f4bb9d5
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/5f4bb9d5
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/5f4bb9d5
Branch: refs/heads/jasmine
Commit: 5f4bb9d5f80cb654559d660bfef11bdbfa13fff5
Parents: 442e12b
Author: Fil Maj <ma...@gmail.com>
Authored: Fri Apr 19 14:59:44 2013 -0700
Committer: Fil Maj <ma...@gmail.com>
Committed: Fri Apr 19 14:59:44 2013 -0700
----------------------------------------------------------------------
main.js | 47 +++--
package.json | 4 +-
platforms/android.js | 216 --------------------
platforms/blackberry.js | 172 ----------------
platforms/ios.js | 370 -----------------------------------
plugman.js | 137 +------------
src/fetch.js | 29 +++
src/install.js | 94 +++++++++
src/platforms.js | 5 +
src/platforms/android.js | 229 ++++++++++++++++++++++
src/platforms/blackberry.js | 172 ++++++++++++++++
src/platforms/ios.js | 370 +++++++++++++++++++++++++++++++++++
src/prepare.js | 152 ++++++++++++++
src/remove.js | 14 ++
src/uninstall.js | 75 +++++++
src/util/config-changes.js | 38 ++++
src/util/fs.js | 34 ++++
src/util/plugins.js | 119 +++++++++++
src/util/search-and-replace.js | 37 ++++
src/util/test-helpers.js | 26 +++
src/util/xml-helpers.js | 175 +++++++++++++++++
test/android-one-install.js | 52 ++---
util/config-changes.js | 38 ----
util/fs.js | 34 ----
util/plugin_loader.js | 152 --------------
util/plugins.js | 114 -----------
util/search-and-replace.js | 37 ----
util/test-helpers.js | 26 ---
util/xml-helpers.js | 175 -----------------
29 files changed, 1623 insertions(+), 1520 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/main.js
----------------------------------------------------------------------
diff --git a/main.js b/main.js
index f33e00b..f3ada41 100755
--- a/main.js
+++ b/main.js
@@ -23,7 +23,7 @@ var path = require('path')
, url = require('url')
, package = require(path.join(__dirname, 'package'))
, nopt = require('nopt')
- , plugins = require('./util/plugins')
+ , plugins = require('./src/util/plugins')
, plugman = require('./plugman');
var known_opts = { 'platform' : [ 'ios', 'android', 'blackberry' ]
@@ -55,49 +55,52 @@ if (cli_opts.plugins_dir || cli_opts.project) {
}
// only dump stack traces in debug mode, otherwise show error message and exit
-if (!cli_opts.debug) {
- // provide clean output on exceptions rather than dumping a stack trace
- process.on('uncaughtException', function(error){
+// provide clean output on exceptions rather than dumping a stack trace
+process.on('uncaughtException', function(error){
+ if (cli_opts.debug) {
+ console.error(error.stack);
+ } else {
console.error(error + '\n');
- process.exit(1);
- });
-}
+ }
+ process.exit(1);
+});
if (cli_opts.v) {
console.log(package.name + ' version ' + package.version);
}
else if (cli_opts.list) {
plugins.listAllPlugins(function(plugins) {
- for(var i = 0, j = plugins.length ; i < j ; i++) {
- console.log(plugins[i].value.name, '-', plugins[i].value.description);
- }
+ for(var i = 0, j = plugins.length ; i < j ; i++) {
+ console.log(plugins[i].value.name, '-', plugins[i].value.description);
+ }
});
}
else if (cli_opts.prepare && cli_opts.project) {
- plugman.handlePrepare(cli_opts.project, cli_opts.platform, plugins_dir);
+ plugman.prepare(cli_opts.project, cli_opts.platform, plugins_dir);
}
else if (cli_opts.remove) {
- plugman.removePlugin(cli_opts.plugin, plugins_dir);
+ plugman.remove(cli_opts.plugin, plugins_dir);
+ console.log('Plugin ' + cli_opts.plugin + ' deleted.');
}
else if (cli_opts.fetch) {
- plugman.fetchPlugin(cli_opts.plugin, plugins_dir, cli_opts.link);
+ plugman.fetch(cli_opts.plugin, plugins_dir, cli_opts.link);
}
else if (!cli_opts.platform || !cli_opts.project || !cli_opts.plugin) {
printUsage();
}
else if (cli_opts.uninstall) {
- plugman.handlePlugin('uninstall', cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir);
+ plugman.uninstall(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir);
}
else {
- var cli_variables = {}
- if (cli_opts.variable) {
- cli_opts.variable.forEach(function (variable) {
- var tokens = variable.split('=');
- var key = tokens.shift().toUpperCase();
- if (/^[\w-_]+$/.test(key)) cli_variables[key] = tokens.join('=');
+ var cli_variables = {}
+ if (cli_opts.variable) {
+ cli_opts.variable.forEach(function (variable) {
+ var tokens = variable.split('=');
+ var key = tokens.shift().toUpperCase();
+ if (/^[\w-_]+$/.test(key)) cli_variables[key] = tokens.join('=');
});
- }
- plugman.handlePlugin('install', cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, cli_variables);
+ }
+ plugman.install(cli_opts.platform, cli_opts.project, cli_opts.plugin, plugins_dir, cli_variables);
}
function printUsage() {
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 0264af9..691e829 100644
--- a/package.json
+++ b/package.json
@@ -5,9 +5,9 @@
"version": "0.5.7",
"repository": {
"type": "git",
- "url": "git://github.com/imhotep/plugman.git"
+ "url": "git://git-wip-us.apache.org/repos/asf/cordova-plugman.git"
},
- "main": "main.js",
+ "main": "plugman.js",
"engines": {
"node": ">=0.6.7"
},
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/platforms/android.js
----------------------------------------------------------------------
diff --git a/platforms/android.js b/platforms/android.js
deleted file mode 100644
index 885291a..0000000
--- a/platforms/android.js
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- *
- * Copyright 2013 Anis Kadri
- *
- * 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.
- *
-*/
-
-var fs = require('fs') // use existsSync in 0.6.x
- , path = require('path')
- , util = require('util')
- , shell = require('shelljs')
- , et = require('elementtree')
- , getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes'))
- , searchAndReplace = require(path.join(__dirname, '..', 'util', 'search-and-replace'))
- , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'))
- , assetsDir = 'assets/www'
- , sourceDir = 'src';
-
-exports.handlePlugin = function (action, project_dir, plugin_dir, plugin_et, variables) {
- var plugin_id = plugin_et._root.attrib['id']
- , version = plugin_et._root.attrib['version']
- , external_hosts = []
- , i = 0
- , PACKAGE_NAME = androidPackageName(project_dir)
-
-
- var platformTag = plugin_et.find('./platform[@name="android"]');
- if (!platformTag) {
- // Either this plugin doesn't support this platform, or it's a JS-only plugin.
- // Either way, return now.
- return;
- }
-
- var sourceFiles = platformTag.findall('./source-file')
- , libFiles = platformTag.findall('./library-file')
- , configChanges = getConfigChanges(platformTag);
-
- variables = variables || {}
-
- // get config.xml filename
- var config_xml_filename = 'res/xml/config.xml';
- if(fs.existsSync(path.resolve(project_dir, 'res/xml/plugins.xml'))) {
- config_xml_filename = 'res/xml/plugins.xml';
- }
-
- // collision detection
- if(action.match(/force-/) == null) {
- if(action == "install" && pluginInstalled(plugin_et, project_dir, config_xml_filename)) {
- throw new Error("Plugin "+plugin_id+" already installed");
- } else if(action == "uninstall" && !pluginInstalled(plugin_et, project_dir, config_xml_filename)) {
- throw new Error("Plugin "+plugin_id+" not installed");
- }
- } else {
- action = action.replace('force-', '');
- }
-
- root = et.Element("config-file");
- root.attrib['parent'] = '.'
- plugin_et.findall('./access').forEach(function (tag) {
- root.append(tag);
- });
-
- if (root.len()) {
- (configChanges[config_xml_filename]) ?
- configChanges[config_xml_filename].push(root) :
- configChanges[config_xml_filename] = [root];
- }
-
- // find which config-files we're interested in
- Object.keys(configChanges).forEach(function (configFile) {
- if (!fs.existsSync(path.resolve(project_dir, configFile))) {
- delete configChanges[configFile];
- }
- });
-
- // move source files
- sourceFiles.forEach(function (sourceFile) {
-
- var srcDir = path.resolve(project_dir,
- sourceFile.attrib['target-dir'])
- , destFile = path.resolve(srcDir,
- path.basename(sourceFile.attrib['src']));
-
- if (action == 'install') {
- shell.mkdir('-p', srcDir);
- var srcFile = srcPath(plugin_dir, sourceFile.attrib['src']);
- shell.cp(srcFile, destFile);
- } else {
- fs.unlinkSync(destFile);
- // check if directory is empty
- var curDir = srcDir;
- while(curDir !== path.join(project_dir, 'src')) {
- if(fs.readdirSync(curDir).length == 0) {
- fs.rmdirSync(curDir);
- curDir = path.resolve(path.join(curDir, '..'));
- } else {
- // directory not empty...do nothing
- break;
- }
- }
- }
- })
-
- // move library files
- libFiles.forEach(function (libFile) {
- var libDir = path.resolve(project_dir,
- libFile.attrib['target-dir'])
-
- if (action == 'install') {
- shell.mkdir('-p', libDir);
- var src = path.resolve(plugin_dir,
- libFile.attrib['src']),
- dest = path.resolve(libDir,
- path.basename(libFile.attrib['src']));
-
- shell.cp(src, dest);
- } else {
- var destFile = path.resolve(libDir,
- path.basename(libFile.attrib['src']));
-
- fs.unlinkSync(destFile);
- // check if directory is empty
- var files = fs.readdirSync(libDir);
- if(files.length == 0) {
- shell.rm('-rf', libDir);
- }
- }
- })
-
- // edit configuration files
- Object.keys(configChanges).forEach(function (filename) {
- var filepath = path.resolve(project_dir, filename),
- xmlDoc = xml_helpers.parseElementtreeSync(filepath),
- output;
-
- configChanges[filename].forEach(function (configNode) {
- var selector = configNode.attrib["parent"],
- children = configNode.findall('*');
-
- if( action == 'install') {
- if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
- throw new Error('failed to add children to ' + filename);
- }
- } else {
- if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
- throw new Error('failed to remove children from' + filename);
- }
- }
- });
-
- output = xmlDoc.write({indent: 4});
- fs.writeFileSync(filepath, output);
- });
-
- if (action == 'install') {
- variables['PACKAGE_NAME'] = androidPackageName(project_dir);
- searchAndReplace(path.resolve(project_dir, config_xml_filename), variables);
- searchAndReplace(path.resolve(project_dir, 'AndroidManifest.xml'), variables);
- }
-
- // Remove all assets and JS modules installed by this plugin.
- if (action == 'uninstall') {
- var assets = plugin_et.findall('./asset');
- assets && assets.forEach(function(asset) {
- var target = asset.attrib.target;
- shell.rm('-rf', path.join(project_dir, assetsDir, target));
- });
-
- shell.rm('-rf', path.join(project_dir, assetsDir, 'plugins', plugin_id));
- }
-}
-
-
-function srcPath(pluginPath, filename) {
- return path.resolve(pluginPath, filename);
-}
-
-// reads the package name out of the Android Manifest file
-// @param string project_dir the absolute path to the directory containing the project
-// @return string the name of the package
-function androidPackageName(project_dir) {
- var mDoc = xml_helpers.parseElementtreeSync(
- path.resolve(project_dir, 'AndroidManifest.xml'));
-
- return mDoc._root.attrib['package'];
-}
-
-function pluginInstalled(plugin_et, project_dir, config_xml_filename) {
- var tag_xpath = util.format('./platform[@name="android"]/config-file[@target="%s"]/plugin', config_xml_filename);
-
- var plugin_tag = plugin_et.find(tag_xpath);
- if (!plugin_tag) {
- return false;
- }
- var plugin_name = plugin_tag.attrib.name;
-
- return (fs.readFileSync(path.resolve(project_dir, config_xml_filename), 'utf8')
- .match(new RegExp(plugin_name, "g")) != null);
-}
-
-exports.www_dir = function(project_dir) {
- return path.join(project_dir, 'assets', 'www');
-};
-
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/platforms/blackberry.js
----------------------------------------------------------------------
diff --git a/platforms/blackberry.js b/platforms/blackberry.js
deleted file mode 100644
index 03652b5..0000000
--- a/platforms/blackberry.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- *
- * Copyright 2013 Anis Kadri
- *
- * 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.
- *
-*/
-
-var fs = require('fs') // use existsSync in 0.6.x
- , path = require('path')
- , shell = require('shelljs')
- , et = require('elementtree')
- , getConfigChanges = require('../util/config-changes')
- , sourceDir = 'src'
- , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'));
-
-
-exports.handlePlugin = function (action, project_dir, plugin_dir, plugin_et) {
- var plugin_id = plugin_et._root.attrib['id']
- , version = plugin_et._root.attrib['version']
- , external_hosts = []
- , platformTag = plugin_et.find('./platform[@name="blackberry"]')
-
- if (!platformTag) {
- // Either this plugin doesn't support this platform, or it's a JS-only plugin.
- // Either way, return now.
- return;
- }
-
- var sourceFiles = platformTag.findall('./source-file')
- , libFiles = platformTag.findall('./library-file')
- , configChanges = getConfigChanges(platformTag);
- // find which config-files we're interested in
- Object.keys(configChanges).forEach(function (configFile) {
- if (!fs.existsSync(path.resolve(project_dir, configFile))) {
- delete configChanges[configFile];
- }
- });
-
- // collision detection
- if(action.match(/force-/) == null) {
- if(action == "install" && pluginInstalled(plugin_et, project_dir)) {
- throw new Error("Plugin "+plugin_id+" already installed");
- } else if(action == "uninstall" && !pluginInstalled(plugin_et, project_dir)) {
- throw new Error("Plugin "+plugin_id+" not installed");
- }
- } else {
- action = action.replace('force-', '');
- }
-
- // move source files
- sourceFiles.forEach(function (sourceFile) {
- var srcDir = path.resolve(project_dir,
- sourceFile.attrib['target-dir'])
- , destFile = path.resolve(srcDir,
- path.basename(sourceFile.attrib['src']));
-
- if (action == 'install') {
- shell.mkdir('-p', srcDir);
- var srcFile = srcPath(plugin_dir, sourceFile.attrib['src']);
- shell.cp(srcFile, destFile);
- } else {
- fs.unlinkSync(destFile);
- // check if directory is empty
- var curDir = srcDir;
- while(curDir !== path.join(project_dir, 'src')) {
- if(fs.readdirSync(curDir).length == 0) {
- fs.rmdirSync(curDir);
- curDir = path.resolve(curDir, '..');
- } else {
- // directory not empty...do nothing
- break;
- }
- }
- }
- })
-
- // move library files
- libFiles.forEach(function (libFile) {
- var libDir = path.resolve(project_dir,
- libFile.attrib['target-dir'])
-
- if (action == 'install') {
- shell.mkdir('-p', libDir);
-
- var src = path.resolve(plugin_dir,
- libFile.attrib['src']),
- dest = path.resolve(libDir,
- path.basename(libFile.attrib['src']));
-
- shell.cp(src, dest);
- } else {
- var destFile = path.resolve(libDir,
- path.basename(libFile.attrib['src']));
-
- fs.unlinkSync(destFile);
- // check if directory is empty
- var files = fs.readdirSync(libDir);
- if(files.length == 0) {
- shell.rm('-rf', libDir);
- }
- }
- })
-
-
- // edit configuration files
- Object.keys(configChanges).forEach(function (filename) {
- var filepath = path.resolve(project_dir, filename),
- xmlDoc = xml_helpers.parseElementtreeSync(filepath),
- output;
-
- configChanges[filename].forEach(function (configNode) {
- var selector = configNode.attrib["parent"],
- children = configNode.findall('*');
-
- if( action == 'install') {
- if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
- throw new Error('failed to add children to ' + filename);
- }
- } else {
- if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
- throw new Error('failed to remove children from' + filename);
- }
- }
- });
-
- output = xmlDoc.write({indent: 4});
- fs.writeFileSync(filepath, output);
- });
-
- // Remove all assets and JS modules installed by this plugin.
- if (action == 'uninstall') {
- var assets = plugin_et.findall('./asset');
- assets && assets.forEach(function(asset) {
- var target = asset.attrib.target;
- shell.rm('-rf', path.join(project_dir, 'www', target));
- });
-
- shell.rm('-rf', path.join(project_dir, 'www', 'plugins', plugin_id));
- }
-}
-
-
-function srcPath(pluginPath, filename) {
- return path.resolve(pluginPath, filename);
-}
-
-function pluginInstalled(plugin_et, project_dir) {
- var config_tag = plugin_et.find('./platform[@name="blackberry"]/config-file[@target="config.xml"]/feature');
- if (!config_tag) {
- return false;
- }
- var plugin_name = config_tag.attrib.id;
- return (fs.readFileSync(path.resolve(project_dir, 'config.xml'), 'utf8')
- .match(new RegExp(plugin_name, "g")) != null);
-}
-
-exports.www_dir = function(project_dir) {
- return path.join(project_dir, 'www');
-};
-
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/platforms/ios.js
----------------------------------------------------------------------
diff --git a/platforms/ios.js b/platforms/ios.js
deleted file mode 100644
index dda2110..0000000
--- a/platforms/ios.js
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- *
- * Copyright 2013 Anis Kadri
- *
- * 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.
- *
-*/
-
-var path = require('path')
- , fs = require('../util/fs') // use existsSync in 0.6.x
- , glob = require('glob')
- , et = require('elementtree')
- , xcode = require('xcode')
- , plist = require('plist')
- , bplist = require('bplist-parser')
- , shell = require('shelljs')
- , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'))
- , searchAndReplace = require(path.join(__dirname, '..', 'util', 'search-and-replace'))
- , getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes'));
-
-exports.handlePlugin = function (action, project_dir, plugin_dir, plugin_et, variables) {
- var plugin_id = plugin_et._root.attrib['id']
- , version = plugin_et._root.attrib['version']
- , i = 0
- , matched;
-
- variables = variables || {}
-
- var platformTag = plugin_et.find('./platform[@name="ios"]');
-
- if (!platformTag) {
- // Either this plugin doesn't support this platform, or it's a JS-only plugin.
- // Either way, return now.
- return;
- }
-
- var sourceFiles = platformTag.findall('./source-file'),
- headerFiles = platformTag.findall('./header-file'),
- resourceFiles = platformTag.findall('./resource-file'),
- frameworks = platformTag.findall('./framework');
-
- // grab and parse pbxproj
- // we don't want CordovaLib's xcode project
- var project_files = glob.sync(path.join(project_dir, '*.xcodeproj', 'project.pbxproj'));
-
- if (!project_files.length) throw new Error("does not appear to be an xcode project (no xcode project file)");
- var pbxPath = project_files[0];
-
- var xcodeproj = xcode.project(project_files[0]);
- xcodeproj.parseSync();
-
- // grab and parse plist file or config.xml
- var config_files = (glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')).length == 0 ?
- glob.sync(path.join(project_dir, '**', 'config.xml')) :
- glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist'))
- );
-
- config_files = config_files.filter(function (val) {
- return !(/^build\//.test(val));
- });
-
- if (!config_files.length) {
- throw new Error("does not appear to be a PhoneGap project");
- }
-
- var config_file = config_files[0];
- var xcode_dir = path.dirname(config_file);
- var pluginsDir = path.resolve(xcode_dir, 'Plugins');
- var resourcesDir = path.resolve(xcode_dir, 'Resources');
-
- // get project plist for package name
- var project_plists = glob.sync(xcode_dir + '/*-Info.plist');
- var projectPListPath = project_plists[0];
-
- // collision detection
- if(action.match(/force-/) == null) {
- if(action == "install" && pluginInstalled(plugin_et, config_file)) {
- throw new Error("Plugin "+plugin_id+" already installed");
- } else if(action == "uninstall" && !pluginInstalled(plugin_et, config_file)) {
- throw new Error("Plugin "+plugin_id+" not installed");
- }
- } else {
- action = action.replace('force-', '');
- }
-
- // move native files (source/header/resource)
- sourceFiles && sourceFiles.forEach(function (sourceFile) {
- var src = sourceFile.attrib['src'],
- srcFile = path.resolve(plugin_dir, src),
- targetDir = path.resolve(pluginsDir, getRelativeDir(sourceFile)),
- destFile = path.resolve(targetDir, path.basename(src));
-
- if (action == 'install') {
- xcodeproj.addSourceFile(path.join('Plugins', path.relative(pluginsDir, destFile)));
- shell.mkdir('-p', targetDir);
- checkLastCommand();
- shell.cp(srcFile, destFile);
- checkLastCommand();
- } else {
- xcodeproj.removeSourceFile(path.join('Plugins', path.basename(src)));
- if(fs.existsSync(destFile))
- fs.unlinkSync(destFile);
- shell.rm('-rf', targetDir);
- checkLastCommand();
- }
- });
-
- headerFiles && headerFiles.forEach(function (headerFile) {
- var src = headerFile.attrib['src'],
- srcFile = path.resolve(plugin_dir, src),
- targetDir = path.resolve(pluginsDir, getRelativeDir(headerFile)),
- destFile = path.resolve(targetDir, path.basename(src));
-
- if (action == 'install') {
- xcodeproj.addHeaderFile(path.join('Plugins', path.relative(pluginsDir, destFile)));
- shell.mkdir('-p', targetDir);
- checkLastCommand();
- shell.cp(srcFile, destFile);
- checkLastCommand();
- } else {
- xcodeproj.removeHeaderFile(path.join('Plugins', path.basename(src)));
- if(fs.existsSync(destFile))
- fs.unlinkSync(destFile);
- shell.rm('-rf', targetDir);
- checkLastCommand();
- }
- });
-
- resourceFiles && resourceFiles.forEach(function (resource) {
- var src = resource.attrib['src'],
- srcFile = path.resolve(plugin_dir, src),
- destFile = path.resolve(resourcesDir, path.basename(src));
-
- if (action == 'install') {
- xcodeproj.addResourceFile(path.join('Resources', path.basename(src)));
- var st = fs.statSync(srcFile);
- shell.mkdir('-p', resourcesDir);
- if (st.isDirectory()) {
- shell.cp('-R', srcFile, resourcesDir);
- checkLastCommand();
- } else {
- shell.cp(srcFile, destFile);
- checkLastCommand();
- }
- } else {
- xcodeproj.removeResourceFile(path.join('Resources', path.basename(src)));
- shell.rm('-rf', destFile);
- checkLastCommand();
- }
- });
-
- frameworks && frameworks.forEach(function (framework) {
- var src = framework.attrib['src'],
- weak = framework.attrib['weak'];
- if (action == 'install') {
- var opt = { weak: (weak && weak.toLowerCase() == 'true') };
- xcodeproj.addFramework(src, opt);
- } else {
- xcodeproj.removeFramework(src);
- }
- });
-
- // write out xcodeproj file
- fs.writeFileSync(pbxPath, xcodeproj.writeSync());
-
- // add plugin and whitelisted hosts
- try {
- updateConfig(action, config_file, plugin_et);
- } catch(e) {
- throw {
- name: "ConfigurationError",
- level: "ERROR",
- message: "Error updating configuration: "+e.message,
- }
- }
-
- if (action == 'install') {
- variables['PACKAGE_NAME'] = plist.parseFileSync(projectPListPath).CFBundleIdentifier;
- searchAndReplace(pbxPath, variables);
- searchAndReplace(projectPListPath, variables);
- searchAndReplace(config_file, variables);
- }
-
- // Remove all assets and JS modules installed by this plugin.
- if (action == 'uninstall') {
- var assets = plugin_et.findall('./asset');
- assets && assets.forEach(function(asset) {
- var target = asset.attrib.target;
- shell.rm('-rf', path.join(project_dir, 'www', target));
- });
-
- shell.rm('-rf', path.join(project_dir, 'www', 'plugins', plugin_id));
- }
-
-}
-
-function getRelativeDir(file) {
- var targetDir = file.attrib['target-dir'],
- preserveDirs = file.attrib['preserve-dirs'];
-
- if (preserveDirs && preserveDirs.toLowerCase() == 'true') {
- return path.dirname(file.attrib['src']);
- } else if (targetDir) {
- return targetDir;
- } else {
- return '';
- }
-}
-
-// determine if a plist file is binary
-function isBinaryPlist(filename) {
- // I wish there was a synchronous way to read only the first 6 bytes of a
- // file. This is wasteful :/
- var buf = '' + fs.readFileSync(filename, 'utf8');
- // binary plists start with a magic header, "bplist"
- return buf.substring(0, 6) === 'bplist';
-}
-
-function updatePlistFile(action, config_path, plugin_et) {
- var hosts = plugin_et.findall('./access'),
- platformTag = plugin_et.find('./platform[@name="ios"]'), // FIXME: can probably do better than this
- plistEle = platformTag.find('./plugins-plist'),
- external_hosts = [];
-
- // determine if this is a binary or ascii plist and choose the parser
- // this is temporary until binary support is added to node-plist
- var pl = (isBinaryPlist(config_path) ? bplist : plist);
-
- var plistObj = pl.parseFileSync(config_path);
-
- if (action == 'install') {
- // add hosts to whitelist (ExternalHosts) in plist
- hosts && hosts.forEach(function(host) {
- plistObj.ExternalHosts.push(host.attrib['origin']);
- });
-
- // add plugin to plist
- plistObj.Plugins[plistEle.attrib['key']] = plistEle.attrib['string'];
- } else {
- // remove hosts from whitelist (ExternalHosts) in plist
- // check each entry in external hosts, only add it to the plist if
- // it's not an entry added by this plugin
- for(i=0; i < plistObj.ExternalHosts.length;i++) {
- matched = false;
- hosts && hosts.forEach(function(host) {
- if(host === plistObj.ExternalHosts[i]) {
- matched = true;
- }
- });
- if (!matched) {
- external_hosts.push(plistObj.ExternalHosts[i]);
- }
- }
-
- // filtered the external hosts entries out, copy result
- plistObj.ExternalHosts = external_hosts;
-
- delete plistObj.Plugins[plistEle.attrib['key']];
- }
-
- // write out plist
- fs.writeFileSync(config_path, plist.build(plistObj));
-}
-
-function pluginInstalled(plugin_et, config_path) {
- var config_tag = plugin_et.find('./platform[@name="ios"]/config-file[@target="config.xml"]/plugin') ||
- plugin_et.find('./platform[@name="ios"]/plugins-plist');
- if (!config_tag) {
- return false;
- }
- var plugin_name = config_tag.attrib.name || config_tag.attrib.key;
- return (fs.readFileSync(config_path, 'utf8').match(new RegExp(plugin_name, "g")) != null);
-}
-
-function updateConfigXml(action, config_path, plugin_et) {
- var hosts = plugin_et.findall('./access'),
- platformTag = plugin_et.find('./platform[@name="ios"]'), // FIXME: can probably do better than this
- plistEle = platformTag.find('./plugins-plist'), // use this for older that have plugins-plist
- configChanges = getConfigChanges(platformTag),
- base_config_path = path.basename(config_path);
-
- // edit configuration files
- var xmlDoc = xml_helpers.parseElementtreeSync(config_path),
- output,
- pListOnly = plistEle;
-
- if (configChanges[path.basename(config_path)]) {
- configChanges[path.basename(config_path)].forEach(function (val) {
- if (val.find("plugin")) pListOnly = false;
- });
- }
-
- if (pListOnly) {
- // if the plugin supports the old plugins-plist element only
- var name = plistEle.attrib.key;
- var value = plistEle.attrib.string;
- var pluginsEl = xmlDoc.find('plugins');
- if ( action == 'install') {
- var new_plugin = new et.Element('plugin');
- new_plugin.attrib.name = name;
- new_plugin.attrib.value = value;
- pluginsEl.append(new_plugin);
- } else {
- var culprit = pluginsEl.find("plugin[@name='"+name+"']");
- pluginsEl.remove(0, culprit);
- }
- }
-
- // add whitelist hosts
- root = et.Element("config-file");
- root.attrib['parent'] = '.'
- hosts.forEach(function (tag) {
- root.append(tag);
- });
-
- if (root.len()) {
- (configChanges[path.basename(config_path)]) ?
- configChanges[path.basename(config_path)].push(root) :
- configChanges[path.basename(config_path)] = [root];
- }
-
- if (configChanges[path.basename(config_path)]) {
-
- configChanges[base_config_path].forEach(function (configNode) {
- var selector = configNode.attrib["parent"],
- children = configNode.findall('*');
- if( action == 'install') {
- if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
- throw new Error('failed to add children to ' + selector + ' in ' + config_path);
- }
- } else {
- if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
- throw new Error('failed to remove children from ' + selector + ' in ' + config_path);
- }
- }
- });
- }
-
- output = xmlDoc.write({indent: 4});
- fs.writeFileSync(config_path, output);
-}
-
-// updates plist file and/or config.xml
-function updateConfig(action, config_path, plugin_et) {
- if(path.basename(config_path) == "config.xml") {
- updateConfigXml(action, config_path, plugin_et);
- } else {
- updatePlistFile(action, config_path, plugin_et);
- }
-}
-// throws error if last command returns code != 0
-function checkLastCommand() {
- if(shell.error() != null) throw {name: "ShellError", message: shell.error()};
-}
-
-exports.www_dir = function(project_dir) {
- return path.join(project_dir, 'www');
-};
-
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/plugman.js
----------------------------------------------------------------------
diff --git a/plugman.js b/plugman.js
index a5bfb82..5ab22a2 100755
--- a/plugman.js
+++ b/plugman.js
@@ -18,136 +18,11 @@
*/
// copyright (c) 2013 Andrew Lunny, Adobe Systems
-var fs = require('fs')
- , path = require('path')
- , url = require('url')
- , package = require(path.join(__dirname, 'package'))
- , et = require('elementtree')
- , plugins = require('./util/plugins')
- , shell = require('shelljs')
- , plugin_loader = require('./util/plugin_loader')
- , platform_modules = {
- 'android': require('./platforms/android'),
- 'ios': require('./platforms/ios'),
- 'blackberry': require('./platforms/blackberry')
- };
-
-function execAction(action, platform, project_dir, plugin_dir, plugins_dir, cli_variables) {
- var xml_path = path.join(plugin_dir, 'plugin.xml')
- , xml_text = fs.readFileSync(xml_path, 'utf-8')
- , plugin_et = new et.ElementTree(et.XML(xml_text))
- , filtered_variables = {};
-
- if (action == 'install') {
- // checking preferences
- prefs = plugin_et.findall('./preference') || [];
- prefs = prefs.concat(plugin_et.findall('./platform[@name="'+platform+'"]/preference'));
- var missing_vars = [];
- prefs.forEach(function (pref) {
- var key = pref.attrib["name"].toUpperCase();
- if (cli_variables[key] == undefined)
- missing_vars.push(key)
- else
- filtered_variables[key] = cli_variables[key]
- })
- if (missing_vars.length > 0) {
- console.error('Variable missing: ' + missing_vars.join(", "));
- return;
- }
-
- if((info = plugin_et.find('./platform[@name="'+platform+'"]/info'))) {
- console.log(info.text);
- }
- }
-
- // run the platform-specific function
- try {
- platform_modules[platform].handlePlugin(action, project_dir, plugin_dir, plugin_et, filtered_variables);
- exports.handlePrepare(project_dir, platform, plugins_dir);
- console.log('plugin ' + action + 'ed');
- } catch(e) {
- var revert = (action == "install" ? "force-uninstall" : "force-install" );
- console.error("An error occurred for action", action, ":", e.message, "\nTrying to revert changes...");
- try {
- platform_modules[platform].handlePlugin(revert, project_dir, plugin_dir, plugin_et, filtered_variables);
- } catch(e) {
- console.log("Changes might have not been reverted: "+e.message);
- }
- }
-}
-
-exports.fetchPlugin = function(plugin_dir, plugins_dir, link) {
- // Ensure the containing directory exists.
- shell.mkdir('-p', plugins_dir);
-
- // clone from git repository
- if(plugin_dir.indexOf('https://') == 0 || plugin_dir.indexOf('git://') == 0) {
- if (link) {
- throw new Error('--link is not supported for git URLs');
- }
- plugin_dir = plugins.clonePluginGitRepo(plugin_dir, plugins_dir);
- } else { // Copy from the local filesystem.
- var dest = path.join(plugins_dir, path.basename(dest));
-
- shell.rm('-rf', dest);
- if (link) {
- fs.symlinkSync(path.resolve(plugin_dir), dest, 'dir');
- } else {
- shell.cp('-R', plugin_dir, plugins_dir); // Yes, not dest.
- }
-
- plugin_dir = dest;
- }
-};
-
-exports.removePlugin = function(name, plugins_dir) {
- var target = path.join(plugins_dir, name);
- var stat = fs.lstatSync(target);
-
- if (stat.isSymbolicLink()) {
- fs.unlinkSync(target);
- } else {
- shell.rm('-rf', target);
- }
- console.log('Plugin ' + name + ' deleted.');
-};
-
-exports.handlePlugin = function(action, platform, project_dir, name, plugins_dir, cli_variables) {
- var plugin_xml_path, async;
-
- // Check that the plugin has already been fetched.
- var plugin_dir = path.join(plugins_dir, name);
- if (!fs.existsSync(plugin_dir)) {
- throw new Error('Plugin ' + name + ' not found. You may need to --fetch it.');
- }
-
- plugin_xml_path = path.join(plugin_dir, 'plugin.xml');
- if (!fs.existsSync(plugin_xml_path)) {
- // try querying the plugin database
- async = true;
- plugins.getPluginInfo(plugin_dir,
- function(plugin_info) {
- execAction(action, platform, project_dir, plugins.clonePluginGitRepo(plugin_info.url), plugins_dir, cli_variables);
- },
- function(e) {
- throw new Error(action + ' failed. "' + plugin_xml_path + '" not found');
- }
- );
- }
-
- if (!platform_modules[platform]) {
- throw { name: "Platform Error", message: platform + " not supported" }
- }
-
- // check arguments and resolve file paths
- if(!async) {
- execAction(action, platform, project_dir, plugin_dir, plugins_dir, cli_variables);
- }
+module.exports = {
+ install: require('./src/install'),
+ uninstall:require('./src/uninstall'),
+ remove: require('./src/remove'),
+ fetch: require('./src/fetch'),
+ prepare: require('./src/prepare')
};
-
-exports.handlePrepare = function(project_dir, platform, plugins_dir) {
- var www_dir = platform_modules[platform].www_dir(project_dir);
- plugin_loader.handlePrepare(project_dir, plugins_dir, www_dir, platform);
-};
-
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/fetch.js
----------------------------------------------------------------------
diff --git a/src/fetch.js b/src/fetch.js
new file mode 100644
index 0000000..1cbdaca
--- /dev/null
+++ b/src/fetch.js
@@ -0,0 +1,29 @@
+var shell = require('shelljs'),
+ fs = require('fs'),
+ plugins = require('./util/plugins'),
+ path = require('path');
+
+module.exports = function fetchPlugin(plugin_dir, plugins_dir, link, callback) {
+ // Ensure the containing directory exists.
+ shell.mkdir('-p', plugins_dir);
+
+ // clone from git repository
+ if(plugin_dir.indexOf('https://') == 0 || plugin_dir.indexOf('git://') == 0) {
+ if (link) {
+ throw new Error('--link is not supported for git URLs');
+ }
+ plugins.clonePluginGitRepo(plugin_dir, plugins_dir, callback);
+ } else { // Copy from the local filesystem.
+ var dest = path.join(plugins_dir, path.basename(dest));
+
+ shell.rm('-rf', dest);
+ if (link) {
+ fs.symlinkSync(path.resolve(plugin_dir), dest, 'dir');
+ } else {
+ shell.cp('-R', plugin_dir, plugins_dir); // Yes, not dest.
+ }
+
+ plugin_dir = dest;
+ if (callback) callback(null);
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
new file mode 100644
index 0000000..d246b15
--- /dev/null
+++ b/src/install.js
@@ -0,0 +1,94 @@
+var path = require('path'),
+ fs = require('fs'),
+ et = require('elementtree'),
+ platform_modules = require('./platforms');
+
+function runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback) {
+ var xml_path = path.join(plugin_dir, 'plugin.xml')
+ , xml_text = fs.readFileSync(xml_path, 'utf-8')
+ , plugin_et = new et.ElementTree(et.XML(xml_text))
+ , filtered_variables = {}
+ , info;
+
+ // checking preferences
+ prefs = plugin_et.findall('./preference') || [];
+ prefs = prefs.concat(plugin_et.findall('./platform[@name="'+platform+'"]/preference'));
+ var missing_vars = [];
+ prefs.forEach(function (pref) {
+ var key = pref.attrib["name"].toUpperCase();
+ if (cli_variables[key] == undefined)
+ missing_vars.push(key)
+ else
+ filtered_variables[key] = cli_variables[key]
+ })
+ if (missing_vars.length > 0) {
+ var err = new Error('Variable(s) missing: ' + missing_vars.join(", "));
+ if (callback) {
+ callback(err);
+ return;
+ }
+ else throw err;
+ }
+
+ // Log out plugin INFO element contents in case additional install steps are necessary
+ if((info = plugin_et.find('./platform[@name="'+platform+'"]/info'))) {
+ console.log(info.text);
+ }
+
+ // run the platform-specific function
+ try {
+ platform_modules[platform].handleInstall(project_dir, plugin_dir, plugin_et, filtered_variables);
+ require('./../plugman').prepare(project_dir, platform, plugins_dir);
+ if (callback) callback();
+ } catch(e) {
+ var err;
+ try {
+ platform_modules[platform].forceUninstall(project_dir, plugin_dir, plugin_et, filtered_variables);
+ err = new Error('Error during installation of plugin, reverted all changes. Install error: ' + e.message + '\n' + e.stack + '\n----');
+ } catch(etwo) {
+ err = new Error('Error during installation of plugin, reverting changes also caused issues! Reversion probably incomplete. Install error: ' + e.message + '\n' + e.stack + ', reversion error: ' + etwo.message + '\n' + etwo.stack + '\n----');
+ }
+ if (callback) callback(err);
+ else throw err;
+ }
+}
+
+module.exports = function installPlugin(platform, project_dir, name, plugins_dir, cli_variables, callback) {
+ if (!platform_modules[platform]) {
+ var err = new Error(platform + " not supported.");
+ if (callback) {
+ callback(err);
+ return;
+ }
+ else throw err;
+ }
+
+ var plugin_dir = path.join(plugins_dir, name);
+ var plugin_xml_path = path.join(plugin_dir, 'plugin.xml');
+
+ // Check that the plugin has already been fetched.
+ if (!fs.existsSync(plugin_dir) || !fs.existsSync(plugin_xml_path)) {
+ // try querying the plugin database
+ plugins.getPluginInfo(plugin_dir,
+ function(err, plugin_info) {
+ if (err) {
+ var err_obj = new Error('install failed. plugin.xml at "' + plugin_xml_path + '" not found and couldnt query remote server for information about the plugin because ' + err.message);
+ if (callback) callback(err_obj);
+ else throw err_obj;
+ } else {
+ plugins.clonePluginGitRepo(plugin_info.url, plugins_dir, function(err, plugin_dir) {
+ if (err) {
+ var err_obj = new Error('install failed. plugin.xml at "' + plugin_xml_path + '" not found and couldnt clone plugin repo because ' + err.message);
+ if (callback) callback(err_obj);
+ else throw err_obj;
+ } else {
+ runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback);
+ }
+ });
+ }
+ }
+ );
+ } else {
+ runInstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback);
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/platforms.js
----------------------------------------------------------------------
diff --git a/src/platforms.js b/src/platforms.js
new file mode 100644
index 0000000..4ac29e3
--- /dev/null
+++ b/src/platforms.js
@@ -0,0 +1,5 @@
+module.exports = {
+ 'android': require('./platforms/android'),
+ 'ios': require('./platforms/ios'),
+ 'blackberry': require('./platforms/blackberry')
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/platforms/android.js
----------------------------------------------------------------------
diff --git a/src/platforms/android.js b/src/platforms/android.js
new file mode 100644
index 0000000..ac5bd3a
--- /dev/null
+++ b/src/platforms/android.js
@@ -0,0 +1,229 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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.
+ *
+*/
+
+var fs = require('fs') // use existsSync in 0.6.x
+ , path = require('path')
+ , util = require('util')
+ , shell = require('shelljs')
+ , et = require('elementtree')
+ , getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes'))
+ , searchAndReplace = require(path.join(__dirname, '..', 'util', 'search-and-replace'))
+ , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'))
+ , assetsDir = path.join('assets','www')
+ , sourceDir = 'src';
+
+module.exports = {
+ handleInstall:function(project_dir, plugin_dir, plugin_et, variables) {
+ handlePlugin('install', project_dir, plugin_dir, plugin_et, variables);
+ },
+ handleUninstall:function(project_dir, plugin_dir, plugin_et, variables) {
+ handlePlugin('uninstall', project_dir, plugin_dir, plugin_et, variables);
+ },
+ forceInstall:function(project_dir, plugin_dir, plugin_et, variables) {
+ handlePlugin('force-install', project_dir, plugin_dir, plugin_et, variables);
+ },
+ forceUninstall:function(project_dir, plugin_dir, plugin_et, variables) {
+ handlePlugin('force-uninstall', project_dir, plugin_dir, plugin_et, variables);
+ },
+ www_dir:function(project_dir) {
+ return path.join(project_dir, 'assets', 'www');
+ }
+};
+
+function handlePlugin(action, project_dir, plugin_dir, plugin_et, variables) {
+ var plugin_id = plugin_et._root.attrib['id']
+ , version = plugin_et._root.attrib['version']
+ , external_hosts = []
+ , i = 0
+ , PACKAGE_NAME = androidPackageName(project_dir)
+
+
+ var platformTag = plugin_et.find('./platform[@name="android"]');
+ if (!platformTag) {
+ // Either this plugin doesn't support this platform, or it's a JS-only plugin.
+ // Either way, return now.
+ return;
+ }
+
+ var sourceFiles = platformTag.findall('./source-file')
+ , libFiles = platformTag.findall('./library-file')
+ , configChanges = getConfigChanges(platformTag);
+
+ variables = variables || {}
+
+ // get config.xml filename
+ var config_xml_filename = 'res/xml/config.xml';
+ if(fs.existsSync(path.resolve(project_dir, 'res/xml/plugins.xml'))) {
+ config_xml_filename = 'res/xml/plugins.xml';
+ }
+
+ // collision detection
+ if(action.match(/force-/) == null) {
+ if(action == "install" && pluginInstalled(plugin_et, project_dir, config_xml_filename)) {
+ throw new Error("Plugin "+plugin_id+" already installed");
+ } else if(action == "uninstall" && !pluginInstalled(plugin_et, project_dir, config_xml_filename)) {
+ throw new Error("Plugin "+plugin_id+" not installed");
+ }
+ } else {
+ action = action.replace('force-', '');
+ }
+
+ root = et.Element("config-file");
+ root.attrib['parent'] = '.'
+ plugin_et.findall('./access').forEach(function (tag) {
+ root.append(tag);
+ });
+
+ if (root.len()) {
+ (configChanges[config_xml_filename]) ?
+ configChanges[config_xml_filename].push(root) :
+ configChanges[config_xml_filename] = [root];
+ }
+
+ // find which config-files we're interested in
+ Object.keys(configChanges).forEach(function (configFile) {
+ if (!fs.existsSync(path.resolve(project_dir, configFile))) {
+ delete configChanges[configFile];
+ }
+ });
+
+ // move source files
+ sourceFiles.forEach(function (sourceFile) {
+
+ var srcDir = path.resolve(project_dir,
+ sourceFile.attrib['target-dir'])
+ , destFile = path.resolve(srcDir,
+ path.basename(sourceFile.attrib['src']));
+
+ if (action == 'install') {
+ shell.mkdir('-p', srcDir);
+ var srcFile = srcPath(plugin_dir, sourceFile.attrib['src']);
+ shell.cp(srcFile, destFile);
+ } else {
+ fs.unlinkSync(destFile);
+ // check if directory is empty
+ var curDir = srcDir;
+ while(curDir !== path.join(project_dir, 'src')) {
+ if(fs.readdirSync(curDir).length == 0) {
+ fs.rmdirSync(curDir);
+ curDir = path.resolve(path.join(curDir, '..'));
+ } else {
+ // directory not empty...do nothing
+ break;
+ }
+ }
+ }
+ })
+
+ // move library files
+ libFiles.forEach(function (libFile) {
+ var libDir = path.resolve(project_dir,
+ libFile.attrib['target-dir'])
+
+ if (action == 'install') {
+ shell.mkdir('-p', libDir);
+ var src = path.resolve(plugin_dir,
+ libFile.attrib['src']),
+ dest = path.resolve(libDir,
+ path.basename(libFile.attrib['src']));
+
+ shell.cp(src, dest);
+ } else {
+ var destFile = path.resolve(libDir,
+ path.basename(libFile.attrib['src']));
+
+ fs.unlinkSync(destFile);
+ // check if directory is empty
+ var files = fs.readdirSync(libDir);
+ if(files.length == 0) {
+ shell.rm('-rf', libDir);
+ }
+ }
+ })
+
+ // edit configuration files
+ Object.keys(configChanges).forEach(function (filename) {
+ var filepath = path.resolve(project_dir, filename),
+ xmlDoc = xml_helpers.parseElementtreeSync(filepath),
+ output;
+
+ configChanges[filename].forEach(function (configNode) {
+ var selector = configNode.attrib["parent"],
+ children = configNode.findall('*');
+
+ if( action == 'install') {
+ if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
+ throw new Error('failed to add children to ' + filename);
+ }
+ } else {
+ if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
+ throw new Error('failed to remove children from' + filename);
+ }
+ }
+ });
+
+ output = xmlDoc.write({indent: 4});
+ fs.writeFileSync(filepath, output);
+ });
+
+ if (action == 'install') {
+ variables['PACKAGE_NAME'] = androidPackageName(project_dir);
+ searchAndReplace(path.resolve(project_dir, config_xml_filename), variables);
+ searchAndReplace(path.resolve(project_dir, 'AndroidManifest.xml'), variables);
+ }
+
+ // Remove all assets and JS modules installed by this plugin.
+ if (action == 'uninstall') {
+ var assets = plugin_et.findall('./asset');
+ assets && assets.forEach(function(asset) {
+ var target = asset.attrib.target;
+ shell.rm('-rf', path.join(project_dir, assetsDir, target));
+ });
+
+ shell.rm('-rf', path.join(project_dir, assetsDir, 'plugins', plugin_id));
+ }
+}
+
+
+function srcPath(pluginPath, filename) {
+ return path.resolve(pluginPath, filename);
+}
+
+// reads the package name out of the Android Manifest file
+// @param string project_dir the absolute path to the directory containing the project
+// @return string the name of the package
+function androidPackageName(project_dir) {
+ var mDoc = xml_helpers.parseElementtreeSync(
+ path.resolve(project_dir, 'AndroidManifest.xml'));
+
+ return mDoc._root.attrib['package'];
+}
+
+function pluginInstalled(plugin_et, project_dir, config_xml_filename) {
+ var tag_xpath = util.format('./platform[@name="android"]/config-file[@target="%s"]/plugin', config_xml_filename);
+
+ var plugin_tag = plugin_et.find(tag_xpath);
+ if (!plugin_tag) {
+ return false;
+ }
+ var plugin_name = plugin_tag.attrib.name;
+
+ return (fs.readFileSync(path.resolve(project_dir, config_xml_filename), 'utf8')
+ .match(new RegExp(plugin_name, "g")) != null);
+}
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/platforms/blackberry.js
----------------------------------------------------------------------
diff --git a/src/platforms/blackberry.js b/src/platforms/blackberry.js
new file mode 100644
index 0000000..03652b5
--- /dev/null
+++ b/src/platforms/blackberry.js
@@ -0,0 +1,172 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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.
+ *
+*/
+
+var fs = require('fs') // use existsSync in 0.6.x
+ , path = require('path')
+ , shell = require('shelljs')
+ , et = require('elementtree')
+ , getConfigChanges = require('../util/config-changes')
+ , sourceDir = 'src'
+ , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'));
+
+
+exports.handlePlugin = function (action, project_dir, plugin_dir, plugin_et) {
+ var plugin_id = plugin_et._root.attrib['id']
+ , version = plugin_et._root.attrib['version']
+ , external_hosts = []
+ , platformTag = plugin_et.find('./platform[@name="blackberry"]')
+
+ if (!platformTag) {
+ // Either this plugin doesn't support this platform, or it's a JS-only plugin.
+ // Either way, return now.
+ return;
+ }
+
+ var sourceFiles = platformTag.findall('./source-file')
+ , libFiles = platformTag.findall('./library-file')
+ , configChanges = getConfigChanges(platformTag);
+ // find which config-files we're interested in
+ Object.keys(configChanges).forEach(function (configFile) {
+ if (!fs.existsSync(path.resolve(project_dir, configFile))) {
+ delete configChanges[configFile];
+ }
+ });
+
+ // collision detection
+ if(action.match(/force-/) == null) {
+ if(action == "install" && pluginInstalled(plugin_et, project_dir)) {
+ throw new Error("Plugin "+plugin_id+" already installed");
+ } else if(action == "uninstall" && !pluginInstalled(plugin_et, project_dir)) {
+ throw new Error("Plugin "+plugin_id+" not installed");
+ }
+ } else {
+ action = action.replace('force-', '');
+ }
+
+ // move source files
+ sourceFiles.forEach(function (sourceFile) {
+ var srcDir = path.resolve(project_dir,
+ sourceFile.attrib['target-dir'])
+ , destFile = path.resolve(srcDir,
+ path.basename(sourceFile.attrib['src']));
+
+ if (action == 'install') {
+ shell.mkdir('-p', srcDir);
+ var srcFile = srcPath(plugin_dir, sourceFile.attrib['src']);
+ shell.cp(srcFile, destFile);
+ } else {
+ fs.unlinkSync(destFile);
+ // check if directory is empty
+ var curDir = srcDir;
+ while(curDir !== path.join(project_dir, 'src')) {
+ if(fs.readdirSync(curDir).length == 0) {
+ fs.rmdirSync(curDir);
+ curDir = path.resolve(curDir, '..');
+ } else {
+ // directory not empty...do nothing
+ break;
+ }
+ }
+ }
+ })
+
+ // move library files
+ libFiles.forEach(function (libFile) {
+ var libDir = path.resolve(project_dir,
+ libFile.attrib['target-dir'])
+
+ if (action == 'install') {
+ shell.mkdir('-p', libDir);
+
+ var src = path.resolve(plugin_dir,
+ libFile.attrib['src']),
+ dest = path.resolve(libDir,
+ path.basename(libFile.attrib['src']));
+
+ shell.cp(src, dest);
+ } else {
+ var destFile = path.resolve(libDir,
+ path.basename(libFile.attrib['src']));
+
+ fs.unlinkSync(destFile);
+ // check if directory is empty
+ var files = fs.readdirSync(libDir);
+ if(files.length == 0) {
+ shell.rm('-rf', libDir);
+ }
+ }
+ })
+
+
+ // edit configuration files
+ Object.keys(configChanges).forEach(function (filename) {
+ var filepath = path.resolve(project_dir, filename),
+ xmlDoc = xml_helpers.parseElementtreeSync(filepath),
+ output;
+
+ configChanges[filename].forEach(function (configNode) {
+ var selector = configNode.attrib["parent"],
+ children = configNode.findall('*');
+
+ if( action == 'install') {
+ if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
+ throw new Error('failed to add children to ' + filename);
+ }
+ } else {
+ if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
+ throw new Error('failed to remove children from' + filename);
+ }
+ }
+ });
+
+ output = xmlDoc.write({indent: 4});
+ fs.writeFileSync(filepath, output);
+ });
+
+ // Remove all assets and JS modules installed by this plugin.
+ if (action == 'uninstall') {
+ var assets = plugin_et.findall('./asset');
+ assets && assets.forEach(function(asset) {
+ var target = asset.attrib.target;
+ shell.rm('-rf', path.join(project_dir, 'www', target));
+ });
+
+ shell.rm('-rf', path.join(project_dir, 'www', 'plugins', plugin_id));
+ }
+}
+
+
+function srcPath(pluginPath, filename) {
+ return path.resolve(pluginPath, filename);
+}
+
+function pluginInstalled(plugin_et, project_dir) {
+ var config_tag = plugin_et.find('./platform[@name="blackberry"]/config-file[@target="config.xml"]/feature');
+ if (!config_tag) {
+ return false;
+ }
+ var plugin_name = config_tag.attrib.id;
+ return (fs.readFileSync(path.resolve(project_dir, 'config.xml'), 'utf8')
+ .match(new RegExp(plugin_name, "g")) != null);
+}
+
+exports.www_dir = function(project_dir) {
+ return path.join(project_dir, 'www');
+};
+
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/platforms/ios.js
----------------------------------------------------------------------
diff --git a/src/platforms/ios.js b/src/platforms/ios.js
new file mode 100644
index 0000000..dda2110
--- /dev/null
+++ b/src/platforms/ios.js
@@ -0,0 +1,370 @@
+/*
+ *
+ * Copyright 2013 Anis Kadri
+ *
+ * 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.
+ *
+*/
+
+var path = require('path')
+ , fs = require('../util/fs') // use existsSync in 0.6.x
+ , glob = require('glob')
+ , et = require('elementtree')
+ , xcode = require('xcode')
+ , plist = require('plist')
+ , bplist = require('bplist-parser')
+ , shell = require('shelljs')
+ , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'))
+ , searchAndReplace = require(path.join(__dirname, '..', 'util', 'search-and-replace'))
+ , getConfigChanges = require(path.join(__dirname, '..', 'util', 'config-changes'));
+
+exports.handlePlugin = function (action, project_dir, plugin_dir, plugin_et, variables) {
+ var plugin_id = plugin_et._root.attrib['id']
+ , version = plugin_et._root.attrib['version']
+ , i = 0
+ , matched;
+
+ variables = variables || {}
+
+ var platformTag = plugin_et.find('./platform[@name="ios"]');
+
+ if (!platformTag) {
+ // Either this plugin doesn't support this platform, or it's a JS-only plugin.
+ // Either way, return now.
+ return;
+ }
+
+ var sourceFiles = platformTag.findall('./source-file'),
+ headerFiles = platformTag.findall('./header-file'),
+ resourceFiles = platformTag.findall('./resource-file'),
+ frameworks = platformTag.findall('./framework');
+
+ // grab and parse pbxproj
+ // we don't want CordovaLib's xcode project
+ var project_files = glob.sync(path.join(project_dir, '*.xcodeproj', 'project.pbxproj'));
+
+ if (!project_files.length) throw new Error("does not appear to be an xcode project (no xcode project file)");
+ var pbxPath = project_files[0];
+
+ var xcodeproj = xcode.project(project_files[0]);
+ xcodeproj.parseSync();
+
+ // grab and parse plist file or config.xml
+ var config_files = (glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist')).length == 0 ?
+ glob.sync(path.join(project_dir, '**', 'config.xml')) :
+ glob.sync(path.join(project_dir, '**', '{PhoneGap,Cordova}.plist'))
+ );
+
+ config_files = config_files.filter(function (val) {
+ return !(/^build\//.test(val));
+ });
+
+ if (!config_files.length) {
+ throw new Error("does not appear to be a PhoneGap project");
+ }
+
+ var config_file = config_files[0];
+ var xcode_dir = path.dirname(config_file);
+ var pluginsDir = path.resolve(xcode_dir, 'Plugins');
+ var resourcesDir = path.resolve(xcode_dir, 'Resources');
+
+ // get project plist for package name
+ var project_plists = glob.sync(xcode_dir + '/*-Info.plist');
+ var projectPListPath = project_plists[0];
+
+ // collision detection
+ if(action.match(/force-/) == null) {
+ if(action == "install" && pluginInstalled(plugin_et, config_file)) {
+ throw new Error("Plugin "+plugin_id+" already installed");
+ } else if(action == "uninstall" && !pluginInstalled(plugin_et, config_file)) {
+ throw new Error("Plugin "+plugin_id+" not installed");
+ }
+ } else {
+ action = action.replace('force-', '');
+ }
+
+ // move native files (source/header/resource)
+ sourceFiles && sourceFiles.forEach(function (sourceFile) {
+ var src = sourceFile.attrib['src'],
+ srcFile = path.resolve(plugin_dir, src),
+ targetDir = path.resolve(pluginsDir, getRelativeDir(sourceFile)),
+ destFile = path.resolve(targetDir, path.basename(src));
+
+ if (action == 'install') {
+ xcodeproj.addSourceFile(path.join('Plugins', path.relative(pluginsDir, destFile)));
+ shell.mkdir('-p', targetDir);
+ checkLastCommand();
+ shell.cp(srcFile, destFile);
+ checkLastCommand();
+ } else {
+ xcodeproj.removeSourceFile(path.join('Plugins', path.basename(src)));
+ if(fs.existsSync(destFile))
+ fs.unlinkSync(destFile);
+ shell.rm('-rf', targetDir);
+ checkLastCommand();
+ }
+ });
+
+ headerFiles && headerFiles.forEach(function (headerFile) {
+ var src = headerFile.attrib['src'],
+ srcFile = path.resolve(plugin_dir, src),
+ targetDir = path.resolve(pluginsDir, getRelativeDir(headerFile)),
+ destFile = path.resolve(targetDir, path.basename(src));
+
+ if (action == 'install') {
+ xcodeproj.addHeaderFile(path.join('Plugins', path.relative(pluginsDir, destFile)));
+ shell.mkdir('-p', targetDir);
+ checkLastCommand();
+ shell.cp(srcFile, destFile);
+ checkLastCommand();
+ } else {
+ xcodeproj.removeHeaderFile(path.join('Plugins', path.basename(src)));
+ if(fs.existsSync(destFile))
+ fs.unlinkSync(destFile);
+ shell.rm('-rf', targetDir);
+ checkLastCommand();
+ }
+ });
+
+ resourceFiles && resourceFiles.forEach(function (resource) {
+ var src = resource.attrib['src'],
+ srcFile = path.resolve(plugin_dir, src),
+ destFile = path.resolve(resourcesDir, path.basename(src));
+
+ if (action == 'install') {
+ xcodeproj.addResourceFile(path.join('Resources', path.basename(src)));
+ var st = fs.statSync(srcFile);
+ shell.mkdir('-p', resourcesDir);
+ if (st.isDirectory()) {
+ shell.cp('-R', srcFile, resourcesDir);
+ checkLastCommand();
+ } else {
+ shell.cp(srcFile, destFile);
+ checkLastCommand();
+ }
+ } else {
+ xcodeproj.removeResourceFile(path.join('Resources', path.basename(src)));
+ shell.rm('-rf', destFile);
+ checkLastCommand();
+ }
+ });
+
+ frameworks && frameworks.forEach(function (framework) {
+ var src = framework.attrib['src'],
+ weak = framework.attrib['weak'];
+ if (action == 'install') {
+ var opt = { weak: (weak && weak.toLowerCase() == 'true') };
+ xcodeproj.addFramework(src, opt);
+ } else {
+ xcodeproj.removeFramework(src);
+ }
+ });
+
+ // write out xcodeproj file
+ fs.writeFileSync(pbxPath, xcodeproj.writeSync());
+
+ // add plugin and whitelisted hosts
+ try {
+ updateConfig(action, config_file, plugin_et);
+ } catch(e) {
+ throw {
+ name: "ConfigurationError",
+ level: "ERROR",
+ message: "Error updating configuration: "+e.message,
+ }
+ }
+
+ if (action == 'install') {
+ variables['PACKAGE_NAME'] = plist.parseFileSync(projectPListPath).CFBundleIdentifier;
+ searchAndReplace(pbxPath, variables);
+ searchAndReplace(projectPListPath, variables);
+ searchAndReplace(config_file, variables);
+ }
+
+ // Remove all assets and JS modules installed by this plugin.
+ if (action == 'uninstall') {
+ var assets = plugin_et.findall('./asset');
+ assets && assets.forEach(function(asset) {
+ var target = asset.attrib.target;
+ shell.rm('-rf', path.join(project_dir, 'www', target));
+ });
+
+ shell.rm('-rf', path.join(project_dir, 'www', 'plugins', plugin_id));
+ }
+
+}
+
+function getRelativeDir(file) {
+ var targetDir = file.attrib['target-dir'],
+ preserveDirs = file.attrib['preserve-dirs'];
+
+ if (preserveDirs && preserveDirs.toLowerCase() == 'true') {
+ return path.dirname(file.attrib['src']);
+ } else if (targetDir) {
+ return targetDir;
+ } else {
+ return '';
+ }
+}
+
+// determine if a plist file is binary
+function isBinaryPlist(filename) {
+ // I wish there was a synchronous way to read only the first 6 bytes of a
+ // file. This is wasteful :/
+ var buf = '' + fs.readFileSync(filename, 'utf8');
+ // binary plists start with a magic header, "bplist"
+ return buf.substring(0, 6) === 'bplist';
+}
+
+function updatePlistFile(action, config_path, plugin_et) {
+ var hosts = plugin_et.findall('./access'),
+ platformTag = plugin_et.find('./platform[@name="ios"]'), // FIXME: can probably do better than this
+ plistEle = platformTag.find('./plugins-plist'),
+ external_hosts = [];
+
+ // determine if this is a binary or ascii plist and choose the parser
+ // this is temporary until binary support is added to node-plist
+ var pl = (isBinaryPlist(config_path) ? bplist : plist);
+
+ var plistObj = pl.parseFileSync(config_path);
+
+ if (action == 'install') {
+ // add hosts to whitelist (ExternalHosts) in plist
+ hosts && hosts.forEach(function(host) {
+ plistObj.ExternalHosts.push(host.attrib['origin']);
+ });
+
+ // add plugin to plist
+ plistObj.Plugins[plistEle.attrib['key']] = plistEle.attrib['string'];
+ } else {
+ // remove hosts from whitelist (ExternalHosts) in plist
+ // check each entry in external hosts, only add it to the plist if
+ // it's not an entry added by this plugin
+ for(i=0; i < plistObj.ExternalHosts.length;i++) {
+ matched = false;
+ hosts && hosts.forEach(function(host) {
+ if(host === plistObj.ExternalHosts[i]) {
+ matched = true;
+ }
+ });
+ if (!matched) {
+ external_hosts.push(plistObj.ExternalHosts[i]);
+ }
+ }
+
+ // filtered the external hosts entries out, copy result
+ plistObj.ExternalHosts = external_hosts;
+
+ delete plistObj.Plugins[plistEle.attrib['key']];
+ }
+
+ // write out plist
+ fs.writeFileSync(config_path, plist.build(plistObj));
+}
+
+function pluginInstalled(plugin_et, config_path) {
+ var config_tag = plugin_et.find('./platform[@name="ios"]/config-file[@target="config.xml"]/plugin') ||
+ plugin_et.find('./platform[@name="ios"]/plugins-plist');
+ if (!config_tag) {
+ return false;
+ }
+ var plugin_name = config_tag.attrib.name || config_tag.attrib.key;
+ return (fs.readFileSync(config_path, 'utf8').match(new RegExp(plugin_name, "g")) != null);
+}
+
+function updateConfigXml(action, config_path, plugin_et) {
+ var hosts = plugin_et.findall('./access'),
+ platformTag = plugin_et.find('./platform[@name="ios"]'), // FIXME: can probably do better than this
+ plistEle = platformTag.find('./plugins-plist'), // use this for older that have plugins-plist
+ configChanges = getConfigChanges(platformTag),
+ base_config_path = path.basename(config_path);
+
+ // edit configuration files
+ var xmlDoc = xml_helpers.parseElementtreeSync(config_path),
+ output,
+ pListOnly = plistEle;
+
+ if (configChanges[path.basename(config_path)]) {
+ configChanges[path.basename(config_path)].forEach(function (val) {
+ if (val.find("plugin")) pListOnly = false;
+ });
+ }
+
+ if (pListOnly) {
+ // if the plugin supports the old plugins-plist element only
+ var name = plistEle.attrib.key;
+ var value = plistEle.attrib.string;
+ var pluginsEl = xmlDoc.find('plugins');
+ if ( action == 'install') {
+ var new_plugin = new et.Element('plugin');
+ new_plugin.attrib.name = name;
+ new_plugin.attrib.value = value;
+ pluginsEl.append(new_plugin);
+ } else {
+ var culprit = pluginsEl.find("plugin[@name='"+name+"']");
+ pluginsEl.remove(0, culprit);
+ }
+ }
+
+ // add whitelist hosts
+ root = et.Element("config-file");
+ root.attrib['parent'] = '.'
+ hosts.forEach(function (tag) {
+ root.append(tag);
+ });
+
+ if (root.len()) {
+ (configChanges[path.basename(config_path)]) ?
+ configChanges[path.basename(config_path)].push(root) :
+ configChanges[path.basename(config_path)] = [root];
+ }
+
+ if (configChanges[path.basename(config_path)]) {
+
+ configChanges[base_config_path].forEach(function (configNode) {
+ var selector = configNode.attrib["parent"],
+ children = configNode.findall('*');
+ if( action == 'install') {
+ if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
+ throw new Error('failed to add children to ' + selector + ' in ' + config_path);
+ }
+ } else {
+ if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
+ throw new Error('failed to remove children from ' + selector + ' in ' + config_path);
+ }
+ }
+ });
+ }
+
+ output = xmlDoc.write({indent: 4});
+ fs.writeFileSync(config_path, output);
+}
+
+// updates plist file and/or config.xml
+function updateConfig(action, config_path, plugin_et) {
+ if(path.basename(config_path) == "config.xml") {
+ updateConfigXml(action, config_path, plugin_et);
+ } else {
+ updatePlistFile(action, config_path, plugin_et);
+ }
+}
+// throws error if last command returns code != 0
+function checkLastCommand() {
+ if(shell.error() != null) throw {name: "ShellError", message: shell.error()};
+}
+
+exports.www_dir = function(project_dir) {
+ return path.join(project_dir, 'www');
+};
+
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/prepare.js
----------------------------------------------------------------------
diff --git a/src/prepare.js b/src/prepare.js
new file mode 100644
index 0000000..cb5474d
--- /dev/null
+++ b/src/prepare.js
@@ -0,0 +1,152 @@
+/**
+ 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 platform_modules = require('./platforms'),
+ path = require('path'),
+ fs = require('fs'),
+ shell = require('shelljs'),
+ ls = fs.readdirSync,
+ util = require('util'),
+ exec = require('child_process').exec,
+ et = require('elementtree');
+
+// Called on --prepare.
+// Sets up each plugin's Javascript code to be loaded properly.
+// Expects a path to the project (platforms/android in CLI, . in plugman-only),
+// a path to where the plugins are downloaded, the www dir, and the platform ('android', 'ios', etc.).
+module.exports = function handlePrepare(project_dir, platform, plugins_dir) {
+ // Process:
+ // - List all plugins in plugins_dir
+ // - Load and parse their plugin.xml files.
+ // - Skip those without support for this platform. (No <platform> tags means JS-only!)
+ // - Build a list of all their js-modules, including platform-specific js-modules.
+ // - For each js-module (general first, then platform) build up an object storing the path and any clobbers, merges and runs for it.
+ // - Write this object into www/plugins.json.
+ // - Cordova.js contains code to load them at runtime from that file.
+
+ var wwwDir = platform_modules[platform].www_dir(project_dir);
+ var plugins = ls(plugins_dir);
+
+ // This array holds all the metadata for each module and ends up in cordova_plugins.json
+ var moduleObjects = [];
+
+ plugins && plugins.forEach(function(plugin) {
+ var pluginDir = path.join(plugins_dir, plugin);
+ if(fs.statSync(pluginDir).isDirectory()){
+ var xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(pluginDir, 'plugin.xml'), 'utf-8')));
+
+ var plugin_id = xml.getroot().attrib.id;
+
+ // Copy all the <asset>s into the platform's www/
+ var assets = xml.findall('./asset');
+ assets && assets.forEach(function(asset) {
+ var target = asset.attrib.target;
+
+ var lastSlash = target.lastIndexOf('/');
+ var dirname = lastSlash < 0 ? '' : target.substring(0, lastSlash);
+ var basename = lastSlash < 0 ? target : target.substring(lastSlash + 1);
+
+ var targetDir = path.join(wwwDir, dirname);
+
+ shell.mkdir('-p', targetDir);
+
+ var srcFile = path.join(pluginDir, asset.attrib.src);
+ var targetFile = path.join(targetDir, basename);
+
+ var cpOptions = '-f';
+
+ if(fs.statSync(srcFile).isDirectory()){
+ shell.mkdir('-p',targetFile);
+ srcFile = srcFile+'/*';
+ cpOptions = '-Rf';
+ }
+
+ shell.cp(cpOptions, [srcFile], targetFile);
+
+ });
+
+ // And then add the plugins dir to the platform's www.
+ var platformPluginsDir = path.join(wwwDir, 'plugins');
+ shell.mkdir('-p', platformPluginsDir);
+
+ var generalModules = xml.findall('./js-module');
+ var platformTag = xml.find(util.format('./platform[@name="%s"]', platform));
+
+ generalModules = generalModules || [];
+ var platformModules = platformTag ? platformTag.findall('./js-module') : [];
+ var allModules = generalModules.concat(platformModules);
+
+ allModules.forEach(function(module) {
+ // Copy the plugin's files into the www directory.
+ var dirname = module.attrib.src;
+ var lastSlash = dirname.lastIndexOf('/');
+ if (lastSlash >= 0) {
+ dirname = dirname.substring(0, lastSlash);
+ } else {
+ dirname = ''; // Just the file, no subdir.
+ }
+
+ var dir = path.join(platformPluginsDir, plugin_id, dirname);
+ shell.mkdir('-p', dir);
+
+ // Read in the file, prepend the cordova.define, and write it back out.
+ var moduleName = plugin_id + '.';
+ if (module.attrib.name) {
+ moduleName += module.attrib.name;
+ } else {
+ var result = module.attrib.src.match(/([^\/]+)\.js/);
+ moduleName += result[1];
+ }
+
+ var scriptContent = fs.readFileSync(path.join(pluginDir, module.attrib.src), 'utf-8');
+ scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {' + scriptContent + '});\n';
+ fs.writeFileSync(path.join(platformPluginsDir, plugin_id, module.attrib.src), scriptContent, 'utf-8');
+
+ // Prepare the object for cordova_plugins.json.
+ var obj = {
+ file: path.join('plugins', plugin_id, module.attrib.src),
+ id: moduleName
+ };
+
+ // Loop over the children of the js-module tag, collecting clobbers, merges and runs.
+ module.getchildren().forEach(function(child) {
+ if (child.tag.toLowerCase() == 'clobbers') {
+ if (!obj.clobbers) {
+ obj.clobbers = [];
+ }
+ obj.clobbers.push(child.attrib.target);
+ } else if (child.tag.toLowerCase() == 'merges') {
+ if (!obj.merges) {
+ obj.merges = [];
+ }
+ obj.merges.push(child.attrib.target);
+ } else if (child.tag.toLowerCase() == 'runs') {
+ obj.runs = true;
+ }
+ });
+
+ // Add it to the list of module objects bound for cordova_plugins.json
+ moduleObjects.push(obj);
+ });
+ }
+ });
+
+ // Write out moduleObjects as JSON to cordova_plugins.json
+ fs.writeFileSync(path.join(wwwDir, 'cordova_plugins.json'), JSON.stringify(moduleObjects), 'utf-8');
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/remove.js
----------------------------------------------------------------------
diff --git a/src/remove.js b/src/remove.js
new file mode 100644
index 0000000..ada9afa
--- /dev/null
+++ b/src/remove.js
@@ -0,0 +1,14 @@
+var shell = require('shelljs'),
+ fs = require('fs'),
+ path = require('path');
+
+module.exports = function removePlugin(name, plugins_dir) {
+ var target = path.join(plugins_dir, name);
+ var stat = fs.lstatSync(target);
+
+ if (stat.isSymbolicLink()) {
+ fs.unlinkSync(target);
+ } else {
+ shell.rm('-rf', target);
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5f4bb9d5/src/uninstall.js
----------------------------------------------------------------------
diff --git a/src/uninstall.js b/src/uninstall.js
new file mode 100644
index 0000000..b51487c
--- /dev/null
+++ b/src/uninstall.js
@@ -0,0 +1,75 @@
+var path = require('path'),
+ fs = require('fs'),
+ platform_modules = require('./platforms');
+
+function runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback) {
+ var xml_path = path.join(plugin_dir, 'plugin.xml')
+ , xml_text = fs.readFileSync(xml_path, 'utf-8')
+ , plugin_et = new et.ElementTree(et.XML(xml_text))
+
+ // run the platform-specific function
+ try {
+ platform_modules[platform].handleUninstall(project_dir, plugin_dir, plugin_et);
+ require('./../plugman').prepare(project_dir, platform, plugins_dir);
+ if (callback) callback();
+ } catch(e) {
+ var err;
+ try {
+ platform_modules[platform].forceInstall(project_dir, plugin_dir, plugin_et, {});
+ err = new Error('Error during uninstallation of plugin, reverted all changes. Uninstall error: ' + e.message);
+ } catch(etwo) {
+ err = new Error('Error during uninstallation of plugin, reverting changes also caused issues! Reversion probably incomplete. Uninstall error: ' + e.message + ', reversion error: ' + etwo.message);
+ }
+ if (callback) callback(err);
+ else throw err;
+ }
+}
+
+module.exports = function uninstallPlugin(platform, project_dir, name, plugins_dir, cli_variables, callback) {
+ if (!platform_modules[platform]) {
+ var err = new Error(platform + " not supported.");
+ if (callback) {
+ callback(err);
+ return;
+ }
+ else throw err;
+ }
+
+ // Check that the plugin has already been fetched.
+ var plugin_dir = path.join(plugins_dir, name);
+ var plugin_xml_path = path.join(plugin_dir, 'plugin.xml');
+
+ if (!fs.existsSync(plugin_dir)) {
+ var err = new Error('Plugin ' + name + ' not found. Already uninstalled?');
+ if (callback) {
+ callback(err);
+ return;
+ }
+ else throw err;
+ }
+
+ if (!fs.existsSync(plugin_xml_path)) {
+ // try querying the plugin database
+ plugins.getPluginInfo(plugin_dir,
+ function(err, plugin_info) {
+ if (err) {
+ var err_obj = new Error('uninstall failed. plugin.xml at "' + plugin_xml_path + '" not found and couldnt query remote server for information about the plugin because ' + err.message);
+ if (callback) callback(err_obj);
+ else throw err_obj;
+ } else {
+ plugins.clonePluginGitRepo(plugin_info.url, plugins_dir, function(err, plugin_dir) {
+ if (err) {
+ var err_obj = new Error('uninstall failed. plugin.xml at "' + plugin_xml_path + '" not found and couldnt clone plugin repo because ' + err.message);
+ if (callback) callback(err_obj);
+ else throw err_obj;
+ } else {
+ runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback);
+ }
+ });
+ }
+ }
+ );
+ } else {
+ runUninstall(platform, project_dir, plugin_dir, plugins_dir, cli_variables, callback);
+ }
+};