Skip to content

fix(volar): use ts.getTokenPosOfNode instead of node.getStart#2630

Merged
posva merged 2 commits intovuejs:mainfrom
KazariEX:fix/issue-language-tools-5962
Feb 17, 2026
Merged

fix(volar): use ts.getTokenPosOfNode instead of node.getStart#2630
posva merged 2 commits intovuejs:mainfrom
KazariEX:fix/issue-language-tools-5962

Conversation

@KazariEX
Copy link
Member

@KazariEX KazariEX commented Feb 14, 2026

fix vuejs/language-tools#5962

Summary by CodeRabbit

  • Refactor
    • Improved placement accuracy of generated/injected code inside embedded script blocks to reduce mismatches and rendering anomalies.
    • Results in more reliable embedded content handling with fewer edge-case placement issues.
    • No changes to public APIs or exported interfaces.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 14, 2026

📝 Walkthrough

Walkthrough

Replaced direct use of node.getStart(...) with a helper getStartEnd(node, ast) that uses ts.getTokenPosOfNode(node, ast) for the start and node.end for the end when computing replacement ranges for injected code in the SFC script setup path.

Changes

Cohort / File(s) Summary
SFC Typed Router Volar Plugin
packages/router/src/volar/entries/sfc-typed-router.ts
Replaced node.getStart(sfc.scriptSetup!.ast) with getStartEnd(node, sfc.scriptSetup!.ast) which uses (ts as any).getTokenPosOfNode(node, ast) for start and node.end for end; updated replacement usage to use returned start/end values; added internal getStartEnd helper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • posva

Poem

🐰 I hopped through tokens, sniffed the end,
Found start with care and made it mend.
A helper nibble, positions found,
No more surprises when types abound.
Squeak — the router's smooth and sound.

🚥 Pre-merge checks | ✅ 5 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: replacing node.getStart with ts.getTokenPosOfNode, which directly addresses the runtime error mentioned in the linked issue.
Linked Issues check ✅ Passed The changes directly address the node.getStart is not a function runtime error [#5962] by implementing the suggested fix using ts.getTokenPosOfNode for computing token positions.
Out of Scope Changes check ✅ Passed All changes are scoped to the sfc-typed-router.ts file and directly target the problematic code path, with no extraneous modifications outside the issue requirements.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@netlify
Copy link

netlify bot commented Feb 14, 2026

Deploy Preview for vue-router canceled.

Name Link
🔨 Latest commit 3d82172
🔍 Latest deploy log https://app.netlify.com/projects/vue-router/deploys/698ff24f1310a900085e4459

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 14, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vue-router@2630

commit: 3d82172

@codecov
Copy link

codecov bot commented Feb 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.58%. Comparing base (cdf7b44) to head (3d82172).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2630   +/-   ##
=======================================
  Coverage   85.58%   85.58%           
=======================================
  Files          86       86           
  Lines        9963     9963           
  Branches     2285     2284    -1     
=======================================
  Hits         8527     8527           
  Misses       1423     1423           
  Partials       13       13           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/router/src/volar/entries/sfc-typed-router.ts`:
- Around line 78-81: Add a short explanatory comment above the (ts as
any).getTokenPosOfNode(...) usage that documents why the internal API
getTokenPosOfNode is used instead of node.getStart(), referencing the symbols
getTokenPosOfNode, node.getStart and sfc.scriptSetup!.ast; state that
node.getStart is missing on some JS AST node shapes causing the "node.getStart
is not a function" runtime error, acknowledge that getTokenPosOfNode is an
internal TypeScript API (fragile across TS versions), and note that this is an
intentional pragmatic workaround for Volar plugin behavior to be revisited if
TypeScript exposes a stable public alternative.

Comment on lines 78 to 81
const start: number = (ts as any).getTokenPosOfNode(
node,
sfc.scriptSetup!.ast
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

TypeScript getTokenPosOfNode internal API private unstable

💡 Result:

getTokenPosOfNode is not a supported TypeScript Compiler API: in the TypeScript source it’s explicitly marked /** @internal */ (i.e., intended for TypeScript’s own compiler/language-service code, not external consumers). It computes the “first real token” position for a node by skipping trivia (whitespace/comments) with special handling for JSDoc and SyntaxList. [1]

Because it’s @internal, it’s intentionally omitted from the supported public surface area (TypeScript has historically removed internal .d.ts surfaces and can change/remove internal helpers without notice). The TypeScript team documents that internal APIs/helpers are not intended for external use and have been cleaned up/removed in the past. [2]

If you need a stable alternative, prefer public node positioning APIs such as:

  • node.getStart(sourceFile?, includeJsDocComment?)
  • node.getFullStart()
  • node.getEnd()

These are part of the supported compiler API, unlike getTokenPosOfNode. [1]

Sources:
[1] TypeScript source (utilities.ts) showing getTokenPosOfNode is @internal and how it works.
[2] TypeScript Wiki: API breaking changes / internal helpers and removal of internal .d.ts surfaces.


🏁 Script executed:

# Check the actual file to confirm the code and context
fd "sfc-typed-router.ts" --type f

Repository: vuejs/router

Length of output: 109


🏁 Script executed:

# Search for references to getTokenPosOfNode in the codebase
rg "getTokenPosOfNode" --type ts

Repository: vuejs/router

Length of output: 174


🏁 Script executed:

# Look for the issue reference in commit messages or PRs
rg "language-tools#5962|node.getStart is not a function" --type ts -A 2 -B 2

Repository: vuejs/router

Length of output: 38


🏁 Script executed:

# Read the full context around lines 78-81 in the file
cat -n packages/router/src/volar/entries/sfc-typed-router.ts | sed -n '60,95p'

Repository: vuejs/router

Length of output: 1431


🏁 Script executed:

# Look for any comments or documentation about why internal API is used
rg -A 5 -B 5 "getTokenPosOfNode" packages/router/src/volar/entries/sfc-typed-router.ts

Repository: vuejs/router

Length of output: 396


🏁 Script executed:

# Check imports and understand what `ts` object is
cat -n packages/router/src/volar/entries/sfc-typed-router.ts | sed -n '1,30p'

Repository: vuejs/router

Length of output: 1398


🏁 Script executed:

# Search for any discussions or comments about the fix in the file
rg "getStart|getTokenPos" packages/router/src/volar/entries/sfc-typed-router.ts -B 3 -A 3

Repository: vuejs/router

Length of output: 258


Pragmatic use of internal TypeScript API getTokenPosOfNode to fix runtime error.

This is a necessary fix for the node.getStart is not a function runtime error when processing JavaScript files. However, getTokenPosOfNode is marked @internal in TypeScript and not part of the public API surface, making it fragile against future TypeScript releases.

While Volar plugins inherently depend on TypeScript internals, consider adding a comment explaining why this internal API is necessary instead of node.getStart(), both for future maintainers and to acknowledge the trade-off:

             } else {
+              // Use internal ts.getTokenPosOfNode to compute the token start position
+              // node.getStart() fails at runtime in this context when processing JS files
               const start: number = (ts as any).getTokenPosOfNode(
                 node,
                 sfc.scriptSetup!.ast
               )
🤖 Prompt for AI Agents
In `@packages/router/src/volar/entries/sfc-typed-router.ts` around lines 78 - 81,
Add a short explanatory comment above the (ts as any).getTokenPosOfNode(...)
usage that documents why the internal API getTokenPosOfNode is used instead of
node.getStart(), referencing the symbols getTokenPosOfNode, node.getStart and
sfc.scriptSetup!.ast; state that node.getStart is missing on some JS AST node
shapes causing the "node.getStart is not a function" runtime error, acknowledge
that getTokenPosOfNode is an internal TypeScript API (fragile across TS
versions), and note that this is an intentional pragmatic workaround for Volar
plugin behavior to be revisited if TypeScript exposes a stable public
alternative.

Copy link
Member

@posva posva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Is there really no way to avoid the as any? Is this what you told me about ts internal APIs that have existed for a long time?

@KazariEX
Copy link
Member Author

KazariEX commented Feb 17, 2026

Not that but it has also been stable for many years and has been used in language tools.

@posva posva merged commit 0b0504b into vuejs:main Feb 17, 2026
10 checks passed
@KazariEX KazariEX deleted the fix/issue-language-tools-5962 branch February 17, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[vue-tsc] causes unexpected errors with vue-router v5

2 participants

Comments