rnx-kit
rnx-kit copied to clipboard
Importing files outside root directory while using metro-plugin-typescript
What happened?
When trying to use files outside the current root project (Using babel module-resolver aliases), this error occurrs:
error: Error: Cannot find project root for source file PATH_TO_PROJECT\shared\index.ts' at findProjectRoot (PATH_TO_PROJECT\mobile\node_modules@rnx-kit\metro-plugin-typescript\lib\projectCache.js:58:19) at Object.getProjectInfo (PATH_TO_PROJECT\mobile\node_modules@rnx-kit\metro-plugin-typescript\lib\projectCache.js:112:22)
PATH_TO_PROJECT is just a placeholder for my actual full path.
For information, using metro without rnx-kit works
Affected Package
@rnx-kit\metro-plugin-typescript
Version
0.4.4
Which platforms are you seeing this issue on?
- [X] Android
- [X] iOS
- [ ] macOS
- [ ] Windows
System Information
This is a pure JavaScript issue.
Steps to Reproduce
- Import a file that is outside the current project's root directory, using babel's module-resolver plugin "alias" option.
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
I did this rough hack to get it to work, just defaulted everything to the current project, since there is only one right now.
function createProjectCache(print) {
const documentRegistry = typescript_1.default.createDocumentRegistry();
const diagnosticWriter = (0, typescript_service_1.createDiagnosticWriter)(print);
// Collection of projects organized by root directory, then by platform.
const projects = {};
function findProjectRoot(sourceFile) {
// Search known root directories to see if the source file is in one of them.
for (const root of Object.keys(projects)) {
---> return root;
if (sourceFile.startsWith(root)) {
return root;
}
Supporting shared modules seems like an important feature, I think this issue should be prioritized.
Thanks 🙏
What does your tsconfig.json
look like? Does it match with how you've configured module-resolver
?
@tido64 Yes the aliases are similar, the proof is that when I added the hack, the build worked correctly. Also the logic that exists in createProjectCache() makes it clear why this fails. You have a file that is outside the root directory which is being compared with the the root directory using .startsWith. That check will always fail.
For info, this issue does not occur with stock Metro bundler.
@tido64 You can validate this issue by creating a very small project that imports a file that is outside the root directory, using a path alias (Configured in both tsconfig.json and babel config)
Example babel configuration
module.exports = {
presets: [ "@rnx-kit/babel-preset-metro-react-native" ],
plugins: [
[
"module-resolver",
{
root: [ "." ],
extensions: [ ".ts", ".tsx", ".json" ],
alias: {
"shared": "../shared"
}
}
]
]
};
Example tsconfig configuration
{
"extends": "@react-native/typescript-config/tsconfig.json",
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"shared/*": [ "../../shared/*" ]
}
},
"include": [
"./src",
"../shared"
],
"exclude": [
"./node_modules"
]
}
Am I understanding you correctly if I say you have a structure that looks something like:
root
├── app1
│ ├── <.js files>
│ └── package.json
├── app2
│ ├── <.js files>
│ └── package.json
└── shared
└── <loose .js files>
Where root
is not a monorepo, and shared
is not a package (i.e. no package.json
)? In that case, could you make shared
a package by adding a package.json
?
The error you're seeing above is because the loose files do not belong to a package, which means that TypeScript can't cache it. I'm not sure of the consequences of adding it to a random root like you've suggested. This will take time to investigate, and it's an edge case I'm not sure we want to support. Adding a package.json
should fix the issue you're seeing. If that doesn't work then we have a bigger issue at hand.
I agree with your assessement, though I assumed that it wouldn't be a random root since there is always a single root project, no? in terms of where we start the bundler from.
Either way, thank you 🙏, your suggestion has cleared the error, however will the babel and tsconfig that exist in the root project be used to process shared code as well or will the system try to lookup tsconfig files upstream?
As far as I can tell, each root folder is treated as a project. If a folder contains a tsconfig.json
, it is treated as a TS project and its files will be type-checked by the TypeScript service. Otherwise, the folder is treated as a JS project and will not be type-checked.
cc @afoxman in case I'm mistaken.
@tido64 I see, what strategy would one have to follow to have the same tsconfig apply to these shared projects, without having to duplicate the file twice?
Use shared configs: https://www.typescriptlang.org/tsconfig#extends