🌐 AI搜索 & 代理 主页
Skip to content

Allow projectService to use the default project for virtual processor child files #11838

@CyberT33N

Description

@CyberT33N

Before You File a Proposal Please Confirm You Have Done The Following...

Relevant Package

typescript-estree

My proposal is suitable for this project

  • I believe my proposal would be useful to the broader TypeScript community (meaning it is not a niche proposal).

Description

Typed linting currently fails for virtual child files produced by processors (e.g. docs/example.md/0.ts, file.mdc/0_0.ts) when using parserOptions.projectService: true, because those virtual paths are not recognized as eligible for the TypeScript default project.
This issue proposes enhancing @typescript-eslint/typescript-estree so that such virtual children nested under a real file can be type-checked via the default project without extra configuration or temp files.

Context & Background

  • When using processors such as @eslint/markdown or similar, TypeScript code blocks are extracted into virtual child paths that do not exist on disk and cannot be added to tsconfig.json (e.g. path/docs/example.md/0.ts, path/rules/Example.mdc/0_0.ts).
  • With parserOptions.projectService: true, the TypeScript project service currently only understands real filesystem paths; it does not know how to associate these virtual children with the default project.
  • As a result, typed rules do not run on those code blocks unless users:
    • Add fragile allowDefaultProject globs that attempt to match all possible virtual child paths, or
    • Implement workarounds that materialize temporary .ts files on disk (as explored in eslint/eslint#20378 and related discussions).
  • That behavior pushes complexity into each processor and into end-user configuration, instead of handling the virtual-child concept centrally in typescript-estree’s project-service integration.

Environment

  • @typescript-eslint/parser: current releases with parserOptions.projectService: true (exact versions not critical to the behavior)
  • @typescript-eslint/typescript-estree: current releases and main
  • Processors: tools that emit virtual TS children, e.g. @eslint/markdown or equivalent Markdown/MDX/.mdc processors
  • TypeScript: Not determined; the behavior stems from project service handling of non-existent paths rather than a specific TS version
  • OS/Platform: Not determined; behavior appears OS-agnostic (depends on filesystem existence checks and project service)

Affected Versions / Branch

  • Affected: Current @typescript-eslint/parser / typescript-estree when parserOptions.projectService: true is enabled and virtual child paths are produced by a processor
  • Introduced in: Not determined (likely since project-service integration began handling file eligibility strictly based on real filesystem paths and explicit allowDefaultProject globs)

Steps to Reproduce

  1. Create a TypeScript project with a standard tsconfig.json that includes your .ts source files (but not virtual child paths like *.md/0.ts).
  2. Configure ESLint to use @typescript-eslint/parser with parserOptions.projectService: true.
  3. Add and configure a processor such as @eslint/markdown (or similar) that:
    • Extracts fenced TS code blocks from .md / .mdx / .mdc files, and
    • Emits virtual filenames such as docs/example.md/0.ts or file.mdc/0_0.ts.
  4. Run ESLint on a Markdown/MDX/.mdc file containing a fenced ts or typescript code block.
  5. Observe that:
    • Typed rules do not run on the extracted code block, and
    • The project service reports that the virtual child path was not found.

Expected Behavior

  • Virtual processor child paths that are nested under an existing real file on disk (e.g. docs/example.md/0.ts under docs/example.md) should be implicitly eligible to use the TypeScript default project when projectService is enabled.
  • Users should not need to:
    • Add brittle allowDefaultProject glob patterns for every possible virtual child naming scheme, or
    • Materialize temporary .ts files on disk in processors just to make typed linting work.
  • Real files on disk should continue to respect existing allowDefaultProject behavior and other project-service constraints exactly as today.

Actual Behavior

  • Virtual child paths are treated as if they were normal filesystem paths that happen not to exist:
    • They cannot be added to tsconfig.json because they are not real files.
    • They are not considered eligible for the default project unless explicitly matched by allowDefaultProject globs.
  • As a result, type-aware linting for code extracted from Markdown/MDX/.mdc files fails with errors such as:
<path>/file.mdc/0_0.ts was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject.
  • This blocks or discourages typed linting for documentation code samples and other processor-driven virtual files.

Impact & Severity

  • Severity: Major
  • Impact:
    • Projects that rely on processors for documentation or MDX content cannot easily get type-aware linting on those code blocks with projectService.
    • Workarounds (temp files, broad globs) increase configuration complexity and are error-prone (e.g., cleanup, missed patterns).
    • The issue affects any ecosystem tooling that emits similar virtual child paths, not just a single processor.

Logs & Evidence

<project>/file.mdc/0_0.ts was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject.

(Representative error when running ESLint with parserOptions.projectService: true on a Markdown/MDX/.mdc file containing TypeScript code blocks.)

Code References

  • packages/typescript-estree/src/useProgramFromProjectService.ts
    • Contains the logic that:
      • Resolves filePathAbsolute / filePathRelative
      • Checks allowDefaultProject globs
      • Decides whether the default project is allowed for a given file
    • Today, this logic only considers real filesystem paths and explicit allow-listing; it does not have a concept of “virtual child” paths nested under an existing file.

Proposed Direction / Next Steps

  • Teach typescript-estree’s project-service integration to recognize virtual child paths and treat them as default-project-eligible when appropriate, for example:

    • If the exact filePathAbsolute exists on disk → treat it as a normal file (current behavior).
    • If it does not exist, walk up the path toward the filesystem root and:
      • If an ancestor path is a real file (e.g. docs/example.md), treat the original path as a virtual child of that file and implicitly allow the default project.
      • If no file ancestor is found, keep current behavior (no implicit default-project allowance).
  • Preserve existing semantics for:

    • Real files and allowDefaultProject globs,
    • Non-standard extensions / extraFileExtensions,
    • Default-project matching limits and error messaging,
    • Reload / singleRun behavior.
  • If virtual-path detection fails unexpectedly (e.g. filesystem error), fall back to current non-virtual behavior to stay robust.

I’m very open to alternative heuristics or a different API shape here; the goal is to solve the underlying problem (typed linting for processor-generated virtual children) in a way that fits well with the existing projectService design.

While testing this locally with my fork, I noticed that I had to increase maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING. When a single Markdown file contains several TypeScript code blocks, the current default limit of 8 is reached quite quickly, so this limit (or its recommended configuration) may need special consideration for virtual-children scenarios.

Acceptance Criteria

  • A minimal reproduction using a processor that emits virtual TS children and parserOptions.projectService: true is documented and confirmed.
  • Virtual child paths nested under a real file on disk can be type-checked via the default project without additional allowDefaultProject configuration.
  • Existing behavior for real files and allowDefaultProject remains unchanged and is covered by tests.
  • Unit tests cover:
    • Virtual child paths whose parent exists as a file,
    • Non-virtual paths and error-path behavior (including filesystem errors),
    • Interaction with allowDefaultProject when virtual detection is not applicable or fails.

Additional Links

Suggested Labels (non-binding)

  • type: enhancement
  • pkg: typescript-estree
  • area: typed-linting

Additional Info

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttriageWaiting for team members to take a look

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions