Skip to content

fix: gracefully fail when node module can't be found#6037

Merged
NathanWalker merged 2 commits into
mainfrom
fix/gracefully-fail-node-modules
May 29, 2026
Merged

fix: gracefully fail when node module can't be found#6037
NathanWalker merged 2 commits into
mainfrom
fix/gracefully-fail-node-modules

Conversation

@edusperoni
Copy link
Copy Markdown
Collaborator

@edusperoni edusperoni commented May 28, 2026

This solves the annoying error

The "path" argument must be of type string or an instance of Buffer or URL. Received undefined

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of unresolved plugin module lookups: now logs a warning and excludes unresolved entries so installed plugin lists no longer contain null/invalid items.
  • Refactor

    • Internal service implementation cleaned up for clearer error messaging and validation flow. No breaking changes to public APIs.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7f402f66-0bec-454c-a190-043d7d49e6d9

📥 Commits

Reviewing files that changed from the base of the PR and between be805e4 and 7f99acb.

📒 Files selected for processing (1)
  • lib/services/plugins-service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/services/plugins-service.ts

📝 Walkthrough

Walkthrough

Resolve module package.json paths in PluginsService; if resolution fails, warn and return null from getNodeModuleData. getAllInstalledModules now filters out null entries so unresolved modules are excluded from the installed plugins list.

Changes

PluginsService Refactoring

Layer / File(s) Summary
Resolve package.json and filter unresolved modules
lib/services/plugins-service.ts
getNodeModuleData() now calls getPackageJsonFilePathForModule to get a module's package.json path and returns null (with a logged warning) when resolution fails. getAllInstalledModules() filters mapped results with .filter(Boolean) to remove null entries.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A rabbit sniffs the node_modules trail,
If package.json's missing, it leaves a pale tail,
A warning hops out, the nils are gone,
Installed list stays tidy at dawn. 🐇✨

🚥 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 directly describes the main change: gracefully handling failures when a Node module cannot be found, which matches the core fix in the changeset.
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.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@lib/services/plugins-service.ts`:
- Around line 722-737: getNodeModuleData and convertToPluginData can return null
but are typed non-nullable and their callers (notably remove) call
platformProjectService.removePluginNativeCode with that result, which can crash
(e.g., IOSProjectService.removePluginNativeCode dereferences pluginData). Update
getNodeModuleData and convertToPluginData types to allow null/undefined
(INodeModuleData | null, IPluginData | null), and add null guards where their
values are used: in remove(), check the result of
convertToPluginData(getNodeModuleData(...)) before calling
platformProjectService.removePluginNativeCode and skip/log when null; also
update other call sites (like the installed modules mapping) to handle nulls
appropriately (filter/skip or log) so no null is passed into
removePluginNativeCode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 248d5abe-977f-4ec3-853a-82d1dc4a82af

📥 Commits

Reviewing files that changed from the base of the PR and between d88337c and be805e4.

📒 Files selected for processing (1)
  • lib/services/plugins-service.ts

Comment on lines 722 to +737
private getNodeModuleData(
module: string,
projectDir: string
projectDir: string,
): INodeModuleData {
// module can be modulePath or moduleName
if (!this.$fs.exists(module) || path.basename(module) !== "package.json") {
module = this.getPackageJsonFilePathForModule(module, projectDir);
const resolvedPath = this.getPackageJsonFilePathForModule(
module,
projectDir,
);
if (!resolvedPath) {
this.$logger.warn(
`Could not find module ${color.yellow(module)}. It may have been removed or is not installed. Skipping.`,
);
return null;
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify TypeScript nullability settings and all getNodeModuleData call sites.

set -euo pipefail

echo "== tsconfig strict/nullability settings =="
fd -i 'tsconfig*.json' | while read -r f; do
  echo "--- $f"
  rg -n '"strict"\s*:|"strictNullChecks"\s*:' "$f" || true
done

echo
echo "== getNodeModuleData definition and call sites =="
rg -nP --type=ts -C3 '\bgetNodeModuleData\s*\(' lib/services/plugins-service.ts

echo
echo "== remove flow around removePluginNativeCode =="
rg -nP --type=ts -C5 '\bremovePluginNativeCode\s*\(' lib/services/plugins-service.ts

Repository: NativeScript/nativescript-cli

Length of output: 1277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="lib/services/plugins-service.ts"

echo "== Snippet: getNodeModuleData implementation (around 700-780) =="
sed -n '700,820p' "$file" | nl -ba | sed -n '1,160p'

echo
echo "== Find INodeModuleData type/interface definition =="
rg -n --type=ts '\binterface\s+INodeModuleData\b|\btype\s+INodeModuleData\b' "$file" -S || true

echo
echo "== Search for INodeModuleData usages in the file (limit output) =="
rg -n --type=ts '\bINodeModuleData\b' "$file" | head -n 50

echo
echo "== convertToPluginData signature + return type (and null handling) =="
rg -n --type=ts '\bconvertToPluginData\s*\(' "$file" -n
# show small window around first hit
line=$(rg -n --type=ts '\bconvertToPluginData\s*\(' "$file" | head -n1 | cut -d: -f1)
start=$((line-20))
end=$((line+80))
sed -n "${start},${end}p" "$file" | nl -ba

echo
echo "== removePluginNativeCode call site context (around 120-190) =="
sed -n '120,200p' "$file" | nl -ba

Repository: NativeScript/nativescript-cli

Length of output: 199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="lib/services/plugins-service.ts"

echo "== getNodeModuleData implementation (lines 700-820) =="
sed -n '700,820p' "$file" | cat -n

echo
echo "== Lines in this file that contain getNodeModuleData + return null (limit output) =="
rg -n --type=ts 'getNodeModuleData|return null' "$file" | head -n 200

echo
echo "== convertToPluginData definition + return type/body (limit output) =="
# Find definition line for convertToPluginData, then show a window
def_line=$(rg -n --type=ts 'convertToPluginData\s*\(' "$file" | head -n1 | cut -d: -f1)
start=$((def_line-20))
end=$((def_line+120))
sed -n "${start},${end}p" "$file" | cat -n

echo
echo "== removePluginNativeCode call site context (lines 130-210) =="
sed -n '130,210p' "$file" | cat -n

echo
echo "== Locate INodeModuleData definition/import in repo (not just this file) =="
rg -n --type=ts '\binterface\s+INodeModuleData\b|\btype\s+INodeModuleData\b' . | head -n 50

echo
echo "== Locate convertToPluginData call sites inside this file =="
rg -n --type=ts 'convertToPluginData\s*\(' "$file"

Repository: NativeScript/nativescript-cli

Length of output: 12916


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect INodeModuleData definition
defs="lib/definitions/plugins.d.ts"
echo "== INodeModuleData definition in $defs =="
sed -n '1,120p' "$defs" | cat -n

echo
echo "== convertToPluginData definition around line ~622 =="
file="lib/services/plugins-service.ts"
sed -n '580,720p' "$file" | cat -n

echo
echo "== getNodeModuleData return null path context (around ~680-710) =="
sed -n '700,760p' "$file" | cat -n

echo
echo "== Find platformProjectService.removePluginNativeCode signature/type =="
# Locate definition(s) and then show nearby lines
rg -n --type=ts 'removePluginNativeCode\s*\(' . | head -n 50

echo
echo "== Show likely type/interface for removePluginNativeCode (best-effort: matching file names) =="
# try to open the first couple hits with some context
hits=$(rg -n --type=ts 'removePluginNativeCode\s*\(' . | head -n 5 | cut -d: -f1 | sort -u)
for f in $hits; do
  echo "--- $f"
  sed -n '1,200p' "$f" | head -n 200 | cat -n
done

Repository: NativeScript/nativescript-cli

Length of output: 48646


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tsconfig strict/nullability settings values =="
for f in tsconfig.json packages/doctor/tsconfig.json packages/nativescript-envinfo/tsconfig.json; do
  if [ -f "$f" ]; then
    echo "--- $f"
    rg -n '"strictNullChecks"\s*:\s*(true|false)|"strict"\s*:\s*' "$f" || true
    # also show "compilerOptions" block header lines around strict settings
    rg -n '"compilerOptions"\s*:\s*{' -n "$f" || true
  fi
done

echo
echo "== removePluginNativeCode implementation (android) around declaration =="
rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/android-project-service.ts
lineA=$(rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/android-project-service.ts | head -n1 | cut -d: -f1)
sed -n "$((lineA-40)),$((lineA+120))p" lib/services/android-project-service.ts | cat -n

echo
echo "== removePluginNativeCode implementation (ios) around declaration =="
rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/ios-project-service.ts
lineI=$(rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/ios-project-service.ts | head -n1 | cut -d: -f1)
sed -n "$((lineI-40)),$((lineI+180))p" lib/services/ios-project-service.ts | cat -n

echo
echo "== Other convertToPluginData call sites (context) =="
file="lib/services/plugins-service.ts"
rg -n --type=ts 'convertToPluginData\s*\(' "$file" | while read -r l; do echo "$l"; done
# show contexts around each occurrence (first 6)
for line in $(rg -n --type=ts 'convertToPluginData\s*\(' "$file" | cut -d: -f1 | head -n 6); do
  echo "--- context around $line"
  sed -n "$((line-15)),$((line+35))p" "$file" | cat -n
done

Repository: NativeScript/nativescript-cli

Length of output: 24048


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tsconfig strict/nullability settings values =="
for f in tsconfig.json packages/doctor/tsconfig.json packages/nativescript-envinfo/tsconfig.json; do
  if [ -f "$f" ]; then
    echo "--- $f"
    rg -n '"strictNullChecks"\s*:\s*(true|false)|"strict"\s*:\s*' "$f" || true
  fi
done

echo
echo "== removePluginNativeCode implementation (android) around declaration =="
rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/android-project-service.ts
lineA=$(rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/android-project-service.ts | head -n1 | cut -d: -f1)
sed -n "$((lineA-40)),$((lineA+140))p" lib/services/android-project-service.ts | cat -n

echo
echo "== removePluginNativeCode implementation (ios) around declaration =="
rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/ios-project-service.ts
lineI=$(rg -n --type=ts 'removePluginNativeCode\s*\(' lib/services/ios-project-service.ts | head -n1 | cut -d: -f1)
sed -n "$((lineI-40)),$((lineI+200))p" lib/services/ios-project-service.ts | cat -n

echo
echo "== Other convertToPluginData call sites in plugins-service.ts (context) =="
file="lib/services/plugins-service.ts"
lines=$(rg -n --type=ts 'convertToPluginData\s*\(' "$file" | cut -d: -f1)
for line in $(echo "$lines" | head -n 6); do
  echo "--- context around $line"
  sed -n "$((line-15)),$((line+40))p" "$file" | cat -n
done

Repository: NativeScript/nativescript-cli

Length of output: 25683


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in tsconfig.json packages/doctor/tsconfig.json packages/nativescript-envinfo/tsconfig.json; do
  echo "== $f =="
  if [ -f "$f" ]; then
    rg -n 'strictNullChecks|"\s*strict"\s*:\s*(true|false)|noImplicitAny' "$f" || true
    # show the surrounding compilerOptions block header (if present)
    rg -n '"compilerOptions"\s*:' "$f" || true
  fi
  echo
done

Repository: NativeScript/nativescript-cli

Length of output: 281


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in tsconfig.json packages/doctor/tsconfig.json packages/nativescript-envinfo/tsconfig.json; do
  echo "== $f (compilerOptions block) =="
  # Print first ~120 lines with line numbers for locating strictNullChecks quickly (these files are usually small)
  sed -n '1,160p' "$f" | cat -n
  echo
done

Repository: NativeScript/nativescript-cli

Length of output: 1038


Fix nullability contract for getNodeModuleData/remove to avoid passing null into removePluginNativeCode.

getNodeModuleData is typed to return INodeModuleData but returns null when the module/package.json can’t be resolved; convertToPluginData is typed to return IPluginData but can also return null on errors. In remove(), the result of convertToPluginData(getNodeModuleData(...)) is passed directly to platformProjectService.removePluginNativeCode without a null guard—IOSProjectService.removePluginNativeCode dereferences pluginData.pluginPlatformsFolderPath(...), so a missing module can crash instead of being skipped.

💡 Proposed fix
-	private getNodeModuleData(
+	private getNodeModuleData(
 		module: string,
 		projectDir: string,
-	): INodeModuleData {
+	): INodeModuleData | null {
 		const removePluginNativeCodeAction = async (
 			modulesDestinationPath: string,
 			platform: string,
 			platformData: IPlatformData,
 		): Promise<void> => {
-			const pluginData = this.convertToPluginData(
-				this.getNodeModuleData(pluginName, projectData.projectDir),
-				projectData.projectDir,
-			);
+			const nodeModuleData = this.getNodeModuleData(
+				pluginName,
+				projectData.projectDir,
+			);
+			if (!nodeModuleData) {
+				this.$logger.warn(
+					`Skipping native cleanup for missing module ${color.yellow(pluginName)}.`,
+				);
+				return;
+			}
+			const pluginData = this.convertToPluginData(
+				nodeModuleData,
+				projectData.projectDir,
+			);
+			if (!pluginData) {
+				return;
+			}

Also update other getNodeModuleData/convertToPluginData call sites (e.g., the “installed modules” mapping) to align types with the new null behavior.

🤖 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/plugins-service.ts` around lines 722 - 737, getNodeModuleData
and convertToPluginData can return null but are typed non-nullable and their
callers (notably remove) call platformProjectService.removePluginNativeCode with
that result, which can crash (e.g., IOSProjectService.removePluginNativeCode
dereferences pluginData). Update getNodeModuleData and convertToPluginData types
to allow null/undefined (INodeModuleData | null, IPluginData | null), and add
null guards where their values are used: in remove(), check the result of
convertToPluginData(getNodeModuleData(...)) before calling
platformProjectService.removePluginNativeCode and skip/log when null; also
update other call sites (like the installed modules mapping) to handle nulls
appropriately (filter/skip or log) so no null is passed into
removePluginNativeCode.

@NathanWalker
Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Copy link
Copy Markdown
Contributor

Copilot AI commented May 29, 2026

@copilot resolve the merge conflicts in this pull request

Resolved and merged origin/main into this branch; conflicts are fixed in commit 7f99acbc.

Copilot AI requested a review from NathanWalker May 29, 2026 21:14
@NativeScript NativeScript deleted a comment from coderabbitai Bot May 29, 2026
@NathanWalker NathanWalker merged commit d1930d3 into main May 29, 2026
1 check passed
@NathanWalker NathanWalker deleted the fix/gracefully-fail-node-modules branch May 29, 2026 21:20
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.

3 participants