Skip to content

[api] Let a program created with createPrograms use content mappersΒ #64641

Description

πŸ” Search Terms

createPrograms, CreateProgramOptions, synthetic program, contentMappers, content mapper, createModuleResolver, runExternalCode

βœ… Viability Checklist

⭐ Suggestion

An optional contentMappers member of CreateProgramOptions, taking the entries a configuration's top-level contentMappers takes plus the directory their packages resolve from, resolved and gated by runExternalCode as a configuration's are. A program created through createPrograms then reads the files a content mapper handles, as a configured project does.

A smaller alternative: an API request that sets the inferred-project content mappers, as custom/setContentMapperContributions does for the language server. The snapshot builder already passes those mappers to every created and reconfigured program. Only Session.SetContentMapperContributions sets them, and the API has no request that calls it.

πŸ“ƒ Motivating Example

A configuration with "contentMappers": [{ "package": "comp-mapper", "extensions": [".comp"] }], a src/main.ts that imports ./widget.comp, and the same files loaded two ways on 7.1.0-dev.20261004.1 with runExternalCode: true:

const api = new API({ cwd: dir, runExternalCode: true });
const parsed = api.parseConfigFile(configFile);

const configured = api.createSnapshot({ openProjects: [configFile] });
report("openProjects:", configured.getProject(configFile).program);

const synthetic = api.createSnapshot({
  createPrograms: [{ rootFiles: parsed.fileNames, compilerOptions: parsed.options }],
});
report("createPrograms:", synthetic.operation.createdPrograms[0]);
openProjects: {"files":["src/widget.comp","src/main.ts"],"errors":[]}
createPrograms: {"files":["src/main.ts"],"errors":["TS2307: Cannot find module './widget.comp' or its corresponding type declarations."]}

Passing src/widget.comp as a root file gives the same TS2307.

With the suggestion, the second program passes the mappers itself:

const synthetic = api.createSnapshot({
  createPrograms: [{
    rootFiles: parsed.fileNames,
    compilerOptions: parsed.options,
    options: { contentMappers: { directory: dir, entries: [{ package: "comp-mapper", extensions: [".comp"] }] } },
  }],
});

πŸ’» Use Cases

Impact: a tool that needs its own module resolver cannot see .vue or .svelte files: every import of one is a TS2307, and nothing inside them is analyzed.

  1. A client that builds its program itself, typically to give it a module resolver (createModuleResolver, [api] Provide module resolution overridesΒ #64299), in a repository with .vue or .svelte files. I met this while building deadset-ts, the TypeScript analyzer of deadset, on the TypeScript 7 API: it answers workspace-package imports from source with a resolver.
  2. A module resolver can only be given to a program created through createPrograms, and content mappers only reach a configured project, so a client cannot have both. CreateProgramOptions has no field for mappers.
  3. The workaround restates the resolver as paths in a configuration written into a snapshot fileSystem layer and opens that as a project. This loses what a resolver can express: an answer per importing directory and per resolution mode, and leaving imports from node_modules alone.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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