mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-04 00:02:03 +08:00
updated node_modules
This commit is contained in:
20
node_modules/cordova-common/src/.jshintrc
generated
vendored
20
node_modules/cordova-common/src/.jshintrc
generated
vendored
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"node": true
|
||||
, "bitwise": true
|
||||
, "undef": true
|
||||
, "trailing": true
|
||||
, "quotmark": true
|
||||
, "indent": 4
|
||||
, "unused": "vars"
|
||||
, "latedef": "nofunc"
|
||||
}
|
||||
{
|
||||
"node": true
|
||||
, "bitwise": true
|
||||
, "undef": true
|
||||
, "trailing": true
|
||||
, "quotmark": true
|
||||
, "indent": 4
|
||||
, "unused": "vars"
|
||||
, "latedef": "nofunc"
|
||||
}
|
||||
|
||||
170
node_modules/cordova-common/src/ActionStack.js
generated
vendored
170
node_modules/cordova-common/src/ActionStack.js
generated
vendored
@@ -1,85 +1,85 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint quotmark:false */
|
||||
|
||||
var events = require('./events'),
|
||||
Q = require('q');
|
||||
|
||||
function ActionStack() {
|
||||
this.stack = [];
|
||||
this.completed = [];
|
||||
}
|
||||
|
||||
ActionStack.prototype = {
|
||||
createAction:function(handler, action_params, reverter, revert_params) {
|
||||
return {
|
||||
handler:{
|
||||
run:handler,
|
||||
params:action_params
|
||||
},
|
||||
reverter:{
|
||||
run:reverter,
|
||||
params:revert_params
|
||||
}
|
||||
};
|
||||
},
|
||||
push:function(tx) {
|
||||
this.stack.push(tx);
|
||||
},
|
||||
// Returns a promise.
|
||||
process:function(platform) {
|
||||
events.emit('verbose', 'Beginning processing of action stack for ' + platform + ' project...');
|
||||
|
||||
while (this.stack.length) {
|
||||
var action = this.stack.shift();
|
||||
var handler = action.handler.run;
|
||||
var action_params = action.handler.params;
|
||||
|
||||
try {
|
||||
handler.apply(null, action_params);
|
||||
} catch(e) {
|
||||
events.emit('warn', 'Error during processing of action! Attempting to revert...');
|
||||
this.stack.unshift(action);
|
||||
var issue = 'Uh oh!\n';
|
||||
// revert completed tasks
|
||||
while(this.completed.length) {
|
||||
var undo = this.completed.shift();
|
||||
var revert = undo.reverter.run;
|
||||
var revert_params = undo.reverter.params;
|
||||
|
||||
try {
|
||||
revert.apply(null, revert_params);
|
||||
} catch(err) {
|
||||
events.emit('warn', 'Error during reversion of action! We probably really messed up your project now, sorry! D:');
|
||||
issue += 'A reversion action failed: ' + err.message + '\n';
|
||||
}
|
||||
}
|
||||
e.message = issue + e.message;
|
||||
return Q.reject(e);
|
||||
}
|
||||
this.completed.push(action);
|
||||
}
|
||||
events.emit('verbose', 'Action stack processing complete.');
|
||||
|
||||
return Q();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ActionStack;
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint quotmark:false */
|
||||
|
||||
var events = require('./events'),
|
||||
Q = require('q');
|
||||
|
||||
function ActionStack() {
|
||||
this.stack = [];
|
||||
this.completed = [];
|
||||
}
|
||||
|
||||
ActionStack.prototype = {
|
||||
createAction:function(handler, action_params, reverter, revert_params) {
|
||||
return {
|
||||
handler:{
|
||||
run:handler,
|
||||
params:action_params
|
||||
},
|
||||
reverter:{
|
||||
run:reverter,
|
||||
params:revert_params
|
||||
}
|
||||
};
|
||||
},
|
||||
push:function(tx) {
|
||||
this.stack.push(tx);
|
||||
},
|
||||
// Returns a promise.
|
||||
process:function(platform) {
|
||||
events.emit('verbose', 'Beginning processing of action stack for ' + platform + ' project...');
|
||||
|
||||
while (this.stack.length) {
|
||||
var action = this.stack.shift();
|
||||
var handler = action.handler.run;
|
||||
var action_params = action.handler.params;
|
||||
|
||||
try {
|
||||
handler.apply(null, action_params);
|
||||
} catch(e) {
|
||||
events.emit('warn', 'Error during processing of action! Attempting to revert...');
|
||||
this.stack.unshift(action);
|
||||
var issue = 'Uh oh!\n';
|
||||
// revert completed tasks
|
||||
while(this.completed.length) {
|
||||
var undo = this.completed.shift();
|
||||
var revert = undo.reverter.run;
|
||||
var revert_params = undo.reverter.params;
|
||||
|
||||
try {
|
||||
revert.apply(null, revert_params);
|
||||
} catch(err) {
|
||||
events.emit('warn', 'Error during reversion of action! We probably really messed up your project now, sorry! D:');
|
||||
issue += 'A reversion action failed: ' + err.message + '\n';
|
||||
}
|
||||
}
|
||||
e.message = issue + e.message;
|
||||
return Q.reject(e);
|
||||
}
|
||||
this.completed.push(action);
|
||||
}
|
||||
events.emit('verbose', 'Action stack processing complete.');
|
||||
|
||||
return Q();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ActionStack;
|
||||
|
||||
650
node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js
generated
vendored
650
node_modules/cordova-common/src/ConfigChanges/ConfigChanges.js
generated
vendored
@@ -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 ****/
|
||||
|
||||
416
node_modules/cordova-common/src/ConfigChanges/ConfigFile.js
generated
vendored
416
node_modules/cordova-common/src/ConfigChanges/ConfigFile.js
generated
vendored
@@ -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;
|
||||
|
||||
130
node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js
generated
vendored
130
node_modules/cordova-common/src/ConfigChanges/ConfigKeeper.js
generated
vendored
@@ -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;
|
||||
|
||||
320
node_modules/cordova-common/src/ConfigChanges/munge-util.js
generated
vendored
320
node_modules/cordova-common/src/ConfigChanges/munge-util.js
generated
vendored
@@ -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);
|
||||
};
|
||||
|
||||
998
node_modules/cordova-common/src/ConfigParser/ConfigParser.js
generated
vendored
998
node_modules/cordova-common/src/ConfigParser/ConfigParser.js
generated
vendored
@@ -1,499 +1,499 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var et = require('elementtree'),
|
||||
xml= require('../util/xml-helpers'),
|
||||
CordovaError = require('../CordovaError/CordovaError'),
|
||||
fs = require('fs'),
|
||||
events = require('../events');
|
||||
|
||||
|
||||
/** Wraps a config.xml file */
|
||||
function ConfigParser(path) {
|
||||
this.path = path;
|
||||
try {
|
||||
this.doc = xml.parseElementtreeSync(path);
|
||||
this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc);
|
||||
et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0');
|
||||
} catch (e) {
|
||||
console.error('Parsing '+path+' failed');
|
||||
throw e;
|
||||
}
|
||||
var r = this.doc.getroot();
|
||||
if (r.tag !== 'widget') {
|
||||
throw new CordovaError(path + ' has incorrect root node name (expected "widget", was "' + r.tag + '")');
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeTextSafe(el) {
|
||||
return el && el.text && el.text.trim();
|
||||
}
|
||||
|
||||
function findOrCreate(doc, name) {
|
||||
var ret = doc.find(name);
|
||||
if (!ret) {
|
||||
ret = new et.Element(name);
|
||||
doc.getroot().append(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getCordovaNamespacePrefix(doc){
|
||||
var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib);
|
||||
var prefix = 'cdv';
|
||||
for (var j = 0; j < rootAtribs.length; j++ ) {
|
||||
if(rootAtribs[j].indexOf('xmlns:') === 0 &&
|
||||
doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0'){
|
||||
var strings = rootAtribs[j].split(':');
|
||||
prefix = strings[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the value of an element's attribute
|
||||
* @param {String} attributeName Name of the attribute to search for
|
||||
* @param {Array} elems An array of ElementTree nodes
|
||||
* @return {String}
|
||||
*/
|
||||
function findElementAttributeValue(attributeName, elems) {
|
||||
|
||||
elems = Array.isArray(elems) ? elems : [ elems ];
|
||||
|
||||
var value = elems.filter(function (elem) {
|
||||
return elem.attrib.name.toLowerCase() === attributeName.toLowerCase();
|
||||
}).map(function (filteredElems) {
|
||||
return filteredElems.attrib.value;
|
||||
}).pop();
|
||||
|
||||
return value ? value : '';
|
||||
}
|
||||
|
||||
ConfigParser.prototype = {
|
||||
packageName: function(id) {
|
||||
return this.doc.getroot().attrib['id'];
|
||||
},
|
||||
setPackageName: function(id) {
|
||||
this.doc.getroot().attrib['id'] = id;
|
||||
},
|
||||
android_packageName: function() {
|
||||
return this.doc.getroot().attrib['android-packageName'];
|
||||
},
|
||||
android_activityName: function() {
|
||||
return this.doc.getroot().attrib['android-activityName'];
|
||||
},
|
||||
ios_CFBundleIdentifier: function() {
|
||||
return this.doc.getroot().attrib['ios-CFBundleIdentifier'];
|
||||
},
|
||||
name: function() {
|
||||
return getNodeTextSafe(this.doc.find('name'));
|
||||
},
|
||||
setName: function(name) {
|
||||
var el = findOrCreate(this.doc, 'name');
|
||||
el.text = name;
|
||||
},
|
||||
description: function() {
|
||||
return getNodeTextSafe(this.doc.find('description'));
|
||||
},
|
||||
setDescription: function(text) {
|
||||
var el = findOrCreate(this.doc, 'description');
|
||||
el.text = text;
|
||||
},
|
||||
version: function() {
|
||||
return this.doc.getroot().attrib['version'];
|
||||
},
|
||||
windows_packageVersion: function() {
|
||||
return this.doc.getroot().attrib('windows-packageVersion');
|
||||
},
|
||||
android_versionCode: function() {
|
||||
return this.doc.getroot().attrib['android-versionCode'];
|
||||
},
|
||||
ios_CFBundleVersion: function() {
|
||||
return this.doc.getroot().attrib['ios-CFBundleVersion'];
|
||||
},
|
||||
setVersion: function(value) {
|
||||
this.doc.getroot().attrib['version'] = value;
|
||||
},
|
||||
author: function() {
|
||||
return getNodeTextSafe(this.doc.find('author'));
|
||||
},
|
||||
getGlobalPreference: function (name) {
|
||||
return findElementAttributeValue(name, this.doc.findall('preference'));
|
||||
},
|
||||
setGlobalPreference: function (name, value) {
|
||||
var pref = this.doc.find('preference[@name="' + name + '"]');
|
||||
if (!pref) {
|
||||
pref = new et.Element('preference');
|
||||
pref.attrib.name = name;
|
||||
this.doc.getroot().append(pref);
|
||||
}
|
||||
pref.attrib.value = value;
|
||||
},
|
||||
getPlatformPreference: function (name, platform) {
|
||||
return findElementAttributeValue(name, this.doc.findall('platform[@name=\'' + platform + '\']/preference'));
|
||||
},
|
||||
getPreference: function(name, platform) {
|
||||
|
||||
var platformPreference = '';
|
||||
|
||||
if (platform) {
|
||||
platformPreference = this.getPlatformPreference(name, platform);
|
||||
}
|
||||
|
||||
return platformPreference ? platformPreference : this.getGlobalPreference(name);
|
||||
|
||||
},
|
||||
/**
|
||||
* Returns all resources for the platform specified.
|
||||
* @param {String} platform The platform.
|
||||
* @param {string} resourceName Type of static resources to return.
|
||||
* "icon" and "splash" currently supported.
|
||||
* @return {Array} Resources for the platform specified.
|
||||
*/
|
||||
getStaticResources: function(platform, resourceName) {
|
||||
var ret = [],
|
||||
staticResources = [];
|
||||
if (platform) { // platform specific icons
|
||||
this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function(elt){
|
||||
elt.platform = platform; // mark as platform specific resource
|
||||
staticResources.push(elt);
|
||||
});
|
||||
}
|
||||
// root level resources
|
||||
staticResources = staticResources.concat(this.doc.findall(resourceName));
|
||||
// parse resource elements
|
||||
var that = this;
|
||||
staticResources.forEach(function (elt) {
|
||||
var res = {};
|
||||
res.src = elt.attrib.src;
|
||||
res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix+':density'] || elt.attrib['gap:density'];
|
||||
res.platform = elt.platform || null; // null means icon represents default icon (shared between platforms)
|
||||
res.width = +elt.attrib.width || undefined;
|
||||
res.height = +elt.attrib.height || undefined;
|
||||
|
||||
// default icon
|
||||
if (!res.width && !res.height && !res.density) {
|
||||
ret.defaultResource = res;
|
||||
}
|
||||
ret.push(res);
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns resource with specified width and/or height.
|
||||
* @param {number} width Width of resource.
|
||||
* @param {number} height Height of resource.
|
||||
* @return {Resource} Resource object or null if not found.
|
||||
*/
|
||||
ret.getBySize = function(width, height) {
|
||||
return ret.filter(function(res) {
|
||||
if (!res.width && !res.height) {
|
||||
return false;
|
||||
}
|
||||
return ((!res.width || (width == res.width)) &&
|
||||
(!res.height || (height == res.height)));
|
||||
})[0] || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns resource with specified density.
|
||||
* @param {string} density Density of resource.
|
||||
* @return {Resource} Resource object or null if not found.
|
||||
*/
|
||||
ret.getByDensity = function(density) {
|
||||
return ret.filter(function(res) {
|
||||
return res.density == density;
|
||||
})[0] || null;
|
||||
};
|
||||
|
||||
/** Returns default icons */
|
||||
ret.getDefault = function() {
|
||||
return ret.defaultResource;
|
||||
};
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all icons for specific platform.
|
||||
* @param {string} platform Platform name
|
||||
* @return {Resource[]} Array of icon objects.
|
||||
*/
|
||||
getIcons: function(platform) {
|
||||
return this.getStaticResources(platform, 'icon');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all splash images for specific platform.
|
||||
* @param {string} platform Platform name
|
||||
* @return {Resource[]} Array of Splash objects.
|
||||
*/
|
||||
getSplashScreens: function(platform) {
|
||||
return this.getStaticResources(platform, 'splash');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all hook scripts for the hook type specified.
|
||||
* @param {String} hook The hook type.
|
||||
* @param {Array} platforms Platforms to look for scripts into (root scripts will be included as well).
|
||||
* @return {Array} Script elements.
|
||||
*/
|
||||
getHookScripts: function(hook, platforms) {
|
||||
var self = this;
|
||||
var scriptElements = self.doc.findall('./hook');
|
||||
|
||||
if(platforms) {
|
||||
platforms.forEach(function (platform) {
|
||||
scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook'));
|
||||
});
|
||||
}
|
||||
|
||||
function filterScriptByHookType(el) {
|
||||
return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
|
||||
}
|
||||
|
||||
return scriptElements.filter(filterScriptByHookType);
|
||||
},
|
||||
/**
|
||||
* Returns a list of plugin (IDs).
|
||||
*
|
||||
* This function also returns any plugin's that
|
||||
* were defined using the legacy <feature> tags.
|
||||
* @return {string[]} Array of plugin IDs
|
||||
*/
|
||||
getPluginIdList: function () {
|
||||
var plugins = this.doc.findall('plugin');
|
||||
var result = plugins.map(function(plugin){
|
||||
return plugin.attrib.name;
|
||||
});
|
||||
var features = this.doc.findall('feature');
|
||||
features.forEach(function(element ){
|
||||
var idTag = element.find('./param[@name="id"]');
|
||||
if(idTag){
|
||||
result.push(idTag.attrib.value);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
getPlugins: function () {
|
||||
return this.getPluginIdList().map(function (pluginId) {
|
||||
return this.getPlugin(pluginId);
|
||||
}, this);
|
||||
},
|
||||
/**
|
||||
* Adds a plugin element. Does not check for duplicates.
|
||||
* @name addPlugin
|
||||
* @function
|
||||
* @param {object} attributes name and spec are supported
|
||||
* @param {Array|object} variables name, value or arbitary object
|
||||
*/
|
||||
addPlugin: function (attributes, variables) {
|
||||
if (!attributes && !attributes.name) return;
|
||||
var el = new et.Element('plugin');
|
||||
el.attrib.name = attributes.name;
|
||||
if (attributes.spec) {
|
||||
el.attrib.spec = attributes.spec;
|
||||
}
|
||||
|
||||
// support arbitrary object as variables source
|
||||
if (variables && typeof variables === 'object' && !Array.isArray(variables)) {
|
||||
variables = Object.keys(variables)
|
||||
.map(function (variableName) {
|
||||
return {name: variableName, value: variables[variableName]};
|
||||
});
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
variables.forEach(function (variable) {
|
||||
el.append(new et.Element('variable', { name: variable.name, value: variable.value }));
|
||||
});
|
||||
}
|
||||
this.doc.getroot().append(el);
|
||||
},
|
||||
/**
|
||||
* Retrives the plugin with the given id or null if not found.
|
||||
*
|
||||
* This function also returns any plugin's that
|
||||
* were defined using the legacy <feature> tags.
|
||||
* @name getPlugin
|
||||
* @function
|
||||
* @param {String} id
|
||||
* @returns {object} plugin including any variables
|
||||
*/
|
||||
getPlugin: function(id){
|
||||
if(!id){
|
||||
return undefined;
|
||||
}
|
||||
var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]');
|
||||
if (null === pluginElement) {
|
||||
var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
|
||||
if(legacyFeature){
|
||||
events.emit('log', 'Found deprecated feature entry for ' + id +' in config.xml.');
|
||||
return featureToPlugin(legacyFeature);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
var plugin = {};
|
||||
|
||||
plugin.name = pluginElement.attrib.name;
|
||||
plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version;
|
||||
plugin.variables = {};
|
||||
var variableElements = pluginElement.findall('variable');
|
||||
variableElements.forEach(function(varElement){
|
||||
var name = varElement.attrib.name;
|
||||
var value = varElement.attrib.value;
|
||||
if(name){
|
||||
plugin.variables[name] = value;
|
||||
}
|
||||
});
|
||||
return plugin;
|
||||
},
|
||||
/**
|
||||
* Remove the plugin entry with give name (id).
|
||||
*
|
||||
* This function also operates on any plugin's that
|
||||
* were defined using the legacy <feature> tags.
|
||||
* @name removePlugin
|
||||
* @function
|
||||
* @param id name of the plugin
|
||||
*/
|
||||
removePlugin: function(id){
|
||||
if(id){
|
||||
var plugins = this.doc.findall('./plugin/[@name="' + id + '"]')
|
||||
.concat(this.doc.findall('./feature/param[@name="id"][@value="' + id + '"]/..'));
|
||||
var children = this.doc.getroot().getchildren();
|
||||
plugins.forEach(function (plugin) {
|
||||
var idx = children.indexOf(plugin);
|
||||
if (idx > -1) {
|
||||
children.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Add any element to the root
|
||||
addElement: function(name, attributes) {
|
||||
var el = et.Element(name);
|
||||
for (var a in attributes) {
|
||||
el.attrib[a] = attributes[a];
|
||||
}
|
||||
this.doc.getroot().append(el);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an engine. Does not check for duplicates.
|
||||
* @param {String} name the engine name
|
||||
* @param {String} spec engine source location or version (optional)
|
||||
*/
|
||||
addEngine: function(name, spec){
|
||||
if(!name) return;
|
||||
var el = et.Element('engine');
|
||||
el.attrib.name = name;
|
||||
if(spec){
|
||||
el.attrib.spec = spec;
|
||||
}
|
||||
this.doc.getroot().append(el);
|
||||
},
|
||||
/**
|
||||
* Removes all the engines with given name
|
||||
* @param {String} name the engine name.
|
||||
*/
|
||||
removeEngine: function(name){
|
||||
var engines = this.doc.findall('./engine/[@name="' +name+'"]');
|
||||
for(var i=0; i < engines.length; i++){
|
||||
var children = this.doc.getroot().getchildren();
|
||||
var idx = children.indexOf(engines[i]);
|
||||
if(idx > -1){
|
||||
children.splice(idx,1);
|
||||
}
|
||||
}
|
||||
},
|
||||
getEngines: function(){
|
||||
var engines = this.doc.findall('./engine');
|
||||
return engines.map(function(engine){
|
||||
var spec = engine.attrib.spec || engine.attrib.version;
|
||||
return {
|
||||
'name': engine.attrib.name,
|
||||
'spec': spec ? spec : null
|
||||
};
|
||||
});
|
||||
},
|
||||
/* Get all the access tags */
|
||||
getAccesses: function() {
|
||||
var accesses = this.doc.findall('./access');
|
||||
return accesses.map(function(access){
|
||||
var minimum_tls_version = access.attrib['minimum-tls-version']; /* String */
|
||||
var requires_forward_secrecy = access.attrib['requires-forward-secrecy']; /* Boolean */
|
||||
return {
|
||||
'origin': access.attrib.origin,
|
||||
'minimum_tls_version': minimum_tls_version,
|
||||
'requires_forward_secrecy' : requires_forward_secrecy
|
||||
};
|
||||
});
|
||||
},
|
||||
/* Get all the allow-navigation tags */
|
||||
getAllowNavigations: function() {
|
||||
var allow_navigations = this.doc.findall('./allow-navigation');
|
||||
return allow_navigations.map(function(allow_navigation){
|
||||
var minimum_tls_version = allow_navigation.attrib['minimum-tls-version']; /* String */
|
||||
var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy']; /* Boolean */
|
||||
return {
|
||||
'href': allow_navigation.attrib.href,
|
||||
'minimum_tls_version': minimum_tls_version,
|
||||
'requires_forward_secrecy' : requires_forward_secrecy
|
||||
};
|
||||
});
|
||||
},
|
||||
write:function() {
|
||||
fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
|
||||
}
|
||||
};
|
||||
|
||||
function featureToPlugin(featureElement) {
|
||||
var plugin = {};
|
||||
plugin.variables = [];
|
||||
var pluginVersion,
|
||||
pluginSrc;
|
||||
|
||||
var nodes = featureElement.findall('param');
|
||||
nodes.forEach(function (element) {
|
||||
var n = element.attrib.name;
|
||||
var v = element.attrib.value;
|
||||
if (n === 'id') {
|
||||
plugin.name = v;
|
||||
} else if (n === 'version') {
|
||||
pluginVersion = v;
|
||||
} else if (n === 'url' || n === 'installPath') {
|
||||
pluginSrc = v;
|
||||
} else {
|
||||
plugin.variables[n] = v;
|
||||
}
|
||||
});
|
||||
|
||||
var spec = pluginSrc || pluginVersion;
|
||||
if (spec) {
|
||||
plugin.spec = spec;
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
module.exports = ConfigParser;
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var et = require('elementtree'),
|
||||
xml= require('../util/xml-helpers'),
|
||||
CordovaError = require('../CordovaError/CordovaError'),
|
||||
fs = require('fs'),
|
||||
events = require('../events');
|
||||
|
||||
|
||||
/** Wraps a config.xml file */
|
||||
function ConfigParser(path) {
|
||||
this.path = path;
|
||||
try {
|
||||
this.doc = xml.parseElementtreeSync(path);
|
||||
this.cdvNamespacePrefix = getCordovaNamespacePrefix(this.doc);
|
||||
et.register_namespace(this.cdvNamespacePrefix, 'http://cordova.apache.org/ns/1.0');
|
||||
} catch (e) {
|
||||
console.error('Parsing '+path+' failed');
|
||||
throw e;
|
||||
}
|
||||
var r = this.doc.getroot();
|
||||
if (r.tag !== 'widget') {
|
||||
throw new CordovaError(path + ' has incorrect root node name (expected "widget", was "' + r.tag + '")');
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeTextSafe(el) {
|
||||
return el && el.text && el.text.trim();
|
||||
}
|
||||
|
||||
function findOrCreate(doc, name) {
|
||||
var ret = doc.find(name);
|
||||
if (!ret) {
|
||||
ret = new et.Element(name);
|
||||
doc.getroot().append(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getCordovaNamespacePrefix(doc){
|
||||
var rootAtribs = Object.getOwnPropertyNames(doc.getroot().attrib);
|
||||
var prefix = 'cdv';
|
||||
for (var j = 0; j < rootAtribs.length; j++ ) {
|
||||
if(rootAtribs[j].indexOf('xmlns:') === 0 &&
|
||||
doc.getroot().attrib[rootAtribs[j]] === 'http://cordova.apache.org/ns/1.0'){
|
||||
var strings = rootAtribs[j].split(':');
|
||||
prefix = strings[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the value of an element's attribute
|
||||
* @param {String} attributeName Name of the attribute to search for
|
||||
* @param {Array} elems An array of ElementTree nodes
|
||||
* @return {String}
|
||||
*/
|
||||
function findElementAttributeValue(attributeName, elems) {
|
||||
|
||||
elems = Array.isArray(elems) ? elems : [ elems ];
|
||||
|
||||
var value = elems.filter(function (elem) {
|
||||
return elem.attrib.name.toLowerCase() === attributeName.toLowerCase();
|
||||
}).map(function (filteredElems) {
|
||||
return filteredElems.attrib.value;
|
||||
}).pop();
|
||||
|
||||
return value ? value : '';
|
||||
}
|
||||
|
||||
ConfigParser.prototype = {
|
||||
packageName: function(id) {
|
||||
return this.doc.getroot().attrib['id'];
|
||||
},
|
||||
setPackageName: function(id) {
|
||||
this.doc.getroot().attrib['id'] = id;
|
||||
},
|
||||
android_packageName: function() {
|
||||
return this.doc.getroot().attrib['android-packageName'];
|
||||
},
|
||||
android_activityName: function() {
|
||||
return this.doc.getroot().attrib['android-activityName'];
|
||||
},
|
||||
ios_CFBundleIdentifier: function() {
|
||||
return this.doc.getroot().attrib['ios-CFBundleIdentifier'];
|
||||
},
|
||||
name: function() {
|
||||
return getNodeTextSafe(this.doc.find('name'));
|
||||
},
|
||||
setName: function(name) {
|
||||
var el = findOrCreate(this.doc, 'name');
|
||||
el.text = name;
|
||||
},
|
||||
description: function() {
|
||||
return getNodeTextSafe(this.doc.find('description'));
|
||||
},
|
||||
setDescription: function(text) {
|
||||
var el = findOrCreate(this.doc, 'description');
|
||||
el.text = text;
|
||||
},
|
||||
version: function() {
|
||||
return this.doc.getroot().attrib['version'];
|
||||
},
|
||||
windows_packageVersion: function() {
|
||||
return this.doc.getroot().attrib('windows-packageVersion');
|
||||
},
|
||||
android_versionCode: function() {
|
||||
return this.doc.getroot().attrib['android-versionCode'];
|
||||
},
|
||||
ios_CFBundleVersion: function() {
|
||||
return this.doc.getroot().attrib['ios-CFBundleVersion'];
|
||||
},
|
||||
setVersion: function(value) {
|
||||
this.doc.getroot().attrib['version'] = value;
|
||||
},
|
||||
author: function() {
|
||||
return getNodeTextSafe(this.doc.find('author'));
|
||||
},
|
||||
getGlobalPreference: function (name) {
|
||||
return findElementAttributeValue(name, this.doc.findall('preference'));
|
||||
},
|
||||
setGlobalPreference: function (name, value) {
|
||||
var pref = this.doc.find('preference[@name="' + name + '"]');
|
||||
if (!pref) {
|
||||
pref = new et.Element('preference');
|
||||
pref.attrib.name = name;
|
||||
this.doc.getroot().append(pref);
|
||||
}
|
||||
pref.attrib.value = value;
|
||||
},
|
||||
getPlatformPreference: function (name, platform) {
|
||||
return findElementAttributeValue(name, this.doc.findall('platform[@name=\'' + platform + '\']/preference'));
|
||||
},
|
||||
getPreference: function(name, platform) {
|
||||
|
||||
var platformPreference = '';
|
||||
|
||||
if (platform) {
|
||||
platformPreference = this.getPlatformPreference(name, platform);
|
||||
}
|
||||
|
||||
return platformPreference ? platformPreference : this.getGlobalPreference(name);
|
||||
|
||||
},
|
||||
/**
|
||||
* Returns all resources for the platform specified.
|
||||
* @param {String} platform The platform.
|
||||
* @param {string} resourceName Type of static resources to return.
|
||||
* "icon" and "splash" currently supported.
|
||||
* @return {Array} Resources for the platform specified.
|
||||
*/
|
||||
getStaticResources: function(platform, resourceName) {
|
||||
var ret = [],
|
||||
staticResources = [];
|
||||
if (platform) { // platform specific icons
|
||||
this.doc.findall('platform[@name=\'' + platform + '\']/' + resourceName).forEach(function(elt){
|
||||
elt.platform = platform; // mark as platform specific resource
|
||||
staticResources.push(elt);
|
||||
});
|
||||
}
|
||||
// root level resources
|
||||
staticResources = staticResources.concat(this.doc.findall(resourceName));
|
||||
// parse resource elements
|
||||
var that = this;
|
||||
staticResources.forEach(function (elt) {
|
||||
var res = {};
|
||||
res.src = elt.attrib.src;
|
||||
res.density = elt.attrib['density'] || elt.attrib[that.cdvNamespacePrefix+':density'] || elt.attrib['gap:density'];
|
||||
res.platform = elt.platform || null; // null means icon represents default icon (shared between platforms)
|
||||
res.width = +elt.attrib.width || undefined;
|
||||
res.height = +elt.attrib.height || undefined;
|
||||
|
||||
// default icon
|
||||
if (!res.width && !res.height && !res.density) {
|
||||
ret.defaultResource = res;
|
||||
}
|
||||
ret.push(res);
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns resource with specified width and/or height.
|
||||
* @param {number} width Width of resource.
|
||||
* @param {number} height Height of resource.
|
||||
* @return {Resource} Resource object or null if not found.
|
||||
*/
|
||||
ret.getBySize = function(width, height) {
|
||||
return ret.filter(function(res) {
|
||||
if (!res.width && !res.height) {
|
||||
return false;
|
||||
}
|
||||
return ((!res.width || (width == res.width)) &&
|
||||
(!res.height || (height == res.height)));
|
||||
})[0] || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns resource with specified density.
|
||||
* @param {string} density Density of resource.
|
||||
* @return {Resource} Resource object or null if not found.
|
||||
*/
|
||||
ret.getByDensity = function(density) {
|
||||
return ret.filter(function(res) {
|
||||
return res.density == density;
|
||||
})[0] || null;
|
||||
};
|
||||
|
||||
/** Returns default icons */
|
||||
ret.getDefault = function() {
|
||||
return ret.defaultResource;
|
||||
};
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all icons for specific platform.
|
||||
* @param {string} platform Platform name
|
||||
* @return {Resource[]} Array of icon objects.
|
||||
*/
|
||||
getIcons: function(platform) {
|
||||
return this.getStaticResources(platform, 'icon');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all splash images for specific platform.
|
||||
* @param {string} platform Platform name
|
||||
* @return {Resource[]} Array of Splash objects.
|
||||
*/
|
||||
getSplashScreens: function(platform) {
|
||||
return this.getStaticResources(platform, 'splash');
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all hook scripts for the hook type specified.
|
||||
* @param {String} hook The hook type.
|
||||
* @param {Array} platforms Platforms to look for scripts into (root scripts will be included as well).
|
||||
* @return {Array} Script elements.
|
||||
*/
|
||||
getHookScripts: function(hook, platforms) {
|
||||
var self = this;
|
||||
var scriptElements = self.doc.findall('./hook');
|
||||
|
||||
if(platforms) {
|
||||
platforms.forEach(function (platform) {
|
||||
scriptElements = scriptElements.concat(self.doc.findall('./platform[@name="' + platform + '"]/hook'));
|
||||
});
|
||||
}
|
||||
|
||||
function filterScriptByHookType(el) {
|
||||
return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
|
||||
}
|
||||
|
||||
return scriptElements.filter(filterScriptByHookType);
|
||||
},
|
||||
/**
|
||||
* Returns a list of plugin (IDs).
|
||||
*
|
||||
* This function also returns any plugin's that
|
||||
* were defined using the legacy <feature> tags.
|
||||
* @return {string[]} Array of plugin IDs
|
||||
*/
|
||||
getPluginIdList: function () {
|
||||
var plugins = this.doc.findall('plugin');
|
||||
var result = plugins.map(function(plugin){
|
||||
return plugin.attrib.name;
|
||||
});
|
||||
var features = this.doc.findall('feature');
|
||||
features.forEach(function(element ){
|
||||
var idTag = element.find('./param[@name="id"]');
|
||||
if(idTag){
|
||||
result.push(idTag.attrib.value);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
getPlugins: function () {
|
||||
return this.getPluginIdList().map(function (pluginId) {
|
||||
return this.getPlugin(pluginId);
|
||||
}, this);
|
||||
},
|
||||
/**
|
||||
* Adds a plugin element. Does not check for duplicates.
|
||||
* @name addPlugin
|
||||
* @function
|
||||
* @param {object} attributes name and spec are supported
|
||||
* @param {Array|object} variables name, value or arbitary object
|
||||
*/
|
||||
addPlugin: function (attributes, variables) {
|
||||
if (!attributes && !attributes.name) return;
|
||||
var el = new et.Element('plugin');
|
||||
el.attrib.name = attributes.name;
|
||||
if (attributes.spec) {
|
||||
el.attrib.spec = attributes.spec;
|
||||
}
|
||||
|
||||
// support arbitrary object as variables source
|
||||
if (variables && typeof variables === 'object' && !Array.isArray(variables)) {
|
||||
variables = Object.keys(variables)
|
||||
.map(function (variableName) {
|
||||
return {name: variableName, value: variables[variableName]};
|
||||
});
|
||||
}
|
||||
|
||||
if (variables) {
|
||||
variables.forEach(function (variable) {
|
||||
el.append(new et.Element('variable', { name: variable.name, value: variable.value }));
|
||||
});
|
||||
}
|
||||
this.doc.getroot().append(el);
|
||||
},
|
||||
/**
|
||||
* Retrives the plugin with the given id or null if not found.
|
||||
*
|
||||
* This function also returns any plugin's that
|
||||
* were defined using the legacy <feature> tags.
|
||||
* @name getPlugin
|
||||
* @function
|
||||
* @param {String} id
|
||||
* @returns {object} plugin including any variables
|
||||
*/
|
||||
getPlugin: function(id){
|
||||
if(!id){
|
||||
return undefined;
|
||||
}
|
||||
var pluginElement = this.doc.find('./plugin/[@name="' + id + '"]');
|
||||
if (null === pluginElement) {
|
||||
var legacyFeature = this.doc.find('./feature/param[@name="id"][@value="' + id + '"]/..');
|
||||
if(legacyFeature){
|
||||
events.emit('log', 'Found deprecated feature entry for ' + id +' in config.xml.');
|
||||
return featureToPlugin(legacyFeature);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
var plugin = {};
|
||||
|
||||
plugin.name = pluginElement.attrib.name;
|
||||
plugin.spec = pluginElement.attrib.spec || pluginElement.attrib.src || pluginElement.attrib.version;
|
||||
plugin.variables = {};
|
||||
var variableElements = pluginElement.findall('variable');
|
||||
variableElements.forEach(function(varElement){
|
||||
var name = varElement.attrib.name;
|
||||
var value = varElement.attrib.value;
|
||||
if(name){
|
||||
plugin.variables[name] = value;
|
||||
}
|
||||
});
|
||||
return plugin;
|
||||
},
|
||||
/**
|
||||
* Remove the plugin entry with give name (id).
|
||||
*
|
||||
* This function also operates on any plugin's that
|
||||
* were defined using the legacy <feature> tags.
|
||||
* @name removePlugin
|
||||
* @function
|
||||
* @param id name of the plugin
|
||||
*/
|
||||
removePlugin: function(id){
|
||||
if(id){
|
||||
var plugins = this.doc.findall('./plugin/[@name="' + id + '"]')
|
||||
.concat(this.doc.findall('./feature/param[@name="id"][@value="' + id + '"]/..'));
|
||||
var children = this.doc.getroot().getchildren();
|
||||
plugins.forEach(function (plugin) {
|
||||
var idx = children.indexOf(plugin);
|
||||
if (idx > -1) {
|
||||
children.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Add any element to the root
|
||||
addElement: function(name, attributes) {
|
||||
var el = et.Element(name);
|
||||
for (var a in attributes) {
|
||||
el.attrib[a] = attributes[a];
|
||||
}
|
||||
this.doc.getroot().append(el);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an engine. Does not check for duplicates.
|
||||
* @param {String} name the engine name
|
||||
* @param {String} spec engine source location or version (optional)
|
||||
*/
|
||||
addEngine: function(name, spec){
|
||||
if(!name) return;
|
||||
var el = et.Element('engine');
|
||||
el.attrib.name = name;
|
||||
if(spec){
|
||||
el.attrib.spec = spec;
|
||||
}
|
||||
this.doc.getroot().append(el);
|
||||
},
|
||||
/**
|
||||
* Removes all the engines with given name
|
||||
* @param {String} name the engine name.
|
||||
*/
|
||||
removeEngine: function(name){
|
||||
var engines = this.doc.findall('./engine/[@name="' +name+'"]');
|
||||
for(var i=0; i < engines.length; i++){
|
||||
var children = this.doc.getroot().getchildren();
|
||||
var idx = children.indexOf(engines[i]);
|
||||
if(idx > -1){
|
||||
children.splice(idx,1);
|
||||
}
|
||||
}
|
||||
},
|
||||
getEngines: function(){
|
||||
var engines = this.doc.findall('./engine');
|
||||
return engines.map(function(engine){
|
||||
var spec = engine.attrib.spec || engine.attrib.version;
|
||||
return {
|
||||
'name': engine.attrib.name,
|
||||
'spec': spec ? spec : null
|
||||
};
|
||||
});
|
||||
},
|
||||
/* Get all the access tags */
|
||||
getAccesses: function() {
|
||||
var accesses = this.doc.findall('./access');
|
||||
return accesses.map(function(access){
|
||||
var minimum_tls_version = access.attrib['minimum-tls-version']; /* String */
|
||||
var requires_forward_secrecy = access.attrib['requires-forward-secrecy']; /* Boolean */
|
||||
return {
|
||||
'origin': access.attrib.origin,
|
||||
'minimum_tls_version': minimum_tls_version,
|
||||
'requires_forward_secrecy' : requires_forward_secrecy
|
||||
};
|
||||
});
|
||||
},
|
||||
/* Get all the allow-navigation tags */
|
||||
getAllowNavigations: function() {
|
||||
var allow_navigations = this.doc.findall('./allow-navigation');
|
||||
return allow_navigations.map(function(allow_navigation){
|
||||
var minimum_tls_version = allow_navigation.attrib['minimum-tls-version']; /* String */
|
||||
var requires_forward_secrecy = allow_navigation.attrib['requires-forward-secrecy']; /* Boolean */
|
||||
return {
|
||||
'href': allow_navigation.attrib.href,
|
||||
'minimum_tls_version': minimum_tls_version,
|
||||
'requires_forward_secrecy' : requires_forward_secrecy
|
||||
};
|
||||
});
|
||||
},
|
||||
write:function() {
|
||||
fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
|
||||
}
|
||||
};
|
||||
|
||||
function featureToPlugin(featureElement) {
|
||||
var plugin = {};
|
||||
plugin.variables = [];
|
||||
var pluginVersion,
|
||||
pluginSrc;
|
||||
|
||||
var nodes = featureElement.findall('param');
|
||||
nodes.forEach(function (element) {
|
||||
var n = element.attrib.name;
|
||||
var v = element.attrib.value;
|
||||
if (n === 'id') {
|
||||
plugin.name = v;
|
||||
} else if (n === 'version') {
|
||||
pluginVersion = v;
|
||||
} else if (n === 'url' || n === 'installPath') {
|
||||
pluginSrc = v;
|
||||
} else {
|
||||
plugin.variables[n] = v;
|
||||
}
|
||||
});
|
||||
|
||||
var spec = pluginSrc || pluginVersion;
|
||||
if (spec) {
|
||||
plugin.spec = spec;
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
module.exports = ConfigParser;
|
||||
|
||||
172
node_modules/cordova-common/src/ConfigParser/README.md
generated
vendored
172
node_modules/cordova-common/src/ConfigParser/README.md
generated
vendored
@@ -1,86 +1,86 @@
|
||||
<!--
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
-->
|
||||
|
||||
# Cordova-Lib
|
||||
|
||||
## ConfigParser
|
||||
|
||||
wraps a valid cordova config.xml file
|
||||
|
||||
### Usage
|
||||
|
||||
### Include the ConfigParser module in a projet
|
||||
|
||||
var ConfigParser = require('cordova-lib').configparser;
|
||||
|
||||
### Create a new ConfigParser
|
||||
|
||||
var config = new ConfigParser('path/to/config/xml/');
|
||||
|
||||
### Utility Functions
|
||||
|
||||
#### packageName(id)
|
||||
returns document root 'id' attribute value
|
||||
#### Usage
|
||||
|
||||
config.packageName: function(id)
|
||||
|
||||
/*
|
||||
* sets document root element 'id' attribute to @id
|
||||
*
|
||||
* @id - new id value
|
||||
*
|
||||
*/
|
||||
#### setPackageName(id)
|
||||
set document root 'id' attribute to
|
||||
function(id) {
|
||||
this.doc.getroot().attrib['id'] = id;
|
||||
},
|
||||
|
||||
###
|
||||
name: function() {
|
||||
return getNodeTextSafe(this.doc.find('name'));
|
||||
},
|
||||
setName: function(name) {
|
||||
var el = findOrCreate(this.doc, 'name');
|
||||
el.text = name;
|
||||
},
|
||||
|
||||
### read the description element
|
||||
|
||||
config.description()
|
||||
|
||||
var text = "New and improved description of App"
|
||||
setDescription(text)
|
||||
|
||||
### version management
|
||||
version()
|
||||
android_versionCode()
|
||||
ios_CFBundleVersion()
|
||||
setVersion()
|
||||
|
||||
### read author element
|
||||
|
||||
config.author();
|
||||
|
||||
### read preference
|
||||
|
||||
config.getPreference(name);
|
||||
<!--
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
-->
|
||||
|
||||
# Cordova-Lib
|
||||
|
||||
## ConfigParser
|
||||
|
||||
wraps a valid cordova config.xml file
|
||||
|
||||
### Usage
|
||||
|
||||
### Include the ConfigParser module in a projet
|
||||
|
||||
var ConfigParser = require('cordova-lib').configparser;
|
||||
|
||||
### Create a new ConfigParser
|
||||
|
||||
var config = new ConfigParser('path/to/config/xml/');
|
||||
|
||||
### Utility Functions
|
||||
|
||||
#### packageName(id)
|
||||
returns document root 'id' attribute value
|
||||
#### Usage
|
||||
|
||||
config.packageName: function(id)
|
||||
|
||||
/*
|
||||
* sets document root element 'id' attribute to @id
|
||||
*
|
||||
* @id - new id value
|
||||
*
|
||||
*/
|
||||
#### setPackageName(id)
|
||||
set document root 'id' attribute to
|
||||
function(id) {
|
||||
this.doc.getroot().attrib['id'] = id;
|
||||
},
|
||||
|
||||
###
|
||||
name: function() {
|
||||
return getNodeTextSafe(this.doc.find('name'));
|
||||
},
|
||||
setName: function(name) {
|
||||
var el = findOrCreate(this.doc, 'name');
|
||||
el.text = name;
|
||||
},
|
||||
|
||||
### read the description element
|
||||
|
||||
config.description()
|
||||
|
||||
var text = "New and improved description of App"
|
||||
setDescription(text)
|
||||
|
||||
### version management
|
||||
version()
|
||||
android_versionCode()
|
||||
ios_CFBundleVersion()
|
||||
setVersion()
|
||||
|
||||
### read author element
|
||||
|
||||
config.author();
|
||||
|
||||
### read preference
|
||||
|
||||
config.getPreference(name);
|
||||
|
||||
182
node_modules/cordova-common/src/CordovaError/CordovaError.js
generated
vendored
182
node_modules/cordova-common/src/CordovaError/CordovaError.js
generated
vendored
@@ -1,91 +1,91 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint proto:true */
|
||||
|
||||
var EOL = require('os').EOL;
|
||||
|
||||
/**
|
||||
* A derived exception class. See usage example in cli.js
|
||||
* Based on:
|
||||
* stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/8460753#8460753
|
||||
* @param {String} message Error message
|
||||
* @param {Number} [code=0] Error code
|
||||
* @param {CordovaExternalToolErrorContext} [context] External tool error context object
|
||||
* @constructor
|
||||
*/
|
||||
function CordovaError(message, code, context) {
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.code = code || CordovaError.UNKNOWN_ERROR;
|
||||
this.context = context;
|
||||
}
|
||||
CordovaError.prototype.__proto__ = Error.prototype;
|
||||
|
||||
// TODO: Extend error codes according the projects specifics
|
||||
CordovaError.UNKNOWN_ERROR = 0;
|
||||
CordovaError.EXTERNAL_TOOL_ERROR = 1;
|
||||
|
||||
/**
|
||||
* Translates instance's error code number into error code name, e.g. 0 -> UNKNOWN_ERROR
|
||||
* @returns {string} Error code string name
|
||||
*/
|
||||
CordovaError.prototype.getErrorCodeName = function() {
|
||||
for(var key in CordovaError) {
|
||||
if(CordovaError.hasOwnProperty(key)) {
|
||||
if(CordovaError[key] === this.code) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts CordovaError instance to string representation
|
||||
* @param {Boolean} [isVerbose] Set up verbose mode. Used to provide more
|
||||
* details including information about error code name and context
|
||||
* @return {String} Stringified error representation
|
||||
*/
|
||||
CordovaError.prototype.toString = function(isVerbose) {
|
||||
var message = '', codePrefix = '';
|
||||
|
||||
if(this.code !== CordovaError.UNKNOWN_ERROR) {
|
||||
codePrefix = 'code: ' + this.code + (isVerbose ? (' (' + this.getErrorCodeName() + ')') : '') + ' ';
|
||||
}
|
||||
|
||||
if(this.code === CordovaError.EXTERNAL_TOOL_ERROR) {
|
||||
if(typeof this.context !== 'undefined') {
|
||||
if(isVerbose) {
|
||||
message = codePrefix + EOL + this.context.toString(isVerbose) + '\n failed with an error: ' +
|
||||
this.message + EOL + 'Stack trace: ' + this.stack;
|
||||
} else {
|
||||
message = codePrefix + '\'' + this.context.toString(isVerbose) + '\' ' + this.message;
|
||||
}
|
||||
} else {
|
||||
message = 'External tool failed with an error: ' + this.message;
|
||||
}
|
||||
} else {
|
||||
message = isVerbose ? codePrefix + this.stack : codePrefix + this.message;
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
module.exports = CordovaError;
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint proto:true */
|
||||
|
||||
var EOL = require('os').EOL;
|
||||
|
||||
/**
|
||||
* A derived exception class. See usage example in cli.js
|
||||
* Based on:
|
||||
* stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/8460753#8460753
|
||||
* @param {String} message Error message
|
||||
* @param {Number} [code=0] Error code
|
||||
* @param {CordovaExternalToolErrorContext} [context] External tool error context object
|
||||
* @constructor
|
||||
*/
|
||||
function CordovaError(message, code, context) {
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.code = code || CordovaError.UNKNOWN_ERROR;
|
||||
this.context = context;
|
||||
}
|
||||
CordovaError.prototype.__proto__ = Error.prototype;
|
||||
|
||||
// TODO: Extend error codes according the projects specifics
|
||||
CordovaError.UNKNOWN_ERROR = 0;
|
||||
CordovaError.EXTERNAL_TOOL_ERROR = 1;
|
||||
|
||||
/**
|
||||
* Translates instance's error code number into error code name, e.g. 0 -> UNKNOWN_ERROR
|
||||
* @returns {string} Error code string name
|
||||
*/
|
||||
CordovaError.prototype.getErrorCodeName = function() {
|
||||
for(var key in CordovaError) {
|
||||
if(CordovaError.hasOwnProperty(key)) {
|
||||
if(CordovaError[key] === this.code) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts CordovaError instance to string representation
|
||||
* @param {Boolean} [isVerbose] Set up verbose mode. Used to provide more
|
||||
* details including information about error code name and context
|
||||
* @return {String} Stringified error representation
|
||||
*/
|
||||
CordovaError.prototype.toString = function(isVerbose) {
|
||||
var message = '', codePrefix = '';
|
||||
|
||||
if(this.code !== CordovaError.UNKNOWN_ERROR) {
|
||||
codePrefix = 'code: ' + this.code + (isVerbose ? (' (' + this.getErrorCodeName() + ')') : '') + ' ';
|
||||
}
|
||||
|
||||
if(this.code === CordovaError.EXTERNAL_TOOL_ERROR) {
|
||||
if(typeof this.context !== 'undefined') {
|
||||
if(isVerbose) {
|
||||
message = codePrefix + EOL + this.context.toString(isVerbose) + '\n failed with an error: ' +
|
||||
this.message + EOL + 'Stack trace: ' + this.stack;
|
||||
} else {
|
||||
message = codePrefix + '\'' + this.context.toString(isVerbose) + '\' ' + this.message;
|
||||
}
|
||||
} else {
|
||||
message = 'External tool failed with an error: ' + this.message;
|
||||
}
|
||||
} else {
|
||||
message = isVerbose ? codePrefix + this.stack : codePrefix + this.message;
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
module.exports = CordovaError;
|
||||
|
||||
96
node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js
generated
vendored
96
node_modules/cordova-common/src/CordovaError/CordovaExternalToolErrorContext.js
generated
vendored
@@ -1,48 +1,48 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint proto:true */
|
||||
|
||||
var path = require('path');
|
||||
|
||||
/**
|
||||
* @param {String} cmd Command full path
|
||||
* @param {String[]} args Command args
|
||||
* @param {String} [cwd] Command working directory
|
||||
* @constructor
|
||||
*/
|
||||
function CordovaExternalToolErrorContext(cmd, args, cwd) {
|
||||
this.cmd = cmd;
|
||||
// Helper field for readability
|
||||
this.cmdShortName = path.basename(cmd);
|
||||
this.args = args;
|
||||
this.cwd = cwd;
|
||||
}
|
||||
|
||||
CordovaExternalToolErrorContext.prototype.toString = function(isVerbose) {
|
||||
if(isVerbose) {
|
||||
return 'External tool \'' + this.cmdShortName + '\'' +
|
||||
'\nCommand full path: ' + this.cmd + '\nCommand args: ' + this.args +
|
||||
(typeof this.cwd !== 'undefined' ? '\nCommand cwd: ' + this.cwd : '');
|
||||
}
|
||||
|
||||
return this.cmdShortName;
|
||||
};
|
||||
|
||||
module.exports = CordovaExternalToolErrorContext;
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint proto:true */
|
||||
|
||||
var path = require('path');
|
||||
|
||||
/**
|
||||
* @param {String} cmd Command full path
|
||||
* @param {String[]} args Command args
|
||||
* @param {String} [cwd] Command working directory
|
||||
* @constructor
|
||||
*/
|
||||
function CordovaExternalToolErrorContext(cmd, args, cwd) {
|
||||
this.cmd = cmd;
|
||||
// Helper field for readability
|
||||
this.cmdShortName = path.basename(cmd);
|
||||
this.args = args;
|
||||
this.cwd = cwd;
|
||||
}
|
||||
|
||||
CordovaExternalToolErrorContext.prototype.toString = function(isVerbose) {
|
||||
if(isVerbose) {
|
||||
return 'External tool \'' + this.cmdShortName + '\'' +
|
||||
'\nCommand full path: ' + this.cmd + '\nCommand args: ' + this.args +
|
||||
(typeof this.cwd !== 'undefined' ? '\nCommand cwd: ' + this.cwd : '');
|
||||
}
|
||||
|
||||
return this.cmdShortName;
|
||||
};
|
||||
|
||||
module.exports = CordovaExternalToolErrorContext;
|
||||
|
||||
310
node_modules/cordova-common/src/PlatformJson.js
generated
vendored
310
node_modules/cordova-common/src/PlatformJson.js
generated
vendored
@@ -1,155 +1,155 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
/* jshint sub:true */
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var shelljs = require('shelljs');
|
||||
var mungeutil = require('./ConfigChanges/munge-util');
|
||||
var pluginMappernto = require('cordova-registry-mapper').newToOld;
|
||||
var pluginMapperotn = require('cordova-registry-mapper').oldToNew;
|
||||
|
||||
function PlatformJson(filePath, platform, root) {
|
||||
this.filePath = filePath;
|
||||
this.platform = platform;
|
||||
this.root = fix_munge(root || {});
|
||||
}
|
||||
|
||||
PlatformJson.load = function(plugins_dir, platform) {
|
||||
var filePath = path.join(plugins_dir, platform + '.json');
|
||||
var root = null;
|
||||
if (fs.existsSync(filePath)) {
|
||||
root = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||
}
|
||||
return new PlatformJson(filePath, platform, root);
|
||||
};
|
||||
|
||||
PlatformJson.prototype.save = function() {
|
||||
shelljs.mkdir('-p', path.dirname(this.filePath));
|
||||
fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 4), 'utf-8');
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether the specified plugin is installed as a top-level (not as
|
||||
* dependency to others)
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to check for.
|
||||
* @return {Boolean} true if plugin installed as top-level, otherwise false.
|
||||
*/
|
||||
PlatformJson.prototype.isPluginTopLevel = function(pluginId) {
|
||||
var installedPlugins = this.root.installed_plugins;
|
||||
return installedPlugins[pluginId] ||
|
||||
installedPlugins[pluginMappernto[pluginId]] ||
|
||||
installedPlugins[pluginMapperotn[pluginId]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether the specified plugin is installed as a dependency to other
|
||||
* plugin.
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to check for.
|
||||
* @return {Boolean} true if plugin installed as a dependency, otherwise false.
|
||||
*/
|
||||
PlatformJson.prototype.isPluginDependent = function(pluginId) {
|
||||
var dependentPlugins = this.root.dependent_plugins;
|
||||
return dependentPlugins[pluginId] ||
|
||||
dependentPlugins[pluginMappernto[pluginId]] ||
|
||||
dependentPlugins[pluginMapperotn[pluginId]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether plugin is installed either as top-level or as dependency.
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to check for.
|
||||
* @return {Boolean} true if plugin installed, otherwise false.
|
||||
*/
|
||||
PlatformJson.prototype.isPluginInstalled = function(pluginId) {
|
||||
return this.isPluginTopLevel(pluginId) ||
|
||||
this.isPluginDependent(pluginId);
|
||||
};
|
||||
|
||||
PlatformJson.prototype.addPlugin = function(pluginId, variables, isTopLevel) {
|
||||
var pluginsList = isTopLevel ?
|
||||
this.root.installed_plugins :
|
||||
this.root.dependent_plugins;
|
||||
|
||||
pluginsList[pluginId] = variables;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) {
|
||||
var pluginsList = isTopLevel ?
|
||||
this.root.installed_plugins :
|
||||
this.root.dependent_plugins;
|
||||
|
||||
delete pluginsList[pluginId];
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
PlatformJson.prototype.addInstalledPluginToPrepareQueue = function(pluginDirName, vars, is_top_level) {
|
||||
this.root.prepare_queue.installed.push({'plugin':pluginDirName, 'vars':vars, 'topLevel':is_top_level});
|
||||
};
|
||||
|
||||
PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function(pluginId, is_top_level) {
|
||||
this.root.prepare_queue.uninstalled.push({'plugin':pluginId, 'id':pluginId, 'topLevel':is_top_level});
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves plugin, specified by id to top-level plugins. If plugin is top-level
|
||||
* already, then does nothing.
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to make top-level.
|
||||
* @return {PlatformJson} PlatformJson instance.
|
||||
*/
|
||||
PlatformJson.prototype.makeTopLevel = function(pluginId) {
|
||||
var plugin = this.root.dependent_plugins[pluginId];
|
||||
if (plugin) {
|
||||
delete this.root.dependent_plugins[pluginId];
|
||||
this.root.installed_plugins[pluginId] = plugin;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// convert a munge from the old format ([file][parent][xml] = count) to the current one
|
||||
function fix_munge(root) {
|
||||
root.prepare_queue = root.prepare_queue || {installed:[], uninstalled:[]};
|
||||
root.config_munge = root.config_munge || {files: {}};
|
||||
root.installed_plugins = root.installed_plugins || {};
|
||||
root.dependent_plugins = root.dependent_plugins || {};
|
||||
|
||||
var munge = root.config_munge;
|
||||
if (!munge.files) {
|
||||
var new_munge = { files: {} };
|
||||
for (var file in munge) {
|
||||
for (var selector in munge[file]) {
|
||||
for (var xml_child in munge[file][selector]) {
|
||||
var val = parseInt(munge[file][selector][xml_child]);
|
||||
for (var i = 0; i < val; i++) {
|
||||
mungeutil.deep_add(new_munge, [file, selector, { xml: xml_child, count: val }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
root.config_munge = new_munge;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
module.exports = PlatformJson;
|
||||
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
/* jshint sub:true */
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var shelljs = require('shelljs');
|
||||
var mungeutil = require('./ConfigChanges/munge-util');
|
||||
var pluginMappernto = require('cordova-registry-mapper').newToOld;
|
||||
var pluginMapperotn = require('cordova-registry-mapper').oldToNew;
|
||||
|
||||
function PlatformJson(filePath, platform, root) {
|
||||
this.filePath = filePath;
|
||||
this.platform = platform;
|
||||
this.root = fix_munge(root || {});
|
||||
}
|
||||
|
||||
PlatformJson.load = function(plugins_dir, platform) {
|
||||
var filePath = path.join(plugins_dir, platform + '.json');
|
||||
var root = null;
|
||||
if (fs.existsSync(filePath)) {
|
||||
root = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||
}
|
||||
return new PlatformJson(filePath, platform, root);
|
||||
};
|
||||
|
||||
PlatformJson.prototype.save = function() {
|
||||
shelljs.mkdir('-p', path.dirname(this.filePath));
|
||||
fs.writeFileSync(this.filePath, JSON.stringify(this.root, null, 4), 'utf-8');
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether the specified plugin is installed as a top-level (not as
|
||||
* dependency to others)
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to check for.
|
||||
* @return {Boolean} true if plugin installed as top-level, otherwise false.
|
||||
*/
|
||||
PlatformJson.prototype.isPluginTopLevel = function(pluginId) {
|
||||
var installedPlugins = this.root.installed_plugins;
|
||||
return installedPlugins[pluginId] ||
|
||||
installedPlugins[pluginMappernto[pluginId]] ||
|
||||
installedPlugins[pluginMapperotn[pluginId]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether the specified plugin is installed as a dependency to other
|
||||
* plugin.
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to check for.
|
||||
* @return {Boolean} true if plugin installed as a dependency, otherwise false.
|
||||
*/
|
||||
PlatformJson.prototype.isPluginDependent = function(pluginId) {
|
||||
var dependentPlugins = this.root.dependent_plugins;
|
||||
return dependentPlugins[pluginId] ||
|
||||
dependentPlugins[pluginMappernto[pluginId]] ||
|
||||
dependentPlugins[pluginMapperotn[pluginId]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether plugin is installed either as top-level or as dependency.
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to check for.
|
||||
* @return {Boolean} true if plugin installed, otherwise false.
|
||||
*/
|
||||
PlatformJson.prototype.isPluginInstalled = function(pluginId) {
|
||||
return this.isPluginTopLevel(pluginId) ||
|
||||
this.isPluginDependent(pluginId);
|
||||
};
|
||||
|
||||
PlatformJson.prototype.addPlugin = function(pluginId, variables, isTopLevel) {
|
||||
var pluginsList = isTopLevel ?
|
||||
this.root.installed_plugins :
|
||||
this.root.dependent_plugins;
|
||||
|
||||
pluginsList[pluginId] = variables;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
PlatformJson.prototype.removePlugin = function(pluginId, isTopLevel) {
|
||||
var pluginsList = isTopLevel ?
|
||||
this.root.installed_plugins :
|
||||
this.root.dependent_plugins;
|
||||
|
||||
delete pluginsList[pluginId];
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
PlatformJson.prototype.addInstalledPluginToPrepareQueue = function(pluginDirName, vars, is_top_level) {
|
||||
this.root.prepare_queue.installed.push({'plugin':pluginDirName, 'vars':vars, 'topLevel':is_top_level});
|
||||
};
|
||||
|
||||
PlatformJson.prototype.addUninstalledPluginToPrepareQueue = function(pluginId, is_top_level) {
|
||||
this.root.prepare_queue.uninstalled.push({'plugin':pluginId, 'id':pluginId, 'topLevel':is_top_level});
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves plugin, specified by id to top-level plugins. If plugin is top-level
|
||||
* already, then does nothing.
|
||||
* @method function
|
||||
* @param {String} pluginId A plugin id to make top-level.
|
||||
* @return {PlatformJson} PlatformJson instance.
|
||||
*/
|
||||
PlatformJson.prototype.makeTopLevel = function(pluginId) {
|
||||
var plugin = this.root.dependent_plugins[pluginId];
|
||||
if (plugin) {
|
||||
delete this.root.dependent_plugins[pluginId];
|
||||
this.root.installed_plugins[pluginId] = plugin;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// convert a munge from the old format ([file][parent][xml] = count) to the current one
|
||||
function fix_munge(root) {
|
||||
root.prepare_queue = root.prepare_queue || {installed:[], uninstalled:[]};
|
||||
root.config_munge = root.config_munge || {files: {}};
|
||||
root.installed_plugins = root.installed_plugins || {};
|
||||
root.dependent_plugins = root.dependent_plugins || {};
|
||||
|
||||
var munge = root.config_munge;
|
||||
if (!munge.files) {
|
||||
var new_munge = { files: {} };
|
||||
for (var file in munge) {
|
||||
for (var selector in munge[file]) {
|
||||
for (var xml_child in munge[file][selector]) {
|
||||
var val = parseInt(munge[file][selector][xml_child]);
|
||||
for (var i = 0; i < val; i++) {
|
||||
mungeutil.deep_add(new_munge, [file, selector, { xml: xml_child, count: val }]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
root.config_munge = new_munge;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
module.exports = PlatformJson;
|
||||
|
||||
|
||||
832
node_modules/cordova-common/src/PluginInfo/PluginInfo.js
generated
vendored
832
node_modules/cordova-common/src/PluginInfo/PluginInfo.js
generated
vendored
@@ -1,416 +1,416 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true, laxcomma:true, laxbreak:true */
|
||||
|
||||
/*
|
||||
A class for holidng the information currently stored in plugin.xml
|
||||
It should also be able to answer questions like whether the plugin
|
||||
is compatible with a given engine version.
|
||||
|
||||
TODO (kamrik): refactor this to not use sync functions and return promises.
|
||||
*/
|
||||
|
||||
|
||||
var path = require('path')
|
||||
, fs = require('fs')
|
||||
, xml_helpers = require('../util/xml-helpers')
|
||||
, CordovaError = require('../CordovaError/CordovaError')
|
||||
;
|
||||
|
||||
function PluginInfo(dirname) {
|
||||
var self = this;
|
||||
|
||||
// METHODS
|
||||
// Defined inside the constructor to avoid the "this" binding problems.
|
||||
|
||||
// <preference> tag
|
||||
// Example: <preference name="API_KEY" />
|
||||
// Used to require a variable to be specified via --variable when installing the plugin.
|
||||
self.getPreferences = getPreferences;
|
||||
function getPreferences(platform) {
|
||||
var arprefs = _getTags(self._et, 'preference', platform, _parsePreference);
|
||||
|
||||
var prefs= {};
|
||||
for(var i in arprefs)
|
||||
{
|
||||
var pref=arprefs[i];
|
||||
prefs[pref.preference]=pref.default;
|
||||
}
|
||||
// returns { key : default | null}
|
||||
return prefs;
|
||||
}
|
||||
|
||||
function _parsePreference(prefTag) {
|
||||
var name = prefTag.attrib.name.toUpperCase();
|
||||
var def = prefTag.attrib.default || null;
|
||||
return {preference: name, default: def};
|
||||
}
|
||||
|
||||
// <asset>
|
||||
self.getAssets = getAssets;
|
||||
function getAssets(platform) {
|
||||
var assets = _getTags(self._et, 'asset', platform, _parseAsset);
|
||||
return assets;
|
||||
}
|
||||
|
||||
function _parseAsset(tag) {
|
||||
var src = tag.attrib.src;
|
||||
var target = tag.attrib.target;
|
||||
|
||||
if ( !src || !target) {
|
||||
var msg =
|
||||
'Malformed <asset> tag. Both "src" and "target" attributes'
|
||||
+ 'must be specified in\n'
|
||||
+ self.filepath
|
||||
;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
var asset = {
|
||||
itemType: 'asset',
|
||||
src: src,
|
||||
target: target
|
||||
};
|
||||
return asset;
|
||||
}
|
||||
|
||||
|
||||
// <dependency>
|
||||
// Example:
|
||||
// <dependency id="com.plugin.id"
|
||||
// url="https://github.com/myuser/someplugin"
|
||||
// commit="428931ada3891801"
|
||||
// subdir="some/path/here" />
|
||||
self.getDependencies = getDependencies;
|
||||
function getDependencies(platform) {
|
||||
var deps = _getTags(
|
||||
self._et,
|
||||
'dependency',
|
||||
platform,
|
||||
_parseDependency
|
||||
);
|
||||
return deps;
|
||||
}
|
||||
|
||||
function _parseDependency(tag) {
|
||||
var dep =
|
||||
{ id : tag.attrib.id
|
||||
, url : tag.attrib.url || ''
|
||||
, subdir : tag.attrib.subdir || ''
|
||||
, commit : tag.attrib.commit
|
||||
};
|
||||
|
||||
dep.git_ref = dep.commit;
|
||||
|
||||
if ( !dep.id ) {
|
||||
var msg =
|
||||
'<dependency> tag is missing id attribute in '
|
||||
+ self.filepath
|
||||
;
|
||||
throw new CordovaError(msg);
|
||||
}
|
||||
return dep;
|
||||
}
|
||||
|
||||
|
||||
// <config-file> tag
|
||||
self.getConfigFiles = getConfigFiles;
|
||||
function getConfigFiles(platform) {
|
||||
var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile);
|
||||
return configFiles;
|
||||
}
|
||||
|
||||
function _parseConfigFile(tag) {
|
||||
var configFile =
|
||||
{ target : tag.attrib['target']
|
||||
, parent : tag.attrib['parent']
|
||||
, after : tag.attrib['after']
|
||||
, xmls : tag.getchildren()
|
||||
// To support demuxing via versions
|
||||
, versions : tag.attrib['versions']
|
||||
, deviceTarget: tag.attrib['device-target']
|
||||
};
|
||||
return configFile;
|
||||
}
|
||||
|
||||
// <info> tags, both global and within a <platform>
|
||||
// TODO (kamrik): Do we ever use <info> under <platform>? Example wanted.
|
||||
self.getInfo = getInfo;
|
||||
function getInfo(platform) {
|
||||
var infos = _getTags(
|
||||
self._et,
|
||||
'info',
|
||||
platform,
|
||||
function(elem) { return elem.text; }
|
||||
);
|
||||
// Filter out any undefined or empty strings.
|
||||
infos = infos.filter(Boolean);
|
||||
return infos;
|
||||
}
|
||||
|
||||
// <source-file>
|
||||
// Examples:
|
||||
// <source-file src="src/ios/someLib.a" framework="true" />
|
||||
// <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
|
||||
self.getSourceFiles = getSourceFiles;
|
||||
function getSourceFiles(platform) {
|
||||
var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile);
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
function _parseSourceFile(tag) {
|
||||
return {
|
||||
itemType: 'source-file',
|
||||
src: tag.attrib.src,
|
||||
framework: isStrTrue(tag.attrib.framework),
|
||||
weak: isStrTrue(tag.attrib.weak),
|
||||
compilerFlags: tag.attrib['compiler-flags'],
|
||||
targetDir: tag.attrib['target-dir']
|
||||
};
|
||||
}
|
||||
|
||||
// <header-file>
|
||||
// Example:
|
||||
// <header-file src="CDVFoo.h" />
|
||||
self.getHeaderFiles = getHeaderFiles;
|
||||
function getHeaderFiles(platform) {
|
||||
var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function(tag) {
|
||||
return {
|
||||
itemType: 'header-file',
|
||||
src: tag.attrib.src,
|
||||
targetDir: tag.attrib['target-dir']
|
||||
};
|
||||
});
|
||||
return headerFiles;
|
||||
}
|
||||
|
||||
// <resource-file>
|
||||
// Example:
|
||||
// <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" device-target="win" arch="x86" versions=">=8.1" />
|
||||
self.getResourceFiles = getResourceFiles;
|
||||
function getResourceFiles(platform) {
|
||||
var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function(tag) {
|
||||
return {
|
||||
itemType: 'resource-file',
|
||||
src: tag.attrib.src,
|
||||
target: tag.attrib.target,
|
||||
versions: tag.attrib.versions,
|
||||
deviceTarget: tag.attrib['device-target'],
|
||||
arch: tag.attrib.arch
|
||||
};
|
||||
});
|
||||
return resourceFiles;
|
||||
}
|
||||
|
||||
// <lib-file>
|
||||
// Example:
|
||||
// <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
|
||||
self.getLibFiles = getLibFiles;
|
||||
function getLibFiles(platform) {
|
||||
var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function(tag) {
|
||||
return {
|
||||
itemType: 'lib-file',
|
||||
src: tag.attrib.src,
|
||||
arch: tag.attrib.arch,
|
||||
Include: tag.attrib.Include,
|
||||
versions: tag.attrib.versions,
|
||||
deviceTarget: tag.attrib['device-target'] || tag.attrib.target
|
||||
};
|
||||
});
|
||||
return libFiles;
|
||||
}
|
||||
|
||||
// <hook>
|
||||
// Example:
|
||||
// <hook type="before_build" src="scripts/beforeBuild.js" />
|
||||
self.getHookScripts = getHookScripts;
|
||||
function getHookScripts(hook, platforms) {
|
||||
var scriptElements = self._et.findall('./hook');
|
||||
|
||||
if(platforms) {
|
||||
platforms.forEach(function (platform) {
|
||||
scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook'));
|
||||
});
|
||||
}
|
||||
|
||||
function filterScriptByHookType(el) {
|
||||
return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
|
||||
}
|
||||
|
||||
return scriptElements.filter(filterScriptByHookType);
|
||||
}
|
||||
|
||||
self.getJsModules = getJsModules;
|
||||
function getJsModules(platform) {
|
||||
var modules = _getTags(self._et, 'js-module', platform, _parseJsModule);
|
||||
return modules;
|
||||
}
|
||||
|
||||
function _parseJsModule(tag) {
|
||||
var ret = {
|
||||
itemType: 'js-module',
|
||||
name: tag.attrib.name,
|
||||
src: tag.attrib.src,
|
||||
clobbers: tag.findall('clobbers').map(function(tag) { return { target: tag.attrib.target }; }),
|
||||
merges: tag.findall('merges').map(function(tag) { return { target: tag.attrib.target }; }),
|
||||
runs: tag.findall('runs').length > 0
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
self.getEngines = function() {
|
||||
return self._et.findall('engines/engine').map(function(n) {
|
||||
return {
|
||||
name: n.attrib.name,
|
||||
version: n.attrib.version,
|
||||
platform: n.attrib.platform,
|
||||
scriptSrc: n.attrib.scriptSrc
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
self.getPlatforms = function() {
|
||||
return self._et.findall('platform').map(function(n) {
|
||||
return { name: n.attrib.name };
|
||||
});
|
||||
};
|
||||
|
||||
self.getPlatformsArray = function() {
|
||||
return self._et.findall('platform').map(function(n) {
|
||||
return n.attrib.name;
|
||||
});
|
||||
};
|
||||
self.getFrameworks = function(platform) {
|
||||
return _getTags(self._et, 'framework', platform, function(el) {
|
||||
var ret = {
|
||||
itemType: 'framework',
|
||||
type: el.attrib.type,
|
||||
parent: el.attrib.parent,
|
||||
custom: isStrTrue(el.attrib.custom),
|
||||
src: el.attrib.src,
|
||||
weak: isStrTrue(el.attrib.weak),
|
||||
versions: el.attrib.versions,
|
||||
targetDir: el.attrib['target-dir'],
|
||||
deviceTarget: el.attrib['device-target'] || el.attrib.target,
|
||||
arch: el.attrib.arch
|
||||
};
|
||||
return ret;
|
||||
});
|
||||
};
|
||||
|
||||
self.getFilesAndFrameworks = getFilesAndFrameworks;
|
||||
function getFilesAndFrameworks(platform) {
|
||||
// Please avoid changing the order of the calls below, files will be
|
||||
// installed in this order.
|
||||
var items = [].concat(
|
||||
self.getSourceFiles(platform),
|
||||
self.getHeaderFiles(platform),
|
||||
self.getResourceFiles(platform),
|
||||
self.getFrameworks(platform),
|
||||
self.getLibFiles(platform)
|
||||
);
|
||||
return items;
|
||||
}
|
||||
///// End of PluginInfo methods /////
|
||||
|
||||
|
||||
///// PluginInfo Constructor logic /////
|
||||
self.filepath = path.join(dirname, 'plugin.xml');
|
||||
if (!fs.existsSync(self.filepath)) {
|
||||
throw new CordovaError('Cannot find plugin.xml for plugin \'' + path.basename(dirname) + '\'. Please try adding it again.');
|
||||
}
|
||||
|
||||
self.dir = dirname;
|
||||
var et = self._et = xml_helpers.parseElementtreeSync(self.filepath);
|
||||
var pelem = et.getroot();
|
||||
self.id = pelem.attrib.id;
|
||||
self.version = pelem.attrib.version;
|
||||
|
||||
// Optional fields
|
||||
self.name = pelem.findtext('name');
|
||||
self.description = pelem.findtext('description');
|
||||
self.license = pelem.findtext('license');
|
||||
self.repo = pelem.findtext('repo');
|
||||
self.issue = pelem.findtext('issue');
|
||||
self.keywords = pelem.findtext('keywords');
|
||||
self.info = pelem.findtext('info');
|
||||
if (self.keywords) {
|
||||
self.keywords = self.keywords.split(',').map( function(s) { return s.trim(); } );
|
||||
}
|
||||
self.getKeywordsAndPlatforms = function () {
|
||||
var ret = self.keywords || [];
|
||||
return ret.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray()));
|
||||
};
|
||||
} // End of PluginInfo constructor.
|
||||
|
||||
// Helper function used to prefix every element of an array with cordova-
|
||||
// Useful when we want to modify platforms to be cordova-platform
|
||||
function addCordova(someArray) {
|
||||
var newArray = someArray.map(function(element) {
|
||||
return 'cordova-' + element;
|
||||
});
|
||||
return newArray;
|
||||
}
|
||||
|
||||
// Helper function used by most of the getSomething methods of PluginInfo.
|
||||
// Get all elements of a given name. Both in root and in platform sections
|
||||
// for the given platform. If transform is given and is a function, it is
|
||||
// applied to each element.
|
||||
function _getTags(pelem, tag, platform, transform) {
|
||||
var platformTag = pelem.find('./platform[@name="' + platform + '"]');
|
||||
if (platform == 'windows' && !platformTag) {
|
||||
platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
|
||||
}
|
||||
var tagsInRoot = pelem.findall(tag);
|
||||
tagsInRoot = tagsInRoot || [];
|
||||
var tagsInPlatform = platformTag ? platformTag.findall(tag) : [];
|
||||
var tags = tagsInRoot.concat(tagsInPlatform);
|
||||
if ( typeof transform === 'function' ) {
|
||||
tags = tags.map(transform);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
// Same as _getTags() but only looks inside a platfrom section.
|
||||
function _getTagsInPlatform(pelem, tag, platform, transform) {
|
||||
var platformTag = pelem.find('./platform[@name="' + platform + '"]');
|
||||
if (platform == 'windows' && !platformTag) {
|
||||
platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
|
||||
}
|
||||
var tags = platformTag ? platformTag.findall(tag) : [];
|
||||
if ( typeof transform === 'function' ) {
|
||||
tags = tags.map(transform);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
// Check if x is a string 'true'.
|
||||
function isStrTrue(x) {
|
||||
return String(x).toLowerCase() == 'true';
|
||||
}
|
||||
|
||||
module.exports = PluginInfo;
|
||||
// Backwards compat:
|
||||
PluginInfo.PluginInfo = PluginInfo;
|
||||
PluginInfo.loadPluginsDir = function(dir) {
|
||||
var PluginInfoProvider = require('./PluginInfoProvider');
|
||||
return new PluginInfoProvider().getAllWithinSearchPath(dir);
|
||||
};
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true, laxcomma:true, laxbreak:true */
|
||||
|
||||
/*
|
||||
A class for holidng the information currently stored in plugin.xml
|
||||
It should also be able to answer questions like whether the plugin
|
||||
is compatible with a given engine version.
|
||||
|
||||
TODO (kamrik): refactor this to not use sync functions and return promises.
|
||||
*/
|
||||
|
||||
|
||||
var path = require('path')
|
||||
, fs = require('fs')
|
||||
, xml_helpers = require('../util/xml-helpers')
|
||||
, CordovaError = require('../CordovaError/CordovaError')
|
||||
;
|
||||
|
||||
function PluginInfo(dirname) {
|
||||
var self = this;
|
||||
|
||||
// METHODS
|
||||
// Defined inside the constructor to avoid the "this" binding problems.
|
||||
|
||||
// <preference> tag
|
||||
// Example: <preference name="API_KEY" />
|
||||
// Used to require a variable to be specified via --variable when installing the plugin.
|
||||
self.getPreferences = getPreferences;
|
||||
function getPreferences(platform) {
|
||||
var arprefs = _getTags(self._et, 'preference', platform, _parsePreference);
|
||||
|
||||
var prefs= {};
|
||||
for(var i in arprefs)
|
||||
{
|
||||
var pref=arprefs[i];
|
||||
prefs[pref.preference]=pref.default;
|
||||
}
|
||||
// returns { key : default | null}
|
||||
return prefs;
|
||||
}
|
||||
|
||||
function _parsePreference(prefTag) {
|
||||
var name = prefTag.attrib.name.toUpperCase();
|
||||
var def = prefTag.attrib.default || null;
|
||||
return {preference: name, default: def};
|
||||
}
|
||||
|
||||
// <asset>
|
||||
self.getAssets = getAssets;
|
||||
function getAssets(platform) {
|
||||
var assets = _getTags(self._et, 'asset', platform, _parseAsset);
|
||||
return assets;
|
||||
}
|
||||
|
||||
function _parseAsset(tag) {
|
||||
var src = tag.attrib.src;
|
||||
var target = tag.attrib.target;
|
||||
|
||||
if ( !src || !target) {
|
||||
var msg =
|
||||
'Malformed <asset> tag. Both "src" and "target" attributes'
|
||||
+ 'must be specified in\n'
|
||||
+ self.filepath
|
||||
;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
var asset = {
|
||||
itemType: 'asset',
|
||||
src: src,
|
||||
target: target
|
||||
};
|
||||
return asset;
|
||||
}
|
||||
|
||||
|
||||
// <dependency>
|
||||
// Example:
|
||||
// <dependency id="com.plugin.id"
|
||||
// url="https://github.com/myuser/someplugin"
|
||||
// commit="428931ada3891801"
|
||||
// subdir="some/path/here" />
|
||||
self.getDependencies = getDependencies;
|
||||
function getDependencies(platform) {
|
||||
var deps = _getTags(
|
||||
self._et,
|
||||
'dependency',
|
||||
platform,
|
||||
_parseDependency
|
||||
);
|
||||
return deps;
|
||||
}
|
||||
|
||||
function _parseDependency(tag) {
|
||||
var dep =
|
||||
{ id : tag.attrib.id
|
||||
, url : tag.attrib.url || ''
|
||||
, subdir : tag.attrib.subdir || ''
|
||||
, commit : tag.attrib.commit
|
||||
};
|
||||
|
||||
dep.git_ref = dep.commit;
|
||||
|
||||
if ( !dep.id ) {
|
||||
var msg =
|
||||
'<dependency> tag is missing id attribute in '
|
||||
+ self.filepath
|
||||
;
|
||||
throw new CordovaError(msg);
|
||||
}
|
||||
return dep;
|
||||
}
|
||||
|
||||
|
||||
// <config-file> tag
|
||||
self.getConfigFiles = getConfigFiles;
|
||||
function getConfigFiles(platform) {
|
||||
var configFiles = _getTags(self._et, 'config-file', platform, _parseConfigFile);
|
||||
return configFiles;
|
||||
}
|
||||
|
||||
function _parseConfigFile(tag) {
|
||||
var configFile =
|
||||
{ target : tag.attrib['target']
|
||||
, parent : tag.attrib['parent']
|
||||
, after : tag.attrib['after']
|
||||
, xmls : tag.getchildren()
|
||||
// To support demuxing via versions
|
||||
, versions : tag.attrib['versions']
|
||||
, deviceTarget: tag.attrib['device-target']
|
||||
};
|
||||
return configFile;
|
||||
}
|
||||
|
||||
// <info> tags, both global and within a <platform>
|
||||
// TODO (kamrik): Do we ever use <info> under <platform>? Example wanted.
|
||||
self.getInfo = getInfo;
|
||||
function getInfo(platform) {
|
||||
var infos = _getTags(
|
||||
self._et,
|
||||
'info',
|
||||
platform,
|
||||
function(elem) { return elem.text; }
|
||||
);
|
||||
// Filter out any undefined or empty strings.
|
||||
infos = infos.filter(Boolean);
|
||||
return infos;
|
||||
}
|
||||
|
||||
// <source-file>
|
||||
// Examples:
|
||||
// <source-file src="src/ios/someLib.a" framework="true" />
|
||||
// <source-file src="src/ios/someLib.a" compiler-flags="-fno-objc-arc" />
|
||||
self.getSourceFiles = getSourceFiles;
|
||||
function getSourceFiles(platform) {
|
||||
var sourceFiles = _getTagsInPlatform(self._et, 'source-file', platform, _parseSourceFile);
|
||||
return sourceFiles;
|
||||
}
|
||||
|
||||
function _parseSourceFile(tag) {
|
||||
return {
|
||||
itemType: 'source-file',
|
||||
src: tag.attrib.src,
|
||||
framework: isStrTrue(tag.attrib.framework),
|
||||
weak: isStrTrue(tag.attrib.weak),
|
||||
compilerFlags: tag.attrib['compiler-flags'],
|
||||
targetDir: tag.attrib['target-dir']
|
||||
};
|
||||
}
|
||||
|
||||
// <header-file>
|
||||
// Example:
|
||||
// <header-file src="CDVFoo.h" />
|
||||
self.getHeaderFiles = getHeaderFiles;
|
||||
function getHeaderFiles(platform) {
|
||||
var headerFiles = _getTagsInPlatform(self._et, 'header-file', platform, function(tag) {
|
||||
return {
|
||||
itemType: 'header-file',
|
||||
src: tag.attrib.src,
|
||||
targetDir: tag.attrib['target-dir']
|
||||
};
|
||||
});
|
||||
return headerFiles;
|
||||
}
|
||||
|
||||
// <resource-file>
|
||||
// Example:
|
||||
// <resource-file src="FooPluginStrings.xml" target="res/values/FooPluginStrings.xml" device-target="win" arch="x86" versions=">=8.1" />
|
||||
self.getResourceFiles = getResourceFiles;
|
||||
function getResourceFiles(platform) {
|
||||
var resourceFiles = _getTagsInPlatform(self._et, 'resource-file', platform, function(tag) {
|
||||
return {
|
||||
itemType: 'resource-file',
|
||||
src: tag.attrib.src,
|
||||
target: tag.attrib.target,
|
||||
versions: tag.attrib.versions,
|
||||
deviceTarget: tag.attrib['device-target'],
|
||||
arch: tag.attrib.arch
|
||||
};
|
||||
});
|
||||
return resourceFiles;
|
||||
}
|
||||
|
||||
// <lib-file>
|
||||
// Example:
|
||||
// <lib-file src="src/BlackBerry10/native/device/libfoo.so" arch="device" />
|
||||
self.getLibFiles = getLibFiles;
|
||||
function getLibFiles(platform) {
|
||||
var libFiles = _getTagsInPlatform(self._et, 'lib-file', platform, function(tag) {
|
||||
return {
|
||||
itemType: 'lib-file',
|
||||
src: tag.attrib.src,
|
||||
arch: tag.attrib.arch,
|
||||
Include: tag.attrib.Include,
|
||||
versions: tag.attrib.versions,
|
||||
deviceTarget: tag.attrib['device-target'] || tag.attrib.target
|
||||
};
|
||||
});
|
||||
return libFiles;
|
||||
}
|
||||
|
||||
// <hook>
|
||||
// Example:
|
||||
// <hook type="before_build" src="scripts/beforeBuild.js" />
|
||||
self.getHookScripts = getHookScripts;
|
||||
function getHookScripts(hook, platforms) {
|
||||
var scriptElements = self._et.findall('./hook');
|
||||
|
||||
if(platforms) {
|
||||
platforms.forEach(function (platform) {
|
||||
scriptElements = scriptElements.concat(self._et.findall('./platform[@name="' + platform + '"]/hook'));
|
||||
});
|
||||
}
|
||||
|
||||
function filterScriptByHookType(el) {
|
||||
return el.attrib.src && el.attrib.type && el.attrib.type.toLowerCase() === hook;
|
||||
}
|
||||
|
||||
return scriptElements.filter(filterScriptByHookType);
|
||||
}
|
||||
|
||||
self.getJsModules = getJsModules;
|
||||
function getJsModules(platform) {
|
||||
var modules = _getTags(self._et, 'js-module', platform, _parseJsModule);
|
||||
return modules;
|
||||
}
|
||||
|
||||
function _parseJsModule(tag) {
|
||||
var ret = {
|
||||
itemType: 'js-module',
|
||||
name: tag.attrib.name,
|
||||
src: tag.attrib.src,
|
||||
clobbers: tag.findall('clobbers').map(function(tag) { return { target: tag.attrib.target }; }),
|
||||
merges: tag.findall('merges').map(function(tag) { return { target: tag.attrib.target }; }),
|
||||
runs: tag.findall('runs').length > 0
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
self.getEngines = function() {
|
||||
return self._et.findall('engines/engine').map(function(n) {
|
||||
return {
|
||||
name: n.attrib.name,
|
||||
version: n.attrib.version,
|
||||
platform: n.attrib.platform,
|
||||
scriptSrc: n.attrib.scriptSrc
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
self.getPlatforms = function() {
|
||||
return self._et.findall('platform').map(function(n) {
|
||||
return { name: n.attrib.name };
|
||||
});
|
||||
};
|
||||
|
||||
self.getPlatformsArray = function() {
|
||||
return self._et.findall('platform').map(function(n) {
|
||||
return n.attrib.name;
|
||||
});
|
||||
};
|
||||
self.getFrameworks = function(platform) {
|
||||
return _getTags(self._et, 'framework', platform, function(el) {
|
||||
var ret = {
|
||||
itemType: 'framework',
|
||||
type: el.attrib.type,
|
||||
parent: el.attrib.parent,
|
||||
custom: isStrTrue(el.attrib.custom),
|
||||
src: el.attrib.src,
|
||||
weak: isStrTrue(el.attrib.weak),
|
||||
versions: el.attrib.versions,
|
||||
targetDir: el.attrib['target-dir'],
|
||||
deviceTarget: el.attrib['device-target'] || el.attrib.target,
|
||||
arch: el.attrib.arch
|
||||
};
|
||||
return ret;
|
||||
});
|
||||
};
|
||||
|
||||
self.getFilesAndFrameworks = getFilesAndFrameworks;
|
||||
function getFilesAndFrameworks(platform) {
|
||||
// Please avoid changing the order of the calls below, files will be
|
||||
// installed in this order.
|
||||
var items = [].concat(
|
||||
self.getSourceFiles(platform),
|
||||
self.getHeaderFiles(platform),
|
||||
self.getResourceFiles(platform),
|
||||
self.getFrameworks(platform),
|
||||
self.getLibFiles(platform)
|
||||
);
|
||||
return items;
|
||||
}
|
||||
///// End of PluginInfo methods /////
|
||||
|
||||
|
||||
///// PluginInfo Constructor logic /////
|
||||
self.filepath = path.join(dirname, 'plugin.xml');
|
||||
if (!fs.existsSync(self.filepath)) {
|
||||
throw new CordovaError('Cannot find plugin.xml for plugin \'' + path.basename(dirname) + '\'. Please try adding it again.');
|
||||
}
|
||||
|
||||
self.dir = dirname;
|
||||
var et = self._et = xml_helpers.parseElementtreeSync(self.filepath);
|
||||
var pelem = et.getroot();
|
||||
self.id = pelem.attrib.id;
|
||||
self.version = pelem.attrib.version;
|
||||
|
||||
// Optional fields
|
||||
self.name = pelem.findtext('name');
|
||||
self.description = pelem.findtext('description');
|
||||
self.license = pelem.findtext('license');
|
||||
self.repo = pelem.findtext('repo');
|
||||
self.issue = pelem.findtext('issue');
|
||||
self.keywords = pelem.findtext('keywords');
|
||||
self.info = pelem.findtext('info');
|
||||
if (self.keywords) {
|
||||
self.keywords = self.keywords.split(',').map( function(s) { return s.trim(); } );
|
||||
}
|
||||
self.getKeywordsAndPlatforms = function () {
|
||||
var ret = self.keywords || [];
|
||||
return ret.concat('ecosystem:cordova').concat(addCordova(self.getPlatformsArray()));
|
||||
};
|
||||
} // End of PluginInfo constructor.
|
||||
|
||||
// Helper function used to prefix every element of an array with cordova-
|
||||
// Useful when we want to modify platforms to be cordova-platform
|
||||
function addCordova(someArray) {
|
||||
var newArray = someArray.map(function(element) {
|
||||
return 'cordova-' + element;
|
||||
});
|
||||
return newArray;
|
||||
}
|
||||
|
||||
// Helper function used by most of the getSomething methods of PluginInfo.
|
||||
// Get all elements of a given name. Both in root and in platform sections
|
||||
// for the given platform. If transform is given and is a function, it is
|
||||
// applied to each element.
|
||||
function _getTags(pelem, tag, platform, transform) {
|
||||
var platformTag = pelem.find('./platform[@name="' + platform + '"]');
|
||||
if (platform == 'windows' && !platformTag) {
|
||||
platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
|
||||
}
|
||||
var tagsInRoot = pelem.findall(tag);
|
||||
tagsInRoot = tagsInRoot || [];
|
||||
var tagsInPlatform = platformTag ? platformTag.findall(tag) : [];
|
||||
var tags = tagsInRoot.concat(tagsInPlatform);
|
||||
if ( typeof transform === 'function' ) {
|
||||
tags = tags.map(transform);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
// Same as _getTags() but only looks inside a platfrom section.
|
||||
function _getTagsInPlatform(pelem, tag, platform, transform) {
|
||||
var platformTag = pelem.find('./platform[@name="' + platform + '"]');
|
||||
if (platform == 'windows' && !platformTag) {
|
||||
platformTag = pelem.find('platform[@name="' + 'windows8' + '"]');
|
||||
}
|
||||
var tags = platformTag ? platformTag.findall(tag) : [];
|
||||
if ( typeof transform === 'function' ) {
|
||||
tags = tags.map(transform);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
// Check if x is a string 'true'.
|
||||
function isStrTrue(x) {
|
||||
return String(x).toLowerCase() == 'true';
|
||||
}
|
||||
|
||||
module.exports = PluginInfo;
|
||||
// Backwards compat:
|
||||
PluginInfo.PluginInfo = PluginInfo;
|
||||
PluginInfo.loadPluginsDir = function(dir) {
|
||||
var PluginInfoProvider = require('./PluginInfoProvider');
|
||||
return new PluginInfoProvider().getAllWithinSearchPath(dir);
|
||||
};
|
||||
|
||||
164
node_modules/cordova-common/src/PluginInfo/PluginInfoProvider.js
generated
vendored
164
node_modules/cordova-common/src/PluginInfo/PluginInfoProvider.js
generated
vendored
@@ -1,82 +1,82 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true, laxcomma:true, laxbreak:true */
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var PluginInfo = require('./PluginInfo');
|
||||
var events = require('../events');
|
||||
|
||||
function PluginInfoProvider() {
|
||||
this._cache = {};
|
||||
this._getAllCache = {};
|
||||
}
|
||||
|
||||
PluginInfoProvider.prototype.get = function(dirName) {
|
||||
var absPath = path.resolve(dirName);
|
||||
if (!this._cache[absPath]) {
|
||||
this._cache[absPath] = new PluginInfo(dirName);
|
||||
}
|
||||
return this._cache[absPath];
|
||||
};
|
||||
|
||||
// Normally you don't need to put() entries, but it's used
|
||||
// when copying plugins, and in unit tests.
|
||||
PluginInfoProvider.prototype.put = function(pluginInfo) {
|
||||
var absPath = path.resolve(pluginInfo.dir);
|
||||
this._cache[absPath] = pluginInfo;
|
||||
};
|
||||
|
||||
// Used for plugin search path processing.
|
||||
// Given a dir containing multiple plugins, create a PluginInfo object for
|
||||
// each of them and return as array.
|
||||
// Should load them all in parallel and return a promise, but not yet.
|
||||
PluginInfoProvider.prototype.getAllWithinSearchPath = function(dirName) {
|
||||
var absPath = path.resolve(dirName);
|
||||
if (!this._getAllCache[absPath]) {
|
||||
this._getAllCache[absPath] = getAllHelper(absPath, this);
|
||||
}
|
||||
return this._getAllCache[absPath];
|
||||
};
|
||||
|
||||
function getAllHelper(absPath, provider) {
|
||||
if (!fs.existsSync(absPath)){
|
||||
return [];
|
||||
}
|
||||
// If dir itself is a plugin, return it in an array with one element.
|
||||
if (fs.existsSync(path.join(absPath, 'plugin.xml'))) {
|
||||
return [provider.get(absPath)];
|
||||
}
|
||||
var subdirs = fs.readdirSync(absPath);
|
||||
var plugins = [];
|
||||
subdirs.forEach(function(subdir) {
|
||||
var d = path.join(absPath, subdir);
|
||||
if (fs.existsSync(path.join(d, 'plugin.xml'))) {
|
||||
try {
|
||||
plugins.push(provider.get(d));
|
||||
} catch (e) {
|
||||
events.emit('warn', 'Error parsing ' + path.join(d, 'plugin.xml.\n' + e.stack));
|
||||
}
|
||||
}
|
||||
});
|
||||
return plugins;
|
||||
}
|
||||
|
||||
module.exports = PluginInfoProvider;
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true, laxcomma:true, laxbreak:true */
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var PluginInfo = require('./PluginInfo');
|
||||
var events = require('../events');
|
||||
|
||||
function PluginInfoProvider() {
|
||||
this._cache = {};
|
||||
this._getAllCache = {};
|
||||
}
|
||||
|
||||
PluginInfoProvider.prototype.get = function(dirName) {
|
||||
var absPath = path.resolve(dirName);
|
||||
if (!this._cache[absPath]) {
|
||||
this._cache[absPath] = new PluginInfo(dirName);
|
||||
}
|
||||
return this._cache[absPath];
|
||||
};
|
||||
|
||||
// Normally you don't need to put() entries, but it's used
|
||||
// when copying plugins, and in unit tests.
|
||||
PluginInfoProvider.prototype.put = function(pluginInfo) {
|
||||
var absPath = path.resolve(pluginInfo.dir);
|
||||
this._cache[absPath] = pluginInfo;
|
||||
};
|
||||
|
||||
// Used for plugin search path processing.
|
||||
// Given a dir containing multiple plugins, create a PluginInfo object for
|
||||
// each of them and return as array.
|
||||
// Should load them all in parallel and return a promise, but not yet.
|
||||
PluginInfoProvider.prototype.getAllWithinSearchPath = function(dirName) {
|
||||
var absPath = path.resolve(dirName);
|
||||
if (!this._getAllCache[absPath]) {
|
||||
this._getAllCache[absPath] = getAllHelper(absPath, this);
|
||||
}
|
||||
return this._getAllCache[absPath];
|
||||
};
|
||||
|
||||
function getAllHelper(absPath, provider) {
|
||||
if (!fs.existsSync(absPath)){
|
||||
return [];
|
||||
}
|
||||
// If dir itself is a plugin, return it in an array with one element.
|
||||
if (fs.existsSync(path.join(absPath, 'plugin.xml'))) {
|
||||
return [provider.get(absPath)];
|
||||
}
|
||||
var subdirs = fs.readdirSync(absPath);
|
||||
var plugins = [];
|
||||
subdirs.forEach(function(subdir) {
|
||||
var d = path.join(absPath, subdir);
|
||||
if (fs.existsSync(path.join(d, 'plugin.xml'))) {
|
||||
try {
|
||||
plugins.push(provider.get(d));
|
||||
} catch (e) {
|
||||
events.emit('warn', 'Error parsing ' + path.join(d, 'plugin.xml.\n' + e.stack));
|
||||
}
|
||||
}
|
||||
});
|
||||
return plugins;
|
||||
}
|
||||
|
||||
module.exports = PluginInfoProvider;
|
||||
|
||||
38
node_modules/cordova-common/src/events.js
generated
vendored
38
node_modules/cordova-common/src/events.js
generated
vendored
@@ -1,19 +1,19 @@
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
module.exports = new (require('events').EventEmitter)();
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
module.exports = new (require('events').EventEmitter)();
|
||||
|
||||
308
node_modules/cordova-common/src/superspawn.js
generated
vendored
308
node_modules/cordova-common/src/superspawn.js
generated
vendored
@@ -1,154 +1,154 @@
|
||||
/**
|
||||
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 child_process = require('child_process');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var _ = require('underscore');
|
||||
var Q = require('q');
|
||||
var shell = require('shelljs');
|
||||
var events = require('./events');
|
||||
var iswin32 = process.platform == 'win32';
|
||||
|
||||
// On Windows, spawn() for batch files requires absolute path & having the extension.
|
||||
function resolveWindowsExe(cmd) {
|
||||
var winExtensions = ['.exe', '.cmd', '.bat', '.js', '.vbs'];
|
||||
function isValidExe(c) {
|
||||
return winExtensions.indexOf(path.extname(c)) !== -1 && fs.existsSync(c);
|
||||
}
|
||||
if (isValidExe(cmd)) {
|
||||
return cmd;
|
||||
}
|
||||
cmd = shell.which(cmd) || cmd;
|
||||
if (!isValidExe(cmd)) {
|
||||
winExtensions.some(function(ext) {
|
||||
if (fs.existsSync(cmd + ext)) {
|
||||
cmd = cmd + ext;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
function maybeQuote(a) {
|
||||
if (/^[^"].*[ &].*[^"]/.test(a)) return '"' + a + '"';
|
||||
return a;
|
||||
}
|
||||
|
||||
// opts:
|
||||
// printCommand: Whether to log the command (default: false)
|
||||
// stdio: 'default' is to capture output and returning it as a string to success (same as exec)
|
||||
// 'ignore' means don't bother capturing it
|
||||
// 'inherit' means pipe the input & output. This is required for anything that prompts.
|
||||
// env: Map of extra environment variables.
|
||||
// cwd: Working directory for the command.
|
||||
// chmod: If truthy, will attempt to set the execute bit before executing on non-Windows platforms.
|
||||
// Returns a promise that succeeds only for return code = 0.
|
||||
exports.spawn = function(cmd, args, opts) {
|
||||
args = args || [];
|
||||
opts = opts || {};
|
||||
var spawnOpts = {};
|
||||
var d = Q.defer();
|
||||
|
||||
if (iswin32) {
|
||||
cmd = resolveWindowsExe(cmd);
|
||||
// If we couldn't find the file, likely we'll end up failing,
|
||||
// but for things like "del", cmd will do the trick.
|
||||
if (path.extname(cmd) != '.exe') {
|
||||
var cmdArgs = '"' + [cmd].concat(args).map(maybeQuote).join(' ') + '"';
|
||||
// We need to use /s to ensure that spaces are parsed properly with cmd spawned content
|
||||
args = [['/s', '/c', cmdArgs].join(' ')];
|
||||
cmd = 'cmd';
|
||||
spawnOpts.windowsVerbatimArguments = true;
|
||||
} else if (!fs.existsSync(cmd)) {
|
||||
// We need to use /s to ensure that spaces are parsed properly with cmd spawned content
|
||||
args = ['/s', '/c', cmd].concat(args).map(maybeQuote);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.stdio == 'ignore') {
|
||||
spawnOpts.stdio = 'ignore';
|
||||
} else if (opts.stdio == 'inherit') {
|
||||
spawnOpts.stdio = 'inherit';
|
||||
}
|
||||
if (opts.cwd) {
|
||||
spawnOpts.cwd = opts.cwd;
|
||||
}
|
||||
if (opts.env) {
|
||||
spawnOpts.env = _.extend(_.extend({}, process.env), opts.env);
|
||||
}
|
||||
if (opts.chmod && !iswin32) {
|
||||
try {
|
||||
// This fails when module is installed in a system directory (e.g. via sudo npm install)
|
||||
fs.chmodSync(cmd, '755');
|
||||
} catch (e) {
|
||||
// If the perms weren't set right, then this will come as an error upon execution.
|
||||
}
|
||||
}
|
||||
|
||||
events.emit(opts.printCommand ? 'log' : 'verbose', 'Running command: ' + maybeQuote(cmd) + ' ' + args.map(maybeQuote).join(' '));
|
||||
|
||||
var child = child_process.spawn(cmd, args, spawnOpts);
|
||||
var capturedOut = '';
|
||||
var capturedErr = '';
|
||||
|
||||
if (child.stdout) {
|
||||
child.stdout.setEncoding('utf8');
|
||||
child.stdout.on('data', function(data) {
|
||||
capturedOut += data;
|
||||
});
|
||||
|
||||
child.stderr.setEncoding('utf8');
|
||||
child.stderr.on('data', function(data) {
|
||||
capturedErr += data;
|
||||
});
|
||||
}
|
||||
|
||||
child.on('close', whenDone);
|
||||
child.on('error', whenDone);
|
||||
function whenDone(arg) {
|
||||
child.removeListener('close', whenDone);
|
||||
child.removeListener('error', whenDone);
|
||||
var code = typeof arg == 'number' ? arg : arg && arg.code;
|
||||
|
||||
events.emit('verbose', 'Command finished with error code ' + code + ': ' + cmd + ' ' + args);
|
||||
if (code === 0) {
|
||||
d.resolve(capturedOut.trim());
|
||||
} else {
|
||||
var errMsg = cmd + ': Command failed with exit code ' + code;
|
||||
if (capturedErr) {
|
||||
errMsg += ' Error output:\n' + capturedErr.trim();
|
||||
}
|
||||
var err = new Error(errMsg);
|
||||
err.code = code;
|
||||
d.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
exports.maybeSpawn = function(cmd, args, opts) {
|
||||
if (fs.existsSync(cmd)) {
|
||||
return exports.spawn(cmd, args, opts);
|
||||
}
|
||||
return Q(null);
|
||||
};
|
||||
|
||||
/**
|
||||
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 child_process = require('child_process');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var _ = require('underscore');
|
||||
var Q = require('q');
|
||||
var shell = require('shelljs');
|
||||
var events = require('./events');
|
||||
var iswin32 = process.platform == 'win32';
|
||||
|
||||
// On Windows, spawn() for batch files requires absolute path & having the extension.
|
||||
function resolveWindowsExe(cmd) {
|
||||
var winExtensions = ['.exe', '.cmd', '.bat', '.js', '.vbs'];
|
||||
function isValidExe(c) {
|
||||
return winExtensions.indexOf(path.extname(c)) !== -1 && fs.existsSync(c);
|
||||
}
|
||||
if (isValidExe(cmd)) {
|
||||
return cmd;
|
||||
}
|
||||
cmd = shell.which(cmd) || cmd;
|
||||
if (!isValidExe(cmd)) {
|
||||
winExtensions.some(function(ext) {
|
||||
if (fs.existsSync(cmd + ext)) {
|
||||
cmd = cmd + ext;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
function maybeQuote(a) {
|
||||
if (/^[^"].*[ &].*[^"]/.test(a)) return '"' + a + '"';
|
||||
return a;
|
||||
}
|
||||
|
||||
// opts:
|
||||
// printCommand: Whether to log the command (default: false)
|
||||
// stdio: 'default' is to capture output and returning it as a string to success (same as exec)
|
||||
// 'ignore' means don't bother capturing it
|
||||
// 'inherit' means pipe the input & output. This is required for anything that prompts.
|
||||
// env: Map of extra environment variables.
|
||||
// cwd: Working directory for the command.
|
||||
// chmod: If truthy, will attempt to set the execute bit before executing on non-Windows platforms.
|
||||
// Returns a promise that succeeds only for return code = 0.
|
||||
exports.spawn = function(cmd, args, opts) {
|
||||
args = args || [];
|
||||
opts = opts || {};
|
||||
var spawnOpts = {};
|
||||
var d = Q.defer();
|
||||
|
||||
if (iswin32) {
|
||||
cmd = resolveWindowsExe(cmd);
|
||||
// If we couldn't find the file, likely we'll end up failing,
|
||||
// but for things like "del", cmd will do the trick.
|
||||
if (path.extname(cmd) != '.exe') {
|
||||
var cmdArgs = '"' + [cmd].concat(args).map(maybeQuote).join(' ') + '"';
|
||||
// We need to use /s to ensure that spaces are parsed properly with cmd spawned content
|
||||
args = [['/s', '/c', cmdArgs].join(' ')];
|
||||
cmd = 'cmd';
|
||||
spawnOpts.windowsVerbatimArguments = true;
|
||||
} else if (!fs.existsSync(cmd)) {
|
||||
// We need to use /s to ensure that spaces are parsed properly with cmd spawned content
|
||||
args = ['/s', '/c', cmd].concat(args).map(maybeQuote);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.stdio == 'ignore') {
|
||||
spawnOpts.stdio = 'ignore';
|
||||
} else if (opts.stdio == 'inherit') {
|
||||
spawnOpts.stdio = 'inherit';
|
||||
}
|
||||
if (opts.cwd) {
|
||||
spawnOpts.cwd = opts.cwd;
|
||||
}
|
||||
if (opts.env) {
|
||||
spawnOpts.env = _.extend(_.extend({}, process.env), opts.env);
|
||||
}
|
||||
if (opts.chmod && !iswin32) {
|
||||
try {
|
||||
// This fails when module is installed in a system directory (e.g. via sudo npm install)
|
||||
fs.chmodSync(cmd, '755');
|
||||
} catch (e) {
|
||||
// If the perms weren't set right, then this will come as an error upon execution.
|
||||
}
|
||||
}
|
||||
|
||||
events.emit(opts.printCommand ? 'log' : 'verbose', 'Running command: ' + maybeQuote(cmd) + ' ' + args.map(maybeQuote).join(' '));
|
||||
|
||||
var child = child_process.spawn(cmd, args, spawnOpts);
|
||||
var capturedOut = '';
|
||||
var capturedErr = '';
|
||||
|
||||
if (child.stdout) {
|
||||
child.stdout.setEncoding('utf8');
|
||||
child.stdout.on('data', function(data) {
|
||||
capturedOut += data;
|
||||
});
|
||||
|
||||
child.stderr.setEncoding('utf8');
|
||||
child.stderr.on('data', function(data) {
|
||||
capturedErr += data;
|
||||
});
|
||||
}
|
||||
|
||||
child.on('close', whenDone);
|
||||
child.on('error', whenDone);
|
||||
function whenDone(arg) {
|
||||
child.removeListener('close', whenDone);
|
||||
child.removeListener('error', whenDone);
|
||||
var code = typeof arg == 'number' ? arg : arg && arg.code;
|
||||
|
||||
events.emit('verbose', 'Command finished with error code ' + code + ': ' + cmd + ' ' + args);
|
||||
if (code === 0) {
|
||||
d.resolve(capturedOut.trim());
|
||||
} else {
|
||||
var errMsg = cmd + ': Command failed with exit code ' + code;
|
||||
if (capturedErr) {
|
||||
errMsg += ' Error output:\n' + capturedErr.trim();
|
||||
}
|
||||
var err = new Error(errMsg);
|
||||
err.code = code;
|
||||
d.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
return d.promise;
|
||||
};
|
||||
|
||||
exports.maybeSpawn = function(cmd, args, opts) {
|
||||
if (fs.existsSync(cmd)) {
|
||||
return exports.spawn(cmd, args, opts);
|
||||
}
|
||||
return Q(null);
|
||||
};
|
||||
|
||||
|
||||
202
node_modules/cordova-common/src/util/plist-helpers.js
generated
vendored
202
node_modules/cordova-common/src/util/plist-helpers.js
generated
vendored
@@ -1,101 +1,101 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2013 Brett Rudd
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// contains PLIST utility functions
|
||||
var __ = require('underscore');
|
||||
var plist = require('plist');
|
||||
|
||||
// adds node to doc at selector
|
||||
module.exports.graftPLIST = graftPLIST;
|
||||
function graftPLIST(doc, xml, selector) {
|
||||
var obj = plist.parse('<plist>'+xml+'</plist>');
|
||||
|
||||
var node = doc[selector];
|
||||
if (node && Array.isArray(node) && Array.isArray(obj)){
|
||||
node = node.concat(obj);
|
||||
for (var i =0;i<node.length; i++){
|
||||
for (var j=i+1; j<node.length; ++j) {
|
||||
if (nodeEqual(node[i], node[j]))
|
||||
node.splice(j--,1);
|
||||
}
|
||||
}
|
||||
doc[selector] = node;
|
||||
} else {
|
||||
//plist uses objects for <dict>. If we have two dicts we merge them instead of
|
||||
// overriding the old one. See CB-6472
|
||||
if (node && __.isObject(node) && __.isObject(obj) && !__.isDate(node) && !__.isDate(obj)){//arrays checked above
|
||||
__.extend(obj,node);
|
||||
}
|
||||
doc[selector] = obj;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// removes node from doc at selector
|
||||
module.exports.prunePLIST = prunePLIST;
|
||||
function prunePLIST(doc, xml, selector) {
|
||||
var obj = plist.parse('<plist>'+xml+'</plist>');
|
||||
|
||||
pruneOBJECT(doc, selector, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function pruneOBJECT(doc, selector, fragment) {
|
||||
if (Array.isArray(fragment) && Array.isArray(doc[selector])) {
|
||||
var empty = true;
|
||||
for (var i in fragment) {
|
||||
for (var j in doc[selector]) {
|
||||
empty = pruneOBJECT(doc[selector], j, fragment[i]) && empty;
|
||||
}
|
||||
}
|
||||
if (empty)
|
||||
{
|
||||
delete doc[selector];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (nodeEqual(doc[selector], fragment)) {
|
||||
delete doc[selector];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function nodeEqual(node1, node2) {
|
||||
if (typeof node1 != typeof node2)
|
||||
return false;
|
||||
else if (typeof node1 == 'string') {
|
||||
node2 = escapeRE(node2).replace(new RegExp('\\$[a-zA-Z0-9-_]+','gm'),'(.*?)');
|
||||
return new RegExp('^' + node2 + '$').test(node1);
|
||||
}
|
||||
else {
|
||||
for (var key in node2) {
|
||||
if (!nodeEqual(node1[key], node2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// escape string for use in regex
|
||||
function escapeRE(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '$&');
|
||||
}
|
||||
/*
|
||||
*
|
||||
* Copyright 2013 Brett Rudd
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// contains PLIST utility functions
|
||||
var __ = require('underscore');
|
||||
var plist = require('plist');
|
||||
|
||||
// adds node to doc at selector
|
||||
module.exports.graftPLIST = graftPLIST;
|
||||
function graftPLIST(doc, xml, selector) {
|
||||
var obj = plist.parse('<plist>'+xml+'</plist>');
|
||||
|
||||
var node = doc[selector];
|
||||
if (node && Array.isArray(node) && Array.isArray(obj)){
|
||||
node = node.concat(obj);
|
||||
for (var i =0;i<node.length; i++){
|
||||
for (var j=i+1; j<node.length; ++j) {
|
||||
if (nodeEqual(node[i], node[j]))
|
||||
node.splice(j--,1);
|
||||
}
|
||||
}
|
||||
doc[selector] = node;
|
||||
} else {
|
||||
//plist uses objects for <dict>. If we have two dicts we merge them instead of
|
||||
// overriding the old one. See CB-6472
|
||||
if (node && __.isObject(node) && __.isObject(obj) && !__.isDate(node) && !__.isDate(obj)){//arrays checked above
|
||||
__.extend(obj,node);
|
||||
}
|
||||
doc[selector] = obj;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// removes node from doc at selector
|
||||
module.exports.prunePLIST = prunePLIST;
|
||||
function prunePLIST(doc, xml, selector) {
|
||||
var obj = plist.parse('<plist>'+xml+'</plist>');
|
||||
|
||||
pruneOBJECT(doc, selector, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function pruneOBJECT(doc, selector, fragment) {
|
||||
if (Array.isArray(fragment) && Array.isArray(doc[selector])) {
|
||||
var empty = true;
|
||||
for (var i in fragment) {
|
||||
for (var j in doc[selector]) {
|
||||
empty = pruneOBJECT(doc[selector], j, fragment[i]) && empty;
|
||||
}
|
||||
}
|
||||
if (empty)
|
||||
{
|
||||
delete doc[selector];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (nodeEqual(doc[selector], fragment)) {
|
||||
delete doc[selector];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function nodeEqual(node1, node2) {
|
||||
if (typeof node1 != typeof node2)
|
||||
return false;
|
||||
else if (typeof node1 == 'string') {
|
||||
node2 = escapeRE(node2).replace(new RegExp('\\$[a-zA-Z0-9-_]+','gm'),'(.*?)');
|
||||
return new RegExp('^' + node2 + '$').test(node1);
|
||||
}
|
||||
else {
|
||||
for (var key in node2) {
|
||||
if (!nodeEqual(node1[key], node2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// escape string for use in regex
|
||||
function escapeRE(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '$&');
|
||||
}
|
||||
|
||||
532
node_modules/cordova-common/src/util/xml-helpers.js
generated
vendored
532
node_modules/cordova-common/src/util/xml-helpers.js
generated
vendored
@@ -1,266 +1,266 @@
|
||||
/*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* jshint sub:true, laxcomma:true */
|
||||
|
||||
/**
|
||||
* contains XML utility functions, some of which are specific to elementtree
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, path = require('path')
|
||||
, _ = require('underscore')
|
||||
, et = require('elementtree')
|
||||
;
|
||||
|
||||
module.exports = {
|
||||
// compare two et.XML nodes, see if they match
|
||||
// compares tagName, text, attributes and children (recursively)
|
||||
equalNodes: function(one, two) {
|
||||
if (one.tag != two.tag) {
|
||||
return false;
|
||||
} else if (one.text.trim() != two.text.trim()) {
|
||||
return false;
|
||||
} else if (one._children.length != two._children.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var oneAttribKeys = Object.keys(one.attrib),
|
||||
twoAttribKeys = Object.keys(two.attrib),
|
||||
i = 0, attribName;
|
||||
|
||||
if (oneAttribKeys.length != twoAttribKeys.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i; i < oneAttribKeys.length; i++) {
|
||||
attribName = oneAttribKeys[i];
|
||||
|
||||
if (one.attrib[attribName] != two.attrib[attribName]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i; i < one._children.length; i++) {
|
||||
if (!module.exports.equalNodes(one._children[i], two._children[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// adds node to doc at selector, creating parent if it doesn't exist
|
||||
graftXML: function(doc, nodes, selector, after) {
|
||||
var parent = resolveParent(doc, selector);
|
||||
if (!parent) {
|
||||
//Try to create the parent recursively if necessary
|
||||
try {
|
||||
var parentToCreate = et.XML('<' + path.basename(selector) + '>'),
|
||||
parentSelector = path.dirname(selector);
|
||||
|
||||
this.graftXML(doc, [parentToCreate], parentSelector);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
parent = resolveParent(doc, selector);
|
||||
if (!parent) return false;
|
||||
}
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
// check if child is unique first
|
||||
if (uniqueChild(node, parent)) {
|
||||
var children = parent.getchildren();
|
||||
var insertIdx = after ? findInsertIdx(children, after) : children.length;
|
||||
|
||||
//TODO: replace with parent.insert after the bug in ElementTree is fixed
|
||||
parent.getchildren().splice(insertIdx, 0, node);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// removes node from doc at selector
|
||||
pruneXML: function(doc, nodes, selector) {
|
||||
var parent = resolveParent(doc, selector);
|
||||
if (!parent) return false;
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
var matchingKid = null;
|
||||
if ((matchingKid = findChild(node, parent)) !== null) {
|
||||
// stupid elementtree takes an index argument it doesn't use
|
||||
// and does not conform to the python lib
|
||||
parent.remove(matchingKid);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
parseElementtreeSync: function (filename) {
|
||||
var contents = fs.readFileSync(filename, 'utf-8');
|
||||
if(contents) {
|
||||
//Windows is the BOM. Skip the Byte Order Mark.
|
||||
contents = contents.substring(contents.indexOf('<'));
|
||||
}
|
||||
return new et.ElementTree(et.XML(contents));
|
||||
}
|
||||
};
|
||||
|
||||
function findChild(node, parent) {
|
||||
var matchingKids = parent.findall(node.tag)
|
||||
, i, j;
|
||||
|
||||
for (i = 0, j = matchingKids.length ; i < j ; i++) {
|
||||
if (module.exports.equalNodes(node, matchingKids[i])) {
|
||||
return matchingKids[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function uniqueChild(node, parent) {
|
||||
var matchingKids = parent.findall(node.tag)
|
||||
, i = 0;
|
||||
|
||||
if (matchingKids.length === 0) {
|
||||
return true;
|
||||
} else {
|
||||
for (i; i < matchingKids.length; i++) {
|
||||
if (module.exports.equalNodes(node, matchingKids[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var ROOT = /^\/([^\/]*)/,
|
||||
ABSOLUTE = /^\/([^\/]*)\/(.*)/;
|
||||
|
||||
function resolveParent(doc, selector) {
|
||||
var parent, tagName, subSelector;
|
||||
|
||||
// handle absolute selector (which elementtree doesn't like)
|
||||
if (ROOT.test(selector)) {
|
||||
tagName = selector.match(ROOT)[1];
|
||||
// test for wildcard "any-tag" root selector
|
||||
if (tagName == '*' || tagName === doc._root.tag) {
|
||||
parent = doc._root;
|
||||
|
||||
// could be an absolute path, but not selecting the root
|
||||
if (ABSOLUTE.test(selector)) {
|
||||
subSelector = selector.match(ABSOLUTE)[2];
|
||||
parent = parent.find(subSelector);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
parent = doc.find(selector);
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
// Find the index at which to insert an entry. After is a ;-separated priority list
|
||||
// of tags after which the insertion should be made. E.g. If we need to
|
||||
// insert an element C, and the rule is that the order of children has to be
|
||||
// As, Bs, Cs. After will be equal to "C;B;A".
|
||||
function findInsertIdx(children, after) {
|
||||
var childrenTags = children.map(function(child) { return child.tag; });
|
||||
var afters = after.split(';');
|
||||
var afterIndexes = afters.map(function(current) { return childrenTags.lastIndexOf(current); });
|
||||
var foundIndex = _.find(afterIndexes, function(index) { return index != -1; });
|
||||
|
||||
//add to the beginning if no matching nodes are found
|
||||
return typeof foundIndex === 'undefined' ? 0 : foundIndex+1;
|
||||
}
|
||||
|
||||
var BLACKLIST = ['platform', 'feature','plugin','engine'];
|
||||
var SINGLETONS = ['content', 'author'];
|
||||
function mergeXml(src, dest, platform, clobber) {
|
||||
// Do nothing for blacklisted tags.
|
||||
if (BLACKLIST.indexOf(src.tag) != -1) return;
|
||||
|
||||
//Handle attributes
|
||||
Object.getOwnPropertyNames(src.attrib).forEach(function (attribute) {
|
||||
if (clobber || !dest.attrib[attribute]) {
|
||||
dest.attrib[attribute] = src.attrib[attribute];
|
||||
}
|
||||
});
|
||||
//Handle text
|
||||
if (src.text && (clobber || !dest.text)) {
|
||||
dest.text = src.text;
|
||||
}
|
||||
//Handle platform
|
||||
if (platform) {
|
||||
src.findall('platform[@name="' + platform + '"]').forEach(function (platformElement) {
|
||||
platformElement.getchildren().forEach(mergeChild);
|
||||
});
|
||||
}
|
||||
|
||||
//Handle children
|
||||
src.getchildren().forEach(mergeChild);
|
||||
|
||||
function mergeChild (srcChild) {
|
||||
var srcTag = srcChild.tag,
|
||||
destChild = new et.Element(srcTag),
|
||||
foundChild,
|
||||
query = srcTag + '',
|
||||
shouldMerge = true;
|
||||
|
||||
if (BLACKLIST.indexOf(srcTag) === -1) {
|
||||
if (SINGLETONS.indexOf(srcTag) !== -1) {
|
||||
foundChild = dest.find(query);
|
||||
if (foundChild) {
|
||||
destChild = foundChild;
|
||||
dest.remove(destChild);
|
||||
}
|
||||
} else {
|
||||
//Check for an exact match and if you find one don't add
|
||||
Object.getOwnPropertyNames(srcChild.attrib).forEach(function (attribute) {
|
||||
query += '[@' + attribute + '="' + srcChild.attrib[attribute] + '"]';
|
||||
});
|
||||
var foundChildren = dest.findall(query);
|
||||
for(var i = 0; i < foundChildren.length; i++) {
|
||||
foundChild = foundChildren[i];
|
||||
if (foundChild && textMatch(srcChild, foundChild) && (Object.keys(srcChild.attrib).length==Object.keys(foundChild.attrib).length)) {
|
||||
destChild = foundChild;
|
||||
dest.remove(destChild);
|
||||
shouldMerge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mergeXml(srcChild, destChild, platform, clobber && shouldMerge);
|
||||
dest.append(destChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expose for testing.
|
||||
module.exports.mergeXml = mergeXml;
|
||||
|
||||
function textMatch(elm1, elm2) {
|
||||
var text1 = elm1.text ? elm1.text.replace(/\s+/, '') : '',
|
||||
text2 = elm2.text ? elm2.text.replace(/\s+/, '') : '';
|
||||
return (text1 === '' || text1 === text2);
|
||||
}
|
||||
/*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* jshint sub:true, laxcomma:true */
|
||||
|
||||
/**
|
||||
* contains XML utility functions, some of which are specific to elementtree
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, path = require('path')
|
||||
, _ = require('underscore')
|
||||
, et = require('elementtree')
|
||||
;
|
||||
|
||||
module.exports = {
|
||||
// compare two et.XML nodes, see if they match
|
||||
// compares tagName, text, attributes and children (recursively)
|
||||
equalNodes: function(one, two) {
|
||||
if (one.tag != two.tag) {
|
||||
return false;
|
||||
} else if (one.text.trim() != two.text.trim()) {
|
||||
return false;
|
||||
} else if (one._children.length != two._children.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var oneAttribKeys = Object.keys(one.attrib),
|
||||
twoAttribKeys = Object.keys(two.attrib),
|
||||
i = 0, attribName;
|
||||
|
||||
if (oneAttribKeys.length != twoAttribKeys.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i; i < oneAttribKeys.length; i++) {
|
||||
attribName = oneAttribKeys[i];
|
||||
|
||||
if (one.attrib[attribName] != two.attrib[attribName]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i; i < one._children.length; i++) {
|
||||
if (!module.exports.equalNodes(one._children[i], two._children[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// adds node to doc at selector, creating parent if it doesn't exist
|
||||
graftXML: function(doc, nodes, selector, after) {
|
||||
var parent = resolveParent(doc, selector);
|
||||
if (!parent) {
|
||||
//Try to create the parent recursively if necessary
|
||||
try {
|
||||
var parentToCreate = et.XML('<' + path.basename(selector) + '>'),
|
||||
parentSelector = path.dirname(selector);
|
||||
|
||||
this.graftXML(doc, [parentToCreate], parentSelector);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
parent = resolveParent(doc, selector);
|
||||
if (!parent) return false;
|
||||
}
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
// check if child is unique first
|
||||
if (uniqueChild(node, parent)) {
|
||||
var children = parent.getchildren();
|
||||
var insertIdx = after ? findInsertIdx(children, after) : children.length;
|
||||
|
||||
//TODO: replace with parent.insert after the bug in ElementTree is fixed
|
||||
parent.getchildren().splice(insertIdx, 0, node);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// removes node from doc at selector
|
||||
pruneXML: function(doc, nodes, selector) {
|
||||
var parent = resolveParent(doc, selector);
|
||||
if (!parent) return false;
|
||||
|
||||
nodes.forEach(function (node) {
|
||||
var matchingKid = null;
|
||||
if ((matchingKid = findChild(node, parent)) !== null) {
|
||||
// stupid elementtree takes an index argument it doesn't use
|
||||
// and does not conform to the python lib
|
||||
parent.remove(matchingKid);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
parseElementtreeSync: function (filename) {
|
||||
var contents = fs.readFileSync(filename, 'utf-8');
|
||||
if(contents) {
|
||||
//Windows is the BOM. Skip the Byte Order Mark.
|
||||
contents = contents.substring(contents.indexOf('<'));
|
||||
}
|
||||
return new et.ElementTree(et.XML(contents));
|
||||
}
|
||||
};
|
||||
|
||||
function findChild(node, parent) {
|
||||
var matchingKids = parent.findall(node.tag)
|
||||
, i, j;
|
||||
|
||||
for (i = 0, j = matchingKids.length ; i < j ; i++) {
|
||||
if (module.exports.equalNodes(node, matchingKids[i])) {
|
||||
return matchingKids[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function uniqueChild(node, parent) {
|
||||
var matchingKids = parent.findall(node.tag)
|
||||
, i = 0;
|
||||
|
||||
if (matchingKids.length === 0) {
|
||||
return true;
|
||||
} else {
|
||||
for (i; i < matchingKids.length; i++) {
|
||||
if (module.exports.equalNodes(node, matchingKids[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var ROOT = /^\/([^\/]*)/,
|
||||
ABSOLUTE = /^\/([^\/]*)\/(.*)/;
|
||||
|
||||
function resolveParent(doc, selector) {
|
||||
var parent, tagName, subSelector;
|
||||
|
||||
// handle absolute selector (which elementtree doesn't like)
|
||||
if (ROOT.test(selector)) {
|
||||
tagName = selector.match(ROOT)[1];
|
||||
// test for wildcard "any-tag" root selector
|
||||
if (tagName == '*' || tagName === doc._root.tag) {
|
||||
parent = doc._root;
|
||||
|
||||
// could be an absolute path, but not selecting the root
|
||||
if (ABSOLUTE.test(selector)) {
|
||||
subSelector = selector.match(ABSOLUTE)[2];
|
||||
parent = parent.find(subSelector);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
parent = doc.find(selector);
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
// Find the index at which to insert an entry. After is a ;-separated priority list
|
||||
// of tags after which the insertion should be made. E.g. If we need to
|
||||
// insert an element C, and the rule is that the order of children has to be
|
||||
// As, Bs, Cs. After will be equal to "C;B;A".
|
||||
function findInsertIdx(children, after) {
|
||||
var childrenTags = children.map(function(child) { return child.tag; });
|
||||
var afters = after.split(';');
|
||||
var afterIndexes = afters.map(function(current) { return childrenTags.lastIndexOf(current); });
|
||||
var foundIndex = _.find(afterIndexes, function(index) { return index != -1; });
|
||||
|
||||
//add to the beginning if no matching nodes are found
|
||||
return typeof foundIndex === 'undefined' ? 0 : foundIndex+1;
|
||||
}
|
||||
|
||||
var BLACKLIST = ['platform', 'feature','plugin','engine'];
|
||||
var SINGLETONS = ['content', 'author'];
|
||||
function mergeXml(src, dest, platform, clobber) {
|
||||
// Do nothing for blacklisted tags.
|
||||
if (BLACKLIST.indexOf(src.tag) != -1) return;
|
||||
|
||||
//Handle attributes
|
||||
Object.getOwnPropertyNames(src.attrib).forEach(function (attribute) {
|
||||
if (clobber || !dest.attrib[attribute]) {
|
||||
dest.attrib[attribute] = src.attrib[attribute];
|
||||
}
|
||||
});
|
||||
//Handle text
|
||||
if (src.text && (clobber || !dest.text)) {
|
||||
dest.text = src.text;
|
||||
}
|
||||
//Handle platform
|
||||
if (platform) {
|
||||
src.findall('platform[@name="' + platform + '"]').forEach(function (platformElement) {
|
||||
platformElement.getchildren().forEach(mergeChild);
|
||||
});
|
||||
}
|
||||
|
||||
//Handle children
|
||||
src.getchildren().forEach(mergeChild);
|
||||
|
||||
function mergeChild (srcChild) {
|
||||
var srcTag = srcChild.tag,
|
||||
destChild = new et.Element(srcTag),
|
||||
foundChild,
|
||||
query = srcTag + '',
|
||||
shouldMerge = true;
|
||||
|
||||
if (BLACKLIST.indexOf(srcTag) === -1) {
|
||||
if (SINGLETONS.indexOf(srcTag) !== -1) {
|
||||
foundChild = dest.find(query);
|
||||
if (foundChild) {
|
||||
destChild = foundChild;
|
||||
dest.remove(destChild);
|
||||
}
|
||||
} else {
|
||||
//Check for an exact match and if you find one don't add
|
||||
Object.getOwnPropertyNames(srcChild.attrib).forEach(function (attribute) {
|
||||
query += '[@' + attribute + '="' + srcChild.attrib[attribute] + '"]';
|
||||
});
|
||||
var foundChildren = dest.findall(query);
|
||||
for(var i = 0; i < foundChildren.length; i++) {
|
||||
foundChild = foundChildren[i];
|
||||
if (foundChild && textMatch(srcChild, foundChild) && (Object.keys(srcChild.attrib).length==Object.keys(foundChild.attrib).length)) {
|
||||
destChild = foundChild;
|
||||
dest.remove(destChild);
|
||||
shouldMerge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mergeXml(srcChild, destChild, platform, clobber && shouldMerge);
|
||||
dest.append(destChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expose for testing.
|
||||
module.exports.mergeXml = mergeXml;
|
||||
|
||||
function textMatch(elm1, elm2) {
|
||||
var text1 = elm1.text ? elm1.text.replace(/\s+/, '') : '',
|
||||
text2 = elm2.text ? elm2.text.replace(/\s+/, '') : '';
|
||||
return (text1 === '' || text1 === text2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user