| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| var Q = require('q'); |
| var fs = require('fs'); |
| var path = require('path'); |
| var shell = require('shelljs'); |
| var et = require('elementtree'); |
| var Version = require('./Version'); |
| var MRTImage = require('./MRTImage'); |
| var AppxManifest = require('./AppxManifest'); |
| var MSBuildTools = require('./MSBuildTools'); |
| var ConfigParser = require('./ConfigParser'); |
| var events = require('cordova-common').events; |
| var xmlHelpers = require('cordova-common').xmlHelpers; |
| var FileUpdater = require('cordova-common').FileUpdater; |
| var PlatformJson = require('cordova-common').PlatformJson; |
| var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger; |
| var PluginInfoProvider = require('cordova-common').PluginInfoProvider; |
| |
| // Default value for VisualElements' Description attribute. |
| // This is equal to the value that comes with default App template |
| var DEFAULT_DESCRIPTION = 'CordovaApp'; |
| |
| var PROJECT_WINDOWS10 = 'CordovaApp.Windows10.jsproj', |
| MANIFEST_WINDOWS = 'package.windows.appxmanifest', |
| MANIFEST_PHONE = 'package.phone.appxmanifest', |
| MANIFEST_WINDOWS10 = 'package.windows10.appxmanifest'; |
| |
| var TEMPLATE = |
| '<?xml version="1.0" encoding="utf-8"?>\n' + |
| '<!--\n This file is automatically generated.\n' + |
| ' Do not modify this file - YOUR CHANGES WILL BE ERASED!\n-->\n'; |
| |
| /** Note: this is only for backward compatibility, since it is being called directly from windows_parser */ |
| module.exports.applyPlatformConfig = function() { |
| var projectRoot = path.join(__dirname, '../..'); |
| var appConfig = new ConfigParser(path.join(projectRoot, '../../config.xml')); |
| updateProjectAccordingTo(appConfig); |
| copyImages(appConfig, projectRoot); |
| }; |
| |
| module.exports.updateBuildConfig = function(buildConfig) { |
| var projectRoot = path.join(__dirname, '../..'); |
| var config = new ConfigParser(path.join(projectRoot, 'config.xml')); |
| |
| // if no buildConfig is provided dont do anything |
| buildConfig = buildConfig || {}; |
| |
| // Merge buildConfig with config |
| for (var attr in buildConfig) { |
| config[attr] = buildConfig[attr]; |
| } |
| |
| var root = new et.Element('Project'); |
| root.set('xmlns', 'http://schemas.microsoft.com/developer/msbuild/2003'); |
| var buildConfigXML = new et.ElementTree(root); |
| var propertyGroup = new et.Element('PropertyGroup'); |
| var itemGroup = new et.Element('ItemGroup'); |
| |
| // Append PropertyGroup and ItemGroup |
| buildConfigXML.getroot().append(propertyGroup); |
| buildConfigXML.getroot().append(itemGroup); |
| |
| // packageCertificateKeyFile - defaults to 'CordovaApp_TemporaryKey.pfx' |
| var packageCertificateKeyFile = config.packageCertificateKeyFile || 'CordovaApp_TemporaryKey.pfx'; |
| |
| if (config.packageCertificateKeyFile) { |
| // Convert packageCertificateKeyFile from absolute to relative path |
| packageCertificateKeyFile = path.relative(projectRoot, packageCertificateKeyFile); |
| } |
| |
| var certificatePropertyElement = new et.Element('PackageCertificateKeyFile'); |
| certificatePropertyElement.text = packageCertificateKeyFile; |
| propertyGroup.append(certificatePropertyElement); |
| |
| var certificateItemElement = new et.Element('None', { 'Include': packageCertificateKeyFile }); |
| itemGroup.append(certificateItemElement); |
| |
| // packageThumbprint |
| if (config.packageThumbprint) { |
| var thumbprintElement = new et.Element('PackageCertificateThumbprint'); |
| thumbprintElement.text = config.packageThumbprint; |
| propertyGroup.append(thumbprintElement); |
| } |
| |
| // DefaultLanguage - defaults to 'en-US' |
| var defaultLocale = config.defaultLocale() || 'en-US'; |
| var defaultLocaleElement = new et.Element('DefaultLanguage'); |
| defaultLocaleElement.text = defaultLocale; |
| propertyGroup.append(defaultLocaleElement); |
| |
| var buildConfigFileName = buildConfig.buildType === 'release' ? |
| path.join(projectRoot, 'CordovaAppRelease.projitems') : |
| path.join(projectRoot, 'CordovaAppDebug.projitems'); |
| |
| fs.writeFileSync(buildConfigFileName, TEMPLATE + buildConfigXML.write({indent: 2, xml_declaration: false}), 'utf-8'); |
| }; |
| |
| function updateManifestFile (config, manifestPath) { |
| var manifest = AppxManifest.get(manifestPath); |
| // Break out Windows 10-specific functionality because we also need to |
| // apply UAP versioning to Windows 10 appx-manifests. |
| var isTargetingWin10 = manifest.prefix === 'uap:'; |
| |
| applyCoreProperties(config, manifest); |
| applyStartPage(config, manifest, isTargetingWin10); |
| |
| if (isTargetingWin10) { |
| applyNavigationWhitelist(config, manifest); |
| } else { |
| applyAccessRules(config, manifest); |
| } |
| |
| // Apply background color, splashscreen background color, etc. |
| manifest.getVisualElements() |
| .trySetBackgroundColor(config.getPreference('BackgroundColor')) |
| .setSplashBackgroundColor(config.getPreference('SplashScreenBackgroundColor')) |
| .setToastCapable(config.getPreference('WindowsToastCapable')) |
| .setOrientation(config.getPreference('Orientation')); |
| |
| if (isTargetingWin10) { |
| manifest.setDependencies(config.getAllMinMaxUAPVersions()); |
| |
| var badCaps = manifest.getRestrictedCapabilities(); |
| if (config.hasRemoteUris() && badCaps) { |
| events.emit('warn', 'The following Capabilities were declared and are restricted:' + |
| '\n\t' + badCaps.map(function(a){return a.name;}).join(', ') + |
| '\nYou will be unable to on-board your app to the public Windows Store with these ' + |
| 'capabilities and access rules permitting access to remote URIs.'); |
| } |
| } |
| |
| //Write out manifest |
| manifest.write(); |
| } |
| |
| function applyCoreProperties(config, manifest) { |
| // CB-9450: iOS/Android and Windows Store have an incompatibility here; Windows Store assigns the |
| // package name that should be used for upload to the store. However, this can't be set for typical |
| // Cordova apps. So, we have to create a Windows-specific preference here. |
| var pkgName = config.getPreference('WindowsStoreIdentityName') || config.packageName(); |
| if (pkgName) { |
| manifest.getIdentity().setName(pkgName); |
| } |
| |
| var version = config.windows_packageVersion() || config.version(); |
| if (version) { |
| manifest.getIdentity().setVersion(version); |
| } |
| |
| // Update publisher id (identity) |
| if (config.publisherId) { |
| manifest.getIdentity().setPublisher(config.publisherId); |
| } |
| |
| // Update name (windows8 has it in the Application[@Id] and Application.VisualElements[@DisplayName]) |
| var baselinePackageName = config.packageName(); |
| if (baselinePackageName) { |
| manifest.getApplication().setId(baselinePackageName); |
| } |
| |
| var name = config.name(); |
| if (name) { |
| manifest.getVisualElements().setDisplayName(name); |
| } |
| |
| var description = config.description(); |
| manifest.getProperties().setDescription(description); |
| // 'Description' attribute is required for VisualElements node (see |
| // https://msdn.microsoft.com/en-us/library/windows/apps/br211471.aspx), |
| // so we set it to '<description>' from config.xml or default value |
| manifest.getVisualElements().setDescription(description || DEFAULT_DESCRIPTION); |
| |
| // CB-9410: Get a display name and publisher display name. In the Windows Store, certain |
| // strings which are typically used in Cordova aren't valid for Store ingestion. |
| // Here, we check for Windows-specific preferences, and if we find it, prefer that over |
| // the Cordova <widget> areas. |
| var displayName = config.getPreference('WindowsStoreDisplayName') || name; |
| var publisherName = config.getPreference('WindowsStorePublisherName') || config.author(); |
| |
| // Update properties |
| manifest.getProperties() |
| .setDisplayName(displayName) |
| .setPublisherDisplayName(publisherName); |
| } |
| |
| function applyStartPage(config, manifest, targetingWin10) { |
| // If not specified, set default value |
| // http://cordova.apache.org/docs/en/edge/config_ref_index.md.html#The%20config.xml%20File |
| var startPage = config.startPage() || 'index.html'; |
| |
| var uriPrefix = ''; |
| if (targetingWin10) { |
| // for Win10, we respect config options such as WindowsDefaultUriPrefix and default to |
| // ms-appx-web:// as the homepage. Set those here. |
| |
| // Only add a URI prefix if the start page doesn't specify a URI scheme |
| if (!(/^[\w-]+?\:\/\//i).test(startPage)) { |
| uriPrefix = config.getPreference('WindowsDefaultUriPrefix'); |
| if (!uriPrefix) { |
| uriPrefix = 'ms-appx-web://'; |
| } |
| else if (/^ms\-appx\:\/\/$/i.test(uriPrefix)) { |
| // Explicitly ignore the ms-appx:// scheme because it doesn't validate |
| // in the Windows 10 build schema (treat it as the root). |
| uriPrefix = ''; |
| } |
| } |
| } |
| |
| var startPagePrefix = 'www/'; |
| if ((uriPrefix && uriPrefix.toLowerCase().substring(0, 4) === 'http') || |
| startPage.toLowerCase().substring(0, 4) === 'http') { |
| startPagePrefix = ''; |
| } |
| else if (uriPrefix.toLowerCase().substring(0, 7) === 'ms-appx') { |
| var pkgName = config.getPreference('WindowsStoreIdentityName') || config.packageName(); |
| // Workaround to avoid WWAHost.exe bug: https://issues.apache.org/jira/browse/CB-10446 |
| uriPrefix += pkgName.toLowerCase() + '/'; // add Identity.Name |
| } |
| |
| manifest.getApplication().setStartPage(uriPrefix + startPagePrefix + startPage); |
| } |
| |
| function applyAccessRules (config, manifest) { |
| var accessRules = config.getAccesses() |
| .filter(function(rule) { |
| // https:// rules are always good, * rules are always good |
| if (rule.origin.indexOf('https://') === 0 || rule.origin === '*') return true; |
| |
| events.emit('warn', 'Access rules must begin with "https://", the following rule will be ignored: ' + rule.origin); |
| return false; |
| }).map(function (rule) { |
| return rule.origin; |
| }); |
| |
| // If * is specified, emit no access rules. |
| if (accessRules.indexOf('*') > -1) { |
| accessRules = []; |
| } |
| |
| manifest.getApplication().setAccessRules(accessRules); |
| } |
| |
| /** |
| * Windows 10-based whitelist-plugin-compatible support for the enhanced navigation whitelist. |
| * Allows WinRT access to origins specified by <allow-navigation href="origin" /> elements. |
| */ |
| function applyNavigationWhitelist(config, manifest) { |
| |
| if (manifest.prefix !== 'uap:') { |
| // This never should happen, but to be sure let's check |
| throw new Error('AllowNavigation whitelist rules must be applied to Windows 10 appxmanifest only.'); |
| } |
| |
| var UriSchemeTest = /^(?:https?|ms-appx-web):\/\//i; |
| |
| var whitelistRules = config.getAllowNavigations() |
| .filter(function(rule) { |
| if (UriSchemeTest.test(rule.href)) return true; |
| |
| events.emit('warn', 'The following navigation rule had an invalid URI scheme and will be ignored: "' + rule.href + '".'); |
| return false; |
| }) |
| .map(function (rule) { |
| return rule.href; |
| }); |
| |
| var defaultPrefix = config.getPreference('WindowsDefaultUriPrefix'); |
| if ('ms-appx://' !== defaultPrefix) { |
| var hasMsAppxWeb = whitelistRules.some(function(rule) { |
| return /^ms-appx-web:\/\/\/$/i.test(rule); |
| }); |
| if (!hasMsAppxWeb) { |
| whitelistRules.push('ms-appx-web:///'); |
| } |
| } |
| |
| manifest.getApplication().setAccessRules(whitelistRules); |
| } |
| |
| function mapImageResources(images, imagesDir) { |
| var pathMap = {}; |
| |
| // Platform default images |
| var platformImages = [ |
| {dest: 'Square150x150Logo.scale-100.png', width: 150, height: 150}, |
| {dest: 'Square30x30Logo.scale-100.png', width: 30, height: 30}, |
| {dest: 'StoreLogo.scale-100.png', width: 50, height: 50}, |
| {dest: 'SplashScreen.scale-100.png', width: 620, height: 300}, |
| // scaled images are specified here for backward compatibility only so we can find them by size |
| {dest: 'StoreLogo.scale-240.png', width: 120, height: 120}, |
| {dest: 'Square44x44Logo.scale-100.png', width: 44, height: 44}, |
| {dest: 'Square44x44Logo.scale-240.png', width: 106, height: 106}, |
| {dest: 'Square70x70Logo.scale-100.png', width: 70, height: 70}, |
| {dest: 'Square71x71Logo.scale-100.png', width: 71, height: 71}, |
| {dest: 'Square71x71Logo.scale-240.png', width: 170, height: 170}, |
| {dest: 'Square150x150Logo.scale-240.png', width: 360, height: 360}, |
| {dest: 'Square310x310Logo.scale-100.png', width: 310, height: 310}, |
| {dest: 'Wide310x150Logo.scale-100.png', width: 310, height: 150}, |
| {dest: 'Wide310x150Logo.scale-240.png', width: 744, height: 360}, |
| {dest: 'SplashScreenPhone.scale-240.png', width: 1152, height: 1920} |
| ]; |
| |
| function findPlatformImage(width, height) { |
| if (!width && !height){ |
| // this could be default image, |
| // Windows requires specific image dimension so we can't apply it |
| return null; |
| } |
| for (var idx in platformImages){ |
| var res = platformImages[idx]; |
| // If only one of width or height is not specified, use another parameter for comparation |
| // If both specified, compare both. |
| if ((!width || (width == res.width)) && |
| (!height || (height == res.height))){ |
| return res; |
| } |
| } |
| return null; |
| } |
| |
| images.forEach(function (img) { |
| if (img.target) { |
| // Parse source path into new MRTImage |
| var imageToCopy = new MRTImage(img.src); |
| |
| // then get all matching MRT images in source directory |
| var candidates = fs.readdirSync(imageToCopy.location) |
| .map(function (file) { return new MRTImage(path.join(imageToCopy.location, file)); }) |
| .filter(imageToCopy.matchesTo, imageToCopy); |
| |
| // Warn user if no images were copied |
| if (candidates.length === 0) { |
| events.emit('warn', 'No images found for target: ' + img.target); |
| } else { |
| candidates.forEach(function(mrtImage) { |
| // copy images with new base name but keeping qualifier |
| var targetPath = path.join(imagesDir, mrtImage.generateFilenameFrom(img.target)); |
| pathMap[targetPath] = mrtImage.path; |
| }); |
| } |
| } else { |
| // find target image by size |
| var targetImg = findPlatformImage(img.width, img.height); |
| if (targetImg) { |
| var targetPath = path.join(imagesDir, targetImg.dest); |
| pathMap[targetPath] = img.src; |
| } else { |
| events.emit('warn', 'The following image was skipped because it has an unsupported size (' + img.width + 'x' + img.height + '): ' + img.src); |
| } |
| } |
| }); |
| |
| return pathMap; |
| } |
| |
| function copyImages(cordovaProject, locations) { |
| var images = cordovaProject.projectConfig.getIcons('windows') |
| .concat(cordovaProject.projectConfig.getSplashScreens('windows')); |
| |
| if (images.length === 0) { |
| events.emit('verbose', 'This app does not have any icons or splash screens defined'); |
| return; |
| } |
| |
| var imagesDir = path.join(path.relative(cordovaProject.root, locations.root), 'images'); |
| var resourceMap = mapImageResources(images, imagesDir); |
| events.emit('verbose', 'Updating icons and splash screens at ' + imagesDir); |
| FileUpdater.updatePaths( |
| resourceMap, { rootDir: cordovaProject.root }, logFileOp); |
| } |
| |
| function cleanImages(projectRoot, projectConfig, locations) { |
| var images = projectConfig.getIcons('windows') |
| .concat(projectConfig.getSplashScreens('windows')); |
| |
| if (images.length > 0) { |
| var imagesDir = path.join(path.relative(projectRoot, locations.root), 'images'); |
| var resourceMap = mapImageResources(images, imagesDir); |
| Object.keys(resourceMap).forEach(function (targetImagePath) { |
| resourceMap[targetImagePath] = null; |
| }); |
| events.emit('verbose', 'Cleaning icons and splash screens at ' + imagesDir); |
| |
| // Source paths are removed from the map, so updatePaths() will delete the target files. |
| FileUpdater.updatePaths( |
| resourceMap, { rootDir: projectRoot, all: true }, logFileOp); |
| } |
| } |
| |
| function applyUAPVersionToProject(projectFilePath, uapVersionInfo) { |
| // No uapVersionInfo means that there is no UAP SDKs installed and there is nothing to do for us |
| if (!uapVersionInfo) return; |
| |
| var fileContents = fs.readFileSync(projectFilePath).toString().trim(); |
| var xml = et.parse(fileContents); |
| var tpv = xml.find('./PropertyGroup/TargetPlatformVersion'); |
| var tpmv = xml.find('./PropertyGroup/TargetPlatformMinVersion'); |
| |
| tpv.text = uapVersionInfo.targetUAPVersion.toString(); |
| tpmv.text = uapVersionInfo.minUAPVersion.toString(); |
| |
| fs.writeFileSync(projectFilePath, xml.write({ indent: 4 }), {}); |
| } |
| |
| // returns {minUAPVersion: Version, targetUAPVersion: Version} | false |
| function getUAPVersions(config) { |
| var baselineVersions = MSBuildTools.getAvailableUAPVersions(); |
| if (!baselineVersions || baselineVersions.length === 0) { |
| return false; |
| } |
| |
| baselineVersions.sort(Version.comparer); |
| |
| var uapTargetMinPreference = config.getUAPTargetMinVersion(); |
| |
| return { |
| minUAPVersion: uapTargetMinPreference, |
| targetUAPVersion: baselineVersions[baselineVersions.length - 1] /* The highest available SDK on the system */ |
| }; |
| } |
| |
| module.exports.prepare = function (cordovaProject, options) { |
| var self = this; |
| |
| var platformJson = PlatformJson.load(this.root, this.platform); |
| var munger = new PlatformMunger(this.platform, this.root, platformJson, new PluginInfoProvider()); |
| this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations); |
| |
| // CB-10845 avoid using cached appxmanifests since they could be |
| // previously modififed outside of AppxManifest class |
| // TODO: invalidate only entries that were affected by config munge |
| AppxManifest.purgeCache(); |
| |
| // Update own www dir with project's www assets and plugins' assets and js-files |
| return Q.when(updateWww(cordovaProject, this.locations)) |
| .then(function () { |
| // update project according to config.xml changes. |
| return updateProjectAccordingTo(self._config, self.locations); |
| }) |
| .then(function () { |
| copyImages(cordovaProject, self.locations); |
| // CB-5421 Add BOM to all html, js, css files |
| // to ensure app can pass Windows Store Certification |
| addBOMSignature(self.locations.www); |
| }) |
| .then(function () { |
| events.emit('verbose', 'Prepared windows project successfully'); |
| }); |
| }; |
| |
| module.exports.clean = function (options) { |
| // A cordovaProject isn't passed into the clean() function, because it might have |
| // been called from the platform shell script rather than the CLI. Check for the |
| // noPrepare option passed in by the non-CLI clean script. If that's present, or if |
| // there's no config.xml found at the project root, then don't clean prepared files. |
| var projectRoot = path.resolve(this.root, '../..'); |
| var projectConfigFile = path.join(projectRoot, 'config.xml'); |
| if ((options && options.noPrepare) || !fs.existsSync(projectConfigFile) || |
| !fs.existsSync(this.locations.configXml)) { |
| return Q(); |
| } |
| |
| var projectConfig = new ConfigParser(this.locations.configXml); |
| |
| var self = this; |
| return Q().then(function () { |
| cleanWww(projectRoot, self.locations); |
| cleanImages(projectRoot, projectConfig, self.locations); |
| }); |
| }; |
| |
| /** |
| * Adds BOM signature at the beginning of all js|html|css|json files in |
| * specified folder and all subfolders. This is required for application to |
| * pass Windows store certification successfully. |
| * |
| * @param {String} directory Directory where we need to update files |
| */ |
| function addBOMSignature(directory) { |
| shell.ls('-R', directory) |
| .forEach(function (file) { |
| if (!file.match(/\.(js|htm|html|css|json)$/i)) { |
| return; |
| } |
| |
| var filePath = path.join(directory, file); |
| // skip if this is a folder |
| if (!fs.lstatSync(filePath).isFile()) { |
| return; |
| } |
| |
| var content = fs.readFileSync(filePath); |
| if (content[0] !== 0xEF && content[1] !== 0xBE && content[2] !== 0xBB) { |
| fs.writeFileSync(filePath, '\ufeff' + content); |
| } |
| }); |
| } |
| |
| /** |
| * Updates config files in project based on app's config.xml and config munge, |
| * generated by plugins. |
| * |
| * @param {ConfigParser} sourceConfig A project's configuration that will |
| * be merged into platform's config.xml |
| * @param {ConfigChanges} configMunger An initialized ConfigChanges instance |
| * for this platform. |
| * @param {Object} locations A map of locations for this platform |
| * |
| * @return {ConfigParser} An instance of ConfigParser, that |
| * represents current project's configuration. When returned, the |
| * configuration is already dumped to appropriate config.xml file. |
| */ |
| function updateConfigFilesFrom(sourceConfig, configMunger, locations) { |
| // First cleanup current config and merge project's one into own |
| var defaultConfig = locations.defaultConfigXml; |
| var ownConfig = locations.configXml; |
| var sourceCfg = sourceConfig.path; |
| // If defaults.xml is present, overwrite platform config.xml with it. |
| // Otherwise save whatever is there as defaults so it can be |
| // restored or copy project config into platform if none exists. |
| if (fs.existsSync(defaultConfig)) { |
| events.emit('verbose', 'Generating platform-specific config.xml from defaults for windows at ' + ownConfig); |
| shell.cp('-f', defaultConfig, ownConfig); |
| } else if (fs.existsSync(ownConfig)) { |
| shell.cp('-f', ownConfig, defaultConfig); |
| } else { |
| shell.cp('-f', sourceCfg, ownConfig); |
| } |
| |
| // Then apply config changes from global munge to all config files |
| // in project (including project's config) |
| configMunger.reapply_global_munge().save_all(); |
| |
| events.emit('verbose', 'Merging project\'s config.xml into platform-specific windows config.xml'); |
| // Merge changes from app's config.xml into platform's one |
| var config = new ConfigParser(ownConfig); |
| xmlHelpers.mergeXml(sourceConfig.doc.getroot(), |
| config.doc.getroot(), 'windows', /*clobber=*/true); |
| |
| config.write(); |
| return config; |
| } |
| |
| /** |
| * Logs all file operations via the verbose event stream, indented. |
| */ |
| function logFileOp(message) { |
| events.emit('verbose', ' ' + message); |
| } |
| |
| /** |
| * Updates platform 'www' directory by replacing it with contents of |
| * 'platform_www' and app www. Also copies project's overrides' folder into |
| * the platform 'www' folder |
| * |
| * @param {Object} cordovaProject An object which describes cordova project. |
| * @param {Object} destinations An object that contains destination |
| * paths for www files. |
| */ |
| function updateWww(cordovaProject, destinations) { |
| var sourceDirs = [ |
| path.relative(cordovaProject.root, cordovaProject.locations.www), |
| path.relative(cordovaProject.root, destinations.platformWww) |
| ]; |
| |
| // If project contains 'merges' for our platform, use them as another overrides |
| var merges_path = path.join(cordovaProject.root, 'merges', 'windows'); |
| if (fs.existsSync(merges_path)) { |
| events.emit('verbose', 'Found "merges/windows" folder. Copying its contents into the windows project.'); |
| sourceDirs.push(path.join('merges', 'windows')); |
| } |
| |
| var targetDir = path.relative(cordovaProject.root, destinations.www); |
| events.emit( |
| 'verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir); |
| FileUpdater.mergeAndUpdateDir( |
| sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp); |
| } |
| |
| /** |
| * Cleans all files from the platform 'www' directory. |
| */ |
| function cleanWww(projectRoot, locations) { |
| var targetDir = path.relative(projectRoot, locations.www); |
| events.emit('verbose', 'Cleaning ' + targetDir); |
| |
| // No source paths are specified, so mergeAndUpdateDir() will clear the target directory. |
| FileUpdater.mergeAndUpdateDir( |
| [], targetDir, { rootDir: projectRoot, all: true }, logFileOp); |
| } |
| |
| /** |
| * Updates project structure and AppxManifest according to project's configuration. |
| * |
| * @param {ConfigParser} projectConfig A project's configuration that will |
| * be used to update project |
| * @param {Object} locations A map of locations for this platform |
| */ |
| function updateProjectAccordingTo(projectConfig, locations) { |
| // Apply appxmanifest changes |
| [MANIFEST_WINDOWS, MANIFEST_WINDOWS10, MANIFEST_PHONE] |
| .forEach(function(manifestFile) { |
| updateManifestFile(projectConfig, path.join(locations.root, manifestFile)); |
| }); |
| |
| if (process.platform === 'win32') { |
| applyUAPVersionToProject(path.join(locations.root, PROJECT_WINDOWS10), getUAPVersions(projectConfig)); |
| } |
| } |