diff --git a/CHANGELOG.md b/CHANGELOG.md index ff6a1ea219..0aec157050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ NativeScript CLI Changelog ### Fixed * [Fixed #5187](https://github.com/NativeScript/nativescript-cli/issues/5187): Inaccessible native source code without modulemap +* [Fixed #5239](https://github.com/NativeScript/nativescript-cli/issues/5239): Temporary files created by CLI are not deleted in some cases 6.3.3 (2020, January 13) diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 6955923c3d..865c99723b 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -236,3 +236,4 @@ $injector.require("ipService", "./services/ip-service"); $injector.require("jsonFileSettingsService", "./common/services/json-file-settings-service"); $injector.require("markingModeService", "./services/marking-mode-service"); $injector.require("metadataFilteringService", "./services/metadata-filtering-service"); +$injector.require("tempService", "./services/temp-service"); diff --git a/lib/commands/plugin/build-plugin.ts b/lib/commands/plugin/build-plugin.ts index 390da7a596..b8e1eb04a8 100644 --- a/lib/commands/plugin/build-plugin.ts +++ b/lib/commands/plugin/build-plugin.ts @@ -1,7 +1,7 @@ import { EOL } from "os"; import * as path from "path"; import * as constants from "../../constants"; -import * as temp from "temp"; + export class BuildPluginCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; public pluginProjectPath: string; @@ -10,7 +10,8 @@ export class BuildPluginCommand implements ICommand { private $errors: IErrors, private $logger: ILogger, private $fs: IFileSystem, - private $options: IOptions) { + private $options: IOptions, + private $tempService: ITempService) { this.pluginProjectPath = path.resolve(this.$options.path || "."); } @@ -29,8 +30,7 @@ export class BuildPluginCommand implements ICommand { } } - temp.track(); - const tempAndroidProject = temp.mkdirSync("android-project"); + const tempAndroidProject = await this.$tempService.mkdirSync("android-project"); const options: IPluginBuildOptions = { aarOutputDir: platformsAndroidPath, diff --git a/lib/common/mobile/android/android-device-file-system.ts b/lib/common/mobile/android/android-device-file-system.ts index ef523a6960..d5f205384f 100644 --- a/lib/common/mobile/android/android-device-file-system.ts +++ b/lib/common/mobile/android/android-device-file-system.ts @@ -1,6 +1,5 @@ import * as path from "path"; import * as semver from "semver"; -import * as temp from "temp"; import { AndroidDeviceHashService } from "./android-device-hash-service"; import { executeActionByChunks } from "../../helpers"; import { DEFAULT_CHUNK_SIZE } from '../../constants'; @@ -12,6 +11,7 @@ export class AndroidDeviceFileSystem implements Mobile.IDeviceFileSystem { private $fs: IFileSystem, private $logger: ILogger, private $mobileHelper: Mobile.IMobileHelper, + private $tempService: ITempService, private $injector: IInjector) { } public async listFiles(devicePath: string, appIdentifier?: string): Promise { @@ -27,8 +27,7 @@ export class AndroidDeviceFileSystem implements Mobile.IDeviceFileSystem { const stdout = !outputPath; if (stdout) { - temp.track(); - outputPath = temp.path({ prefix: "sync", suffix: ".tmp" }); + outputPath = await this.$tempService.path({ prefix: "sync", suffix: ".tmp" }); } await this.adb.executeCommand(["pull", deviceFilePath, outputPath]); @@ -134,7 +133,7 @@ export class AndroidDeviceFileSystem implements Mobile.IDeviceFileSystem { } public async createFileOnDevice(deviceFilePath: string, fileContent: string): Promise { - const hostTmpDir = this.getTempDir(); + const hostTmpDir = await this.$tempService.mkdirSync("application-"); const commandsFileHostPath = path.join(hostTmpDir, "temp.commands.file"); this.$fs.writeFile(commandsFileHostPath, fileContent); @@ -159,9 +158,4 @@ export class AndroidDeviceFileSystem implements Mobile.IDeviceFileSystem { return this._deviceHashServices[appIdentifier]; } - - private getTempDir(): string { - temp.track(); - return temp.mkdirSync("application-"); - } } diff --git a/lib/common/mobile/android/android-device-hash-service.ts b/lib/common/mobile/android/android-device-hash-service.ts index 220db52331..64b43b7214 100644 --- a/lib/common/mobile/android/android-device-hash-service.ts +++ b/lib/common/mobile/android/android-device-hash-service.ts @@ -1,5 +1,4 @@ import * as path from "path"; -import * as temp from "temp"; import { cache } from "../../decorators"; import { executeActionByChunks } from "../../helpers"; import { DEFAULT_CHUNK_SIZE, LiveSyncPaths } from "../../constants"; @@ -10,7 +9,8 @@ export class AndroidDeviceHashService implements Mobile.IAndroidDeviceHashServic constructor(private adb: Mobile.IDeviceAndroidDebugBridge, private appIdentifier: string, private $fs: IFileSystem, - private $mobileHelper: Mobile.IMobileHelper) { + private $mobileHelper: Mobile.IMobileHelper, + private $tempService: ITempService) { } @cache() @@ -34,8 +34,9 @@ export class AndroidDeviceHashService implements Mobile.IAndroidDeviceHashServic } public async uploadHashFileToDevice(data: IStringDictionary): Promise { - this.$fs.writeJson(this.hashFileLocalPath, data); - await this.adb.pushFile(this.hashFileLocalPath, this.hashFileDevicePath); + const hashFileLocalPath = await this.getHashFileLocalPath(); + this.$fs.writeJson(hashFileLocalPath, data); + await this.adb.pushFile(hashFileLocalPath, this.hashFileDevicePath); } public async updateHashes(localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { @@ -86,20 +87,20 @@ export class AndroidDeviceHashService implements Mobile.IAndroidDeviceHashServic } @cache() - private get hashFileLocalPath(): string { - return path.join(this.tempDir, AndroidDeviceHashService.HASH_FILE_NAME); + private async getHashFileLocalPath(): Promise { + return path.join(await this.getTempDir(), AndroidDeviceHashService.HASH_FILE_NAME); } @cache() - private get tempDir(): string { - temp.track(); - return temp.mkdirSync(`android-device-hash-service-${this.appIdentifier}`); + private getTempDir(): Promise { + return this.$tempService.mkdirSync(`android-device-hash-service-${this.appIdentifier}`); } private async downloadHashFileFromDevice(): Promise { - if (!this.$fs.exists(this.hashFileLocalPath)) { - await this.adb.executeCommand(["pull", this.hashFileDevicePath, this.tempDir]); + const hashFileLocalPath = await this.getHashFileLocalPath(); + if (!this.$fs.exists(hashFileLocalPath)) { + await this.adb.executeCommand(["pull", this.hashFileDevicePath, await this.getTempDir()]); } - return this.hashFileLocalPath; + return hashFileLocalPath; } } diff --git a/lib/common/mobile/ios/simulator/ios-simulator-application-manager.ts b/lib/common/mobile/ios/simulator/ios-simulator-application-manager.ts index f155e93052..891f92b708 100644 --- a/lib/common/mobile/ios/simulator/ios-simulator-application-manager.ts +++ b/lib/common/mobile/ios/simulator/ios-simulator-application-manager.ts @@ -4,7 +4,6 @@ import { hook, getPidFromiOSSimulatorLogs } from "../../../helpers"; import { cache } from "../../../decorators"; import { IOS_LOG_PREDICATE } from "../../../constants"; import * as path from "path"; -import * as temp from "temp"; import * as log4js from "log4js"; export class IOSSimulatorApplicationManager extends ApplicationManagerBase { @@ -16,6 +15,7 @@ export class IOSSimulatorApplicationManager extends ApplicationManagerBase { private $options: IOptions, private $fs: IFileSystem, protected $deviceLogProvider: Mobile.IDeviceLogProvider, + private $tempService: ITempService, $logger: ILogger, $hooksService: IHooksService) { super($logger, $hooksService, $deviceLogProvider); @@ -28,8 +28,7 @@ export class IOSSimulatorApplicationManager extends ApplicationManagerBase { @hook('install') public async installApplication(packageFilePath: string): Promise { if (this.$fs.exists(packageFilePath) && path.extname(packageFilePath) === ".zip") { - temp.track(); - const dir = temp.mkdirSync("simulatorPackage"); + const dir = await this.$tempService.mkdirSync("simulatorPackage"); await this.$fs.unzip(packageFilePath, dir); const app = _.find(this.$fs.readDirectory(dir), directory => path.extname(directory) === ".app"); if (app) { diff --git a/lib/common/mobile/mobile-helper.ts b/lib/common/mobile/mobile-helper.ts index 59746402c7..311026d296 100644 --- a/lib/common/mobile/mobile-helper.ts +++ b/lib/common/mobile/mobile-helper.ts @@ -1,13 +1,13 @@ import * as helpers from "../helpers"; import * as shell from "shelljs"; -import * as temp from "temp"; export class MobileHelper implements Mobile.IMobileHelper { private static DEVICE_PATH_SEPARATOR = "/"; constructor(private $errors: IErrors, private $fs: IFileSystem, - private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants) { } + private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + private $tempService: ITempService) { } public get platformNames(): string[] { return [this.$devicePlatformsConstants.iOS, this.$devicePlatformsConstants.Android]; @@ -58,8 +58,7 @@ export class MobileHelper implements Mobile.IMobileHelper { } public async getDeviceFileContent(device: Mobile.IDevice, deviceFilePath: string, projectData: IProjectData): Promise { - temp.track(); - const uniqueFilePath = temp.path({ suffix: ".tmp" }); + const uniqueFilePath = await this.$tempService.path({ suffix: ".tmp" }); const platform = device.deviceInfo.platform.toLowerCase(); try { await device.fileSystem.getFile(deviceFilePath, projectData.projectIdentifiers[platform], uniqueFilePath); diff --git a/lib/common/test/unit-tests/mobile/android-device-file-system.ts b/lib/common/test/unit-tests/mobile/android-device-file-system.ts index fb61e25f8e..f33bc15359 100644 --- a/lib/common/test/unit-tests/mobile/android-device-file-system.ts +++ b/lib/common/test/unit-tests/mobile/android-device-file-system.ts @@ -8,6 +8,7 @@ import { DevicePlatformsConstants } from "../../../mobile/device-platforms-const import * as path from "path"; import { assert } from "chai"; import { LiveSyncPaths } from "../../../constants"; +import { TempServiceStub } from "../../../../../test/stubs"; const myTestAppIdentifier = "org.nativescript.myApp"; let isAdbPushExecuted = false; @@ -64,7 +65,7 @@ function createTestInjector(): IInjector { injector.register("errors", Errors); injector.register("devicePlatformsConstants", DevicePlatformsConstants); injector.register("projectFilesManager", {}); - + injector.register("tempService", TempServiceStub); return injector; } diff --git a/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts b/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts index 50a733e705..ecf85c774a 100644 --- a/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts +++ b/lib/common/test/unit-tests/mobile/ios-simulator-discovery.ts @@ -5,7 +5,7 @@ import { assert } from "chai"; import { DeviceDiscoveryEventNames, CONNECTED_STATUS } from "../../../constants"; import { DevicePlatformsConstants } from "../../../mobile/device-platforms-constants"; import { ErrorsStub, CommonLoggerStub, HooksServiceStub, LockServiceStub } from "../stubs"; -import { FileSystemStub, ChildProcessStub } from "../../../../../test/stubs"; +import { FileSystemStub, ChildProcessStub, TempServiceStub } from "../../../../../test/stubs"; import { DeviceConnectionType } from "../../../../constants"; let currentlyRunningSimulators: Mobile.IiSimDevice[]; @@ -45,7 +45,7 @@ function createTestInjector(): IInjector { injector.register("options", {}); injector.register("hooksService", HooksServiceStub); injector.register("logger", CommonLoggerStub); - + injector.register("tempService", TempServiceStub); return injector; } diff --git a/lib/common/test/unit-tests/mobile/project-files-manager.ts b/lib/common/test/unit-tests/mobile/project-files-manager.ts index b94972c78d..e1136f3cd4 100644 --- a/lib/common/test/unit-tests/mobile/project-files-manager.ts +++ b/lib/common/test/unit-tests/mobile/project-files-manager.ts @@ -14,6 +14,7 @@ import { ProjectFilesProviderBase } from "../../../services/project-files-provid import temp = require("temp"); import { LiveSyncPaths } from "../../../constants"; +import { TempServiceStub } from "../../../../../test/stubs"; temp.track(); const testedApplicationIdentifier = "com.telerik.myApp"; @@ -49,6 +50,7 @@ function createTestInjector(): IInjector { }); testInjector.register("logger", Logger); testInjector.register("config", {}); + testInjector.register("tempService", TempServiceStub); return testInjector; } diff --git a/lib/definitions/ios.d.ts b/lib/definitions/ios.d.ts index 9ca1fa5932..91356ddd49 100644 --- a/lib/definitions/ios.d.ts +++ b/lib/definitions/ios.d.ts @@ -30,8 +30,8 @@ declare global { } interface IExportOptionsPlistService { - createDevelopmentExportOptionsPlist(archivePath: string, projectData: IProjectData, buildConfig: IBuildConfig): IExportOptionsPlistOutput; - createDistributionExportOptionsPlist(projectRoot: string, projectData: IProjectData, buildConfig: IBuildConfig): IExportOptionsPlistOutput; + createDevelopmentExportOptionsPlist(archivePath: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise; + createDistributionExportOptionsPlist(projectRoot: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise; } interface IExportOptionsPlistOutput { diff --git a/lib/definitions/plugins.d.ts b/lib/definitions/plugins.d.ts index 9cc8be5360..6f70fa6518 100644 --- a/lib/definitions/plugins.d.ts +++ b/lib/definitions/plugins.d.ts @@ -4,7 +4,7 @@ interface IPluginsService { addToPackageJson(plugin: string, version: string, isDev: boolean, projectDir: string): void; removeFromPackageJson(plugin: string, projectDir: string): void; getAllInstalledPlugins(projectData: IProjectData): Promise; - getAllProductionPlugins(projectData: IProjectData, dependencies?: IDependencyData[]): IPluginData[]; + getAllProductionPlugins(projectData: IProjectData, platform: string, dependencies?: IDependencyData[]): IPluginData[]; ensureAllDependenciesAreInstalled(projectData: IProjectData): Promise; /** diff --git a/lib/definitions/temp-service.d.ts b/lib/definitions/temp-service.d.ts new file mode 100644 index 0000000000..23b3abe045 --- /dev/null +++ b/lib/definitions/temp-service.d.ts @@ -0,0 +1,7 @@ +/** + * Declares wrapped functions of temp module + */ +interface ITempService { + mkdirSync(affixes: string): Promise; + path(options: ITempPathOptions): Promise; +} \ No newline at end of file diff --git a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts index 24e7d9481d..a1c5493032 100644 --- a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts +++ b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts @@ -2,7 +2,6 @@ import { EventEmitter } from "events"; import { CONNECTION_ERROR_EVENT_NAME } from "../../constants"; import * as net from "net"; import * as ws from "ws"; -import temp = require("temp"); import { MessageUnpackStream } from "ios-device-lib"; export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebugSocketProxyFactory { @@ -13,6 +12,7 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu private $errors: IErrors, private $lockService: ILockService, private $options: IOptions, + private $tempService: ITempService, private $net: INet) { super(); } @@ -72,7 +72,7 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu frontendSocket.resume(); }); - const socketFileLocation = temp.path({ suffix: ".sock" }); + const socketFileLocation = await this.$tempService.path({ suffix: ".sock" }); server.listen(socketFileLocation); if (!this.$options.client) { this.$logger.info("socket-file-location: " + socketFileLocation); diff --git a/lib/helpers/platform-command-helper.ts b/lib/helpers/platform-command-helper.ts index 37094d6fbf..e8474c39cc 100644 --- a/lib/helpers/platform-command-helper.ts +++ b/lib/helpers/platform-command-helper.ts @@ -1,6 +1,5 @@ import * as path from "path"; import * as semver from "semver"; -import * as temp from "temp"; import * as constants from "../constants"; import { PlatformController } from "../controllers/platform-controller"; import { PlatformValidationService } from "../services/platform/platform-validation-service"; @@ -17,7 +16,8 @@ export class PlatformCommandHelper implements IPlatformCommandHelper { private $platformsDataService: IPlatformsDataService, private $platformValidationService: PlatformValidationService, private $projectChangesService: IProjectChangesService, - private $projectDataService: IProjectDataService + private $projectDataService: IProjectDataService, + private $tempService: ITempService ) { } public async addPlatforms(platforms: string[], projectData: IProjectData, frameworkPath: string): Promise { @@ -147,7 +147,7 @@ export class PlatformCommandHelper implements IPlatformCommandHelper { const data = this.$projectDataService.getNSValue(projectData.projectDir, platformData.frameworkPackageName); const currentVersion = data && data.version ? data.version : "0.2.0"; - const installedModuleDir = temp.mkdirSync("runtime-to-update"); + const installedModuleDir = await this.$tempService.mkdirSync("runtime-to-update"); let newVersion = version === constants.PackageVersion.NEXT ? await this.$packageInstallationManager.getNextVersion(platformData.frameworkPackageName) : version || await this.$packageInstallationManager.getLatestCompatibleVersion(platformData.frameworkPackageName); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index efc873f680..ec146e5d4c 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -7,7 +7,6 @@ import { attachAwaitDetach } from "../common/helpers"; import * as projectServiceBaseLib from "./platform-project-service-base"; import { PlistSession, Reporter } from "plist-merge-patch"; import { EOL } from "os"; -import * as temp from "temp"; import * as plist from "plist"; import { IOSProvisionService } from "./ios-provision-service"; import { IOSEntitlementsService } from "./ios-entitlements-service"; @@ -55,7 +54,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $iOSExtensionsService: IIOSExtensionsService, private $iOSWatchAppService: IIOSWatchAppService, private $iOSNativeTargetService: IIOSNativeTargetService, - private $sysInfo: ISysInfo) { + private $sysInfo: ISysInfo, + private $tempService: ITempService) { super($fs, $projectDataService); } @@ -471,7 +471,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ } private getAllProductionPlugins(projectData: IProjectData): IPluginData[] { - return (this.$injector.resolve("pluginsService")).getAllProductionPlugins(projectData); + return (this.$injector.resolve("pluginsService")).getAllProductionPlugins(projectData, this.getPlatformData(projectData).platformNameLowerCase); } private replace(name: string): string { @@ -814,8 +814,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ // Set Entitlements Property to point to default file if not set explicitly by the user. const entitlementsPropertyValue = this.$xcconfigService.readPropertyValue(pluginsXcconfigFilePath, constants.CODE_SIGN_ENTITLEMENTS); if (entitlementsPropertyValue === null && this.$fs.exists(this.$iOSEntitlementsService.getPlatformsEntitlementsPath(projectData))) { - temp.track(); - const tempEntitlementsDir = temp.mkdirSync("entitlements"); + const tempEntitlementsDir = await this.$tempService.mkdirSync("entitlements"); const tempEntitlementsFilePath = path.join(tempEntitlementsDir, "set-entitlements.xcconfig"); const entitlementsRelativePath = this.$iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData); this.$fs.writeFile(tempEntitlementsFilePath, `CODE_SIGN_ENTITLEMENTS = ${entitlementsRelativePath}${EOL}`); diff --git a/lib/services/ios/export-options-plist-service.ts b/lib/services/ios/export-options-plist-service.ts index 7302e50f0e..2db3378824 100644 --- a/lib/services/ios/export-options-plist-service.ts +++ b/lib/services/ios/export-options-plist-service.ts @@ -1,11 +1,11 @@ import * as path from "path"; -import * as temp from "temp"; import * as mobileProvisionFinder from "ios-mobileprovision-finder"; export class ExportOptionsPlistService implements IExportOptionsPlistService { - constructor(private $fs: IFileSystem) { } + constructor(private $fs: IFileSystem, + private $tempService: ITempService) { } - public createDevelopmentExportOptionsPlist(archivePath: string, projectData: IProjectData, buildConfig: IBuildConfig): IExportOptionsPlistOutput { + public async createDevelopmentExportOptionsPlist(archivePath: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise { const exportOptionsMethod = this.getExportOptionsMethod(projectData, archivePath); const provision = buildConfig.provision || buildConfig.mobileProvisionIdentifier; const iCloudContainerEnvironment = buildConfig.iCloudContainerEnvironment; @@ -37,8 +37,7 @@ export class ExportOptionsPlistService implements IExportOptionsPlistService { `; // Save the options... - temp.track(); - const exportOptionsPlistFilePath = temp.path({ prefix: "export-", suffix: ".plist" }); + const exportOptionsPlistFilePath = await this.$tempService.path({ prefix: "export-", suffix: ".plist" }); this.$fs.writeFile(exportOptionsPlistFilePath, plistTemplate); // The xcodebuild exportPath expects directory and writes the .ipa at that directory. @@ -48,7 +47,7 @@ export class ExportOptionsPlistService implements IExportOptionsPlistService { return { exportFileDir, exportFilePath, exportOptionsPlistFilePath }; } - public createDistributionExportOptionsPlist(archivePath: string, projectData: IProjectData, buildConfig: IBuildConfig): IExportOptionsPlistOutput { + public async createDistributionExportOptionsPlist(archivePath: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise { const provision = buildConfig.provision || buildConfig.mobileProvisionIdentifier; const teamId = buildConfig.teamId; let plistTemplate = ` @@ -80,8 +79,7 @@ export class ExportOptionsPlistService implements IExportOptionsPlistService { `; // Save the options... - temp.track(); - const exportOptionsPlistFilePath = temp.path({ prefix: "export-", suffix: ".plist" }); + const exportOptionsPlistFilePath = await this.$tempService.path({ prefix: "export-", suffix: ".plist" }); this.$fs.writeFile(exportOptionsPlistFilePath, plistTemplate); const exportFileDir = path.resolve(path.dirname(archivePath)); diff --git a/lib/services/ios/xcodebuild-service.ts b/lib/services/ios/xcodebuild-service.ts index d1a3affef9..ac91a38cde 100644 --- a/lib/services/ios/xcodebuild-service.ts +++ b/lib/services/ios/xcodebuild-service.ts @@ -28,7 +28,7 @@ export class XcodebuildService implements IXcodebuildService { private async createDevelopmentArchive(platformData: IPlatformData, projectData: IProjectData, buildConfig: IBuildConfig): Promise { const archivePath = path.join(platformData.getBuildOutputPath(buildConfig), projectData.projectName + ".xcarchive"); - const output = this.$exportOptionsPlistService.createDevelopmentExportOptionsPlist(archivePath, projectData, buildConfig); + const output = await this.$exportOptionsPlistService.createDevelopmentExportOptionsPlist(archivePath, projectData, buildConfig); const args = [ "-exportArchive", "-archivePath", archivePath, @@ -42,7 +42,7 @@ export class XcodebuildService implements IXcodebuildService { private async createDistributionArchive(platformData: IPlatformData, projectData: IProjectData, buildConfig: IBuildConfig): Promise { const archivePath = path.join(platformData.getBuildOutputPath(buildConfig), projectData.projectName + ".xcarchive"); - const output = this.$exportOptionsPlistService.createDistributionExportOptionsPlist(archivePath, projectData, buildConfig); + const output = await this.$exportOptionsPlistService.createDistributionExportOptionsPlist(archivePath, projectData, buildConfig); const args = [ "-exportArchive", "-archivePath", archivePath, diff --git a/lib/services/itmstransporter-service.ts b/lib/services/itmstransporter-service.ts index 411a3713b5..7a029bddd8 100644 --- a/lib/services/itmstransporter-service.ts +++ b/lib/services/itmstransporter-service.ts @@ -1,5 +1,4 @@ import * as path from "path"; -import * as temp from "temp"; import { ITMSConstants, INFO_PLIST_FILE_NAME } from "../constants"; import { quoteString } from "../common/helpers"; import { cache } from "../common/decorators"; @@ -13,7 +12,8 @@ export class ITMSTransporterService implements IITMSTransporterService { private $injector: IInjector, private $logger: ILogger, private $plistParser: IPlistParser, - private $xcodeSelectService: IXcodeSelectService) { } + private $xcodeSelectService: IXcodeSelectService, + private $tempService: ITempService) { } private get $projectData(): IProjectData { return this.$injector.resolve("projectData"); @@ -27,10 +27,9 @@ export class ITMSTransporterService implements IITMSTransporterService { } public async upload(data: IITMSData): Promise { - temp.track(); const itmsTransporterPath = await this.getITMSTransporterPath(); const ipaFileName = "app.ipa"; - const itmsDirectory = temp.mkdirSync("itms-"); + const itmsDirectory = await this.$tempService.mkdirSync("itms-"); const innerDirectory = path.join(itmsDirectory, "mybundle.itmsp"); const ipaFileLocation = path.join(innerDirectory, ipaFileName); const loggingLevel = data.verboseLogging ? ITMSConstants.VerboseLoggingLevels.Verbose : ITMSConstants.VerboseLoggingLevels.Informational; @@ -68,8 +67,7 @@ export class ITMSTransporterService implements IITMSTransporterService { } this.$logger.trace("--ipa set - extracting .ipa file to get app's bundle identifier"); - temp.track(); - const destinationDir = temp.mkdirSync("ipa-"); + const destinationDir = await this.$tempService.mkdirSync("ipa-"); await this.$fs.unzip(ipaFilePath, destinationDir); const payloadDir = path.join(destinationDir, "Payload"); diff --git a/lib/services/livesync/android-device-livesync-sockets-service.ts b/lib/services/livesync/android-device-livesync-sockets-service.ts index 9a70f51241..a661707347 100644 --- a/lib/services/livesync/android-device-livesync-sockets-service.ts +++ b/lib/services/livesync/android-device-livesync-sockets-service.ts @@ -3,7 +3,6 @@ import { APP_FOLDER_NAME } from "../../constants"; import { LiveSyncPaths } from "../../common/constants"; import { AndroidLivesyncTool } from "./android-livesync-tool"; import * as path from "path"; -import * as temp from "temp"; import * as semver from "semver"; export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncServiceBase implements IAndroidNativeScriptDeviceLiveSyncService, INativeScriptDeviceLiveSyncService { @@ -22,6 +21,7 @@ export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncSe private $cleanupService: ICleanupService, private $fs: IFileSystem, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + private $tempService: ITempService, $filesHashService: IFilesHashService) { super($injector, platformsDataService, $filesHashService, $logger, device); this.livesyncTool = this.$injector.resolve(AndroidLivesyncTool); @@ -30,7 +30,7 @@ export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncSe public async beforeLiveSyncAction(deviceAppData: Mobile.IDeviceAppData): Promise { if (!this.livesyncTool.hasConnection()) { try { - const pathToLiveSyncFile = temp.path({ prefix: "livesync" }); + const pathToLiveSyncFile = await this.$tempService.path({ prefix: "livesync" }); this.$fs.writeFile(pathToLiveSyncFile, ""); await this.device.fileSystem.putFile(pathToLiveSyncFile, this.getPathToLiveSyncFileOnDevice(deviceAppData.appIdentifier), deviceAppData.appIdentifier); await this.device.applicationManager.startApplication({ appId: deviceAppData.appIdentifier, projectName: this.data.projectName, justLaunch: true, waitForDebugger: false, projectDir: deviceAppData.projectDir }); diff --git a/lib/services/livesync/ios-livesync-service.ts b/lib/services/livesync/ios-livesync-service.ts index 92162beeb2..311b09792f 100644 --- a/lib/services/livesync/ios-livesync-service.ts +++ b/lib/services/livesync/ios-livesync-service.ts @@ -1,5 +1,4 @@ import * as path from "path"; -import * as temp from "temp"; import { IOSDeviceLiveSyncService } from "./ios-device-livesync-service"; import { PlatformLiveSyncServiceBase } from "./platform-livesync-service-base"; @@ -11,6 +10,7 @@ export class IOSLiveSyncService extends PlatformLiveSyncServiceBase implements I protected $platformsDataService: IPlatformsDataService, protected $projectFilesManager: IProjectFilesManager, private $injector: IInjector, + private $tempService: ITempService, $devicePathProvider: IDevicePathProvider, $logger: ILogger) { super($fs, $logger, $platformsDataService, $projectFilesManager, $devicePathProvider); @@ -28,8 +28,7 @@ export class IOSLiveSyncService extends PlatformLiveSyncServiceBase implements I const deviceAppData = await this.getAppData(syncInfo); const projectFilesPath = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME); - temp.track(); - const tempZip = temp.path({ prefix: "sync", suffix: ".zip" }); + const tempZip = await this.$tempService.path({ prefix: "sync", suffix: ".zip" }); this.$logger.trace("Creating zip file: " + tempZip); const filesToTransfer = this.$fs.enumerateFilesInDirectorySync(projectFilesPath); diff --git a/lib/services/metadata-filtering-service.ts b/lib/services/metadata-filtering-service.ts index dbc3fb2a61..368cc5e2e1 100644 --- a/lib/services/metadata-filtering-service.ts +++ b/lib/services/metadata-filtering-service.ts @@ -23,7 +23,7 @@ export class MetadataFilteringService implements IMetadataFilteringService { if (nativeApiConfiguration) { const whitelistedItems: string[] = []; if (nativeApiConfiguration["whitelist-plugins-usages"]) { - const plugins = this.$pluginsService.getAllProductionPlugins(projectData); + const plugins = this.$pluginsService.getAllProductionPlugins(projectData, platform); for (const pluginData of plugins) { const pathToPlatformsDir = pluginData.pluginPlatformsFolderPath(platform); const pathToPluginsMetadataConfig = path.join(pathToPlatformsDir, MetadataFilteringConstants.NATIVE_API_USAGE_FILE_NAME); diff --git a/lib/services/platform/add-platform-service.ts b/lib/services/platform/add-platform-service.ts index 4f795e3076..057f3507bb 100644 --- a/lib/services/platform/add-platform-service.ts +++ b/lib/services/platform/add-platform-service.ts @@ -1,5 +1,4 @@ import * as path from "path"; -import * as temp from "temp"; import { PROJECT_FRAMEWORK_FOLDER_NAME, TrackActionNames, AnalyticsEventLabelDelimiter } from "../../constants"; import { performanceLog } from "../../common/decorators"; @@ -9,7 +8,8 @@ export class AddPlatformService implements IAddPlatformService { private $pacoteService: IPacoteService, private $projectDataService: IProjectDataService, private $terminalSpinnerService: ITerminalSpinnerService, - private $analyticsService: IAnalyticsService + private $analyticsService: IAnalyticsService, + private $tempService: ITempService ) { } public async addPlatformSafe(projectData: IProjectData, platformData: IPlatformData, packageToInstall: string, nativePrepare: INativePrepare): Promise { @@ -45,11 +45,9 @@ export class AddPlatformService implements IAddPlatformService { } private async extractPackage(pkg: string): Promise { - temp.track(); - const downloadedPackagePath = temp.mkdirSync("runtimeDir"); + const downloadedPackagePath = await this.$tempService.mkdirSync("runtimeDir"); await this.$pacoteService.extractPackage(pkg, downloadedPackagePath); const frameworkDir = path.join(downloadedPackagePath, PROJECT_FRAMEWORK_FOLDER_NAME); - return path.resolve(frameworkDir); } diff --git a/lib/services/plugins-service.ts b/lib/services/plugins-service.ts index 8c887a4740..14528c7d68 100644 --- a/lib/services/plugins-service.ts +++ b/lib/services/plugins-service.ts @@ -172,7 +172,7 @@ export class PluginsService implements IPluginsService { return _.filter(nodeModules, nodeModuleData => nodeModuleData && nodeModuleData.isPlugin); } - public getAllProductionPlugins(projectData: IProjectData, dependencies?: IDependencyData[]): IPluginData[] { + public getAllProductionPlugins(projectData: IProjectData, platform: string, dependencies?: IDependencyData[]): IPluginData[] { dependencies = dependencies || this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir); if (_.isEmpty(dependencies)) { @@ -180,7 +180,7 @@ export class PluginsService implements IPluginsService { } let productionPlugins: IDependencyData[] = dependencies.filter(d => !!d.nativescript); - productionPlugins = this.ensureValidProductionPlugins(productionPlugins, projectData.projectDir); + productionPlugins = this.ensureValidProductionPlugins(productionPlugins, projectData.projectDir, platform); const pluginData = productionPlugins.map(plugin => this.convertToPluginData(plugin, projectData.projectDir)); return pluginData; } @@ -202,15 +202,27 @@ export class PluginsService implements IPluginsService { return pluginPackageJsonContent && pluginPackageJsonContent.nativescript; } - private ensureValidProductionPlugins = _.memoize<(productionDependencies: IDependencyData[], projectDir: string) => IDependencyData[]>(this._ensureValidProductionPlugins, (productionDependencies: IDependencyData[], projectDir: string) => { + private ensureValidProductionPlugins = _.memoize<(productionDependencies: IDependencyData[], projectDir: string, platform: string) => IDependencyData[]>(this._ensureValidProductionPlugins, (productionDependencies: IDependencyData[], projectDir: string, platform: string) => { let key = _.sortBy(productionDependencies, p => p.directory).map(d => JSON.stringify(d, null, 2)).join("\n"); - key += projectDir; + key += projectDir + platform; return key; }); - private _ensureValidProductionPlugins(productionDependencies: IDependencyData[], projectDir: string): IDependencyData[] { - const clonedProductionDependencies = _.cloneDeep(productionDependencies); - const dependenciesGroupedByName = _.groupBy(clonedProductionDependencies, p => p.name); + private _ensureValidProductionPlugins(productionDependencies: IDependencyData[], projectDir: string, platform: string): IDependencyData[] { + let clonedProductionDependencies = _.cloneDeep(productionDependencies); + platform = platform.toLowerCase(); + + if (this.$mobileHelper.isAndroidPlatform(platform)) { + this.ensureValidProductionPluginsForAndroid(clonedProductionDependencies); + } else if (this.$mobileHelper.isiOSPlatform(platform)) { + clonedProductionDependencies = this.ensureValidProductionPluginsForIOS(clonedProductionDependencies, projectDir, platform); + } + + return clonedProductionDependencies; + } + + private ensureValidProductionPluginsForAndroid(productionDependencies: IDependencyData[]): void { + const dependenciesGroupedByName = _.groupBy(productionDependencies, p => p.name); _.each(dependenciesGroupedByName, (dependencyOccurrences, dependencyName) => { if (dependencyOccurrences.length > 1) { // the dependency exists multiple times in node_modules @@ -218,31 +230,79 @@ export class PluginsService implements IPluginsService { const versions = _.keys(dependencyOccurrencesGroupedByVersion); if (versions.length === 1) { // all dependencies with this name have the same version - this.$logger.warn(`Detected same versions (${_.first(versions)}) of the ${dependencyName} installed at locations: ${_.map(dependencyOccurrences, d => d.directory).join(", ")}`); - const selectedPackage = _.minBy(dependencyOccurrences, d => d.depth); - this.$logger.info(`CLI will use only the native code from '${selectedPackage.directory}'.`.green); - _.each(dependencyOccurrences, dependency => { - if (dependency !== selectedPackage) { - clonedProductionDependencies.splice(clonedProductionDependencies.indexOf(dependency), 1); - } - }); + this.$logger.debug(`Detected same versions (${_.first(versions)}) of the ${dependencyName} installed at locations: ${_.map(dependencyOccurrences, d => d.directory).join(", ")}`); } else { - const message = this.getFailureMessageForDifferentDependencyVersions(dependencyName, dependencyOccurrencesGroupedByVersion, projectDir); - this.$errors.fail(message); + this.$logger.debug(`Detected different versions of the ${dependencyName} installed at locations: ${_.map(dependencyOccurrences, d => d.directory).join(", ")}\nThis can cause build failures.`); } } }); + } - return clonedProductionDependencies; + private ensureValidProductionPluginsForIOS(productionDependencies: IDependencyData[], projectDir: string, platform: string): IDependencyData[] { + const dependenciesWithFrameworks: any[] = []; + _.each(productionDependencies, d => { + const pathToPlatforms = path.join(d.directory, "platforms", platform); + if (this.$fs.exists(pathToPlatforms)) { + const contents = this.$fs.readDirectory(pathToPlatforms); + _.each(contents, file => { + if (path.extname(file) === ".framework") { + dependenciesWithFrameworks.push({ ...d, frameworkName: path.basename(file), frameworkLocation: path.join(pathToPlatforms, file) }); + } + }); + } + }); + + if (dependenciesWithFrameworks.length > 0) { + const dependenciesGroupedByFrameworkName = _.groupBy(dependenciesWithFrameworks, d => d.frameworkName); + _.each(dependenciesGroupedByFrameworkName, (dependencyOccurrences, frameworkName) => { + if (dependencyOccurrences.length > 1) { + // A framework exists multiple times in node_modules + const groupedByName = _.groupBy(dependencyOccurrences, d => d.name); + const pluginsNames = _.keys(groupedByName); + if (pluginsNames.length > 1) { + // fail - the same framework is installed by different dependencies. + const locations = dependencyOccurrences.map(d => d.frameworkLocation); + let msg = `Detected the framework ${frameworkName} is installed from multiple plugins at locations:\n${locations.join("\n")}\n`; + msg += this.getHelpMessage(projectDir); + this.$errors.fail(msg); + } + + const dependencyName = _.first(pluginsNames); + const dependencyOccurrencesGroupedByVersion = _.groupBy(dependencyOccurrences, g => g.version); + const versions = _.keys(dependencyOccurrencesGroupedByVersion); + if (versions.length === 1) { + // all dependencies with this name have the same version + this.$logger.warn(`Detected the framework ${frameworkName} is installed multiple times from the same versions of plugin (${_.first(versions)}) at locations: ${_.map(dependencyOccurrences, d => d.directory).join(", ")}`); + const selectedPackage = _.minBy(dependencyOccurrences, d => d.depth); + this.$logger.info(`CLI will use only the native code from '${selectedPackage.directory}'.`.green); + _.each(dependencyOccurrences, dependency => { + if (dependency !== selectedPackage) { + productionDependencies.splice(productionDependencies.indexOf(dependency), 1); + } + }); + } else { + const message = this.getFailureMessageForDifferentDependencyVersions(dependencyName, frameworkName, dependencyOccurrencesGroupedByVersion, projectDir); + this.$errors.fail(message); + } + } + }); + } + + return productionDependencies; } - private getFailureMessageForDifferentDependencyVersions(dependencyName: string, dependencyOccurrencesGroupedByVersion: IDictionary, projectDir: string): string { - let message = `Cannot use different versions of a NativeScript plugin in your application. -${dependencyName} plugin occurs multiple times in node_modules:\n`; + private getFailureMessageForDifferentDependencyVersions(dependencyName: string, frameworkName: string, dependencyOccurrencesGroupedByVersion: IDictionary, projectDir: string): string { + let message = `Cannot use the same framework ${frameworkName} multiple times in your application. +This framework comes from ${dependencyName} plugin, which is installed multiple times in node_modules:\n`; _.each(dependencyOccurrencesGroupedByVersion, (dependencies, version) => { message += dependencies.map(d => `* Path: ${d.directory}, version: ${d.version}\n`); }); + message += this.getHelpMessage(projectDir); + return message; + } + + private getHelpMessage(projectDir: string): string { const existingLockFiles: string[] = []; PluginsService.LOCK_FILES.forEach(lockFile => { if (this.$fs.exists(path.join(projectDir, lockFile))) { @@ -255,8 +315,7 @@ ${dependencyName} plugin occurs multiple times in node_modules:\n`; msgForLockFiles += ` and ${existingLockFiles.join(", ")}`; } - message += `Probably you need to update your dependencies, remove node_modules${msgForLockFiles} and try again.`; - return message; + return `\nProbably you need to update your dependencies, remove node_modules${msgForLockFiles} and try again.`; } private convertToPluginData(cacheData: IDependencyData | INodeModuleData, projectDir: string): IPluginData { diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 6cfbc8a481..9bb05d8b66 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -4,7 +4,6 @@ import * as shelljs from "shelljs"; import { format } from "util"; import { exported } from "../common/decorators"; import { Hooks, TemplatesV2PackageJsonKeysToRemove } from "../constants"; -import * as temp from "temp"; import { performanceLog } from "../common/decorators"; export class ProjectService implements IProjectService { @@ -20,7 +19,8 @@ export class ProjectService implements IProjectService { private $projectNameService: IProjectNameService, private $projectTemplatesService: IProjectTemplatesService, private $staticConfig: IStaticConfig, - private $packageInstallationManager: IPackageInstallationManager) { } + private $packageInstallationManager: IPackageInstallationManager, + private $tempService: ITempService) { } public async validateProjectName(opts: { projectName: string, force: boolean, pathToProject: string }): Promise { let projectName = opts.projectName; @@ -152,7 +152,7 @@ export class ProjectService implements IProjectService { if (!this.$fs.exists(appResourcesDestinationPath)) { this.$fs.createDirectory(appResourcesDestinationPath); - const tempDir = temp.mkdirSync("ns-default-template"); + const tempDir = await this.$tempService.mkdirSync("ns-default-template"); // the template installed doesn't have App_Resources -> get from a default template await this.$pacoteService.extractPackage(constants.RESERVED_TEMPLATE_NAMES["default"], tempDir); const templateProjectData = this.$projectDataService.getProjectData(tempDir); diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts index 767a805191..49d9a1c43b 100644 --- a/lib/services/project-templates-service.ts +++ b/lib/services/project-templates-service.ts @@ -1,9 +1,7 @@ import * as path from "path"; -import * as temp from "temp"; import * as constants from "../constants"; import { format } from "util"; import { performanceLog } from "../common/decorators"; -temp.track(); export class ProjectTemplatesService implements IProjectTemplatesService { private templatePackageContents: IDictionary = {}; diff --git a/lib/services/temp-service.ts b/lib/services/temp-service.ts new file mode 100644 index 0000000000..21e42a223b --- /dev/null +++ b/lib/services/temp-service.ts @@ -0,0 +1,21 @@ +import * as temp from "temp"; + +export class TempService implements ITempService { + constructor(private $cleanupService: ICleanupService) { + temp.track(); + } + + public async mkdirSync(affixes: string): Promise { + const pathToDir = temp.mkdirSync(affixes); + await this.$cleanupService.addCleanupDeleteAction(pathToDir); + return pathToDir; + } + + public async path(options: ITempPathOptions): Promise { + const pathToFile = temp.path(options); + await this.$cleanupService.addCleanupDeleteAction(pathToFile); + return pathToFile; + } +} + +$injector.register("tempService", TempService); diff --git a/lib/tools/node-modules/node-modules-builder.ts b/lib/tools/node-modules/node-modules-builder.ts index f67267299b..82ebc5b1ec 100644 --- a/lib/tools/node-modules/node-modules-builder.ts +++ b/lib/tools/node-modules/node-modules-builder.ts @@ -9,7 +9,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder { const dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir); await platformData.platformProjectService.beforePrepareAllPlugins(projectData, dependencies); - const pluginsData = this.$pluginsService.getAllProductionPlugins(projectData, dependencies); + const pluginsData = this.$pluginsService.getAllProductionPlugins(projectData, platformData.platformNameLowerCase, dependencies); if (_.isEmpty(pluginsData)) { return; } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 400806a91c..a29aa306b6 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "nativescript", - "version": "6.4.0", + "version": "6.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3f42f9ae0d..f21bc6710c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nativescript", "preferGlobal": true, - "version": "6.4.0", + "version": "6.5.0", "author": "Telerik ", "description": "Command-line interface for building NativeScript projects", "bin": { diff --git a/test/controllers/add-platform-controller.ts b/test/controllers/add-platform-controller.ts index 2af133929d..f390e514bd 100644 --- a/test/controllers/add-platform-controller.ts +++ b/test/controllers/add-platform-controller.ts @@ -1,4 +1,4 @@ -import { InjectorStub, PacoteServiceStub } from "../stubs"; +import { InjectorStub, PacoteServiceStub, TempServiceStub } from "../stubs"; import { PlatformController } from "../../lib/controllers/platform-controller"; import { AddPlatformService } from "../../lib/services/platform/add-platform-service"; import { assert } from "chai"; @@ -23,6 +23,7 @@ function createInjector(data?: { latestFrameworkVersion: string }) { injector.register("pacoteService", { extractPackage: async (name: string): Promise => { extractedPackageFromPacote = name; } }); + injector.register("tempService", TempServiceStub); const logger = injector.resolve("logger"); logger.info = (message: string) => actualMessage = message; diff --git a/test/controllers/prepare-controller.ts b/test/controllers/prepare-controller.ts index 842882babe..2c842c56d5 100644 --- a/test/controllers/prepare-controller.ts +++ b/test/controllers/prepare-controller.ts @@ -1,7 +1,7 @@ import { assert } from "chai"; import { PrepareController } from "../../lib/controllers/prepare-controller"; import { MobileHelper } from "../../lib/common/mobile/mobile-helper"; -import { InjectorStub } from "../stubs"; +import { InjectorStub, TempServiceStub } from "../stubs"; import { PREPARE_READY_EVENT_NAME } from "../../lib/constants"; const projectDir = "/path/to/my/projecDir"; @@ -62,6 +62,8 @@ function createTestInjector(data: { hasNativeChanges: boolean }): IInjector { trackEventActionInGoogleAnalytics: () => ({}) }); + injector.register("tempService", TempServiceStub); + const prepareController: PrepareController = injector.resolve("prepareController"); prepareController.emit = (eventName: string, eventData: any) => { emittedEventNames.push(eventName); diff --git a/test/controllers/run-controller.ts b/test/controllers/run-controller.ts index 6b0c200278..40f3e74d0f 100644 --- a/test/controllers/run-controller.ts +++ b/test/controllers/run-controller.ts @@ -1,5 +1,5 @@ import { RunController } from "../../lib/controllers/run-controller"; -import { InjectorStub } from "../stubs"; +import { InjectorStub, TempServiceStub } from "../stubs"; import { LiveSyncServiceResolver } from "../../lib/resolvers/livesync-service-resolver"; import { MobileHelper } from "../../lib/common/mobile/mobile-helper"; import { assert } from "chai"; @@ -107,6 +107,7 @@ function createTestInjector() { injector.register("analyticsService", ({})); injector.register("debugController", {}); injector.register("liveSyncProcessDataService", LiveSyncProcessDataService); + injector.register("tempService", TempServiceStub); const devicesService = injector.resolve("devicesService"); devicesService.getDevicesForPlatform = () => [{ identifier: "myTestDeviceId1" }]; diff --git a/test/helpers/platform-command-helper.ts b/test/helpers/platform-command-helper.ts index be769607b8..47289bea67 100644 --- a/test/helpers/platform-command-helper.ts +++ b/test/helpers/platform-command-helper.ts @@ -1,6 +1,6 @@ import { assert } from "chai"; -import { InjectorStub } from "../stubs"; +import { InjectorStub, TempServiceStub } from "../stubs"; import { MobileHelper } from "../../lib/common/mobile/mobile-helper"; import { DevicePlatformsConstants } from "../../lib/common/mobile/device-platforms-constants"; import { PlatformCommandHelper } from "../../lib/helpers/platform-command-helper"; @@ -35,6 +35,7 @@ function createTestInjector() { injector.register("mobileHelper", MobileHelper); injector.register("devicePlatformsConstants", DevicePlatformsConstants); + injector.register("tempService", TempServiceStub); return injector; } diff --git a/test/ios-entitlements-service.ts b/test/ios-entitlements-service.ts index 41e5101c14..3ab491add5 100644 --- a/test/ios-entitlements-service.ts +++ b/test/ios-entitlements-service.ts @@ -31,6 +31,8 @@ describe("IOSEntitlements Service Tests", () => { getAllInstalledPlugins: async (): Promise => [] }); + testInjector.register("tempService", stubs.TempServiceStub); + return testInjector; }; diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 3df7b97f03..8ba03c84f2 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -30,7 +30,7 @@ import { YarnPackageManager } from "../lib/yarn-package-manager"; import { assert } from "chai"; import { SettingsService } from "../lib/common/test/unit-tests/stubs"; import { BUILD_XCCONFIG_FILE_NAME } from "../lib/constants"; -import { ProjectDataStub } from "./stubs"; +import { ProjectDataStub, TempServiceStub } from "./stubs"; import { xcode } from "../lib/node/xcode"; import temp = require("temp"); import { CocoaPodsPlatformManager } from "../lib/services/cocoapods-platform-manager"; @@ -181,6 +181,8 @@ function createTestInjector(projectPath: string, projectName: string, xCode?: IX testInjector.register("iOSNativeTargetService", { setXcodeTargetBuildConfigurationProperties: () => { /* */ } }); + testInjector.register("tempService", TempServiceStub); + return testInjector; } diff --git a/test/platform-commands.ts b/test/platform-commands.ts index 0832227ff7..ee6c7adb8a 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -192,6 +192,7 @@ function createTestInjector() { }); testInjector.register("addPlatformService", {}); testInjector.register("platformController", {}); + testInjector.register("tempService", stubs.TempServiceStub); testInjector.register("platformCommandHelper", PlatformCommandHelper); return testInjector; diff --git a/test/plugins-service.ts b/test/plugins-service.ts index bc7d220236..a987a61dda 100644 --- a/test/plugins-service.ts +++ b/test/plugins-service.ts @@ -161,6 +161,7 @@ function createTestInjector() { setShouldDispose: (shouldDispose: boolean): void => undefined }); testInjector.register("nodeModulesDependenciesBuilder", {}); + testInjector.register("tempService", stubs.TempServiceStub); return testInjector; } @@ -550,6 +551,7 @@ describe("Plugins service", () => { unitTestsInjector.register("mobileHelper", MobileHelper); unitTestsInjector.register("devicePlatformsConstants", DevicePlatformsConstants); unitTestsInjector.register("nodeModulesDependenciesBuilder", {}); + unitTestsInjector.register("tempService", stubs.TempServiceStub); const pluginsService: PluginsService = unitTestsInjector.resolve(PluginsService); testData.pluginsService = pluginsService; @@ -586,7 +588,8 @@ describe("Plugins service", () => { unitTestsInjector.register("platformsDataService", {}); unitTestsInjector.register("filesHashService", {}); unitTestsInjector.register("fs", { - exists: (filePath: string) => false + exists: (filePath: string) => filePath.indexOf("ios") !== -1, + readDirectory: (dir: string) => dir.indexOf("nativescript-ui-core") !== -1 ? ["a.framework"] : [] }); unitTestsInjector.register("packageManager", {}); unitTestsInjector.register("options", {}); @@ -596,6 +599,7 @@ describe("Plugins service", () => { unitTestsInjector.register("mobileHelper", MobileHelper); unitTestsInjector.register("devicePlatformsConstants", DevicePlatformsConstants); unitTestsInjector.register("nodeModulesDependenciesBuilder", {}); + unitTestsInjector.register("tempService", stubs.TempServiceStub); return unitTestsInjector; }; @@ -796,7 +800,7 @@ describe("Plugins service", () => { "version": "4.0.0", } ], - expectedWarning: "Detected same versions (4.0.0) of the nativescript-ui-core installed at locations: /Users/username/projectDir/node_modules/nativescript-ui-core, " + + expectedWarning: "Detected the framework a.framework is installed multiple times from the same versions of plugin (4.0.0) at locations: /Users/username/projectDir/node_modules/nativescript-ui-core, " + "/Users/username/projectDir/node_modules/nativescript-ui-listview/node_modules/nativescript-ui-core" }, { @@ -844,10 +848,45 @@ describe("Plugins service", () => { "dependencies": [] }, ], - expectedOutput: new Error(`Cannot use different versions of a NativeScript plugin in your application. -nativescript-ui-core plugin occurs multiple times in node_modules:\n` + + expectedOutput: new Error(`Cannot use the same framework a.framework multiple times in your application. +This framework comes from nativescript-ui-core plugin, which is installed multiple times in node_modules:\n` + "* Path: /Users/username/projectDir/node_modules/nativescript-ui-core, version: 3.0.0\n" + - "* Path: /Users/username/projectDir/node_modules/nativescript-ui-listview/node_modules/nativescript-ui-core, version: 4.0.0\n" + + "* Path: /Users/username/projectDir/node_modules/nativescript-ui-listview/node_modules/nativescript-ui-core, version: 4.0.0\n\n" + + `Probably you need to update your dependencies, remove node_modules and try again.`), + }, + { + testName: "fails when same framework is installed from multiple plugins", + inputDependencies: [ + { + "name": "nativescript-ui-core-forked", + "directory": "/Users/username/projectDir/node_modules/nativescript-ui-core-forked", + "depth": 0, + "version": "3.0.0", + "nativescript": { + "platforms": { + "android": "6.0.0", + "ios": "6.0.0" + } + }, + "dependencies": [] + }, + { + "name": "nativescript-ui-core", + "directory": "/Users/username/projectDir/node_modules/nativescript-ui-core", + "depth": 0, + "version": "3.0.0", + "nativescript": { + "platforms": { + "android": "6.0.0", + "ios": "6.0.0" + } + }, + "dependencies": [] + }, + ], + expectedOutput: new Error(`Detected the framework a.framework is installed from multiple plugins at locations:\n` + + "/Users/username/projectDir/node_modules/nativescript-ui-core-forked/platforms/ios/a.framework\n" + + "/Users/username/projectDir/node_modules/nativescript-ui-core/platforms/ios/a.framework\n\n" + `Probably you need to update your dependencies, remove node_modules and try again.`), } ]; @@ -858,9 +897,9 @@ nativescript-ui-core plugin occurs multiple times in node_modules:\n` + const pluginsService: IPluginsService = unitTestsInjector.resolve(PluginsService); if (testCase.expectedOutput instanceof Error) { - assert.throws(() => pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, testCase.inputDependencies), testCase.expectedOutput.message); + assert.throws(() => pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, "ios", testCase.inputDependencies), testCase.expectedOutput.message); } else { - const plugins = pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, testCase.inputDependencies); + const plugins = pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, "ios", testCase.inputDependencies); if (testCase.expectedWarning) { const logger = unitTestsInjector.resolve("logger"); @@ -884,8 +923,8 @@ nativescript-ui-core plugin occurs multiple times in node_modules:\n` + const pluginsService: IPluginsService = unitTestsInjector.resolve(PluginsService); const inputDependencies = [ { - "name": "tns-core-modules", - "directory": "/Users/username/projectDir/node_modules/tns-core-modules", + "name": "nativescript-ui-core", + "directory": "/Users/username/projectDir/node_modules/nativescript-ui-core", "depth": 0, "version": "6.3.0", "nativescript": { @@ -899,8 +938,8 @@ nativescript-ui-core plugin occurs multiple times in node_modules:\n` + ] }, { - "name": "tns-core-modules", - "directory": "/Users/username/projectDir/node_modules/some-package/tns-core-modules", + "name": "nativescript-ui-core", + "directory": "/Users/username/projectDir/node_modules/some-package/nativescript-ui-core", "depth": 1, "version": "6.3.0", "nativescript": { @@ -916,16 +955,16 @@ nativescript-ui-core plugin occurs multiple times in node_modules:\n` + ]; _.range(3).forEach(() => { - pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, inputDependencies); + pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, "ios", inputDependencies); }); const logger = unitTestsInjector.resolve("logger"); - const expectedWarnMessage = "Detected same versions (%s) of the tns-core-modules installed at locations: /Users/username/projectDir/node_modules/tns-core-modules, /Users/username/projectDir/node_modules/some-package/tns-core-modules\n"; + const expectedWarnMessage = "Detected the framework a.framework is installed multiple times from the same versions of plugin (%s) at locations: /Users/username/projectDir/node_modules/nativescript-ui-core, /Users/username/projectDir/node_modules/some-package/nativescript-ui-core\n"; assert.equal(logger.warnOutput, util.format(expectedWarnMessage, "6.3.0"), "The warn message must be shown only once - the result of the private method must be cached as input dependencies have not changed"); inputDependencies[0].version = "1.0.0"; inputDependencies[1].version = "1.0.0"; - pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, inputDependencies); + pluginsService.getAllProductionPlugins({ projectDir: "projectDir" }, "ios", inputDependencies); assert.equal(logger.warnOutput, util.format(expectedWarnMessage, "6.3.0") + util.format(expectedWarnMessage, "1.0.0"), "When something in input dependencies change, the cached value shouldn't be taken into account"); }); }); diff --git a/test/project-service.ts b/test/project-service.ts index 04b302076e..0a35411367 100644 --- a/test/project-service.ts +++ b/test/project-service.ts @@ -3,7 +3,7 @@ import * as constants from "../lib/constants"; import * as ProjectServiceLib from "../lib/services/project-service"; import { assert } from "chai"; import { SettingsService } from "../lib/common/test/unit-tests/stubs"; -import { LoggerStub, ErrorsStub } from "./stubs"; +import { LoggerStub, ErrorsStub, TempServiceStub } from "./stubs"; import * as path from "path"; describe("projectService", () => { @@ -67,6 +67,7 @@ describe("projectService", () => { downloadAndExtract: () => Promise.resolve(), extractPackage: () => Promise.resolve() }); + testInjector.register("tempService", TempServiceStub); return testInjector; }; @@ -126,6 +127,7 @@ describe("projectService", () => { manifest: () => Promise.resolve(), downloadAndExtract: () => Promise.resolve() }); + testInjector.register("tempService", TempServiceStub); return testInjector; }; diff --git a/test/services/ios/export-options-plist-service.ts b/test/services/ios/export-options-plist-service.ts index 66abec28e3..b4132d8b2f 100644 --- a/test/services/ios/export-options-plist-service.ts +++ b/test/services/ios/export-options-plist-service.ts @@ -1,6 +1,7 @@ import { Yok } from "../../../lib/common/yok"; import { ExportOptionsPlistService } from "../../../lib/services/ios/export-options-plist-service"; import { assert } from "chai"; +import { TempServiceStub } from "../../stubs"; let actualPlistTemplate: string = null; const projectName = "myProjectName"; @@ -15,6 +16,7 @@ function createTestInjector() { } }); injector.register("exportOptionsPlistService", ExportOptionsPlistService); + injector.register("tempService", TempServiceStub); return injector; } @@ -45,13 +47,13 @@ describe("ExportOptionsPlistService", () => { _.each(testCases, testCase => { _.each(["Development", "AdHoc", "Distribution", "Enterprise"], provisionType => { - it(testCase.name, () => { + it(testCase.name, async () => { const injector = createTestInjector(); const exportOptionsPlistService = injector.resolve("exportOptionsPlistService"); exportOptionsPlistService.getExportOptionsMethod = () => provisionType; const projectData = { projectName, projectIdentifiers: { ios: "org.nativescript.myTestApp" }}; - exportOptionsPlistService.createDevelopmentExportOptionsPlist(archivePath, projectData, testCase.buildConfig); + await exportOptionsPlistService.createDevelopmentExportOptionsPlist(archivePath, projectData, testCase.buildConfig); const template = actualPlistTemplate.split("\n").join(" "); assert.isTrue(template.indexOf(`method ${provisionType}`) > 0); @@ -88,13 +90,13 @@ describe("ExportOptionsPlistService", () => { ]; _.each(testCases, testCase => { - it(testCase.name, () => { + it(testCase.name, async () => { const injector = createTestInjector(); const exportOptionsPlistService = injector.resolve("exportOptionsPlistService"); exportOptionsPlistService.getExportOptionsMethod = () => "app-store"; const projectData = { projectName, projectIdentifiers: { ios: "org.nativescript.myTestApp" }}; - exportOptionsPlistService.createDistributionExportOptionsPlist(projectRoot, projectData, testCase.buildConfig); + await exportOptionsPlistService.createDistributionExportOptionsPlist(projectRoot, projectData, testCase.buildConfig); const template = actualPlistTemplate.split("\n").join(" "); assert.isTrue(template.indexOf("method app-store") > 0); diff --git a/test/services/ios/xcodebuild-service.ts b/test/services/ios/xcodebuild-service.ts index 17f1dfebca..aabcc1627c 100644 --- a/test/services/ios/xcodebuild-service.ts +++ b/test/services/ios/xcodebuild-service.ts @@ -17,8 +17,8 @@ let actualBuildOptions:IXcodebuildCommandOptions = null; function createTestInjector(): IInjector { const injector = new Yok(); injector.register("exportOptionsPlistService", { - createDevelopmentExportOptionsPlist: () => exportOptionsPlistOutput, - createDistributionExportOptionsPlist: () => exportOptionsPlistOutput + createDevelopmentExportOptionsPlist: async () => exportOptionsPlistOutput, + createDistributionExportOptionsPlist: async () => exportOptionsPlistOutput }); injector.register("xcodebuildArgsService", { getBuildForDeviceArgs: async () => [], diff --git a/test/services/livesync/android-device-livesync-service-base.ts b/test/services/livesync/android-device-livesync-service-base.ts index c24d0c09b6..62046a50df 100644 --- a/test/services/livesync/android-device-livesync-service-base.ts +++ b/test/services/livesync/android-device-livesync-service-base.ts @@ -186,7 +186,7 @@ function setup(options?: ITestSetupInput): ITestSetupOutput { const injector = createTestInjector(); const fs = injector.resolve("fs"); - const deviceHashService = new AndroidDeviceHashService(mockAdb(), appIdentifier, fs, injector.resolve("mobileHelper")); + const deviceHashService = new AndroidDeviceHashService(mockAdb(), appIdentifier, fs, injector.resolve("mobileHelper"), { mkdirSync: async () => "" }); const localToDevicePaths = _.keys(filesToShasums).map(file => injector.resolve(LocalToDevicePathDataMock, { filePath: file })); const deviceAppData = createDeviceAppData(deviceHashService); const androidDeviceLiveSyncServiceBase = new AndroidDeviceLiveSyncServiceBaseMock(injector, mockPlatformsData(), mockFilesHashService(), mockLogger(), mockDevice(deviceHashService)); diff --git a/test/services/livesync/android-livesync-tool.ts b/test/services/livesync/android-livesync-tool.ts index 2e058845a4..2a203a586c 100644 --- a/test/services/livesync/android-livesync-tool.ts +++ b/test/services/livesync/android-livesync-tool.ts @@ -1,7 +1,7 @@ import { Yok } from "../../../lib/common/yok"; import { assert } from "chai"; import * as sinon from "sinon"; -import { LoggerStub } from "../../stubs"; +import { LoggerStub, TempServiceStub } from "../../stubs"; import { AndroidLivesyncTool } from "../../../lib/services/livesync/android-livesync-tool"; import { LiveSyncSocket } from "../../../lib/services/livesync/livesync-socket"; import { MobileHelper } from "../../../lib/common/mobile/mobile-helper"; @@ -86,6 +86,7 @@ const createTestInjector = (socket: INetSocket, fileStreams: IDictionary ({}) }); + injector.register("tempService", TempServiceStub); const fs = injector.resolve("fs"); fs.exists = () => false; diff --git a/test/services/playground/preview-app-livesync-service.ts b/test/services/playground/preview-app-livesync-service.ts index 122a2d5499..2b689996f9 100644 --- a/test/services/playground/preview-app-livesync-service.ts +++ b/test/services/playground/preview-app-livesync-service.ts @@ -1,6 +1,6 @@ import { Yok } from "../../../lib/common/yok"; import * as _ from 'lodash'; -import { LoggerStub, ErrorsStub, MarkingModeServiceStub } from "../../stubs"; +import { LoggerStub, ErrorsStub, MarkingModeServiceStub, TempServiceStub } from "../../stubs"; import { FilePayload, Device, FilesPayload } from "nativescript-preview-sdk"; import * as chai from "chai"; import * as path from "path"; @@ -189,6 +189,7 @@ function createTestInjector(options?: { injector.register("pluginsService", { ensureAllDependenciesAreInstalled: () => { return Promise.resolve(); } }); + injector.register("tempService", TempServiceStub); return injector; } diff --git a/test/stubs.ts b/test/stubs.ts index 8488e3bac1..230df87500 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -10,6 +10,8 @@ import { Yok } from "./../lib/common/yok"; import { HostInfo } from "./../lib/common/host-info"; import { DevicePlatformsConstants } from "./../lib/common/mobile/device-platforms-constants"; import { PrepareData } from "../lib/data/prepare-data"; +import * as temp from "temp"; +temp.track(); export class LoggerStub implements ILogger { initialize(opts?: ILoggerOptions): void { } @@ -942,3 +944,13 @@ export class InjectorStub extends Yok implements IInjector { this.register("terminalSpinnerService", TerminalSpinnerServiceStub); } } + +export class TempServiceStub implements ITempService { + public async mkdirSync(affixes: string): Promise { + return temp.mkdirSync(affixes); + } + + public async path(options: ITempPathOptions): Promise { + return temp.path(options); + } +}