Content mappers: pass an import's query or attributes to `transform`
Mantenedores costumam responder em até 1 dia
@andrewbranch já está trabalhando nisso.
Desde 26/10/2026.
Avaliação
- Dificuldade
- 5/5
- Tempo estimado
- Mais de uma semana
- Facilidade para iniciantes
- 35/100
- Tipo de issue
- Funcionalidade
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- go, typescript
- Domínio
- developer-experience
Direção de pesquisa
Start with the content mapper host implementation mentioned in the issue, especially TransformParams in tsc/internal/contentmapper/hostimpl.go, then trace how import queries and attributes reach module resolution. The issue includes a reproduction and a proposed request shape; use it to investigate the resolver and declaration emit questions. Done means opted-in mappers receive the selected variant while plain imports and files claimed by other mappers keep resolving as they do today.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
🔍 Search Terms
content mapper, contentMappers, query, ?raw, import attributes, module specifier query
✅ Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ Suggestion
Let a content mapper opt in, in its manifest, to seeing which variant an import asks for, through its query, its import attributes, or both. Each distinct variant would be its own module, and the transform request would carry it:
import config from "./app.yaml"; // as today
import widened from "./app.yaml?widened"; // query: "?widened"
import alsoWidened from "./app.yaml" with { types: "widened" }; // attributes: { types: "widened" }
// package.json: "typescript": { "contentMapper": { "exec": [...], "variants": true } }
interface TransformParams {
fileName: string;
content: string;
projectHandle: string;
/** The import's query as written, starting with "?". */
query?: string;
/** The import's attributes. */
attributes?: Record<string, string>;
}
Either key would serve. Attributes are standard syntax and leave resolution as it is: TypeScript already accepts with { types: "widened" } on a mapped file and resolves the path as usual. The query needs the resolver to look past the ?, and in return it also covers variants whose value differs. Node keys modules by URL, so it makes each query its own module, while two imports of one file with different attributes share one module there. That's harmless when only the type differs, as with ?widened, and not when the value differs, as with Vite's ?raw. esbuild separates both.
An import with no query or attributes, and any file no opted-in mapper claims, would resolve as today. Open questions: where to report a variant the mapper doesn't support, and what declaration emit writes for one.
📃 Motivating Example
Vite's ?raw is the prior art: import shader from "./shader.glsl?raw" gives the file's text. vite/client types it with declare module "*?raw" { const src: string; export default src }, which works because a string doesn't depend on the file.
A type that does depend on the file can't be written that way, and the content mapper, which can see the file, never sees the query or the attributes. Motherload types data files as as const would:
import config from "./app.yaml"; // { readonly port: 8080; readonly tags: readonly ["a", "b"] }
import widened from "./app.yaml?widened"; // TS2307; wanted: { port: number; tags: string[] }
Reproduction
With [email protected] (3e92363) and an identity mapper for .foo that logs each request:
// main.ts; tsconfig: "contentMappers": [{ "package": "identity-foo-mapper", "extensions": [".foo"] }]
import config from "./config.foo";
import widened from "./config.foo?widened";
import attributed from "./config.foo" with { types: "widened" };
main.ts(2,21): error TS2307: Cannot find module './config.foo?widened' or its corresponding type declarations.
The attributed import type-checks (under bundler, nodenext and preserve), but the mapper receives one transform, for both the plain and the attributed import:
{"method":"transform","params":{"fileName":"/repro/config.foo","projectHandle":"[email protected]:0","content":"…"}}
On main, TransformParams holds fileName, content and projectHandle.
The mapper, node_modules/identity-foo-mapper/mapper.mjs, with "typescript": { "contentMapper": { "exec": ["node", "mapper.mjs"], "compilerOptions": [] } } in its package.json:
import { appendFileSync } from "node:fs";
const log = (message) => process.env.MAPPER_LOG && appendFileSync(process.env.MAPPER_LOG, JSON.stringify(message) + "\n");
const handlers = {
initialize: () => ({ positionEncoding: "utf-16", diagnosticSource: "foo" }),
openProject: () => ({}),
transform: ({ content }) => ({ text: content, extension: ".ts", mappings: [[0, content.length, 0, content.length, 0]] }),
closeProject: () => ({}),
};
let buffer = Buffer.alloc(0);
process.stdin.on("data", (chunk) => {
buffer = Buffer.concat([buffer, chunk]);
for (;;) {
const end = buffer.indexOf("\r\n\r\n");
if (end === -1) return;
const length = Number(/Content-Length:\s*(\d+)/i.exec(buffer.subarray(0, end).toString())[1]);
if (buffer.length < end + 4 + length) return;
const request = JSON.parse(buffer.subarray(end + 4, end + 4 + length).toString());
buffer = buffer.subarray(end + 4 + length);
const { content, ...params } = request.params ?? {};
log({ method: request.method, params: request.method === "transform" ? { ...params, content: "…" } : params });
if (request.id === undefined) continue;
const payload = Buffer.from(JSON.stringify({ jsonrpc: "2.0", id: request.id, result: handlers[request.method]?.(request.params) ?? {} }));
process.stdout.write(`Content-Length: ${payload.length}\r\n\r\n`);
process.stdout.write(payload);
}
});
💻 Use Cases
- Typing one file differently per import when the type depends on its content, as
?wideneddoes above. - Ambient wildcards can't see the file, and the mapper is never told the query or the attributes. A
contentMappersentry'soptionschoose per extension, not per import. - No workaround: Motherload types every data file
as const, and users widen by hand.
- Linguagem predominante
- Go
- Estrelas
- 112k
- Forks
- 16k
- Merge médio
- 3d 4h
- PRs com merge (30d)
- 145
Preparar o ambiente
Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.
- Sem Dockerfile nem arquivo Docker Compose
- Tem um modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de microsoft/TypeScript
-
API: `tsc.exe` opens a visible console window on Windows when the parent process has no consoleAberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 75/100
microsoft/TypeScript#65083 ·
Mantenedores costumam responder em até 1 dia
-
TS7 LSP: documentHighlight returns nothing when the file path contains '@'Talvez livre de novo Um pull request para esta issue foi fechado sem ser mesclado. AbertaBug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
microsoft/TypeScript#64758 ·
Mantenedores costumam responder em até 1 dia
-
Language server (tsc --lsp) exits with code 1 after `shutdown` and `exit`Talvez já em andamento @rajnish-verma-22172 assumiu há 11 dias. AbertaBug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
microsoft/TypeScript#64742 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Class names `eval` and `arguments` are not reported as invalid strict mode bindingsTalvez já em andamento @dmety assumiu há 24 dias. AbertaBug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
microsoft/TypeScript#64661 ·
Mantenedores costumam responder em até 1 dia
-
Non-enum CLI options with multiple values separated by comma and space aren't whitespace trimmedTalvez já em andamento @RyanCavanaugh assumiu há 25 dias. AbertaBug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
microsoft/TypeScript#64618 · 1 responsável ·
Mantenedores costumam responder em até 1 dia
Todas as issues de microsoft/TypeScript
Issues semelhantes
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 62/100
Mantenedores costumam responder em até 2 dias
-
needs-triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 62/100
cli/cli#14895 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 85/100
kagent-dev/kagent#3462 ·
Mantenedores costumam responder em até 1 dia
-
netrc credential source accepts a password with an embedded CR/LF that every other source rejectsAbertaagent-butler-finding bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
jordansmall/spindrift#6268 ·
Mantenedores costumam responder em até 1 dia
-
bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 62/100
Mantenedores costumam responder em até 7 dias