Skip to content

Commit 8a19136

Browse files
farfromrefugclaude
andcommitted
feat(project): let the project config move the build directory
`platforms` was hardcoded as the directory the native projects are generated into. A project can now point it somewhere else with `buildPath` in `nativescript.config`: ```js export default { buildPath: "build/native", } satisfies NativeScriptConfig; ``` `ProjectData.platformsDir` is derived from it, so everything that already goes through `platformsDir` follows along. The places that reached for the `platforms` constant to talk about the project's own build directory - `ns clean`, `ns migrate`, `ns update` and `ns typings android` - now ask `getBuildRelativeDirectoryPath()` instead. The `platforms` folder inside a plugin's own package is unrelated and untouched. Defaults to `platforms`, so nothing changes for a project that does not set it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 2f9f2e0 commit 8a19136

11 files changed

Lines changed: 70 additions & 9 deletions

File tree

lib/commands/clean.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
IProjectCleanupResult,
77
IProjectCleanupService,
88
IProjectConfigService,
9+
IProjectData,
910
IProjectService,
1011
} from "../definitions/project";
1112

@@ -83,6 +84,7 @@ export class CleanCommand implements ICommand {
8384
constructor(
8485
private $projectCleanupService: IProjectCleanupService,
8586
private $projectConfigService: IProjectConfigService,
87+
private $projectData: IProjectData,
8688
private $terminalSpinnerService: ITerminalSpinnerService,
8789
private $projectService: IProjectService,
8890
private $prompter: IPrompter,
@@ -108,7 +110,7 @@ export class CleanCommand implements ICommand {
108110

109111
let pathsToClean = [
110112
constants.HOOKS_DIR_NAME,
111-
constants.PLATFORMS_DIR_NAME,
113+
this.$projectData.getBuildRelativeDirectoryPath(),
112114
constants.NODE_MODULES_FOLDER_NAME,
113115
];
114116

lib/commands/typings.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,7 @@ export class TypingsCommand implements ICommand {
161161
);
162162

163163
const dtsGeneratorPath = path.resolve(
164-
this.$projectData.projectDir,
165-
"platforms",
164+
this.$projectData.platformsDir,
166165
"android",
167166
"build-tools",
168167
"dts-generator.jar",

lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export const BUNDLE_DIR = "bundle";
6767
export const RESOURCES_DIR = "res";
6868
export const CONFIG_NS_FILE_NAME = "nsconfig.json";
6969
export const CONFIG_NS_APP_RESOURCES_ENTRY = "appResourcesPath";
70+
export const CONFIG_NS_BUILD_ENTRY = "buildPath";
7071
export const CONFIG_NS_APP_ENTRY = "appPath";
7172
export const CONFIG_FILE_NAME_DISPLAY = "nativescript.config.(js|ts)";
7273
export const CONFIG_FILE_NAME_JS = "nativescript.config.js";

lib/contracts/project-data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,6 @@ export abstract class ProjectData {
8989
abstract getAppResourcesDirectoryPath(projectDir?: string): string;
9090

9191
abstract getAppResourcesRelativeDirectoryPath(): string;
92+
93+
abstract getBuildRelativeDirectoryPath(): string;
9294
}

lib/controllers/migrate-controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ export class MigrateController
722722
private async cleanUpProject(projectData: IProjectData): Promise<void> {
723723
await this.$projectCleanupService.clean([
724724
constants.HOOKS_DIR_NAME,
725-
constants.PLATFORMS_DIR_NAME,
725+
projectData.getBuildRelativeDirectoryPath(),
726726
constants.NODE_MODULES_FOLDER_NAME,
727727
constants.PACKAGE_LOCK_JSON_FILE_NAME,
728728
]);

lib/controllers/update-controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export class UpdateController
109109
// clean up project files
110110
this.spinner.info("Cleaning up project files before update");
111111

112-
await this.cleanUpProject();
112+
await this.cleanUpProject(projectData);
113113

114114
this.spinner.succeed("Project files have been cleaned up");
115115

@@ -293,10 +293,10 @@ export class UpdateController
293293
}
294294
}
295295

296-
private async cleanUpProject(): Promise<void> {
296+
private async cleanUpProject(projectData: IProjectData): Promise<void> {
297297
await this.$projectCleanupService.clean([
298298
constants.HOOKS_DIR_NAME,
299-
constants.PLATFORMS_DIR_NAME,
299+
projectData.getBuildRelativeDirectoryPath(),
300300
constants.NODE_MODULES_FOLDER_NAME,
301301
constants.PACKAGE_LOCK_JSON_FILE_NAME,
302302
]);

lib/definitions/project.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ interface INsConfig {
191191
main?: string;
192192
appPath?: string;
193193
appResourcesPath?: string;
194+
/**
195+
* Where the native projects are generated, relative to the project root.
196+
* Defaults to `platforms`.
197+
*/
198+
buildPath?: string;
194199
shared?: boolean;
195200
overridePods?: string;
196201
webpackConfigPath?: string;

lib/project-data.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,18 @@ export class ProjectData implements IProjectData {
169169
nsConfig && nsConfig.projectName
170170
? nsConfig.projectName
171171
: this.$projectHelper.sanitizeName(path.basename(projectDir));
172-
this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME);
172+
// read before `platformsDir`, which is derived from it
173+
this.nsConfig = nsConfig;
174+
this.platformsDir = path.join(
175+
projectDir,
176+
this.getBuildRelativeDirectoryPath(),
177+
);
173178
this.projectFilePath = projectFilePath;
174179
this.projectIdentifiers = this.initializeProjectIdentifiers(nsConfig);
175180
this.packageJsonData = packageJsonData;
176181
this.dependencies = packageJsonData.dependencies;
177182
this.devDependencies = packageJsonData.devDependencies;
178183
this.projectType = this.getProjectType();
179-
this.nsConfig = nsConfig;
180184
this.ignoredDependencies = nsConfig?.ignoredNativeDependencies;
181185
this.appDirectoryPath = this.getAppDirectoryPath();
182186
this.appResourcesDirectoryPath = this.getAppResourcesDirectoryPath();
@@ -276,6 +280,18 @@ export class ProjectData implements IProjectData {
276280
// );
277281
}
278282

283+
/**
284+
* Where the native projects are generated, relative to the project root.
285+
* `buildPath` in the project config overrides the default `platforms`.
286+
*/
287+
public getBuildRelativeDirectoryPath(): string {
288+
if (this.nsConfig && this.nsConfig[constants.CONFIG_NS_BUILD_ENTRY]) {
289+
return this.nsConfig[constants.CONFIG_NS_BUILD_ENTRY];
290+
}
291+
292+
return constants.PLATFORMS_DIR_NAME;
293+
}
294+
279295
public getAppDirectoryPath(projectDir?: string): string {
280296
const appRelativePath = this.getAppDirectoryRelativePath();
281297

test/controllers/update-controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ function createTestInjector(projectDir: string = projectFolder): IInjector {
2020
initializeProjectData: () => {
2121
/* empty */
2222
},
23+
getBuildRelativeDirectoryPath: () => "platforms",
2324
dependencies: {
2425
"@nativescript/core": "next",
2526
},

test/project-data.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ describe("projectData", () => {
5959
bundlerConfigPath?: string;
6060
projectName?: string;
6161
bundler?: string;
62+
buildPath?: string;
6263
};
6364
}): IProjectData => {
6465
const testInjector = createTestInjector();
@@ -96,6 +97,36 @@ describe("projectData", () => {
9697
return projectData;
9798
};
9899

100+
describe("buildPath", () => {
101+
it("defaults to the platforms directory", () => {
102+
const projectData = prepareTest();
103+
104+
assert.deepStrictEqual(
105+
projectData.getBuildRelativeDirectoryPath(),
106+
"platforms",
107+
);
108+
assert.deepStrictEqual(
109+
projectData.platformsDir,
110+
path.join(projectDir, "platforms"),
111+
);
112+
});
113+
114+
it("is read from the project config", () => {
115+
const projectData = prepareTest({
116+
configData: { buildPath: "build/native" },
117+
});
118+
119+
assert.deepStrictEqual(
120+
projectData.getBuildRelativeDirectoryPath(),
121+
"build/native",
122+
);
123+
assert.deepStrictEqual(
124+
projectData.platformsDir,
125+
path.join(projectDir, "build/native"),
126+
);
127+
});
128+
});
129+
99130
describe("projectType", () => {
100131
const assertProjectType = (
101132
dependencies: any,

0 commit comments

Comments
 (0)