From 07383c17d8cb30c6e36fdccec52c90b4c6ba64e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A8=E3=83=AA=E3=82=B9?= Date: Wed, 1 Dec 2021 12:46:17 +0900 Subject: [PATCH] refactor!: do not copy JS lib to platform project (#1203) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raphael von der GrĂ¼n --- .eslintignore | 1 - bin/templates/scripts/cordova/Api.js | 686 +---------------- lib/Api.js | 704 ++++++++++++++++++ .../cordova/lib => lib}/BridgingHeader.js | 0 .../scripts/cordova/lib => lib}/Podfile.js | 0 .../scripts/cordova/lib => lib}/PodsJson.js | 0 .../scripts/cordova/lib => lib}/build.js | 0 .../scripts/cordova/lib => lib}/check_reqs.js | 0 .../scripts/cordova/lib => lib}/clean.js | 0 {bin/lib => lib}/create.js | 9 +- .../scripts/cordova/lib => lib}/list-devices | 0 .../cordova/lib => lib}/list-emulator-images | 0 .../cordova/lib => lib}/listDevices.js | 0 .../lib => lib}/listEmulatorBuildTargets.js | 0 .../cordova/lib => lib}/listEmulatorImages.js | 0 .../lib => lib}/plugman/pluginHandlers.js | 0 .../scripts/cordova/lib => lib}/prepare.js | 0 .../cordova/lib => lib}/projectFile.js | 0 .../scripts/cordova/lib => lib}/run.js | 0 .../scripts/cordova/lib => lib}/versions.js | 0 package.json | 4 +- tests/spec/create.spec.js | 9 +- tests/spec/unit/Api.spec.js | 14 +- tests/spec/unit/BridgingHeader.spec.js | 2 +- tests/spec/unit/Plugman/common.spec.js | 2 +- tests/spec/unit/Plugman/pluginHandler.spec.js | 6 +- tests/spec/unit/Podfile.spec.js | 2 +- tests/spec/unit/PodsJson.spec.js | 2 +- tests/spec/unit/build.spec.js | 4 +- tests/spec/unit/lib/check_reqs.spec.js | 4 +- tests/spec/unit/lib/list-devices.spec.js | 2 +- .../unit/lib/list-emulator-images.spec.js | 2 +- tests/spec/unit/lib/run.spec.js | 2 +- tests/spec/unit/pluginAdd.spec.js | 2 +- tests/spec/unit/prepare.spec.js | 6 +- tests/spec/unit/preparePlatform.spec.js | 2 +- tests/spec/unit/projectFile.spec.js | 2 +- tests/spec/unit/versions.spec.js | 2 +- 38 files changed, 745 insertions(+), 724 deletions(-) create mode 100644 lib/Api.js rename {bin/templates/scripts/cordova/lib => lib}/BridgingHeader.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/Podfile.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/PodsJson.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/build.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/check_reqs.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/clean.js (100%) rename {bin/lib => lib}/create.js (96%) rename {bin/templates/scripts/cordova/lib => lib}/list-devices (100%) rename {bin/templates/scripts/cordova/lib => lib}/list-emulator-images (100%) rename {bin/templates/scripts/cordova/lib => lib}/listDevices.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/listEmulatorBuildTargets.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/listEmulatorImages.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/plugman/pluginHandlers.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/prepare.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/projectFile.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/run.js (100%) rename {bin/templates/scripts/cordova/lib => lib}/versions.js (100%) diff --git a/.eslintignore b/.eslintignore index 27c0ed53e..d5d066965 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ -bin/node_modules/* templates/project/* tests/spec/unit/fixtures/* diff --git a/bin/templates/scripts/cordova/Api.js b/bin/templates/scripts/cordova/Api.js index 180346f5d..204138653 100644 --- a/bin/templates/scripts/cordova/Api.js +++ b/bin/templates/scripts/cordova/Api.js @@ -17,688 +17,4 @@ under the License. */ -/* jslint node: true */ - -/** - * @todo update coho to update this line. - * @todo use `package.json` instead but first - * figure out how this fit in with the platform-centered workflow structure. - * This workflow would not have the `package.json` file. - */ -// Coho updates this line -const VERSION = '7.0.0-dev'; - -const fs = require('fs-extra'); -const path = require('path'); -const unorm = require('unorm'); -const projectFile = require('./lib/projectFile'); -const check_reqs = require('./lib/check_reqs'); -const { - ConfigParser, - CordovaError, - CordovaLogger, - events, - PluginManager -} = require('cordova-common'); -const util = require('util'); -const xcode = require('xcode'); - -function setupEvents (externalEventEmitter) { - if (externalEventEmitter) { - // This will make the platform internal events visible outside - events.forwardEventsTo(externalEventEmitter); - } else { - // There is no logger if external emitter is not present, - // so attach a console logger - CordovaLogger.get().subscribe(events); - } -} - -function getVariableSpec (spec, options) { - return spec.includes('$') ? options.cli_variables[spec.replace('$', '')] : spec; -} - -class Api { - /** - * Creates a new PlatformApi instance. - * - * @param {String} [platform] Platform name, used for backward compatibility - * w/ PlatformPoly (CordovaLib). - * @param {String} [platformRootDir] Platform root location, used for backward - * compatibility w/ PlatformPoly (CordovaLib). - * @param {EventEmitter} [events] An EventEmitter instance that will be used for - * logging purposes. If no EventEmitter provided, all events will be logged to - * console - */ - constructor (platform, platformRootDir, events) { - // 'platform' property is required as per PlatformApi spec - this.platform = platform || 'ios'; - this.root = platformRootDir; - - setupEvents(events); - - let xcodeProjDir; - let xcodeCordovaProj; - - try { - const xcodeProjDir_array = fs.readdirSync(this.root).filter(e => e.match(/\.xcodeproj$/i)); - if (xcodeProjDir_array.length > 1) { - for (let x = 0; x < xcodeProjDir_array.length; x++) { - if (xcodeProjDir_array[x].substring(0, 2) === '._') { - xcodeProjDir_array.splice(x, 1); - } - } - } - xcodeProjDir = xcodeProjDir_array[0]; - - if (!xcodeProjDir) { - throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`); - } - - const cordovaProjName = xcodeProjDir.substring(xcodeProjDir.lastIndexOf(path.sep) + 1, xcodeProjDir.indexOf('.xcodeproj')); - xcodeCordovaProj = path.join(this.root, cordovaProjName); - } catch (e) { - throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`); - } - - this.locations = { - root: this.root, - www: path.join(this.root, 'www'), - platformWww: path.join(this.root, 'platform_www'), - configXml: path.join(xcodeCordovaProj, 'config.xml'), - defaultConfigXml: path.join(this.root, 'cordova/defaults.xml'), - pbxproj: path.join(this.root, xcodeProjDir, 'project.pbxproj'), - xcodeProjDir: path.join(this.root, xcodeProjDir), - xcodeCordovaProj - }; - } - - /** - * Creates platform in a specified directory. - * - * @param {String} destination Destination directory, where install platform to - * @param {ConfigParser} [config] ConfigParser instance, used to retrieve - * project creation options, such as package id and project name. - * @param {Object} [options] An options object. The most common options are: - * @param {String} [options.customTemplate] A path to custom template, that - * should override the default one from platform. - * @param {Boolean} [options.link] Flag that indicates that platform's - * sources will be linked to installed platform instead of copying. - * @param {EventEmitter} [events] An EventEmitter instance that will be used for - * logging purposes. If no EventEmitter provided, all events will be logged to - * console - * - * @return {Promise} Promise either fulfilled with PlatformApi - * instance or rejected with CordovaError. - */ - static createPlatform (destination, config, options, events) { - setupEvents(events); - - // 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 - const name = unorm.nfd(config.name()); - let result; - try { - result = require('../../../lib/create') - .createProject(destination, config.getAttribute('ios-CFBundleIdentifier') || config.packageName(), name, options, config) - .then(() => { - // after platform is created we return Api instance based on new Api.js location - // This is required to correctly resolve paths in the future api calls - const PlatformApi = require(path.resolve(destination, 'cordova/Api')); - return new PlatformApi('ios', destination, events); - }); - } catch (e) { - events.emit('error', 'createPlatform is not callable from the iOS project API.'); - throw e; - } - return result; - } - - /** - * Updates already installed platform. - * - * @param {String} destination Destination directory, where platform installed - * @param {Object} [options] An options object. The most common options are: - * @param {String} [options.customTemplate] A path to custom template, that - * should override the default one from platform. - * @param {Boolean} [options.link] Flag that indicates that platform's - * sources will be linked to installed platform instead of copying. - * @param {EventEmitter} [events] An EventEmitter instance that will be used for - * logging purposes. If no EventEmitter provided, all events will be logged to - * console - * - * @return {Promise} Promise either fulfilled with PlatformApi - * instance or rejected with CordovaError. - */ - static updatePlatform (destination, options, events) { - setupEvents(events); - - const errorString = - 'The update platform command is not supported.\n' + - 'The `platforms` folder is always treated as a build artifact.\n' + - 'To update, you have to remove the old platform and add the new platform.\n' + - 'Make sure to save your plugins beforehand using `cordova plugin save`, and save a copy of the platform first if you had manual changes.\n' + - '\tcordova plugin save\n' + - '\tcordova platform rm ios\n' + - '\tcordova platform add ios\n'; - - return Promise.reject(new CordovaError(errorString)); - } - - /** - * Gets a CordovaPlatform object, that represents the platform structure. - * - * @return {CordovaPlatform} A structure that contains the description of - * platform's file structure and other properties of platform. - */ - getPlatformInfo () { - return { - locations: this.locations, - root: this.root, - name: this.platform, - version: Api.version(), - projectConfig: new ConfigParser(this.locations.configXml) - }; - } - - /** - * Updates installed platform with provided www assets and new app - * configuration. This method is required for CLI workflow and will be called - * each time before build, so the changes, made to app configuration and www - * code, will be applied to platform. - * - * @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a - * project structure and configuration, that should be applied to platform - * (contains project's www location and ConfigParser instance for project's - * config). - * - * @return {Promise} Return a promise either fulfilled, or rejected with - * CordovaError instance. - */ - prepare (cordovaProject) { - cordovaProject.projectConfig = new ConfigParser(cordovaProject.locations.rootConfigXml || cordovaProject.projectConfig.path); - - return require('./lib/prepare').prepare.call(this, cordovaProject); - } - - /** - * Installs a new plugin into platform. It doesn't resolves plugin dependencies. - * - * @param {PluginInfo} plugin A PluginInfo instance that represents plugin - * that will be installed. - * @param {Object} installOptions An options object. Possible options below: - * @param {Boolean} installOptions.link: Flag that specifies that plugin - * sources will be symlinked to app's directory instead of copying (if - * possible). - * @param {Object} installOptions.variables An object that represents - * variables that will be used to install plugin. See more details on plugin - * variables in documentation: - * https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html - * - * @return {Promise} Return a promise either fulfilled, or rejected with - * CordovaError instance. - */ - addPlugin (plugin, installOptions) { - const xcodeproj = projectFile.parse(this.locations); - - installOptions = installOptions || {}; - installOptions.variables = installOptions.variables || {}; - // Add PACKAGE_NAME variable into vars - if (!installOptions.variables.PACKAGE_NAME) { - installOptions.variables.PACKAGE_NAME = xcodeproj.getPackageName(); - } - - return PluginManager.get(this.platform, this.locations, xcodeproj) - .addPlugin(plugin, installOptions) - .then(() => { - if (plugin != null) { - const headerTags = plugin.getHeaderFiles(this.platform); - const bridgingHeaders = headerTags.filter(obj => obj.type === 'BridgingHeader'); - if (bridgingHeaders.length > 0) { - const project_dir = this.locations.root; - const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); - const BridgingHeader = require('./lib/BridgingHeader').BridgingHeader; - const bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h')); - events.emit('verbose', 'Adding Bridging-Headers since the plugin contained with type="BridgingHeader"'); - bridgingHeaders.forEach(obj => { - const bridgingHeaderPath = path.basename(obj.src); - bridgingHeaderFile.addHeader(plugin.id, bridgingHeaderPath); - }); - bridgingHeaderFile.write(); - } - } - }) - .then(() => { - if (plugin != null) { - const podSpecs = plugin.getPodSpecs ? plugin.getPodSpecs(this.platform) : []; - const frameworkTags = plugin.getFrameworks(this.platform); - const frameworkPods = frameworkTags.filter(obj => obj.type === 'podspec'); - return this.addPodSpecs(plugin, podSpecs, frameworkPods, installOptions); - } - }) - // CB-11022 Return truthy value to prevent running prepare after - .then(() => true); - } - - /** - * Removes an installed plugin from platform. - * - * Since method accepts PluginInfo instance as input parameter instead of plugin - * id, caller shoud take care of managing/storing PluginInfo instances for - * future uninstalls. - * - * @param {PluginInfo} plugin A PluginInfo instance that represents plugin - * that will be installed. - * - * @return {Promise} Return a promise either fulfilled, or rejected with - * CordovaError instance. - */ - removePlugin (plugin, uninstallOptions) { - const xcodeproj = projectFile.parse(this.locations); - - return PluginManager.get(this.platform, this.locations, xcodeproj) - .removePlugin(plugin, uninstallOptions) - .then(() => { - if (plugin != null) { - const headerTags = plugin.getHeaderFiles(this.platform); - const bridgingHeaders = headerTags.filter(obj => obj.type === 'BridgingHeader'); - if (bridgingHeaders.length > 0) { - const project_dir = this.locations.root; - const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); - const BridgingHeader = require('./lib/BridgingHeader').BridgingHeader; - const bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h')); - events.emit('verbose', 'Removing Bridging-Headers since the plugin contained with type="BridgingHeader"'); - bridgingHeaders.forEach(obj => { - const bridgingHeaderPath = path.basename(obj.src); - bridgingHeaderFile.removeHeader(plugin.id, bridgingHeaderPath); - }); - bridgingHeaderFile.write(); - } - } - }) - .then(() => { - if (plugin != null) { - const podSpecs = plugin.getPodSpecs ? plugin.getPodSpecs(this.platform) : []; - const frameworkTags = plugin.getFrameworks(this.platform); - const frameworkPods = frameworkTags.filter(obj => obj.type === 'podspec'); - return this.removePodSpecs(plugin, podSpecs, frameworkPods, uninstallOptions); - } - }) - // CB-11022 Return truthy value to prevent running prepare after - .then(() => true); - } - - /** - * adding CocoaPods libraries - * - * @param {PluginInfo} plugin A PluginInfo instance that represents plugin - * that will be installed. - * @param {Object} podSpecs: the return value of plugin.getPodSpecs(this.platform) - * @param {Object} frameworkPods: framework tags object with type === 'podspec' - * @return {Promise} Return a promise - */ - addPodSpecs (plugin, podSpecs, frameworkPods, installOptions) { - const project_dir = this.locations.root; - const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); - const minDeploymentTarget = this.getPlatformInfo().projectConfig.getPreference('deployment-target', 'ios'); - - const Podfile = require('./lib/Podfile').Podfile; - const PodsJson = require('./lib/PodsJson').PodsJson; - const podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME)); - const podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name, minDeploymentTarget); - - if (podSpecs.length) { - events.emit('verbose', 'Adding pods since the plugin contained '); - podSpecs.forEach(obj => { - // declarations - if (obj.declarations) { - Object.keys(obj.declarations).forEach(key => { - if (obj.declarations[key] === 'true') { - const declaration = Podfile.proofDeclaration(key); - const podJson = { - declaration - }; - const val = podsjsonFile.getDeclaration(declaration); - if (val) { - podsjsonFile.incrementDeclaration(declaration); - } else { - podJson.count = 1; - podsjsonFile.setJsonDeclaration(declaration, podJson); - podfileFile.addDeclaration(podJson.declaration); - } - } - }); - } - - // sources - if (obj.sources) { - Object.keys(obj.sources).forEach(key => { - const podJson = { - source: obj.sources[key].source - }; - const val = podsjsonFile.getSource(key); - if (val) { - podsjsonFile.incrementSource(key); - } else { - podJson.count = 1; - podsjsonFile.setJsonSource(key, podJson); - podfileFile.addSource(podJson.source); - } - }); - } - - // libraries - if (obj.libraries) { - Object.keys(obj.libraries).forEach(key => { - const podJson = Object.assign({}, obj.libraries[key]); - if (podJson.spec) { - podJson.spec = getVariableSpec(podJson.spec, installOptions); - } - const val = podsjsonFile.getLibrary(key); - if (val) { - events.emit('warn', `${plugin.id} depends on ${podJson.name}, which may conflict with another plugin. ${podJson.name}@${val.spec} is already installed and was not overwritten.`); - podsjsonFile.incrementLibrary(key); - } else { - podJson.count = 1; - podsjsonFile.setJsonLibrary(key, podJson); - podfileFile.addSpec(podJson.name, podJson); - } - }); - } - }); - } - - if (frameworkPods.length) { - events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.'); - events.emit('verbose', 'Adding pods since the plugin contained (s) with type="podspec"'); - frameworkPods.forEach(obj => { - const spec = getVariableSpec(obj.spec, installOptions); - const podJson = { - name: obj.src, - type: obj.type, - spec - }; - - const val = podsjsonFile.getLibrary(podJson.name); - if (val) { // found - if (podJson.spec !== val.spec) { // exists, different spec, print warning - events.emit('warn', `${plugin.id} depends on ${podJson.name}@${podJson.spec}, which conflicts with another plugin. ${podJson.name}@${val.spec} is already installed and was not overwritten.`); - } - // increment count, but don't add in Podfile because it already exists - podsjsonFile.incrementLibrary(podJson.name); - } else { // not found, write new - podJson.count = 1; - podsjsonFile.setJsonLibrary(podJson.name, podJson); - // add to Podfile - podfileFile.addSpec(podJson.name, podJson.spec); - } - }); - } - - if (podSpecs.length > 0 || frameworkPods.length > 0) { - // now that all the pods have been processed, write to pods.json - podsjsonFile.write(); - - // only write and pod install if the Podfile changed - if (podfileFile.isDirty()) { - podfileFile.write(); - events.emit('verbose', 'Running `pod install` (to install plugins)'); - projectFile.purgeProjectFileCache(this.locations.root); - - return podfileFile.install(check_reqs.check_cocoapods) - .then(() => this.setSwiftVersionForCocoaPodsLibraries(podsjsonFile)); - } else { - events.emit('verbose', 'Podfile unchanged, skipping `pod install`'); - } - } - return Promise.resolve(); - } - - /** - * removing CocoaPods libraries - * - * @param {PluginInfo} plugin A PluginInfo instance that represents plugin - * that will be installed. - * @param {Object} podSpecs: the return value of plugin.getPodSpecs(this.platform) - * @param {Object} frameworkPods: framework tags object with type === 'podspec' - * @return {Promise} Return a promise - */ - - removePodSpecs (plugin, podSpecs, frameworkPods, uninstallOptions) { - const project_dir = this.locations.root; - const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); - - const Podfile = require('./lib/Podfile').Podfile; - const PodsJson = require('./lib/PodsJson').PodsJson; - const podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME)); - const podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name); - - if (podSpecs.length) { - events.emit('verbose', 'Adding pods since the plugin contained '); - podSpecs.forEach(obj => { - // declarations - Object.keys(obj.declarations).forEach(key => { - if (obj.declarations[key] === 'true') { - const declaration = Podfile.proofDeclaration(key); - const podJson = { - declaration - }; - const val = podsjsonFile.getDeclaration(declaration); - if (val) { - podsjsonFile.decrementDeclaration(declaration); - } else { - const message = util.format('plugin \"%s\" declaration \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.declaration); /* eslint no-useless-escape : 0 */ - events.emit('verbose', message); - } - if (!val || val.count === 0) { - podfileFile.removeDeclaration(podJson.declaration); - } - } - }); - // sources - Object.keys(obj.sources).forEach(key => { - const podJson = { - source: obj.sources[key].source - }; - const val = podsjsonFile.getSource(key); - if (val) { - podsjsonFile.decrementSource(key); - } else { - const message = util.format('plugin \"%s\" source \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.source); /* eslint no-useless-escape : 0 */ - events.emit('verbose', message); - } - if (!val || val.count === 0) { - podfileFile.removeSource(podJson.source); - } - }); - // libraries - Object.keys(obj.libraries).forEach(key => { - const podJson = Object.assign({}, obj.libraries[key]); - if (podJson.spec) { - podJson.spec = getVariableSpec(podJson.spec, uninstallOptions); - } - const val = podsjsonFile.getLibrary(key); - if (val) { - podsjsonFile.decrementLibrary(key); - } else { - const message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */ - events.emit('verbose', message); - } - if (!val || val.count === 0) { - podfileFile.removeSpec(podJson.name); - } - }); - }); - } - - if (frameworkPods.length) { - events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.'); - events.emit('verbose', 'Adding pods since the plugin contained (s) with type=\"podspec\"'); /* eslint no-useless-escape : 0 */ - frameworkPods.forEach(obj => { - const spec = getVariableSpec(obj.spec, uninstallOptions); - const podJson = { - name: obj.src, - type: obj.type, - spec - }; - - const val = podsjsonFile.getLibrary(podJson.name); - if (val) { // found, decrement count - podsjsonFile.decrementLibrary(podJson.name); - } else { // not found (perhaps a sync error) - const message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */ - events.emit('verbose', message); - } - - // always remove from the Podfile - podfileFile.removeSpec(podJson.name); - }); - } - - if (podSpecs.length > 0 || frameworkPods.length > 0) { - // now that all the pods have been processed, write to pods.json - podsjsonFile.write(); - - if (podfileFile.isDirty()) { - podfileFile.write(); - events.emit('verbose', 'Running `pod install` (to uninstall pods)'); - - return podfileFile.install(check_reqs.check_cocoapods) - .then(() => this.setSwiftVersionForCocoaPodsLibraries(podsjsonFile)); - } else { - events.emit('verbose', 'Podfile unchanged, skipping `pod install`'); - } - } - return Promise.resolve(); - } - - /** - * set Swift Version for all CocoaPods libraries - * - * @param {PodsJson} podsjsonFile A PodsJson instance that represents pods.json - */ - setSwiftVersionForCocoaPodsLibraries (podsjsonFile) { - let __dirty = false; - return check_reqs.check_cocoapods().then(toolOptions => { - if (toolOptions.ignore) { - events.emit('verbose', '=== skip Swift Version Settings For Cocoapods Libraries'); - } else { - const podPbxPath = path.join(this.root, 'Pods', 'Pods.xcodeproj', 'project.pbxproj'); - const podXcodeproj = xcode.project(podPbxPath); - podXcodeproj.parseSync(); - const podTargets = podXcodeproj.pbxNativeTargetSection(); - const podConfigurationList = podXcodeproj.pbxXCConfigurationList(); - const podConfigs = podXcodeproj.pbxXCBuildConfigurationSection(); - - const libraries = podsjsonFile.getLibraries(); - Object.keys(libraries).forEach(key => { - const podJson = libraries[key]; - const name = podJson.name; - const swiftVersion = podJson['swift-version']; - if (swiftVersion) { - __dirty = true; - Object.keys(podTargets) - .filter(targetKey => podTargets[targetKey].productName === name) - .map(targetKey => podTargets[targetKey].buildConfigurationList) - .map(buildConfigurationListId => podConfigurationList[buildConfigurationListId]) - .map(buildConfigurationList => buildConfigurationList.buildConfigurations) - .reduce((acc, buildConfigurations) => acc.concat(buildConfigurations), []) - .map(buildConfiguration => buildConfiguration.value) - .forEach(buildId => { - __dirty = true; - podConfigs[buildId].buildSettings.SWIFT_VERSION = swiftVersion; - }); - } - }); - if (__dirty) { - fs.writeFileSync(podPbxPath, podXcodeproj.writeSync(), 'utf-8'); - } - } - }); - } - - /** - * Builds an application package for current platform. - * - * @param {Object} buildOptions A build options. This object's structure is - * highly depends on platform's specific. The most common options are: - * @param {Boolean} buildOptions.debug Indicates that packages should be - * built with debug configuration. This is set to true by default unless the - * 'release' option is not specified. - * @param {Boolean} buildOptions.release Indicates that packages should be - * built with release configuration. If not set to true, debug configuration - * will be used. - * @param {Boolean} buildOptions.device Specifies that built app is intended - * to run on device - * @param {Boolean} buildOptions.emulator: Specifies that built app is - * intended to run on emulator - * @param {String} buildOptions.target Specifies the device id that will be - * used to run built application. - * @param {Boolean} buildOptions.nobuild Indicates that this should be a - * dry-run call, so no build artifacts will be produced. - * @param {String[]} buildOptions.archs Specifies chip architectures which - * app packages should be built for. List of valid architectures is depends on - * platform. - * @param {String} buildOptions.buildConfig The path to build configuration - * file. The format of this file is depends on platform. - * @param {String[]} buildOptions.argv Raw array of command-line arguments, - * passed to `build` command. The purpose of this property is to pass a - * platform-specific arguments, and eventually let platform define own - * arguments processing logic. - * - * @return {Promise} Return a promise either fulfilled, or rejected with - * CordovaError instance. - */ - build (buildOptions) { - return check_reqs.run() - .then(() => require('./lib/build').run.call(this, buildOptions)); - } - - /** - * Builds an application package for current platform and runs it on - * specified/default device. If no 'device'/'emulator'/'target' options are - * specified, then tries to run app on default device if connected, otherwise - * runs the app on emulator. - * - * @param {Object} runOptions An options object. The structure is the same - * as for build options. - * - * @return {Promise} A promise either fulfilled if package was built and ran - * successfully, or rejected with CordovaError. - */ - run (runOptions) { - return check_reqs.run() - .then(() => require('./lib/run').run.call(this, runOptions)); - } - - /** - * Cleans out the build artifacts from platform's directory. - * - * @return {Promise} Return a promise either fulfilled, or rejected with - * CordovaError. - */ - clean (cleanOptions) { - return check_reqs.run() - .then(() => require('./lib/clean').run.call(this, cleanOptions)) - .then(() => require('./lib/prepare').clean.call(this, cleanOptions)); - } - - /** - * Performs a requirements check for current platform. Each platform defines its - * own set of requirements, which should be resolved before platform can be - * built successfully. - * - * @return {Promise} Promise, resolved with set of Requirement - * objects for current platform. - */ - requirements () { - return check_reqs.check_all(); - } - - static version () { - return VERSION; - } -} - -module.exports = Api; +module.exports = require('cordova-ios'); diff --git a/lib/Api.js b/lib/Api.js new file mode 100644 index 000000000..dc5b976d5 --- /dev/null +++ b/lib/Api.js @@ -0,0 +1,704 @@ +/** + 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. +*/ + +/* jslint node: true */ + +/** + * @todo update coho to update this line. + * @todo use `package.json` instead but first + * figure out how this fit in with the platform-centered workflow structure. + * This workflow would not have the `package.json` file. + */ +// Coho updates this line +const VERSION = '7.0.0-dev'; + +const fs = require('fs-extra'); +const path = require('path'); +const unorm = require('unorm'); +const projectFile = require('./projectFile'); +const check_reqs = require('./check_reqs'); +const { + ConfigParser, + CordovaError, + CordovaLogger, + events, + PluginManager +} = require('cordova-common'); +const util = require('util'); +const xcode = require('xcode'); + +function setupEvents (externalEventEmitter) { + if (externalEventEmitter) { + // This will make the platform internal events visible outside + events.forwardEventsTo(externalEventEmitter); + } else { + // There is no logger if external emitter is not present, + // so attach a console logger + CordovaLogger.get().subscribe(events); + } +} + +function getVariableSpec (spec, options) { + return spec.includes('$') ? options.cli_variables[spec.replace('$', '')] : spec; +} + +class Api { + /** + * Creates a new PlatformApi instance. + * + * @param {String} [platform] Platform name, used for backward compatibility + * w/ PlatformPoly (CordovaLib). + * @param {String} [platformRootDir] Platform root location, used for backward + * compatibility w/ PlatformPoly (CordovaLib). + * @param {EventEmitter} [events] An EventEmitter instance that will be used for + * logging purposes. If no EventEmitter provided, all events will be logged to + * console + */ + constructor (platform, platformRootDir, events) { + // 'platform' property is required as per PlatformApi spec + this.platform = platform || 'ios'; + this.root = platformRootDir; + + setupEvents(events); + + let xcodeProjDir; + let xcodeCordovaProj; + + try { + const xcodeProjDir_array = fs.readdirSync(this.root).filter(e => e.match(/\.xcodeproj$/i)); + if (xcodeProjDir_array.length > 1) { + for (let x = 0; x < xcodeProjDir_array.length; x++) { + if (xcodeProjDir_array[x].substring(0, 2) === '._') { + xcodeProjDir_array.splice(x, 1); + } + } + } + xcodeProjDir = xcodeProjDir_array[0]; + + if (!xcodeProjDir) { + throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`); + } + + const cordovaProjName = xcodeProjDir.substring(xcodeProjDir.lastIndexOf(path.sep) + 1, xcodeProjDir.indexOf('.xcodeproj')); + xcodeCordovaProj = path.join(this.root, cordovaProjName); + } catch (e) { + throw new CordovaError(`The provided path "${this.root}" is not a Cordova iOS project.`); + } + + this.locations = { + root: this.root, + www: path.join(this.root, 'www'), + platformWww: path.join(this.root, 'platform_www'), + configXml: path.join(xcodeCordovaProj, 'config.xml'), + defaultConfigXml: path.join(this.root, 'cordova/defaults.xml'), + pbxproj: path.join(this.root, xcodeProjDir, 'project.pbxproj'), + xcodeProjDir: path.join(this.root, xcodeProjDir), + xcodeCordovaProj + }; + } + + /** + * Creates platform in a specified directory. + * + * @param {String} destination Destination directory, where install platform to + * @param {ConfigParser} [config] ConfigParser instance, used to retrieve + * project creation options, such as package id and project name. + * @param {Object} [options] An options object. The most common options are: + * @param {String} [options.customTemplate] A path to custom template, that + * should override the default one from platform. + * @param {Boolean} [options.link] Flag that indicates that platform's + * sources will be linked to installed platform instead of copying. + * @param {EventEmitter} [events] An EventEmitter instance that will be used for + * logging purposes. If no EventEmitter provided, all events will be logged to + * console + * + * @return {Promise} Promise either fulfilled with PlatformApi + * instance or rejected with CordovaError. + */ + static createPlatform (destination, config, options, events) { + setupEvents(events); + + // 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 + const name = unorm.nfd(config.name()); + let result; + try { + result = require('./create') + .createProject(destination, config.getAttribute('ios-CFBundleIdentifier') || config.packageName(), name, options, config) + .then(() => { + // after platform is created we return Api instance based on new Api.js location + // This is required to correctly resolve paths in the future api calls + const PlatformApi = require(path.resolve(destination, 'cordova/Api')); + return new PlatformApi('ios', destination, events); + }); + } catch (e) { + events.emit('error', 'createPlatform is not callable from the iOS project API.'); + throw e; + } + return result; + } + + /** + * Updates already installed platform. + * + * @param {String} destination Destination directory, where platform installed + * @param {Object} [options] An options object. The most common options are: + * @param {String} [options.customTemplate] A path to custom template, that + * should override the default one from platform. + * @param {Boolean} [options.link] Flag that indicates that platform's + * sources will be linked to installed platform instead of copying. + * @param {EventEmitter} [events] An EventEmitter instance that will be used for + * logging purposes. If no EventEmitter provided, all events will be logged to + * console + * + * @return {Promise} Promise either fulfilled with PlatformApi + * instance or rejected with CordovaError. + */ + static updatePlatform (destination, options, events) { + setupEvents(events); + + const errorString = + 'The update platform command is not supported.\n' + + 'The `platforms` folder is always treated as a build artifact.\n' + + 'To update, you have to remove the old platform and add the new platform.\n' + + 'Make sure to save your plugins beforehand using `cordova plugin save`, and save a copy of the platform first if you had manual changes.\n' + + '\tcordova plugin save\n' + + '\tcordova platform rm ios\n' + + '\tcordova platform add ios\n'; + + return Promise.reject(new CordovaError(errorString)); + } + + /** + * Gets a CordovaPlatform object, that represents the platform structure. + * + * @return {CordovaPlatform} A structure that contains the description of + * platform's file structure and other properties of platform. + */ + getPlatformInfo () { + return { + locations: this.locations, + root: this.root, + name: this.platform, + version: Api.version(), + projectConfig: new ConfigParser(this.locations.configXml) + }; + } + + /** + * Updates installed platform with provided www assets and new app + * configuration. This method is required for CLI workflow and will be called + * each time before build, so the changes, made to app configuration and www + * code, will be applied to platform. + * + * @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a + * project structure and configuration, that should be applied to platform + * (contains project's www location and ConfigParser instance for project's + * config). + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ + prepare (cordovaProject) { + cordovaProject.projectConfig = new ConfigParser(cordovaProject.locations.rootConfigXml || cordovaProject.projectConfig.path); + + return require('./prepare').prepare.call(this, cordovaProject); + } + + /** + * Installs a new plugin into platform. It doesn't resolves plugin dependencies. + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * @param {Object} installOptions An options object. Possible options below: + * @param {Boolean} installOptions.link: Flag that specifies that plugin + * sources will be symlinked to app's directory instead of copying (if + * possible). + * @param {Object} installOptions.variables An object that represents + * variables that will be used to install plugin. See more details on plugin + * variables in documentation: + * https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ + addPlugin (plugin, installOptions) { + const xcodeproj = projectFile.parse(this.locations); + + installOptions = installOptions || {}; + installOptions.variables = installOptions.variables || {}; + // Add PACKAGE_NAME variable into vars + if (!installOptions.variables.PACKAGE_NAME) { + installOptions.variables.PACKAGE_NAME = xcodeproj.getPackageName(); + } + + return PluginManager.get(this.platform, this.locations, xcodeproj) + .addPlugin(plugin, installOptions) + .then(() => { + if (plugin != null) { + const headerTags = plugin.getHeaderFiles(this.platform); + const bridgingHeaders = headerTags.filter(obj => obj.type === 'BridgingHeader'); + if (bridgingHeaders.length > 0) { + const project_dir = this.locations.root; + const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); + const BridgingHeader = require('./BridgingHeader').BridgingHeader; + const bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h')); + events.emit('verbose', 'Adding Bridging-Headers since the plugin contained with type="BridgingHeader"'); + bridgingHeaders.forEach(obj => { + const bridgingHeaderPath = path.basename(obj.src); + bridgingHeaderFile.addHeader(plugin.id, bridgingHeaderPath); + }); + bridgingHeaderFile.write(); + } + } + }) + .then(() => { + if (plugin != null) { + const podSpecs = plugin.getPodSpecs ? plugin.getPodSpecs(this.platform) : []; + const frameworkTags = plugin.getFrameworks(this.platform); + const frameworkPods = frameworkTags.filter(obj => obj.type === 'podspec'); + return this.addPodSpecs(plugin, podSpecs, frameworkPods, installOptions); + } + }) + // CB-11022 Return truthy value to prevent running prepare after + .then(() => true); + } + + /** + * Removes an installed plugin from platform. + * + * Since method accepts PluginInfo instance as input parameter instead of plugin + * id, caller shoud take care of managing/storing PluginInfo instances for + * future uninstalls. + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ + removePlugin (plugin, uninstallOptions) { + const xcodeproj = projectFile.parse(this.locations); + + return PluginManager.get(this.platform, this.locations, xcodeproj) + .removePlugin(plugin, uninstallOptions) + .then(() => { + if (plugin != null) { + const headerTags = plugin.getHeaderFiles(this.platform); + const bridgingHeaders = headerTags.filter(obj => obj.type === 'BridgingHeader'); + if (bridgingHeaders.length > 0) { + const project_dir = this.locations.root; + const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); + const BridgingHeader = require('./BridgingHeader').BridgingHeader; + const bridgingHeaderFile = new BridgingHeader(path.join(project_dir, project_name, 'Bridging-Header.h')); + events.emit('verbose', 'Removing Bridging-Headers since the plugin contained with type="BridgingHeader"'); + bridgingHeaders.forEach(obj => { + const bridgingHeaderPath = path.basename(obj.src); + bridgingHeaderFile.removeHeader(plugin.id, bridgingHeaderPath); + }); + bridgingHeaderFile.write(); + } + } + }) + .then(() => { + if (plugin != null) { + const podSpecs = plugin.getPodSpecs ? plugin.getPodSpecs(this.platform) : []; + const frameworkTags = plugin.getFrameworks(this.platform); + const frameworkPods = frameworkTags.filter(obj => obj.type === 'podspec'); + return this.removePodSpecs(plugin, podSpecs, frameworkPods, uninstallOptions); + } + }) + // CB-11022 Return truthy value to prevent running prepare after + .then(() => true); + } + + /** + * adding CocoaPods libraries + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * @param {Object} podSpecs: the return value of plugin.getPodSpecs(this.platform) + * @param {Object} frameworkPods: framework tags object with type === 'podspec' + * @return {Promise} Return a promise + */ + addPodSpecs (plugin, podSpecs, frameworkPods, installOptions) { + const project_dir = this.locations.root; + const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); + const minDeploymentTarget = this.getPlatformInfo().projectConfig.getPreference('deployment-target', 'ios'); + + const Podfile = require('./Podfile').Podfile; + const PodsJson = require('./PodsJson').PodsJson; + const podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME)); + const podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name, minDeploymentTarget); + + if (podSpecs.length) { + events.emit('verbose', 'Adding pods since the plugin contained '); + podSpecs.forEach(obj => { + // declarations + if (obj.declarations) { + Object.keys(obj.declarations).forEach(key => { + if (obj.declarations[key] === 'true') { + const declaration = Podfile.proofDeclaration(key); + const podJson = { + declaration + }; + const val = podsjsonFile.getDeclaration(declaration); + if (val) { + podsjsonFile.incrementDeclaration(declaration); + } else { + podJson.count = 1; + podsjsonFile.setJsonDeclaration(declaration, podJson); + podfileFile.addDeclaration(podJson.declaration); + } + } + }); + } + + // sources + if (obj.sources) { + Object.keys(obj.sources).forEach(key => { + const podJson = { + source: obj.sources[key].source + }; + const val = podsjsonFile.getSource(key); + if (val) { + podsjsonFile.incrementSource(key); + } else { + podJson.count = 1; + podsjsonFile.setJsonSource(key, podJson); + podfileFile.addSource(podJson.source); + } + }); + } + + // libraries + if (obj.libraries) { + Object.keys(obj.libraries).forEach(key => { + const podJson = Object.assign({}, obj.libraries[key]); + if (podJson.spec) { + podJson.spec = getVariableSpec(podJson.spec, installOptions); + } + const val = podsjsonFile.getLibrary(key); + if (val) { + events.emit('warn', `${plugin.id} depends on ${podJson.name}, which may conflict with another plugin. ${podJson.name}@${val.spec} is already installed and was not overwritten.`); + podsjsonFile.incrementLibrary(key); + } else { + podJson.count = 1; + podsjsonFile.setJsonLibrary(key, podJson); + podfileFile.addSpec(podJson.name, podJson); + } + }); + } + }); + } + + if (frameworkPods.length) { + events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.'); + events.emit('verbose', 'Adding pods since the plugin contained (s) with type="podspec"'); + frameworkPods.forEach(obj => { + const spec = getVariableSpec(obj.spec, installOptions); + const podJson = { + name: obj.src, + type: obj.type, + spec + }; + + const val = podsjsonFile.getLibrary(podJson.name); + if (val) { // found + if (podJson.spec !== val.spec) { // exists, different spec, print warning + events.emit('warn', `${plugin.id} depends on ${podJson.name}@${podJson.spec}, which conflicts with another plugin. ${podJson.name}@${val.spec} is already installed and was not overwritten.`); + } + // increment count, but don't add in Podfile because it already exists + podsjsonFile.incrementLibrary(podJson.name); + } else { // not found, write new + podJson.count = 1; + podsjsonFile.setJsonLibrary(podJson.name, podJson); + // add to Podfile + podfileFile.addSpec(podJson.name, podJson.spec); + } + }); + } + + if (podSpecs.length > 0 || frameworkPods.length > 0) { + // now that all the pods have been processed, write to pods.json + podsjsonFile.write(); + + // only write and pod install if the Podfile changed + if (podfileFile.isDirty()) { + podfileFile.write(); + events.emit('verbose', 'Running `pod install` (to install plugins)'); + projectFile.purgeProjectFileCache(this.locations.root); + + return podfileFile.install(check_reqs.check_cocoapods) + .then(() => this.setSwiftVersionForCocoaPodsLibraries(podsjsonFile)); + } else { + events.emit('verbose', 'Podfile unchanged, skipping `pod install`'); + } + } + return Promise.resolve(); + } + + /** + * removing CocoaPods libraries + * + * @param {PluginInfo} plugin A PluginInfo instance that represents plugin + * that will be installed. + * @param {Object} podSpecs: the return value of plugin.getPodSpecs(this.platform) + * @param {Object} frameworkPods: framework tags object with type === 'podspec' + * @return {Promise} Return a promise + */ + + removePodSpecs (plugin, podSpecs, frameworkPods, uninstallOptions) { + const project_dir = this.locations.root; + const project_name = this.locations.xcodeCordovaProj.split(path.sep).pop(); + + const Podfile = require('./Podfile').Podfile; + const PodsJson = require('./PodsJson').PodsJson; + const podsjsonFile = new PodsJson(path.join(project_dir, PodsJson.FILENAME)); + const podfileFile = new Podfile(path.join(project_dir, Podfile.FILENAME), project_name); + + if (podSpecs.length) { + events.emit('verbose', 'Adding pods since the plugin contained '); + podSpecs.forEach(obj => { + // declarations + Object.keys(obj.declarations).forEach(key => { + if (obj.declarations[key] === 'true') { + const declaration = Podfile.proofDeclaration(key); + const podJson = { + declaration + }; + const val = podsjsonFile.getDeclaration(declaration); + if (val) { + podsjsonFile.decrementDeclaration(declaration); + } else { + const message = util.format('plugin \"%s\" declaration \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.declaration); /* eslint no-useless-escape : 0 */ + events.emit('verbose', message); + } + if (!val || val.count === 0) { + podfileFile.removeDeclaration(podJson.declaration); + } + } + }); + // sources + Object.keys(obj.sources).forEach(key => { + const podJson = { + source: obj.sources[key].source + }; + const val = podsjsonFile.getSource(key); + if (val) { + podsjsonFile.decrementSource(key); + } else { + const message = util.format('plugin \"%s\" source \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.source); /* eslint no-useless-escape : 0 */ + events.emit('verbose', message); + } + if (!val || val.count === 0) { + podfileFile.removeSource(podJson.source); + } + }); + // libraries + Object.keys(obj.libraries).forEach(key => { + const podJson = Object.assign({}, obj.libraries[key]); + if (podJson.spec) { + podJson.spec = getVariableSpec(podJson.spec, uninstallOptions); + } + const val = podsjsonFile.getLibrary(key); + if (val) { + podsjsonFile.decrementLibrary(key); + } else { + const message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */ + events.emit('verbose', message); + } + if (!val || val.count === 0) { + podfileFile.removeSpec(podJson.name); + } + }); + }); + } + + if (frameworkPods.length) { + events.emit('warn', '"framework" tag with type "podspec" is deprecated and will be removed. Please use the "podspec" tag.'); + events.emit('verbose', 'Adding pods since the plugin contained (s) with type=\"podspec\"'); /* eslint no-useless-escape : 0 */ + frameworkPods.forEach(obj => { + const spec = getVariableSpec(obj.spec, uninstallOptions); + const podJson = { + name: obj.src, + type: obj.type, + spec + }; + + const val = podsjsonFile.getLibrary(podJson.name); + if (val) { // found, decrement count + podsjsonFile.decrementLibrary(podJson.name); + } else { // not found (perhaps a sync error) + const message = util.format('plugin \"%s\" podspec \"%s\" does not seem to be in pods.json, nothing to remove. Will attempt to remove from Podfile.', plugin.id, podJson.name); /* eslint no-useless-escape : 0 */ + events.emit('verbose', message); + } + + // always remove from the Podfile + podfileFile.removeSpec(podJson.name); + }); + } + + if (podSpecs.length > 0 || frameworkPods.length > 0) { + // now that all the pods have been processed, write to pods.json + podsjsonFile.write(); + + if (podfileFile.isDirty()) { + podfileFile.write(); + events.emit('verbose', 'Running `pod install` (to uninstall pods)'); + + return podfileFile.install(check_reqs.check_cocoapods) + .then(() => this.setSwiftVersionForCocoaPodsLibraries(podsjsonFile)); + } else { + events.emit('verbose', 'Podfile unchanged, skipping `pod install`'); + } + } + return Promise.resolve(); + } + + /** + * set Swift Version for all CocoaPods libraries + * + * @param {PodsJson} podsjsonFile A PodsJson instance that represents pods.json + */ + setSwiftVersionForCocoaPodsLibraries (podsjsonFile) { + let __dirty = false; + return check_reqs.check_cocoapods().then(toolOptions => { + if (toolOptions.ignore) { + events.emit('verbose', '=== skip Swift Version Settings For Cocoapods Libraries'); + } else { + const podPbxPath = path.join(this.root, 'Pods', 'Pods.xcodeproj', 'project.pbxproj'); + const podXcodeproj = xcode.project(podPbxPath); + podXcodeproj.parseSync(); + const podTargets = podXcodeproj.pbxNativeTargetSection(); + const podConfigurationList = podXcodeproj.pbxXCConfigurationList(); + const podConfigs = podXcodeproj.pbxXCBuildConfigurationSection(); + + const libraries = podsjsonFile.getLibraries(); + Object.keys(libraries).forEach(key => { + const podJson = libraries[key]; + const name = podJson.name; + const swiftVersion = podJson['swift-version']; + if (swiftVersion) { + __dirty = true; + Object.keys(podTargets) + .filter(targetKey => podTargets[targetKey].productName === name) + .map(targetKey => podTargets[targetKey].buildConfigurationList) + .map(buildConfigurationListId => podConfigurationList[buildConfigurationListId]) + .map(buildConfigurationList => buildConfigurationList.buildConfigurations) + .reduce((acc, buildConfigurations) => acc.concat(buildConfigurations), []) + .map(buildConfiguration => buildConfiguration.value) + .forEach(buildId => { + __dirty = true; + podConfigs[buildId].buildSettings.SWIFT_VERSION = swiftVersion; + }); + } + }); + if (__dirty) { + fs.writeFileSync(podPbxPath, podXcodeproj.writeSync(), 'utf-8'); + } + } + }); + } + + /** + * Builds an application package for current platform. + * + * @param {Object} buildOptions A build options. This object's structure is + * highly depends on platform's specific. The most common options are: + * @param {Boolean} buildOptions.debug Indicates that packages should be + * built with debug configuration. This is set to true by default unless the + * 'release' option is not specified. + * @param {Boolean} buildOptions.release Indicates that packages should be + * built with release configuration. If not set to true, debug configuration + * will be used. + * @param {Boolean} buildOptions.device Specifies that built app is intended + * to run on device + * @param {Boolean} buildOptions.emulator: Specifies that built app is + * intended to run on emulator + * @param {String} buildOptions.target Specifies the device id that will be + * used to run built application. + * @param {Boolean} buildOptions.nobuild Indicates that this should be a + * dry-run call, so no build artifacts will be produced. + * @param {String[]} buildOptions.archs Specifies chip architectures which + * app packages should be built for. List of valid architectures is depends on + * platform. + * @param {String} buildOptions.buildConfig The path to build configuration + * file. The format of this file is depends on platform. + * @param {String[]} buildOptions.argv Raw array of command-line arguments, + * passed to `build` command. The purpose of this property is to pass a + * platform-specific arguments, and eventually let platform define own + * arguments processing logic. + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError instance. + */ + build (buildOptions) { + return check_reqs.run() + .then(() => require('./build').run.call(this, buildOptions)); + } + + /** + * Builds an application package for current platform and runs it on + * specified/default device. If no 'device'/'emulator'/'target' options are + * specified, then tries to run app on default device if connected, otherwise + * runs the app on emulator. + * + * @param {Object} runOptions An options object. The structure is the same + * as for build options. + * + * @return {Promise} A promise either fulfilled if package was built and ran + * successfully, or rejected with CordovaError. + */ + run (runOptions) { + return check_reqs.run() + .then(() => require('./run').run.call(this, runOptions)); + } + + /** + * Cleans out the build artifacts from platform's directory. + * + * @return {Promise} Return a promise either fulfilled, or rejected with + * CordovaError. + */ + clean (cleanOptions) { + return check_reqs.run() + .then(() => require('./clean').run.call(this, cleanOptions)) + .then(() => require('./prepare').clean.call(this, cleanOptions)); + } + + /** + * Performs a requirements check for current platform. Each platform defines its + * own set of requirements, which should be resolved before platform can be + * built successfully. + * + * @return {Promise} Promise, resolved with set of Requirement + * objects for current platform. + */ + requirements () { + return check_reqs.check_all(); + } + + static version () { + return VERSION; + } +} + +module.exports = Api; diff --git a/bin/templates/scripts/cordova/lib/BridgingHeader.js b/lib/BridgingHeader.js similarity index 100% rename from bin/templates/scripts/cordova/lib/BridgingHeader.js rename to lib/BridgingHeader.js diff --git a/bin/templates/scripts/cordova/lib/Podfile.js b/lib/Podfile.js similarity index 100% rename from bin/templates/scripts/cordova/lib/Podfile.js rename to lib/Podfile.js diff --git a/bin/templates/scripts/cordova/lib/PodsJson.js b/lib/PodsJson.js similarity index 100% rename from bin/templates/scripts/cordova/lib/PodsJson.js rename to lib/PodsJson.js diff --git a/bin/templates/scripts/cordova/lib/build.js b/lib/build.js similarity index 100% rename from bin/templates/scripts/cordova/lib/build.js rename to lib/build.js diff --git a/bin/templates/scripts/cordova/lib/check_reqs.js b/lib/check_reqs.js similarity index 100% rename from bin/templates/scripts/cordova/lib/check_reqs.js rename to lib/check_reqs.js diff --git a/bin/templates/scripts/cordova/lib/clean.js b/lib/clean.js similarity index 100% rename from bin/templates/scripts/cordova/lib/clean.js rename to lib/clean.js diff --git a/bin/lib/create.js b/lib/create.js similarity index 96% rename from bin/lib/create.js rename to lib/create.js index 29088ae82..5d79cef0b 100755 --- a/bin/lib/create.js +++ b/lib/create.js @@ -21,9 +21,9 @@ const path = require('path'); const fs = require('fs-extra'); const xmlescape = require('xml-escape'); const { CordovaError, events } = require('cordova-common'); -const pkg = require('../../package'); +const pkg = require('../package'); -const ROOT = path.join(__dirname, '../..'); +const ROOT = path.join(__dirname, '..'); /** * Creates a new iOS project with the following options: @@ -101,11 +101,6 @@ class ProjectCreator { const srcScriptsDir = path.join(ROOT, 'bin/templates/scripts/cordova'); const destScriptsDir = this.projectPath('cordova'); fs.copySync(srcScriptsDir, destScriptsDir); - - const nodeModulesDir = path.join(ROOT, 'node_modules'); - if (fs.existsSync(nodeModulesDir)) { - fs.copySync(nodeModulesDir, path.join(destScriptsDir, 'node_modules')); - } } expandTokens () { diff --git a/bin/templates/scripts/cordova/lib/list-devices b/lib/list-devices similarity index 100% rename from bin/templates/scripts/cordova/lib/list-devices rename to lib/list-devices diff --git a/bin/templates/scripts/cordova/lib/list-emulator-images b/lib/list-emulator-images similarity index 100% rename from bin/templates/scripts/cordova/lib/list-emulator-images rename to lib/list-emulator-images diff --git a/bin/templates/scripts/cordova/lib/listDevices.js b/lib/listDevices.js similarity index 100% rename from bin/templates/scripts/cordova/lib/listDevices.js rename to lib/listDevices.js diff --git a/bin/templates/scripts/cordova/lib/listEmulatorBuildTargets.js b/lib/listEmulatorBuildTargets.js similarity index 100% rename from bin/templates/scripts/cordova/lib/listEmulatorBuildTargets.js rename to lib/listEmulatorBuildTargets.js diff --git a/bin/templates/scripts/cordova/lib/listEmulatorImages.js b/lib/listEmulatorImages.js similarity index 100% rename from bin/templates/scripts/cordova/lib/listEmulatorImages.js rename to lib/listEmulatorImages.js diff --git a/bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js b/lib/plugman/pluginHandlers.js similarity index 100% rename from bin/templates/scripts/cordova/lib/plugman/pluginHandlers.js rename to lib/plugman/pluginHandlers.js diff --git a/bin/templates/scripts/cordova/lib/prepare.js b/lib/prepare.js similarity index 100% rename from bin/templates/scripts/cordova/lib/prepare.js rename to lib/prepare.js diff --git a/bin/templates/scripts/cordova/lib/projectFile.js b/lib/projectFile.js similarity index 100% rename from bin/templates/scripts/cordova/lib/projectFile.js rename to lib/projectFile.js diff --git a/bin/templates/scripts/cordova/lib/run.js b/lib/run.js similarity index 100% rename from bin/templates/scripts/cordova/lib/run.js rename to lib/run.js diff --git a/bin/templates/scripts/cordova/lib/versions.js b/lib/versions.js similarity index 100% rename from bin/templates/scripts/cordova/lib/versions.js rename to lib/versions.js diff --git a/package.json b/package.json index 1e148db22..68a647c49 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "cordova-ios", "version": "7.0.0-dev", "description": "cordova-ios release", - "main": "bin/templates/scripts/cordova/Api.js", + "main": "lib/Api.js", "repository": "github:apache/cordova-ios", "bugs": "https://github.com/apache/cordova-ios/issues", "keywords": [ @@ -54,7 +54,7 @@ }, "nyc": { "include": [ - "bin/templates/scripts/**" + "lib/**" ], "reporter": [ "lcov", diff --git a/tests/spec/create.spec.js b/tests/spec/create.spec.js index 63a08156e..ff2f79921 100644 --- a/tests/spec/create.spec.js +++ b/tests/spec/create.spec.js @@ -21,7 +21,7 @@ const fs = require('fs-extra'); const os = require('os'); const path = require('path'); const xcode = require('xcode'); -const create = require('../../bin/lib/create'); +const create = require('../../lib/create'); const makeTempDir = () => path.join( fs.realpathSync(os.tmpdir()), @@ -64,6 +64,13 @@ function verifyProjectBundleIdentifier (tmpDir, projectName, expectedBundleIdent * @returns {Promise} */ function verifyBuild (tmpDir) { + // Allow test project to find the `cordova-ios` module + fs.ensureSymlinkSync( + path.join(__dirname, '../..'), + path.join(tmpDir, 'node_modules/cordova-ios'), + 'junction' + ); + const Api = require(path.join(tmpDir, 'cordova/Api.js')); return expectAsync(new Api('ios', tmpDir).build({ emulator: true })) diff --git a/tests/spec/unit/Api.spec.js b/tests/spec/unit/Api.spec.js index e5bbc7a7b..290211976 100644 --- a/tests/spec/unit/Api.spec.js +++ b/tests/spec/unit/Api.spec.js @@ -21,8 +21,8 @@ const path = require('path'); const fs = require('fs-extra'); const EventEmitter = require('events'); const PluginManager = require('cordova-common').PluginManager; -const Api = require('../../../bin/templates/scripts/cordova/Api'); -const check_reqs = require('../../../bin/templates/scripts/cordova/lib/check_reqs'); +const Api = require('../../../lib/Api'); +const check_reqs = require('../../../lib/check_reqs'); // The lib/run module pulls in ios-sim, which has a hard requirement that it // be run on a Mac OS - simply requiring the module is enough to trigger the @@ -31,13 +31,13 @@ const check_reqs = require('../../../bin/templates/scripts/cordova/lib/check_req // method (more below). let run_mod; if (process.platform === 'darwin') { - run_mod = require('../../../bin/templates/scripts/cordova/lib/run'); + run_mod = require('../../../lib/run'); } -const projectFile = require('../../../bin/templates/scripts/cordova/lib/projectFile'); -const BridgingHeader_mod = require('../../../bin/templates/scripts/cordova/lib/BridgingHeader.js'); -const Podfile_mod = require('../../../bin/templates/scripts/cordova/lib/Podfile'); -const PodsJson_mod = require('../../../bin/templates/scripts/cordova/lib/PodsJson'); +const projectFile = require('../../../lib/projectFile'); +const BridgingHeader_mod = require('../../../lib/BridgingHeader.js'); +const Podfile_mod = require('../../../lib/Podfile'); +const PodsJson_mod = require('../../../lib/PodsJson'); const FIXTURES = path.join(__dirname, 'fixtures'); const iosProjectFixture = path.join(FIXTURES, 'ios-config-xml'); diff --git a/tests/spec/unit/BridgingHeader.spec.js b/tests/spec/unit/BridgingHeader.spec.js index 02bfde70c..f57af790e 100644 --- a/tests/spec/unit/BridgingHeader.spec.js +++ b/tests/spec/unit/BridgingHeader.spec.js @@ -20,7 +20,7 @@ const fs = require('fs-extra'); const path = require('path'); -const BridgingHeader = require(path.resolve(path.join(__dirname, '..', '..', '..', 'bin', 'templates', 'scripts', 'cordova', 'lib', 'BridgingHeader.js'))).BridgingHeader; +const BridgingHeader = require(path.resolve(path.join(__dirname, '../../../lib/BridgingHeader.js'))).BridgingHeader; const fixtureBridgingHeader = fs.readFileSync(path.resolve(__dirname, 'fixtures', 'test-Bridging-Header.h'), 'utf-8'); describe('unit tests for BridgingHeader module', () => { diff --git a/tests/spec/unit/Plugman/common.spec.js b/tests/spec/unit/Plugman/common.spec.js index 762c611a8..5b70a6e62 100644 --- a/tests/spec/unit/Plugman/common.spec.js +++ b/tests/spec/unit/Plugman/common.spec.js @@ -21,7 +21,7 @@ const path = require('path'); const osenv = require('os'); const rewire = require('rewire'); -const common = rewire('../../../../bin/templates/scripts/cordova/lib/plugman/pluginHandlers'); +const common = rewire('../../../../lib/plugman/pluginHandlers'); const test_dir = path.join(osenv.tmpdir(), 'test_plugman'); const project_dir = path.join(test_dir, 'project'); diff --git a/tests/spec/unit/Plugman/pluginHandler.spec.js b/tests/spec/unit/Plugman/pluginHandler.spec.js index 7766b9009..e46f0cb5d 100644 --- a/tests/spec/unit/Plugman/pluginHandler.spec.js +++ b/tests/spec/unit/Plugman/pluginHandler.spec.js @@ -24,9 +24,9 @@ const rewire = require('rewire'); const EventEmitter = require('events'); const PluginInfo = require('cordova-common').PluginInfo; -const Api = require('../../../../bin/templates/scripts/cordova/Api'); -const projectFile = require('../../../../bin/templates/scripts/cordova/lib/projectFile'); -const pluginHandlers = rewire('../../../../bin/templates/scripts/cordova/lib/plugman/pluginHandlers'); +const Api = require('../../../../lib/Api'); +const projectFile = require('../../../../lib/projectFile'); +const pluginHandlers = rewire('../../../../lib/plugman/pluginHandlers'); const temp = path.join(os.tmpdir(), 'plugman'); diff --git a/tests/spec/unit/Podfile.spec.js b/tests/spec/unit/Podfile.spec.js index ebd8be6fd..7b43415a0 100644 --- a/tests/spec/unit/Podfile.spec.js +++ b/tests/spec/unit/Podfile.spec.js @@ -23,7 +23,7 @@ const fs = require('fs-extra'); const CordovaError = require('cordova-common').CordovaError; const PROJECT_NAME = 'testProj'; -const Podfile = require(path.resolve(path.join(__dirname, '..', '..', '..', 'bin', 'templates', 'scripts', 'cordova', 'lib', 'Podfile.js'))).Podfile; +const Podfile = require(path.resolve(path.join(__dirname, '../../../lib/Podfile.js'))).Podfile; const fixturePodfile = path.resolve(__dirname, 'fixtures', PROJECT_NAME, 'platforms', 'ios', 'Podfile'); const fixturePodXcconfigDebug = path.resolve(__dirname, 'fixtures', PROJECT_NAME, 'platforms', 'ios', 'pods-debug.xcconfig'); const fixturePodXcconfigRelease = path.resolve(__dirname, 'fixtures', PROJECT_NAME, 'platforms', 'ios', 'pods-release.xcconfig'); diff --git a/tests/spec/unit/PodsJson.spec.js b/tests/spec/unit/PodsJson.spec.js index cd8190aee..891ffe77f 100644 --- a/tests/spec/unit/PodsJson.spec.js +++ b/tests/spec/unit/PodsJson.spec.js @@ -22,7 +22,7 @@ const path = require('path'); const util = require('util'); const CordovaError = require('cordova-common').CordovaError; -const PodsJson = require(path.resolve(path.join(__dirname, '..', '..', '..', 'bin', 'templates', 'scripts', 'cordova', 'lib', 'PodsJson.js'))).PodsJson; +const PodsJson = require(path.resolve(path.join(__dirname, '../../../lib/PodsJson.js'))).PodsJson; const fixturePodsJson = path.resolve(__dirname, 'fixtures', 'testProj', 'platforms', 'ios', 'pods.json'); // tests are nested in a describe to ensure clean up happens after all unit tests are run diff --git a/tests/spec/unit/build.spec.js b/tests/spec/unit/build.spec.js index 638a8674a..cc9dfe35d 100644 --- a/tests/spec/unit/build.spec.js +++ b/tests/spec/unit/build.spec.js @@ -21,7 +21,7 @@ const path = require('path'); const fs = require('fs-extra'); const rewire = require('rewire'); const { CordovaError, events } = require('cordova-common'); -const build = rewire('../../../bin/templates/scripts/cordova/lib/build'); +const build = rewire('../../../lib/build'); describe('build', () => { const testProjectPath = path.join('/test', 'project', 'path'); @@ -385,7 +385,7 @@ describe('build', () => { beforeEach(() => { // rewire causes some issues so for these tests, we will require instead. - buildRequire = require('../../../bin/templates/scripts/cordova/lib/build'); + buildRequire = require('../../../lib/build'); spyOn(events, 'emit'); }); diff --git a/tests/spec/unit/lib/check_reqs.spec.js b/tests/spec/unit/lib/check_reqs.spec.js index 3a16f1586..771725a92 100644 --- a/tests/spec/unit/lib/check_reqs.spec.js +++ b/tests/spec/unit/lib/check_reqs.spec.js @@ -19,12 +19,12 @@ const rewire = require('rewire'); const which = require('which'); -const versions = require('../../../../bin/templates/scripts/cordova/lib/versions'); +const versions = require('../../../../lib/versions'); describe('check_reqs', () => { let checkReqs; beforeEach(() => { - checkReqs = rewire('../../../../bin/templates/scripts/cordova/lib/check_reqs'); + checkReqs = rewire('../../../../lib/check_reqs'); }); describe('checkTool method', () => { diff --git a/tests/spec/unit/lib/list-devices.spec.js b/tests/spec/unit/lib/list-devices.spec.js index fa3e7ce4c..fb049850e 100644 --- a/tests/spec/unit/lib/list-devices.spec.js +++ b/tests/spec/unit/lib/list-devices.spec.js @@ -21,7 +21,7 @@ const fs = require('fs-extra'); const path = require('path'); const rewire = require('rewire'); -const list_devices = rewire('../../../../bin/templates/scripts/cordova/lib/listDevices'); +const list_devices = rewire('../../../../lib/listDevices'); const sampleData = fs.readFileSync(path.resolve(__dirname, '../fixtures/sample-ioreg-output.txt'), 'utf-8'); diff --git a/tests/spec/unit/lib/list-emulator-images.spec.js b/tests/spec/unit/lib/list-emulator-images.spec.js index 8153bef7a..db3415cdb 100644 --- a/tests/spec/unit/lib/list-emulator-images.spec.js +++ b/tests/spec/unit/lib/list-emulator-images.spec.js @@ -22,7 +22,7 @@ // allow for interacting with iOS Simulators. On Windows+Linux we are // bound to not-have-that. if (process.platform === 'darwin') { - const list_emus = require('../../../../bin/templates/scripts/cordova/lib/listEmulatorImages'); + const list_emus = require('../../../../lib/listEmulatorImages'); const iossim = require('ios-sim'); describe('cordova/lib/listEmulatorImages', () => { diff --git a/tests/spec/unit/lib/run.spec.js b/tests/spec/unit/lib/run.spec.js index 28d1f3804..a08ce31b7 100644 --- a/tests/spec/unit/lib/run.spec.js +++ b/tests/spec/unit/lib/run.spec.js @@ -22,7 +22,7 @@ // environment bits that allow for interacting with iOS Simulators. On // Windows+Linux we are bound to not-have-that. if (process.platform === 'darwin') { - const run = require('../../../../bin/templates/scripts/cordova/lib/run'); + const run = require('../../../../lib/run'); describe('cordova/lib/run', () => { describe('--list option', () => { diff --git a/tests/spec/unit/pluginAdd.spec.js b/tests/spec/unit/pluginAdd.spec.js index 1d4d69441..52573d0d0 100644 --- a/tests/spec/unit/pluginAdd.spec.js +++ b/tests/spec/unit/pluginAdd.spec.js @@ -22,7 +22,7 @@ const fs = require('fs-extra'); const EventEmitter = require('events').EventEmitter; const ConfigParser = require('cordova-common').ConfigParser; const PluginInfo = require('cordova-common').PluginInfo; -const Api = require('../../../bin/templates/scripts/cordova/Api'); +const Api = require('../../../lib/Api'); const FIXTURES = path.join(__dirname, 'fixtures'); const DUMMY_PLUGIN = 'org.test.plugins.dummyplugin'; diff --git a/tests/spec/unit/prepare.spec.js b/tests/spec/unit/prepare.spec.js index dcfc7a037..ce165defc 100644 --- a/tests/spec/unit/prepare.spec.js +++ b/tests/spec/unit/prepare.spec.js @@ -25,8 +25,8 @@ const path = require('path'); const plist = require('plist'); const xcode = require('xcode'); const rewire = require('rewire'); -const prepare = rewire('../../../bin/templates/scripts/cordova/lib/prepare'); -const projectFile = require('../../../bin/templates/scripts/cordova/lib/projectFile'); +const prepare = rewire('../../../lib/prepare'); +const projectFile = require('../../../lib/projectFile'); const FileUpdater = require('cordova-common').FileUpdater; const tmpDir = path.join(__dirname, '../../../tmp'); @@ -42,7 +42,7 @@ describe('prepare', () => { let p; let Api; beforeEach(() => { - Api = rewire('../../../bin/templates/scripts/cordova/Api'); + Api = rewire('../../../lib/Api'); fs.ensureDirSync(iosPlatform); fs.copySync(iosProjectFixture, iosPlatform); diff --git a/tests/spec/unit/preparePlatform.spec.js b/tests/spec/unit/preparePlatform.spec.js index 7333c750f..4d7eaed0b 100644 --- a/tests/spec/unit/preparePlatform.spec.js +++ b/tests/spec/unit/preparePlatform.spec.js @@ -22,7 +22,7 @@ const fs = require('fs-extra'); const EventEmitter = require('events').EventEmitter; const ConfigParser = require('cordova-common').ConfigParser; const PluginInfo = require('cordova-common').PluginInfo; -const Api = require('../../../bin/templates/scripts/cordova/Api'); +const Api = require('../../../lib/Api'); const FIXTURES = path.join(__dirname, 'fixtures'); const DUMMY_PLUGIN = 'org.test.plugins.dummyplugin'; diff --git a/tests/spec/unit/projectFile.spec.js b/tests/spec/unit/projectFile.spec.js index 6cd829eec..adc77563a 100644 --- a/tests/spec/unit/projectFile.spec.js +++ b/tests/spec/unit/projectFile.spec.js @@ -20,7 +20,7 @@ const os = require('os'); const path = require('path'); const fs = require('fs-extra'); -const projectFile = require('../../../bin/templates/scripts/cordova/lib/projectFile'); +const projectFile = require('../../../lib/projectFile'); const iosProject = path.join(os.tmpdir(), 'plugman/projectFile'); const iosProjectFixture = path.join(__dirname, 'fixtures/ios-config-xml'); diff --git a/tests/spec/unit/versions.spec.js b/tests/spec/unit/versions.spec.js index 58ecd1fec..b14a3f1f0 100644 --- a/tests/spec/unit/versions.spec.js +++ b/tests/spec/unit/versions.spec.js @@ -18,7 +18,7 @@ */ const semver = require('semver'); -const versions = require('../../../bin/templates/scripts/cordova/lib/versions'); +const versions = require('../../../lib/versions'); // These tests can not run on windows. if (process.platform === 'darwin') {