Skip to content

BUNDLER: entryPoints() regex matches import/export text inside comments #158

@xenopia

Description

@xenopia

TLDR

before I had it over to the bots who are smarter than me:

-The bundler treats commented-out import/export text as real module syntax.

  • This caused quite a bit of confusion in my codebase, leaving me with a file that was fully commented out, and yet I could not delete without causing a cascade of TS errors. I gave up and left this "load bearing" comment file for many months.
  • After finally finding this bug, I finally deleted the file and realized those TS errors actually were real. But with the file, TS appeared to be fully satisfied. I
  • I wonder if this could have other unexpected impacts in complex TS projects, some of which are open issues

Summary

entryPoints() in src/bundler/index.ts:482 filters TypeScript entry points using a raw-text regex:

/^\s{0,100}(import|export)/m.test(contents)

Because the check is not syntax-aware, files containing only commented-out import/export text are classified as TypeScript modules and included in _generated/api.d.ts as phantom entries.

Reproducer

A convex/sentinel.ts containing only:

/*
import { query } from "./_generated/server";
export const oldDebugQuery = query({ args: {}, handler: async () => "x" });
*/

…has no top-level import or export. But after npx convex codegen, sentinel appears in _generated/api.d.ts. Remove the commented import/export lines and rerun codegen — sentinel disappears. Codegen output is being driven by comment text.

Concrete failure: invalid generated .d.ts under path-alias collision

Given:

  • convex/foo_bar.ts — a real module
  • convex/foo/bar.ts — comment-only

Both paths normalize to the import alias foo_bar. Generated output:

import type * as foo_bar from "../foo/bar.js";
import type * as foo_bar from "../foo_bar.js";

Strict-mode check of the generated file fails:

TS2300: Duplicate identifier 'foo_bar'.
TS2306: File '.../convex/foo/bar.ts' is not a module.

Real-world impact: phantom entries silently widen unrelated references to any

In a production project (~250 modules), a controlled experiment showed that the presence of a single 3-line comment-only .ts file changed how unrelated internal[...] references resolve:

State internal["internal/X"] resolves to
Comment-only file present any (silent failure)
Comment-only file deleted TS2339: Property does not exist

Control reference (internal.admin) resolves to a real type in both states.

The phantom entry adds enough complexity to push specific key resolutions in ApiFromModules<...> past TypeScript's instantiation depth limit, which silently widens those subexpressions to any instead of erroring.

Suggested fix

@babel/parser is already imported in src/bundler/index.ts (line 4) and used elsewhere in the same file (lines 358, 509). The check can be replaced with a syntax-aware top-level declaration scan with no new dependency. A conservative fallback can strip comments and apply the original regex if parsing fails, preserving behavior on syntax errors.

Versions

  • Reconfirmed against main at a487de2 — regex unchanged at src/bundler/index.ts:482
  • Originally encountered in convex@1.28.2; reproduces in every version since

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions