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
no-unused-expressions and template literals (`) #7632
no-unused-expressions and template literals (`) #7632
Comments
UPD: Looks like I was wrong assuming // OK (with brackets)
injectGlobal([`
body {
color: #000,
}
`]);
// causes eslint error (no brackets)
injectGlobal`
body {
background: #f00,
}
`; This does not cancel the issue though. |
I'm treating this as a rule enhancement request, since we probably never thought about whether template tags should or shouldn't be regarded as an expression with side effects. I think it makes sense to enhance the rule in this way. 👍 from me. |
I'm not sure about this. I agree that the tag function does get called and might have side effects, so reporting it as an unused expression could be considered a bug. On the other hand, while I haven't used tagged templates extensively, my understanding is that it's good practice for template tags to be pure functions, which would imply that they avoid side effects. So maybe the rule should report template tag functions, the same way that it reports getters: // This might have side effects if foo.bar is a getter.
// But a getter with side effects is usually a code smell,
// and this is likely to be an error.
foo.bar; I think the decision should come down to whether there is common use-case for tagged templates with side effects. |
I can name at least two somewhat popular libraries where template tags have side effects. After thinking about linting template tags for a bit, I realised that the best scenario for me as a user would be to treat
The second rule will work like this: injectGlobal`body{ color: red; }`; // OK (semantically meaningful)
gql`...some graphql query...`; // error (warms up Apollo cache, but unlikely to be useful) |
I could get behind an option as well, so that the user is at least fully in control over how the rule will enforce template tags. |
👍 for adding 👎 for adding My gut impression is that adding an option like /* eslint no-unused-expressions: [error, {"allowTemplateTags": ["injectGlobal", "injectLocal"]}] */
(foo ? injectGlobal : injectLocal)`
body {
color: red;
}
` // incorrectly reports an error
var foo = {inject: injectGlobal};
foo.inject`
body {
color: red;
}
` // incorrectly reports an error We don't have an option like |
@not-an-aardvark I understand the limitations of the second case and also think that you've demonstrated them quite well. Nevertheless IMO the existence of an option to create a white list does not harm. Those who want to have dynamic template tags may just stick with Those two libraries I mentioned suggest a very simple usage scenario: you just import a specific template literal and then apply it once or a couple of times. There is no space for dynamic naming really (from the semantic point of view, not a technical one). |
I could use this as well. I've created a simple template tag for debug logging (with object pretty-printing) specific for a utility I'm writing, and having to invoke it each time would quickly get cumbersome. I'd rather not have to convert this into a variadic function. const {zip} = require("lodash")
const {inspect} = require("util")
function debug(parts, ...args) {
const items = [parts[0]].concat(...zip(args.map(arg => inspect(arg)), parts.slice(1)))
console.error("module-name %d: %s", process.pid, items.join(""))
}
// example usage
debug `invoke module: ${module.name}, method: ${method.name}`
// variadic, way uglier
function debug(...args) {
console.error(`module-name ${process.pid} ${
args.map(arg => typeof arg === "string" ? arg : inspect(arg)).join("")
}`)
}
debug("invoke module: ", module.name, ", method: ", method.name) |
Hmm, I felt it's abuse of tagged templates since I think "template" is not designed to intend as having side effects. |
@mysticatea I feel the main purpose of template tags are for DSLs. Some DSLs are inherently stateful (e.g. running SQL queries, shell commands), while others are not (e.g. DOM generation, internationalization), and I feel both should be okay. We may agree to disagree here on whether template tags should be allowed to have side effects, but the stateful use cases like above are why I'm interested in adding an option like this. |
I see the evaluating label is still set for this one and not many people 👍'ed the I use a library of mine to construct queries and it's using template literals extensively. In one case, it's creating a side effect which now gets flagged as export function up(transaction) {
// transaction is a queue which executes queries in series
transaction.sql `CREATE TABLE accounts (
...
)`;
} I'm willing to work on this if needed. |
Team we need a champion for this or else it's time to close the issue. |
I'll champion this, for I've removed my 👍 on the rule comment since I'm now championing the rule. We need two more 👍s to get this accepted. |
I'm down for this rule. Do you need assistance getting it to work? |
It looks like we need one more 👍 from an ESLint team member in order to mark the change as "accepted". @jhitchins88, we'd certainly appreciate a PR, although it might be better to wait until this issue is marked as accepted so that you don't waste your time in the event that this issue doesn't get accepted. |
@eslint/eslint-team Anyone else want to 👍 this issue so we can accept it? (At this point, goal is to implement an @kachkaev @isiahmeadows @jhitchins88 Would any of you be interested in implementing, if this is accepted? |
I'll support this 👍 |
Working on this now. |
Looks like template literals with custom interpolators with no brackets are not treated by eslint as proper function calls. The following example produces an error:
What parser (default, Babel-ESLint, etc.) are you using? babel-eslint 7.1.1
Please show your full configuration:
What did you do? Please include the actual source code causing the issue.
MWE: https://github.com/kachkaev/eslint-no-unused-expressions
What did you expect to happen?
I expected both
injectGlobal(
andinjectGlobal
not to produce any eslint errors as these are just normal function calls; they are identical to each other.The text was updated successfully, but these errors were encountered: