@astroscope/eslint-plugin-i18n 0.1.1 → 0.1.2

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.
Files changed (2) hide show
  1. package/dist/index.js +98 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -41,14 +41,21 @@ var DEFAULT_IGNORE_PATTERNS = [
41
41
  // numbers
42
42
  /^\s*\|\s*$/,
43
43
  // pipe separators
44
- /^\s*[•·–—]\s*$/
44
+ /^\s*[•·–—]\s*$/,
45
45
  // bullets / dashes
46
+ /^[\s\p{P}\p{S}]+$/u,
47
+ // punctuation / symbols only (e.g. "(", ",", "/ —")
48
+ /^#[0-9a-fA-F]{3,8}$/,
49
+ // hex colors (e.g. "#003366", "#fff")
50
+ /^\d+x\d+$/
51
+ // dimensions (e.g. "180x180")
46
52
  ];
47
53
  var DEFAULT_IGNORE_ATTRIBUTES = [
48
- "className",
49
- "class",
54
+ // html attributes
50
55
  "id",
51
- "key",
56
+ "class",
57
+ "className",
58
+ "style",
52
59
  "href",
53
60
  "src",
54
61
  "type",
@@ -60,9 +67,70 @@ var DEFAULT_IGNORE_ATTRIBUTES = [
60
67
  "rel",
61
68
  "method",
62
69
  "action",
70
+ "loading",
71
+ "decoding",
72
+ "autoComplete",
73
+ "allow",
74
+ "as",
75
+ "sizes",
76
+ "color",
77
+ "crossorigin",
78
+ "referrerpolicy",
79
+ "charset",
80
+ "lang",
81
+ "form",
82
+ "key",
83
+ "slot",
84
+ // common component props
85
+ "variant",
86
+ "size",
87
+ "mode",
88
+ "orientation",
89
+ "align",
90
+ "icon",
91
+ "tag",
92
+ "tagName",
93
+ // html aria attributes (non-text)
94
+ "aria-hidden",
95
+ "aria-live",
96
+ "aria-atomic",
97
+ // astro attributes
98
+ "class:list",
99
+ // testing attributes
63
100
  "data-testid",
64
101
  "data-cy",
65
- "slot"
102
+ // svg attributes
103
+ "d",
104
+ "viewBox",
105
+ "xmlns",
106
+ "fill",
107
+ "stroke",
108
+ "strokeWidth",
109
+ "strokeLinecap",
110
+ "strokeLinejoin",
111
+ "clipPath",
112
+ "transform",
113
+ "points",
114
+ "pathLength",
115
+ "filter",
116
+ "filterUnits",
117
+ "colorInterpolationFilters",
118
+ "floodOpacity",
119
+ "in",
120
+ "in2",
121
+ "result",
122
+ "stroke-linecap",
123
+ "stroke-linejoin",
124
+ "clip-rule",
125
+ "fill-rule",
126
+ "path",
127
+ "values"
128
+ ];
129
+ var DEFAULT_IGNORE_ATTRIBUTE_PATTERNS = [
130
+ /className$/i,
131
+ // *ClassName, *classname (e.g. labelClassName, pictureClassName)
132
+ /^data-/
133
+ // data-* attributes
66
134
  ];
67
135
  var noRawStringsInJsx = {
68
136
  meta: {
@@ -92,21 +160,24 @@ var noRawStringsInJsx = {
92
160
  },
93
161
  create(context) {
94
162
  const options = context.options[0] ?? {};
95
- const userPatterns = (options.ignorePatterns ?? []).map((p) => new RegExp(p));
96
- const allPatterns = [...DEFAULT_IGNORE_PATTERNS, ...userPatterns];
97
- const ignoreAttributes = /* @__PURE__ */ new Set([...DEFAULT_IGNORE_ATTRIBUTES, ...options.ignoreAttributes ?? []]);
163
+ const allPatterns = options.ignorePatterns ? options.ignorePatterns.map((p) => new RegExp(p)) : DEFAULT_IGNORE_PATTERNS;
164
+ const ignoreAttributes = new Set(options.ignoreAttributes ?? DEFAULT_IGNORE_ATTRIBUTES);
98
165
  function shouldIgnore(text) {
99
166
  return allPatterns.some((p) => p.test(text));
100
167
  }
168
+ function shouldIgnoreAttribute(name) {
169
+ if (ignoreAttributes.has(name)) return true;
170
+ return DEFAULT_IGNORE_ATTRIBUTE_PATTERNS.some((p) => p.test(name));
171
+ }
101
172
  function isInsideIgnoredAttribute(node) {
102
173
  const parent = node.parent;
103
174
  if (parent?.type === "JSXAttribute") {
104
175
  const attrName = parent.name?.type === "JSXIdentifier" ? parent.name.name : parent.name?.type === "JSXNamespacedName" ? `${parent.name.namespace.name}:${parent.name.name.name}` : null;
105
- if (attrName && ignoreAttributes.has(attrName)) return true;
176
+ if (attrName && shouldIgnoreAttribute(attrName)) return true;
106
177
  }
107
178
  if (parent?.type === "JSXExpressionContainer" && parent.parent?.type === "JSXAttribute") {
108
179
  const attrName = parent.parent.name?.type === "JSXIdentifier" ? parent.parent.name.name : null;
109
- if (attrName && ignoreAttributes.has(attrName)) return true;
180
+ if (attrName && shouldIgnoreAttribute(attrName)) return true;
110
181
  }
111
182
  return false;
112
183
  }
@@ -130,12 +201,28 @@ var noRawStringsInJsx = {
130
201
  if (shouldIgnore(text)) return;
131
202
  if (isInsideIgnoredAttribute(node)) return;
132
203
  const attrName = node.name?.type === "JSXIdentifier" ? node.name.name : node.name?.type === "JSXNamespacedName" ? `${node.name.namespace.name}:${node.name.name.name}` : null;
133
- if (attrName && ignoreAttributes.has(attrName)) return;
204
+ if (attrName && shouldIgnoreAttribute(attrName)) return;
134
205
  context.report({
135
206
  node: node.value,
136
207
  messageId: "rawString",
137
208
  data: { text: text.length > 40 ? `${text.slice(0, 40)}...` : text }
138
209
  });
210
+ },
211
+ // string literals inside JSX expressions: <div title={value ?? 'fallback'} /> or <div>{'text'}</div>
212
+ Literal(node) {
213
+ if (typeof node.value !== "string") return;
214
+ const ancestors = context.sourceCode.getAncestors(node);
215
+ const inJsxExpression = ancestors.some((a) => a.type === "JSXExpressionContainer");
216
+ if (!inJsxExpression) return;
217
+ if (ancestors.some((a) => a.type === "CallExpression")) return;
218
+ const text = node.value;
219
+ if (shouldIgnore(text)) return;
220
+ if (isInsideIgnoredAttribute(node)) return;
221
+ context.report({
222
+ node,
223
+ messageId: "rawString",
224
+ data: { text: text.length > 40 ? `${text.slice(0, 40)}...` : text }
225
+ });
139
226
  }
140
227
  };
141
228
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astroscope/eslint-plugin-i18n",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "ESLint rules for @astroscope/i18n",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",