Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/polite-bikes-stay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-native-node-api": patch
---

Fixed visualizing duplicate library names
25 changes: 10 additions & 15 deletions packages/host/src/node/cli/link-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import {
findNodeApiModulePathsByDependency,
getAutolinkPath,
getLibraryName,
logModulePaths,
visualizeLibraryMap,
NamingStrategy,
PlatformName,
getLibraryMap,
} from "../path-utils";

export type ModuleLinker = (
Expand Down Expand Up @@ -78,9 +79,14 @@ export async function linkModules({
),
);

if (hasDuplicateLibraryNames(absoluteModulePaths, naming)) {
logModulePaths(absoluteModulePaths, naming);
throw new Error("Found conflicting library names");
const libraryMap = getLibraryMap(absoluteModulePaths, naming);
const duplicates = new Map(
Array.from(libraryMap.entries()).filter(([, paths]) => paths.length > 1),
);

if (duplicates.size > 0) {
const visualized = visualizeLibraryMap(duplicates);
throw new Error("Found conflicting library names:\n" + visualized);
}

return Promise.all(
Expand Down Expand Up @@ -133,17 +139,6 @@ export async function pruneLinkedModules(
);
}

export function hasDuplicateLibraryNames(
modulePaths: string[],
naming: NamingStrategy,
): boolean {
const libraryNames = modulePaths.map((modulePath) => {
return getLibraryName(modulePath, naming);
});
const uniqueNames = new Set(libraryNames);
return uniqueNames.size !== libraryNames.length;
}

export function getLinkedModuleOutputPath(
platform: PlatformName,
modulePath: string,
Expand Down
12 changes: 7 additions & 5 deletions packages/host/src/node/cli/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import {
findNodeApiModulePathsByDependency,
getAutolinkPath,
getLibraryName,
logModulePaths,
visualizeLibraryMap,
normalizeModulePath,
PlatformName,
PLATFORMS,
getLibraryMap,
} from "../path-utils";

import { command as vendorHermes } from "./hermes";
Expand Down Expand Up @@ -115,10 +116,10 @@ program
successText: `Linked ${platformDisplayName} Node-API modules into ${prettyPath(
platformOutputPath,
)}`,
failText: (error) =>
failText: () =>
`Failed to link ${platformDisplayName} Node-API modules into ${prettyPath(
platformOutputPath,
)}: ${error.message}`,
)}`,
Comment on lines +119 to +122
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

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

The error parameter was removed from the failText callback, but the error's message is no longer included in the output. This loses valuable debugging information. Consider retaining the error parameter and including error.message in the error text, or document why the error details are intentionally omitted.

Copilot uses AI. Check for mistakes.
},
);

Expand Down Expand Up @@ -209,14 +210,15 @@ program
dependencies,
)) {
console.log(
chalk.blueBright(dependencyName),
"\n" + chalk.blueBright(dependencyName),
"→",
prettyPath(dependency.path),
);
logModulePaths(
const libraryMap = getLibraryMap(
dependency.modulePaths.map((p) => path.join(dependency.path, p)),
{ packageName, pathSuffix },
);
console.log(visualizeLibraryMap(libraryMap));
}
}
}),
Expand Down
12 changes: 0 additions & 12 deletions packages/host/src/node/duplicates.ts

This file was deleted.

35 changes: 17 additions & 18 deletions packages/host/src/node/path-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { createRequire } from "node:module";

import { chalk, prettyPath } from "@react-native-node-api/cli-utils";

import { findDuplicates } from "./duplicates";

// TODO: Change to .apple.node
export const PLATFORMS = ["android", "apple"] as const;
export type PlatformName = "android" | "apple";
Expand Down Expand Up @@ -267,32 +265,33 @@ export function resolvePackageRoot(
}
}

export function logModulePaths(
modulePaths: string[],
// TODO: Default to iterating and printing for all supported naming strategies
naming: NamingStrategy,
) {
const pathsPerName = new Map<string, string[]>();
/**
* Module paths per library name.
*/
export type LibraryMap = Map<string, string[]>;

export function getLibraryMap(modulePaths: string[], naming: NamingStrategy) {
const result = new Map<string, string[]>();
for (const modulePath of modulePaths) {
const libraryName = getLibraryName(modulePath, naming);
const existingPaths = pathsPerName.get(libraryName) ?? [];
const existingPaths = result.get(libraryName) ?? [];
existingPaths.push(modulePath);
pathsPerName.set(libraryName, existingPaths);
result.set(libraryName, existingPaths);
}
return result;
}

const allModulePaths = modulePaths.map((modulePath) => modulePath);
const duplicatePaths = findDuplicates(allModulePaths);
for (const [libraryName, modulePaths] of pathsPerName) {
console.log(
export function visualizeLibraryMap(libraryMap: LibraryMap) {
const result = [];
for (const [libraryName, modulePaths] of libraryMap) {
result.push(
chalk.greenBright(`${libraryName}`),
...modulePaths.flatMap((modulePath) => {
const line = duplicatePaths.has(modulePath)
? chalk.redBright(prettyPath(modulePath))
: prettyPath(modulePath);
return `\n ↳ ${line}`;
return ` ↳ ${prettyPath(modulePath)}`;
}),
);
}
return result.join("\n");
}

/**
Expand Down
Loading