CB-12870 : rebased and updated paths
Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/6b98390e Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/6b98390e Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/6b98390e Branch: refs/heads/master Commit: 6b98390ecefb28a7e2249ac32d5ace2b463971a6 Parents: 60bbd8b Author: Audrey So <[email protected]> Authored: Thu Aug 10 11:25:39 2017 -0700 Committer: Audrey So <[email protected]> Committed: Tue Aug 29 16:12:42 2017 -0700 ---------------------------------------------------------------------- integration-tests/pkgJson-restore.spec.js | 2 +- package.json | 2 +- .../windows/cordova/lib/AppxManifest.js | 733 ------------------- .../windows/cordova/lib/ConfigChanges.js | 164 ----- .../windows/cordova/lib/JsprojManager.js | 626 ---------------- .../windows/cordova/lib/PluginHandler.js | 282 ------- .../platforms/windows/cordova/lib/PluginInfo.js | 139 ---- .../windows/cordova/lib/AppxManifest.js | 733 +++++++++++++++++++ .../windows/cordova/lib/ConfigChanges.js | 164 +++++ .../windows/cordova/lib/JsprojManager.js | 626 ++++++++++++++++ .../windows/cordova/lib/PluginHandler.js | 282 +++++++ .../platforms/windows/cordova/lib/PluginInfo.js | 139 ++++ spec/cordova/platforms/platforms.spec.js | 2 +- spec/cordova/util.spec.js | 23 +- src/cordova/util.js | 25 +- src/plugman/init-defaults.js | 18 +- 16 files changed, 1979 insertions(+), 1981 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6b98390e/integration-tests/pkgJson-restore.spec.js ---------------------------------------------------------------------- diff --git a/integration-tests/pkgJson-restore.spec.js b/integration-tests/pkgJson-restore.spec.js index feaf090..dfd1f10 100644 --- a/integration-tests/pkgJson-restore.spec.js +++ b/integration-tests/pkgJson-restore.spec.js @@ -1249,7 +1249,7 @@ describe('platforms and plugins should be restored with config.xml even without // Copy then move because we need to copy everything, but that means it will copy the whole directory. // Using /* doesn't work because of hidden files. // Use basePkgJson13 because pkg.json and config.xml contain only android - shell.cp('-R', path.join(__dirname, '..', 'spec-cordova', 'fixtures', 'basePkgJson13'), tmpDir); + shell.cp('-R', path.join(__dirname, '..', 'spec', 'cordova', 'fixtures', 'basePkgJson13'), tmpDir); shell.mv(path.join(tmpDir, 'basePkgJson13'), project); process.chdir(project); events.on('results', function (res) { results = res; }); http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6b98390e/package.json ---------------------------------------------------------------------- diff --git a/package.json b/package.json index 3834ad5..bd611e5 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "eslint-plugin-standard": "^3.0.1", "istanbul": "^0.4.5", "jasmine": "^2.5.2", - "rewire": "2.5.2" + "rewire": "^2.5.2" }, "scripts": { "test": "npm run eslint && npm run unit-tests && npm run e2e-tests", http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6b98390e/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/AppxManifest.js ---------------------------------------------------------------------- diff --git a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/AppxManifest.js b/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/AppxManifest.js deleted file mode 100644 index 1875bf7..0000000 --- a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/AppxManifest.js +++ /dev/null @@ -1,733 +0,0 @@ -/** - 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 fs = require('fs'); -var util = require('util'); -var et = require('elementtree'); -var path = require('path'); -var xml= require('cordova-common').xmlHelpers; - -var UAP_RESTRICTED_CAPS = ['enterpriseAuthentication', 'sharedUserCertificates', - 'documentsLibrary', 'musicLibrary', 'picturesLibrary', - 'videosLibrary', 'removableStorage', 'internetClientClientServer', - 'privateNetworkClientServer']; - -// UAP namespace capabilities come from the XSD type ST_Capability_Uap from AppxManifestTypes.xsd -var CAPS_NEEDING_UAPNS = ['documentsLibrary', 'picturesLibrary', 'videosLibrary', - 'musicLibrary', 'enterpriseAuthentication', 'sharedUserCertificates', - 'removableStorage', 'appointments', 'contacts', 'userAccountInformation', - 'phoneCall', 'blockedChatMessages', 'objects3D']; - -var KNOWN_ORIENTATIONS = { - 'default': ['portrait', 'landscape', 'landscapeFlipped'], - 'portrait': ['portrait'], - 'landscape': ['landscape', 'landscapeFlipped'] -}; - -/** - * Store to cache appxmanifest files based on file location - * @type {Object} - */ -var manifestCache = {}; - -/** - * @constructor - * @constructs AppxManifest - * - * Wraps an AppxManifest file. Shouldn't be instantiated directly. - * AppxManifest.get should be used instead to select proper manifest type - * (AppxManifest for Win 8/8.1/Phone 8.1, Win10AppxManifest for Win 10) - * - * @param {string} path Path to appxmanifest to wrap - * @param {string} prefix A namespace prefix used to prepend some elements. - * Depends on manifest type. - */ -function AppxManifest(path, prefix) { - this.path = path; - // Append ':' to prefix if needed - prefix = prefix || ''; - this.prefix = (prefix.indexOf(':') === prefix.length - 1) ? prefix : prefix + ':'; - this.doc = xml.parseElementtreeSync(path); - if (this.doc.getroot().tag !== 'Package') { - // Some basic validation - throw new Error(path + ' has incorrect root node name (expected "Package")'); - } - - // Indicates that this manifest is for phone application (either WinPhone 8.1 or Universal Windows 10) - this.hasPhoneIdentity = this.prefix === 'uap:' || this.prefix === 'm3:'; -} - -// Static read-only property to get capabilities which need to be prefixed with uap -Object.defineProperty(AppxManifest, 'CapsNeedUapPrefix', { - writable: false, - configurable: false, - value: CAPS_NEEDING_UAPNS -}); - -/** - * @static - * @constructs AppxManifest|Win10AppxManifest - * - * Instantiates a new AppxManifest/Win10AppxManifest class. Chooses which - * constructor to use based on xmlns attributes of Package node - * - * @param {String} fileName File to create manifest for - * @param {Boolean} [ignoreCache=false] Specifies, whether manifest cache will be - * used to return resultant object - * - * @return {AppxManifest|Win10AppxManifest} Manifest instance - */ -AppxManifest.get = function (fileName, ignoreCache) { - - if (!ignoreCache && manifestCache[fileName]) { - return manifestCache[fileName]; - } - - var root = xml.parseElementtreeSync(fileName).getroot(); - var prefixes = Object.keys(root.attrib) - .reduce(function (result, attrib) { - if (attrib.indexOf('xmlns') === 0 && attrib !== 'xmlns:mp') { - result.push(attrib.replace('xmlns', '').replace(':', '')); - } - - return result; - }, []).sort(); - - var prefix = prefixes[prefixes.length - 1]; - var Manifest = prefix === 'uap' ? Win10AppxManifest : AppxManifest; - var result = new Manifest(fileName, prefix); - - if (!ignoreCache) { - manifestCache[fileName] = result; - } - - return result; -}; - -/** - * Removes manifests from cache to prevent using stale entries - * - * @param {String|String[]} [cacheKeys] The keys to delete from cache. If not - * specified, the whole cache will be purged - */ -AppxManifest.purgeCache = function (cacheKeys) { - if (!cacheKeys) { - // if no arguments passed, remove all entries - manifestCache = {}; - return; - } - - var keys = Array.isArray(cacheKeys) ? cacheKeys : [cacheKeys]; - keys.forEach(function (key) { - delete manifestCache[key]; - }); -}; - -AppxManifest.prototype.getPhoneIdentity = function () { - var phoneIdentity = this.doc.getroot().find('./mp:PhoneIdentity'); - if (!phoneIdentity) - throw new Error('Failed to find PhoneIdentity element in appxmanifest at ' + this.path); - - return { - getPhoneProductId: function () { - return phoneIdentity.attrib.PhoneProductId; - }, - setPhoneProductId: function (id) { - if (!id) throw new Error('Argument for "setPhoneProductId" must be defined in appxmanifest at ' + this.path); - phoneIdentity.attrib.PhoneProductId = id; - return this; - } - }; -}; - -AppxManifest.prototype.getIdentity = function () { - var identity = this.doc.getroot().find('./Identity'); - if (!identity) - throw new Error('Failed to find "Identity" node. The appxmanifest at ' + this.path + ' is invalid'); - - return { - getName: function () { - return identity.attrib.Name; - }, - setName: function (name) { - if (!name) throw new TypeError('Identity.Name attribute must be non-empty in appxmanifest at ' + this.path); - identity.attrib.Name = name; - return this; - }, - getPublisher: function () { - return identity.attrib.Publisher; - }, - setPublisher: function (publisherId) { - if (!publisherId) throw new TypeError('Identity.Publisher attribute must be non-empty in appxmanifest at ' + this.path); - identity.attrib.Publisher = publisherId; - return this; - }, - getVersion: function () { - return identity.attrib.Version; - }, - setVersion: function (version) { - if (!version) throw new TypeError('Identity.Version attribute must be non-empty in appxmanifest at ' + this.path ); - - // Adjust version number as per CB-5337 Windows8 build fails due to invalid app version - if(version && version.match(/\.\d/g)) { - var numVersionComponents = version.match(/\.\d/g).length + 1; - while (numVersionComponents++ < 4) { - version += '.0'; - } - } - - identity.attrib.Version = version; - return this; - } - }; -}; - -AppxManifest.prototype.getProperties = function () { - var properties = this.doc.getroot().find('./Properties'); - - if (!properties) - throw new Error('Failed to find "Properties" node. The appxmanifest at ' + this.path + ' is invalid'); - - return { - getDisplayName: function () { - var displayName = properties.find('./DisplayName'); - return displayName && displayName.text; - }, - setDisplayName: function (name) { - if (!name) throw new TypeError('Properties.DisplayName elements must be non-empty in appxmanifest at ' + this.path); - var displayName = properties.find('./DisplayName'); - - if (!displayName) { - displayName = new et.Element('DisplayName'); - properties.append(displayName); - } - - displayName.text = name; - - return this; - }, - getPublisherDisplayName: function () { - var publisher = properties.find('./PublisherDisplayName'); - return publisher && publisher.text; - }, - setPublisherDisplayName: function (name) { - if (!name) throw new TypeError('Properties.PublisherDisplayName elements must be non-empty in appxmanifest at ' + this.path); - var publisher = properties.find('./PublisherDisplayName'); - - if (!publisher) { - publisher = new et.Element('PublisherDisplayName'); - properties.append(publisher); - } - - publisher.text = name; - - return this; - }, - getDescription: function () { - var description = properties.find('./Description'); - return description && description.text; - }, - setDescription: function (text) { - - var description = properties.find('./Description'); - - if (!text || text.length === 0) { - if (description) properties.remove(description); - return this; - } - - if (!description) { - description = new et.Element('Description'); - properties.append(description); - } - - description.text = processDescription(text); - - return this; - }, - }; -}; - -AppxManifest.prototype.getApplication = function () { - var application = this.doc.getroot().find('./Applications/Application'); - if (!application) - throw new Error('Failed to find "Application" element. The appxmanifest at ' + this.path + ' is invalid'); - - var self = this; - - return { - _node: application, - getVisualElements: function () { - return self.getVisualElements(); - }, - getId: function () { - return application.attrib.Id; - }, - setId: function (id) { - if (!id) throw new TypeError('Application.Id attribute must be defined in appxmanifest at ' + this.path); - // 64 symbols restriction goes from manifest schema definition - // http://msdn.microsoft.com/en-us/library/windows/apps/br211415.aspx - var appId = id.length <= 64 ? id : id.substr(0, 64); - application.attrib.Id = appId; - return this; - }, - getStartPage: function () { - return application.attrib.StartPage; - }, - setStartPage: function (page) { - if (!page) page = 'www/index.html'; // Default valur is always index.html - application.attrib.StartPage = page; - return this; - }, - getAccessRules: function () { - return application - .findall('./ApplicationContentUriRules/Rule') - .map(function (rule) { - return rule.attrib.Match; - }); - }, - setAccessRules: function (rules) { - var appUriRules = application.find('ApplicationContentUriRules'); - if (appUriRules) { - application.remove(appUriRules); - } - - // No rules defined - if (!rules || rules.length === 0) { - return; - } - - appUriRules = new et.Element('ApplicationContentUriRules'); - application.append(appUriRules); - - rules.forEach(function(rule) { - appUriRules.append(new et.Element('Rule', {Match: rule, Type: 'include'})); - }); - - return this; - } - }; -}; - -AppxManifest.prototype.getVisualElements = function () { - var self = this; - var visualElements = this.doc.getroot().find('./Applications/Application/' + - this.prefix + 'VisualElements'); - - if (!visualElements) - throw new Error('Failed to find "VisualElements" node. The appxmanifest at ' + this.path + ' is invalid'); - - return { - _node: visualElements, - getDisplayName: function () { - return visualElements.attrib.DisplayName; - }, - setDisplayName: function (name) { - if (!name) throw new TypeError('VisualElements.DisplayName attribute must be defined in appxmanifest at ' + this.path); - visualElements.attrib.DisplayName = name; - return this; - }, - getOrientation: function () { - return visualElements.findall(self.prefix + 'Rotation') - .map(function (element) { - return element.attrib.Preference; - }); - }, - setOrientation: function (orientation) { - if (!orientation || orientation === ''){ - orientation = 'default'; - } - - var rotationPreferenceRootName = self.prefix + 'InitialRotationPreference'; - var rotationPreferenceRoot = visualElements.find('./' + rotationPreferenceRootName); - - if (!orientation && rotationPreferenceRoot) { - // Remove InitialRotationPreference root element to revert to defaults - visualElements.remove(rotationPreferenceRoot); - return this; - } - - if(!rotationPreferenceRoot) { - rotationPreferenceRoot = new et.Element(rotationPreferenceRootName); - visualElements.append(rotationPreferenceRoot); - } - - rotationPreferenceRoot.clear(); - - var orientations = KNOWN_ORIENTATIONS[orientation] || orientation.split(','); - orientations.forEach(function(orientation) { - var el = new et.Element(self.prefix + 'Rotation', {Preference: orientation} ); - rotationPreferenceRoot.append(el); - }); - - return this; - }, - getBackgroundColor: function () { - return visualElements.attrib.BackgroundColor; - }, - setBackgroundColor: function (color) { - if (!color) - throw new TypeError('VisualElements.BackgroundColor attribute must be defined in appxmanifest at ' + this.path); - - visualElements.attrib.BackgroundColor = refineColor(color); - return this; - }, - trySetBackgroundColor: function (color) { - try { - return this.setBackgroundColor(color); - } catch (e) { return this; } - }, - getForegroundText: function () { - return visualElements.attrib.ForegroundText; - }, - setForegroundText: function (color) { - // If color is not set, fall back to 'light' by default - visualElements.attrib.ForegroundText = color || 'light'; - - return this; - }, - getSplashBackgroundColor: function () { - var splashNode = visualElements.find('./' + self.prefix + 'SplashScreen'); - return splashNode && splashNode.attrib.BackgroundColor; - }, - setSplashBackgroundColor: function (color) { - var splashNode = visualElements.find('./' + self.prefix + 'SplashScreen'); - if (splashNode) { - if (color) { - splashNode.attrib.BackgroundColor = refineColor(color); - } else { - delete splashNode.attrib.BackgroundColor; - } - } - return this; - }, - getSplashScreenExtension: function (extension) { - var splashNode = visualElements.find('./' + self.prefix + 'SplashScreen'); - return splashNode && splashNode.attrib.Image && path.extname(splashNode.attrib.Image); - }, - setSplashScreenExtension: function (extension) { - var splashNode = visualElements.find('./' + self.prefix + 'SplashScreen'); - if (splashNode) { - var oldPath = splashNode.attrib.Image; - splashNode.attrib.Image = path.dirname(oldPath) + '\\' + path.basename(oldPath, path.extname(oldPath)) + extension; - } - return this; - }, - getToastCapable: function () { - return visualElements.attrib.ToastCapable; - }, - setToastCapable: function (isToastCapable) { - if (isToastCapable === true || isToastCapable.toString().toLowerCase() === 'true') { - visualElements.attrib.ToastCapable = 'true'; - } else { - delete visualElements.attrib.ToastCapable; - } - - return this; - }, - getDescription: function () { - return visualElements.attrib.Description; - }, - setDescription: function (description) { - if (!description || description.length === 0) - throw new TypeError('VisualElements.Description attribute must be defined and non-empty in appxmanifest at ' + this.path); - - visualElements.attrib.Description = processDescription(description); - return this; - }, - }; -}; - -AppxManifest.prototype.getCapabilities = function () { - var capabilities = this.doc.find('./Capabilities'); - if (!capabilities) return []; - - return capabilities.getchildren() - .map(function (element) { - return { type: element.tag, name: element.attrib.Name }; - }); -}; - -function isCSSColorName(color) { - return color.indexOf('0x') === -1 && color.indexOf('#') === -1; -} - -function refineColor(color) { - if (isCSSColorName(color)) { - return color; - } - - // return three-byte hexadecimal number preceded by "#" (required for Windows) - color = color.replace('0x', '').replace('#', ''); - if (color.length == 3) { - color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2]; - } - // alpha is not supported, so we remove it - if (color.length == 8) { // AArrggbb - color = color.slice(2); - } - return '#' + color; -} - -function processDescription(text) { - var result = text; - - // Description value limitations: https://msdn.microsoft.com/en-us/library/windows/apps/br211429.aspx - // value should be no longer than 2048 characters - if (text.length > 2048) { - result = text.substr(0, 2048); - } - - // value should not contain newlines and tabs - return result.replace(/(\n|\r)/g, ' ').replace(/\t/g, ' '); -} - -// Shortcut for getIdentity.setName -AppxManifest.prototype.setPackageName = function (name) { - this.getIdentity().setName(name); - return this; -}; - -// Shortcut for multiple inner methods calls -AppxManifest.prototype.setAppName = function (name) { - this.getProperties().setDisplayName(name); - this.getVisualElements().setDisplayName(name); - - return this; -}; - -/** - * Writes manifest to disk syncronously. If filename is specified, then manifest - * will be written to that file - * - * @param {String} [destPath] File to write manifest to. If omitted, - * manifest will be written to file it has been read from. - */ -AppxManifest.prototype.write = function(destPath) { - // sort Capability elements as per CB-5350 Windows8 build fails due to invalid 'Capabilities' definition - sortCapabilities(this.doc); - fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8'); -}; - -/** - * Sorts 'capabilities' elements in manifest in ascending order - * @param {Elementtree.Document} manifest An XML document that represents - * appxmanifest - */ -function sortCapabilities(manifest) { - - // removes namespace prefix (m3:Capability -> Capability) - // this is required since elementtree returns qualified name with namespace - function extractLocalName(tag) { - return tag.split(':').pop(); // takes last part of string after ':' - } - - var capabilitiesRoot = manifest.find('.//Capabilities'), - capabilities = capabilitiesRoot.getchildren() || []; - // to sort elements we remove them and then add again in the appropriate order - capabilities.forEach(function(elem) { // no .clear() method - capabilitiesRoot.remove(elem); - // CB-7601 we need local name w/o namespace prefix to sort capabilities correctly - elem.localName = extractLocalName(elem.tag); - }); - capabilities.sort(function(a, b) { - return (a.localName > b.localName) ? 1: -1; - }); - capabilities.forEach(function(elem) { - capabilitiesRoot.append(elem); - }); -} - - -function Win10AppxManifest(path) { - AppxManifest.call(this, path, /*prefix=*/'uap'); -} - -util.inherits(Win10AppxManifest, AppxManifest); - -Win10AppxManifest.prototype.getApplication = function () { - // Call overridden method - var result = AppxManifest.prototype.getApplication.call(this); - var application = result._node; - - result.getAccessRules = function () { - return application - .findall('./uap:ApplicationContentUriRules/uap:Rule') - .map(function (rule) { - return rule.attrib.Match; - }); - }; - - result.setAccessRules = function (rules) { - var appUriRules = application.find('./uap:ApplicationContentUriRules'); - if (appUriRules) { - application.remove(appUriRules); - } - - // No rules defined - if (!rules || rules.length === 0) { - return; - } - - appUriRules = new et.Element('uap:ApplicationContentUriRules'); - application.append(appUriRules); - - rules.forEach(function(rule) { - appUriRules.append(new et.Element('uap:Rule', { Match: rule, Type: 'include', WindowsRuntimeAccess: 'all' })); - }); - - return this; - }; - - return result; -}; - -Win10AppxManifest.prototype.getVisualElements = function () { - // Call base method and extend its results - var result = AppxManifest.prototype.getVisualElements.call(this); - var defaultTitle = result._node.find('./uap:DefaultTile'); - - result.getDefaultTitle = function () { - return { - getShortName: function () { - return defaultTitle.attrib.ShortName; - }, - setShortName: function (name) { - if (!name) throw new TypeError('Argument for "setDisplayName" must be defined in appxmanifest at ' + this.path); - defaultTitle.attrib.ShortName = name; - return this; - } - }; - }; - - // ToastCapable attribute was removed in Windows 10. - // See https://msdn.microsoft.com/ru-ru/library/windows/apps/dn423310.aspx - result.getToastCapable = function () {}; - result.setToastCapable = function () { return this; }; - - // ForegroundText was removed in Windows 10 as well - result.getForegroundText = function () {}; - result.setForegroundText = function () { return this; }; - - return result; -}; - -// Shortcut for multiple inner methods calls -Win10AppxManifest.prototype.setAppName = function (name) { - // Call base method - AppxManifest.prototype.setAppName.call(this, name); - this.getVisualElements().getDefaultTitle().setShortName(name); - - return this; -}; - -/** - * Checks for capabilities which are Restricted in Windows 10 UAP. - * @return {string[]|false} An array of restricted capability names, or false. - */ -Win10AppxManifest.prototype.getRestrictedCapabilities = function () { - var restrictedCapabilities = this.getCapabilities() - .filter(function (capability) { - return UAP_RESTRICTED_CAPS.indexOf(capability.name) >= 0; - }); - - return restrictedCapabilities.length === 0 ? false : restrictedCapabilities; -}; - -/** - * Sets up a Dependencies section for appxmanifest. If no arguments provided, - * deletes Dependencies section. - * - * @param {Object[]} dependencies Array of arbitrary object, which fields - * will be used to set each dependency attributes. - * - * @returns {Win10AppxManifest} self instance - */ -Win10AppxManifest.prototype.setDependencies = function (dependencies) { - var dependenciesElement = this.doc.find('./Dependencies'); - - if ((!dependencies || dependencies.length === 0) && dependenciesElement) { - this.doc.remove(dependenciesElement); - return this; - } - - if (!dependenciesElement) { - dependenciesElement = new et.Element('Dependencies'); - this.doc.append(dependenciesElement); - } - - if (dependenciesElement.len() > 0) { - dependenciesElement.clear(); - } - - dependencies.forEach(function (uapVersionInfo) { - dependenciesElement.append(new et.Element('TargetDeviceFamily', uapVersionInfo)); - }); -}; - -/** - * Writes manifest to disk syncronously. If filename is specified, then manifest - * will be written to that file - * - * @param {String} [destPath] File to write manifest to. If omitted, - * manifest will be written to file it has been read from. - */ -Win10AppxManifest.prototype.write = function(destPath) { - fs.writeFileSync(destPath || this.path, this.writeToString(), 'utf-8'); -}; - -Win10AppxManifest.prototype.writeToString = function() { - ensureUapPrefixedCapabilities(this.doc.find('.//Capabilities')); - ensureUniqueCapabilities(this.doc.find('.//Capabilities')); - // sort Capability elements as per CB-5350 Windows8 build fails due to invalid 'Capabilities' definition - sortCapabilities(this.doc); - return this.doc.write({indent: 4}); -}; - -/** - * Checks for capabilities which require the uap: prefix in Windows 10. - * @param capabilities {ElementTree.Element} The appx manifest element for <capabilities> - */ -function ensureUapPrefixedCapabilities(capabilities) { - capabilities.getchildren() - .forEach(function(el) { - if (CAPS_NEEDING_UAPNS.indexOf(el.attrib.Name) > -1 && el.tag.indexOf('uap:') !== 0) { - el.tag = 'uap:' + el.tag; - } - }); -} - -/** - * Cleans up duplicate capability declarations that were generated during the prepare process - * @param capabilities {ElementTree.Element} The appx manifest element for <capabilities> - */ -function ensureUniqueCapabilities(capabilities) { - var uniqueCapabilities = []; - capabilities.getchildren() - .forEach(function(el) { - var name = el.attrib.Name; - if (uniqueCapabilities.indexOf(name) !== -1) { - capabilities.remove(el); - } else { - uniqueCapabilities.push(name); - } - }); -} - -module.exports = AppxManifest; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6b98390e/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/ConfigChanges.js ---------------------------------------------------------------------- diff --git a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/ConfigChanges.js b/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/ConfigChanges.js deleted file mode 100644 index c07a77a..0000000 --- a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/ConfigChanges.js +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 util = require('util'); -var path = require('path'); -var CommonMunger = require('cordova-common').ConfigChanges.PlatformMunger; -var CapsNeedUapPrefix = require(path.join(__dirname, 'AppxManifest')).CapsNeedUapPrefix; - -var CAPS_SELECTOR = '/Package/Capabilities'; -var WINDOWS10_MANIFEST = 'package.windows10.appxmanifest'; - -function PlatformMunger(platform, project_dir, platformJson, pluginInfoProvider) { - CommonMunger.apply(this, arguments); -} - -util.inherits(PlatformMunger, CommonMunger); - -/** - * This is an override of apply_file_munge method from cordova-common's PlatformMunger class. - * In addition to parent's method logic also removes capabilities with 'uap:' prefix that were - * added by AppxManifest class - * - * @param {String} file A file name to apply munge to - * @param {Object} munge Serialized changes that need to be applied to the file - * @param {Boolean} [remove=false] Flag that specifies whether the changes - * need to be removed or added to the file - */ -PlatformMunger.prototype.apply_file_munge = function (file, munge, remove) { - - // Create a copy to avoid modification of original munge - var mungeCopy = cloneObject(munge); - var capabilities = mungeCopy.parents[CAPS_SELECTOR]; - - if (capabilities) { - // Add 'uap' prefixes for windows 10 manifest - if (file === WINDOWS10_MANIFEST) { - capabilities = generateUapCapabilities(capabilities); - } - - // Remove duplicates and sort capabilities when installing plugin - if (!remove) { - capabilities = getUniqueCapabilities(capabilities).sort(compareCapabilities); - } - - // Put back capabilities into munge's copy - mungeCopy.parents[CAPS_SELECTOR] = capabilities; - } - - PlatformMunger.super_.prototype.apply_file_munge.call(this, file, mungeCopy, remove); -}; - -// Recursive function to clone an object -function cloneObject(obj) { - if (obj === null || typeof obj !== 'object') { - return obj; - } - - var copy = obj.constructor(); - Object.keys(obj).forEach(function(key) { - copy[key] = cloneObject(obj[key]); - }); - - return copy; -} - -/** - * Retrieve capabality name from xml field - * @param {Object} capability with xml field like <Capability Name="CapabilityName"> - * @return {String} name of capability - */ -function getCapabilityName(capability) { - var reg = /Name\s*=\s*"(.*?)"/; - return capability.xml.match(reg)[1]; -} - -/** - * Remove capabilities with same names - * @param {Object} an array of capabilities - * @return {Object} an unique array of capabilities - */ -function getUniqueCapabilities(capabilities) { - return capabilities.reduce(function(uniqueCaps, currCap) { - - var isRepeated = uniqueCaps.some(function(cap) { - return getCapabilityName(cap) === getCapabilityName(currCap); - }); - - return isRepeated ? uniqueCaps : uniqueCaps.concat([currCap]); - }, []); -} - -/** - * Comparator function to pass to Array.sort - * @param {Object} firstCap first capability - * @param {Object} secondCap second capability - * @return {Number} either -1, 0 or 1 - */ -function compareCapabilities(firstCap, secondCap) { - var firstCapName = getCapabilityName(firstCap); - var secondCapName = getCapabilityName(secondCap); - - if (firstCapName < secondCapName) { - return -1; - } - - if (firstCapName > secondCapName) { - return 1; - } - - return 0; -} - - -/** - * Generates a new munge that contains <uap:Capability> elements created based on - * corresponding <Capability> elements from base munge. If there are no such elements - * found in base munge, the empty munge is returned (selectors might be present under - * the 'parents' key, but they will contain no changes). - * - * @param {Object} capabilities A list of capabilities - * @return {Object} A list with 'uap'-prefixed capabilities - */ -function generateUapCapabilities(capabilities) { - - function hasCapabilityChange(change) { - return /^\s*<(\w+:)?(Device)?Capability\s/.test(change.xml); - } - - function createPrefixedCapabilityChange(change) { - if (CapsNeedUapPrefix.indexOf(getCapabilityName(change)) < 0) { - return change; - } - - // If capability is already prefixed, avoid adding another prefix - var replaceXML = change.xml.indexOf('uap:') > 0 ? change.xml : change.xml.replace(/Capability/, 'uap:Capability'); - return { - xml: replaceXML, - count: change.count, - before: change.before - }; - } - - return capabilities - // For every xml change check if it adds a <Capability> element ... - .filter(hasCapabilityChange) - // ... and create a duplicate with 'uap:' prefix - .map(createPrefixedCapabilityChange); - -} - -exports.PlatformMunger = PlatformMunger; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6b98390e/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/JsprojManager.js ---------------------------------------------------------------------- diff --git a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/JsprojManager.js b/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/JsprojManager.js deleted file mode 100644 index 28b2fa3..0000000 --- a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/JsprojManager.js +++ /dev/null @@ -1,626 +0,0 @@ -/** - 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 */ - -/* - Helper for dealing with Windows Store JS app .jsproj files - */ - -var fs = require('fs'); -var et = require('elementtree'); -var path = require('path'); -var util = require('util'); -var semver = require('semver'); -var shell = require('shelljs'); -var AppxManifest = require('./AppxManifest'); -var PluginHandler = require('./PluginHandler'); -var events = require('cordova-common').events; -var CordovaError = require('cordova-common').CordovaError; -var xml_helpers = require('cordova-common').xmlHelpers; -var AppxManifest = require('./AppxManifest'); - -var WinCSharpProjectTypeGUID = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; // .csproj -var WinCplusplusProjectTypeGUID = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"; // .vcxproj - -// Match a JavaScript Project -var JSPROJ_REGEXP = /(Project\("\{262852C6-CD72-467D-83FE-5EEB1973A190}"\)\s*=\s*"[^"]+",\s*"[^"]+",\s*"\{[0-9a-f\-]+}"[^\r\n]*[\r\n]*)/gi; - -// Chars in a string that need to be escaped when used in a RegExp -var ESCAPE_REGEXP = /([.?*+\^$\[\]\\(){}|\-])/g; - -function jsprojManager(location) { - this.root = path.dirname(location); - this.isUniversalWindowsApp = path.extname(location).toLowerCase() === ".projitems"; - this.projects = []; - this.master = this.isUniversalWindowsApp ? new proj(location) : new jsproj(location); - this.projectFolder = path.dirname(location); - this.www = path.join(this.root, 'www'); - this.platformWww = path.join(this.root, 'platform_www'); -} - -function getProjectName(pluginProjectXML, relative_path) { - var projNameElt = pluginProjectXML.find("PropertyGroup/ProjectName"); - // Falling back on project file name in case ProjectName is missing - return !!projNameElt ? projNameElt.text : path.basename(relative_path, path.extname(relative_path)); -} - -jsprojManager.getProject = function (directory) { - var projectFiles = shell.ls(path.join(directory, '*.projitems')); - if (projectFiles.length === 0) { - throw (new CordovaError('The directory ' + directory + - ' does not appear to be a Unified Windows Store project (no .projitems file found)')); - } - return new jsprojManager(path.normalize(projectFiles[0])); -}; - -jsprojManager.prototype = { - _projects: null, - - getPackageName: function() { - // CB-10394 Do not cache manifest file while getting package name to avoid problems - // with windows.appxmanifest cached twice (here and in ConfigFile module) - return AppxManifest.get(path.join(this.root, 'package.windows.appxmanifest'), /*ignoreCache=*/true) - .getProperties().getDisplayName(); - }, - - write: function () { - this.master.write(); - if (this._projects) { - var that = this; - this._projects.forEach(function (project) { - if (project !== that.master && project.touched) { - project.write(); - } - }); - } - }, - - addSDKRef: function (incText, targetConditions) { - events.emit('verbose', 'jsprojManager.addSDKRef(incText: ' + incText + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - - var item = createItemGroupElement('ItemGroup/SDKReference', incText, targetConditions); - this._getMatchingProjects(targetConditions).forEach(function (project) { - project.appendToRoot(item); - }); - }, - - removeSDKRef: function (incText, targetConditions) { - events.emit('verbose', 'jsprojManager.removeSDKRef(incText: ' + incText + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - - this._getMatchingProjects(targetConditions).forEach(function (project) { - project.removeItemGroupElement('ItemGroup/SDKReference', incText, targetConditions); - }); - }, - - addResourceFileToProject: function (sourcePath, destPath, targetConditions) { - events.emit('verbose', 'jsprojManager.addResourceFile(sourcePath: ' + sourcePath + ', destPath: ' + destPath + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - - // add hint path with full path - var link = new et.Element('Link'); - link.text = destPath; - var children = [link]; - - var copyToOutputDirectory = new et.Element('CopyToOutputDirectory'); - copyToOutputDirectory.text = 'Always'; - children.push(copyToOutputDirectory); - - var item = createItemGroupElement('ItemGroup/Content', sourcePath, targetConditions, children); - - this._getMatchingProjects(targetConditions).forEach(function (project) { - project.appendToRoot(item); - }); - }, - - removeResourceFileFromProject: function (relPath, targetConditions) { - events.emit('verbose', 'jsprojManager.removeResourceFile(relPath: ' + relPath + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - this._getMatchingProjects(targetConditions).forEach(function (project) { - project.removeItemGroupElement('ItemGroup/Content', relPath, targetConditions); - }); - }, - - addReference: function (relPath, targetConditions, implPath) { - events.emit('verbose', 'jsprojManager.addReference(incText: ' + relPath + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - - // add hint path with full path - var hint_path = new et.Element('HintPath'); - hint_path.text = relPath; - var children = [hint_path]; - - var extName = path.extname(relPath); - if (extName === ".winmd") { - var mdFileTag = new et.Element("IsWinMDFile"); - mdFileTag.text = "true"; - children.push(mdFileTag); - } - - // We only need to add <Implementation> tag when dll base name differs from winmd name - if (implPath && path.basename(relPath, '.winmd') !== path.basename(implPath, '.dll')) { - var implementTag = new et.Element('Implementation'); - implementTag.text = path.basename(implPath); - children.push(implementTag); - } - - var item = createItemGroupElement('ItemGroup/Reference', path.basename(relPath, extName), targetConditions, children); - this._getMatchingProjects(targetConditions).forEach(function (project) { - project.appendToRoot(item); - }); - }, - - removeReference: function (relPath, targetConditions) { - events.emit('verbose', 'jsprojManager.removeReference(incText: ' + relPath + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - - var extName = path.extname(relPath); - var includeText = path.basename(relPath, extName); - - this._getMatchingProjects(targetConditions).forEach(function (project) { - project.removeItemGroupElement('ItemGroup/Reference', includeText, targetConditions); - }); - }, - - addSourceFile: function (relative_path) { - events.emit('verbose', 'jsprojManager.addSourceFile(relative_path: ' + relative_path + ')'); - this.master.addSourceFile(relative_path); - }, - - removeSourceFile: function (relative_path) { - events.emit('verbose', 'jsprojManager.removeSourceFile(incText: ' + relative_path + ')'); - this.master.removeSourceFile(relative_path); - }, - - addProjectReference: function (relative_path, targetConditions) { - events.emit('verbose', 'jsprojManager.addProjectReference(incText: ' + relative_path + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - - // relative_path is the actual path to the file in the current OS, where-as inserted_path is what we write in - // the project file, and is always in Windows format. - relative_path = path.normalize(relative_path); - var inserted_path = relative_path.split('/').join('\\'); - - var pluginProjectXML = xml_helpers.parseElementtreeSync(path.resolve(this.projectFolder, relative_path)); - - // find the guid + name of the referenced project - var projectGuid = pluginProjectXML.find("PropertyGroup/ProjectGuid").text; - var projName = getProjectName(pluginProjectXML, relative_path); - - // get the project type - var projectTypeGuid = getProjectTypeGuid(relative_path); - if (!projectTypeGuid) { - throw new CordovaError("Unrecognized project type at " + relative_path + " (not .csproj or .vcxproj)"); - } - - var preInsertText = "\tProjectSection(ProjectDependencies) = postProject\r\n" + - "\t\t" + projectGuid + "=" + projectGuid + "\r\n" + - "\tEndProjectSection\r\n"; - var postInsertText = '\r\nProject("' + projectTypeGuid + '") = "' + - projName + '", "' + inserted_path + '", ' + - '"' + projectGuid + '"\r\nEndProject'; - - var matchingProjects = this._getMatchingProjects(targetConditions); - if (matchingProjects.length === 0) { - // No projects meet the specified target and version criteria, so nothing to do. - return; - } - - // Will we be writing into the .projitems file rather than individual .jsproj files? - var useProjItems = this.isUniversalWindowsApp && matchingProjects.length === 1 && matchingProjects[0] === this.master; - - // There may be multiple solution files (for different VS versions) - process them all - getSolutionPaths(this.projectFolder).forEach(function (solutionPath) { - var solText = fs.readFileSync(solutionPath, {encoding: "utf8"}); - - if (useProjItems) { - // Insert a project dependency into every jsproj in the solution. - var jsProjectFound = false; - solText = solText.replace(JSPROJ_REGEXP, function (match) { - jsProjectFound = true; - return match + preInsertText; - }); - - if (!jsProjectFound) { - throw new CordovaError("No jsproj found in solution"); - } - } else { - // Insert a project dependency only for projects that match specified target and version - matchingProjects.forEach(function (project) { - solText = solText.replace(getJsProjRegExForProject(path.basename(project.location)), function (match) { - return match + preInsertText; - }); - }); - } - - // Add the project after existing projects. Note that this fairly simplistic check should be fine, since the last - // EndProject in the file should actually be an EndProject (and not an EndProjectSection, for example). - var pos = solText.lastIndexOf("EndProject"); - if (pos === -1) { - throw new Error("No EndProject found in solution"); - } - pos += 10; // Move pos to the end of EndProject text - solText = solText.slice(0, pos) + postInsertText + solText.slice(pos); - - fs.writeFileSync(solutionPath, solText, {encoding: "utf8"}); - }); - - // Add the ItemGroup/ProjectReference to each matching cordova project : - // <ItemGroup><ProjectReference Include="blahblah.csproj"/></ItemGroup> - var item = createItemGroupElement('ItemGroup/ProjectReference', inserted_path, targetConditions); - matchingProjects.forEach(function (project) { - project.appendToRoot(item); - }); - }, - - removeProjectReference: function (relative_path, targetConditions) { - events.emit('verbose', 'jsprojManager.removeProjectReference(incText: ' + relative_path + ', targetConditions: ' + JSON.stringify(targetConditions) + ')'); - - // relative_path is the actual path to the file in the current OS, where-as inserted_path is what we write in - // the project file, and is always in Windows format. - relative_path = path.normalize(relative_path); - var inserted_path = relative_path.split('/').join('\\'); - - // find the guid + name of the referenced project - var pluginProjectXML = xml_helpers.parseElementtreeSync(path.resolve(this.projectFolder, relative_path)); - var projectGuid = pluginProjectXML.find("PropertyGroup/ProjectGuid").text; - var projName = getProjectName(pluginProjectXML, relative_path); - - // get the project type - var projectTypeGuid = getProjectTypeGuid(relative_path); - if (!projectTypeGuid) { - throw new Error("Unrecognized project type at " + relative_path + " (not .csproj or .vcxproj)"); - } - - var preInsertTextRegExp = getProjectReferencePreInsertRegExp(projectGuid); - var postInsertTextRegExp = getProjectReferencePostInsertRegExp(projName, projectGuid, inserted_path, projectTypeGuid); - - // There may be multiple solutions (for different VS versions) - process them all - getSolutionPaths(this.projectFolder).forEach(function (solutionPath) { - var solText = fs.readFileSync(solutionPath, {encoding: "utf8"}); - - // To be safe (to handle subtle changes in formatting, for example), use a RegExp to find and remove - // preInsertText and postInsertText - - solText = solText.replace(preInsertTextRegExp, function () { - return ""; - }); - - solText = solText.replace(postInsertTextRegExp, function () { - return ""; - }); - - fs.writeFileSync(solutionPath, solText, {encoding: "utf8"}); - }); - - this._getMatchingProjects(targetConditions).forEach(function (project) { - project.removeItemGroupElement('ItemGroup/ProjectReference', inserted_path, targetConditions); - }); - }, - - _getMatchingProjects: function (targetConditions) { - // If specified, target can be 'all' (default), 'phone' or 'windows'. Ultimately should probably allow a comma - // separated list, but not needed now. - var target = getDeviceTarget(targetConditions); - var versions = getVersions(targetConditions); - - if (target || versions) { - var matchingProjects = this.projects.filter(function (project) { - return (!target || target === project.target) && - (!versions || semver.satisfies(project.getSemVersion(), versions, /* loose */ true)); - }); - - if (matchingProjects.length < this.projects.length) { - return matchingProjects; - } - } - - // All projects match. If this is a universal project, return the projitems file. Otherwise return our single - // project. - return [this.master]; - }, - - get projects() { - var projects = this._projects; - if (!projects) { - projects = []; - this._projects = projects; - - if (this.isUniversalWindowsApp) { - var projectPath = this.projectFolder; - var projectFiles = shell.ls(path.join(projectPath, '*.jsproj')); - projectFiles.forEach(function (projectFile) { - projects.push(new jsproj(projectFile)); - }); - } else { - this.projects.push(this.master); - } - } - - return projects; - } -}; - -jsprojManager.prototype.getInstaller = function (type) { - return PluginHandler.getInstaller(type); -}; - -jsprojManager.prototype.getUninstaller = function (type) { - return PluginHandler.getUninstaller(type); -}; - -function getProjectReferencePreInsertRegExp(projectGuid) { - projectGuid = escapeRegExpString(projectGuid); - return new RegExp("\\s*ProjectSection\\(ProjectDependencies\\)\\s*=\\s*postProject\\s*" + projectGuid + "\\s*=\\s*" + projectGuid + "\\s*EndProjectSection", "gi"); -} - -function getProjectReferencePostInsertRegExp(projName, projectGuid, relative_path, projectTypeGuid) { - projName = escapeRegExpString(projName); - projectGuid = escapeRegExpString(projectGuid); - relative_path = escapeRegExpString(relative_path); - projectTypeGuid = escapeRegExpString(projectTypeGuid); - return new RegExp('\\s*Project\\("' + projectTypeGuid + '"\\)\\s*=\\s*"' + projName + '"\\s*,\\s*"' + relative_path + '"\\s*,\\s*"' + projectGuid + '"\\s*EndProject', 'gi'); -} - -function getSolutionPaths(projectFolder) { - return shell.ls(path.join(projectFolder, "*.sln")); -} - -function escapeRegExpString(regExpString) { - return regExpString.replace(ESCAPE_REGEXP, "\\$1"); -} - -function getJsProjRegExForProject(projectFile) { - projectFile = escapeRegExpString(projectFile); - return new RegExp('(Project\\("\\{262852C6-CD72-467D-83FE-5EEB1973A190}"\\)\\s*=\\s*"[^"]+",\\s*"' + projectFile + '",\\s*"\\{[0-9a-f\\-]+}"[^\\r\\n]*[\\r\\n]*)', 'gi'); -} - -function getProjectTypeGuid(projectPath) { - switch (path.extname(projectPath)) { - case ".vcxproj": - return WinCplusplusProjectTypeGUID; - - case ".csproj": - return WinCSharpProjectTypeGUID; - } - return null; -} - -function createItemGroupElement(path, incText, targetConditions, children) { - path = path.split('/'); - path.reverse(); - - var lastElement = null; - path.forEach(function (elementName) { - var element = new et.Element(elementName); - if (lastElement) { - element.append(lastElement); - } else { - element.attrib.Include = incText; - - var condition = createConditionAttrib(targetConditions); - if (condition) { - element.attrib.Condition = condition; - } - - if (children) { - children.forEach(function (child) { - element.append(child); - }); - } - } - lastElement = element; - }); - - return lastElement; -} - -function getDeviceTarget(targetConditions) { - var target = targetConditions.deviceTarget; - if (target) { - target = target.toLowerCase().trim(); - if (target === "all") { - target = null; - } else if (target === "win") { - // Allow "win" as alternative to "windows" - target = "windows"; - } else if (target !== 'phone' && target !== 'windows') { - throw new Error('Invalid device-target attribute (must be "all", "phone", "windows" or "win"): ' + target); - } - } - return target; -} - -function getVersions(targetConditions) { - var versions = targetConditions.versions; - if (versions && !semver.validRange(versions, /* loose */ true)) { - throw new Error('Invalid versions attribute (must be a valid semantic version range): ' + versions); - } - return versions; -} - - -/* proj */ - -function proj(location) { - // Class to handle simple project xml operations - if (!location) { - throw new Error('Project file location can\'t be null or empty'); - } - this.location = location; - this.xml = xml_helpers.parseElementtreeSync(location); -} - -proj.prototype = { - write: function () { - fs.writeFileSync(this.location, this.xml.write({indent: 4}), 'utf-8'); - }, - - appendToRoot: function (element) { - this.touched = true; - this.xml.getroot().append(element); - }, - - removeItemGroupElement: function (path, incText, targetConditions) { - var xpath = path + '[@Include="' + incText + '"]'; - var condition = createConditionAttrib(targetConditions); - if (condition) { - xpath += '[@Condition="' + condition + '"]'; - } - xpath += '/..'; - - var itemGroup = this.xml.find(xpath); - if (itemGroup) { - this.touched = true; - this.xml.getroot().remove(itemGroup); - } - }, - - addSourceFile: function (relative_path) { - // we allow multiple paths to be passed at once as array so that - // we don't create separate ItemGroup for each source file, CB-6874 - if (!(relative_path instanceof Array)) { - relative_path = [relative_path]; - } - - // make ItemGroup to hold file. - var item = new et.Element('ItemGroup'); - - relative_path.forEach(function (filePath) { - // filePath is never used to find the actual file - it determines what we write to the project file, and so - // should always be in Windows format. - filePath = filePath.split('/').join('\\'); - - var content = new et.Element('Content'); - content.attrib.Include = filePath; - item.append(content); - }); - - this.appendToRoot(item); - }, - - removeSourceFile: function (relative_path) { - var isRegexp = relative_path instanceof RegExp; - if (!isRegexp) { - // relative_path is never used to find the actual file - it determines what we write to the project file, - // and so should always be in Windows format. - relative_path = relative_path.split('/').join('\\'); - } - - var root = this.xml.getroot(); - var that = this; - // iterate through all ItemGroup/Content elements and remove all items matched - this.xml.findall('ItemGroup').forEach(function (group) { - // matched files in current ItemGroup - var filesToRemove = group.findall('Content').filter(function (item) { - if (!item.attrib.Include) { - return false; - } - return isRegexp ? item.attrib.Include.match(relative_path) : item.attrib.Include === relative_path; - }); - - // nothing to remove, skip.. - if (filesToRemove.length < 1) { - return; - } - - filesToRemove.forEach(function (file) { - // remove file reference - group.remove(file); - }); - // remove ItemGroup if empty - if (group.findall('*').length < 1) { - that.touched = true; - root.remove(group); - } - }); - } -}; - - -/* jsproj */ - -function jsproj(location) { - function targetPlatformIdentifierToDevice(jsprojPlatform) { - var index = ["Windows", "WindowsPhoneApp", "UAP"].indexOf(jsprojPlatform); - if (index < 0) { - throw new Error("Unknown TargetPlatformIdentifier '" + jsprojPlatform + "' in project file '" + location + "'"); - } - return ["windows", "phone", "windows"][index]; - } - - function validateVersion(version) { - version = version.split('.'); - while (version.length < 3) { - version.push("0"); - } - return version.join("."); - } - - // Class to handle a jsproj file - proj.call(this, location); - - var propertyGroup = this.xml.find('PropertyGroup[TargetPlatformIdentifier]'); - if (!propertyGroup) { - throw new Error("Unable to find PropertyGroup/TargetPlatformIdentifier in project file '" + this.location + "'"); - } - - var jsprojPlatform = propertyGroup.find('TargetPlatformIdentifier').text; - this.target = targetPlatformIdentifierToDevice(jsprojPlatform); - - var version = propertyGroup.find('TargetPlatformVersion'); - if (!version) { - throw new Error("Unable to find PropertyGroup/TargetPlatformVersion in project file '" + this.location + "'"); - } - this.version = validateVersion(version.text); -} - -util.inherits(jsproj, proj); - -jsproj.prototype.target = null; -jsproj.prototype.version = null; - -// Returns valid semantic version (http://semver.org/). -jsproj.prototype.getSemVersion = function () { - // For example, for version 10.0.10240.0 we will return 10.0.10240 (first three components) - var semVersion = this.version; - var splittedVersion = semVersion.split('.'); - if (splittedVersion.length > 3) { - semVersion = splittedVersion.splice(0, 3).join('.'); - } - - return semVersion; - // Alternative approach could be replacing last dot with plus sign to - // be complaint w/ semver specification, for example - // 10.0.10240.0 -> 10.0.10240+0 -}; - -/* Common support functions */ - -function createConditionAttrib(targetConditions) { - var arch = targetConditions.arch; - if (arch) { - if (arch === "arm") { - // Specifcally allow "arm" as alternative to "ARM" - arch = "ARM"; - } else if (arch !== "x86" && arch !== "x64" && arch !== "ARM") { - throw new Error('Invalid arch attribute (must be "x86", "x64" or "ARM"): ' + arch); - } - return "'$(Platform)'=='" + arch + "'"; - } - return null; -} - - -module.exports = jsprojManager; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6b98390e/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginHandler.js ---------------------------------------------------------------------- diff --git a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginHandler.js b/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginHandler.js deleted file mode 100644 index c264f20..0000000 --- a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginHandler.js +++ /dev/null @@ -1,282 +0,0 @@ -/* - * - * Copyright 2013 Jesse MacFadyen - * - * 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 shell = require('shelljs'); -var events = require('cordova-common').events; -var CordovaError = require('cordova-common').CordovaError; - -// returns relative file path for a file in the plugin's folder that can be referenced -// from a project file. -function getPluginFilePath(plugin, pluginFile, targetDir) { - var src = path.resolve(plugin.dir, pluginFile); - return '$(ProjectDir)' + path.relative(targetDir, src); -} - -var handlers = { - 'source-file': { - install:function(obj, plugin, project, options) { - var dest = path.join('plugins', plugin.id, obj.targetDir || '', path.basename(obj.src)); - if (options && options.force) { - copyFile(plugin.dir, obj.src, project.root, dest); - } else { - copyNewFile(plugin.dir, obj.src, project.root, dest); - } - // add reference to this file to jsproj. - project.addSourceFile(dest); - }, - uninstall:function(obj, plugin, project, options) { - var dest = path.join('plugins', plugin.id, obj.targetDir || '', path.basename(obj.src)); - removeFile(project.root, dest); - // remove reference to this file from csproj. - project.removeSourceFile(dest); - } - }, - 'resource-file':{ - install:function(obj, plugin, project, options) { - if (obj.reference) { - // do not copy, but reference the file in the plugin folder. This allows to - // have multiple source files map to the same target and select the appropriate - // one based on the current build settings, e.g. architecture. - // also, we don't check for existence. This allows to insert build variables - // into the source file name, e.g. - // <resource-file src="$(Platform)/My.dll" target="My.dll" /> - var relativeSrcPath = getPluginFilePath(plugin, obj.src, project.projectFolder); - project.addResourceFileToProject(relativeSrcPath, obj.target, getTargetConditions(obj)); - } else { - // if target already exists, emit warning to consider using a reference instead of copying - if (fs.existsSync(path.resolve(project.root, obj.target))) { - events.emit('warn', '<resource-file> with target ' + obj.target + ' already exists and will be overwritten ' + - 'by a <resource-file> with the same target. Consider using the attribute reference="true" in the ' + - '<resource-file> tag to avoid overwriting files with the same target. Using reference will not copy files ' + - 'to the destination, instead will create a reference to the source path.'); - } - // as per specification resource-file target is specified relative to platform root - copyFile(plugin.dir, obj.src, project.root, obj.target); - project.addResourceFileToProject(obj.target, obj.target, getTargetConditions(obj)); - } - }, - uninstall:function(obj, plugin, project, options) { - if (obj.reference) { - var relativeSrcPath = getPluginFilePath(plugin, obj.src, project.projectFolder); - project.removeResourceFileFromProject(relativeSrcPath, getTargetConditions(obj)); - } else { - removeFile(project.root, obj.target); - project.removeResourceFileFromProject(obj.target, getTargetConditions(obj)); - } - } - }, - 'lib-file': { - install:function(obj, plugin, project, options) { - var inc = obj.Include || obj.src; - project.addSDKRef(inc, getTargetConditions(obj)); - }, - uninstall:function(obj, plugin, project, options) { - events.emit('verbose', 'windows lib-file uninstall :: ' + plugin.id); - var inc = obj.Include || obj.src; - project.removeSDKRef(inc, getTargetConditions(obj)); - } - }, - 'framework': { - install:function(obj, plugin, project, options) { - events.emit('verbose', 'windows framework install :: ' + plugin.id); - - var src = obj.src; - var dest = src; - var type = obj.type; - var targetDir = obj.targetDir || ''; - var implementPath = obj.implementation; - - if(type === 'projectReference') { - dest = path.join(path.relative(project.projectFolder, plugin.dir), targetDir, src); - project.addProjectReference(dest, getTargetConditions(obj)); - } else { - // path.join ignores empty paths passed so we don't check whether targetDir is not empty - dest = path.join('plugins', plugin.id, targetDir, path.basename(src)); - copyFile(plugin.dir, src, project.root, dest); - if (implementPath) { - copyFile(plugin.dir, implementPath, project.root, path.join(path.dirname(dest), path.basename(implementPath))); - } - project.addReference(dest, getTargetConditions(obj), implementPath); - } - - }, - uninstall:function(obj, plugin, project, options) { - events.emit('verbose', 'windows framework uninstall :: ' + plugin.id ); - - var src = obj.src; - var type = obj.type; - - if(type === 'projectReference') { - project.removeProjectReference(path.join(path.relative(project.projectFolder, plugin.dir), src), getTargetConditions(obj)); - } - else { - var targetPath = path.join('plugins', plugin.id); - removeFile(project.root, targetPath); - project.removeReference(src, getTargetConditions(obj)); - } - } - }, - asset:{ - install:function(obj, plugin, project, options) { - if (!obj.src) { - throw new CordovaError(generateAttributeError('src', 'asset', plugin.id)); - } - if (!obj.target) { - throw new CordovaError(generateAttributeError('target', 'asset', plugin.id)); - } - - copyFile(plugin.dir, obj.src, project.www, obj.target); - if (options && options.usePlatformWww) copyFile(plugin.dir, obj.src, project.platformWww, obj.target); - }, - uninstall:function(obj, plugin, project, options) { - var target = obj.target || obj.src; - - if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id)); - - removeFile(project.www, target); - removeFile(project.www, path.join('plugins', plugin.id)); - if (options && options.usePlatformWww) { - removeFile(project.platformWww, target); - removeFile(project.platformWww, path.join('plugins', plugin.id)); - } - } - }, - 'js-module': { - install: function (obj, plugin, project, options) { - // Copy the plugin's files into the www directory. - var moduleSource = path.resolve(plugin.dir, obj.src); - var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src))); - - // Read in the file, prepend the cordova.define, and write it back out. - var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM - if (moduleSource.match(/.*\.json$/)) { - scriptContent = 'module.exports = ' + scriptContent; - } - scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n'; - - var moduleDestination = path.resolve(project.www, 'plugins', plugin.id, obj.src); - shell.mkdir('-p', path.dirname(moduleDestination)); - fs.writeFileSync(moduleDestination, scriptContent, 'utf-8'); - if (options && options.usePlatformWww) { - var platformWwwDestination = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src); - shell.mkdir('-p', path.dirname(platformWwwDestination)); - fs.writeFileSync(platformWwwDestination, scriptContent, 'utf-8'); - } - }, - uninstall: function (obj, plugin, project, options) { - var pluginRelativePath = path.join('plugins', plugin.id, obj.src); - removeFileAndParents(project.www, pluginRelativePath); - if (options && options.usePlatformWww) removeFileAndParents(project.platformWww, pluginRelativePath); - } - } -}; - -// Helpers from common - -module.exports.getInstaller = function (type) { - if (handlers[type] && handlers[type].install) { - return handlers[type].install; - } - - events.emit('verbose', '<' + type + '> is not supported for Windows plugins'); -}; - -module.exports.getUninstaller = function(type) { - if (handlers[type] && handlers[type].uninstall) { - return handlers[type].uninstall; - } - - events.emit('verbose', '<' + type + '> is not supported for Windows plugins'); -}; - -function getTargetConditions(obj) { - return { versions: obj.versions, deviceTarget: obj.deviceTarget, arch: obj.arch }; -} - -function copyFile (plugin_dir, src, project_dir, dest, link) { - src = path.resolve(plugin_dir, src); - if (!fs.existsSync(src)) throw new CordovaError('"' + src + '" not found!'); - - // check that src path is inside plugin directory - var real_path = fs.realpathSync(src); - var real_plugin_path = fs.realpathSync(plugin_dir); - if (real_path.indexOf(real_plugin_path) !== 0) - throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); - - dest = path.resolve(project_dir, dest); - - // check that dest path is located in project directory - if (dest.indexOf(path.resolve(project_dir)) !== 0) - throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); - - shell.mkdir('-p', path.dirname(dest)); - - if (link) { - fs.symlinkSync(path.relative(path.dirname(dest), src), dest); - } else if (fs.statSync(src).isDirectory()) { - // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq - shell.cp('-Rf', src+'/*', dest); - } else { - shell.cp('-f', src, dest); - } -} - -// Same as copy file but throws error if target exists -function copyNewFile (plugin_dir, src, project_dir, dest, link) { - var target_path = path.resolve(project_dir, dest); - if (fs.existsSync(target_path)) - throw new CordovaError('"' + target_path + '" already exists!'); - - copyFile(plugin_dir, src, project_dir, dest, !!link); -} - -// checks if file exists and then deletes. Error if doesn't exist -function removeFile (project_dir, src) { - var file = path.resolve(project_dir, src); - shell.rm('-Rf', file); -} - -function removeFileAndParents (baseDir, destFile, stopper) { - stopper = stopper || '.'; - var file = path.resolve(baseDir, destFile); - if (!fs.existsSync(file)) return; - - shell.rm('-rf', file); - - // check if directory is empty - var curDir = path.dirname(file); - - while(curDir !== path.resolve(baseDir, stopper)) { - if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) { - fs.rmdirSync(curDir); - curDir = path.resolve(curDir, '..'); - } else { - // directory not empty...do nothing - break; - } - } -} - -function generateAttributeError(attribute, element, id) { - return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id; -} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/6b98390e/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginInfo.js ---------------------------------------------------------------------- diff --git a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginInfo.js b/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginInfo.js deleted file mode 100644 index 78c9593..0000000 --- a/spec-cordova/fixtures/projects/platformApi/platforms/windows/cordova/lib/PluginInfo.js +++ /dev/null @@ -1,139 +0,0 @@ -/* - 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 semver = require('semver'); -var CommonPluginInfo = require('cordova-common').PluginInfo; - -var MANIFESTS = { - 'windows': { - '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.1.0': ['package.windows.appxmanifest', 'package.phone.appxmanifest'], - '10.0.0': 'package.windows10.appxmanifest' - } -}; - -var SUBSTS = ['package.phone.appxmanifest', 'package.windows.appxmanifest', 'package.windows10.appxmanifest']; -var TARGETS = ['windows', 'phone', 'all']; - -function processChanges(changes) { - var hasManifestChanges = changes.some(function(change) { - return change.target === 'package.appxmanifest'; - }); - - if (!hasManifestChanges) { - return changes; - } - - // Demux 'package.appxmanifest' into relevant platform-specific appx manifests. - // Only spend the cycles if there are version-specific plugin settings - var oldChanges = changes; - changes = []; - - oldChanges.forEach(function(change) { - // 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 manifestsForChange = getManifestsForChange(change); - changes = changes.concat(demuxChangeWithSubsts(change, manifestsForChange)); - }); - - return changes; -} - -function demuxChangeWithSubsts(change, manifestFiles) { - return manifestFiles.map(function(file) { - return createReplacement(file, change); - }); -} - -function getManifestsForChange(change) { - var hasTarget = (typeof change.deviceTarget !== 'undefined'); - var hasVersion = (typeof change.versions !== 'undefined'); - - var targetDeviceSet = hasTarget ? change.deviceTarget : 'all'; - - if (TARGETS.indexOf(targetDeviceSet) === -1) { - // target-device couldn't be resolved, fix it up here to a valid value - targetDeviceSet = 'all'; - } - - // No semver/device-target for this config-file, pass it through - if (!(hasTarget || hasVersion)) { - return SUBSTS; - } - - var knownWindowsVersionsForTargetDeviceSet = Object.keys(MANIFESTS[targetDeviceSet]); - return knownWindowsVersionsForTargetDeviceSet.reduce(function(manifestFiles, winver) { - if (hasVersion && !semver.satisfies(winver, change.versions)) { - return manifestFiles; - } - return manifestFiles.concat(MANIFESTS[targetDeviceSet][winver]); - }, []); -} - -// This is a local function that creates the new replacement representing the -// mutation. Used to save code further down. -function createReplacement(manifestFile, originalChange) { - var replacement = { - target: manifestFile, - parent: originalChange.parent, - after: originalChange.after, - xmls: originalChange.xmls, - versions: originalChange.versions, - deviceTarget: originalChange.deviceTarget - }; - return replacement; -} - - -/* -A class for holidng the information currently stored in plugin.xml -It's inherited from cordova-common's PluginInfo class -In addition it overrides getConfigFiles, getEditConfigs, getFrameworks methods to use windows-specific logic - */ -function PluginInfo(dirname) { - // We're not using `util.inherit' because original PluginInfo defines - // its' methods inside of constructor - CommonPluginInfo.apply(this, arguments); - var parentGetConfigFiles = this.getConfigFiles; - var parentGetEditConfigs = this.getEditConfigs; - - this.getEditConfigs = function(platform) { - var editConfigs = parentGetEditConfigs(platform); - return processChanges(editConfigs); - }; - - this.getConfigFiles = function(platform) { - var configFiles = parentGetConfigFiles(platform); - return processChanges(configFiles); - }; -} - -exports.PluginInfo = PluginInfo; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
