You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by sg...@apache.org on 2014/09/26 23:20:01 UTC
[02/13] git commit: CB-6481 Added unified hooks support for cordova
app and plugins
CB-6481 Added unified hooks support for cordova app and plugins
* Hooks can be defined in .cordova/hooks/hook_type, hooks/hook_type directories, config.xml and plugins/.../plugin.xml
* Javascript hooks retrieved from config.xml and plugins/.../plugin.xml will be run via new module loader
* Introduced before_plugin_install, after_plugin_install and before_plugin_uninstall hooks
Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/a9724517
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/a9724517
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/a9724517
Branch: refs/heads/master
Commit: a9724517a5f8528308e44c1557c2e8326625ccb3
Parents: 8abfaf1
Author: daserge <da...@yandex.ru>
Authored: Wed Jul 9 16:08:59 2014 +0400
Committer: daserge <da...@yandex.ru>
Committed: Thu Sep 25 18:59:08 2014 +0400
----------------------------------------------------------------------
cordova-lib/src/PluginInfo.js | 20 +++
cordova-lib/src/configparser/ConfigParser.js | 24 +++
cordova-lib/src/cordova/build.js | 16 +-
cordova-lib/src/cordova/compile.js | 1 +
cordova-lib/src/cordova/emulate.js | 1 +
cordova-lib/src/cordova/lazy_load.js | 1 +
.../src/cordova/metadata/windows_parser.js | 1 +
cordova-lib/src/cordova/metadata/wp8_parser.js | 1 +
cordova-lib/src/cordova/platform.js | 1 +
cordova-lib/src/cordova/plugin.js | 1 +
cordova-lib/src/cordova/prepare.js | 1 +
cordova-lib/src/cordova/run.js | 1 +
cordova-lib/src/cordova/serve.js | 1 +
cordova-lib/src/hooks/Context.js | 52 ++++++
cordova-lib/src/hooks/Hooker.js | 94 +++++++++++
cordova-lib/src/hooks/ScriptsFinder.js | 158 ++++++++++++++++++
cordova-lib/src/hooks/ScriptsRunner.js | 162 +++++++++++++++++++
cordova-lib/src/plugman/install.js | 26 ++-
cordova-lib/src/plugman/uninstall.js | 27 +++-
19 files changed, 578 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/PluginInfo.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/PluginInfo.js b/cordova-lib/src/PluginInfo.js
index 94d6fb1..98e4fec 100644
--- a/cordova-lib/src/PluginInfo.js
+++ b/cordova-lib/src/PluginInfo.js
@@ -200,6 +200,26 @@ function PluginInfo(dirname) {
var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, cloneAttribs);
return libFiles;
}
+
+ // <script>
+ // Example:
+ // <script type="before_build" src="scripts/beforeBuild.js" />
+ self.getHookScripts = getHookScripts;
+ function getHookScripts(hook, platforms) {
+ var scriptElements = self._et.findall('./script');
+
+ if(platforms) {
+ platforms.forEach(function (platform) {
+ scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/script'));
+ });
+ }
+
+ function filterScriptByHookType(el) {
+ return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
+ }
+
+ return scriptElements.filter(filterScriptByHookType);
+ }
///// End of PluginInfo methods /////
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/configparser/ConfigParser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/configparser/ConfigParser.js b/cordova-lib/src/configparser/ConfigParser.js
index da6376e..e02c4e8 100644
--- a/cordova-lib/src/configparser/ConfigParser.js
+++ b/cordova-lib/src/configparser/ConfigParser.js
@@ -228,6 +228,29 @@ ConfigParser.prototype = {
getSplashScreens: function(platform) {
return this.getStaticResources(platform, 'splash');
},
+
+ /**
+ * 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('./script');
+
+ if(platforms) {
+ platforms.forEach(function (platform) {
+ scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/script'));
+ });
+ }
+
+ function filterScriptByHookType(el) {
+ return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
+ }
+
+ return scriptElements.filter(filterScriptByHookType);
+ },
/**
* Returns a list of features (IDs)
@@ -316,6 +339,7 @@ ConfigParser.prototype = {
}
},
+
/**
*This does not check for duplicate feature entries
*/
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/build.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/build.js b/cordova-lib/src/cordova/build.js
index 384d9e2..1469c6b 100644
--- a/cordova-lib/src/cordova/build.js
+++ b/cordova-lib/src/cordova/build.js
@@ -21,12 +21,12 @@
indent:4, unused:vars, latedef:nofunc
*/
-var cordova_util = require('./util'),
- hooker = require('./hooker');
+var cordovaUtil = require('./util'),
+ Hooker = require('../hooks/Hooker');
// Returns a promise.
module.exports = function build(options) {
- var projectRoot = cordova_util.cdProjectRoot();
+ var projectRoot = cordovaUtil.cdProjectRoot();
if (!options) {
options = {
@@ -36,16 +36,18 @@ module.exports = function build(options) {
};
}
- options = cordova_util.preProcessOptions(options);
+ options = cordovaUtil.preProcessOptions(options);
+
+ var hookOptions = { projectRoot: projectRoot, cordova: options };
// fire build hooks
- var hooks = new hooker(projectRoot);
- return hooks.fire('before_build', options)
+ var hooker = new Hooker(projectRoot);
+ return hooker.fire('before_build', hookOptions)
.then(function() {
return require('./cordova').raw.prepare(options);
}).then(function() {
return require('./cordova').raw.compile(options);
}).then(function() {
- return hooks.fire('after_build', options);
+ return hooker.fire('after_build', hookOptions);
});
};
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/compile.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/compile.js b/cordova-lib/src/cordova/compile.js
index 814d3eb..558b0c0 100644
--- a/cordova-lib/src/cordova/compile.js
+++ b/cordova-lib/src/cordova/compile.js
@@ -31,6 +31,7 @@ module.exports = function compile(options) {
var projectRoot = cordova_util.cdProjectRoot();
options = cordova_util.preProcessOptions(options);
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
var ret = hooks.fire('before_compile', options);
options.platforms.forEach(function(platform) {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/emulate.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/emulate.js b/cordova-lib/src/cordova/emulate.js
index 2f23197..651a434 100644
--- a/cordova-lib/src/cordova/emulate.js
+++ b/cordova-lib/src/cordova/emulate.js
@@ -32,6 +32,7 @@ module.exports = function emulate(options) {
var projectRoot = cordova_util.cdProjectRoot();
options = cordova_util.preProcessOptions(options);
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
return hooks.fire('before_emulate', options)
.then(function() {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/lazy_load.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/lazy_load.js b/cordova-lib/src/cordova/lazy_load.js
index 8ea791b..943adc8 100644
--- a/cordova-lib/src/cordova/lazy_load.js
+++ b/cordova-lib/src/cordova/lazy_load.js
@@ -186,6 +186,7 @@ function custom(platforms, platform) {
lib_dir = path.join(url, subdir);
return Q(lib_dir);
}
+ // TODO: Replace with unified Hooker
return hooker.fire('before_library_download', {
platform:platform,
url:url,
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/metadata/windows_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/windows_parser.js b/cordova-lib/src/cordova/metadata/windows_parser.js
index 353d68b..3e4a632 100644
--- a/cordova-lib/src/cordova/metadata/windows_parser.js
+++ b/cordova-lib/src/cordova/metadata/windows_parser.js
@@ -244,6 +244,7 @@ module.exports.prototype = {
var that = this;
var projectRoot = util.isCordova(process.cwd());
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
return hooks.fire('pre_package', { wwwPath:this.www_dir(), platforms: [this.isOldProjectTemplate ? 'windows8' : 'windows'] })
.then(function() {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/metadata/wp8_parser.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/metadata/wp8_parser.js b/cordova-lib/src/cordova/metadata/wp8_parser.js
index 4d5b332..9798a6e 100644
--- a/cordova-lib/src/cordova/metadata/wp8_parser.js
+++ b/cordova-lib/src/cordova/metadata/wp8_parser.js
@@ -209,6 +209,7 @@ module.exports.prototype = {
var that = this;
var projectRoot = util.isCordova(process.cwd());
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
return hooks.fire('pre_package', { wwwPath:this.www_dir(), platforms: ['wp8'] })
.then(function() {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/platform.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/platform.js b/cordova-lib/src/cordova/platform.js
index 487803e..7becd9e 100644
--- a/cordova-lib/src/cordova/platform.js
+++ b/cordova-lib/src/cordova/platform.js
@@ -203,6 +203,7 @@ function check(hooks, projectRoot) {
var result = Q.defer();
cordova.raw.create(scratch)
.then(function () {
+ // TODO: Replace with unified Hooker
var h = new hooker(scratch);
// Acquire the version number of each platform we have installed, and output that too.
Q.all(platforms_on_fs.map(function(p) {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/plugin.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/plugin.js b/cordova-lib/src/cordova/plugin.js
index 5d4562f..f608a38 100644
--- a/cordova-lib/src/cordova/plugin.js
+++ b/cordova-lib/src/cordova/plugin.js
@@ -59,6 +59,7 @@ module.exports = function plugin(command, targets, opts) {
opts.options = opts.options || [];
opts.plugins = [];
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
var platformList = cordova_util.listPlatforms(projectRoot);
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/prepare.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/prepare.js b/cordova-lib/src/cordova/prepare.js
index 8b4b545..1906d78 100644
--- a/cordova-lib/src/cordova/prepare.js
+++ b/cordova-lib/src/cordova/prepare.js
@@ -56,6 +56,7 @@ function prepare(options) {
});
options.paths = paths;
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
return hooks.fire('before_prepare', options)
.then(function() {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/run.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/run.js b/cordova-lib/src/cordova/run.js
index 8ccdb38..abde56f 100644
--- a/cordova-lib/src/cordova/run.js
+++ b/cordova-lib/src/cordova/run.js
@@ -32,6 +32,7 @@ module.exports = function run(options) {
var projectRoot = cordova_util.cdProjectRoot();
options = cordova_util.preProcessOptions(options);
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
return hooks.fire('before_run', options)
.then(function() {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/cordova/serve.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/cordova/serve.js b/cordova-lib/src/cordova/serve.js
index 26c114b..2d88d7b 100644
--- a/cordova-lib/src/cordova/serve.js
+++ b/cordova-lib/src/cordova/serve.js
@@ -247,6 +247,7 @@ module.exports = function server(port) {
var projectRoot = cordova_util.cdProjectRoot();
port = +port || 8000;
+ // TODO: Replace with unified Hooker
var hooks = new hooker(projectRoot);
hooks.fire('before_serve')
.then(function() {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/hooks/Context.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/hooks/Context.js b/cordova-lib/src/hooks/Context.js
new file mode 100644
index 0000000..936c9c1
--- /dev/null
+++ b/cordova-lib/src/hooks/Context.js
@@ -0,0 +1,52 @@
+/**
+ 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 Q = require('q'),
+ fs = require('fs'),
+ path = require('path'),
+ os = require('os'),
+ events = require('../events');
+
+/**
+ * Creates hook script context
+ * @constructor
+ * @param {String} hook The hook type
+ * @param {Object} opts Hook options
+ * @returns {Object} */
+function Context(hook, opts) {
+ this.hook = hook;
+ this.opts = opts;
+ this.cmdLine = process.argv.join(' ');
+ this.commonModules = {
+ Q: Q, fs: fs, path: path, os: os,
+ events: events, plugin: require('../cordova/plugin'),
+ util: require('util'),
+ cordovaUtil: require('../cordova/util')
+ };
+}
+
+/**
+ * Returns a required module
+ * @param {String} path Module path
+ * @returns {Object} */
+Context.prototype.requireCordovaModule = function (path) {
+ return require(path);
+};
+
+module.exports = Context;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/hooks/Hooker.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/hooks/Hooker.js b/cordova-lib/src/hooks/Hooker.js
new file mode 100644
index 0000000..9951e20
--- /dev/null
+++ b/cordova-lib/src/hooks/Hooker.js
@@ -0,0 +1,94 @@
+/**
+ 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 util = require('../cordova/util'),
+ events = require('../events'),
+ Q = require('q'),
+ plugin = require('../cordova/plugin'),
+ ScriptsFinder = require('./ScriptsFinder'),
+ ScriptsRunner = require('./ScriptsRunner'),
+ Context = require('./Context'),
+ CordovaError = require('../CordovaError');
+
+/**
+ * Tries to create a hooker for passed project root.
+ * @constructor
+ */
+function Hooker(projectRoot) {
+ if (!util.isCordova(projectRoot)) {
+ throw new CordovaError('Not a Cordova project ("' + projectRoot + '"), can\'t use hooks.');
+ }
+}
+
+/**
+ * Fires all event handlers and scripts for a passed hook type.
+ * Returns a promise.
+ */
+Hooker.prototype.fire = function fire(hook, opts) {
+ // args check
+ if (!hook) {
+ throw new CordovaError('hook type is not specified');
+ }
+ // execute hook event listeners first
+ return setPluginsProperty(opts).then(function(){
+ setCordovaVersionProperty(opts);
+
+ var handlers = events.listeners(hook);
+ return executeHandlersSerially(handlers, opts);
+ // then execute hook script files
+ }).then(function() {
+ var scripts = ScriptsFinder.getHookScripts(hook, opts);
+ var context = new Context(hook, opts);
+ return ScriptsRunner.runScriptsSerially(scripts, context);
+ });
+};
+
+/**
+ * Sets hook options cordova.plugins list if it was not set.
+ * Returns a promise.
+ */
+function setPluginsProperty(opts) {
+ if(!opts.cordova.plugins) {
+ return plugin().then(function(plugins) {
+ opts.cordova.plugins = plugins;
+ return Q();
+ });
+ }
+ return Q();
+}
+
+/**
+ * Sets hook options cordova.version if it was not set.
+ */
+function setCordovaVersionProperty(opts) {
+ opts.cordova.version = opts.cordova.version || require('../../package').version;
+}
+
+// Returns a promise.
+function executeHandlersSerially(handlers, opts) {
+ if (handlers.length) {
+ // Chain the handlers in series.
+ return handlers.reduce(function(soFar, f) {
+ return soFar.then(function() { return f(opts); });
+ }, Q());
+ } else {
+ return Q(); // Nothing to do.
+ }
+}
+
+module.exports = Hooker;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/hooks/ScriptsFinder.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/hooks/ScriptsFinder.js b/cordova-lib/src/hooks/ScriptsFinder.js
new file mode 100644
index 0000000..5fb9ed2
--- /dev/null
+++ b/cordova-lib/src/hooks/ScriptsFinder.js
@@ -0,0 +1,158 @@
+/**
+ 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 path = require('path'),
+ fs = require('fs'),
+ cordovaUtil = require('../cordova/util'),
+ events = require('../events'),
+ Q = require('q'),
+ plugin = require('../cordova/plugin'),
+ PluginInfo = require('../PluginInfo'),
+ ConfigParser = require('../configparser/ConfigParser'),
+ CordovaError = require('../CordovaError'),
+ Context = require('./Context');
+
+/**
+ * Implements logic to retrieve hook script files defined in special folders and configuration
+ * files: config.xml, hooks/hook_type, plugins/../plugin.xml, etc
+ */
+module.exports = {
+ /**
+ * Returns all script files for the hook type specified.
+ */
+ getHookScripts: function(hook, opts) {
+ // args check
+ if (!hook) {
+ throw new CordovaError('hook type is not specified');
+ }
+ return getApplicationHookScripts(hook, opts)
+ .concat(getPluginsHookScripts(hook, opts));
+ }
+};
+
+/**
+ * Returns script files defined on application level.
+ * They are stored in .cordova/hooks folders and in config.xml.
+ */
+function getApplicationHookScripts(hook, opts) {
+ // args check
+ if (!hook) {
+ throw new CordovaError('hook type is not specified');
+ }
+ return getApplicationHookScriptsFromDir(path.join(opts.projectRoot, '.cordova', 'hooks', hook))
+ .concat(getApplicationHookScriptsFromDir(path.join(opts.projectRoot, 'hooks', hook)))
+ .concat(getScriptsFromConfigXml(hook, opts));
+}
+
+/**
+ * Returns script files defined by plugin developers as part of plugin.xml.
+ */
+function getPluginsHookScripts(hook, opts) {
+ // args check
+ if (!hook) {
+ throw new CordovaError('hook type is not specified');
+ }
+
+ // In case before_plugin_install, after_plugin_install, before_plugin_uninstall hooks we receive opts.plugin and
+ // retrieve scripts exclusive for this plugin.
+ if(opts.plugin) {
+ events.emit('debug', 'Executing "' + hook + '" hook for "' + opts.plugin.id + '" on ' + opts.plugin.platform + '.');
+
+ return getPluginScriptFiles(opts.plugin, hook, [ opts.plugin.platform ]);
+ }
+
+ events.emit('debug', 'Executing "' + hook + '" hook for all plugins.');
+ return getAllPluginsHookScriptFiles(hook, opts);
+}
+
+/**
+ * Gets application level hooks from the directrory specified.
+ */
+function getApplicationHookScriptsFromDir(dir) {
+ if (!(fs.existsSync(dir))) {
+ return [];
+ }
+
+ var compareNumbers = function(a, b) {
+ // TODO SG looks very complex, do we really need this?
+ return isNaN (parseInt(a, 10)) ? a.toLowerCase().localeCompare(b.toLowerCase ? b.toLowerCase(): b)
+ : parseInt(a, 10) > parseInt(b, 10) ? 1 : parseInt(a, 10) < parseInt(b, 10) ? -1 : 0;
+ };
+
+ var scripts = fs.readdirSync(dir).sort(compareNumbers).filter(function(s) {
+ return s[0] != '.';
+ });
+ return scripts.map(function (scriptPath) {
+ // for old style hook files we don't use module loader for backward compatibility
+ return { path: scriptPath, fullPath: path.join(dir, scriptPath), useModuleLoader: false };
+ });
+}
+
+/**
+ * Gets all scripts defined in config.xml with the specified type and platforms.
+ */
+function getScriptsFromConfigXml(hook, opts) {
+ var configPath = cordovaUtil.projectConfig(opts.projectRoot);
+ var configXml = new ConfigParser(configPath);
+
+ return configXml.getHookScripts(hook, opts.cordova.platforms).map(function(scriptElement) {
+ return {
+ path: scriptElement.attrib.src,
+ fullPath: path.join(opts.projectRoot, scriptElement.attrib.src)
+ };
+ });
+}
+
+/**
+ * Gets hook scripts defined by the plugin.
+ */
+function getPluginScriptFiles(plugin, hook, platforms) {
+ var scriptElements = plugin.pluginInfo.getHookScripts(hook, platforms);
+
+ return scriptElements.map(function(scriptElement) {
+ return {
+ path: scriptElement.attrib.src,
+ fullPath: path.join(plugin.dir, scriptElement.attrib.src),
+ plugin: plugin
+ };
+ });
+}
+
+/**
+ * Gets hook scripts defined by all plugins.
+ */
+function getAllPluginsHookScriptFiles(hook, opts) {
+ var scripts = [];
+ var pluginDir;
+ var pluginInfo;
+ var currentPluginOptions;
+
+ opts.cordova.plugins.forEach(function(pluginId) {
+ pluginDir = path.join(opts.projectRoot, 'plugins', pluginId);
+
+ currentPluginOptions = {
+ id: pluginId,
+ pluginInfo: new PluginInfo.PluginInfo(pluginDir),
+ dir: pluginDir
+ };
+
+ scripts = scripts.concat(getPluginScriptFiles(currentPluginOptions, hook, opts.cordova.platforms));
+ });
+ return scripts;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/hooks/ScriptsRunner.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/hooks/ScriptsRunner.js b/cordova-lib/src/hooks/ScriptsRunner.js
new file mode 100644
index 0000000..c7ff70d
--- /dev/null
+++ b/cordova-lib/src/hooks/ScriptsRunner.js
@@ -0,0 +1,162 @@
+/**
+ 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 Q = require('q'),
+ fs = require('fs'),
+ os = require('os'),
+ path = require('path'),
+ superspawn = require('../cordova/superspawn'),
+ CordovaError = require('../CordovaError'),
+ Context = require('./Context');
+
+var isWindows = os.platform().slice(0, 3) === 'win';
+
+module.exports = {
+ /**
+ * Serially fires scripts either via Q(require(pathToScript)(context)) or via child_process.spawn.
+ * Returns promise.
+ */
+ runScriptsSerially: function(scripts, context) {
+ var deferral = new Q.defer();
+
+ function executePendingScript() {
+ try {
+ if (scripts.length === 0) {
+ deferral.resolve();
+ return;
+ }
+ var nextScript = scripts[0];
+ scripts.shift();
+
+ runScript(nextScript, context).then(executePendingScript, function(err){
+ deferral.reject(err);
+ });
+ } catch (ex) {
+ deferral.reject(ex);
+ }
+ }
+ executePendingScript();
+ return deferral.promise;
+ }
+};
+
+/**
+ * Async runs single script file.
+ */
+function runScript(script, context) {
+ if (typeof script.useModuleLoader == 'undefined') {
+ // if it is not explicitly defined whether we should use modeule loader or not
+ // we assume we should use module loader for .js files
+ script.useModuleLoader = path.extname(script.path).toLowerCase() == '.js';
+ }
+ if(script.useModuleLoader) {
+ return runScriptViaModuleLoader(script, context);
+ } else {
+ return runScriptViaChildProcessSpawn(script, context);
+ }
+}
+
+/**
+ * Runs script using require.
+ * Returns a promise. */
+function runScriptViaModuleLoader(script, context) {
+ if(!fs.existsSync(script.fullPath)) {
+ events.emit('warn', "Script file does't exist and will be skipped: " + script.fullPath);
+ return Q();
+ }
+ var scriptFn = require(script.fullPath);
+ context.scriptLocation = script.fullPath;
+ context.opts.plugin = script.plugin;
+
+ // We can't run script if it is a plain Node script - it will run its commands when we require it.
+ // This is not a desired case as we want to pass context, but added for compatibility.
+ if (scriptFn instanceof Function) {
+ // If hook is async it can return promise instance and we will handle it.
+ return Q(scriptFn(context));
+ } else {
+ return Q();
+ }
+}
+
+/**
+ * Runs script using child_process spawn method.
+ * Returns a promise. */
+function runScriptViaChildProcessSpawn(script, context) {
+ var opts = context.opts;
+ var command = script.fullPath;
+ var args = [opts.projectRoot];
+ if (isWindows) {
+ // TODO: Make shebang sniffing a setting (not everyone will want this).
+ var interpreter = extractSheBangInterpreter(script.fullPath);
+ // we have shebang, so try to run this script using correct interpreter
+ if (interpreter) {
+ args.unshift(command);
+ command = interpreter;
+ }
+ }
+
+ var execOpts = {cwd: opts.projectRoot, printCommand: true, stdio: 'inherit'};
+ execOpts.env = {};
+ execOpts.env.CORDOVA_VERSION = require('../../package').version;
+ execOpts.env.CORDOVA_PLATFORMS = opts.cordova.platforms ? opts.cordova.platforms.join() : '';
+ execOpts.env.CORDOVA_PLUGINS = opts.cordova.plugins ? opts.cordova.plugins.join() : '';
+ execOpts.env.CORDOVA_HOOK = script.fullPath;
+ execOpts.env.CORDOVA_CMDLINE = process.argv.join(' ');
+
+ return superspawn.spawn(command, args, execOpts)
+ .catch(function(err) {
+ // Don't treat non-executable files as errors. They could be READMEs, or Windows-only scripts.
+ if (!isWindows && err.code == 'EACCES') {
+ events.emit('verbose', 'skipped non-executable file: ' + script.fullPath);
+ } else {
+ throw new CordovaError('Hook failed with error code ' + err.code + ': ' + script.fullPath);
+ }
+ });
+}
+
+/**
+ * Extracts shebang interpreter from script' source. */
+function extractSheBangInterpreter(fullpath) {
+ var fileChunk;
+ var octetsRead;
+ var fileData;
+ var hookFd = fs.openSync(fullpath, "r");
+ try {
+ // this is a modern cluster size. no need to read less
+ fileData = new Buffer(4096);
+ octetsRead = fs.readSync(hookFd, fileData, 0, 4096, 0);
+ fileChunk = fileData.toString();
+ } finally {
+ fs.closeSync(hookFd);
+ }
+
+ var hookCmd, shMatch;
+ // Filter out /usr/bin/env so that "/usr/bin/env node" works like "node".
+ var shebangMatch = fileChunk.match(/^#!(?:\/usr\/bin\/env )?([^\r\n]+)/m);
+ if (octetsRead == 4096 && !fileChunk.match(/[\r\n]/))
+ events.emit('warn', 'shebang is too long for "' + fullpath + '"');
+ if (shebangMatch)
+ hookCmd = shebangMatch[1];
+ // Likewise, make /usr/bin/bash work like "bash".
+ if (hookCmd)
+ shMatch = hookCmd.match(/bin\/((?:ba)?sh)$/);
+ if (shMatch)
+ hookCmd = shMatch[1];
+ return hookCmd;
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/plugman/install.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/install.js b/cordova-lib/src/plugman/install.js
index 045afb7..4823b68 100644
--- a/cordova-lib/src/plugman/install.js
+++ b/cordova-lib/src/plugman/install.js
@@ -38,7 +38,9 @@ var path = require('path'),
shell = require('shelljs'),
events = require('../events'),
plugman = require('./plugman'),
- isWindows = (os.platform().substr(0,3) === 'win');
+ Hooker = require('../hooks/Hooker'),
+ isWindows = (os.platform().substr(0,3) === 'win'),
+ cordovaUtil = require('../cordova/util');
/* INSTALL FLOW
------------
@@ -319,7 +321,27 @@ function runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, opt
copyPlugin(plugin_dir, plugins_dir, options.link);
}
- return handleInstall(actions, pluginInfo, platform, project_dir, plugins_dir, install_plugin_dir, filtered_variables, options);
+ var projectRoot = cordovaUtil.isCordova();
+
+ // using unified hooker
+ var hookOptions = {
+ projectRoot: projectRoot,
+ cordova: { platforms: [ platform ], plugins: options.plugins },
+ plugin: {
+ id: pluginInfo.id,
+ pluginInfo: pluginInfo,
+ platform: install.platform,
+ dir: install.top_plugin_dir
+ }
+ };
+
+ var hooker = new Hooker(projectRoot);
+
+ hooker.fire('before_plugin_install', hookOptions).then(function() {
+ return handleInstall(actions, pluginInfo, platform, project_dir, plugins_dir, install_plugin_dir, filtered_variables, options);
+ }).then(function(){
+ return hooker.fire('after_plugin_install', hookOptions);
+ });
}
).fail(
function (error) {
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a9724517/cordova-lib/src/plugman/uninstall.js
----------------------------------------------------------------------
diff --git a/cordova-lib/src/plugman/uninstall.js b/cordova-lib/src/plugman/uninstall.js
index 06bcb85..0a48fa2 100644
--- a/cordova-lib/src/plugman/uninstall.js
+++ b/cordova-lib/src/plugman/uninstall.js
@@ -32,11 +32,13 @@ var path = require('path'),
CordovaError = require('../CordovaError'),
underscore = require('underscore'),
Q = require('q'),
- underscore = require('underscore'),
events = require('../events'),
platform_modules = require('./platforms'),
plugman = require('./plugman'),
- promiseutil = require('../util/promise-util');
+ promiseutil = require('../util/promise-util'),
+ Hooker = require('../hooks/Hooker'),
+ PluginInfo = require('../PluginInfo'),
+ cordovaUtil = require('../cordova/util');
// possible options: cli_variables, www_dir
// Returns a promise.
@@ -237,8 +239,29 @@ function runUninstallPlatform(actions, platform, project_dir, plugin_dir, plugin
promise = Q();
}
+ var projectRoot = cordovaUtil.isCordova();
+ var pluginInfo = new PluginInfo.PluginInfo(plugin_dir);
+
+ // using unified hooker
+ var hookerOptions = {
+ projectRoot: projectRoot,
+ cordova: { platforms: [ platform ], plugins: options.plugins },
+ plugin: {
+ id: pluginInfo.id,
+ pluginInfo: pluginInfo,
+ platform: platform,
+ dir: plugin_dir
+ }
+ };
+
+ var hooker = new Hooker(projectRoot);
+
return promise.then(function() {
+ return hooker.fire('before_plugin_uninstall', hookerOptions);
+ }).then(function() {
return handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, options.www_dir, plugins_dir, plugin_dir, options.is_top_level, options);
+ }).then(function(){
+ return hooker.fire('after_plugin_uninstall', hookerOptions);
});
}