#18230·babel

@babel/eslint-parser: Inconsistent TemplateElement ranges for PropertyDefinition vs. AccessorProperty

Author: jpradelleCreated Sep 9, 2026Updated Sep 11, 2026
Labelsi: needs triage

  • Would you like to work on a fix?

How are you using Babel?

@babel/eslint-parser

Input code

I don't find a way to reproduce on Babel REPL. Here is a 4 files project to reproduce: with-accessor.js

javascript
class Test {
  /**/accessor foo = html`
    <foo-baz icon="${' '}" action="${' '}"></foo-baz>
  `;
}

without-accessor.js

javascript
class Test {
  /*accessor*/ foo = html`
    <foo-baz icon="${' '}" action="${' '}"></foo-baz>
  `;
}

(template elements are at same positions on those 2 files.

package.json

json
{
  "devDependencies": {
    "@babel/core": "^8.0.1",
    "@babel/eslint-parser": "^8.0.1",
    "@babel/plugin-proposal-decorators": "^8.0.2",
    "eslint": "^10.10.0"
  },
  "scripts": {
    "bug": "node inspect-ast.js"
  }
}

inspect-ast.js

javascript
const parser = require("@babel/eslint-parser");
const fs = require("fs");

const withAccessor = fs.readFileSync("with-accessor.js", "utf8");
const withoutAccessor = fs.readFileSync("without-accessor.js", "utf8");

const options = {
  requireConfigFile: false,
  babelOptions: {
    plugins: [
      ['@babel/plugin-proposal-decorators', {version: '2023-11'}]
    ],
  },
};

const parsingWithAccessor = parser.parseForESLint(withAccessor, options);
const parsingWithoutAccessor = parser.parseForESLint(withoutAccessor, options);

function findTemplateElements(node) {
  if (node.type === 'PropertyDefinition' || node.type === 'AccessorProperty')
    console.log(node.type)
  if (node.type === 'TemplateElement') {
    console.log(`TemplateElement: ${node.value.raw}, range: [${node.range[0]}, ${node.range[1]}]`);
  }
  for (const key in node) {
    if (node[key] && typeof node[key] === 'object' && node[key].type) {
      findTemplateElements(node[key]);
    } else if (Array.isArray(node[key])) {
      for (const item of node[key]) {
        if (item && typeof item === 'object' && item.type) {
          findTemplateElements(item);
        }
      }
    }
  }
}

console.log("=== With accessor ===")
findTemplateElements(parsingWithAccessor.ast);
console.log("=== Without accessor ===")
findTemplateElements(parsingWithoutAccessor.ast);

Run npm install and npm run bug

This will output

=== With accessor ===
AccessorProperty
TemplateElement: 
    <foo-baz icon=", range: [39, 59]
TemplateElement: " action=", range: [65, 75]
TemplateElement: "></foo-baz>
  , range: [81, 96]
=== Without accessor ===
PropertyDefinition
TemplateElement: 
    <foo-baz icon=", range: [38, 61]
TemplateElement: " action=", range: [64, 77]
TemplateElement: "></foo-baz>
  , range: [80, 97]

Ranges of quasis placed at same place in code are not the same and same length whether access keyword is here or not. I think that's an issue.

Configuration file name

No response

Configuration

No response

Current and expected behavior

I think template parsing of AccessorProperty should be the same as PropertyDefinition

Environment

System: OS: Linux 6.6 Ubuntu 24.04.4 LTS 24.04.4 LTS (Noble Numbat) Binaries: Node: 24.11.0 Yarn: 1.22.22 npm: 11.6.1 npmPackages: @babel/core: ^8.0.1 => 8.0.1 @babel/eslint-parser: ^8.0.1 => 8.0.1 @babel/plugin-proposal-decorators: ^8.0.2 => 8.0.2 eslint: ^10.10.0 => 10.10.0

Possible solution

No response

Additional context

On babel REPL, parsings seems to the same for both, consistent with AccessorProperty parsing from my sample project. https://babel.dev/repl#?config_lz=N4IgZglgNgpgdgQwLYxALhAJxgBygOgCsBnEAGhB22JgBdS0BtRkeAN3NFoUwHM6GIACYwwCAK5R65EEgD2QyTAZioNAL4BdMi2Ld-AWgBMnYTADGczAlpXiANRiZiEOXHQgjABiMBmAwCMATI4EDgwUBBwMAAKmHI4csQIUB4AFgjmANYgWtogxHLimOYwACoAnuEe8oqwuUA&code_lz=MYGwhgzhAEAqCmEAu0DeAoa0D0AqX2YwwiEA9gE7QBmZZ0AvNABZIC2IABpltADy0yAWgBGYAF7QAlsDIA7BgCIAJKgDk0NQF9F0IkinylqjdsUA-PtkGiJ5npwDc6LeiA&lineWrap=true&version=8.0.4

https://babel.dev/repl#?config_lz=N4IgZglgNgpgdgQwLYxALhAJxgBygOgCsBnEAGhB22JgBdS0BtRkeAN3NFoUwHM6GIACYwwCAK5R65EEgD2QyTAZioNAL4BdMi2Ld-AWgBMnYTADGczAlpXiANRiZiEOXHQgjABiMBmAwCMATI4EDgwUBBwMAAKmHI4csQIUB4AFgjmANYgWtqUUOK8UQzMIAACAEYIlREA9HhFUQZUCUkpBiKW1rbOpmxOLm4e3n6BwXkUxHLimOYwACoAnuEe8oqwuUA&code_lz=MYGwhgzhAEAqCmEAu0DeAoa0D0AqX2YwwiEA9gE7QBmZZ0AvNABZIC2IABpltADy0yAWgBGYAF7QAlsDIA7BgCIAJKgDk0NQF9F0IkinylqjdsUA-PtkGiJ5npwDc6LeiA&lineWrap=true&version=8.0.4