Skip to content

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

@DZakh

Description

@DZakh

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions