microsoft / microsoft/typespec
[Bug]: Emitter options not resolved for subpath export emitters
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
### Describe the bug
# Emitter options not resolved for subpath export emitters
## Bug Report
### Describe the bug
When an emitter is exposed as a [subpath export](https://nodejs.org/api/packages.html#subpath-exports) of a package (e.g. `@my-org/my-emitter/typescript`), the compiler fails to match `tspconfig.yaml` options to the emitter. The emitter receives an empty options object `{}` instead of the configured values.
### Reproduction
**Package structure** (`@my-org/my-emitter`):
```json
{
"name": "@my-org/my-emitter",
"exports": {
".": { "default": "./dist/src/index.js" },
"./typescript": { "default": "./dist/src/typescript/index.js" },
"./json-schema": { "default": "./dist/src/json-schema/index.js" }
}
}
```
Each subpath export has its own `$lib` and `$onEmit`:
```typescript
// src/typescript/index.ts
export const $lib = createTypeSpecLibrary({
name: "@my-org/my-emitter/typescript",
diagnostics: {},
emitter: { options: myOptionsSchema },
});
export { $onEmit } from "./emitter.js";
```
**tspconfig.yaml:**
```yaml
emit:
- "@my-org/my-emitter/typescript"
options:
"@my-org/my-emitter/typescript":
emitter-output-dir: "{project-root}/generated"
my-custom-option: "hello"
```
**Result:** `context.options` in `$onEmit` is `{}`. The `my-custom-option` is never passed through.
### Root cause
In [`program.ts` `loadEmitter()`](https://github.com/microsoft/typespec/blob/main/packages/compiler/src/core/program.ts), the options lookup is:
```typescript
emittersOptions[metadata.name ?? emitterNameOrPath] ?? {}
```
For subpath exports, `metadata.name` comes from `computeModuleMetadata()`, which reads `module.manifest.name` — the **parent package.json `name` field** (`"@my-org/my-emitter"`), not the subpath specifier (`"@my-org/my-emitter/typescript"`).
Since `metadata.name` is always defined for npm packages, the fallback to `emitterNameOrPath` is never reached. The lookup becomes:
```
emittersOptions["@my-org/my-emitter"] // ← wrong key, undefined
```
instead of:
```
emittersOptions["@my-org/my-emitter/typescript"] // ← what the user configured
```
Additionally, `computeLibraryMetadata()` ignores `libDefinition?.name` (the `$lib.name`) for module-type packages:
```typescript
function computeLibraryMetadata(module, libDefinition) {
if (module.type === "file") {
return { type: "file", name: libDefinition?.name }; // ← uses $lib.name
}
return computeModuleMetadata(module); // ← ignores $lib.name, uses manifest.name
}
```
This means there's **no way** for a subpath emitter to receive its own options, and when multiple subpath emitters exist in the same package, they can't have distinct options (they'd all resolve to the same parent package name key).
### Suggested fix
Either:
1. **Prefer `emitterNameOrPath`** (the specifier from tspconfig) over `metadata.name` for the options lookup:
```typescript
emittersOptions[emitterNameOrPath] ?? emittersOptions[metadata.name] ?? {}
```
2. **Prefer `libDefinition?.name`** over `module.manifest.name` in `computeLibraryMetadata` for module types:
```typescript
function computeLibraryMetadata(module, libDefinition) {
if (module.type === "file") {
return { type: "file", name: libDefinition?.name };
}
const moduleMetadata = computeModuleMetadata(module);
if (libDefinition?.name) {
moduleMetadata.name = libDefinition.name;
}
return moduleMetadata;
}
```
Option 1 is simpler and backwards-compatible — it matches user intent (the key they wrote in tspconfig.yaml) while falling back to the current behavior.
### Workaround
Hardcode emitter-specific values in the `$onEmit` function instead of reading from `context.options`.
### Checklist
- [x] Verified in `@typespec/compiler` v1.10.0
- [x] Traced through compiled `program.js` and `module-resolver.js`
- [x] Both `emitter-output-dir` and custom options are affected
### Reproduction
[typespec-subpath-emitter-options-repro.tar.gz](https://github.com/user-attachments/files/26356298/typespec-subpath-emitter-options-repro.tar.gz)
### Checklist
- [x] Follow our [Code of Conduct](https://github.com/microsoft/typespec/blob/main/CODE_OF_CONDUCT.md)
- [x] Check that there isn't already an issue that request the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/Microsoft/typespec/discussions).
- [x] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
Contributor guide
Assessment
This issue has not been assessed yet.