diff options
Diffstat (limited to 'StoneIsland/platforms/ios/cordova/lib/prepare.js')
| -rw-r--r--[-rwxr-xr-x] | StoneIsland/platforms/ios/cordova/lib/prepare.js | 863 |
1 files changed, 432 insertions, 431 deletions
diff --git a/StoneIsland/platforms/ios/cordova/lib/prepare.js b/StoneIsland/platforms/ios/cordova/lib/prepare.js index 17bbfeb7..c98cb8f4 100755..100644 --- a/StoneIsland/platforms/ios/cordova/lib/prepare.js +++ b/StoneIsland/platforms/ios/cordova/lib/prepare.js @@ -18,53 +18,46 @@ */ 'use strict'; -var Q = require('q'); -var fs = require('fs'); -var path = require('path'); -var shell = require('shelljs'); -var xcode = require('xcode'); -var unorm = require('unorm'); -var plist = require('plist'); -var URL = require('url'); -var events = require('cordova-common').events; -var xmlHelpers = require('cordova-common').xmlHelpers; -var ConfigParser = require('cordova-common').ConfigParser; -var CordovaError = require('cordova-common').CordovaError; -var PlatformJson = require('cordova-common').PlatformJson; -var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger; -var PluginInfoProvider = require('cordova-common').PluginInfoProvider; -var FileUpdater = require('cordova-common').FileUpdater; -var projectFile = require('./projectFile'); + +const fs = require('fs-extra'); +const path = require('path'); +const unorm = require('unorm'); +const plist = require('plist'); +const URL = require('url'); +const events = require('cordova-common').events; +const xmlHelpers = require('cordova-common').xmlHelpers; +const ConfigParser = require('cordova-common').ConfigParser; +const CordovaError = require('cordova-common').CordovaError; +const PlatformJson = require('cordova-common').PlatformJson; +const PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger; +const PluginInfoProvider = require('cordova-common').PluginInfoProvider; +const FileUpdater = require('cordova-common').FileUpdater; +const projectFile = require('./projectFile'); // launch storyboard and related constants -var LAUNCHIMAGE_BUILD_SETTING = 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME'; -var LAUNCHIMAGE_BUILD_SETTING_VALUE = 'LaunchImage'; -var UI_LAUNCH_STORYBOARD_NAME = 'UILaunchStoryboardName'; -var CDV_LAUNCH_STORYBOARD_NAME = 'CDVLaunchScreen'; -var IMAGESET_COMPACT_SIZE_CLASS = 'compact'; -var CDV_ANY_SIZE_CLASS = 'any'; +const IMAGESET_COMPACT_SIZE_CLASS = 'compact'; +const CDV_ANY_SIZE_CLASS = 'any'; module.exports.prepare = function (cordovaProject, options) { - var self = this; - - var platformJson = PlatformJson.load(this.locations.root, 'ios'); - var munger = new PlatformMunger('ios', this.locations.root, platformJson, new PluginInfoProvider()); + const platformJson = PlatformJson.load(this.locations.root, 'ios'); + const munger = new PlatformMunger('ios', this.locations.root, platformJson, new PluginInfoProvider()); this._config = updateConfigFile(cordovaProject.projectConfig, munger, this.locations); // 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 updateProject(self._config, self.locations); + return updateWww(cordovaProject, this.locations) + // update project according to config.xml changes. + .then(() => updateProject(this._config, this.locations)) + .then(() => { + updateIcons(cordovaProject, this.locations); + updateLaunchStoryboardImages(cordovaProject, this.locations); + updateBackgroundColor(cordovaProject, this.locations); + updateFileResources(cordovaProject, this.locations); }) - .then(function () { - updateIcons(cordovaProject, self.locations); - updateSplashScreens(cordovaProject, self.locations); - updateLaunchStoryboardImages(cordovaProject, self.locations); - updateFileResources(cordovaProject, self.locations); + .then(() => { + alertDeprecatedPreference(this._config); }) - .then(function () { + .then(() => { events.emit('verbose', 'Prepared iOS project successfully'); }); }; @@ -74,22 +67,21 @@ module.exports.clean = function (options) { // 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'); + const projectRoot = path.resolve(this.root, '../..'); + const projectConfigFile = path.join(projectRoot, 'config.xml'); if ((options && options.noPrepare) || !fs.existsSync(projectConfigFile) || !fs.existsSync(this.locations.configXml)) { - return Q(); + return Promise.resolve(); } - var projectConfig = new ConfigParser(this.locations.configXml); + const projectConfig = new ConfigParser(this.locations.configXml); - var self = this; - return Q().then(function () { - cleanWww(projectRoot, self.locations); - cleanIcons(projectRoot, projectConfig, self.locations); - cleanSplashScreens(projectRoot, projectConfig, self.locations); - cleanLaunchStoryboardImages(projectRoot, projectConfig, self.locations); - cleanFileResources(projectRoot, projectConfig, self.locations); + return Promise.resolve().then(() => { + cleanWww(projectRoot, this.locations); + cleanIcons(projectRoot, projectConfig, this.locations); + cleanLaunchStoryboardImages(projectRoot, projectConfig, this.locations); + cleanBackgroundColor(projectRoot, projectConfig, this.locations); + cleanFileResources(projectRoot, projectConfig, this.locations); }); }; @@ -108,11 +100,11 @@ module.exports.clean = function (options) { * configuration is already dumped to appropriate config.xml file. */ function updateConfigFile (sourceConfig, configMunger, locations) { - events.emit('verbose', 'Generating platform-specific config.xml from defaults for iOS at ' + locations.configXml); + events.emit('verbose', `Generating platform-specific config.xml from defaults for iOS at ${locations.configXml}`); // First cleanup current config and merge project's one into own // Overwrite platform config.xml with defaults.xml. - shell.cp('-f', locations.defaultConfigXml, locations.configXml); + fs.copySync(locations.defaultConfigXml, locations.configXml); // Then apply config changes from global munge to all config files // in project (including project's config) @@ -120,7 +112,7 @@ function updateConfigFile (sourceConfig, configMunger, locations) { events.emit('verbose', 'Merging project\'s config.xml into platform-specific iOS config.xml'); // Merge changes from app's config.xml into platform's one - var config = new ConfigParser(locations.configXml); + const config = new ConfigParser(locations.configXml); xmlHelpers.mergeXml(sourceConfig.doc.getroot(), config.doc.getroot(), 'ios', /* clobber= */true); @@ -132,7 +124,7 @@ function updateConfigFile (sourceConfig, configMunger, locations) { * Logs all file operations via the verbose event stream, indented. */ function logFileOp (message) { - events.emit('verbose', ' ' + message); + events.emit('verbose', ` ${message}`); } /** @@ -145,31 +137,33 @@ function logFileOp (message) { * paths for www files. */ function updateWww (cordovaProject, destinations) { - var sourceDirs = [ + const 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', 'ios'); + const merges_path = path.join(cordovaProject.root, 'merges', 'ios'); if (fs.existsSync(merges_path)) { events.emit('verbose', 'Found "merges/ios" folder. Copying its contents into the iOS project.'); sourceDirs.push(path.join('merges', 'ios')); } - var targetDir = path.relative(cordovaProject.root, destinations.www); + const targetDir = path.relative(cordovaProject.root, destinations.www); events.emit( - 'verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir); + 'verbose', `Merging and updating files from [${sourceDirs.join(', ')}] to ${targetDir}`); FileUpdater.mergeAndUpdateDir( sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp); + + return Promise.resolve(); } /** * Cleans all files from the platform 'www' directory. */ function cleanWww (projectRoot, locations) { - var targetDir = path.relative(projectRoot, locations.www); - events.emit('verbose', 'Cleaning ' + targetDir); + const 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( @@ -184,169 +178,176 @@ function cleanWww (projectRoot, locations) { * @param {Object} locations A map of locations for this platform (In/Out) */ function updateProject (platformConfig, locations) { - // CB-6992 it is necessary to normalize characters // because node and shell scripts handles unicode symbols differently // We need to normalize the name to NFD form since iOS uses NFD unicode form - var name = unorm.nfd(platformConfig.name()); - var pkg = platformConfig.getAttribute('ios-CFBundleIdentifier') || platformConfig.packageName(); - var version = platformConfig.version(); - var displayName = platformConfig.shortName && platformConfig.shortName(); + const name = unorm.nfd(platformConfig.name()); + const version = platformConfig.version(); + const displayName = platformConfig.shortName && platformConfig.shortName(); - var originalName = path.basename(locations.xcodeCordovaProj); + const originalName = path.basename(locations.xcodeCordovaProj); // Update package id (bundle id) - var plistFile = path.join(locations.xcodeCordovaProj, originalName + '-Info.plist'); - var infoPlist = plist.parse(fs.readFileSync(plistFile, 'utf8')); - infoPlist['CFBundleIdentifier'] = pkg; + const plistFile = path.join(locations.xcodeCordovaProj, `${originalName}-Info.plist`); + const infoPlist = plist.parse(fs.readFileSync(plistFile, 'utf8')); // Update version (bundle version) - infoPlist['CFBundleShortVersionString'] = version; - var CFBundleVersion = platformConfig.getAttribute('ios-CFBundleVersion') || default_CFBundleVersion(version); - infoPlist['CFBundleVersion'] = CFBundleVersion; + infoPlist.CFBundleShortVersionString = version; + const CFBundleVersion = platformConfig.getAttribute('ios-CFBundleVersion') || default_CFBundleVersion(version); + infoPlist.CFBundleVersion = CFBundleVersion; if (platformConfig.getAttribute('defaultlocale')) { - infoPlist['CFBundleDevelopmentRegion'] = platformConfig.getAttribute('defaultlocale'); + infoPlist.CFBundleDevelopmentRegion = platformConfig.getAttribute('defaultlocale'); } if (displayName) { - infoPlist['CFBundleDisplayName'] = displayName; + infoPlist.CFBundleDisplayName = displayName; } // replace Info.plist ATS entries according to <access> and <allow-navigation> config.xml entries - var ats = writeATSEntries(platformConfig); + const ats = writeATSEntries(platformConfig); if (Object.keys(ats).length > 0) { - infoPlist['NSAppTransportSecurity'] = ats; + infoPlist.NSAppTransportSecurity = ats; } else { - delete infoPlist['NSAppTransportSecurity']; + delete infoPlist.NSAppTransportSecurity; } handleOrientationSettings(platformConfig, infoPlist); - updateProjectPlistForLaunchStoryboard(platformConfig, infoPlist); - var info_contents = plist.build(infoPlist); + /* eslint-disable no-tabs */ + // Write out the plist file with the same formatting as Xcode does + let info_contents = plist.build(infoPlist, { indent: '\t', offset: -1 }); + /* eslint-enable no-tabs */ + info_contents = info_contents.replace(/<string>[\s\r\n]*<\/string>/g, '<string></string>'); fs.writeFileSync(plistFile, info_contents, 'utf-8'); - events.emit('verbose', 'Wrote out iOS Bundle Identifier "' + pkg + '" and iOS Bundle Version "' + version + '" to ' + plistFile); + events.emit('verbose', `Wrote out iOS Bundle Version "${version}" to ${plistFile}`); - return handleBuildSettings(platformConfig, locations, infoPlist).then(function () { + return handleBuildSettings(platformConfig, locations, infoPlist).then(() => { if (name === originalName) { - events.emit('verbose', 'iOS Product Name has not changed (still "' + originalName + '")'); - return Q(); + events.emit('verbose', `iOS Product Name has not changed (still "${originalName}")`); + return Promise.resolve(); } else { // CB-11712 <name> was changed, we don't support it' - var errorString = + const errorString = 'The product name change (<name> tag) in config.xml is not supported dynamically.\n' + 'To change your product name, you have to remove, then add your ios platform again.\n' + 'Make sure you save your plugins beforehand using `cordova plugin save`.\n' + '\tcordova plugin save\n' + '\tcordova platform rm ios\n' + - '\tcordova platform add ios\n' - ; + '\tcordova platform add ios\n'; - return Q.reject(new CordovaError(errorString)); + return Promise.reject(new CordovaError(errorString)); } }); } function handleOrientationSettings (platformConfig, infoPlist) { - switch (getOrientationValue(platformConfig)) { case 'portrait': - infoPlist['UIInterfaceOrientation'] = [ 'UIInterfaceOrientationPortrait' ]; - infoPlist['UISupportedInterfaceOrientations'] = [ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown' ]; - infoPlist['UISupportedInterfaceOrientations~ipad'] = [ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown' ]; + infoPlist.UIInterfaceOrientation = ['UIInterfaceOrientationPortrait']; + infoPlist.UISupportedInterfaceOrientations = ['UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown']; + infoPlist['UISupportedInterfaceOrientations~ipad'] = ['UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown']; break; case 'landscape': - infoPlist['UIInterfaceOrientation'] = [ 'UIInterfaceOrientationLandscapeLeft' ]; - infoPlist['UISupportedInterfaceOrientations'] = [ 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]; - infoPlist['UISupportedInterfaceOrientations~ipad'] = [ 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]; + infoPlist.UIInterfaceOrientation = ['UIInterfaceOrientationLandscapeLeft']; + infoPlist.UISupportedInterfaceOrientations = ['UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight']; + infoPlist['UISupportedInterfaceOrientations~ipad'] = ['UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight']; break; case 'all': - infoPlist['UIInterfaceOrientation'] = [ 'UIInterfaceOrientationPortrait' ]; - infoPlist['UISupportedInterfaceOrientations'] = [ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]; - infoPlist['UISupportedInterfaceOrientations~ipad'] = [ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]; + infoPlist.UIInterfaceOrientation = ['UIInterfaceOrientationPortrait']; + infoPlist.UISupportedInterfaceOrientations = ['UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight']; + infoPlist['UISupportedInterfaceOrientations~ipad'] = ['UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight']; break; case 'default': - infoPlist['UISupportedInterfaceOrientations'] = [ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]; - infoPlist['UISupportedInterfaceOrientations~ipad'] = [ 'UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight' ]; - delete infoPlist['UIInterfaceOrientation']; + infoPlist.UISupportedInterfaceOrientations = ['UIInterfaceOrientationPortrait', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight']; + infoPlist['UISupportedInterfaceOrientations~ipad'] = ['UIInterfaceOrientationPortrait', 'UIInterfaceOrientationPortraitUpsideDown', 'UIInterfaceOrientationLandscapeLeft', 'UIInterfaceOrientationLandscapeRight']; + delete infoPlist.UIInterfaceOrientation; } } function handleBuildSettings (platformConfig, locations, infoPlist) { - var targetDevice = parseTargetDevicePreference(platformConfig.getPreference('target-device', 'ios')); - var deploymentTarget = platformConfig.getPreference('deployment-target', 'ios'); - var needUpdatedBuildSettingsForLaunchStoryboard = checkIfBuildSettingsNeedUpdatedForLaunchStoryboard(platformConfig, infoPlist); + const pkg = platformConfig.getAttribute('ios-CFBundleIdentifier') || platformConfig.packageName(); + const targetDevice = parseTargetDevicePreference(platformConfig.getPreference('target-device', 'ios')); + const deploymentTarget = platformConfig.getPreference('deployment-target', 'ios'); + const swiftVersion = platformConfig.getPreference('SwiftVersion', 'ios'); + + let project; + + try { + project = projectFile.parse(locations); + } catch (err) { + return Promise.reject(new CordovaError(`Could not parse ${locations.pbxproj}: ${err}`)); + } + + const origPkg = project.xcode.getBuildProperty('PRODUCT_BUNDLE_IDENTIFIER', undefined, platformConfig.name()); // no build settings provided and we don't need to update build settings for launch storyboards, // then we don't need to parse and update .pbxproj file - if (!targetDevice && !deploymentTarget && !needUpdatedBuildSettingsForLaunchStoryboard) { - return Q(); + if (origPkg === pkg && !targetDevice && !deploymentTarget && !swiftVersion) { + return Promise.resolve(); } - var proj = new xcode.project(locations.pbxproj); /* eslint new-cap : 0 */ - - try { - proj.parseSync(); - } catch (err) { - return Q.reject(new CordovaError('Could not parse project.pbxproj: ' + err)); + if (origPkg !== pkg) { + events.emit('verbose', `Set PRODUCT_BUNDLE_IDENTIFIER to ${pkg}.`); + project.xcode.updateBuildProperty('PRODUCT_BUNDLE_IDENTIFIER', pkg, null, platformConfig.name()); } if (targetDevice) { - events.emit('verbose', 'Set TARGETED_DEVICE_FAMILY to ' + targetDevice + '.'); - proj.updateBuildProperty('TARGETED_DEVICE_FAMILY', targetDevice); + events.emit('verbose', `Set TARGETED_DEVICE_FAMILY to ${targetDevice}.`); + project.xcode.updateBuildProperty('TARGETED_DEVICE_FAMILY', targetDevice); } if (deploymentTarget) { - events.emit('verbose', 'Set IPHONEOS_DEPLOYMENT_TARGET to "' + deploymentTarget + '".'); - proj.updateBuildProperty('IPHONEOS_DEPLOYMENT_TARGET', deploymentTarget); + events.emit('verbose', `Set IPHONEOS_DEPLOYMENT_TARGET to "${deploymentTarget}".`); + project.xcode.updateBuildProperty('IPHONEOS_DEPLOYMENT_TARGET', deploymentTarget); } - updateBuildSettingsForLaunchStoryboard(proj, platformConfig, infoPlist); + if (swiftVersion) { + events.emit('verbose', `Set SwiftVersion to "${swiftVersion}".`); + project.xcode.updateBuildProperty('SWIFT_VERSION', swiftVersion); + } - fs.writeFileSync(locations.pbxproj, proj.writeSync(), 'utf-8'); + project.write(); - return Q(); + return Promise.resolve(); } function mapIconResources (icons, iconsDir) { // See https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/IconMatrix.html // for launch images sizes reference. - var platformIcons = [ - {dest: 'icon-20.png', width: 20, height: 20}, - {dest: 'icon-20@2x.png', width: 40, height: 40}, - {dest: 'icon-20@3x.png', width: 60, height: 60}, - {dest: 'icon-40.png', width: 40, height: 40}, - {dest: 'icon-50.png', width: 50, height: 50}, - {dest: 'icon-50@2x.png', width: 100, height: 100}, - {dest: 'icon-60@2x.png', width: 120, height: 120}, - {dest: 'icon-60@3x.png', width: 180, height: 180}, - {dest: 'icon-72.png', width: 72, height: 72}, - {dest: 'icon-72@2x.png', width: 144, height: 144}, - {dest: 'icon-76.png', width: 76, height: 76}, - {dest: 'icon-76@2x.png', width: 152, height: 152}, - {dest: 'icon-83.5@2x.png', width: 167, height: 167}, - {dest: 'icon-1024.png', width: 1024, height: 1024}, - {dest: 'icon-small.png', width: 29, height: 29}, - {dest: 'icon-small@2x.png', width: 58, height: 58}, - {dest: 'icon-small@3x.png', width: 87, height: 87}, - {dest: 'icon.png', width: 57, height: 57}, - {dest: 'icon@2x.png', width: 114, height: 114}, - {dest: 'AppIcon24x24@2x.png', width: 48, height: 48}, - {dest: 'AppIcon27.5x27.5@2x.png', width: 55, height: 55}, - {dest: 'AppIcon29x29@2x.png', width: 58, height: 58}, - {dest: 'AppIcon29x29@3x.png', width: 87, height: 87}, - {dest: 'AppIcon40x40@2x.png', width: 80, height: 80}, - {dest: 'AppIcon44x44@2x.png', width: 88, height: 88}, - {dest: 'AppIcon86x86@2x.png', width: 172, height: 172}, - {dest: 'AppIcon98x98@2x.png', width: 196, height: 196} + const platformIcons = [ + { dest: 'icon-20.png', width: 20, height: 20 }, + { dest: 'icon-20@2x.png', width: 40, height: 40 }, + { dest: 'icon-20@3x.png', width: 60, height: 60 }, + { dest: 'icon-40.png', width: 40, height: 40 }, + { dest: 'icon-40@2x.png', width: 80, height: 80 }, + { dest: 'icon-50.png', width: 50, height: 50 }, + { dest: 'icon-50@2x.png', width: 100, height: 100 }, + { dest: 'icon-60@2x.png', width: 120, height: 120 }, + { dest: 'icon-60@3x.png', width: 180, height: 180 }, + { dest: 'icon-72.png', width: 72, height: 72 }, + { dest: 'icon-72@2x.png', width: 144, height: 144 }, + { dest: 'icon-76.png', width: 76, height: 76 }, + { dest: 'icon-76@2x.png', width: 152, height: 152 }, + { dest: 'icon-83.5@2x.png', width: 167, height: 167 }, + { dest: 'icon-1024.png', width: 1024, height: 1024 }, + { dest: 'icon-29.png', width: 29, height: 29 }, + { dest: 'icon-29@2x.png', width: 58, height: 58 }, + { dest: 'icon-29@3x.png', width: 87, height: 87 }, + { dest: 'icon.png', width: 57, height: 57 }, + { dest: 'icon@2x.png', width: 114, height: 114 }, + { dest: 'icon-24@2x.png', width: 48, height: 48 }, + { dest: 'icon-27.5@2x.png', width: 55, height: 55 }, + { dest: 'icon-44@2x.png', width: 88, height: 88 }, + { dest: 'icon-86@2x.png', width: 172, height: 172 }, + { dest: 'icon-98@2x.png', width: 196, height: 196 } ]; - var pathMap = {}; - platformIcons.forEach(function (item) { - var icon = icons.getBySize(item.width, item.height) || icons.getDefault(); + const pathMap = {}; + platformIcons.forEach(item => { + const icon = icons.getBySize(item.width, item.height) || icons.getDefault(); if (icon) { - var target = path.join(iconsDir, item.dest); + const target = path.join(iconsDir, item.dest); pathMap[target] = icon.src; } }); @@ -354,8 +355,8 @@ function mapIconResources (icons, iconsDir) { } function getIconsDir (projectRoot, platformProjDir) { - var iconsDir; - var xcassetsExists = folderExists(path.join(projectRoot, platformProjDir, 'Images.xcassets/')); + let iconsDir; + const xcassetsExists = folderExists(path.join(projectRoot, platformProjDir, 'Images.xcassets/')); if (xcassetsExists) { iconsDir = path.join(platformProjDir, 'Images.xcassets/AppIcon.appiconset/'); @@ -367,31 +368,31 @@ function getIconsDir (projectRoot, platformProjDir) { } function updateIcons (cordovaProject, locations) { - var icons = cordovaProject.projectConfig.getIcons('ios'); + const icons = cordovaProject.projectConfig.getIcons('ios'); if (icons.length === 0) { events.emit('verbose', 'This app does not have icons defined'); return; } - var platformProjDir = path.relative(cordovaProject.root, locations.xcodeCordovaProj); - var iconsDir = getIconsDir(cordovaProject.root, platformProjDir); - var resourceMap = mapIconResources(icons, iconsDir); - events.emit('verbose', 'Updating icons at ' + iconsDir); + const platformProjDir = path.relative(cordovaProject.root, locations.xcodeCordovaProj); + const iconsDir = getIconsDir(cordovaProject.root, platformProjDir); + const resourceMap = mapIconResources(icons, iconsDir); + events.emit('verbose', `Updating icons at ${iconsDir}`); FileUpdater.updatePaths( resourceMap, { rootDir: cordovaProject.root }, logFileOp); } function cleanIcons (projectRoot, projectConfig, locations) { - var icons = projectConfig.getIcons('ios'); + const icons = projectConfig.getIcons('ios'); if (icons.length > 0) { - var platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); - var iconsDir = getIconsDir(projectRoot, platformProjDir); - var resourceMap = mapIconResources(icons, iconsDir); - Object.keys(resourceMap).forEach(function (targetIconPath) { + const platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); + const iconsDir = getIconsDir(projectRoot, platformProjDir); + const resourceMap = mapIconResources(icons, iconsDir); + Object.keys(resourceMap).forEach(targetIconPath => { resourceMap[targetIconPath] = null; }); - events.emit('verbose', 'Cleaning icons at ' + iconsDir); + events.emit('verbose', `Cleaning icons at ${iconsDir}`); // Source paths are removed from the map, so updatePaths() will delete the target files. FileUpdater.updatePaths( @@ -399,74 +400,120 @@ function cleanIcons (projectRoot, projectConfig, locations) { } } -function mapSplashScreenResources (splashScreens, splashScreensDir) { - var platformSplashScreens = [ - {dest: 'Default~iphone.png', width: 320, height: 480}, - {dest: 'Default@2x~iphone.png', width: 640, height: 960}, - {dest: 'Default-Portrait~ipad.png', width: 768, height: 1024}, - {dest: 'Default-Portrait@2x~ipad.png', width: 1536, height: 2048}, - {dest: 'Default-Landscape~ipad.png', width: 1024, height: 768}, - {dest: 'Default-Landscape@2x~ipad.png', width: 2048, height: 1536}, - {dest: 'Default-568h@2x~iphone.png', width: 640, height: 1136}, - {dest: 'Default-667h.png', width: 750, height: 1334}, - {dest: 'Default-736h.png', width: 1242, height: 2208}, - {dest: 'Default-Landscape-736h.png', width: 2208, height: 1242} - ]; - - var pathMap = {}; - platformSplashScreens.forEach(function (item) { - var splash = splashScreens.getBySize(item.width, item.height); - if (splash) { - var target = path.join(splashScreensDir, item.dest); - pathMap[target] = splash.src; - } - }); - return pathMap; +/** + * Returns the directory for the BackgroundColor.colorset asset, or null if no + * xcassets exist. + * + * @param {string} projectRoot The project's root directory + * @param {string} platformProjDir The platform's project directory + */ +function getBackgroundColorDir (projectRoot, platformProjDir) { + if (folderExists(path.join(projectRoot, platformProjDir, 'Images.xcassets/'))) { + return path.join(platformProjDir, 'Images.xcassets', 'BackgroundColor.colorset'); + } else { + return null; + } } -function getSplashScreensDir (projectRoot, platformProjDir) { - var splashScreensDir; - var xcassetsExists = folderExists(path.join(projectRoot, platformProjDir, 'Images.xcassets/')); +function colorPreferenceToComponents (pref) { + if (!pref || !pref.match(/^(#[0-9A-F]{3}|(0x|#)([0-9A-F]{2})?[0-9A-F]{6})$/)) { + return { + platform: 'ios', + reference: 'systemBackgroundColor' + }; + } - if (xcassetsExists) { - splashScreensDir = path.join(platformProjDir, 'Images.xcassets/LaunchImage.launchimage/'); - } else { - splashScreensDir = path.join(platformProjDir, 'Resources/splash/'); + let red = 'FF'; + let green = 'FF'; + let blue = 'FF'; + let alpha = 1.0; + + if (pref[0] === '#' && pref.length === 4) { + red = pref[1] + pref[1]; + green = pref[2] + pref[2]; + blue = pref[3] + pref[3]; } - return splashScreensDir; -} + if (pref.length >= 7 && (pref[0] === '#' || pref.substring(0, 2) === '0x')) { + let offset = pref[0] === '#' ? 1 : 2; -function updateSplashScreens (cordovaProject, locations) { - var splashScreens = cordovaProject.projectConfig.getSplashScreens('ios'); + if (pref.substring(offset).length === 8) { + alpha = parseInt(pref.substring(offset, offset + 2), 16) / 255.0; + offset += 2; + } - if (splashScreens.length === 0) { - events.emit('verbose', 'This app does not have splash screens defined'); - return; + red = pref.substring(offset, offset + 2); + green = pref.substring(offset + 2, offset + 4); + blue = pref.substring(offset + 4, offset + 6); } - var platformProjDir = path.relative(cordovaProject.root, locations.xcodeCordovaProj); - var splashScreensDir = getSplashScreensDir(cordovaProject.root, platformProjDir); - var resourceMap = mapSplashScreenResources(splashScreens, splashScreensDir); - events.emit('verbose', 'Updating splash screens at ' + splashScreensDir); - FileUpdater.updatePaths( - resourceMap, { rootDir: cordovaProject.root }, logFileOp); + return { + 'color-space': 'srgb', + components: { + red: '0x' + red, + green: '0x' + green, + blue: '0x' + blue, + alpha: alpha.toFixed(3) + } + }; } -function cleanSplashScreens (projectRoot, projectConfig, locations) { - var splashScreens = projectConfig.getSplashScreens('ios'); - if (splashScreens.length > 0) { - var platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); - var splashScreensDir = getSplashScreensDir(projectRoot, platformProjDir); - var resourceMap = mapIconResources(splashScreens, splashScreensDir); - Object.keys(resourceMap).forEach(function (targetSplashPath) { - resourceMap[targetSplashPath] = null; - }); - events.emit('verbose', 'Cleaning splash screens at ' + splashScreensDir); +/** + * Update the background color Contents.json in xcassets. + * + * @param {Object} cordovaProject The cordova project + * @param {Object} locations A dictionary containing useful location paths + */ +function updateBackgroundColor (cordovaProject, locations) { + const pref = cordovaProject.projectConfig.getPreference('BackgroundColor', 'ios') || ''; - // Source paths are removed from the map, so updatePaths() will delete the target files. - FileUpdater.updatePaths( - resourceMap, { rootDir: projectRoot, all: true }, logFileOp); + const platformProjDir = path.relative(cordovaProject.root, locations.xcodeCordovaProj); + const backgroundColorDir = getBackgroundColorDir(cordovaProject.root, platformProjDir); + + if (backgroundColorDir) { + const contentsJSON = { + colors: [{ + idiom: 'universal', + color: colorPreferenceToComponents(pref) + }], + info: { + author: 'Xcode', + version: 1 + } + }; + + events.emit('verbose', 'Updating Background Color color set Contents.json'); + fs.writeFileSync(path.join(cordovaProject.root, backgroundColorDir, 'Contents.json'), + JSON.stringify(contentsJSON, null, 2)); + } +} + +/** + * Resets the background color Contents.json in xcassets to default. + * + * @param {string} projectRoot Path to the project root + * @param {Object} projectConfig The project's config.xml + * @param {Object} locations A dictionary containing useful location paths + */ +function cleanBackgroundColor (projectRoot, projectConfig, locations) { + const platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); + const backgroundColorDir = getBackgroundColorDir(projectRoot, platformProjDir); + + if (backgroundColorDir) { + const contentsJSON = { + colors: [{ + idiom: 'universal', + color: colorPreferenceToComponents(null) + }], + info: { + author: 'Xcode', + version: 1 + } + }; + + events.emit('verbose', 'Cleaning Background Color color set Contents.json'); + fs.writeFileSync(path.join(projectRoot, backgroundColorDir, 'Contents.json'), + JSON.stringify(contentsJSON, null, 2)); } } @@ -482,9 +529,9 @@ function updateFileResources (cordovaProject, locations) { return; } - let resourceMap = {}; - files.forEach(function (res) { - let src = res.src; + const resourceMap = {}; + files.forEach(res => { + const src = res.src; let target = res.target; if (!target) { @@ -494,29 +541,75 @@ function updateFileResources (cordovaProject, locations) { let targetPath = path.join(project.resources_dir, target); targetPath = path.relative(cordovaProject.root, targetPath); - project.xcode.addResourceFile(target); + if (!fs.existsSync(targetPath)) { + project.xcode.addResourceFile(target); + } else { + events.emit('warn', `Overwriting existing resource file at ${targetPath}`); + } resourceMap[targetPath] = src; }); - events.emit('verbose', 'Updating resource files at ' + platformDir); + events.emit('verbose', `Updating resource files at ${platformDir}`); FileUpdater.updatePaths( resourceMap, { rootDir: cordovaProject.root }, logFileOp); project.write(); } +function alertDeprecatedPreference (configParser) { + const deprecatedToNewPreferences = { + MediaPlaybackRequiresUserAction: { + newPreference: 'MediaTypesRequiringUserActionForPlayback', + isDeprecated: true + }, + MediaPlaybackAllowsAirPlay: { + newPreference: 'AllowsAirPlayForMediaPlayback', + isDeprecated: false + } + }; + + Object.keys(deprecatedToNewPreferences).forEach(oldKey => { + if (configParser.getPreference(oldKey)) { + const isDeprecated = deprecatedToNewPreferences[oldKey].isDeprecated; + const verb = isDeprecated ? 'has been' : 'is being'; + const newPreferenceKey = deprecatedToNewPreferences[oldKey].newPreference; + + // Create the Log Message + const log = [`The preference name "${oldKey}" ${verb} deprecated.`]; + if (newPreferenceKey) { + log.push(`It is recommended to replace this preference with "${newPreferenceKey}."`); + } else { + log.push('There is no replacement for this preference.'); + } + + /** + * If the preference has been deprecated, the usage of the old preference is no longer used. + * Therefore, the following line is not appended. It is added only if the old preference is still used. + * We are only keeping the top lines for deprecated items only for an additional major release when + * the pre-warning was not provided in a past major release due to a necessary quick deprecation. + * Typically caused by implementation nature or third-party requirement changes. + */ + if (!isDeprecated) { + log.push('Please note that this preference will be removed in the near future.'); + } + + events.emit('warn', log.join(' ')); + } + }); +} + function cleanFileResources (projectRoot, projectConfig, locations) { const platformDir = path.relative(projectRoot, locations.root); const files = projectConfig.getFileResources('ios', true); if (files.length > 0) { - events.emit('verbose', 'Cleaning resource files at ' + platformDir); + events.emit('verbose', `Cleaning resource files at ${platformDir}`); const project = projectFile.parse(locations); - var resourceMap = {}; - files.forEach(function (res) { - let src = res.src; + const resourceMap = {}; + files.forEach(res => { + const src = res.src; let target = res.target; if (!target) { @@ -532,7 +625,7 @@ function cleanFileResources (projectRoot, projectConfig, locations) { }); FileUpdater.updatePaths( - resourceMap, {rootDir: projectRoot, all: true}, logFileOp); + resourceMap, { rootDir: projectRoot, all: true }, logFileOp); project.write(); } @@ -555,7 +648,8 @@ function cleanFileResources (projectRoot, projectConfig, locations) { * height: 'any|com', * filename: undefined|'Default@scale~idiom~widthheight.png', * src: undefined|'path/to/original/matched/image/from/splash/screens.png', - * target: undefined|'path/to/asset/library/Default@scale~idiom~widthheight.png' + * target: undefined|'path/to/asset/library/Default@scale~idiom~widthheight.png', + * appearence: undefined|'dark'|'light' * }, ... * ] * @@ -564,51 +658,54 @@ function cleanFileResources (projectRoot, projectConfig, locations) { * @return {Array<Object>} */ function mapLaunchStoryboardContents (splashScreens, launchStoryboardImagesDir) { - var platformLaunchStoryboardImages = []; - var idioms = ['universal', 'ipad', 'iphone']; - var scalesForIdiom = { + const platformLaunchStoryboardImages = []; + const idioms = ['universal', 'ipad', 'iphone']; + const scalesForIdiom = { universal: ['1x', '2x', '3x'], ipad: ['1x', '2x'], iphone: ['1x', '2x', '3x'] }; - var sizes = ['com', 'any']; + const sizes = ['com', 'any']; + const appearences = ['', 'dark', 'light']; - idioms.forEach(function (idiom) { - scalesForIdiom[idiom].forEach(function (scale) { - sizes.forEach(function (width) { - sizes.forEach(function (height) { - var item = { - idiom: idiom, - scale: scale, - width: width, - height: height - }; + idioms.forEach(idiom => { + scalesForIdiom[idiom].forEach(scale => { + sizes.forEach(width => { + sizes.forEach(height => { + appearences.forEach(appearence => { + const item = { idiom, scale, width, height }; - /* examples of the search pattern: - * scale ~ idiom ~ width height - * @2x ~ universal ~ any any - * @3x ~ iphone ~ com any - * @2x ~ ipad ~ com any - */ - var searchPattern = '@' + scale + '~' + idiom + '~' + width + height; + if (appearence !== '') { + item.appearence = appearence; + } - /* because old node versions don't have Array.find, the below is - * functionally equivalent to this: - * var launchStoryboardImage = splashScreens.find(function(item) { - * return item.src.indexOf(searchPattern) >= 0; - * }); - */ - var launchStoryboardImage = splashScreens.reduce(function (p, c) { - return (c.src.indexOf(searchPattern) >= 0) ? c : p; - }, undefined); + /* examples of the search pattern: + * scale ~ idiom ~ width height ~ appearence + * @2x ~ universal ~ any any + * @3x ~ iphone ~ com any ~ dark + * @2x ~ ipad ~ com any ~ light + */ + const searchPattern = '@' + scale + '~' + idiom + '~' + width + height + (appearence ? '~' + appearence : ''); - if (launchStoryboardImage) { - item.filename = 'Default' + searchPattern + '.png'; - item.src = launchStoryboardImage.src; - item.target = path.join(launchStoryboardImagesDir, item.filename); - } + /* because old node versions don't have Array.find, the below is + * functionally equivalent to this: + * var launchStoryboardImage = splashScreens.find(function(item) { + * return (item.src.indexOf(searchPattern) >= 0) ? (appearence !== '' ? true : ((item.src.indexOf(searchPattern + '~light') >= 0 || (item.src.indexOf(searchPattern + '~dark') >= 0)) ? false : true)) : false; + * }); + */ + const launchStoryboardImage = splashScreens.reduce( + (p, c) => (c.src.indexOf(searchPattern) >= 0) ? (appearence !== '' ? c : ((c.src.indexOf(searchPattern + '~light') >= 0 || (c.src.indexOf(searchPattern + '~dark') >= 0)) ? p : c)) : p, + undefined + ); - platformLaunchStoryboardImages.push(item); + if (launchStoryboardImage) { + item.filename = `Default${searchPattern}.png`; + item.src = launchStoryboardImage.src; + item.target = path.join(launchStoryboardImagesDir, item.filename); + } + + platformLaunchStoryboardImages.push(item); + }); }); }); }); @@ -632,9 +729,9 @@ function mapLaunchStoryboardContents (splashScreens, launchStoryboardImagesDir) * @return {Object} */ function mapLaunchStoryboardResources (splashScreens, launchStoryboardImagesDir) { - var platformLaunchStoryboardImages = mapLaunchStoryboardContents(splashScreens, launchStoryboardImagesDir); - var pathMap = {}; - platformLaunchStoryboardImages.forEach(function (item) { + const platformLaunchStoryboardImages = mapLaunchStoryboardContents(splashScreens, launchStoryboardImagesDir); + const pathMap = {}; + platformLaunchStoryboardImages.forEach(item => { if (item.target) { pathMap[item.target] = item.src; } @@ -654,6 +751,7 @@ function mapLaunchStoryboardResources (splashScreens, launchStoryboardImagesDir) * scale: '1x|2x|3x', * width-class: undefined|'compact', * height-class: undefined|'compact' + * ... * }, ... * ], * info: { @@ -670,17 +768,16 @@ function mapLaunchStoryboardResources (splashScreens, launchStoryboardImagesDir) * @return {Object} */ function getLaunchStoryboardContentsJSON (splashScreens, launchStoryboardImagesDir) { - - var platformLaunchStoryboardImages = mapLaunchStoryboardContents(splashScreens, launchStoryboardImagesDir); - var contentsJSON = { + const platformLaunchStoryboardImages = mapLaunchStoryboardContents(splashScreens, launchStoryboardImagesDir); + const contentsJSON = { images: [], info: { author: 'Xcode', version: 1 } }; - contentsJSON.images = platformLaunchStoryboardImages.map(function (item) { - var newItem = { + contentsJSON.images = platformLaunchStoryboardImages.map(item => { + const newItem = { idiom: item.idiom, scale: item.scale }; @@ -694,6 +791,10 @@ function getLaunchStoryboardContentsJSON (splashScreens, launchStoryboardImagesD newItem['height-class'] = IMAGESET_COMPACT_SIZE_CLASS; } + if (item.appearence) { + newItem.appearances = [{ appearance: 'luminosity', value: item.appearence }]; + } + // Xcode doesn't want a filename property if there's no image for these traits if (item.filename) { newItem.filename = item.filename; @@ -704,107 +805,6 @@ function getLaunchStoryboardContentsJSON (splashScreens, launchStoryboardImagesD } /** - * Determines if the project's build settings may need to be updated for launch storyboard support - * - */ -function checkIfBuildSettingsNeedUpdatedForLaunchStoryboard (platformConfig, infoPlist) { - var hasLaunchStoryboardImages = platformHasLaunchStoryboardImages(platformConfig); - var hasLegacyLaunchImages = platformHasLegacyLaunchImages(platformConfig); - var currentLaunchStoryboard = infoPlist[UI_LAUNCH_STORYBOARD_NAME]; - - if (hasLaunchStoryboardImages && currentLaunchStoryboard === CDV_LAUNCH_STORYBOARD_NAME && !hasLegacyLaunchImages) { - // don't need legacy launch images if we are using our launch storyboard - // so we do need to update the project file - events.emit('verbose', 'Need to update build settings because project is using our launch storyboard.'); - return true; - } else if (hasLegacyLaunchImages && !currentLaunchStoryboard) { - // we do need to ensure legacy launch images are used if there's no launch storyboard present - // so we do need to update the project file - events.emit('verbose', 'Need to update build settings because project is using legacy launch images and no storyboard.'); - return true; - } - events.emit('verbose', 'No need to update build settings for launch storyboard support.'); - return false; -} - -function updateBuildSettingsForLaunchStoryboard (proj, platformConfig, infoPlist) { - var hasLaunchStoryboardImages = platformHasLaunchStoryboardImages(platformConfig); - var hasLegacyLaunchImages = platformHasLegacyLaunchImages(platformConfig); - var currentLaunchStoryboard = infoPlist[UI_LAUNCH_STORYBOARD_NAME]; - - if (hasLaunchStoryboardImages && currentLaunchStoryboard === CDV_LAUNCH_STORYBOARD_NAME && !hasLegacyLaunchImages) { - // don't need legacy launch images if we are using our launch storyboard - events.emit('verbose', 'Removed ' + LAUNCHIMAGE_BUILD_SETTING + ' because project is using our launch storyboard.'); - proj.removeBuildProperty(LAUNCHIMAGE_BUILD_SETTING); - } else if (hasLegacyLaunchImages && !currentLaunchStoryboard) { - // we do need to ensure legacy launch images are used if there's no launch storyboard present - events.emit('verbose', 'Set ' + LAUNCHIMAGE_BUILD_SETTING + ' to ' + LAUNCHIMAGE_BUILD_SETTING_VALUE + ' because project is using legacy launch images and no storyboard.'); - proj.updateBuildProperty(LAUNCHIMAGE_BUILD_SETTING, LAUNCHIMAGE_BUILD_SETTING_VALUE); - } else { - events.emit('verbose', 'Did not update build settings for launch storyboard support.'); - } -} - -function splashScreensHaveLaunchStoryboardImages (contentsJSON) { - /* do we have any launch images do we have for our launch storyboard? - * Again, for old Node versions, the below code is equivalent to this: - * return !!contentsJSON.images.find(function (item) { - * return item.filename !== undefined; - * }); - */ - return !!contentsJSON.images.reduce(function (p, c) { - return (c.filename !== undefined) ? c : p; - }, undefined); -} - -function platformHasLaunchStoryboardImages (platformConfig) { - var splashScreens = platformConfig.getSplashScreens('ios'); - var contentsJSON = getLaunchStoryboardContentsJSON(splashScreens, ''); // note: we don't need a file path here; we're just counting - return splashScreensHaveLaunchStoryboardImages(contentsJSON); -} - -function platformHasLegacyLaunchImages (platformConfig) { - var splashScreens = platformConfig.getSplashScreens('ios'); - return !!splashScreens.reduce(function (p, c) { - return (c.width !== undefined || c.height !== undefined) ? c : p; - }, undefined); -} - -/** - * Updates the project's plist based upon our launch storyboard images. If there are no images, then we should - * fall back to the regular launch images that might be supplied (that is, our app will be scaled on an iPad Pro), - * and if there are some images, we need to alter the UILaunchStoryboardName property to point to - * CDVLaunchScreen. - * - * There's some logic here to avoid overwriting changes the user might have made to their plist if they are using - * their own launch storyboard. - */ -function updateProjectPlistForLaunchStoryboard (platformConfig, infoPlist) { - var currentLaunchStoryboard = infoPlist[UI_LAUNCH_STORYBOARD_NAME]; - events.emit('verbose', 'Current launch storyboard ' + currentLaunchStoryboard); - - var hasLaunchStoryboardImages = platformHasLaunchStoryboardImages(platformConfig); - - if (hasLaunchStoryboardImages && !currentLaunchStoryboard) { - // only change the launch storyboard if we have images to use AND the current value is blank - // if it's not blank, we've either done this before, or the user has their own launch storyboard - events.emit('verbose', 'Changing info plist to use our launch storyboard'); - infoPlist[UI_LAUNCH_STORYBOARD_NAME] = CDV_LAUNCH_STORYBOARD_NAME; - return; - } - - if (!hasLaunchStoryboardImages && currentLaunchStoryboard === CDV_LAUNCH_STORYBOARD_NAME) { - // only revert to using the launch images if we have don't have any images for the launch storyboard - // but only clear it if current launch storyboard is our storyboard; the user might be using their - // own storyboard instead. - events.emit('verbose', 'Changing info plist to use legacy launch images'); - delete infoPlist[UI_LAUNCH_STORYBOARD_NAME]; - return; - } - events.emit('verbose', 'Not changing launch storyboard setting in info plist.'); -} - -/** * Returns the directory for the Launch Storyboard image set, if image sets are being used. If they aren't * being used, returns null. * @@ -812,8 +812,8 @@ function updateProjectPlistForLaunchStoryboard (platformConfig, infoPlist) { * @param {string} platformProjDir The platform's project directory */ function getLaunchStoryboardImagesDir (projectRoot, platformProjDir) { - var launchStoryboardImagesDir; - var xcassetsExists = folderExists(path.join(projectRoot, platformProjDir, 'Images.xcassets/')); + let launchStoryboardImagesDir; + const xcassetsExists = folderExists(path.join(projectRoot, platformProjDir, 'Images.xcassets/')); if (xcassetsExists) { launchStoryboardImagesDir = path.join(platformProjDir, 'Images.xcassets/LaunchStoryboard.imageset/'); @@ -832,15 +832,15 @@ function getLaunchStoryboardImagesDir (projectRoot, platformProjDir) { * @param {Object} locations A dictionary containing useful location paths */ function updateLaunchStoryboardImages (cordovaProject, locations) { - var splashScreens = cordovaProject.projectConfig.getSplashScreens('ios'); - var platformProjDir = path.relative(cordovaProject.root, locations.xcodeCordovaProj); - var launchStoryboardImagesDir = getLaunchStoryboardImagesDir(cordovaProject.root, platformProjDir); + const splashScreens = cordovaProject.projectConfig.getSplashScreens('ios'); + const platformProjDir = path.relative(cordovaProject.root, locations.xcodeCordovaProj); + const launchStoryboardImagesDir = getLaunchStoryboardImagesDir(cordovaProject.root, platformProjDir); if (launchStoryboardImagesDir) { - var resourceMap = mapLaunchStoryboardResources(splashScreens, launchStoryboardImagesDir); - var contentsJSON = getLaunchStoryboardContentsJSON(splashScreens, launchStoryboardImagesDir); + const resourceMap = mapLaunchStoryboardResources(splashScreens, launchStoryboardImagesDir); + const contentsJSON = getLaunchStoryboardContentsJSON(splashScreens, launchStoryboardImagesDir); - events.emit('verbose', 'Updating launch storyboard images at ' + launchStoryboardImagesDir); + events.emit('verbose', `Updating launch storyboard images at ${launchStoryboardImagesDir}`); FileUpdater.updatePaths( resourceMap, { rootDir: cordovaProject.root }, logFileOp); @@ -859,24 +859,24 @@ function updateLaunchStoryboardImages (cordovaProject, locations) { * @param {Object} locations A dictionary containing useful location paths */ function cleanLaunchStoryboardImages (projectRoot, projectConfig, locations) { - var splashScreens = projectConfig.getSplashScreens('ios'); - var platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); - var launchStoryboardImagesDir = getLaunchStoryboardImagesDir(projectRoot, platformProjDir); + const splashScreens = projectConfig.getSplashScreens('ios'); + const platformProjDir = path.relative(projectRoot, locations.xcodeCordovaProj); + const launchStoryboardImagesDir = getLaunchStoryboardImagesDir(projectRoot, platformProjDir); if (launchStoryboardImagesDir) { - var resourceMap = mapLaunchStoryboardResources(splashScreens, launchStoryboardImagesDir); - var contentsJSON = getLaunchStoryboardContentsJSON(splashScreens, launchStoryboardImagesDir); + const resourceMap = mapLaunchStoryboardResources(splashScreens, launchStoryboardImagesDir); + const contentsJSON = getLaunchStoryboardContentsJSON(splashScreens, launchStoryboardImagesDir); - Object.keys(resourceMap).forEach(function (targetPath) { + Object.keys(resourceMap).forEach(targetPath => { resourceMap[targetPath] = null; }); - events.emit('verbose', 'Cleaning storyboard image set at ' + launchStoryboardImagesDir); + events.emit('verbose', `Cleaning storyboard image set at ${launchStoryboardImagesDir}`); // Source paths are removed from the map, so updatePaths() will delete the target files. FileUpdater.updatePaths( resourceMap, { rootDir: projectRoot, all: true }, logFileOp); // delete filename from contents.json - contentsJSON.images.forEach(function (image) { + contentsJSON.images.forEach(image => { image.filename = undefined; }); @@ -896,10 +896,9 @@ function cleanLaunchStoryboardImages (projectRoot, projectConfig, locations) { * (or empty string if both are undefined). */ function getOrientationValue (platformConfig) { + const ORIENTATION_DEFAULT = 'default'; - var ORIENTATION_DEFAULT = 'default'; - - var orientation = platformConfig.getPreference('orientation'); + let orientation = platformConfig.getPreference('orientation'); if (!orientation) { return ''; } @@ -911,8 +910,7 @@ function getOrientationValue (platformConfig) { return orientation; } - events.emit('warn', 'Unrecognized value for Orientation preference: ' + orientation + - '. Defaulting to value: ' + ORIENTATION_DEFAULT + '.'); + events.emit('warn', `Unrecognized value for Orientation preference: ${orientation}. Defaulting to value: ${ORIENTATION_DEFAULT}.`); return ORIENTATION_DEFAULT; } @@ -938,20 +936,20 @@ function getOrientationValue (platformConfig) { } */ function processAccessAndAllowNavigationEntries (config) { - var accesses = config.getAccesses(); - var allow_navigations = config.getAllowNavigations(); + const accesses = config.getAccesses(); + const allow_navigations = config.getAllowNavigations(); return allow_navigations - // we concat allow_navigations and accesses, after processing accesses - .concat(accesses.map(function (obj) { + // we concat allow_navigations and accesses, after processing accesses + .concat(accesses.map(obj => { // map accesses to a common key interface using 'href', not origin obj.href = obj.origin; delete obj.origin; return obj; })) // we reduce the array to an object with all the entries processed (key is Hostname) - .reduce(function (previousReturn, currentElement) { - var options = { + .reduce((previousReturn, currentElement) => { + const options = { minimum_tls_version: currentElement.minimum_tls_version, requires_forward_secrecy: currentElement.requires_forward_secrecy, requires_certificate_transparency: currentElement.requires_certificate_transparency, @@ -959,16 +957,16 @@ function processAccessAndAllowNavigationEntries (config) { allows_arbitrary_loads_in_web_content: currentElement.allows_arbitrary_loads_in_web_content, allows_local_networking: currentElement.allows_local_networking }; - var obj = parseWhitelistUrlForATS(currentElement.href, options); + const obj = parseWhitelistUrlForATS(currentElement.href, options); if (obj) { // we 'union' duplicate entries - var item = previousReturn[obj.Hostname]; + let item = previousReturn[obj.Hostname]; if (!item) { item = {}; } - for (var o in obj) { - if (obj.hasOwnProperty(o)) { + for (const o in obj) { + if (Object.prototype.hasOwnProperty.call(obj, o)) { item[o] = obj[o]; } } @@ -999,15 +997,16 @@ function processAccessAndAllowNavigationEntries (config) { null is returned if the URL cannot be parsed, or is to be skipped for ATS. */ function parseWhitelistUrlForATS (url, options) { - var href = URL.parse(url); - var retObj = {}; + // @todo 'url.parse' was deprecated since v11.0.0. Use 'url.URL' constructor instead. + const href = URL.parse(url); // eslint-disable-line + const retObj = {}; retObj.Hostname = href.hostname; // Guiding principle: we only set values in retObj if they are NOT the default if (url === '*') { retObj.Hostname = '*'; - var val; + let val; val = (options.allows_arbitrary_loads_in_web_content === 'true'); if (options.allows_arbitrary_loads_in_web_content && val) { // default is false @@ -1029,10 +1028,12 @@ function parseWhitelistUrlForATS (url, options) { if (!retObj.Hostname) { // check origin, if it allows subdomains (wildcard in hostname), we set NSIncludesSubdomains to YES. Default is NO - var subdomain1 = '/*.'; // wildcard in hostname - var subdomain2 = '*://*.'; // wildcard in hostname and protocol - var subdomain3 = '*://'; // wildcard in protocol only - if (href.pathname.indexOf(subdomain1) === 0) { + const subdomain1 = '/*.'; // wildcard in hostname + const subdomain2 = '*://*.'; // wildcard in hostname and protocol + const subdomain3 = '*://'; // wildcard in protocol only + if (!href.pathname) { + return null; + } else if (href.pathname.indexOf(subdomain1) === 0) { retObj.NSIncludesSubdomains = true; retObj.Hostname = href.pathname.substring(subdomain1.length); } else if (href.pathname.indexOf(subdomain2) === 0) { @@ -1050,12 +1051,12 @@ function parseWhitelistUrlForATS (url, options) { retObj.NSExceptionMinimumTLSVersion = options.minimum_tls_version; } - var rfs = (options.requires_forward_secrecy === 'true'); + const rfs = (options.requires_forward_secrecy === 'true'); if (options.requires_forward_secrecy && !rfs) { // default is true retObj.NSExceptionRequiresForwardSecrecy = false; } - var rct = (options.requires_certificate_transparency === 'true'); + const rct = (options.requires_certificate_transparency === 'true'); if (options.requires_certificate_transparency && rct) { // default is false retObj.NSRequiresCertificateTransparency = true; } @@ -1075,48 +1076,48 @@ function parseWhitelistUrlForATS (url, options) { in config.xml */ function writeATSEntries (config) { - var pObj = processAccessAndAllowNavigationEntries(config); + const pObj = processAccessAndAllowNavigationEntries(config); - var ats = {}; + const ats = {}; - for (var hostname in pObj) { - if (pObj.hasOwnProperty(hostname)) { - var entry = pObj[hostname]; + for (const hostname in pObj) { + if (Object.prototype.hasOwnProperty.call(pObj, hostname)) { + const entry = pObj[hostname]; // Guiding principle: we only set values if they are available if (hostname === '*') { // always write this, for iOS 9, since in iOS 10 it will be overriden if // any of the other three keys are written - ats['NSAllowsArbitraryLoads'] = true; + ats.NSAllowsArbitraryLoads = true; // at least one of the overriding keys is present if (entry.NSAllowsArbitraryLoadsInWebContent) { - ats['NSAllowsArbitraryLoadsInWebContent'] = true; + ats.NSAllowsArbitraryLoadsInWebContent = true; } if (entry.NSAllowsArbitraryLoadsForMedia) { - ats['NSAllowsArbitraryLoadsForMedia'] = true; + ats.NSAllowsArbitraryLoadsForMedia = true; } if (entry.NSAllowsLocalNetworking) { - ats['NSAllowsLocalNetworking'] = true; + ats.NSAllowsLocalNetworking = true; } continue; } - var exceptionDomain = {}; + const exceptionDomain = {}; - for (var key in entry) { - if (entry.hasOwnProperty(key) && key !== 'Hostname') { + for (const key in entry) { + if (Object.prototype.hasOwnProperty.call(entry, key) && key !== 'Hostname') { exceptionDomain[key] = entry[key]; } } - if (!ats['NSExceptionDomains']) { - ats['NSExceptionDomains'] = {}; + if (!ats.NSExceptionDomains) { + ats.NSExceptionDomains = {}; } - ats['NSExceptionDomains'][hostname] = exceptionDomain; + ats.NSExceptionDomains[hostname] = exceptionDomain; } } @@ -1125,7 +1126,7 @@ function writeATSEntries (config) { function folderExists (folderPath) { try { - var stat = fs.statSync(folderPath); + const stat = fs.statSync(folderPath); return stat && stat.isDirectory(); } catch (e) { return false; @@ -1141,10 +1142,10 @@ function default_CFBundleVersion (version) { // Converts cordova specific representation of target device to XCode value function parseTargetDevicePreference (value) { if (!value) return null; - var map = {'universal': '"1,2"', 'handset': '"1"', 'tablet': '"2"'}; + const map = { universal: '"1,2"', handset: '"1"', tablet: '"2"' }; if (map[value.toLowerCase()]) { return map[value.toLowerCase()]; } - events.emit('warn', 'Unrecognized value for target-device preference: ' + value + '.'); + events.emit('warn', `Unrecognized value for target-device preference: ${value}.`); return null; } |
