Skip to content
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

node.range in TemplateElement doesn't match code #13360

Closed
scinos opened this issue May 26, 2020 · 3 comments
Closed

node.range in TemplateElement doesn't match code #13360

scinos opened this issue May 26, 2020 · 3 comments
Labels
archived due to age This issue has been archived; please open a new issue for any further discussion question This issue asks a question about ESLint

Comments

@scinos
Copy link

scinos commented May 26, 2020

Tell us about your environment

Node version: v12.16.1
npm version: v6.13.4
Local ESLint version: v7.1.0 (Currently used)
Global ESLint version: Not found

What parser (default, Babel-ESLint, etc.) are you using?

Default

Please show your full code:

Repo with reproduction in https://github.com/scinos/eslint-v7-template-literal-range/blob/master/index.js

Example of a broken rule:

const rule = {
    create: (context) => {
        return {
            TemplateLiteral: (node) => {
                context.report( {
                    node,
                    message: "NOOP",
                    fix: (fixer) => node.quasis
                        .filter(q => q.type === 'TemplateElement')
                        .map(q => {
                            return fixer.replaceTextRange(q.range, q.value.raw)
                        })
                } );
            }
        }
    }
}

var RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester({ env: { es6: true } });
ruleTester.run("my-rule", rule, {
    valid: [],
    invalid: [
        {
            code: "console.log(`test`);",

            output: "console.log(`test`);",   //Throws `[ERR_ASSERTION]: Output is incorrect`
            //output: "console.log(test);", //Does not throw, but code is invalid

            errors: [ { message: "NOOP" } ]
        }
    ]
});

What did you expect to happen?

When running the above rule with node index.js, I'd expect to not throw, and the auto-fixer to not modify the code (fixer.replaceTextRange(q.range, q.value.raw) should be equivalent to a noop).

What actually happened?

It removes the backticks from the output, so console.log(`test`); becomes console.log(test); which is not valid. Before Eslint v7 I was using [q.start, q.end] and it did work.

A few examples form AST Explorer to understand what is going on (https://astexplorer.net/#/gist/0da4480b5208cd870986ea4d35555537/aae58b9c2e76361ed021265d3c04140edbb793f4)

  • When using espree the TemplateElement has range:[12,18], start:13, end: 17
  • When using esprima the TemplateElement has range:[12,18]
  • When using acorn the TemplateElement has start:13, end:17

The difference in the numbers seems to come from https://github.com/eslint/espree/blob/master/lib/espree.js#L263

The problem is that range computation includes the backticks (`test`) but value.raw does not include them, so there is a mismatch. start/end does not include the backticks, therefore it matches value.raw.

So I think esprima has the wrong(*) range, acorn has the correct start/end and espree has both. The change introduced in eslint/rfcs#25 forbid custom rules to use start/end, so the only option is to use range which doesn't match value.raw anymore. Any rule doing text substitution on a TemplateElement can potentially break because of this change.

(* I don't actually know if the range is wrong, or the value in value.raw is wrong)

Are you willing to submit a pull request to fix this bug?

Yes, but not sure where the bug actually is :) I don't know if eslint should bring back start/end, if espree should leave range unmodified, if esprima should change range to exclude the backticks, if the node raw value should include them...

@scinos scinos added bug ESLint is working incorrectly triage An ESLint team member will look at this issue soon labels May 26, 2020
@mysticatea
Copy link
Member

mysticatea commented May 26, 2020

Thank you for your report.

Like the range of string literal nodes contain quotes, the range of TemplateElement nodes contains backtick, ${, and }. I think it's intentional.

@mysticatea mysticatea added question This issue asks a question about ESLint bug ESLint is working incorrectly evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion and removed bug ESLint is working incorrectly triage An ESLint team member will look at this issue soon question This issue asks a question about ESLint labels May 26, 2020
@mysticatea
Copy link
Member

mysticatea commented May 26, 2020

Sorry, I missed your point.

  • The node.value.raw property contains the text value before processing escape sequences.
  • The node.value.cooked property contains the text value after processing escape sequences.

The node.value.cooked and node.value.raw properties are corresponded. But the node.value.raw doesn't mean the whole code of the node. It just means the content before processing escape sequences.

The range of the TemplateElement nodes contain whole template element tokens; it starts with backtick or } and ends with backtick or ${.

If you want to get the whole text of the node, use context.getSourceCode().getText(node) as same as other nodes.

@mysticatea mysticatea added question This issue asks a question about ESLint and removed bug ESLint is working incorrectly evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels May 26, 2020
@scinos
Copy link
Author

scinos commented May 26, 2020

@mysticatea thanks for your answer.

That makes lots of sense, looks like I misunderstood the purpose of node.value.raw.

I'll close this issue now.

@scinos scinos closed this as completed May 26, 2020
@eslint-deprecated eslint-deprecated bot locked and limited conversation to collaborators Nov 23, 2020
@eslint-deprecated eslint-deprecated bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Nov 23, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
archived due to age This issue has been archived; please open a new issue for any further discussion question This issue asks a question about ESLint
Projects
None yet
Development

No branches or pull requests

2 participants