@fjall/eslint-plugin 16.0.1 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* content.replace(re, (m, g1) => `${g1}x`) // deliberate backreferences
|
|
21
21
|
* content.replace(re, "literal $1") // static literal — `$` visible
|
|
22
22
|
* content.replace(re, `static template`) // no expressions — static
|
|
23
|
+
* content.replace(re, String.raw`a\d+`) // raw tag, no expressions
|
|
23
24
|
* const REP = "static"; s.replace(re, REP) // resolves to a const with a
|
|
24
25
|
* // static string init
|
|
25
26
|
*
|
|
@@ -40,6 +41,43 @@ function isStaticString(node) {
|
|
|
40
41
|
return false;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* `String.raw` applied to a template with no expressions.
|
|
46
|
+
*
|
|
47
|
+
* Static in exactly the sense this rule cares about: the tag returns the raw
|
|
48
|
+
* text verbatim, so every `$` in the source is a `$` the author typed and can
|
|
49
|
+
* see. Escapes are the whole reason the shape exists — `String.raw` is how a
|
|
50
|
+
* replacement carrying backslashes is written without doubling them — and
|
|
51
|
+
* flagging it pushes authors to wrap a constant in an arrow for no gain.
|
|
52
|
+
*
|
|
53
|
+
* A tag WITH expressions is runtime-composed and stays flagged. Any other tag
|
|
54
|
+
* stays flagged too: it is an arbitrary function call whose result this rule
|
|
55
|
+
* cannot see.
|
|
56
|
+
*
|
|
57
|
+
* `String` is checked for shadowing, since a locally-bound `String` makes
|
|
58
|
+
* `String.raw` an ordinary call to unknown code. The built-in resolves to a
|
|
59
|
+
* variable with no declarations, which is what separates it from a shadow —
|
|
60
|
+
* merely finding a binding is not enough, because ESLint puts the ES globals
|
|
61
|
+
* in scope like any other name.
|
|
62
|
+
*/
|
|
63
|
+
function isRawStaticTemplate(context, node) {
|
|
64
|
+
if (!node || node.type !== "TaggedTemplateExpression") return false;
|
|
65
|
+
if (node.quasi.expressions.length !== 0) return false;
|
|
66
|
+
|
|
67
|
+
const tag = node.tag;
|
|
68
|
+
if (tag.type !== "MemberExpression" || tag.computed) return false;
|
|
69
|
+
if (tag.object.type !== "Identifier" || tag.object.name !== "String") {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if (tag.property.type !== "Identifier" || tag.property.name !== "raw") {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const scope = context.sourceCode.getScope(node);
|
|
77
|
+
const binding = resolveVariable(scope, "String");
|
|
78
|
+
return binding === null || binding.defs.length === 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
43
81
|
function isFunctionNode(node) {
|
|
44
82
|
return (
|
|
45
83
|
node &&
|
|
@@ -108,6 +146,7 @@ export default {
|
|
|
108
146
|
const replacement = node.arguments[1];
|
|
109
147
|
if (isFunctionNode(replacement)) return;
|
|
110
148
|
if (isStaticString(replacement)) return;
|
|
149
|
+
if (isRawStaticTemplate(context, replacement)) return;
|
|
111
150
|
if (
|
|
112
151
|
replacement.type === "Identifier" &&
|
|
113
152
|
identifierResolvesToSafeBinding(context, replacement)
|