Skip to content

Commit fc276be

Browse files
authored
fix: entry point resolver hijacking relative imports (#68)
Rewrites the implementation of the entry point resolver so it no longer mistakenly hijacks relative imports that originate from third-party packages instead of the root directory.
1 parent 0238e69 commit fc276be

2 files changed

Lines changed: 31 additions & 33 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@react-native-harness/metro': prerelease
3+
---
4+
5+
Rewrites the implementation of the entry point resolver so it no longer mistakenly hijacks relative imports that originate from third-party packages instead of the root directory.

packages/metro/src/resolvers/resolver.ts

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,43 @@
11
import type { MetroConfig } from '@react-native/metro-config';
22
import type { Config as HarnessConfig } from '@react-native-harness/config';
3+
import path from 'node:path';
34
import { createHarnessResolver } from './composite-resolver';
45
import { createTsConfigResolver } from './tsconfig-resolver';
56
import type { HarnessResolver, MetroResolver } from './types';
67

7-
export const createHarnessEntryPointResolver = (
8-
harnessConfig: HarnessConfig
9-
): HarnessResolver => {
10-
// Can be relative to the project root or absolute, need to normalize it
11-
const resolvedEntryPointPath = require.resolve(harnessConfig.entryPoint, {
12-
paths: [process.cwd()],
13-
});
8+
// Safely resolves a path and strips its extension
9+
const getExtensionlessAbsolutePath = (basePath: string, relativePath = ''): string => {
10+
const fullPath = path.resolve(basePath, relativePath);
11+
const parsed = path.parse(fullPath);
12+
return path.join(parsed.dir, parsed.name);
13+
}
1414

15-
return (_context, moduleName, _platform) => {
16-
if (moduleName === resolvedEntryPointPath) {
17-
return {
18-
type: 'sourceFile',
19-
filePath: require.resolve('@react-native-harness/runtime/entry-point'),
20-
};
15+
export const createHarnessEntryPointResolver = (harnessConfig: HarnessConfig): HarnessResolver => {
16+
const rootPath = path.resolve(process.cwd());
17+
const expectedEntryPoint = getExtensionlessAbsolutePath(rootPath, harnessConfig.entryPoint);
18+
const resolvedHarnessPath = require.resolve('@react-native-harness/runtime/entry-point');
19+
20+
return (context, moduleName, _platform) => {
21+
// 1. Resolve the origin path of the file making the import
22+
const currentOrigin = path.resolve(context.originModulePath);
23+
24+
// Fast Fail: If the import isn't happening from the root directory, skip it immediately
25+
if (currentOrigin !== rootPath) {
26+
return null;
2127
}
2228

23-
if (moduleName === harnessConfig.entryPoint) {
29+
// 2. Resolve the module being imported and strip its extension
30+
// This safely normalizes './index', './index.js', 'index.js', etc.
31+
const requestedModule = getExtensionlessAbsolutePath(currentOrigin, moduleName);
32+
33+
// 3. String comparison
34+
if (requestedModule === expectedEntryPoint) {
2435
return {
2536
type: 'sourceFile',
26-
filePath: require.resolve('@react-native-harness/runtime/entry-point'),
37+
filePath: resolvedHarnessPath,
2738
};
2839
}
2940

30-
if (typeof moduleName === 'string') {
31-
try {
32-
const resolvedModuleName = require.resolve(moduleName, {
33-
paths: [process.cwd()],
34-
});
35-
if (resolvedModuleName === resolvedEntryPointPath) {
36-
return {
37-
type: 'sourceFile',
38-
filePath: require.resolve(
39-
'@react-native-harness/runtime/entry-point'
40-
),
41-
};
42-
}
43-
} catch {
44-
// Ignore and fall through
45-
}
46-
}
47-
4841
return null;
4942
};
5043
};

0 commit comments

Comments
 (0)