diff --git a/lib/commands/clean.ts b/lib/commands/clean.ts index 62a00bf8ce..d891ead677 100644 --- a/lib/commands/clean.ts +++ b/lib/commands/clean.ts @@ -6,6 +6,7 @@ import { IProjectCleanupResult, IProjectCleanupService, IProjectConfigService, + IProjectData, IProjectService, } from "../definitions/project"; @@ -83,6 +84,7 @@ export class CleanCommand implements ICommand { constructor( private $projectCleanupService: IProjectCleanupService, private $projectConfigService: IProjectConfigService, + private $projectData: IProjectData, private $terminalSpinnerService: ITerminalSpinnerService, private $projectService: IProjectService, private $prompter: IPrompter, @@ -108,7 +110,7 @@ export class CleanCommand implements ICommand { let pathsToClean = [ constants.HOOKS_DIR_NAME, - constants.PLATFORMS_DIR_NAME, + this.$projectData.getBuildRelativeDirectoryPath(), constants.NODE_MODULES_FOLDER_NAME, ]; diff --git a/lib/commands/typings.ts b/lib/commands/typings.ts index 9159e6f5f5..d2e296fa91 100644 --- a/lib/commands/typings.ts +++ b/lib/commands/typings.ts @@ -161,8 +161,7 @@ export class TypingsCommand implements ICommand { ); const dtsGeneratorPath = path.resolve( - this.$projectData.projectDir, - "platforms", + this.$projectData.platformsDir, "android", "build-tools", "dts-generator.jar", diff --git a/lib/constants.ts b/lib/constants.ts index 729d257ccf..7420893fab 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -67,6 +67,7 @@ export const BUNDLE_DIR = "bundle"; 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_BUILD_ENTRY = "buildPath"; export const CONFIG_NS_APP_ENTRY = "appPath"; export const CONFIG_FILE_NAME_DISPLAY = "nativescript.config.(js|ts)"; export const CONFIG_FILE_NAME_JS = "nativescript.config.js"; diff --git a/lib/contracts/project-data.ts b/lib/contracts/project-data.ts index aea166166e..976d8a0ec6 100644 --- a/lib/contracts/project-data.ts +++ b/lib/contracts/project-data.ts @@ -89,4 +89,6 @@ export abstract class ProjectData { abstract getAppResourcesDirectoryPath(projectDir?: string): string; abstract getAppResourcesRelativeDirectoryPath(): string; + + abstract getBuildRelativeDirectoryPath(): string; } diff --git a/lib/controllers/migrate-controller.ts b/lib/controllers/migrate-controller.ts index 3db61d6cd4..098e17f12b 100644 --- a/lib/controllers/migrate-controller.ts +++ b/lib/controllers/migrate-controller.ts @@ -722,7 +722,7 @@ export class MigrateController private async cleanUpProject(projectData: IProjectData): Promise { await this.$projectCleanupService.clean([ constants.HOOKS_DIR_NAME, - constants.PLATFORMS_DIR_NAME, + projectData.getBuildRelativeDirectoryPath(), constants.NODE_MODULES_FOLDER_NAME, constants.PACKAGE_LOCK_JSON_FILE_NAME, ]); diff --git a/lib/controllers/update-controller.ts b/lib/controllers/update-controller.ts index bcb4a5abf0..52034bc6b2 100644 --- a/lib/controllers/update-controller.ts +++ b/lib/controllers/update-controller.ts @@ -109,7 +109,7 @@ export class UpdateController // clean up project files this.spinner.info("Cleaning up project files before update"); - await this.cleanUpProject(); + await this.cleanUpProject(projectData); this.spinner.succeed("Project files have been cleaned up"); @@ -293,10 +293,10 @@ export class UpdateController } } - private async cleanUpProject(): Promise { + private async cleanUpProject(projectData: IProjectData): Promise { await this.$projectCleanupService.clean([ constants.HOOKS_DIR_NAME, - constants.PLATFORMS_DIR_NAME, + projectData.getBuildRelativeDirectoryPath(), constants.NODE_MODULES_FOLDER_NAME, constants.PACKAGE_LOCK_JSON_FILE_NAME, ]); diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 7ab75c5b0f..921e91657b 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -191,6 +191,11 @@ interface INsConfig { main?: string; appPath?: string; appResourcesPath?: string; + /** + * Where the native projects are generated, relative to the project root. + * Defaults to `platforms`. + */ + buildPath?: string; shared?: boolean; overridePods?: string; webpackConfigPath?: string; diff --git a/lib/project-data.ts b/lib/project-data.ts index 277dbf32d1..124a0eb74e 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -169,14 +169,18 @@ export class ProjectData implements IProjectData { nsConfig && nsConfig.projectName ? nsConfig.projectName : this.$projectHelper.sanitizeName(path.basename(projectDir)); - this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME); + // read before `platformsDir`, which is derived from it + this.nsConfig = nsConfig; + this.platformsDir = path.join( + projectDir, + this.getBuildRelativeDirectoryPath(), + ); this.projectFilePath = projectFilePath; this.projectIdentifiers = this.initializeProjectIdentifiers(nsConfig); this.packageJsonData = packageJsonData; this.dependencies = packageJsonData.dependencies; this.devDependencies = packageJsonData.devDependencies; this.projectType = this.getProjectType(); - this.nsConfig = nsConfig; this.ignoredDependencies = nsConfig?.ignoredNativeDependencies; this.appDirectoryPath = this.getAppDirectoryPath(); this.appResourcesDirectoryPath = this.getAppResourcesDirectoryPath(); @@ -276,6 +280,18 @@ export class ProjectData implements IProjectData { // ); } + /** + * Where the native projects are generated, relative to the project root. + * `buildPath` in the project config overrides the default `platforms`. + */ + public getBuildRelativeDirectoryPath(): string { + if (this.nsConfig && this.nsConfig[constants.CONFIG_NS_BUILD_ENTRY]) { + return this.nsConfig[constants.CONFIG_NS_BUILD_ENTRY]; + } + + return constants.PLATFORMS_DIR_NAME; + } + public getAppDirectoryPath(projectDir?: string): string { const appRelativePath = this.getAppDirectoryRelativePath(); diff --git a/test/controllers/update-controller.ts b/test/controllers/update-controller.ts index a99cf9802d..2037d970aa 100644 --- a/test/controllers/update-controller.ts +++ b/test/controllers/update-controller.ts @@ -20,6 +20,7 @@ function createTestInjector(projectDir: string = projectFolder): IInjector { initializeProjectData: () => { /* empty */ }, + getBuildRelativeDirectoryPath: () => "platforms", dependencies: { "@nativescript/core": "next", }, diff --git a/test/project-data.ts b/test/project-data.ts index 5b8c747bd1..8551eedbad 100644 --- a/test/project-data.ts +++ b/test/project-data.ts @@ -59,6 +59,7 @@ describe("projectData", () => { bundlerConfigPath?: string; projectName?: string; bundler?: string; + buildPath?: string; }; }): IProjectData => { const testInjector = createTestInjector(); @@ -96,6 +97,36 @@ describe("projectData", () => { return projectData; }; + describe("buildPath", () => { + it("defaults to the platforms directory", () => { + const projectData = prepareTest(); + + assert.deepStrictEqual( + projectData.getBuildRelativeDirectoryPath(), + "platforms", + ); + assert.deepStrictEqual( + projectData.platformsDir, + path.join(projectDir, "platforms"), + ); + }); + + it("is read from the project config", () => { + const projectData = prepareTest({ + configData: { buildPath: "build/native" }, + }); + + assert.deepStrictEqual( + projectData.getBuildRelativeDirectoryPath(), + "build/native", + ); + assert.deepStrictEqual( + projectData.platformsDir, + path.join(projectDir, "build/native"), + ); + }); + }); + describe("projectType", () => { const assertProjectType = ( dependencies: any, diff --git a/test/stubs.ts b/test/stubs.ts index 96bc5e2a33..7be77bc26a 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -731,6 +731,10 @@ export class ProjectDataStub implements IProjectData { public getAppDirectoryRelativePath(): string { return "app"; } + + public getBuildRelativeDirectoryPath(): string { + return constants.PLATFORMS_DIR_NAME; + } } export class AndroidPluginBuildServiceStub implements IAndroidPluginBuildService {