Skip to content

feat(compiler-core): add openTagLoc and closeTagLoc to element ast node #12928

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/compiler-core/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ export interface BaseElementNode extends Node {
type: NodeTypes.ELEMENT
ns: Namespace
tag: string
openTagLoc?: SourceLocation
closeTagLoc?: SourceLocation
tagType: ElementTypes
Comment on lines +133 to 135
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mismatch with test expectations – property should be tagLoc or an alias must be added

The interface now exposes openTagLoc / closeTagLoc, but the updated unit-test asserts against a tagLoc field.
Unless an alias is added or the tests are updated, the type checker and the runtime equality check (toStrictEqual) will fail.

   tag: string
-  openTagLoc?: SourceLocation
-  closeTagLoc?: SourceLocation
+  // location of `<tag>` name
+  openTagLoc?: SourceLocation
+  // location of `</tag>` name
+  closeTagLoc?: SourceLocation
+  /** @deprecated – kept for backwards-compatibility with existing tooling/tests */
+  tagLoc?: SourceLocation

Alternatively, rename the tests to use openTagLoc. Keep one consistent public field, or expose an alias – but make it deliberate.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
openTagLoc?: SourceLocation
closeTagLoc?: SourceLocation
tagType: ElementTypes
tag: string
// location of `<tag>` name
openTagLoc?: SourceLocation
// location of `</tag>` name
closeTagLoc?: SourceLocation
/** @deprecated – kept for backwards-compatibility with existing tooling/tests */
tagLoc?: SourceLocation
tagType: ElementTypes
🤖 Prompt for AI Agents
In packages/compiler-core/src/ast.ts around lines 133 to 135, the interface uses
separate properties openTagLoc and closeTagLoc, but the unit tests expect a
single tagLoc property. To fix this, either rename the interface property to
tagLoc to match the tests, add a tagLoc alias that combines or references
openTagLoc, or update the tests to use openTagLoc consistently. Ensure the
property naming is consistent between the interface and tests to avoid type
checker and runtime equality failures.

props: Array<AttributeNode | DirectiveNode>
children: TemplateChildNode[]
Expand Down
5 changes: 5 additions & 0 deletions packages/compiler-core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ export interface ParserOptions
* @default false
*/
prefixIdentifiers?: boolean
/**
* Whether to calculate the location of open tag and close tag of each node.
* @default false
*/
tagLocations?: boolean
/**
* A list of parser plugins to enable for `@babel/parser`, which is used to
* parse expressions in bindings and interpolations.
Expand Down
7 changes: 7 additions & 0 deletions packages/compiler-core/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const defaultParserOptions: MergedParserOptions = {
onWarn: defaultOnWarn,
comments: __DEV__,
prefixIdentifiers: false,
tagLocations: false,
}

let currentOptions: MergedParserOptions = defaultParserOptions
Expand Down Expand Up @@ -146,6 +147,9 @@ const tokenizer = new Tokenizer(stack, {
loc: getLoc(start - 1, end),
codegenNode: undefined,
}
if (currentOptions.tagLocations) {
currentOpenTag.openTagLoc = getLoc(start, end)
}
Comment on lines +150 to +152
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Set legacy alias for forward compatibility

When the option is active you attach openTagLoc, but not tagLoc.
If you decide to keep the test’s tagLoc, add the alias at the same time:

 if (currentOptions.tagLocations) {
   currentOpenTag.openTagLoc = getLoc(start, end)
+  // backwards compat
+  ;(currentOpenTag as any).tagLoc = currentOpenTag.openTagLoc
 }

This avoids breaking userland tools that already consume tagLoc.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (currentOptions.tagLocations) {
currentOpenTag.openTagLoc = getLoc(start, end)
}
if (currentOptions.tagLocations) {
currentOpenTag.openTagLoc = getLoc(start, end)
// backwards compat
;(currentOpenTag as any).tagLoc = currentOpenTag.openTagLoc
}
🤖 Prompt for AI Agents
In packages/compiler-core/src/parser.ts around lines 150 to 152, the code sets
currentOpenTag.openTagLoc when currentOptions.tagLocations is true but does not
set the legacy alias currentOpenTag.tagLoc. To fix this, add an assignment
setting currentOpenTag.tagLoc equal to currentOpenTag.openTagLoc within the same
conditional block to maintain backward compatibility with userland tools relying
on tagLoc.

},

onopentagend(end) {
Expand All @@ -165,6 +169,9 @@ const tokenizer = new Tokenizer(stack, {
}
for (let j = 0; j <= i; j++) {
const el = stack.shift()!
if (j === i && currentOptions.tagLocations) {
el.closeTagLoc = getLoc(start, end)
}
Comment on lines 171 to +174
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same alias needed on close tag

 if (j === i && currentOptions.tagLocations) {
   el.closeTagLoc = getLoc(start, end)
+  ;(el as any).tagLoc = el.openTagLoc // keep alias symmetrical
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const el = stack.shift()!
if (j === i && currentOptions.tagLocations) {
el.closeTagLoc = getLoc(start, end)
}
const el = stack.shift()!
if (j === i && currentOptions.tagLocations) {
el.closeTagLoc = getLoc(start, end)
;(el as any).tagLoc = el.openTagLoc // keep alias symmetrical
}
🤖 Prompt for AI Agents
In packages/compiler-core/src/parser.ts around lines 171 to 174, the close tag
element lacks the same alias as the opening tag. To fix this, ensure that the
close tag element uses the same alias as the corresponding opening tag by
assigning the alias property from the opening tag to the close tag element
within the conditional block where closeTagLoc is set.

onCloseTag(el, end, j < i)
}
break
Expand Down