CB-11138 Reuse PluginManager from common to add/rm plugins

This closes #301
This commit is contained in:
Vladimir Kotikov 2016-04-22 16:03:02 +03:00
parent dc6384d063
commit d351e316bf
4 changed files with 42 additions and 214 deletions

View File

@ -17,19 +17,11 @@
under the License.
*/
var Q = require('q');
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var CordovaError = require('cordova-common').CordovaError;
var PlatformJson = require('cordova-common').PlatformJson;
var ActionStack = require('cordova-common').ActionStack;
var AndroidProject = require('./lib/AndroidProject');
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
var PluginManager = require('cordova-common').PluginManager;
var pluginHandlers = require('./lib/pluginHandlers');
var CordovaLogger = require('cordova-common').CordovaLogger;
var selfEvents = require('cordova-common').events;
@ -65,10 +57,6 @@ function Api(platform, platformRootDir, events) {
setupEvents(events);
this._platformJson = PlatformJson.load(this.root, platform);
this._pluginInfoProvider = new PluginInfoProvider();
this._munger = new PlatformMunger(this.platform, this.root, this._platformJson, this._pluginInfoProvider);
var self = this;
this.locations = {
@ -197,59 +185,25 @@ Api.prototype.prepare = function (cordovaProject) {
* CordovaError instance.
*/
Api.prototype.addPlugin = function (plugin, installOptions) {
if (!plugin || plugin.constructor.name !== 'PluginInfo')
return Q.reject(new CordovaError('The parameter is incorrect. The first parameter to addPlugin should be a PluginInfo instance'));
var project = AndroidProject.getProjectFile(this.root);
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
var self = this;
var actions = new ActionStack();
var project = AndroidProject.getProjectFile(this.root);
return PluginManager.get(this.platform, this.locations, project)
.addPlugin(plugin, installOptions)
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
// gather all files needs to be handled during install
plugin.getFilesAndFrameworks(this.platform)
.concat(plugin.getAssets(this.platform))
.concat(plugin.getJsModules(this.platform))
.forEach(function(item) {
actions.push(actions.createAction(
pluginHandlers.getInstaller(item.itemType), [item, plugin, project, installOptions],
pluginHandlers.getUninstaller(item.itemType), [item, plugin, project, installOptions]));
});
// run through the action stack
return actions.process(this.platform)
.then(function () {
if (project) {
project.write();
}
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
self._munger
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
.add_plugin_changes(plugin, installOptions.variables, /*is_top_level=*/true, /*should_increment=*/true)
.save_all();
if (plugin.getFrameworks(self.platform).length > 0) {
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
}
var targetDirs = [self.locations.www];
// CB-11022 if usePlatformWww is specified we need to mirror modules metadata to both directories
if (installOptions.usePlatformWww) targetDirs.push(self.locations.platformWww);
self._addModulesInfo(plugin, targetDirs);
// CB-11022 Indicate to caller that we have copied js-files and assets
// to both 'www' and 'platform_www' so prepare is not needed
return true;
});
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
/**
@ -266,51 +220,17 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
* CordovaError instance.
*/
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
if (!plugin || plugin.constructor.name !== 'PluginInfo')
return Q.reject(new CordovaError('The parameter is incorrect. The first parameter to addPlugin should be a PluginInfo instance'));
var self = this;
var actions = new ActionStack();
var project = AndroidProject.getProjectFile(this.root);
return PluginManager.get(this.platform, this.locations, project)
.removePlugin(plugin, uninstallOptions)
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
// queue up plugin files
plugin.getFilesAndFrameworks(this.platform)
.concat(plugin.getAssets(this.platform))
.concat(plugin.getJsModules(this.platform))
.forEach(function(item) {
actions.push(actions.createAction(
pluginHandlers.getUninstaller(item.itemType), [item, plugin, project, uninstallOptions],
pluginHandlers.getInstaller(item.itemType), [item, plugin, project, uninstallOptions]));
});
// run through the action stack
return actions.process(this.platform)
.then(function() {
if (project) {
project.write();
}
self._munger
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
.remove_plugin_changes(plugin, /*is_top_level=*/true)
.save_all();
if (plugin.getFrameworks(self.platform).length > 0) {
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
this.events.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
}
var targetDirs = [self.locations.www];
// CB-11022 if usePlatformWww is specified we need to mirror modules metadata to both directories
if (uninstallOptions.usePlatformWww) targetDirs.push(self.locations.platformWww);
self._removeModulesInfo(plugin, targetDirs);
// CB-11022 Indicate to caller that we have copied js-files and assets
// to both 'www' and 'platform_www' so prepare is not needed
return true;
});
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
/**
@ -424,105 +344,3 @@ Api.prototype.requirements = function() {
};
module.exports = Api;
/**
* Removes the specified modules from list of installed modules and updates
* platform_json and cordova_plugins.js on disk.
*
* @param {PluginInfo} plugin PluginInfo instance for plugin, which modules
* needs to be added.
* @param {String[]} targetDirs The directories, where updated cordova_plugins.js
* should be written to.
*/
Api.prototype._addModulesInfo = function(plugin, targetDirs) {
var installedModules = this._platformJson.root.modules || [];
var installedPaths = installedModules.map(function (installedModule) {
return installedModule.file;
});
var modulesToInstall = plugin.getJsModules(this.platform)
.filter(function (moduleToInstall) {
return installedPaths.indexOf(moduleToInstall.file) === -1;
}).map(function (moduleToInstall) {
var moduleName = plugin.id + '.' + ( moduleToInstall.name || moduleToInstall.src.match(/([^\/]+)\.js/)[1] );
var obj = {
file: ['plugins', plugin.id, moduleToInstall.src].join('/'),
id: moduleName
};
if (moduleToInstall.clobbers.length > 0) {
obj.clobbers = moduleToInstall.clobbers.map(function(o) { return o.target; });
}
if (moduleToInstall.merges.length > 0) {
obj.merges = moduleToInstall.merges.map(function(o) { return o.target; });
}
if (moduleToInstall.runs) {
obj.runs = true;
}
return obj;
});
this._platformJson.root.modules = installedModules.concat(modulesToInstall);
if (!this._platformJson.root.plugin_metadata) {
this._platformJson.root.plugin_metadata = {};
}
this._platformJson.root.plugin_metadata[plugin.id] = plugin.version;
this._writePluginModules(targetDirs);
this._platformJson.save();
};
/**
* Removes the specified modules from list of installed modules and updates
* platform_json and cordova_plugins.js on disk.
*
* @param {PluginInfo} plugin PluginInfo instance for plugin, which modules
* needs to be removed.
* @param {String[]} targetDirs The directories, where updated cordova_plugins.js
* should be written to.
*/
Api.prototype._removeModulesInfo = function(plugin, targetDirs) {
var installedModules = this._platformJson.root.modules || [];
var modulesToRemove = plugin.getJsModules(this.platform)
.map(function (jsModule) {
return ['plugins', plugin.id, jsModule.src].join('/');
});
var updatedModules = installedModules
.filter(function (installedModule) {
return (modulesToRemove.indexOf(installedModule.file) === -1);
});
this._platformJson.root.modules = updatedModules;
if (this._platformJson.root.plugin_metadata) {
delete this._platformJson.root.plugin_metadata[plugin.id];
}
this._writePluginModules(targetDirs);
this._platformJson.save();
};
/**
* Fetches all installed modules, generates cordova_plugins contents and writes
* it to cordova_plugins.js file at specified directories.
*
* @param {String[]} targetDirs Directories, where write cordova_plugins.js to.
* Ususally it is either <platform>/www or <platform>/platform_www or both.
*/
Api.prototype._writePluginModules = function (targetDirs) {
// Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js
var final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n';
final_contents += 'module.exports = ' + JSON.stringify(this._platformJson.root.modules, null, ' ') + ';\n';
final_contents += 'module.exports.metadata = \n';
final_contents += '// TOP OF METADATA\n';
final_contents += JSON.stringify(this._platformJson.root.plugin_metadata, null, 4) + ';\n';
final_contents += '// BOTTOM OF METADATA\n';
final_contents += '});'; // Close cordova.define.
targetDirs.forEach(function (targetDir) {
shell.mkdir('-p', targetDir);
fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8');
});
};

View File

@ -21,6 +21,7 @@ var fs = require('fs');
var path = require('path');
var properties_parser = require('properties-parser');
var AndroidManifest = require('./AndroidManifest');
var pluginHandlers = require('./pluginHandlers');
var projectFileCache = {};
@ -180,5 +181,13 @@ AndroidProject.prototype._getPropertiesFile = function (filename) {
return this._propertiesEditors[filename];
};
AndroidProject.prototype.getInstaller = function (type) {
return pluginHandlers.getInstaller(type);
};
AndroidProject.prototype.getUninstaller = function (type) {
return pluginHandlers.getUninstaller(type);
};
module.exports = AndroidProject;

View File

@ -26,13 +26,18 @@ var AndroidManifest = require('./AndroidManifest');
var xmlHelpers = require('cordova-common').xmlHelpers;
var CordovaError = require('cordova-common').CordovaError;
var ConfigParser = require('cordova-common').ConfigParser;
var PlatformJson = require('cordova-common').PlatformJson;
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
module.exports.prepare = function (cordovaProject) {
var self = this;
this._config = updateConfigFilesFrom(cordovaProject.projectConfig,
this._munger, this.locations);
var platformJson = PlatformJson.load(this.locations.root, this.platform);
var munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
// Update own www dir with project's www assets and plugins' assets and js-files
return Q.when(updateWwwFrom(cordovaProject, this.locations))

View File

@ -36,20 +36,16 @@ describe('addPlugin method', function () {
var api, fail, gradleBuilder;
beforeEach(function() {
var ActionStack = jasmine.createSpyObj('ActionStack', ['createAction', 'push', 'process']);
ActionStack.process.andReturn(Q());
spyOn(common, 'ActionStack').andReturn(ActionStack);
var pluginManager = jasmine.createSpyObj('pluginManager', ['addPlugin']);
pluginManager.addPlugin.andReturn(Q());
spyOn(common.PluginManager, 'get').andReturn(pluginManager);
spyOn(AndroidProject, 'getProjectFile')
.andReturn(jasmine.createSpyObj('AndroidProject', ['getPackageName', 'write']));
var projectSpy = jasmine.createSpyObj('AndroidProject', ['getPackageName', 'write']);
spyOn(AndroidProject, 'getProjectFile').andReturn(projectSpy);
var Api = require('../../bin/templates/cordova/Api');
api = new Api('android', FAKE_PROJECT_DIR);
spyOn(api, '_addModulesInfo');
spyOn(api._munger, 'add_plugin_changes')
.andReturn(jasmine.createSpyObj('munger', ['save_all']));
fail = jasmine.createSpy('fail');
gradleBuilder = jasmine.createSpyObj('gradleBuilder', ['prepBuildFiles']);
spyOn(builders, 'getBuilder').andReturn(gradleBuilder);