Summary
A // comment at the end of a line hid the newline that ends the statement, so the scanner read the next line into it. vue-router's encoding.ts has:
export const SLASH_RE = /\//g // %2F
export const PLUS_RE = /\+/g // %2Bdtsx declares SLASH_RE: unknown and drops PLUS_RE. Any initializer does the same: export const a = 1 // note gives a: unknown and swallows the declaration on the next line. After a type annotation or a type alias the next statement leaks into the .d.ts: export type A = string // note followed by export const z = 3 emits export const z = 3;. Namespace members and export default x // note are hit the same way.
Changes
skipNonCode stepped over the comment's newline, and the ASI checks only run on a newline. It now stops at the newline and remembers where the comment started, so the text read up to that newline leaves the comment out (codeEndBefore).
None of the ASI rules was written for that newline, so it ends the statement only in checkASITopLevel, and only before a }, the end of the file or a declaration whose keyword is followed by its name (export, const x, type X =, interface X {, a static import, ...), which nothing can run on into. function and class are left out because either can be an operand (void // note then function f() {}), and so is a comment after a . (api. // note then export()). Everywhere else the scanner reads past the newline as before, so a line that continues after a comment (a: string | // note in a class, a call on the next line) comes out as it does today.
The Zig port skips line comments the same way (skipNonCode in packages/zig-dtsx/src/scanner.zig) and gives the same output; it is not changed here. I can send that as a follow-up.
Testing
New cases in test/type-asi.test.ts under "a line comment before the newline". 11 fail on main, for example Expected to contain: "const a: 1;", Received: "export declare const a: unknown;", and for the type alias Received: "export type A = string // note\nexport const z = 3;". All pass with the fix. The same block has guards for lines that continue after a comment (an operator or keyword before it, a class member, a return type, a call, a decorated class) and for // inside a regex or a template literal; those pass before and after.
bun test (Zig library built with the pinned Zig, so its tests run too), typecheck, lint, zig build test and the Zig fixture tests pass. I also ran dtsx over 4,192 files from vue-core, vue-router, pinia and knip before and after, with LF and converted to CRLF: 4 outputs change, all of them cases like the above.