Skip to content

Commit dce454d

Browse files
mani3xisMariusz Pasinski
andauthored
Cherry pick the findNodeAddonForBindings() to path-utils (#152)
* feat: cherry-pick findNodeAddonForBindings() * feat: relax the condition for CJS modules without file exts This small change relaxes the condition for taking the shortcut, as CommonJS modules (in contrast to ESM) do not require developers to explicitly include the file extensions. The Node.js module resolution algorithm (https://nodejs.org/api/modules.html#all-together) in step 4 of LOAD_AS_FILE(X) would try appending the `.node` extension. In theory, we should make sure that other extensions checked in previous steps are not present, but given that we are implementing it for `requireNodeAddon()`, it should be safe to skip those. # Conflicts: # packages/host/src/node/path-utils.ts * refactor: check each path in separate tests * style: fix eslint issues --------- Co-authored-by: Mariusz Pasinski <mariusz.pasinski@callstack.com>
1 parent 8e53ab5 commit dce454d

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

packages/host/src/node/path-utils.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import fswin from "fswin";
77
import {
88
determineModuleContext,
99
findNodeApiModulePaths,
10+
findNodeAddonForBindings,
1011
findPackageDependencyPaths,
1112
getLibraryName,
1213
isNodeApiModule,
@@ -372,3 +373,30 @@ describe("determineModuleContext", () => {
372373
assert.equal(readCount, 1);
373374
});
374375
});
376+
377+
describe("findNodeAddonForBindings()", () => {
378+
const expectedPaths = {
379+
"addon_1": "addon_1.node",
380+
"addon_2": "build/Release/addon_2.node",
381+
"addon_3": "build/Debug/addon_3.node",
382+
"addon_4": "build/addon_4.node",
383+
"addon_5": "out/Release/addon_5.node",
384+
"addon_6": "out/Debug/addon_6.node",
385+
"addon_7": "Release/addon_7.node",
386+
"addon_8": "Debug/addon_8.node",
387+
};
388+
389+
for (const [name, relPath] of Object.entries(expectedPaths)) {
390+
it(`should look for addons in common paths (${name} in "${relPath}")`, (context) => {
391+
// Arrange
392+
const tempDirectoryPath = setupTempDirectory(context, {
393+
[relPath]: "// This is supposed to be a binary file",
394+
});
395+
// Act
396+
const actualPath = findNodeAddonForBindings(name, tempDirectoryPath);
397+
// Assert
398+
const expectedAbsPath = path.join(tempDirectoryPath, relPath);
399+
assert.equal(actualPath, expectedAbsPath);
400+
});
401+
}
402+
});

packages/host/src/node/path-utils.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ const packageNameCache = new Map<string, string>();
3131
* TODO: Consider checking for a specific platform extension.
3232
*/
3333
export function isNodeApiModule(modulePath: string): boolean {
34+
{
35+
// HACK: Take a shortcut (if applicable): existing `.node` files are addons
36+
try {
37+
fs.accessSync(modulePath.endsWith(".node") ? modulePath : `${modulePath}.node`);
38+
return true;
39+
} catch {
40+
// intentionally left empty
41+
}
42+
}
3443
const dir = path.dirname(modulePath);
3544
const baseName = path.basename(modulePath, ".node");
3645
let entries: string[];
@@ -385,3 +394,29 @@ export function getLatestMtime(fromPath: string): number {
385394

386395
return latest;
387396
}
397+
398+
// NOTE: List of paths influenced by `node-bindings` itself
399+
// https://github.com/TooTallNate/node-bindings/blob/v1.3.0/bindings.js#L21
400+
const nodeBindingsSubdirs = [
401+
"./",
402+
"./build/Release",
403+
"./build/Debug",
404+
"./build",
405+
"./out/Release",
406+
"./out/Debug",
407+
"./Release",
408+
"./Debug",
409+
];
410+
411+
export function findNodeAddonForBindings(id: string, fromDir: string) {
412+
const idWithExt = id.endsWith(".node") ? id : `${id}.node`;
413+
// Support traversing the filesystem to find the Node-API module.
414+
// Currently, we check the most common directories like `bindings` does.
415+
for (const subdir of nodeBindingsSubdirs) {
416+
const resolvedPath = path.join(fromDir, subdir, idWithExt);
417+
if (isNodeApiModule(resolvedPath)) {
418+
return resolvedPath;
419+
}
420+
}
421+
return undefined;
422+
}

0 commit comments

Comments
 (0)