mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-23 00:00:09 +08:00
updated node_modules
This commit is contained in:
+325
-325
@@ -1,325 +1,325 @@
|
||||
/*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module deals with shared configuration / dependency "stuff". That is:
|
||||
* - XML configuration files such as config.xml, AndroidManifest.xml or WMAppManifest.xml.
|
||||
* - plist files in iOS
|
||||
* Essentially, any type of shared resources that we need to handle with awareness
|
||||
* of how potentially multiple plugins depend on a single shared resource, should be
|
||||
* handled in this module.
|
||||
*
|
||||
* The implementation uses an object as a hash table, with "leaves" of the table tracking
|
||||
* reference counts.
|
||||
*/
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
et = require('elementtree'),
|
||||
semver = require('semver'),
|
||||
events = require('../events'),
|
||||
ConfigKeeper = require('./ConfigKeeper');
|
||||
|
||||
var mungeutil = require('./munge-util');
|
||||
|
||||
exports.PlatformMunger = PlatformMunger;
|
||||
|
||||
exports.process = function(plugins_dir, project_dir, platform, platformJson, pluginInfoProvider) {
|
||||
var munger = new PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider);
|
||||
munger.process(plugins_dir);
|
||||
munger.save_all();
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* PlatformMunger class
|
||||
*
|
||||
* Can deal with config file of a single project.
|
||||
* Parsed config files are cached in a ConfigKeeper object.
|
||||
******************************************************************************/
|
||||
function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider) {
|
||||
this.platform = platform;
|
||||
this.project_dir = project_dir;
|
||||
this.config_keeper = new ConfigKeeper(project_dir);
|
||||
this.platformJson = platformJson;
|
||||
this.pluginInfoProvider = pluginInfoProvider;
|
||||
}
|
||||
|
||||
// Write out all unsaved files.
|
||||
PlatformMunger.prototype.save_all = PlatformMunger_save_all;
|
||||
function PlatformMunger_save_all() {
|
||||
this.config_keeper.save_all();
|
||||
this.platformJson.save();
|
||||
}
|
||||
|
||||
// Apply a munge object to a single config file.
|
||||
// The remove parameter tells whether to add the change or remove it.
|
||||
PlatformMunger.prototype.apply_file_munge = PlatformMunger_apply_file_munge;
|
||||
function PlatformMunger_apply_file_munge(file, munge, remove) {
|
||||
var self = this;
|
||||
|
||||
for (var selector in munge.parents) {
|
||||
for (var xml_child in munge.parents[selector]) {
|
||||
// this xml child is new, graft it (only if config file exists)
|
||||
var config_file = self.config_keeper.get(self.project_dir, self.platform, file);
|
||||
if (config_file.exists) {
|
||||
if (remove) config_file.prune_child(selector, munge.parents[selector][xml_child]);
|
||||
else config_file.graft_child(selector, munge.parents[selector][xml_child]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes;
|
||||
function remove_plugin_changes(pluginInfo, is_top_level) {
|
||||
var self = this;
|
||||
var platform_config = self.platformJson.root;
|
||||
var plugin_vars = is_top_level ?
|
||||
platform_config.installed_plugins[pluginInfo.id] :
|
||||
platform_config.dependent_plugins[pluginInfo.id];
|
||||
|
||||
// get config munge, aka how did this plugin change various config files
|
||||
var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars);
|
||||
// global munge looks at all plugins' changes to config files
|
||||
var global_munge = platform_config.config_munge;
|
||||
var munge = mungeutil.decrement_munge(global_munge, config_munge);
|
||||
|
||||
for (var file in munge.files) {
|
||||
// CB-6976 Windows Universal Apps. Compatibility fix for existing plugins.
|
||||
if (self.platform == 'windows' && file == 'package.appxmanifest' &&
|
||||
!fs.existsSync(path.join(self.project_dir, 'package.appxmanifest'))) {
|
||||
// New windows template separate manifest files for Windows8, Windows8.1 and WP8.1
|
||||
var substs = ['package.phone.appxmanifest', 'package.windows.appxmanifest', 'package.windows80.appxmanifest', 'package.windows10.appxmanifest'];
|
||||
/* jshint loopfunc:true */
|
||||
substs.forEach(function(subst) {
|
||||
events.emit('verbose', 'Applying munge to ' + subst);
|
||||
self.apply_file_munge(subst, munge.files[file], true);
|
||||
});
|
||||
/* jshint loopfunc:false */
|
||||
}
|
||||
self.apply_file_munge(file, munge.files[file], /* remove = */ true);
|
||||
}
|
||||
|
||||
// Remove from installed_plugins
|
||||
self.platformJson.removePlugin(pluginInfo.id, is_top_level);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
PlatformMunger.prototype.add_plugin_changes = add_plugin_changes;
|
||||
function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increment) {
|
||||
var self = this;
|
||||
var platform_config = self.platformJson.root;
|
||||
|
||||
// get config munge, aka how should this plugin change various config files
|
||||
var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars);
|
||||
// global munge looks at all plugins' changes to config files
|
||||
|
||||
// TODO: The should_increment param is only used by cordova-cli and is going away soon.
|
||||
// If should_increment is set to false, avoid modifying the global_munge (use clone)
|
||||
// and apply the entire config_munge because it's already a proper subset of the global_munge.
|
||||
var munge, global_munge;
|
||||
if (should_increment) {
|
||||
global_munge = platform_config.config_munge;
|
||||
munge = mungeutil.increment_munge(global_munge, config_munge);
|
||||
} else {
|
||||
global_munge = mungeutil.clone_munge(platform_config.config_munge);
|
||||
munge = config_munge;
|
||||
}
|
||||
|
||||
for (var file in munge.files) {
|
||||
// CB-6976 Windows Universal Apps. Compatibility fix for existing plugins.
|
||||
if (self.platform == 'windows' && file == 'package.appxmanifest' &&
|
||||
!fs.existsSync(path.join(self.project_dir, 'package.appxmanifest'))) {
|
||||
var substs = ['package.phone.appxmanifest', 'package.windows.appxmanifest', 'package.windows80.appxmanifest', 'package.windows10.appxmanifest'];
|
||||
/* jshint loopfunc:true */
|
||||
substs.forEach(function(subst) {
|
||||
events.emit('verbose', 'Applying munge to ' + subst);
|
||||
self.apply_file_munge(subst, munge.files[file]);
|
||||
});
|
||||
/* jshint loopfunc:false */
|
||||
}
|
||||
self.apply_file_munge(file, munge.files[file]);
|
||||
}
|
||||
|
||||
// Move to installed/dependent_plugins
|
||||
self.platformJson.addPlugin(pluginInfo.id, plugin_vars || {}, is_top_level);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
// Load the global munge from platform json and apply all of it.
|
||||
// Used by cordova prepare to re-generate some config file from platform
|
||||
// defaults and the global munge.
|
||||
PlatformMunger.prototype.reapply_global_munge = reapply_global_munge ;
|
||||
function reapply_global_munge () {
|
||||
var self = this;
|
||||
|
||||
var platform_config = self.platformJson.root;
|
||||
var global_munge = platform_config.config_munge;
|
||||
for (var file in global_munge.files) {
|
||||
self.apply_file_munge(file, global_munge.files[file]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
// generate_plugin_config_munge
|
||||
// Generate the munge object from plugin.xml + vars
|
||||
PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge;
|
||||
function generate_plugin_config_munge(pluginInfo, vars) {
|
||||
var self = this;
|
||||
|
||||
vars = vars || {};
|
||||
var munge = { files: {} };
|
||||
var changes = pluginInfo.getConfigFiles(self.platform);
|
||||
|
||||
// Demux 'package.appxmanifest' into relevant platform-specific appx manifests.
|
||||
// Only spend the cycles if there are version-specific plugin settings
|
||||
if (self.platform === 'windows' &&
|
||||
changes.some(function(change) {
|
||||
return ((typeof change.versions !== 'undefined') ||
|
||||
(typeof change.deviceTarget !== 'undefined'));
|
||||
}))
|
||||
{
|
||||
var manifests = {
|
||||
'windows': {
|
||||
'8.0.0': 'package.windows80.appxmanifest',
|
||||
'8.1.0': 'package.windows.appxmanifest',
|
||||
'10.0.0': 'package.windows10.appxmanifest'
|
||||
},
|
||||
'phone': {
|
||||
'8.1.0': 'package.phone.appxmanifest',
|
||||
'10.0.0': 'package.windows10.appxmanifest'
|
||||
},
|
||||
'all': {
|
||||
'8.0.0': 'package.windows80.appxmanifest',
|
||||
'8.1.0': ['package.windows.appxmanifest', 'package.phone.appxmanifest'],
|
||||
'10.0.0': 'package.windows10.appxmanifest'
|
||||
}
|
||||
};
|
||||
|
||||
var oldChanges = changes;
|
||||
changes = [];
|
||||
|
||||
oldChanges.forEach(function(change, changeIndex) {
|
||||
// Only support semver/device-target demux for package.appxmanifest
|
||||
// Pass through in case something downstream wants to use it
|
||||
if (change.target !== 'package.appxmanifest') {
|
||||
changes.push(change);
|
||||
return;
|
||||
}
|
||||
|
||||
var hasVersion = (typeof change.versions !== 'undefined');
|
||||
var hasTargets = (typeof change.deviceTarget !== 'undefined');
|
||||
|
||||
// No semver/device-target for this config-file, pass it through
|
||||
if (!(hasVersion || hasTargets)) {
|
||||
changes.push(change);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetDeviceSet = hasTargets ? change.deviceTarget : 'all';
|
||||
if (['windows', 'phone', 'all'].indexOf(targetDeviceSet) === -1) {
|
||||
// target-device couldn't be resolved, fix it up here to a valid value
|
||||
targetDeviceSet = 'all';
|
||||
}
|
||||
var knownWindowsVersionsForTargetDeviceSet = Object.keys(manifests[targetDeviceSet]);
|
||||
|
||||
// at this point, 'change' targets package.appxmanifest and has a version attribute
|
||||
knownWindowsVersionsForTargetDeviceSet.forEach(function(winver) {
|
||||
// This is a local function that creates the new replacement representing the
|
||||
// mutation. Used to save code further down.
|
||||
var createReplacement = function(manifestFile, originalChange) {
|
||||
var replacement = {
|
||||
target: manifestFile,
|
||||
parent: originalChange.parent,
|
||||
after: originalChange.after,
|
||||
xmls: originalChange.xmls,
|
||||
versions: originalChange.versions,
|
||||
deviceTarget: originalChange.deviceTarget
|
||||
};
|
||||
return replacement;
|
||||
};
|
||||
|
||||
// version doesn't satisfy, so skip
|
||||
if (hasVersion && !semver.satisfies(winver, change.versions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var versionSpecificManifests = manifests[targetDeviceSet][winver];
|
||||
if (versionSpecificManifests.constructor === Array) {
|
||||
// e.g. all['8.1.0'] === ['pkg.windows.appxmanifest', 'pkg.phone.appxmanifest']
|
||||
versionSpecificManifests.forEach(function(manifestFile) {
|
||||
changes.push(createReplacement(manifestFile, change));
|
||||
});
|
||||
}
|
||||
else {
|
||||
// versionSpecificManifests is actually a single string
|
||||
changes.push(createReplacement(versionSpecificManifests, change));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
changes.forEach(function(change) {
|
||||
change.xmls.forEach(function(xml) {
|
||||
// 1. stringify each xml
|
||||
var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
|
||||
// interp vars
|
||||
if (vars) {
|
||||
Object.keys(vars).forEach(function(key) {
|
||||
var regExp = new RegExp('\\$' + key, 'g');
|
||||
stringified = stringified.replace(regExp, vars[key]);
|
||||
});
|
||||
}
|
||||
// 2. add into munge
|
||||
mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after });
|
||||
});
|
||||
});
|
||||
return munge;
|
||||
}
|
||||
|
||||
// Go over the prepare queue and apply the config munges for each plugin
|
||||
// that has been (un)installed.
|
||||
PlatformMunger.prototype.process = PlatformMunger_process;
|
||||
function PlatformMunger_process(plugins_dir) {
|
||||
var self = this;
|
||||
var platform_config = self.platformJson.root;
|
||||
|
||||
// Uninstallation first
|
||||
platform_config.prepare_queue.uninstalled.forEach(function(u) {
|
||||
var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
|
||||
self.remove_plugin_changes(pluginInfo, u.topLevel);
|
||||
});
|
||||
|
||||
// Now handle installation
|
||||
platform_config.prepare_queue.installed.forEach(function(u) {
|
||||
var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
|
||||
self.add_plugin_changes(pluginInfo, u.vars, u.topLevel, true);
|
||||
});
|
||||
|
||||
// Empty out installed/ uninstalled queues.
|
||||
platform_config.prepare_queue.uninstalled = [];
|
||||
platform_config.prepare_queue.installed = [];
|
||||
}
|
||||
/**** END of PlatformMunger ****/
|
||||
/*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module deals with shared configuration / dependency "stuff". That is:
|
||||
* - XML configuration files such as config.xml, AndroidManifest.xml or WMAppManifest.xml.
|
||||
* - plist files in iOS
|
||||
* Essentially, any type of shared resources that we need to handle with awareness
|
||||
* of how potentially multiple plugins depend on a single shared resource, should be
|
||||
* handled in this module.
|
||||
*
|
||||
* The implementation uses an object as a hash table, with "leaves" of the table tracking
|
||||
* reference counts.
|
||||
*/
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
et = require('elementtree'),
|
||||
semver = require('semver'),
|
||||
events = require('../events'),
|
||||
ConfigKeeper = require('./ConfigKeeper');
|
||||
|
||||
var mungeutil = require('./munge-util');
|
||||
|
||||
exports.PlatformMunger = PlatformMunger;
|
||||
|
||||
exports.process = function(plugins_dir, project_dir, platform, platformJson, pluginInfoProvider) {
|
||||
var munger = new PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider);
|
||||
munger.process(plugins_dir);
|
||||
munger.save_all();
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
* PlatformMunger class
|
||||
*
|
||||
* Can deal with config file of a single project.
|
||||
* Parsed config files are cached in a ConfigKeeper object.
|
||||
******************************************************************************/
|
||||
function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider) {
|
||||
this.platform = platform;
|
||||
this.project_dir = project_dir;
|
||||
this.config_keeper = new ConfigKeeper(project_dir);
|
||||
this.platformJson = platformJson;
|
||||
this.pluginInfoProvider = pluginInfoProvider;
|
||||
}
|
||||
|
||||
// Write out all unsaved files.
|
||||
PlatformMunger.prototype.save_all = PlatformMunger_save_all;
|
||||
function PlatformMunger_save_all() {
|
||||
this.config_keeper.save_all();
|
||||
this.platformJson.save();
|
||||
}
|
||||
|
||||
// Apply a munge object to a single config file.
|
||||
// The remove parameter tells whether to add the change or remove it.
|
||||
PlatformMunger.prototype.apply_file_munge = PlatformMunger_apply_file_munge;
|
||||
function PlatformMunger_apply_file_munge(file, munge, remove) {
|
||||
var self = this;
|
||||
|
||||
for (var selector in munge.parents) {
|
||||
for (var xml_child in munge.parents[selector]) {
|
||||
// this xml child is new, graft it (only if config file exists)
|
||||
var config_file = self.config_keeper.get(self.project_dir, self.platform, file);
|
||||
if (config_file.exists) {
|
||||
if (remove) config_file.prune_child(selector, munge.parents[selector][xml_child]);
|
||||
else config_file.graft_child(selector, munge.parents[selector][xml_child]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlatformMunger.prototype.remove_plugin_changes = remove_plugin_changes;
|
||||
function remove_plugin_changes(pluginInfo, is_top_level) {
|
||||
var self = this;
|
||||
var platform_config = self.platformJson.root;
|
||||
var plugin_vars = is_top_level ?
|
||||
platform_config.installed_plugins[pluginInfo.id] :
|
||||
platform_config.dependent_plugins[pluginInfo.id];
|
||||
|
||||
// get config munge, aka how did this plugin change various config files
|
||||
var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars);
|
||||
// global munge looks at all plugins' changes to config files
|
||||
var global_munge = platform_config.config_munge;
|
||||
var munge = mungeutil.decrement_munge(global_munge, config_munge);
|
||||
|
||||
for (var file in munge.files) {
|
||||
// CB-6976 Windows Universal Apps. Compatibility fix for existing plugins.
|
||||
if (self.platform == 'windows' && file == 'package.appxmanifest' &&
|
||||
!fs.existsSync(path.join(self.project_dir, 'package.appxmanifest'))) {
|
||||
// New windows template separate manifest files for Windows8, Windows8.1 and WP8.1
|
||||
var substs = ['package.phone.appxmanifest', 'package.windows.appxmanifest', 'package.windows80.appxmanifest', 'package.windows10.appxmanifest'];
|
||||
/* jshint loopfunc:true */
|
||||
substs.forEach(function(subst) {
|
||||
events.emit('verbose', 'Applying munge to ' + subst);
|
||||
self.apply_file_munge(subst, munge.files[file], true);
|
||||
});
|
||||
/* jshint loopfunc:false */
|
||||
}
|
||||
self.apply_file_munge(file, munge.files[file], /* remove = */ true);
|
||||
}
|
||||
|
||||
// Remove from installed_plugins
|
||||
self.platformJson.removePlugin(pluginInfo.id, is_top_level);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
PlatformMunger.prototype.add_plugin_changes = add_plugin_changes;
|
||||
function add_plugin_changes(pluginInfo, plugin_vars, is_top_level, should_increment) {
|
||||
var self = this;
|
||||
var platform_config = self.platformJson.root;
|
||||
|
||||
// get config munge, aka how should this plugin change various config files
|
||||
var config_munge = self.generate_plugin_config_munge(pluginInfo, plugin_vars);
|
||||
// global munge looks at all plugins' changes to config files
|
||||
|
||||
// TODO: The should_increment param is only used by cordova-cli and is going away soon.
|
||||
// If should_increment is set to false, avoid modifying the global_munge (use clone)
|
||||
// and apply the entire config_munge because it's already a proper subset of the global_munge.
|
||||
var munge, global_munge;
|
||||
if (should_increment) {
|
||||
global_munge = platform_config.config_munge;
|
||||
munge = mungeutil.increment_munge(global_munge, config_munge);
|
||||
} else {
|
||||
global_munge = mungeutil.clone_munge(platform_config.config_munge);
|
||||
munge = config_munge;
|
||||
}
|
||||
|
||||
for (var file in munge.files) {
|
||||
// CB-6976 Windows Universal Apps. Compatibility fix for existing plugins.
|
||||
if (self.platform == 'windows' && file == 'package.appxmanifest' &&
|
||||
!fs.existsSync(path.join(self.project_dir, 'package.appxmanifest'))) {
|
||||
var substs = ['package.phone.appxmanifest', 'package.windows.appxmanifest', 'package.windows80.appxmanifest', 'package.windows10.appxmanifest'];
|
||||
/* jshint loopfunc:true */
|
||||
substs.forEach(function(subst) {
|
||||
events.emit('verbose', 'Applying munge to ' + subst);
|
||||
self.apply_file_munge(subst, munge.files[file]);
|
||||
});
|
||||
/* jshint loopfunc:false */
|
||||
}
|
||||
self.apply_file_munge(file, munge.files[file]);
|
||||
}
|
||||
|
||||
// Move to installed/dependent_plugins
|
||||
self.platformJson.addPlugin(pluginInfo.id, plugin_vars || {}, is_top_level);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
// Load the global munge from platform json and apply all of it.
|
||||
// Used by cordova prepare to re-generate some config file from platform
|
||||
// defaults and the global munge.
|
||||
PlatformMunger.prototype.reapply_global_munge = reapply_global_munge ;
|
||||
function reapply_global_munge () {
|
||||
var self = this;
|
||||
|
||||
var platform_config = self.platformJson.root;
|
||||
var global_munge = platform_config.config_munge;
|
||||
for (var file in global_munge.files) {
|
||||
self.apply_file_munge(file, global_munge.files[file]);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
// generate_plugin_config_munge
|
||||
// Generate the munge object from plugin.xml + vars
|
||||
PlatformMunger.prototype.generate_plugin_config_munge = generate_plugin_config_munge;
|
||||
function generate_plugin_config_munge(pluginInfo, vars) {
|
||||
var self = this;
|
||||
|
||||
vars = vars || {};
|
||||
var munge = { files: {} };
|
||||
var changes = pluginInfo.getConfigFiles(self.platform);
|
||||
|
||||
// Demux 'package.appxmanifest' into relevant platform-specific appx manifests.
|
||||
// Only spend the cycles if there are version-specific plugin settings
|
||||
if (self.platform === 'windows' &&
|
||||
changes.some(function(change) {
|
||||
return ((typeof change.versions !== 'undefined') ||
|
||||
(typeof change.deviceTarget !== 'undefined'));
|
||||
}))
|
||||
{
|
||||
var manifests = {
|
||||
'windows': {
|
||||
'8.0.0': 'package.windows80.appxmanifest',
|
||||
'8.1.0': 'package.windows.appxmanifest',
|
||||
'10.0.0': 'package.windows10.appxmanifest'
|
||||
},
|
||||
'phone': {
|
||||
'8.1.0': 'package.phone.appxmanifest',
|
||||
'10.0.0': 'package.windows10.appxmanifest'
|
||||
},
|
||||
'all': {
|
||||
'8.0.0': 'package.windows80.appxmanifest',
|
||||
'8.1.0': ['package.windows.appxmanifest', 'package.phone.appxmanifest'],
|
||||
'10.0.0': 'package.windows10.appxmanifest'
|
||||
}
|
||||
};
|
||||
|
||||
var oldChanges = changes;
|
||||
changes = [];
|
||||
|
||||
oldChanges.forEach(function(change, changeIndex) {
|
||||
// Only support semver/device-target demux for package.appxmanifest
|
||||
// Pass through in case something downstream wants to use it
|
||||
if (change.target !== 'package.appxmanifest') {
|
||||
changes.push(change);
|
||||
return;
|
||||
}
|
||||
|
||||
var hasVersion = (typeof change.versions !== 'undefined');
|
||||
var hasTargets = (typeof change.deviceTarget !== 'undefined');
|
||||
|
||||
// No semver/device-target for this config-file, pass it through
|
||||
if (!(hasVersion || hasTargets)) {
|
||||
changes.push(change);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetDeviceSet = hasTargets ? change.deviceTarget : 'all';
|
||||
if (['windows', 'phone', 'all'].indexOf(targetDeviceSet) === -1) {
|
||||
// target-device couldn't be resolved, fix it up here to a valid value
|
||||
targetDeviceSet = 'all';
|
||||
}
|
||||
var knownWindowsVersionsForTargetDeviceSet = Object.keys(manifests[targetDeviceSet]);
|
||||
|
||||
// at this point, 'change' targets package.appxmanifest and has a version attribute
|
||||
knownWindowsVersionsForTargetDeviceSet.forEach(function(winver) {
|
||||
// This is a local function that creates the new replacement representing the
|
||||
// mutation. Used to save code further down.
|
||||
var createReplacement = function(manifestFile, originalChange) {
|
||||
var replacement = {
|
||||
target: manifestFile,
|
||||
parent: originalChange.parent,
|
||||
after: originalChange.after,
|
||||
xmls: originalChange.xmls,
|
||||
versions: originalChange.versions,
|
||||
deviceTarget: originalChange.deviceTarget
|
||||
};
|
||||
return replacement;
|
||||
};
|
||||
|
||||
// version doesn't satisfy, so skip
|
||||
if (hasVersion && !semver.satisfies(winver, change.versions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var versionSpecificManifests = manifests[targetDeviceSet][winver];
|
||||
if (versionSpecificManifests.constructor === Array) {
|
||||
// e.g. all['8.1.0'] === ['pkg.windows.appxmanifest', 'pkg.phone.appxmanifest']
|
||||
versionSpecificManifests.forEach(function(manifestFile) {
|
||||
changes.push(createReplacement(manifestFile, change));
|
||||
});
|
||||
}
|
||||
else {
|
||||
// versionSpecificManifests is actually a single string
|
||||
changes.push(createReplacement(versionSpecificManifests, change));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
changes.forEach(function(change) {
|
||||
change.xmls.forEach(function(xml) {
|
||||
// 1. stringify each xml
|
||||
var stringified = (new et.ElementTree(xml)).write({xml_declaration:false});
|
||||
// interp vars
|
||||
if (vars) {
|
||||
Object.keys(vars).forEach(function(key) {
|
||||
var regExp = new RegExp('\\$' + key, 'g');
|
||||
stringified = stringified.replace(regExp, vars[key]);
|
||||
});
|
||||
}
|
||||
// 2. add into munge
|
||||
mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after });
|
||||
});
|
||||
});
|
||||
return munge;
|
||||
}
|
||||
|
||||
// Go over the prepare queue and apply the config munges for each plugin
|
||||
// that has been (un)installed.
|
||||
PlatformMunger.prototype.process = PlatformMunger_process;
|
||||
function PlatformMunger_process(plugins_dir) {
|
||||
var self = this;
|
||||
var platform_config = self.platformJson.root;
|
||||
|
||||
// Uninstallation first
|
||||
platform_config.prepare_queue.uninstalled.forEach(function(u) {
|
||||
var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
|
||||
self.remove_plugin_changes(pluginInfo, u.topLevel);
|
||||
});
|
||||
|
||||
// Now handle installation
|
||||
platform_config.prepare_queue.installed.forEach(function(u) {
|
||||
var pluginInfo = self.pluginInfoProvider.get(path.join(plugins_dir, u.plugin));
|
||||
self.add_plugin_changes(pluginInfo, u.vars, u.topLevel, true);
|
||||
});
|
||||
|
||||
// Empty out installed/ uninstalled queues.
|
||||
platform_config.prepare_queue.uninstalled = [];
|
||||
platform_config.prepare_queue.installed = [];
|
||||
}
|
||||
/**** END of PlatformMunger ****/
|
||||
|
||||
+208
-208
@@ -1,208 +1,208 @@
|
||||
/*
|
||||
* 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');
|
||||
var path = require('path');
|
||||
|
||||
var bplist = require('bplist-parser');
|
||||
var et = require('elementtree');
|
||||
var glob = require('glob');
|
||||
var plist = require('plist');
|
||||
|
||||
var plist_helpers = require('../util/plist-helpers');
|
||||
var xml_helpers = require('../util/xml-helpers');
|
||||
|
||||
/******************************************************************************
|
||||
* ConfigFile class
|
||||
*
|
||||
* Can load and keep various types of config files. Provides some functionality
|
||||
* specific to some file types such as grafting XML children. In most cases it
|
||||
* should be instantiated by ConfigKeeper.
|
||||
*
|
||||
* For plugin.xml files use as:
|
||||
* plugin_config = self.config_keeper.get(plugin_dir, '', 'plugin.xml');
|
||||
*
|
||||
* TODO: Consider moving it out to a separate file and maybe partially with
|
||||
* overrides in platform handlers.
|
||||
******************************************************************************/
|
||||
function ConfigFile(project_dir, platform, file_tag) {
|
||||
this.project_dir = project_dir;
|
||||
this.platform = platform;
|
||||
this.file_tag = file_tag;
|
||||
this.is_changed = false;
|
||||
|
||||
this.load();
|
||||
}
|
||||
|
||||
// ConfigFile.load()
|
||||
ConfigFile.prototype.load = ConfigFile_load;
|
||||
function ConfigFile_load() {
|
||||
var self = this;
|
||||
|
||||
// config file may be in a place not exactly specified in the target
|
||||
var filepath = self.filepath = resolveConfigFilePath(self.project_dir, self.platform, self.file_tag);
|
||||
|
||||
if ( !filepath || !fs.existsSync(filepath) ) {
|
||||
self.exists = false;
|
||||
return;
|
||||
}
|
||||
self.exists = true;
|
||||
self.mtime = fs.statSync(self.filepath).mtime;
|
||||
|
||||
var ext = path.extname(filepath);
|
||||
// Windows8 uses an appxmanifest, and wp8 will likely use
|
||||
// the same in a future release
|
||||
if (ext == '.xml' || ext == '.appxmanifest') {
|
||||
self.type = 'xml';
|
||||
self.data = xml_helpers.parseElementtreeSync(filepath);
|
||||
} else {
|
||||
// plist file
|
||||
self.type = 'plist';
|
||||
// TODO: isBinaryPlist() reads the file and then parse re-reads it again.
|
||||
// We always write out text plist, not binary.
|
||||
// Do we still need to support binary plist?
|
||||
// If yes, use plist.parseStringSync() and read the file once.
|
||||
self.data = isBinaryPlist(filepath) ?
|
||||
bplist.parseBuffer(fs.readFileSync(filepath)) :
|
||||
plist.parse(fs.readFileSync(filepath, 'utf8'));
|
||||
}
|
||||
}
|
||||
|
||||
ConfigFile.prototype.save = function ConfigFile_save() {
|
||||
var self = this;
|
||||
if (self.type === 'xml') {
|
||||
fs.writeFileSync(self.filepath, self.data.write({indent: 4}), 'utf-8');
|
||||
} else {
|
||||
// plist
|
||||
var regExp = new RegExp('<string>[ \t\r\n]+?</string>', 'g');
|
||||
fs.writeFileSync(self.filepath, plist.build(self.data).replace(regExp, '<string></string>'));
|
||||
}
|
||||
self.is_changed = false;
|
||||
};
|
||||
|
||||
ConfigFile.prototype.graft_child = function ConfigFile_graft_child(selector, xml_child) {
|
||||
var self = this;
|
||||
var filepath = self.filepath;
|
||||
var result;
|
||||
if (self.type === 'xml') {
|
||||
var xml_to_graft = [et.XML(xml_child.xml)];
|
||||
result = xml_helpers.graftXML(self.data, xml_to_graft, selector, xml_child.after);
|
||||
if ( !result) {
|
||||
throw new Error('grafting xml at selector "' + selector + '" from "' + filepath + '" during config install went bad :(');
|
||||
}
|
||||
} else {
|
||||
// plist file
|
||||
result = plist_helpers.graftPLIST(self.data, xml_child.xml, selector);
|
||||
if ( !result ) {
|
||||
throw new Error('grafting to plist "' + filepath + '" during config install went bad :(');
|
||||
}
|
||||
}
|
||||
self.is_changed = true;
|
||||
};
|
||||
|
||||
ConfigFile.prototype.prune_child = function ConfigFile_prune_child(selector, xml_child) {
|
||||
var self = this;
|
||||
var filepath = self.filepath;
|
||||
var result;
|
||||
if (self.type === 'xml') {
|
||||
var xml_to_graft = [et.XML(xml_child.xml)];
|
||||
result = xml_helpers.pruneXML(self.data, xml_to_graft, selector);
|
||||
} else {
|
||||
// plist file
|
||||
result = plist_helpers.prunePLIST(self.data, xml_child.xml, selector);
|
||||
}
|
||||
if (!result) {
|
||||
var err_msg = 'Pruning at selector "' + selector + '" from "' + filepath + '" went bad.';
|
||||
throw new Error(err_msg);
|
||||
}
|
||||
self.is_changed = true;
|
||||
};
|
||||
|
||||
// Some config-file target attributes are not qualified with a full leading directory, or contain wildcards.
|
||||
// Resolve to a real path in this function.
|
||||
// TODO: getIOSProjectname is slow because of glob, try to avoid calling it several times per project.
|
||||
function resolveConfigFilePath(project_dir, platform, file) {
|
||||
var filepath = path.join(project_dir, file);
|
||||
var matches;
|
||||
|
||||
if (file.indexOf('*') > -1) {
|
||||
// handle wildcards in targets using glob.
|
||||
matches = glob.sync(path.join(project_dir, '**', file));
|
||||
if (matches.length) filepath = matches[0];
|
||||
|
||||
// [CB-5989] multiple Info.plist files may exist. default to $PROJECT_NAME-Info.plist
|
||||
if(matches.length > 1 && file.indexOf('-Info.plist')>-1){
|
||||
var plistName = getIOSProjectname(project_dir)+'-Info.plist';
|
||||
for (var i=0; i < matches.length; i++) {
|
||||
if(matches[i].indexOf(plistName) > -1){
|
||||
filepath = matches[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filepath;
|
||||
}
|
||||
|
||||
// special-case config.xml target that is just "config.xml". This should be resolved to the real location of the file.
|
||||
// TODO: move the logic that contains the locations of config.xml from cordova CLI into plugman.
|
||||
if (file == 'config.xml') {
|
||||
if (platform == 'ubuntu') {
|
||||
filepath = path.join(project_dir, 'config.xml');
|
||||
} else if (platform == 'ios') {
|
||||
var iospath = getIOSProjectname(project_dir);
|
||||
filepath = path.join(project_dir,iospath, 'config.xml');
|
||||
} else if (platform == 'android') {
|
||||
filepath = path.join(project_dir, 'res', 'xml', 'config.xml');
|
||||
} else {
|
||||
matches = glob.sync(path.join(project_dir, '**', 'config.xml'));
|
||||
if (matches.length) filepath = matches[0];
|
||||
}
|
||||
return filepath;
|
||||
}
|
||||
|
||||
// None of the special cases matched, returning project_dir/file.
|
||||
return filepath;
|
||||
}
|
||||
|
||||
// Find out the real name of an iOS project
|
||||
// TODO: glob is slow, need a better way or caching, or avoid using more than once.
|
||||
function getIOSProjectname(project_dir) {
|
||||
var matches = glob.sync(path.join(project_dir, '*.xcodeproj'));
|
||||
var iospath;
|
||||
if (matches.length === 1) {
|
||||
iospath = path.basename(matches[0],'.xcodeproj');
|
||||
} else {
|
||||
var msg;
|
||||
if (matches.length === 0) {
|
||||
msg = 'Does not appear to be an xcode project, no xcode project file in ' + project_dir;
|
||||
} else {
|
||||
msg = 'There are multiple *.xcodeproj dirs in ' + project_dir;
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
return iospath;
|
||||
}
|
||||
|
||||
// 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';
|
||||
}
|
||||
|
||||
module.exports = ConfigFile;
|
||||
/*
|
||||
* 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');
|
||||
var path = require('path');
|
||||
|
||||
var bplist = require('bplist-parser');
|
||||
var et = require('elementtree');
|
||||
var glob = require('glob');
|
||||
var plist = require('plist');
|
||||
|
||||
var plist_helpers = require('../util/plist-helpers');
|
||||
var xml_helpers = require('../util/xml-helpers');
|
||||
|
||||
/******************************************************************************
|
||||
* ConfigFile class
|
||||
*
|
||||
* Can load and keep various types of config files. Provides some functionality
|
||||
* specific to some file types such as grafting XML children. In most cases it
|
||||
* should be instantiated by ConfigKeeper.
|
||||
*
|
||||
* For plugin.xml files use as:
|
||||
* plugin_config = self.config_keeper.get(plugin_dir, '', 'plugin.xml');
|
||||
*
|
||||
* TODO: Consider moving it out to a separate file and maybe partially with
|
||||
* overrides in platform handlers.
|
||||
******************************************************************************/
|
||||
function ConfigFile(project_dir, platform, file_tag) {
|
||||
this.project_dir = project_dir;
|
||||
this.platform = platform;
|
||||
this.file_tag = file_tag;
|
||||
this.is_changed = false;
|
||||
|
||||
this.load();
|
||||
}
|
||||
|
||||
// ConfigFile.load()
|
||||
ConfigFile.prototype.load = ConfigFile_load;
|
||||
function ConfigFile_load() {
|
||||
var self = this;
|
||||
|
||||
// config file may be in a place not exactly specified in the target
|
||||
var filepath = self.filepath = resolveConfigFilePath(self.project_dir, self.platform, self.file_tag);
|
||||
|
||||
if ( !filepath || !fs.existsSync(filepath) ) {
|
||||
self.exists = false;
|
||||
return;
|
||||
}
|
||||
self.exists = true;
|
||||
self.mtime = fs.statSync(self.filepath).mtime;
|
||||
|
||||
var ext = path.extname(filepath);
|
||||
// Windows8 uses an appxmanifest, and wp8 will likely use
|
||||
// the same in a future release
|
||||
if (ext == '.xml' || ext == '.appxmanifest') {
|
||||
self.type = 'xml';
|
||||
self.data = xml_helpers.parseElementtreeSync(filepath);
|
||||
} else {
|
||||
// plist file
|
||||
self.type = 'plist';
|
||||
// TODO: isBinaryPlist() reads the file and then parse re-reads it again.
|
||||
// We always write out text plist, not binary.
|
||||
// Do we still need to support binary plist?
|
||||
// If yes, use plist.parseStringSync() and read the file once.
|
||||
self.data = isBinaryPlist(filepath) ?
|
||||
bplist.parseBuffer(fs.readFileSync(filepath)) :
|
||||
plist.parse(fs.readFileSync(filepath, 'utf8'));
|
||||
}
|
||||
}
|
||||
|
||||
ConfigFile.prototype.save = function ConfigFile_save() {
|
||||
var self = this;
|
||||
if (self.type === 'xml') {
|
||||
fs.writeFileSync(self.filepath, self.data.write({indent: 4}), 'utf-8');
|
||||
} else {
|
||||
// plist
|
||||
var regExp = new RegExp('<string>[ \t\r\n]+?</string>', 'g');
|
||||
fs.writeFileSync(self.filepath, plist.build(self.data).replace(regExp, '<string></string>'));
|
||||
}
|
||||
self.is_changed = false;
|
||||
};
|
||||
|
||||
ConfigFile.prototype.graft_child = function ConfigFile_graft_child(selector, xml_child) {
|
||||
var self = this;
|
||||
var filepath = self.filepath;
|
||||
var result;
|
||||
if (self.type === 'xml') {
|
||||
var xml_to_graft = [et.XML(xml_child.xml)];
|
||||
result = xml_helpers.graftXML(self.data, xml_to_graft, selector, xml_child.after);
|
||||
if ( !result) {
|
||||
throw new Error('grafting xml at selector "' + selector + '" from "' + filepath + '" during config install went bad :(');
|
||||
}
|
||||
} else {
|
||||
// plist file
|
||||
result = plist_helpers.graftPLIST(self.data, xml_child.xml, selector);
|
||||
if ( !result ) {
|
||||
throw new Error('grafting to plist "' + filepath + '" during config install went bad :(');
|
||||
}
|
||||
}
|
||||
self.is_changed = true;
|
||||
};
|
||||
|
||||
ConfigFile.prototype.prune_child = function ConfigFile_prune_child(selector, xml_child) {
|
||||
var self = this;
|
||||
var filepath = self.filepath;
|
||||
var result;
|
||||
if (self.type === 'xml') {
|
||||
var xml_to_graft = [et.XML(xml_child.xml)];
|
||||
result = xml_helpers.pruneXML(self.data, xml_to_graft, selector);
|
||||
} else {
|
||||
// plist file
|
||||
result = plist_helpers.prunePLIST(self.data, xml_child.xml, selector);
|
||||
}
|
||||
if (!result) {
|
||||
var err_msg = 'Pruning at selector "' + selector + '" from "' + filepath + '" went bad.';
|
||||
throw new Error(err_msg);
|
||||
}
|
||||
self.is_changed = true;
|
||||
};
|
||||
|
||||
// Some config-file target attributes are not qualified with a full leading directory, or contain wildcards.
|
||||
// Resolve to a real path in this function.
|
||||
// TODO: getIOSProjectname is slow because of glob, try to avoid calling it several times per project.
|
||||
function resolveConfigFilePath(project_dir, platform, file) {
|
||||
var filepath = path.join(project_dir, file);
|
||||
var matches;
|
||||
|
||||
if (file.indexOf('*') > -1) {
|
||||
// handle wildcards in targets using glob.
|
||||
matches = glob.sync(path.join(project_dir, '**', file));
|
||||
if (matches.length) filepath = matches[0];
|
||||
|
||||
// [CB-5989] multiple Info.plist files may exist. default to $PROJECT_NAME-Info.plist
|
||||
if(matches.length > 1 && file.indexOf('-Info.plist')>-1){
|
||||
var plistName = getIOSProjectname(project_dir)+'-Info.plist';
|
||||
for (var i=0; i < matches.length; i++) {
|
||||
if(matches[i].indexOf(plistName) > -1){
|
||||
filepath = matches[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filepath;
|
||||
}
|
||||
|
||||
// special-case config.xml target that is just "config.xml". This should be resolved to the real location of the file.
|
||||
// TODO: move the logic that contains the locations of config.xml from cordova CLI into plugman.
|
||||
if (file == 'config.xml') {
|
||||
if (platform == 'ubuntu') {
|
||||
filepath = path.join(project_dir, 'config.xml');
|
||||
} else if (platform == 'ios') {
|
||||
var iospath = getIOSProjectname(project_dir);
|
||||
filepath = path.join(project_dir,iospath, 'config.xml');
|
||||
} else if (platform == 'android') {
|
||||
filepath = path.join(project_dir, 'res', 'xml', 'config.xml');
|
||||
} else {
|
||||
matches = glob.sync(path.join(project_dir, '**', 'config.xml'));
|
||||
if (matches.length) filepath = matches[0];
|
||||
}
|
||||
return filepath;
|
||||
}
|
||||
|
||||
// None of the special cases matched, returning project_dir/file.
|
||||
return filepath;
|
||||
}
|
||||
|
||||
// Find out the real name of an iOS project
|
||||
// TODO: glob is slow, need a better way or caching, or avoid using more than once.
|
||||
function getIOSProjectname(project_dir) {
|
||||
var matches = glob.sync(path.join(project_dir, '*.xcodeproj'));
|
||||
var iospath;
|
||||
if (matches.length === 1) {
|
||||
iospath = path.basename(matches[0],'.xcodeproj');
|
||||
} else {
|
||||
var msg;
|
||||
if (matches.length === 0) {
|
||||
msg = 'Does not appear to be an xcode project, no xcode project file in ' + project_dir;
|
||||
} else {
|
||||
msg = 'There are multiple *.xcodeproj dirs in ' + project_dir;
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
return iospath;
|
||||
}
|
||||
|
||||
// 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';
|
||||
}
|
||||
|
||||
module.exports = ConfigFile;
|
||||
|
||||
+65
-65
@@ -1,65 +1,65 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
/* jshint sub:true */
|
||||
|
||||
var path = require('path');
|
||||
var ConfigFile = require('./ConfigFile');
|
||||
|
||||
/******************************************************************************
|
||||
* ConfigKeeper class
|
||||
*
|
||||
* Used to load and store config files to avoid re-parsing and writing them out
|
||||
* multiple times.
|
||||
*
|
||||
* The config files are referred to by a fake path constructed as
|
||||
* project_dir/platform/file
|
||||
* where file is the name used for the file in config munges.
|
||||
******************************************************************************/
|
||||
function ConfigKeeper(project_dir, plugins_dir) {
|
||||
this.project_dir = project_dir;
|
||||
this.plugins_dir = plugins_dir;
|
||||
this._cached = {};
|
||||
}
|
||||
|
||||
ConfigKeeper.prototype.get = function ConfigKeeper_get(project_dir, platform, file) {
|
||||
var self = this;
|
||||
|
||||
// This fixes a bug with older plugins - when specifying config xml instead of res/xml/config.xml
|
||||
// https://issues.apache.org/jira/browse/CB-6414
|
||||
if(file == 'config.xml' && platform == 'android'){
|
||||
file = 'res/xml/config.xml';
|
||||
}
|
||||
var fake_path = path.join(project_dir, platform, file);
|
||||
|
||||
if (self._cached[fake_path]) {
|
||||
return self._cached[fake_path];
|
||||
}
|
||||
// File was not cached, need to load.
|
||||
var config_file = new ConfigFile(project_dir, platform, file);
|
||||
self._cached[fake_path] = config_file;
|
||||
return config_file;
|
||||
};
|
||||
|
||||
|
||||
ConfigKeeper.prototype.save_all = function ConfigKeeper_save_all() {
|
||||
var self = this;
|
||||
Object.keys(self._cached).forEach(function (fake_path) {
|
||||
var config_file = self._cached[fake_path];
|
||||
if (config_file.is_changed) config_file.save();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = ConfigKeeper;
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
/* jshint sub:true */
|
||||
|
||||
var path = require('path');
|
||||
var ConfigFile = require('./ConfigFile');
|
||||
|
||||
/******************************************************************************
|
||||
* ConfigKeeper class
|
||||
*
|
||||
* Used to load and store config files to avoid re-parsing and writing them out
|
||||
* multiple times.
|
||||
*
|
||||
* The config files are referred to by a fake path constructed as
|
||||
* project_dir/platform/file
|
||||
* where file is the name used for the file in config munges.
|
||||
******************************************************************************/
|
||||
function ConfigKeeper(project_dir, plugins_dir) {
|
||||
this.project_dir = project_dir;
|
||||
this.plugins_dir = plugins_dir;
|
||||
this._cached = {};
|
||||
}
|
||||
|
||||
ConfigKeeper.prototype.get = function ConfigKeeper_get(project_dir, platform, file) {
|
||||
var self = this;
|
||||
|
||||
// This fixes a bug with older plugins - when specifying config xml instead of res/xml/config.xml
|
||||
// https://issues.apache.org/jira/browse/CB-6414
|
||||
if(file == 'config.xml' && platform == 'android'){
|
||||
file = 'res/xml/config.xml';
|
||||
}
|
||||
var fake_path = path.join(project_dir, platform, file);
|
||||
|
||||
if (self._cached[fake_path]) {
|
||||
return self._cached[fake_path];
|
||||
}
|
||||
// File was not cached, need to load.
|
||||
var config_file = new ConfigFile(project_dir, platform, file);
|
||||
self._cached[fake_path] = config_file;
|
||||
return config_file;
|
||||
};
|
||||
|
||||
|
||||
ConfigKeeper.prototype.save_all = function ConfigKeeper_save_all() {
|
||||
var self = this;
|
||||
Object.keys(self._cached).forEach(function (fake_path) {
|
||||
var config_file = self._cached[fake_path];
|
||||
if (config_file.is_changed) config_file.save();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = ConfigKeeper;
|
||||
|
||||
+160
-160
@@ -1,160 +1,160 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
/* jshint sub:true */
|
||||
|
||||
var _ = require('underscore');
|
||||
|
||||
// add the count of [key1][key2]...[keyN] to obj
|
||||
// return true if it didn't exist before
|
||||
exports.deep_add = function deep_add(obj, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
|
||||
return exports.process_munge(obj, true/*createParents*/, function (parentArray, k) {
|
||||
var found = _.find(parentArray, function(element) {
|
||||
return element.xml == k.xml;
|
||||
});
|
||||
if (found) {
|
||||
found.after = found.after || k.after;
|
||||
found.count += k.count;
|
||||
} else {
|
||||
parentArray.push(k);
|
||||
}
|
||||
return !found;
|
||||
}, keys);
|
||||
};
|
||||
|
||||
// decrement the count of [key1][key2]...[keyN] from obj and remove if it reaches 0
|
||||
// return true if it was removed or not found
|
||||
exports.deep_remove = function deep_remove(obj, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
|
||||
var result = exports.process_munge(obj, false/*createParents*/, function (parentArray, k) {
|
||||
var index = -1;
|
||||
var found = _.find(parentArray, function (element) {
|
||||
index++;
|
||||
return element.xml == k.xml;
|
||||
});
|
||||
if (found) {
|
||||
found.count -= k.count;
|
||||
if (found.count > 0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
parentArray.splice(index, 1);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}, keys);
|
||||
|
||||
return typeof result === 'undefined' ? true : result;
|
||||
};
|
||||
|
||||
// search for [key1][key2]...[keyN]
|
||||
// return the object or undefined if not found
|
||||
exports.deep_find = function deep_find(obj, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
|
||||
return exports.process_munge(obj, false/*createParents?*/, function (parentArray, k) {
|
||||
return _.find(parentArray, function (element) {
|
||||
return element.xml == (k.xml || k);
|
||||
});
|
||||
}, keys);
|
||||
};
|
||||
|
||||
// Execute func passing it the parent array and the xmlChild key.
|
||||
// When createParents is true, add the file and parent items they are missing
|
||||
// When createParents is false, stop and return undefined if the file and/or parent items are missing
|
||||
|
||||
exports.process_munge = function process_munge(obj, createParents, func, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
var k = keys[0];
|
||||
if (keys.length == 1) {
|
||||
return func(obj, k);
|
||||
} else if (keys.length == 2) {
|
||||
if (!obj.parents[k] && !createParents) {
|
||||
return undefined;
|
||||
}
|
||||
obj.parents[k] = obj.parents[k] || [];
|
||||
return exports.process_munge(obj.parents[k], createParents, func, keys.slice(1));
|
||||
} else if (keys.length == 3){
|
||||
if (!obj.files[k] && !createParents) {
|
||||
return undefined;
|
||||
}
|
||||
obj.files[k] = obj.files[k] || { parents: {} };
|
||||
return exports.process_munge(obj.files[k], createParents, func, keys.slice(1));
|
||||
} else {
|
||||
throw new Error('Invalid key format. Must contain at most 3 elements (file, parent, xmlChild).');
|
||||
}
|
||||
};
|
||||
|
||||
// All values from munge are added to base as
|
||||
// base[file][selector][child] += munge[file][selector][child]
|
||||
// Returns a munge object containing values that exist in munge
|
||||
// but not in base.
|
||||
exports.increment_munge = function increment_munge(base, munge) {
|
||||
var diff = { files: {} };
|
||||
|
||||
for (var file in munge.files) {
|
||||
for (var selector in munge.files[file].parents) {
|
||||
for (var xml_child in munge.files[file].parents[selector]) {
|
||||
var val = munge.files[file].parents[selector][xml_child];
|
||||
// if node not in base, add it to diff and base
|
||||
// else increment it's value in base without adding to diff
|
||||
var newlyAdded = exports.deep_add(base, [file, selector, val]);
|
||||
if (newlyAdded) {
|
||||
exports.deep_add(diff, file, selector, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
};
|
||||
|
||||
// Update the base munge object as
|
||||
// base[file][selector][child] -= munge[file][selector][child]
|
||||
// nodes that reached zero value are removed from base and added to the returned munge
|
||||
// object.
|
||||
exports.decrement_munge = function decrement_munge(base, munge) {
|
||||
var zeroed = { files: {} };
|
||||
|
||||
for (var file in munge.files) {
|
||||
for (var selector in munge.files[file].parents) {
|
||||
for (var xml_child in munge.files[file].parents[selector]) {
|
||||
var val = munge.files[file].parents[selector][xml_child];
|
||||
// if node not in base, add it to diff and base
|
||||
// else increment it's value in base without adding to diff
|
||||
var removed = exports.deep_remove(base, [file, selector, val]);
|
||||
if (removed) {
|
||||
exports.deep_add(zeroed, file, selector, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return zeroed;
|
||||
};
|
||||
|
||||
// For better readability where used
|
||||
exports.clone_munge = function clone_munge(munge) {
|
||||
return exports.increment_munge({}, munge);
|
||||
};
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
/* jshint sub:true */
|
||||
|
||||
var _ = require('underscore');
|
||||
|
||||
// add the count of [key1][key2]...[keyN] to obj
|
||||
// return true if it didn't exist before
|
||||
exports.deep_add = function deep_add(obj, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
|
||||
return exports.process_munge(obj, true/*createParents*/, function (parentArray, k) {
|
||||
var found = _.find(parentArray, function(element) {
|
||||
return element.xml == k.xml;
|
||||
});
|
||||
if (found) {
|
||||
found.after = found.after || k.after;
|
||||
found.count += k.count;
|
||||
} else {
|
||||
parentArray.push(k);
|
||||
}
|
||||
return !found;
|
||||
}, keys);
|
||||
};
|
||||
|
||||
// decrement the count of [key1][key2]...[keyN] from obj and remove if it reaches 0
|
||||
// return true if it was removed or not found
|
||||
exports.deep_remove = function deep_remove(obj, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
|
||||
var result = exports.process_munge(obj, false/*createParents*/, function (parentArray, k) {
|
||||
var index = -1;
|
||||
var found = _.find(parentArray, function (element) {
|
||||
index++;
|
||||
return element.xml == k.xml;
|
||||
});
|
||||
if (found) {
|
||||
found.count -= k.count;
|
||||
if (found.count > 0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
parentArray.splice(index, 1);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}, keys);
|
||||
|
||||
return typeof result === 'undefined' ? true : result;
|
||||
};
|
||||
|
||||
// search for [key1][key2]...[keyN]
|
||||
// return the object or undefined if not found
|
||||
exports.deep_find = function deep_find(obj, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
|
||||
return exports.process_munge(obj, false/*createParents?*/, function (parentArray, k) {
|
||||
return _.find(parentArray, function (element) {
|
||||
return element.xml == (k.xml || k);
|
||||
});
|
||||
}, keys);
|
||||
};
|
||||
|
||||
// Execute func passing it the parent array and the xmlChild key.
|
||||
// When createParents is true, add the file and parent items they are missing
|
||||
// When createParents is false, stop and return undefined if the file and/or parent items are missing
|
||||
|
||||
exports.process_munge = function process_munge(obj, createParents, func, keys /* or key1, key2 .... */ ) {
|
||||
if ( !Array.isArray(keys) ) {
|
||||
keys = Array.prototype.slice.call(arguments, 1);
|
||||
}
|
||||
var k = keys[0];
|
||||
if (keys.length == 1) {
|
||||
return func(obj, k);
|
||||
} else if (keys.length == 2) {
|
||||
if (!obj.parents[k] && !createParents) {
|
||||
return undefined;
|
||||
}
|
||||
obj.parents[k] = obj.parents[k] || [];
|
||||
return exports.process_munge(obj.parents[k], createParents, func, keys.slice(1));
|
||||
} else if (keys.length == 3){
|
||||
if (!obj.files[k] && !createParents) {
|
||||
return undefined;
|
||||
}
|
||||
obj.files[k] = obj.files[k] || { parents: {} };
|
||||
return exports.process_munge(obj.files[k], createParents, func, keys.slice(1));
|
||||
} else {
|
||||
throw new Error('Invalid key format. Must contain at most 3 elements (file, parent, xmlChild).');
|
||||
}
|
||||
};
|
||||
|
||||
// All values from munge are added to base as
|
||||
// base[file][selector][child] += munge[file][selector][child]
|
||||
// Returns a munge object containing values that exist in munge
|
||||
// but not in base.
|
||||
exports.increment_munge = function increment_munge(base, munge) {
|
||||
var diff = { files: {} };
|
||||
|
||||
for (var file in munge.files) {
|
||||
for (var selector in munge.files[file].parents) {
|
||||
for (var xml_child in munge.files[file].parents[selector]) {
|
||||
var val = munge.files[file].parents[selector][xml_child];
|
||||
// if node not in base, add it to diff and base
|
||||
// else increment it's value in base without adding to diff
|
||||
var newlyAdded = exports.deep_add(base, [file, selector, val]);
|
||||
if (newlyAdded) {
|
||||
exports.deep_add(diff, file, selector, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return diff;
|
||||
};
|
||||
|
||||
// Update the base munge object as
|
||||
// base[file][selector][child] -= munge[file][selector][child]
|
||||
// nodes that reached zero value are removed from base and added to the returned munge
|
||||
// object.
|
||||
exports.decrement_munge = function decrement_munge(base, munge) {
|
||||
var zeroed = { files: {} };
|
||||
|
||||
for (var file in munge.files) {
|
||||
for (var selector in munge.files[file].parents) {
|
||||
for (var xml_child in munge.files[file].parents[selector]) {
|
||||
var val = munge.files[file].parents[selector][xml_child];
|
||||
// if node not in base, add it to diff and base
|
||||
// else increment it's value in base without adding to diff
|
||||
var removed = exports.deep_remove(base, [file, selector, val]);
|
||||
if (removed) {
|
||||
exports.deep_add(zeroed, file, selector, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return zeroed;
|
||||
};
|
||||
|
||||
// For better readability where used
|
||||
exports.clone_munge = function clone_munge(munge) {
|
||||
return exports.increment_munge({}, munge);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user