rescript-lang / rescript-lang/rescript
`genType` emits relative `./S.gen` for workspace (file:) dependencies instead of package-qualified sury/src/S.gen
Nobody has claimed this yet.
- Dominant language
- OCaml
- Stars
- 7.5k
- Forks
- 485
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 55
Description
Summary
When a consumer package depends on another package via a pnpm/npm workspace file: link, genType emits the import path for cross-package types as a relative path (./S.gen) instead of the package-qualified path (sury/src/S.gen). The relative path does not resolve — there's no S.gen.{ts,d.ts} next to the generated file — so downstream tsc fails with TS2307: Cannot find module './S.gen'.
Versions
rescript:12.2.0(also reproduces on12.0.0-beta.5)- Package manager:
pnpm@9.0.5 - Node:
22.6.0 - OS: Ubuntu 24.04
Minimal repro
Two-package pnpm workspace. lib publishes hand-written S.gen.d.ts; app imports its type via @genType.
repro/
├─ pnpm-workspace.yaml # packages: ["packages/*"]
├─ packages/
│ ├─ lib/
│ │ ├─ package.json # { "name": "lib" }
│ │ ├─ rescript.json # below
│ │ └─ src/
│ │ ├─ S.res # let string: t<string> ; type error = {…}
│ │ └─ S.gen.d.ts # hand-written: export type t<T> = …; export type error = …
│ └─ app/
│ ├─ package.json # { "dependencies": { "lib": "file:../lib" } }
│ ├─ rescript.json # below
│ └─ src/
│ └─ Demo.res # @genType let x: S.error = …
packages/lib/rescript.json:
{
"name": "lib",
"namespace": false,
"suffix": ".res.mjs",
"package-specs": { "module": "esmodule", "in-source": true },
"sources": [{ "dir": "src", "public": ["S"] }]
}
packages/app/rescript.json:
{
"name": "app",
"sources": [{ "dir": "src", "subdirs": true }],
"package-specs": { "module": "esmodule", "in-source": true },
"suffix": ".res.mjs",
"dependencies": ["lib"],
"gentypeconfig": { "generatedFileExtension": ".gen.ts" }
}
packages/app/src/Demo.res:
@genType
let err: S.error = S.Error.make(…)
Run pnpm install && pnpm --filter=app rescript build.
Actual output
packages/app/src/Demo.gen.ts:
import type {error as S_error} from './S.gen'; // ❌ relative — no such file next to Demo.gen.ts
tsc --noEmit reports:
Demo.gen.ts(…,…): error TS2307: Cannot find module './S.gen' or its corresponding type declarations.
Expected output
import type {error as S_error} from 'lib/src/S.gen';
(This was the behavior in earlier versions — the same repo has a committed Demo.gen.ts from a prior generation that used from 'lib/src/S.gen'.)
Context / investigation
app/lib/bs/.sourcedirs.jsoncorrectly lists["lib", "/abs/path/packages/lib"]under"pkgs", so the dependency is known to the build.- Poking at the
bsc.exestrings, the relevant logic lives incompiler/gentype/ModuleResolver.ml(resolve_module,import_path_for_reason_module_name,read_bs_dependencies_dirs) andcompiler/gentype/ImportPath.ml. - It looks like when the dep's source directory is reachable via a workspace path, the resolver adds
Sto the consumer's localmodule_name_mapand that takes precedence over the dep lookup, so the import is emitted relative. - No
gentypeconfigoption currently works around this:shimstreats its value as a shim-file path (emits./lib/src/S.gen.shim), and there's nopackageName/importPath/modulesMapoption documented in the bsc binary.
Workaround
Ship a shim next to the generated file that re-exports from the dep:
// packages/app/src/S.gen.ts
export type { error, t } from "lib/src/S.gen";
This keeps the relative ./S.gen import resolving, but is obviously a workaround.
Does it reproduce outside file: deps?
Not when lib is a real npm dependency (resolved under node_modules/lib) — that case emits 'lib/src/S.gen' correctly. Only workspace / file: installs hit this.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in compiler/gentype/ModuleResolver.ml, especially resolve_module, import_path_for_reason_module_name, and read_bs_dependencies_dirs, then inspect compiler/gentype/ImportPath.ml. Reproduce with the pnpm workspace described and run tsc --noEmit. Done means the generated import uses the package-qualified lib/src/S.gen path for file: dependencies and the type check passes without a shim.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, ocaml, typescript
- Domain
- build-system, compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100