From 114268011dd1a8cb22bd858db6abec82d7642cdb Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Thu, 25 Jan 2018 11:39:07 +0200 Subject: [PATCH 1/9] refactor(project-data): read app_resources dir from nsconfig.json Refactor the project-data service to read a config.json for the 'app_resources' property entry, which points relatively to the App_Resources directory. All references to app/App_Resources now retrieve the path from said service. To keep the service backwards compatible, if no config.json is present, or the app_resources entry isn't available, location defaults to projectDir/app/App_Resources. --- lib/constants.ts | 2 ++ lib/definitions/project.d.ts | 2 +- lib/project-data.ts | 22 +++++++++++++++- lib/providers/project-files-provider.ts | 10 +++---- lib/services/android-project-service.ts | 4 +-- lib/services/app-files-updater.ts | 5 ++-- lib/services/ios-entitlements-service.ts | 4 +-- lib/services/ios-project-service.ts | 15 +++++------ lib/services/livesync/livesync-service.ts | 2 ++ lib/services/prepare-platform-js-service.ts | 8 ++++++ lib/services/project-changes-service.ts | 9 ++++--- lib/services/project-service.ts | 2 +- test/ios-entitlements-service.ts | 3 ++- test/ios-project-service.ts | 8 +++--- test/platform-service.ts | 1 - test/project-files-provider.ts | 16 +++++------- test/project-service.ts | 2 +- test/stubs.ts | 29 +++++++++++++++++++-- 18 files changed, 97 insertions(+), 47 deletions(-) diff --git a/lib/constants.ts b/lib/constants.ts index 0525e4f425..64c60c6147 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -28,6 +28,8 @@ export const BUILD_DIR = "build"; export const OUTPUTS_DIR = "outputs"; export const APK_DIR = "apk"; export const RESOURCES_DIR = "res"; +export const CONFIG_NS_FILE_NAME = "nsconfig.json"; +export const CONFIG_NS_APP_RESOURCES_ENTRY = "app_resources"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 671e8e59d6..798a96a3d9 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -61,7 +61,6 @@ interface IProjectData extends IProjectDir { dependencies: any; devDependencies: IStringDictionary; appDirectoryPath: string; - appResourcesDirectoryPath: string; projectType: string; /** * Initializes project data with the given project directory. If none supplied defaults to --path option or cwd. @@ -69,6 +68,7 @@ interface IProjectData extends IProjectDir { * @returns {void} */ initializeProjectData(projectDir?: string): void; + getAppResourcesDirectoryPath(projectDir?: string): string; } interface IProjectDataService { diff --git a/lib/project-data.ts b/lib/project-data.ts index da438d88cc..34febd3a5e 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -69,7 +69,6 @@ export class ProjectData implements IProjectData { this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME); this.projectFilePath = projectFilePath; this.appDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME); - this.appResourcesDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); this.projectId = data.id; this.dependencies = fileContent.dependencies; this.devDependencies = fileContent.devDependencies; @@ -87,6 +86,27 @@ export class ProjectData implements IProjectData { this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", projectDir || this.$options.path || currentDir); } + public getAppResourcesDirectoryPath(projectDir?: string): string { + if (!projectDir) { + projectDir = this.projectDir; + } + + const configNSFilePath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME); + let absoluteAppResourcesDirPath: string; + + if (this.$fs.exists(configNSFilePath)) { + const configNS = this.$fs.readJson(configNSFilePath); + + if (configNS && configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]) { + const appResourcesDirPath = configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]; + + absoluteAppResourcesDirPath = path.resolve(projectDir, appResourcesDirPath); + } + } + + return absoluteAppResourcesDirPath || path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + } + private getProjectType(): string { let detectedProjectType = _.find(ProjectData.PROJECT_TYPES, (projectType) => projectType.isDefaultProjectType).type; diff --git a/lib/providers/project-files-provider.ts b/lib/providers/project-files-provider.ts index 89917e1da0..78b5d7e0db 100644 --- a/lib/providers/project-files-provider.ts +++ b/lib/providers/project-files-provider.ts @@ -6,11 +6,11 @@ import { ProjectFilesProviderBase } from "../common/services/project-files-provi export class ProjectFilesProvider extends ProjectFilesProviderBase { constructor(private $platformsData: IPlatformsData, $mobileHelper: Mobile.IMobileHelper, - $options:IOptions) { - super($mobileHelper, $options); + $options: IOptions) { + super($mobileHelper, $options); } - private static INTERNAL_NONPROJECT_FILES = [ "**/*.ts" ]; + private static INTERNAL_NONPROJECT_FILES = ["**/*.ts"]; public mapFilePath(filePath: string, platform: string, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): string { const platformData = this.$platformsData.getPlatformData(platform.toLowerCase(), projectData); @@ -23,14 +23,14 @@ export class ProjectFilesProvider extends ProjectFilesProviderBase { mappedFilePath = path.join(platformData.appDestinationDirectoryPath, path.relative(projectData.projectDir, parsedFilePath)); } - const appResourcesDirectoryPath = path.join(constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + const appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath(); const platformSpecificAppResourcesDirectoryPath = path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName); if (parsedFilePath.indexOf(appResourcesDirectoryPath) > -1 && parsedFilePath.indexOf(platformSpecificAppResourcesDirectoryPath) === -1) { return null; } if (parsedFilePath.indexOf(platformSpecificAppResourcesDirectoryPath) > -1) { - const appResourcesRelativePath = path.relative(path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, + const appResourcesRelativePath = path.relative(path.join(projectData.getAppResourcesDirectoryPath(), platformData.normalizedPlatformName), parsedFilePath); mappedFilePath = path.join(platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(projectData), appResourcesRelativePath); } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index ae026b8f94..2878788c4b 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -269,7 +269,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject shell.sed('-i', /__PROJECT_NAME__/, this.getProjectNameFromId(projectData), gradleSettingsFilePath); // will replace applicationId in app/App_Resources/Android/app.gradle if it has not been edited by the user - const userAppGradleFilePath = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, "app.gradle"); + const userAppGradleFilePath = path.join(projectData.getAppResourcesDirectoryPath(), this.$devicePlatformsConstants.Android, "app.gradle"); try { shell.sed('-i', /__PACKAGE__/, projectData.projectId, userAppGradleFilePath); @@ -391,7 +391,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } public ensureConfigurationFileInAppResources(projectData: IProjectData): void { - const originalAndroidManifestFilePath = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, this.getPlatformData(projectData).configurationFileName); + const originalAndroidManifestFilePath = path.join(projectData.getAppResourcesDirectoryPath(), this.$devicePlatformsConstants.Android, this.getPlatformData(projectData).configurationFileName); const manifestExists = this.$fs.exists(originalAndroidManifestFilePath); diff --git a/lib/services/app-files-updater.ts b/lib/services/app-files-updater.ts index 885da78f20..3d804647f9 100644 --- a/lib/services/app-files-updater.ts +++ b/lib/services/app-files-updater.ts @@ -54,11 +54,12 @@ export class AppFilesUpdater { protected readSourceDir(): string[] { const tnsDir = path.join(this.appSourceDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); - return this.fs.enumerateFilesInDirectorySync(this.appSourceDirectoryPath, null, { includeEmptyDirectories: true }).filter(dirName => dirName !== tnsDir); + const defaultAppResourcesDir = path.join(this.appSourceDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); + return this.fs.enumerateFilesInDirectorySync(this.appSourceDirectoryPath, null, { includeEmptyDirectories: true }).filter(dirName => dirName !== tnsDir).filter(dirName => !dirName.startsWith(defaultAppResourcesDir)); } protected resolveAppSourceFiles(): string[] { - // Copy all files from app dir, but make sure to exclude tns_modules + // Copy all files from app dir, but make sure to exclude tns_modules and App_Resources let sourceFiles = this.readSourceDir(); if (this.options.release) { diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index 0f749e31cf..3be4826e50 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -1,5 +1,4 @@ import * as path from "path"; -import * as constants from "../constants"; import { PlistSession } from "plist-merge-patch"; export class IOSEntitlementsService { @@ -14,8 +13,7 @@ export class IOSEntitlementsService { private getDefaultAppEntitlementsPath(projectData: IProjectData) : string { const entitlementsName = IOSEntitlementsService.DefaultEntitlementsName; - const entitlementsPath = path.join(projectData.projectDir, - constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, + const entitlementsPath = path.join(projectData.getAppResourcesDirectoryPath(), this.$mobileHelper.normalizePlatformName(this.$devicePlatformsConstants.iOS), entitlementsName); return entitlementsPath; diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index ac344eef83..c008222e85 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -765,9 +765,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f private getInfoPlistPath(projectData: IProjectData): string { return path.join( - projectData.projectDir, - constants.APP_FOLDER_NAME, - constants.APP_RESOURCES_FOLDER_NAME, + projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName ); @@ -787,7 +785,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f private async mergeInfoPlists(buildOptions: IRelease, projectData: IProjectData): Promise { const projectDir = projectData.projectDir; - const infoPlistPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); + const infoPlistPath = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); this.ensureConfigurationFileInAppResources(); if (!this.$fs.exists(infoPlistPath)) { @@ -1217,7 +1215,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } } - const appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); + const appResourcesXcconfigPath = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); if (this.$fs.exists(appResourcesXcconfigPath)) { await this.mergeXcconfigFiles(appResourcesXcconfigPath, pluginsXcconfigFilePath); } @@ -1272,7 +1270,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private getBuildXCConfigFilePath(projectData: IProjectData): string { - const buildXCConfig = path.join(projectData.appResourcesDirectoryPath, + const buildXCConfig = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); return buildXCConfig; } @@ -1334,7 +1332,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f const choicePersist = await this.$prompter.promptForChoice("Do you want to make teamId: " + teamId + " a persistent choice for your app?", choicesPersist); switch (choicesPersist.indexOf(choicePersist)) { case 0: - const xcconfigFile = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); + const xcconfigFile = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); this.$fs.appendFile(xcconfigFile, "\nDEVELOPMENT_TEAM = " + teamId + "\n"); break; case 1: @@ -1352,8 +1350,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private validateApplicationIdentifier(projectData: IProjectData): void { - const projectDir = projectData.projectDir; - const infoPlistPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); + const infoPlistPath = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); const mergedPlistPath = this.getPlatformData(projectData).configurationFilePath; if (!this.$fs.exists(infoPlistPath) || !this.$fs.exists(mergedPlistPath)) { diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 4f113d2e9a..66e83da9d7 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -537,6 +537,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } } + pattern.push(projectData.getAppResourcesDirectoryPath()); + const currentWatcherInfo = this.liveSyncProcessesInfo[liveSyncData.projectDir].watcherInfo; const areWatcherPatternsDifferent = () => _.xor(currentWatcherInfo.patterns, patterns).length; if (!currentWatcherInfo || areWatcherPatternsDifferent()) { diff --git a/lib/services/prepare-platform-js-service.ts b/lib/services/prepare-platform-js-service.ts index ad6fccc4be..e7da7d3a79 100644 --- a/lib/services/prepare-platform-js-service.ts +++ b/lib/services/prepare-platform-js-service.ts @@ -36,6 +36,7 @@ export class PreparePlatformJSService extends PreparePlatformService implements public async preparePlatform(config: IPreparePlatformJSInfo): Promise { if (!config.changesInfo || config.changesInfo.appFilesChanged || config.changesInfo.changesRequirePrepare) { await this.copyAppFiles(config); + this.copyAppResourcesFiles(config); } if (config.changesInfo && !config.changesInfo.changesRequirePrepare) { @@ -101,6 +102,13 @@ export class PreparePlatformJSService extends PreparePlatformService implements this.$errors.failWithoutHelp(`Processing node_modules failed. ${error}`); } } + + private copyAppResourcesFiles(config: IPreparePlatformJSInfo) { + const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const appResourcesSourcePath = config.projectData.getAppResourcesDirectoryPath(); + + shell.cp("-Rf", appResourcesSourcePath, appDestinationDirectoryPath); + } } $injector.register("preparePlatformJSService", PreparePlatformJSService); diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 9e2d1ea3c9..20e224d3ee 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -60,9 +60,10 @@ export class ProjectChangesService implements IProjectChangesService { this._changesInfo = new ProjectChangesInfo(); if (!this.ensurePrepareInfo(platform, projectData, projectChangesOptions)) { this._newFiles = 0; - this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData); + this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.getAppResourcesDirectoryPath(), projectData); this._changesInfo.packageChanged = this.isProjectFileChanged(projectData, platform); - this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.appResourcesDirectoryPath, null, projectData); + this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.getAppResourcesDirectoryPath(), null, projectData); + /*done because currently all node_modules are traversed, a possible improvement could be traversing only the production dependencies*/ this._changesInfo.nativeChanged = projectChangesOptions.skipModulesNativeCheck ? false : this.containsNewerFiles( path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME), path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME, "tns-ios-inspector"), @@ -72,7 +73,7 @@ export class ProjectChangesService implements IProjectChangesService { if (this._newFiles > 0 || this._changesInfo.nativeChanged) { this._changesInfo.modulesChanged = true; } - const platformResourcesDir = path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName); + const platformResourcesDir = path.join(projectData.getAppResourcesDirectoryPath(), platformData.normalizedPlatformName); if (platform === this.$devicePlatformsConstants.iOS.toLowerCase()) { this._changesInfo.configChanged = this.filesChanged([path.join(platformResourcesDir, platformData.configurationFileName), path.join(platformResourcesDir, "LaunchScreen.storyboard"), @@ -281,7 +282,7 @@ export class ProjectChangesService implements IProjectChangesService { return true; } const projectDir = projectData.projectDir; - if (_.startsWith(path.join(projectDir, file), projectData.appResourcesDirectoryPath)) { + if (_.startsWith(path.join(projectDir, file), projectData.getAppResourcesDirectoryPath())) { return true; } if (_.startsWith(file, NODE_MODULES_FOLDER_NAME)) { diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index fcb50d79ca..4e449cf3eb 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -112,7 +112,7 @@ export class ProjectService implements IProjectService { private async ensureAppResourcesExist(projectDir: string): Promise { const appPath = path.join(projectDir, constants.APP_FOLDER_NAME), - appResourcesDestinationPath = path.join(appPath, constants.APP_RESOURCES_FOLDER_NAME); + appResourcesDestinationPath = this.$projectData.getAppResourcesDirectoryPath(projectDir); if (!this.$fs.exists(appResourcesDestinationPath)) { this.$fs.createDirectory(appResourcesDestinationPath); diff --git a/test/ios-entitlements-service.ts b/test/ios-entitlements-service.ts index 2edfb58ba1..181dddf67b 100644 --- a/test/ios-entitlements-service.ts +++ b/test/ios-entitlements-service.ts @@ -19,6 +19,7 @@ describe("IOSEntitlements Service Tests", () => { const testInjector = new yok.Yok(); testInjector.register('platformsData', stubs.PlatformsDataStub); + testInjector.register('projectData', stubs.ProjectDataStub); testInjector.register("logger", stubs.LoggerStub); testInjector.register('iOSEntitlementsService', IOSEntitlementsService); @@ -46,7 +47,7 @@ describe("IOSEntitlements Service Tests", () => { injector = createTestInjector(); platformsData = injector.resolve("platformsData"); - projectData = platformsData.getPlatformData(); + projectData = injector.resolve("projectData"); projectData.projectName = 'testApp'; projectData.platformsDir = temp.mkdirSync("platformsDir"); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index dfccaaf237..dbdc6b3ba6 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -29,11 +29,11 @@ import { Utils } from "../lib/common/utils"; import { CocoaPodsService } from "../lib/services/cocoapods-service"; import { NpmInstallationManager } from "../lib/npm-installation-manager"; import { NodePackageManager } from "../lib/node-package-manager"; -import * as constants from "../lib/constants"; import { assert } from "chai"; import { IOSProvisionService } from "../lib/services/ios-provision-service"; import { SettingsService } from "../lib/common/test/unit-tests/stubs"; +import { ProjectDataStub } from "../test/stubs"; import temp = require("temp"); temp.track(); @@ -65,12 +65,13 @@ function createTestInjector(projectPath: string, projectName: string): IInjector testInjector.register("iOSEntitlementsService", IOSEntitlementsService); testInjector.register("logger", LoggerLib.Logger); testInjector.register("options", OptionsLib.Options); - testInjector.register("projectData", { + const projectData = Object.assign({}, ProjectDataStub, { platformsDir: path.join(projectPath, "platforms"), projectName: projectName, projectPath: projectPath, projectFilePath: path.join(projectPath, "package.json") }); + testInjector.register("projectData", projectData); testInjector.register("projectHelper", {}); testInjector.register("xcodeSelectService", {}); testInjector.register("staticConfig", ConfigLib.StaticConfig); @@ -796,8 +797,7 @@ describe("Merge Project XCConfig files", () => { iOSEntitlementsService = testInjector.resolve("iOSEntitlementsService"); - appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, - constants.APP_RESOURCES_FOLDER_NAME, "iOS", "build.xcconfig"); + appResourcesXcconfigPath = path.join(projectData.getAppResourcesDirectoryPath(), "iOS", "build.xcconfig"); appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html // DEVELOPMENT_TEAM = YOUR_TEAM_ID; diff --git a/test/platform-service.ts b/test/platform-service.ts index aaa2876cbc..3db11dad85 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -452,7 +452,6 @@ describe('Platform Service Tests', () => { const projectData = testInjector.resolve("projectData"); projectData.projectDir = testDirData.tempFolder; projectData.appDirectoryPath = testDirData.appFolderPath; - projectData.appResourcesDirectoryPath = path.join(testDirData.appFolderPath, "App_Resources"); projectData.projectName = "app"; platformService = testInjector.resolve("platformService"); diff --git a/test/project-files-provider.ts b/test/project-files-provider.ts index 0edd3f5566..47b1035a09 100644 --- a/test/project-files-provider.ts +++ b/test/project-files-provider.ts @@ -1,5 +1,6 @@ import { Yok } from "../lib/common/yok"; import { ProjectFilesProvider } from "../lib/providers/project-files-provider"; +import * as stubs from "./stubs"; import { assert } from "chai"; import * as path from "path"; @@ -14,6 +15,8 @@ function createTestInjector(): IInjector { platformNames: ["Android", "iOS"] }); + testInjector.register('projectData', stubs.ProjectDataStub); + testInjector.register("platformsData", { getPlatformData: (platform: string) => { return { @@ -26,10 +29,6 @@ function createTestInjector(): IInjector { }, }); - testInjector.register("projectData", { - projectDir: projectDir - }); - testInjector.register("options", { release: false }); return testInjector; @@ -38,9 +37,12 @@ function createTestInjector(): IInjector { describe("project-files-provider", () => { let testInjector: IInjector; let projectFilesProvider: IProjectFilesProvider; + let projectData: IProjectData; beforeEach(() => { testInjector = createTestInjector(); + projectData = testInjector.resolve("projectData"); + projectData.projectDir = projectDir; projectFilesProvider = testInjector.resolve(ProjectFilesProvider); }); @@ -56,37 +58,31 @@ describe("project-files-provider", () => { describe("mapFilePath", () => { it("returns file path from prepared project when path from app dir is passed", () => { - const projectData: IProjectData = testInjector.resolve("projectData"); const mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.js"), "android", projectData, {}); assert.deepEqual(mappedFilePath, path.join(appDestinationDirectoryPath, "app", "test.js")); }); it("returns file path from prepared project when path from app/App_Resources/platform dir is passed", () => { - const projectData: IProjectData = testInjector.resolve("projectData"); const mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "android", "test.js"), "android", projectData, {}); assert.deepEqual(mappedFilePath, path.join(appResourcesDestinationDirectoryPath, "test.js")); }); it("returns null when path from app/App_Resources/android dir is passed and iOS platform is specified", () => { - const projectData: IProjectData = testInjector.resolve("projectData"); const mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "android", "test.js"), "iOS", projectData, {}); assert.deepEqual(mappedFilePath, null); }); it("returns null when path from app/App_Resources/ dir (not platform specific) is passed", () => { - const projectData: IProjectData = testInjector.resolve("projectData"); const mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "test.js"), "android", projectData, {}); assert.deepEqual(mappedFilePath, null); }); it("returns file path from prepared project when path from app dir is passed and it contains platform in its name", () => { - const projectData: IProjectData = testInjector.resolve("projectData"); const mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.android.js"), "android", projectData, {}); assert.deepEqual(mappedFilePath, path.join(appDestinationDirectoryPath, "app", "test.js")); }); it("returns file path from prepared project when path from app dir is passed and it contains configuration in its name", () => { - const projectData: IProjectData = testInjector.resolve("projectData"); const mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.debug.js"), "android", projectData, {}); assert.deepEqual(mappedFilePath, path.join(appDestinationDirectoryPath, "app", "test.js")); }); diff --git a/test/project-service.ts b/test/project-service.ts index 94add49581..6eff78b1e5 100644 --- a/test/project-service.ts +++ b/test/project-service.ts @@ -133,7 +133,7 @@ class ProjectIntegrationTest { this.testInjector.register("projectHelper", ProjectHelperLib.ProjectHelper); this.testInjector.register("projectTemplatesService", ProjectTemplatesService); this.testInjector.register("projectNameValidator", mockProjectNameValidator); - this.testInjector.register("projectData", {}); + this.testInjector.register("projectData", stubs.ProjectDataStub); this.testInjector.register("fs", FileSystem); this.testInjector.register("projectDataService", ProjectDataServiceLib.ProjectDataService); diff --git a/test/stubs.ts b/test/stubs.ts index 2e32020b30..05bfab57fd 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -4,6 +4,10 @@ import * as util from "util"; import * as chai from "chai"; import { EventEmitter } from "events"; +import * as fs from "fs"; +import * as path from "path"; +import * as constants from "./../lib/constants"; + export class LoggerStub implements ILogger { setLevel(level: string): void { } getLevel(): string { return undefined; } @@ -244,16 +248,37 @@ export class ProjectDataStub implements IProjectData { get platformsDir(): string { return ""; } + set platformsDir(value) { + } projectFilePath: string; projectId: string; dependencies: any; appDirectoryPath: string; - appResourcesDirectoryPath: string; devDependencies: IStringDictionary; projectType: string; - initializeProjectData(projectDir?: string): void { + public initializeProjectData(projectDir?: string): void { this.projectDir = this.projectDir || projectDir; } + public getAppResourcesDirectoryPath(projectDir?: string): string { + if (!projectDir) { + projectDir = this.projectDir; + } + + const configNSFilePath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME); + let absoluteAppResourcesDirPath: string; + + if (fs.existsSync(configNSFilePath)) { + const configNS = JSON.parse(fs.readFileSync(configNSFilePath).toString()); + + if (configNS && configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]) { + const appResourcesDirPath = configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]; + + absoluteAppResourcesDirPath = path.resolve(projectDir, appResourcesDirPath); + } + } + + return absoluteAppResourcesDirPath || path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + } } export class PlatformProjectServiceStub extends EventEmitter implements IPlatformProjectService { From 21abc36195136b5be037ae46edfac33da551c8b3 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Fri, 2 Feb 2018 15:09:41 +0200 Subject: [PATCH 2/9] refactor(project-data): make appResourcesDirectoryPath getter --- lib/definitions/project.d.ts | 1 + lib/project-data.ts | 4 +++- lib/providers/project-files-provider.ts | 4 ++-- lib/services/android-project-service.ts | 4 ++-- lib/services/ios-entitlements-service.ts | 2 +- lib/services/ios-project-service.ts | 12 ++++++------ lib/services/livesync/livesync-service.ts | 2 +- lib/services/prepare-platform-js-service.ts | 2 +- lib/services/project-changes-service.ts | 8 ++++---- 9 files changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 798a96a3d9..863ada0176 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -61,6 +61,7 @@ interface IProjectData extends IProjectDir { dependencies: any; devDependencies: IStringDictionary; appDirectoryPath: string; + appResourcesDirectoryPath: string; projectType: string; /** * Initializes project data with the given project directory. If none supplied defaults to --path option or cwd. diff --git a/lib/project-data.ts b/lib/project-data.ts index 34febd3a5e..def80a92ad 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -33,7 +33,9 @@ export class ProjectData implements IProjectData { public projectId: string; public projectName: string; public appDirectoryPath: string; - public appResourcesDirectoryPath: string; + get appResourcesDirectoryPath(): string { + return this.getAppResourcesDirectoryPath(); + } public dependencies: any; public devDependencies: IStringDictionary; public projectType: string; diff --git a/lib/providers/project-files-provider.ts b/lib/providers/project-files-provider.ts index 78b5d7e0db..2885f904c1 100644 --- a/lib/providers/project-files-provider.ts +++ b/lib/providers/project-files-provider.ts @@ -23,14 +23,14 @@ export class ProjectFilesProvider extends ProjectFilesProviderBase { mappedFilePath = path.join(platformData.appDestinationDirectoryPath, path.relative(projectData.projectDir, parsedFilePath)); } - const appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath(); + const appResourcesDirectoryPath = projectData.appResourcesDirectoryPath; const platformSpecificAppResourcesDirectoryPath = path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName); if (parsedFilePath.indexOf(appResourcesDirectoryPath) > -1 && parsedFilePath.indexOf(platformSpecificAppResourcesDirectoryPath) === -1) { return null; } if (parsedFilePath.indexOf(platformSpecificAppResourcesDirectoryPath) > -1) { - const appResourcesRelativePath = path.relative(path.join(projectData.getAppResourcesDirectoryPath(), + const appResourcesRelativePath = path.relative(path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName), parsedFilePath); mappedFilePath = path.join(platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(projectData), appResourcesRelativePath); } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 2878788c4b..ae026b8f94 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -269,7 +269,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject shell.sed('-i', /__PROJECT_NAME__/, this.getProjectNameFromId(projectData), gradleSettingsFilePath); // will replace applicationId in app/App_Resources/Android/app.gradle if it has not been edited by the user - const userAppGradleFilePath = path.join(projectData.getAppResourcesDirectoryPath(), this.$devicePlatformsConstants.Android, "app.gradle"); + const userAppGradleFilePath = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, "app.gradle"); try { shell.sed('-i', /__PACKAGE__/, projectData.projectId, userAppGradleFilePath); @@ -391,7 +391,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } public ensureConfigurationFileInAppResources(projectData: IProjectData): void { - const originalAndroidManifestFilePath = path.join(projectData.getAppResourcesDirectoryPath(), this.$devicePlatformsConstants.Android, this.getPlatformData(projectData).configurationFileName); + const originalAndroidManifestFilePath = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, this.getPlatformData(projectData).configurationFileName); const manifestExists = this.$fs.exists(originalAndroidManifestFilePath); diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index 3be4826e50..71d2cde947 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -13,7 +13,7 @@ export class IOSEntitlementsService { private getDefaultAppEntitlementsPath(projectData: IProjectData) : string { const entitlementsName = IOSEntitlementsService.DefaultEntitlementsName; - const entitlementsPath = path.join(projectData.getAppResourcesDirectoryPath(), + const entitlementsPath = path.join(projectData.appResourcesDirectoryPath, this.$mobileHelper.normalizePlatformName(this.$devicePlatformsConstants.iOS), entitlementsName); return entitlementsPath; diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index c008222e85..803fad1d23 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -765,7 +765,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f private getInfoPlistPath(projectData: IProjectData): string { return path.join( - projectData.getAppResourcesDirectoryPath(), + projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName ); @@ -785,7 +785,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f private async mergeInfoPlists(buildOptions: IRelease, projectData: IProjectData): Promise { const projectDir = projectData.projectDir; - const infoPlistPath = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); + const infoPlistPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); this.ensureConfigurationFileInAppResources(); if (!this.$fs.exists(infoPlistPath)) { @@ -1215,7 +1215,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } } - const appResourcesXcconfigPath = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); + const appResourcesXcconfigPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); if (this.$fs.exists(appResourcesXcconfigPath)) { await this.mergeXcconfigFiles(appResourcesXcconfigPath, pluginsXcconfigFilePath); } @@ -1270,7 +1270,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private getBuildXCConfigFilePath(projectData: IProjectData): string { - const buildXCConfig = path.join(projectData.getAppResourcesDirectoryPath(), + const buildXCConfig = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); return buildXCConfig; } @@ -1332,7 +1332,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f const choicePersist = await this.$prompter.promptForChoice("Do you want to make teamId: " + teamId + " a persistent choice for your app?", choicesPersist); switch (choicesPersist.indexOf(choicePersist)) { case 0: - const xcconfigFile = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); + const xcconfigFile = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); this.$fs.appendFile(xcconfigFile, "\nDEVELOPMENT_TEAM = " + teamId + "\n"); break; case 1: @@ -1350,7 +1350,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private validateApplicationIdentifier(projectData: IProjectData): void { - const infoPlistPath = path.join(projectData.getAppResourcesDirectoryPath(), this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); + const infoPlistPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); const mergedPlistPath = this.getPlatformData(projectData).configurationFilePath; if (!this.$fs.exists(infoPlistPath) || !this.$fs.exists(mergedPlistPath)) { diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 66e83da9d7..4e7e01a615 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -537,7 +537,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } } - pattern.push(projectData.getAppResourcesDirectoryPath()); + pattern.push(projectData.appResourcesDirectoryPath); const currentWatcherInfo = this.liveSyncProcessesInfo[liveSyncData.projectDir].watcherInfo; const areWatcherPatternsDifferent = () => _.xor(currentWatcherInfo.patterns, patterns).length; diff --git a/lib/services/prepare-platform-js-service.ts b/lib/services/prepare-platform-js-service.ts index e7da7d3a79..0c4e5aca36 100644 --- a/lib/services/prepare-platform-js-service.ts +++ b/lib/services/prepare-platform-js-service.ts @@ -105,7 +105,7 @@ export class PreparePlatformJSService extends PreparePlatformService implements private copyAppResourcesFiles(config: IPreparePlatformJSInfo) { const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const appResourcesSourcePath = config.projectData.getAppResourcesDirectoryPath(); + const appResourcesSourcePath = config.projectData.appResourcesDirectoryPath; shell.cp("-Rf", appResourcesSourcePath, appDestinationDirectoryPath); } diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 20e224d3ee..74a8195d1a 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -60,9 +60,9 @@ export class ProjectChangesService implements IProjectChangesService { this._changesInfo = new ProjectChangesInfo(); if (!this.ensurePrepareInfo(platform, projectData, projectChangesOptions)) { this._newFiles = 0; - this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.getAppResourcesDirectoryPath(), projectData); + this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData); this._changesInfo.packageChanged = this.isProjectFileChanged(projectData, platform); - this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.getAppResourcesDirectoryPath(), null, projectData); + this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.appResourcesDirectoryPath, null, projectData); /*done because currently all node_modules are traversed, a possible improvement could be traversing only the production dependencies*/ this._changesInfo.nativeChanged = projectChangesOptions.skipModulesNativeCheck ? false : this.containsNewerFiles( path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME), @@ -73,7 +73,7 @@ export class ProjectChangesService implements IProjectChangesService { if (this._newFiles > 0 || this._changesInfo.nativeChanged) { this._changesInfo.modulesChanged = true; } - const platformResourcesDir = path.join(projectData.getAppResourcesDirectoryPath(), platformData.normalizedPlatformName); + const platformResourcesDir = path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName); if (platform === this.$devicePlatformsConstants.iOS.toLowerCase()) { this._changesInfo.configChanged = this.filesChanged([path.join(platformResourcesDir, platformData.configurationFileName), path.join(platformResourcesDir, "LaunchScreen.storyboard"), @@ -282,7 +282,7 @@ export class ProjectChangesService implements IProjectChangesService { return true; } const projectDir = projectData.projectDir; - if (_.startsWith(path.join(projectDir, file), projectData.getAppResourcesDirectoryPath())) { + if (_.startsWith(path.join(projectDir, file), projectData.appResourcesDirectoryPath)) { return true; } if (_.startsWith(file, NODE_MODULES_FOLDER_NAME)) { From 3eebb1eac6506667d5efc107e97e0e4f24407880 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Fri, 9 Feb 2018 16:05:34 +0200 Subject: [PATCH 3/9] chore(appresorces): fix tests --- test/platform-service.ts | 6 +++++- test/stubs.ts | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test/platform-service.ts b/test/platform-service.ts index 3db11dad85..6a8a56b7d4 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -401,6 +401,9 @@ describe('Platform Service Tests', () => { const appDestFolderPath = path.join(tempFolder, "appDest"); const appResourcesFolderPath = path.join(appDestFolderPath, "App_Resources"); + const appResourcesPath = path.join(appFolderPath, "App_Resources/Android"); + fs.createDirectory(appResourcesPath); + fs.writeFile(path.join(appResourcesPath, "test.txt"), "test"); fs.writeJson(path.join(tempFolder, "package.json"), { name: "testname", nativescript: { @@ -866,11 +869,12 @@ describe('Platform Service Tests', () => { projectRoot: testDirData.tempFolder, platformProjectService: { prepareProject: (): any => null, + prepareAppResources: (): any => null, validate: () => Promise.resolve(), createProject: (projectRoot: string, frameworkDir: string) => Promise.resolve(), interpolateData: (projectRoot: string) => Promise.resolve(), afterCreateProject: (projectRoot: string): any => null, - getAppResourcesDestinationDirectoryPath: () => "", + getAppResourcesDestinationDirectoryPath: () => testDirData.appResourcesFolderPath, processConfigurationFilesFromAppResources: () => Promise.resolve(), ensureConfigurationFileInAppResources: (): any => null, interpolateConfigurationFile: (): void => undefined, diff --git a/test/stubs.ts b/test/stubs.ts index 05bfab57fd..34e08263be 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -256,6 +256,9 @@ export class ProjectDataStub implements IProjectData { appDirectoryPath: string; devDependencies: IStringDictionary; projectType: string; + get appResourcesDirectoryPath(): string { + return this.getAppResourcesDirectoryPath(); + } public initializeProjectData(projectDir?: string): void { this.projectDir = this.projectDir || projectDir; } From 28f2f9556ae3fc23b4e621666b9af0388c6c9795 Mon Sep 17 00:00:00 2001 From: "Kristian D. Dimitrov" Date: Tue, 13 Feb 2018 17:16:32 +0200 Subject: [PATCH 4/9] chore(tests) fix xcconfig test --- test/ios-project-service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index dbdc6b3ba6..10b869a330 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -33,7 +33,7 @@ import { NodePackageManager } from "../lib/node-package-manager"; import { assert } from "chai"; import { IOSProvisionService } from "../lib/services/ios-provision-service"; import { SettingsService } from "../lib/common/test/unit-tests/stubs"; -import { ProjectDataStub } from "../test/stubs"; +import { ProjectDataStub } from "./stubs"; import temp = require("temp"); temp.track(); @@ -794,10 +794,11 @@ describe("Merge Project XCConfig files", () => { iOSProjectService = testInjector.resolve("iOSProjectService"); projectData = testInjector.resolve("projectData"); projectData.projectDir = projectPath; + projectData.appResourcesDirectoryPath = path.join(projectData.projectDir, "app", "App_Resources"); iOSEntitlementsService = testInjector.resolve("iOSEntitlementsService"); - appResourcesXcconfigPath = path.join(projectData.getAppResourcesDirectoryPath(), "iOS", "build.xcconfig"); + appResourcesXcconfigPath = path.join(projectData.appResourcesDirectoryPath, "iOS", "build.xcconfig"); appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html // DEVELOPMENT_TEAM = YOUR_TEAM_ID; From 1aba3c1ee9e48104332410dfb29e81901629c854 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Tue, 20 Feb 2018 10:04:24 +0200 Subject: [PATCH 5/9] fix ts compile error --- lib/services/livesync/livesync-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 4e7e01a615..716cbc94f9 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -537,7 +537,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } } - pattern.push(projectData.appResourcesDirectoryPath); + patterns.push(projectData.appResourcesDirectoryPath); const currentWatcherInfo = this.liveSyncProcessesInfo[liveSyncData.projectDir].watcherInfo; const areWatcherPatternsDifferent = () => _.xor(currentWatcherInfo.patterns, patterns).length; From 1122be66f230b068687840932cd5fea897a6273c Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Wed, 21 Feb 2018 18:36:25 +0200 Subject: [PATCH 6/9] refactor: address pr comments, replace app_resources usage for whatever the name of the app_resources directory basename is --- lib/constants.ts | 2 +- lib/project-data.ts | 9 +++++++- lib/services/app-files-updater.ts | 28 ++++++++++++++---------- lib/services/prepare-platform-service.ts | 2 +- test/app-files-updates.ts | 21 +++++++++++++----- test/ios-entitlements-service.ts | 2 +- test/stubs.ts | 17 ++------------ 7 files changed, 46 insertions(+), 35 deletions(-) diff --git a/lib/constants.ts b/lib/constants.ts index 64c60c6147..ef62235ba0 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -29,7 +29,7 @@ export const OUTPUTS_DIR = "outputs"; export const APK_DIR = "apk"; export const RESOURCES_DIR = "res"; export const CONFIG_NS_FILE_NAME = "nsconfig.json"; -export const CONFIG_NS_APP_RESOURCES_ENTRY = "app_resources"; +export const CONFIG_NS_APP_RESOURCES_ENTRY = "appResourcesPath"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/project-data.ts b/lib/project-data.ts index def80a92ad..ac22480443 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -93,6 +93,10 @@ export class ProjectData implements IProjectData { projectDir = this.projectDir; } + if (!projectDir) { + return null; + } + const configNSFilePath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME); let absoluteAppResourcesDirPath: string; @@ -103,10 +107,13 @@ export class ProjectData implements IProjectData { const appResourcesDirPath = configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]; absoluteAppResourcesDirPath = path.resolve(projectDir, appResourcesDirPath); + + return absoluteAppResourcesDirPath; } } - return absoluteAppResourcesDirPath || path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + // if no nsconfig is present default to app/App_Resources + return path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); } private getProjectType(): string { diff --git a/lib/services/app-files-updater.ts b/lib/services/app-files-updater.ts index 3d804647f9..dcdbb60873 100644 --- a/lib/services/app-files-updater.ts +++ b/lib/services/app-files-updater.ts @@ -11,9 +11,9 @@ export class AppFilesUpdater { ) { } - public updateApp(updateAppOptions: IUpdateAppOptions): void { + public updateApp(updateAppOptions: IUpdateAppOptions, projectData: IProjectData): void { this.cleanDestinationApp(updateAppOptions); - const sourceFiles = updateAppOptions.filesToSync || this.resolveAppSourceFiles(); + const sourceFiles = updateAppOptions.filesToSync || this.resolveAppSourceFiles(projectData); updateAppOptions.beforeCopyAction(sourceFiles); this.copyAppSourceFiles(sourceFiles); @@ -52,15 +52,19 @@ export class AppFilesUpdater { this.fs.deleteDirectory(path.join(this.appDestinationDirectoryPath, directoryItem)); } - protected readSourceDir(): string[] { + protected readSourceDir(projectData: IProjectData): string[] { const tnsDir = path.join(this.appSourceDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); - const defaultAppResourcesDir = path.join(this.appSourceDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); - return this.fs.enumerateFilesInDirectorySync(this.appSourceDirectoryPath, null, { includeEmptyDirectories: true }).filter(dirName => dirName !== tnsDir).filter(dirName => !dirName.startsWith(defaultAppResourcesDir)); + + return this.fs.enumerateFilesInDirectorySync(this.appSourceDirectoryPath, null, { includeEmptyDirectories: true }).filter(dirName => dirName !== tnsDir); } - protected resolveAppSourceFiles(): string[] { - // Copy all files from app dir, but make sure to exclude tns_modules and App_Resources - let sourceFiles = this.readSourceDir(); + protected resolveAppSourceFiles(projectData: IProjectData): string[] { + if (this.options.bundle) { + return []; + } + + // Copy all files from app dir, but make sure to exclude tns_modules and application resources + let sourceFiles = this.readSourceDir(projectData); if (this.options.release) { const testsFolderPath = path.join(this.appSourceDirectoryPath, 'tests'); @@ -72,9 +76,11 @@ export class AppFilesUpdater { constants.LIVESYNC_EXCLUDED_FILE_PATTERNS.forEach(pattern => sourceFiles = sourceFiles.filter(file => !minimatch(file, pattern, { nocase: true }))); } - if (this.options.bundle) { - sourceFiles = sourceFiles.filter(file => minimatch(file, "**/App_Resources/**", { nocase: true })); - } + // exclude the app_resources directory from being enumerated + // for copying if it is present in the application sources dir + const appResourcesPath = projectData.appResourcesDirectoryPath; + sourceFiles = sourceFiles.filter(dirName => !path.normalize(dirName).startsWith(path.normalize(appResourcesPath))); + return sourceFiles; } diff --git a/lib/services/prepare-platform-service.ts b/lib/services/prepare-platform-service.ts index 3b6de480a3..8f602a8164 100644 --- a/lib/services/prepare-platform-service.ts +++ b/lib/services/prepare-platform-service.ts @@ -30,6 +30,6 @@ export class PreparePlatformService { filesToSync: copyAppFilesData.filesToSync, filesToRemove: copyAppFilesData.filesToRemove }; - appUpdater.updateApp(appUpdaterOptions); + appUpdater.updateApp(appUpdaterOptions, copyAppFilesData.projectData); } } diff --git a/test/app-files-updates.ts b/test/app-files-updates.ts index 0be6ed7e72..1bf1090e73 100644 --- a/test/app-files-updates.ts +++ b/test/app-files-updates.ts @@ -1,8 +1,17 @@ import { assert } from "chai"; import { AppFilesUpdater } from "../lib/services/app-files-updater"; +import * as yok from "../lib/common/yok"; require("should"); +function createTestInjector(): IInjector { + const testInjector = new yok.Yok(); + + testInjector.register("projectData", { appResourcesDirectoryPath: "App_Resources"}); + + return testInjector; +} + describe("App files cleanup", () => { class CleanUpAppFilesUpdater extends AppFilesUpdater { public deletedDestinationItems: string[] = []; @@ -54,23 +63,25 @@ describe("App files copy", () => { } public copy(): void { - this.copiedDestinationItems = this.resolveAppSourceFiles(); + const injector = createTestInjector(); + const projectData = injector.resolve("projectData"); + this.copiedDestinationItems = this.resolveAppSourceFiles(projectData); } } - it("copies all app files when not bundling", () => { + it("copies all app files but app_resources when not bundling", () => { const updater = new CopyAppFilesUpdater([ "file1", "dir1/file2", "App_Resources/Android/blah.png" ], { bundle: false }); updater.copy(); - assert.deepEqual(["file1", "dir1/file2", "App_Resources/Android/blah.png"], updater.copiedDestinationItems); + assert.deepEqual(["file1", "dir1/file2"], updater.copiedDestinationItems); }); - it("skips copying non-App_Resource files when bundling", () => { + it("skips copying files when bundling", () => { const updater = new CopyAppFilesUpdater([ "file1", "dir1/file2", "App_Resources/Android/blah.png" ], { bundle: true }); updater.copy(); - assert.deepEqual(["App_Resources/Android/blah.png"], updater.copiedDestinationItems); + assert.deepEqual([], updater.copiedDestinationItems); }); }); diff --git a/test/ios-entitlements-service.ts b/test/ios-entitlements-service.ts index 181dddf67b..303db06a52 100644 --- a/test/ios-entitlements-service.ts +++ b/test/ios-entitlements-service.ts @@ -47,7 +47,7 @@ describe("IOSEntitlements Service Tests", () => { injector = createTestInjector(); platformsData = injector.resolve("platformsData"); - projectData = injector.resolve("projectData"); + projectData = $injector.resolve("projectData"); projectData.projectName = 'testApp'; projectData.platformsDir = temp.mkdirSync("platformsDir"); diff --git a/test/stubs.ts b/test/stubs.ts index 34e08263be..2add200898 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -4,7 +4,6 @@ import * as util from "util"; import * as chai from "chai"; import { EventEmitter } from "events"; -import * as fs from "fs"; import * as path from "path"; import * as constants from "./../lib/constants"; @@ -267,20 +266,8 @@ export class ProjectDataStub implements IProjectData { projectDir = this.projectDir; } - const configNSFilePath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME); - let absoluteAppResourcesDirPath: string; - - if (fs.existsSync(configNSFilePath)) { - const configNS = JSON.parse(fs.readFileSync(configNSFilePath).toString()); - - if (configNS && configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]) { - const appResourcesDirPath = configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]; - - absoluteAppResourcesDirPath = path.resolve(projectDir, appResourcesDirPath); - } - } - - return absoluteAppResourcesDirPath || path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + // always return app/App_Resources + return path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); } } From 487c80c00b44f28923c426fd53ed9b26f480b9d5 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Fri, 23 Feb 2018 11:51:36 +0200 Subject: [PATCH 7/9] feat(nsconfig): read app folder path from nsconfig --- lib/bootstrap.ts | 2 +- lib/commands/test-init.ts | 2 +- lib/common | 2 +- lib/constants.ts | 1 + lib/definitions/livesync.d.ts | 2 +- lib/definitions/project.d.ts | 5 + lib/project-data.ts | 136 +++++++++++++++------- lib/providers/project-files-provider.ts | 6 +- lib/services/livesync/livesync-service.ts | 10 +- lib/services/platform-service.ts | 3 +- lib/services/prepare-platform-service.ts | 3 +- lib/services/project-data-service.ts | 8 ++ lib/services/project-service.ts | 6 +- test/ios-entitlements-service.ts | 4 +- test/platform-service.ts | 5 +- test/project-data.ts | 7 +- test/project-files-provider.ts | 2 + test/stubs.ts | 21 +++- 18 files changed, 159 insertions(+), 66 deletions(-) diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 08dcd977b3..be6f999e1d 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -6,7 +6,7 @@ $injector.require("options", "./options"); $injector.require("nativescript-cli", "./nativescript-cli"); $injector.require("projectData", "./project-data"); -$injector.require("projectDataService", "./services/project-data-service"); +$injector.requirePublic("projectDataService", "./services/project-data-service"); $injector.requirePublic("projectService", "./services/project-service"); $injector.require("androidProjectService", "./services/android-project-service"); $injector.require("iOSEntitlementsService", "./services/ios-entitlements-service"); diff --git a/lib/commands/test-init.ts b/lib/commands/test-init.ts index 2cf77223a5..f80b2b0b06 100644 --- a/lib/commands/test-init.ts +++ b/lib/commands/test-init.ts @@ -68,7 +68,7 @@ class TestInitCommand implements ICommand { await this.$pluginsService.add('nativescript-unit-test-runner', this.$projectData); - const testsDir = path.join(projectDir, 'app/tests'); + const testsDir = path.join(this.$projectData.appDirectoryPath, 'tests'); let shouldCreateSampleTests = true; if (this.$fs.exists(testsDir)) { this.$logger.info('app/tests/ directory already exists, will not create an example test project.'); diff --git a/lib/common b/lib/common index 0963b9a3c8..5cf18a14df 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 0963b9a3c84cd637ab97c30b94ff06c6e10ff2e0 +Subproject commit 5cf18a14dfb8cb3eb46c75e272b68d39573f9503 diff --git a/lib/constants.ts b/lib/constants.ts index ef62235ba0..3b1f6a1d73 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -30,6 +30,7 @@ export const APK_DIR = "apk"; export const RESOURCES_DIR = "res"; export const CONFIG_NS_FILE_NAME = "nsconfig.json"; export const CONFIG_NS_APP_RESOURCES_ENTRY = "appResourcesPath"; +export const CONFIG_NS_APP_ENTRY = "appPath"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index 1fa0168408..28cb3fa370 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -214,7 +214,7 @@ interface IDebugLiveSyncService extends ILiveSyncService { * @param {ILiveSyncInfo} liveSyncData Information needed for livesync - for example if bundle is passed or if a release build should be performed. * @returns {Promise} The glob patterns. */ - getWatcherPatterns(liveSyncData: ILiveSyncInfo): Promise; + getWatcherPatterns(liveSyncData: ILiveSyncInfo, projectData: IProjectData): Promise; /** * Prints debug information. diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 863ada0176..9df42871dc 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -63,13 +63,18 @@ interface IProjectData extends IProjectDir { appDirectoryPath: string; appResourcesDirectoryPath: string; projectType: string; + nsConfig: any; /** * Initializes project data with the given project directory. If none supplied defaults to --path option or cwd. * @param {string} projectDir Project root directory. * @returns {void} */ initializeProjectData(projectDir?: string): void; + initializeProjectDataFromContent(packageJsonContent: string, nsconfigContent: string, projectDir?: string): void; + getAppDirectoryPath(projectDir?: string): string; + getAppDirectoryRelativePath(): string; getAppResourcesDirectoryPath(projectDir?: string): string; + getAppResourcesRelativeDirectoryPath(): string; } interface IProjectDataService { diff --git a/lib/project-data.ts b/lib/project-data.ts index ac22480443..c4e90069fd 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -1,5 +1,6 @@ import * as constants from "./constants"; import * as path from "path"; +import { parseJson } from "./common/helpers"; import { EOL } from "os"; interface IProjectType { @@ -32,10 +33,9 @@ export class ProjectData implements IProjectData { public projectFilePath: string; public projectId: string; public projectName: string; + public nsConfig: any; public appDirectoryPath: string; - get appResourcesDirectoryPath(): string { - return this.getAppResourcesDirectoryPath(); - } + public appResourcesDirectoryPath: string; public dependencies: any; public devDependencies: IStringDictionary; public projectType: string; @@ -49,38 +49,70 @@ export class ProjectData implements IProjectData { public initializeProjectData(projectDir?: string): void { projectDir = projectDir || this.$projectHelper.projectDir; + // If no project found, projectDir should be null if (projectDir) { - const projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); - let data: any = null; + const projectFilePath = this.getProjectFilePath(projectDir); if (this.$fs.exists(projectFilePath)) { - let fileContent: any = null; - try { - fileContent = this.$fs.readJson(projectFilePath); - data = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; - } catch (err) { - this.$errors.failWithoutHelp(`The project file ${this.projectFilePath} is corrupted. ${EOL}` + - `Consider restoring an earlier version from your source control or backup.${EOL}` + - `Additional technical info: ${err.toString()}`); - } - - if (data) { - this.projectDir = projectDir; - this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir)); - this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME); - this.projectFilePath = projectFilePath; - this.appDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME); - this.projectId = data.id; - this.dependencies = fileContent.dependencies; - this.devDependencies = fileContent.devDependencies; - this.projectType = this.getProjectType(); - - return; - } + let packageJsonContent: any = null; + packageJsonContent = this.$fs.readText(projectFilePath); + const nsConfigContent: any = this.getNsConfigContent(projectDir); + + this.initializeProjectDataFromContent(packageJsonContent, nsConfigContent, projectDir); } + + return; + } + + this.errorInvalidProject(projectDir); + } + + public initializeProjectDataFromContent(packageJsonContent: string, nsconfigContent: string, projectDir?: string): void { + projectDir = projectDir || this.$projectHelper.projectDir || ""; + const projectFilePath = this.getProjectFilePath(projectDir); + // If no project found, projectDir should be null + let nsData: any = null; + let nsConfig: any = null; + let packageJsonData: any = null; + + try { + packageJsonData = parseJson(packageJsonContent); + nsData = packageJsonData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; + } catch (err) { + this.$errors.failWithoutHelp(`The project file ${this.projectFilePath} is corrupted. ${EOL}` + + `Consider restoring an earlier version from your source control or backup.${EOL}` + + `Additional technical info: ${err.toString()}`); } + try { + nsConfig = nsconfigContent ? parseJson(nsconfigContent) : null; + } catch (err) { + this.$errors.failWithoutHelp(`The NativeScript configuration file ${constants.CONFIG_NS_FILE_NAME} is corrupted. ${EOL}` + + `Consider restoring an earlier version from your source control or backup.${EOL}` + + `Additional technical info: ${err.toString()}`); + } + + if (nsData) { + this.projectDir = projectDir; + this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir)); + this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME); + this.projectFilePath = projectFilePath; + this.projectId = nsData.id; + this.dependencies = packageJsonData.dependencies; + this.devDependencies = packageJsonData.devDependencies; + this.projectType = this.getProjectType(); + this.nsConfig = nsConfig; + this.appDirectoryPath = this.getAppDirectoryPath(); + this.appResourcesDirectoryPath = this.getAppResourcesDirectoryPath(); + + return; + } + + this.errorInvalidProject(projectDir); + } + + private errorInvalidProject(projectDir: string): void { const currentDir = path.resolve("."); this.$logger.trace(`Unable to find project. projectDir: ${projectDir}, options.path: ${this.$options.path}, ${currentDir}`); @@ -88,6 +120,10 @@ export class ProjectData implements IProjectData { this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", projectDir || this.$options.path || currentDir); } + private getProjectFilePath(projectDir: string): string { + return path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); + } + public getAppResourcesDirectoryPath(projectDir?: string): string { if (!projectDir) { projectDir = this.projectDir; @@ -97,23 +133,45 @@ export class ProjectData implements IProjectData { return null; } - const configNSFilePath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME); - let absoluteAppResourcesDirPath: string; + return path.resolve(projectDir, this.getAppResourcesRelativeDirectoryPath()); + } - if (this.$fs.exists(configNSFilePath)) { - const configNS = this.$fs.readJson(configNSFilePath); + public getAppResourcesRelativeDirectoryPath(): string { + if (this.nsConfig && this.nsConfig[constants.CONFIG_NS_APP_RESOURCES_ENTRY]) { + return this.nsConfig[constants.CONFIG_NS_APP_RESOURCES_ENTRY]; + } + + return path.join(this.getAppDirectoryRelativePath(), constants.APP_RESOURCES_FOLDER_NAME); + } - if (configNS && configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]) { - const appResourcesDirPath = configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]; + public getAppDirectoryPath(projectDir?: string): string { + if (!projectDir) { + projectDir = this.projectDir; + } - absoluteAppResourcesDirPath = path.resolve(projectDir, appResourcesDirPath); + if (!projectDir) { + return null; + } - return absoluteAppResourcesDirPath; - } + return path.resolve(projectDir, this.getAppDirectoryRelativePath()); + } + + public getAppDirectoryRelativePath(): string { + if (this.nsConfig && this.nsConfig[constants.CONFIG_NS_APP_ENTRY]) { + return this.nsConfig[constants.CONFIG_NS_APP_ENTRY]; + } + + return constants.APP_FOLDER_NAME; + } + + private getNsConfigContent(projectDir: string): string { + const configNSFilePath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME); + + if (!this.$fs.exists(configNSFilePath)) { + return null; } - // if no nsconfig is present default to app/App_Resources - return path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + return this.$fs.readText(configNSFilePath); } private getProjectType(): string { diff --git a/lib/providers/project-files-provider.ts b/lib/providers/project-files-provider.ts index 2885f904c1..7451b075ac 100644 --- a/lib/providers/project-files-provider.ts +++ b/lib/providers/project-files-provider.ts @@ -16,11 +16,13 @@ export class ProjectFilesProvider extends ProjectFilesProviderBase { const platformData = this.$platformsData.getPlatformData(platform.toLowerCase(), projectData); const parsedFilePath = this.getPreparedFilePath(filePath, projectFilesConfig); let mappedFilePath = ""; + let relativePath; if (parsedFilePath.indexOf(constants.NODE_MODULES_FOLDER_NAME) > -1) { - const relativePath = path.relative(path.join(projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME), parsedFilePath); + relativePath = path.relative(path.join(projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME), parsedFilePath); mappedFilePath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, constants.TNS_MODULES_FOLDER_NAME, relativePath); } else { - mappedFilePath = path.join(platformData.appDestinationDirectoryPath, path.relative(projectData.projectDir, parsedFilePath)); + relativePath = path.relative(projectData.appDirectoryPath, parsedFilePath); + mappedFilePath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, relativePath); } const appResourcesDirectoryPath = projectData.appResourcesDirectoryPath; diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 716cbc94f9..d5a6d95f1d 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -3,7 +3,7 @@ import * as choki from "chokidar"; import { EOL } from "os"; import { EventEmitter } from "events"; import { hook } from "../../common/helpers"; -import { APP_FOLDER_NAME, APP_RESOURCES_FOLDER_NAME, PACKAGE_JSON_FILE_NAME, LiveSyncTrackActionNames, USER_INTERACTION_NEEDED_EVENT_NAME, DEBUGGER_ATTACHED_EVENT_NAME, DEBUGGER_DETACHED_EVENT_NAME, TrackActionNames } from "../../constants"; +import { PACKAGE_JSON_FILE_NAME, LiveSyncTrackActionNames, USER_INTERACTION_NEEDED_EVENT_NAME, DEBUGGER_ATTACHED_EVENT_NAME, DEBUGGER_DETACHED_EVENT_NAME, TrackActionNames } from "../../constants"; import { DeviceTypes, DeviceDiscoveryEventNames } from "../../common/constants"; import { cache } from "../../common/decorators"; @@ -290,9 +290,9 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } @hook('watchPatterns') - public async getWatcherPatterns(liveSyncData: ILiveSyncInfo): Promise { + public async getWatcherPatterns(liveSyncData: ILiveSyncInfo, projectData: IProjectData): Promise { // liveSyncData is used by plugins that make use of the watchPatterns hook - return [APP_FOLDER_NAME, path.join(APP_FOLDER_NAME, APP_RESOURCES_FOLDER_NAME)]; + return [projectData.getAppDirectoryRelativePath(), projectData.getAppResourcesRelativeDirectoryPath()]; } public async disableDebuggingCore(deviceOption: IDisableDebuggingDeviceOptions, debuggingAdditionalOptions: IDebuggingAdditionalOptions): Promise { @@ -525,7 +525,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } private async startWatcher(projectData: IProjectData, liveSyncData: ILiveSyncInfo, platforms: string[]): Promise { - const patterns = await this.getWatcherPatterns(liveSyncData); + const patterns = await this.getWatcherPatterns(liveSyncData, projectData); if (liveSyncData.watchAllFiles) { const productionDependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir); @@ -537,8 +537,6 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } } - patterns.push(projectData.appResourcesDirectoryPath); - const currentWatcherInfo = this.liveSyncProcessesInfo[liveSyncData.projectDir].watcherInfo; const areWatcherPatternsDifferent = () => _.xor(currentWatcherInfo.patterns, patterns).length; if (!currentWatcherInfo || areWatcherPatternsDifferent()) { diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index d955403dd8..5fc4b78292 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -577,10 +577,9 @@ export class PlatformService extends EventEmitter implements IPlatformService { public async cleanDestinationApp(platformInfo: IPreparePlatformInfo): Promise { await this.ensurePlatformInstalled(platformInfo.platform, platformInfo.platformTemplate, platformInfo.projectData, platformInfo.config); - const appSourceDirectoryPath = path.join(platformInfo.projectData.projectDir, constants.APP_FOLDER_NAME); const platformData = this.$platformsData.getPlatformData(platformInfo.platform, platformInfo.projectData); const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, platformInfo.appFilesUpdaterOptions, this.$fs); + const appUpdater = new AppFilesUpdater(platformInfo.projectData.appDirectoryPath, appDestinationDirectoryPath, platformInfo.appFilesUpdaterOptions, this.$fs); appUpdater.cleanDestinationApp(); } diff --git a/lib/services/prepare-platform-service.ts b/lib/services/prepare-platform-service.ts index 8f602a8164..61a04ed249 100644 --- a/lib/services/prepare-platform-service.ts +++ b/lib/services/prepare-platform-service.ts @@ -20,9 +20,8 @@ export class PreparePlatformService { // Copy app folder to native project this.$fs.ensureDirectoryExists(appDestinationDirectoryPath); - const appSourceDirectoryPath = path.join(copyAppFilesData.projectData.projectDir, constants.APP_FOLDER_NAME); - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, copyAppFilesData.appFilesUpdaterOptions, this.$fs); + const appUpdater = new AppFilesUpdater(copyAppFilesData.projectData.appDirectoryPath, appDestinationDirectoryPath, copyAppFilesData.appFilesUpdaterOptions, this.$fs); const appUpdaterOptions: IUpdateAppOptions = { beforeCopyAction: sourceFiles => { this.$xmlValidator.validateXmlFiles(sourceFiles); diff --git a/lib/services/project-data-service.ts b/lib/services/project-data-service.ts index 46703f552d..a1bbdf1524 100644 --- a/lib/services/project-data-service.ts +++ b/lib/services/project-data-service.ts @@ -1,5 +1,6 @@ import * as path from "path"; import { ProjectData } from "../project-data"; +import { exported } from "../common/decorators"; interface IProjectFileData { projectData: any; @@ -41,6 +42,13 @@ export class ProjectDataService implements IProjectDataService { return projectDataInstance; } + @exported("projectDataService") + public getProjectDataFromContent(packageJsonContent: string, nsconfigContent: string, projectDir?: string): IProjectData { + const projectDataInstance = this.$injector.resolve(ProjectData); + projectDataInstance.initializeProjectDataFromContent(packageJsonContent, nsconfigContent, projectDir); + return projectDataInstance; + } + private getValue(projectDir: string, propertyName: string): any { const projectData = this.getProjectFileData(projectDir).projectData; diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 4e449cf3eb..ecabbc9b5e 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -101,7 +101,7 @@ export class ProjectService implements IProjectService { private async extractTemplate(projectDir: string, realTemplatePath: string): Promise { this.$fs.ensureDirectoryExists(projectDir); - const appDestinationPath = path.join(projectDir, constants.APP_FOLDER_NAME); + const appDestinationPath = this.$projectData.getAppDirectoryPath(projectDir); this.$fs.createDirectory(appDestinationPath); this.$logger.trace(`Copying application from '${realTemplatePath}' into '${appDestinationPath}'.`); @@ -111,7 +111,7 @@ export class ProjectService implements IProjectService { } private async ensureAppResourcesExist(projectDir: string): Promise { - const appPath = path.join(projectDir, constants.APP_FOLDER_NAME), + const appPath = this.$projectData.getAppDirectoryPath(projectDir), appResourcesDestinationPath = this.$projectData.getAppResourcesDirectoryPath(projectDir); if (!this.$fs.exists(appResourcesDestinationPath)) { @@ -138,7 +138,7 @@ export class ProjectService implements IProjectService { } private removeMergedDependencies(projectDir: string, templatePackageJsonData: any): void { - const extractedTemplatePackageJsonPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.PACKAGE_JSON_FILE_NAME); + const extractedTemplatePackageJsonPath = path.join(this.$projectData.getAppDirectoryPath(projectDir), constants.PACKAGE_JSON_FILE_NAME); for (const key in templatePackageJsonData) { if (constants.PackageJsonKeysToKeep.indexOf(key) === -1) { delete templatePackageJsonData[key]; diff --git a/test/ios-entitlements-service.ts b/test/ios-entitlements-service.ts index 303db06a52..da9c522039 100644 --- a/test/ios-entitlements-service.ts +++ b/test/ios-entitlements-service.ts @@ -47,11 +47,13 @@ describe("IOSEntitlements Service Tests", () => { injector = createTestInjector(); platformsData = injector.resolve("platformsData"); - projectData = $injector.resolve("projectData"); + projectData = injector.resolve("projectData"); projectData.projectName = 'testApp'; projectData.platformsDir = temp.mkdirSync("platformsDir"); projectData.projectDir = temp.mkdirSync("projectDir"); + projectData.appDirectoryPath = projectData.getAppDirectoryPath(); + projectData.appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath(); fs = injector.resolve("$fs"); diff --git a/test/platform-service.ts b/test/platform-service.ts index 6a8a56b7d4..1886838596 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -454,8 +454,9 @@ describe('Platform Service Tests', () => { const projectData = testInjector.resolve("projectData"); projectData.projectDir = testDirData.tempFolder; - projectData.appDirectoryPath = testDirData.appFolderPath; projectData.projectName = "app"; + projectData.appDirectoryPath = testDirData.appFolderPath; + projectData.appResourcesDirectoryPath = path.join(testDirData.appFolderPath, "App_Resources"); platformService = testInjector.resolve("platformService"); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: release }; @@ -887,6 +888,8 @@ describe('Platform Service Tests', () => { const projectData = testInjector.resolve("projectData"); projectData.projectDir = testDirData.tempFolder; + projectData.appDirectoryPath = projectData.getAppDirectoryPath(); + projectData.appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath(); platformService = testInjector.resolve("platformService"); const oldLoggerWarner = testInjector.resolve("$logger").warn; diff --git a/test/project-data.ts b/test/project-data.ts index 44209529d7..6789af8b4c 100644 --- a/test/project-data.ts +++ b/test/project-data.ts @@ -15,7 +15,8 @@ describe("projectData", () => { testInjector.register("fs", { exists: () => true, - readJson: (): any => null + readJson: (): any => null, + readText: (): any => null }); testInjector.register("staticConfig", { @@ -41,11 +42,11 @@ describe("projectData", () => { const fs = testInjector.resolve("fs"); fs.exists = (filePath: string) => filePath && path.basename(filePath) === "package.json"; - fs.readJson = () => ({ + fs.readText = () => (JSON.stringify({ nativescript: {}, dependencies: dependencies, devDependencies: devDependencies - }); + })); const projectHelper: IProjectHelper = testInjector.resolve("projectHelper"); projectHelper.projectDir = "projectDir"; diff --git a/test/project-files-provider.ts b/test/project-files-provider.ts index 47b1035a09..799d8141e7 100644 --- a/test/project-files-provider.ts +++ b/test/project-files-provider.ts @@ -43,6 +43,8 @@ describe("project-files-provider", () => { testInjector = createTestInjector(); projectData = testInjector.resolve("projectData"); projectData.projectDir = projectDir; + projectData.appDirectoryPath = projectData.getAppDirectoryPath(); + projectData.appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath(); projectFilesProvider = testInjector.resolve(ProjectFilesProvider); }); diff --git a/test/stubs.ts b/test/stubs.ts index 2add200898..2efe888ef8 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -252,15 +252,17 @@ export class ProjectDataStub implements IProjectData { projectFilePath: string; projectId: string; dependencies: any; + nsConfig: any; appDirectoryPath: string; devDependencies: IStringDictionary; projectType: string; - get appResourcesDirectoryPath(): string { - return this.getAppResourcesDirectoryPath(); - } + appResourcesDirectoryPath: string; public initializeProjectData(projectDir?: string): void { this.projectDir = this.projectDir || projectDir; } + public initializeProjectDataFromContent(): void { + return; + } public getAppResourcesDirectoryPath(projectDir?: string): string { if (!projectDir) { projectDir = this.projectDir; @@ -269,6 +271,19 @@ export class ProjectDataStub implements IProjectData { // always return app/App_Resources return path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); } + public getAppResourcesRelativeDirectoryPath(): string { + return ""; + } + public getAppDirectoryPath(projectDir?: string): string { + if (!projectDir) { + projectDir = this.projectDir; + } + + return path.join(projectDir, "app") || ""; + } + public getAppDirectoryRelativePath(): string { + return ""; + } } export class PlatformProjectServiceStub extends EventEmitter implements IPlatformProjectService { From 9c4498546146de9e9d8bbf9eefcafa49aeffef35 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Mon, 26 Feb 2018 16:34:44 +0200 Subject: [PATCH 8/9] chore(nsconfig): fix comments --- lib/definitions/project.d.ts | 7 +++- lib/project-data.ts | 45 ++++++++++----------- lib/services/app-files-updater.ts | 4 +- lib/services/prepare-platform-js-service.ts | 2 +- lib/services/project-data-service.ts | 1 + 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 9df42871dc..ce2e479701 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -53,6 +53,11 @@ interface IProjectService { isValidNativeScriptProject(pathToProject?: string): boolean; } +interface INsConfig { + appPath?: string; + appResourcesPath?:string; +} + interface IProjectData extends IProjectDir { projectName: string; platformsDir: string; @@ -63,7 +68,7 @@ interface IProjectData extends IProjectDir { appDirectoryPath: string; appResourcesDirectoryPath: string; projectType: string; - nsConfig: any; + nsConfig: INsConfig; /** * Initializes project data with the given project directory. If none supplied defaults to --path option or cwd. * @param {string} projectDir Project root directory. diff --git a/lib/project-data.ts b/lib/project-data.ts index c4e90069fd..250557cf04 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -55,9 +55,8 @@ export class ProjectData implements IProjectData { const projectFilePath = this.getProjectFilePath(projectDir); if (this.$fs.exists(projectFilePath)) { - let packageJsonContent: any = null; - packageJsonContent = this.$fs.readText(projectFilePath); - const nsConfigContent: any = this.getNsConfigContent(projectDir); + const packageJsonContent = this.$fs.readText(projectFilePath); + const nsConfigContent = this.getNsConfigContent(projectDir); this.initializeProjectDataFromContent(packageJsonContent, nsConfigContent, projectDir); } @@ -72,9 +71,9 @@ export class ProjectData implements IProjectData { projectDir = projectDir || this.$projectHelper.projectDir || ""; const projectFilePath = this.getProjectFilePath(projectDir); // If no project found, projectDir should be null - let nsData: any = null; - let nsConfig: any = null; - let packageJsonData: any = null; + let nsData = null; + let nsConfig: INsConfig = null; + let packageJsonData = null; try { packageJsonData = parseJson(packageJsonContent); @@ -86,7 +85,7 @@ export class ProjectData implements IProjectData { } try { - nsConfig = nsconfigContent ? parseJson(nsconfigContent) : null; + nsConfig = nsconfigContent ? parseJson(nsconfigContent) : null; } catch (err) { this.$errors.failWithoutHelp(`The NativeScript configuration file ${constants.CONFIG_NS_FILE_NAME} is corrupted. ${EOL}` + `Consider restoring an earlier version from your source control or backup.${EOL}` + @@ -125,15 +124,9 @@ export class ProjectData implements IProjectData { } public getAppResourcesDirectoryPath(projectDir?: string): string { - if (!projectDir) { - projectDir = this.projectDir; - } - - if (!projectDir) { - return null; - } + const appResourcesRelativePath = this.getAppResourcesRelativeDirectoryPath(); - return path.resolve(projectDir, this.getAppResourcesRelativeDirectoryPath()); + return this.resolveToProjectDir(appResourcesRelativePath, projectDir); } public getAppResourcesRelativeDirectoryPath(): string { @@ -145,15 +138,9 @@ export class ProjectData implements IProjectData { } public getAppDirectoryPath(projectDir?: string): string { - if (!projectDir) { - projectDir = this.projectDir; - } - - if (!projectDir) { - return null; - } + const appRelativePath = this.getAppDirectoryRelativePath(); - return path.resolve(projectDir, this.getAppDirectoryRelativePath()); + return this.resolveToProjectDir(appRelativePath, projectDir); } public getAppDirectoryRelativePath(): string { @@ -174,6 +161,18 @@ export class ProjectData implements IProjectData { return this.$fs.readText(configNSFilePath); } + private resolveToProjectDir(pathToResolve: string, projectDir?: string): string { + if (!projectDir) { + projectDir = this.projectDir; + } + + if (!projectDir) { + return null; + } + + return path.resolve(projectDir, pathToResolve); + } + private getProjectType(): string { let detectedProjectType = _.find(ProjectData.PROJECT_TYPES, (projectType) => projectType.isDefaultProjectType).type; diff --git a/lib/services/app-files-updater.ts b/lib/services/app-files-updater.ts index dcdbb60873..98a41b231b 100644 --- a/lib/services/app-files-updater.ts +++ b/lib/services/app-files-updater.ts @@ -78,8 +78,8 @@ export class AppFilesUpdater { // exclude the app_resources directory from being enumerated // for copying if it is present in the application sources dir - const appResourcesPath = projectData.appResourcesDirectoryPath; - sourceFiles = sourceFiles.filter(dirName => !path.normalize(dirName).startsWith(path.normalize(appResourcesPath))); + const appResourcesPathNormalized = path.normalize(projectData.appResourcesDirectoryPath); + sourceFiles = sourceFiles.filter(dirName => !path.normalize(dirName).startsWith(appResourcesPathNormalized)); return sourceFiles; } diff --git a/lib/services/prepare-platform-js-service.ts b/lib/services/prepare-platform-js-service.ts index 0c4e5aca36..dbd2963369 100644 --- a/lib/services/prepare-platform-js-service.ts +++ b/lib/services/prepare-platform-js-service.ts @@ -103,7 +103,7 @@ export class PreparePlatformJSService extends PreparePlatformService implements } } - private copyAppResourcesFiles(config: IPreparePlatformJSInfo) { + private copyAppResourcesFiles(config: IPreparePlatformJSInfo): void { const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); const appResourcesSourcePath = config.projectData.appResourcesDirectoryPath; diff --git a/lib/services/project-data-service.ts b/lib/services/project-data-service.ts index a1bbdf1524..a7098eeeba 100644 --- a/lib/services/project-data-service.ts +++ b/lib/services/project-data-service.ts @@ -36,6 +36,7 @@ export class ProjectDataService implements IProjectDataService { // TODO: Add tests // TODO: Remove $projectData and replace it with $projectDataService.getProjectData + @exported("projectDataService") public getProjectData(projectDir: string): IProjectData { const projectDataInstance = this.$injector.resolve(ProjectData); projectDataInstance.initializeProjectData(projectDir); From 2b5ef94d08d9fb78d7c2eab24aa761755c9d098b Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Tue, 27 Feb 2018 14:06:51 +0200 Subject: [PATCH 9/9] chore(public api): add projectDataService to public api documentation and tests --- PublicAPI.md | 78 ++++++++++++++++++++++++++++++++++++ test/nativescript-cli-lib.ts | 1 + 2 files changed, 79 insertions(+) diff --git a/PublicAPI.md b/PublicAPI.md index 10b6a2f488..eb1e84d569 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -11,6 +11,9 @@ const tns = require("nativescript"); * [projectService](#projectservice) * [createProject](#createproject) * [isValidNativeScriptProject](#isvalidnativescriptproject) +* [projectDataService](#projectdataservice) + * [getProjectData](#getprojectdata) + * [getProjectDataFromContent](#getprojectdatafromcontent) * [extensibilityService](#extensibilityservice) * [installExtension](#installextension) * [uninstallExtension](#uninstallextension) @@ -109,6 +112,81 @@ const isValidProject = tns.projectService.isValidNativeScriptProject("/tmp/myPro console.log(isValidProject); // true or false ``` +## projectDataService +`projectDataService` provides a way to get information about a NativeScript project. + +A common interface describing the results of a method is `IProjectData`: + +```TypeScript +interface IProjectData extends IProjectDir { + projectName: string; + platformsDir: string; + projectFilePath: string; + projectId?: string; + dependencies: any; + devDependencies: IStringDictionary; + appDirectoryPath: string; + appResourcesDirectoryPath: string; + projectType: string; + nsConfig: INsConfig; + /** + * Initializes project data with the given project directory. If none supplied defaults to cwd. + * @param {string} projectDir Project root directory. + * @returns {void} + */ + initializeProjectData(projectDir?: string): void; + /** + * Initializes project data with the given package.json, nsconfig.json content and project directory. If none supplied defaults to cwd. + * @param {string} packageJsonContent: string + * @param {string} nsconfigContent: string + * @param {string} projectDir Project root directory. + * @returns {void} + */ + initializeProjectDataFromContent(packageJsonContent: string, nsconfigContent: string, projectDir?: string): void; + getAppDirectoryPath(projectDir?: string): string; + getAppDirectoryRelativePath(): string; + getAppResourcesDirectoryPath(projectDir?: string): string; + getAppResourcesRelativeDirectoryPath(): string; +} + +interface IProjectDir { + projectDir: string; +} + +interface INsConfig { + appPath?: string; + appResourcesPath?:string; +} +``` + +### getProjectData +Returns an initialized IProjectData object containing data about the NativeScript project in the provided `projectDir`. + +* Definition: +```TypeScript +/** + * Returns an initialized IProjectData object containing data about the NativeScript project in the provided projectDir + * @param {string} projectDir The path to the project + * @returns {IProjectData} Information about the NativeScript project + */ +getProjectData(projectDir: string): IProjectData +``` + +### getProjectDataFromContent +Returns an IProjectData object that is initialized with the provided package.json content, nsconfig.json content and `projectDir`. + +* Definition: +```TypeScript +/** + * Returns an initialized IProjectData object containing data about the NativeScript project in the provided projectDir + * @param {string} packageJsonContent The content of the project.json file in the root of the project + * @param {string} nsconfigContent The content of the nsconfig.json file in the root of the project + * @param {string} projectDir The path to the project + * @returns {IProjectData} Information about the NativeScript project + */ +getProjectDataFromContent(packageJsonContent: string, nsconfigContent: string, projectDir?: string): IProjectData +``` + ## extensibilityService `extensibilityService` module gives access to methods for working with CLI's extensions - list, install, uninstall, load them. The extensions add new functionality to CLI, so once an extension is loaded, all methods added to it's public API are accessible directly through CLI when it is used as a library. Extensions may also add new commands, so they are accessible through command line when using NativeScript CLI. diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index fae558155d..3a34eeff09 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -16,6 +16,7 @@ describe("nativescript-cli-lib", () => { settingsService: ["setSettings"], deviceEmitter: null, projectService: ["createProject", "isValidNativeScriptProject"], + projectDataService: ["getProjectData", "getProjectDataFromContent"], localBuildService: ["build"], deviceLogProvider: null, npm: ["install", "uninstall", "view", "search"],