Skip to content

feat: custom runtime package + info in package.json#6039

Open
farfromrefug wants to merge 1 commit into
NativeScript:mainfrom
Akylas:custom_runtime
Open

feat: custom runtime package + info in package.json#6039
farfromrefug wants to merge 1 commit into
NativeScript:mainfrom
Akylas:custom_runtime

Conversation

@farfromrefug
Copy link
Copy Markdown
Contributor

@farfromrefug farfromrefug commented Jun 2, 2026

As we discussed this PR adds custom android/ios runtime package support.

It also adds those informations + version inside the packaged package.json. This is useful if you want to show the info in your app.

Please test it

Summary by CodeRabbit

Release Notes

  • New Features
    • Added support for configurable platform-specific runtime packages. Users can now optionally specify custom runtime package names for iOS and Android in their NativeScript configuration, with automatic fallback to default packages when not specified.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR enables NativeScript projects to override the default scoped runtime package names per platform. Type definitions add optional runtimePackageName fields to iOS and Android configs; ProjectDataService resolves the configured runtime package names through dependency injection; PrepareController refactors runtime package.json generation to inject platform-specific runtime data; and AndroidPluginBuildService updates gradle and version queries to use the configurable package names.

Changes

Platform-scoped runtime configuration

Layer / File(s) Summary
Type definitions for platform-scoped runtime config
lib/definitions/project.d.ts
INsConfigIOS and INsConfigAndroid gain optional runtimePackageName fields to allow per-platform override of the runtime package name.
ProjectDataService runtime resolution with dependency injection
lib/services/project-data-service.ts
Constructor now accepts injected $projectData. Runtime resolution logic for installed packages and fallback defaults now derives the expected scoped runtime package name from nsConfig.<platform>.runtimePackageName, falling back to the scoped constants when not provided.
PrepareController runtime package.json generation
lib/controllers/prepare-controller.ts
Imports updated with new constants and resolvePackageJSONPath. writeRuntimePackageJson() is substantially refactored to resolve and read the installed runtime package.json, determine the target platform, inject runtime version data into ios or android blocks, map discardUncaughtJsExceptions to top-level, and remove the non-target platform block.
AndroidPluginBuildService gradle and version queries
lib/services/android-plugin-build-service.ts
getLatestRuntimeVersion(), getLocalGradleVersions(), and getGradleVersions() now derive the runtime package name from nsConfig.android.runtimePackageName and use it for all npm registry and installed package lookups instead of hardcoded constants.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A Runtime Bundled with Care
Platforms seek their package names true,
iOS and Android, each with their due—
Config speaks, and the service now hears,
A platform-scoped future: no more runtime fears!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding support for custom runtime packages and injecting runtime information into package.json.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
lib/services/android-plugin-build-service.ts (3)

575-634: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Using wrong project config for gradle version queries.

The method reads this.$projectData.nsConfig.android?.runtimePackageName (line 588) from the injected singleton. This should use the projectData parameter added in the previous fix.

🐛 Proposed fix (continuation of previous fix)

After applying the parameter changes from the previous segment:

 	private async getGradleVersions(
 		runtimeVersion: string,
+		projectData: IProjectData
 	): Promise<IRuntimeGradleVersions> {
 		let runtimeGradleVersions: {
 			versions: { gradle: string; gradleAndroid: string };
 		} = null;
 
-		const localVersionInfo = this.getLocalGradleVersions();
+		const localVersionInfo = this.getLocalGradleVersions(projectData);
 
 		if (localVersionInfo) {
 			return localVersionInfo;
 		}
 
-		const packageName = this.$projectData.nsConfig.android?.runtimePackageName || SCOPED_ANDROID_RUNTIME_NAME;
+		const packageName = projectData.nsConfig.android?.runtimePackageName || SCOPED_ANDROID_RUNTIME_NAME;
 		// fallback to reading from npm...

And update the other call site at line 478:

 		if (!runtimeGradleVersions) {
 			const latestRuntimeVersion = await this.getLatestRuntimeVersion(projectData);
 			runtimeGradleVersions = await this.getGradleVersions(
-				latestRuntimeVersion
+				latestRuntimeVersion,
+				projectData
 			);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/services/android-plugin-build-service.ts` around lines 575 - 634, In
getGradleVersions(...) replace references to the injected singleton
this.$projectData.nsConfig.android?.runtimePackageName with the function
parameter projectData.nsConfig.android?.runtimePackageName so packageName is
derived from the passed-in projectData (falling back to
SCOPED_ANDROID_RUNTIME_NAME as before); also update any other call-sites that
were changed to pass projectData so the method uses that parameter instead of
this.$projectData.

491-514: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Using wrong project config for runtime version lookup.

The method reads this.$projectData.nsConfig.android?.runtimePackageName from the injected singleton, but should use the config from the projectDir being processed. The caller getRuntimeGradleVersions() correctly retrieves projectData at line 459 but doesn't pass it to this method.

🐛 Proposed fix to accept and use correct project data
-	private async getLatestRuntimeVersion(): Promise<string> {
+	private async getLatestRuntimeVersion(projectData: IProjectData): Promise<string> {
 		let runtimeVersion: string = null;
-		const packageName = this.$projectData.nsConfig.android?.runtimePackageName || SCOPED_ANDROID_RUNTIME_NAME;
+		const packageName = projectData.nsConfig.android?.runtimePackageName || SCOPED_ANDROID_RUNTIME_NAME;
 		try {

Then update the call site at line 477:

 		if (!runtimeGradleVersions) {
-			const latestRuntimeVersion = await this.getLatestRuntimeVersion();
+			const latestRuntimeVersion = await this.getLatestRuntimeVersion(projectData);
 			runtimeGradleVersions = await this.getGradleVersions(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/services/android-plugin-build-service.ts` around lines 491 - 514, The
getLatestRuntimeVersion method uses the injected singleton this.$projectData
instead of the per-project data; change its signature (e.g.,
getLatestRuntimeVersion(projectData: IProjectData) or
getLatestRuntimeVersion(projectDir: string) and use the project-specific config
when resolving package name (use
projectData.nsConfig.android?.runtimePackageName ||
SCOPED_ANDROID_RUNTIME_NAME), and update the caller getRuntimeGradleVersions to
pass the projectData it already obtains; keep the existing try/catch lookup
logic but reference the passed-in projectData for the runtimePackageName lookup.

516-573: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Using wrong project config and directory for local gradle versions.

The method reads both this.$projectData.nsConfig.android?.runtimePackageName (line 532) and this.$projectData.projectDir (line 537) from the injected singleton. It should use the config and directory from the project being built.

🐛 Proposed fix to accept and use correct project data
-	private getLocalGradleVersions(): IRuntimeGradleVersions {
+	private getLocalGradleVersions(projectData: IProjectData): IRuntimeGradleVersions {
 		// partial interface of the runtime package.json
 		// including new 8.2+ format and legacy
 		interface IRuntimePackageJSON {
 			// 8.2+
 			version_info?: {
 				gradle: string;
 				gradleAndroid: string;
 			};
 			// legacy
 			gradle?: {
 				version: string;
 				android: string;
 			};
 		}
 
-		const packageName = this.$projectData.nsConfig.android?.runtimePackageName || SCOPED_ANDROID_RUNTIME_NAME;
+		const packageName = projectData.nsConfig.android?.runtimePackageName || SCOPED_ANDROID_RUNTIME_NAME;
 		// try reading from installed runtime first before reading from the npm registry...
 		const installedRuntimePackageJSONPath = resolvePackageJSONPath(
 			packageName,
 			{
-				paths: [this.$projectData.projectDir],
+				paths: [projectData.projectDir],
 			}
 		);

Then update the call site at line 582:

-		const localVersionInfo = this.getLocalGradleVersions();
+		const localVersionInfo = this.getLocalGradleVersions(projectData);

And pass projectData to getGradleVersions from line 466:

 			runtimeGradleVersions = await this.getGradleVersions(
-				projectRuntimeVersion
+				projectRuntimeVersion,
+				projectData
 			);

And update the method signature at line 575:

 	private async getGradleVersions(
-		runtimeVersion: string
+		runtimeVersion: string,
+		projectData: IProjectData
 	): Promise<IRuntimeGradleVersions> {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/services/android-plugin-build-service.ts` around lines 516 - 573, The
method getLocalGradleVersions is using this.$projectData
(this.$projectData.nsConfig.android?.runtimePackageName and
this.$projectData.projectDir) which reads the singleton instead of the project
being built; change getLocalGradleVersions to accept a ProjectData parameter
(e.g., projectData) and use projectData.nsConfig.android?.runtimePackageName and
projectData.projectDir when resolving and reading the runtime package.json, then
update any callers (notably the getGradleVersions call site) to pass the current
projectData through to getLocalGradleVersions and adjust the getGradleVersions
signature if it proxies this call so the correct project config/directory is
used for local gradle version resolution.
lib/services/project-data-service.ts (2)

687-706: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Using wrong project config in fallback return.

The fallback return values also read from this.$projectData.nsConfig instead of the config for the provided projectDir parameter. This is the same contract violation as the previous segment.

🐛 Proposed fix to read config from the correct project
 		// default to the scoped runtimes
 		this.$logger.trace(
 			"Could not find an installed runtime, falling back to default runtimes"
 		);
+		const projectData = this.getProjectData(projectDir);
 		if (platform === constants.PlatformTypes.ios) {
 			return {
-				name: this.$projectData.nsConfig.ios?.runtimePackageName || constants.SCOPED_IOS_RUNTIME_NAME,
+				name: projectData.nsConfig.ios?.runtimePackageName || constants.SCOPED_IOS_RUNTIME_NAME,
 				version: null,
 			};
 		} else if (platform === constants.PlatformTypes.android) {
 			return {
-				name: this.$projectData.nsConfig.android?.runtimePackageName || constants.SCOPED_ANDROID_RUNTIME_NAME,
+				name: projectData.nsConfig.android?.runtimePackageName || constants.SCOPED_ANDROID_RUNTIME_NAME,
 				version: null,
 			};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/services/project-data-service.ts` around lines 687 - 706, The fallback
branch is incorrectly reading nsConfig from this.$projectData instead of the
project-specific config for the provided projectDir; update the returns to use
the local projectData.nsConfig (the same projectData used earlier in this
function) rather than this.$projectData.nsConfig so iOS/Android
runtimePackageName is read from the correct project; keep the existing names
(SCOPED_IOS_RUNTIME_NAME, SCOPED_ANDROID_RUNTIME_NAME,
SCOPED_VISIONOS_RUNTIME_NAME) and null version unchanged.

624-646: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Using wrong project config for runtime package resolution.

The method accepts projectDir as a parameter but reads runtime package names from the injected this.$projectData.nsConfig singleton. When getRuntimePackage() is called with a projectDir different from the default project, the method will use the wrong configuration.

🐛 Proposed fix to read config from the correct project
 	private getInstalledRuntimePackage(
 		projectDir: string,
 		platform: constants.SupportedPlatform
 	): IBasePluginData {
+		const projectData = this.getProjectData(projectDir);
 		const runtimePackage = this.$pluginsService
 			.getDependenciesFromPackageJson(projectDir)
 			.devDependencies.find((d) => {
 				if (platform === constants.PlatformTypes.ios) {
-					const packageName = this.$projectData.nsConfig.ios?.runtimePackageName || constants.SCOPED_IOS_RUNTIME_NAME;
+					const packageName = projectData.nsConfig.ios?.runtimePackageName || constants.SCOPED_IOS_RUNTIME_NAME;
 					return [
 						packageName,
 						constants.TNS_IOS_RUNTIME_NAME,
 					].includes(d.name);
 				} else if (platform === constants.PlatformTypes.android) {
-					const packageName = this.$projectData.nsConfig.android?.runtimePackageName || constants.SCOPED_ANDROID_RUNTIME_NAME;
+					const packageName = projectData.nsConfig.android?.runtimePackageName || constants.SCOPED_ANDROID_RUNTIME_NAME;
 					return [
 						packageName,
 						constants.TNS_ANDROID_RUNTIME_NAME,
 					].includes(d.name);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/services/project-data-service.ts` around lines 624 - 646,
getInstalledRuntimePackage reads runtime package names from the global
this.$projectData.nsConfig which is wrong for arbitrary projectDir; load the
project-specific project data for the supplied projectDir (e.g. obtain a local
projectData variable by calling the service that returns project metadata for
projectDir) and replace all uses of this.$projectData.nsConfig with that
projectData.nsConfig so runtimePackageName resolution for ios/android/visionos
uses the correct config when called with a non-default projectDir.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@lib/services/android-plugin-build-service.ts`:
- Around line 575-634: In getGradleVersions(...) replace references to the
injected singleton this.$projectData.nsConfig.android?.runtimePackageName with
the function parameter projectData.nsConfig.android?.runtimePackageName so
packageName is derived from the passed-in projectData (falling back to
SCOPED_ANDROID_RUNTIME_NAME as before); also update any other call-sites that
were changed to pass projectData so the method uses that parameter instead of
this.$projectData.
- Around line 491-514: The getLatestRuntimeVersion method uses the injected
singleton this.$projectData instead of the per-project data; change its
signature (e.g., getLatestRuntimeVersion(projectData: IProjectData) or
getLatestRuntimeVersion(projectDir: string) and use the project-specific config
when resolving package name (use
projectData.nsConfig.android?.runtimePackageName ||
SCOPED_ANDROID_RUNTIME_NAME), and update the caller getRuntimeGradleVersions to
pass the projectData it already obtains; keep the existing try/catch lookup
logic but reference the passed-in projectData for the runtimePackageName lookup.
- Around line 516-573: The method getLocalGradleVersions is using
this.$projectData (this.$projectData.nsConfig.android?.runtimePackageName and
this.$projectData.projectDir) which reads the singleton instead of the project
being built; change getLocalGradleVersions to accept a ProjectData parameter
(e.g., projectData) and use projectData.nsConfig.android?.runtimePackageName and
projectData.projectDir when resolving and reading the runtime package.json, then
update any callers (notably the getGradleVersions call site) to pass the current
projectData through to getLocalGradleVersions and adjust the getGradleVersions
signature if it proxies this call so the correct project config/directory is
used for local gradle version resolution.

In `@lib/services/project-data-service.ts`:
- Around line 687-706: The fallback branch is incorrectly reading nsConfig from
this.$projectData instead of the project-specific config for the provided
projectDir; update the returns to use the local projectData.nsConfig (the same
projectData used earlier in this function) rather than
this.$projectData.nsConfig so iOS/Android runtimePackageName is read from the
correct project; keep the existing names (SCOPED_IOS_RUNTIME_NAME,
SCOPED_ANDROID_RUNTIME_NAME, SCOPED_VISIONOS_RUNTIME_NAME) and null version
unchanged.
- Around line 624-646: getInstalledRuntimePackage reads runtime package names
from the global this.$projectData.nsConfig which is wrong for arbitrary
projectDir; load the project-specific project data for the supplied projectDir
(e.g. obtain a local projectData variable by calling the service that returns
project metadata for projectDir) and replace all uses of
this.$projectData.nsConfig with that projectData.nsConfig so runtimePackageName
resolution for ios/android/visionos uses the correct config when called with a
non-default projectDir.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: da1377bb-8935-42da-bd83-74f1ea9e69b1

📥 Commits

Reviewing files that changed from the base of the PR and between d1930d3 and 194a783.

📒 Files selected for processing (4)
  • lib/controllers/prepare-controller.ts
  • lib/definitions/project.d.ts
  • lib/services/android-plugin-build-service.ts
  • lib/services/project-data-service.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant