@fjall/eslint-plugin 16.0.1 → 18.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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/eslint-plugin",
3
- "version": "16.0.1",
3
+ "version": "18.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fjall-tech/fjall.git",