rescript-lang / rescript-lang/rescript

`genType` emits relative `./S.gen` for workspace (file:) dependencies instead of package-qualified sury/src/S.gen

Open
#8,375 1 comment 0 reactions 0 assignees View on GitHub

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 on 12.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.json correctly lists ["lib", "/abs/path/packages/lib"] under "pkgs", so the dependency is known to the build.
  • Poking at the bsc.exe strings, the relevant logic lives in compiler/gentype/ModuleResolver.ml (resolve_module, import_path_for_reason_module_name, read_bs_dependencies_dirs) and compiler/gentype/ImportPath.ml.
  • It looks like when the dep's source directory is reachable via a workspace path, the resolver adds S to the consumer's local module_name_map and that takes precedence over the dep lookup, so the import is emitted relative.
  • No gentypeconfig option currently works around this: shims treats its value as a shim-file path (emits ./lib/src/S.gen.shim), and there's no packageName / importPath / modulesMap option 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.