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/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 0525e4f425..3b1f6a1d73 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -28,6 +28,9 @@ 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 = "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 671e8e59d6..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,12 +68,18 @@ interface IProjectData extends IProjectDir { appDirectoryPath: string; appResourcesDirectoryPath: string; projectType: string; + 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. * @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 da438d88cc..250557cf04 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,6 +33,7 @@ export class ProjectData implements IProjectData { public projectFilePath: string; public projectId: string; public projectName: string; + public nsConfig: any; public appDirectoryPath: string; public appResourcesDirectoryPath: string; public dependencies: any; @@ -47,39 +49,69 @@ 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.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; - this.projectType = this.getProjectType(); - - return; - } + const packageJsonContent = this.$fs.readText(projectFilePath); + const nsConfigContent = 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 = null; + let nsConfig: INsConfig = null; + let packageJsonData = 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}`); @@ -87,6 +119,60 @@ 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 { + const appResourcesRelativePath = this.getAppResourcesRelativeDirectoryPath(); + + return this.resolveToProjectDir(appResourcesRelativePath, projectDir); + } + + 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); + } + + public getAppDirectoryPath(projectDir?: string): string { + const appRelativePath = this.getAppDirectoryRelativePath(); + + return this.resolveToProjectDir(appRelativePath, projectDir); + } + + 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; + } + + 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/providers/project-files-provider.ts b/lib/providers/project-files-provider.ts index 89917e1da0..7451b075ac 100644 --- a/lib/providers/project-files-provider.ts +++ b/lib/providers/project-files-provider.ts @@ -6,31 +6,33 @@ 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); 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 = path.join(constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + 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.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, + const appResourcesRelativePath = path.relative(path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName), parsedFilePath); mappedFilePath = path.join(platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(projectData), appResourcesRelativePath); } diff --git a/lib/services/app-files-updater.ts b/lib/services/app-files-updater.ts index 885da78f20..98a41b231b 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,14 +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); + 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 - 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'); @@ -71,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 appResourcesPathNormalized = path.normalize(projectData.appResourcesDirectoryPath); + sourceFiles = sourceFiles.filter(dirName => !path.normalize(dirName).startsWith(appResourcesPathNormalized)); + return sourceFiles; } diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index 0f749e31cf..71d2cde947 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.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 ac344eef83..803fad1d23 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.appResourcesDirectoryPath, 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.appResourcesDirectoryPath, 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.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); if (this.$fs.exists(appResourcesXcconfigPath)) { await this.mergeXcconfigFiles(appResourcesXcconfigPath, pluginsXcconfigFilePath); } @@ -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.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 4f113d2e9a..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); 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-js-service.ts b/lib/services/prepare-platform-js-service.ts index ad6fccc4be..dbd2963369 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): void { + const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const appResourcesSourcePath = config.projectData.appResourcesDirectoryPath; + + shell.cp("-Rf", appResourcesSourcePath, appDestinationDirectoryPath); + } } $injector.register("preparePlatformJSService", PreparePlatformJSService); diff --git a/lib/services/prepare-platform-service.ts b/lib/services/prepare-platform-service.ts index 3b6de480a3..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); @@ -30,6 +29,6 @@ export class PreparePlatformService { filesToSync: copyAppFilesData.filesToSync, filesToRemove: copyAppFilesData.filesToRemove }; - appUpdater.updateApp(appUpdaterOptions); + appUpdater.updateApp(appUpdaterOptions, copyAppFilesData.projectData); } } diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 9e2d1ea3c9..74a8195d1a 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -63,6 +63,7 @@ export class ProjectChangesService implements IProjectChangesService { this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData); this._changesInfo.packageChanged = this.isProjectFileChanged(projectData, platform); 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), path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME, "tns-ios-inspector"), diff --git a/lib/services/project-data-service.ts b/lib/services/project-data-service.ts index 46703f552d..a7098eeeba 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; @@ -35,12 +36,20 @@ 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); 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 fcb50d79ca..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,8 +111,8 @@ 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); + const appPath = this.$projectData.getAppDirectoryPath(projectDir), + appResourcesDestinationPath = this.$projectData.getAppResourcesDirectoryPath(projectDir); if (!this.$fs.exists(appResourcesDestinationPath)) { this.$fs.createDirectory(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/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 2edfb58ba1..da9c522039 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,11 +47,13 @@ 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"); projectData.projectDir = temp.mkdirSync("projectDir"); + projectData.appDirectoryPath = projectData.getAppDirectoryPath(); + projectData.appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath(); fs = injector.resolve("$fs"); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index dfccaaf237..10b869a330 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 "./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); @@ -793,11 +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.projectDir, constants.APP_FOLDER_NAME, - constants.APP_RESOURCES_FOLDER_NAME, "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; 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"], diff --git a/test/platform-service.ts b/test/platform-service.ts index aaa2876cbc..1886838596 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: { @@ -451,9 +454,9 @@ describe('Platform Service Tests', () => { const projectData = testInjector.resolve("projectData"); projectData.projectDir = testDirData.tempFolder; + projectData.projectName = "app"; projectData.appDirectoryPath = testDirData.appFolderPath; projectData.appResourcesDirectoryPath = path.join(testDirData.appFolderPath, "App_Resources"); - projectData.projectName = "app"; platformService = testInjector.resolve("platformService"); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: release }; @@ -867,11 +870,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, @@ -884,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 0edd3f5566..799d8141e7 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,14 @@ 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; + projectData.appDirectoryPath = projectData.getAppDirectoryPath(); + projectData.appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath(); projectFilesProvider = testInjector.resolve(ProjectFilesProvider); }); @@ -56,37 +60,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..2efe888ef8 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -4,6 +4,9 @@ import * as util from "util"; import * as chai from "chai"; import { EventEmitter } from "events"; +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 +247,43 @@ export class ProjectDataStub implements IProjectData { get platformsDir(): string { return ""; } + set platformsDir(value) { + } projectFilePath: string; projectId: string; dependencies: any; + nsConfig: any; appDirectoryPath: string; - appResourcesDirectoryPath: string; devDependencies: IStringDictionary; projectType: string; - initializeProjectData(projectDir?: string): void { + 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; + } + + // 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 {