@blumintinc/eslint-plugin-blumint 1.16.2 → 1.17.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.
- package/README.md +21 -0
- package/lib/index.js +64 -1
- package/lib/rules/enforce-boolean-naming-prefixes.js +30 -14
- package/lib/rules/enforce-cloud-function-id-length.d.ts +5 -0
- package/lib/rules/enforce-cloud-function-id-length.js +104 -0
- package/lib/rules/enforce-is-prefix-validators.d.ts +13 -0
- package/lib/rules/enforce-is-prefix-validators.js +304 -0
- package/lib/rules/enforce-m3-sentence-case.d.ts +12 -0
- package/lib/rules/enforce-m3-sentence-case.js +430 -0
- package/lib/rules/enforce-snapshot-state-narrowing.d.ts +10 -0
- package/lib/rules/enforce-snapshot-state-narrowing.js +281 -0
- package/lib/rules/enforce-types-directory-placement.d.ts +9 -0
- package/lib/rules/enforce-types-directory-placement.js +276 -0
- package/lib/rules/no-direct-function-state.d.ts +8 -0
- package/lib/rules/no-direct-function-state.js +285 -0
- package/lib/rules/no-fill-template-mutation.d.ts +1 -0
- package/lib/rules/no-fill-template-mutation.js +324 -0
- package/lib/rules/no-portal-inside-tooltip.d.ts +10 -0
- package/lib/rules/no-portal-inside-tooltip.js +219 -0
- package/lib/rules/no-redundant-boolean-callback-props.d.ts +11 -0
- package/lib/rules/no-redundant-boolean-callback-props.js +355 -0
- package/lib/rules/no-satisfies-in-frontend-bundle.d.ts +8 -0
- package/lib/rules/no-satisfies-in-frontend-bundle.js +126 -0
- package/lib/rules/no-single-dismiss-dialog-button.d.ts +7 -0
- package/lib/rules/no-single-dismiss-dialog-button.js +127 -0
- package/lib/rules/no-stablehash-react-nodes.d.ts +1 -0
- package/lib/rules/no-stablehash-react-nodes.js +325 -0
- package/lib/rules/parallelize-loop-awaits.d.ts +8 -0
- package/lib/rules/parallelize-loop-awaits.js +582 -0
- package/lib/rules/prefer-flat-transform-each-keys.d.ts +1 -0
- package/lib/rules/prefer-flat-transform-each-keys.js +228 -0
- package/lib/rules/prefer-getter-over-parameterless-method.d.ts +1 -0
- package/lib/rules/prefer-getter-over-parameterless-method.js +44 -0
- package/lib/rules/prefer-spread-over-reassembly.d.ts +5 -0
- package/lib/rules/prefer-spread-over-reassembly.js +401 -0
- package/lib/rules/prefer-sx-prop-over-system-props.d.ts +9 -0
- package/lib/rules/prefer-sx-prop-over-system-props.js +401 -0
- package/lib/rules/prefer-use-base62-id.d.ts +8 -0
- package/lib/rules/prefer-use-base62-id.js +483 -0
- package/lib/rules/prefer-use-theme.d.ts +1 -0
- package/lib/rules/prefer-use-theme.js +206 -0
- package/lib/rules/prefer-utility-function-own-file.d.ts +9 -0
- package/lib/rules/prefer-utility-function-own-file.js +505 -0
- package/lib/rules/require-props-composition.d.ts +10 -0
- package/lib/rules/require-props-composition.js +433 -0
- package/lib/rules/require-server-timestamp-for-firestore-dates.d.ts +9 -0
- package/lib/rules/require-server-timestamp-for-firestore-dates.js +313 -0
- package/package.json +1 -1
- package/release-manifest.json +190 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceM3SentenceCase = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
/**
|
|
7
|
+
* Default props that carry user-facing label text, per the issue spec.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_PROPS_TO_CHECK = new Set([
|
|
10
|
+
'label',
|
|
11
|
+
'title',
|
|
12
|
+
'placeholder',
|
|
13
|
+
'helperText',
|
|
14
|
+
'message',
|
|
15
|
+
'description',
|
|
16
|
+
'tooltip',
|
|
17
|
+
'buttonText',
|
|
18
|
+
'aria-label',
|
|
19
|
+
'alt',
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* Words that are always allowed to keep their original capitalisation.
|
|
23
|
+
* Populated with brand names / proper nouns from the issue spec plus a broad set
|
|
24
|
+
* of common platform, game, and tech names.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_IGNORED_WORDS = new Set([
|
|
27
|
+
// BluMint brand
|
|
28
|
+
'BluMint',
|
|
29
|
+
// Auth / social
|
|
30
|
+
'Google',
|
|
31
|
+
'Apple',
|
|
32
|
+
'Facebook',
|
|
33
|
+
'Discord',
|
|
34
|
+
'Twitch',
|
|
35
|
+
'Steam',
|
|
36
|
+
'YouTube',
|
|
37
|
+
'Twitter',
|
|
38
|
+
'Instagram',
|
|
39
|
+
'TikTok',
|
|
40
|
+
'Reddit',
|
|
41
|
+
'GitHub',
|
|
42
|
+
'LinkedIn',
|
|
43
|
+
// Games / platforms
|
|
44
|
+
'Overwolf',
|
|
45
|
+
'Fortnite',
|
|
46
|
+
'Valorant',
|
|
47
|
+
'Apex',
|
|
48
|
+
'Legends',
|
|
49
|
+
'Rocket',
|
|
50
|
+
'League',
|
|
51
|
+
'Minecraft',
|
|
52
|
+
'Roblox',
|
|
53
|
+
'PlayStation',
|
|
54
|
+
'Xbox',
|
|
55
|
+
'Nintendo',
|
|
56
|
+
// Other tech
|
|
57
|
+
'Windows',
|
|
58
|
+
'macOS',
|
|
59
|
+
'Linux',
|
|
60
|
+
'Android',
|
|
61
|
+
'iPhone',
|
|
62
|
+
'iPad',
|
|
63
|
+
// Common acronyms treated as words (capitalised but not ALL-CAPS)
|
|
64
|
+
'iOS',
|
|
65
|
+
'macOS',
|
|
66
|
+
]);
|
|
67
|
+
/**
|
|
68
|
+
* Short all-caps tokens that are valid acronyms and must not be flagged as
|
|
69
|
+
* ALL-CAPS violations. Entries are case-sensitive (already upper-case).
|
|
70
|
+
*/
|
|
71
|
+
const ACRONYM_ALLOWLIST = new Set([
|
|
72
|
+
'OK',
|
|
73
|
+
'ID',
|
|
74
|
+
'IDs',
|
|
75
|
+
'API',
|
|
76
|
+
'APIs',
|
|
77
|
+
'URL',
|
|
78
|
+
'URLs',
|
|
79
|
+
'URI',
|
|
80
|
+
'URIs',
|
|
81
|
+
'FAQ',
|
|
82
|
+
'FAQs',
|
|
83
|
+
'NFT',
|
|
84
|
+
'NFTs',
|
|
85
|
+
'USD',
|
|
86
|
+
'EUR',
|
|
87
|
+
'GBP',
|
|
88
|
+
'DM',
|
|
89
|
+
'DMs',
|
|
90
|
+
'OBS',
|
|
91
|
+
'RTMP',
|
|
92
|
+
'RTMPS',
|
|
93
|
+
'CDN',
|
|
94
|
+
'SDK',
|
|
95
|
+
'UI',
|
|
96
|
+
'UX',
|
|
97
|
+
'AI',
|
|
98
|
+
'ML',
|
|
99
|
+
'VR',
|
|
100
|
+
'AR',
|
|
101
|
+
'PR',
|
|
102
|
+
'QR',
|
|
103
|
+
'vs',
|
|
104
|
+
'VS',
|
|
105
|
+
]);
|
|
106
|
+
/**
|
|
107
|
+
* Maximum character length for a token that is treated as a valid short acronym
|
|
108
|
+
* even if it is not in the explicit allowlist (e.g. three-letter country codes
|
|
109
|
+
* like "USA", "EUR", etc.).
|
|
110
|
+
*/
|
|
111
|
+
const SHORT_ACRONYM_MAX_LENGTH = 4;
|
|
112
|
+
/**
|
|
113
|
+
* Returns true when a single whitespace-free token should be treated as a
|
|
114
|
+
* valid acronym / abbreviation and ignored.
|
|
115
|
+
*/
|
|
116
|
+
function isAcronymToken(word) {
|
|
117
|
+
if (ACRONYM_ALLOWLIST.has(word))
|
|
118
|
+
return true;
|
|
119
|
+
// All-caps tokens up to the threshold length are treated as acronyms.
|
|
120
|
+
if (word.length <= SHORT_ACRONYM_MAX_LENGTH && /^[A-Z]+$/.test(word)) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Checks whether the raw text looks like code, a URL, or a file-path and
|
|
127
|
+
* should be skipped entirely.
|
|
128
|
+
*/
|
|
129
|
+
function looksLikeCodeOrUrl(text) {
|
|
130
|
+
// URLs
|
|
131
|
+
if (/https?:\/\//i.test(text))
|
|
132
|
+
return true;
|
|
133
|
+
// File paths (starts with /, ./, ../)
|
|
134
|
+
if (/^\.{0,2}\//.test(text))
|
|
135
|
+
return true;
|
|
136
|
+
// camelCase / PascalCase single tokens with no spaces
|
|
137
|
+
if (/^\S+$/.test(text) && /[a-z][A-Z]/.test(text))
|
|
138
|
+
return true;
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Segments a string at sentence boundaries (`.`, `?`, `!`, or `:` followed by
|
|
143
|
+
* whitespace) and at the colon heuristic described in the issue. Each returned
|
|
144
|
+
* segment starts at a sentence-boundary, so its first word is allowed to carry
|
|
145
|
+
* a capital letter.
|
|
146
|
+
*/
|
|
147
|
+
function splitIntoSentences(text) {
|
|
148
|
+
// Split on `. `, `? `, `! `, `: ` — the character after the punctuation is
|
|
149
|
+
// trimmed so each segment's first word is examined independently.
|
|
150
|
+
const segments = text.split(/(?<=[.?!:])\s+/);
|
|
151
|
+
return segments.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Given a sentence (first word allowed to be capitalised), returns the words
|
|
155
|
+
* that carry an unexpected capital — i.e. non-first words whose first letter is
|
|
156
|
+
* upper-case AND which are not in the ignored set AND not acronyms.
|
|
157
|
+
*/
|
|
158
|
+
function titleCaseViolatingWords(sentence, ignoredWordsSet) {
|
|
159
|
+
const words = sentence.split(/\s+/);
|
|
160
|
+
const violating = [];
|
|
161
|
+
words.forEach((raw, index) => {
|
|
162
|
+
// Strip surrounding punctuation to get the bare word for checks
|
|
163
|
+
const word = raw.replace(/^[^\w]+|[^\w]+$/g, '');
|
|
164
|
+
if (!word)
|
|
165
|
+
return;
|
|
166
|
+
// First word of the sentence — allowed to start with a capital
|
|
167
|
+
if (index === 0)
|
|
168
|
+
return;
|
|
169
|
+
// Ignored (proper noun / brand)
|
|
170
|
+
if (ignoredWordsSet.has(word))
|
|
171
|
+
return;
|
|
172
|
+
// Acronym
|
|
173
|
+
if (isAcronymToken(word))
|
|
174
|
+
return;
|
|
175
|
+
// Only flag if the first character is an upper-case letter
|
|
176
|
+
if (/^[A-Z]/.test(word)) {
|
|
177
|
+
violating.push(word);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return violating;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Returns true when the text is an ALL-CAPS violation:
|
|
184
|
+
* — ≥ 2 words
|
|
185
|
+
* — The full letter content is upper-case
|
|
186
|
+
* — It cannot be decomposed into individual acronyms
|
|
187
|
+
*/
|
|
188
|
+
function isAllCapsViolation(text, ignoredWordsSet) {
|
|
189
|
+
const words = text.trim().split(/\s+/);
|
|
190
|
+
if (words.length < 2)
|
|
191
|
+
return false;
|
|
192
|
+
// All letter characters must be upper-case
|
|
193
|
+
const letters = text.replace(/[^a-zA-Z]/g, '');
|
|
194
|
+
if (letters !== letters.toUpperCase())
|
|
195
|
+
return false;
|
|
196
|
+
// If every word is an acronym or ignored, it is not a violation
|
|
197
|
+
const allExempt = words.every((w) => isAcronymToken(w) || ignoredWordsSet.has(w));
|
|
198
|
+
return !allExempt;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Builds the corrected version of the text for the suggestion / fix.
|
|
202
|
+
* — ALL-CAPS: Capitalises only the first letter of the first word, lowercases
|
|
203
|
+
* the rest (respecting acronyms and ignored words).
|
|
204
|
+
* — Title Case: Lowercases non-first words that are not acronyms / proper nouns.
|
|
205
|
+
*/
|
|
206
|
+
function buildSuggestionText(text, ignoredWordsSet) {
|
|
207
|
+
const sentences = splitIntoSentences(text);
|
|
208
|
+
return sentences
|
|
209
|
+
.map((sentence) => {
|
|
210
|
+
const words = sentence.split(/\s+/);
|
|
211
|
+
return words
|
|
212
|
+
.map((raw, index) => {
|
|
213
|
+
const word = raw.replace(/^[^\w]+|[^\w]+$/g, '');
|
|
214
|
+
if (!word)
|
|
215
|
+
return raw;
|
|
216
|
+
if (index === 0) {
|
|
217
|
+
// Preserve ignored words in their original capitalisation; for all-caps
|
|
218
|
+
// first words, lower-case everything except the initial letter.
|
|
219
|
+
if (ignoredWordsSet.has(word))
|
|
220
|
+
return raw;
|
|
221
|
+
if (isAcronymToken(word))
|
|
222
|
+
return raw;
|
|
223
|
+
// Sentence-start: ensure first letter is capital
|
|
224
|
+
return raw.charAt(0).toUpperCase() + raw.slice(1).toLowerCase();
|
|
225
|
+
}
|
|
226
|
+
if (ignoredWordsSet.has(word))
|
|
227
|
+
return raw;
|
|
228
|
+
if (isAcronymToken(word))
|
|
229
|
+
return raw;
|
|
230
|
+
// Non-first word: lower-case
|
|
231
|
+
return raw.charAt(0).toLowerCase() + raw.slice(1);
|
|
232
|
+
})
|
|
233
|
+
.join(' ');
|
|
234
|
+
})
|
|
235
|
+
.join(' ');
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Returns the trimmed text to check and whether it is worth checking.
|
|
239
|
+
* JSXText nodes often contain only whitespace / newlines from formatting.
|
|
240
|
+
*/
|
|
241
|
+
function extractCheckableText(raw) {
|
|
242
|
+
const trimmed = raw.trim();
|
|
243
|
+
// Nothing to check
|
|
244
|
+
if (!trimmed)
|
|
245
|
+
return null;
|
|
246
|
+
// Numeric / symbol only — not user-facing text
|
|
247
|
+
if (/^[\d\W]+$/.test(trimmed))
|
|
248
|
+
return null;
|
|
249
|
+
// Code / URL patterns
|
|
250
|
+
if (looksLikeCodeOrUrl(trimmed))
|
|
251
|
+
return null;
|
|
252
|
+
return trimmed;
|
|
253
|
+
}
|
|
254
|
+
exports.enforceM3SentenceCase = (0, createRule_1.createRule)({
|
|
255
|
+
name: 'enforce-m3-sentence-case',
|
|
256
|
+
meta: {
|
|
257
|
+
type: 'suggestion',
|
|
258
|
+
docs: {
|
|
259
|
+
description: 'Enforce Material Design 3 sentence-case capitalisation for user-facing text — flag Title Case and ALL CAPS strings in JSX text and configured string props.',
|
|
260
|
+
recommended: 'warn',
|
|
261
|
+
},
|
|
262
|
+
hasSuggestions: true,
|
|
263
|
+
schema: [
|
|
264
|
+
{
|
|
265
|
+
type: 'object',
|
|
266
|
+
properties: {
|
|
267
|
+
propsToCheck: {
|
|
268
|
+
type: 'array',
|
|
269
|
+
items: { type: 'string' },
|
|
270
|
+
},
|
|
271
|
+
ignoredWords: {
|
|
272
|
+
type: 'array',
|
|
273
|
+
items: { type: 'string' },
|
|
274
|
+
},
|
|
275
|
+
ignorePatterns: {
|
|
276
|
+
type: 'array',
|
|
277
|
+
items: { type: 'string' },
|
|
278
|
+
},
|
|
279
|
+
allowList: {
|
|
280
|
+
type: 'array',
|
|
281
|
+
items: { type: 'string' },
|
|
282
|
+
},
|
|
283
|
+
checkJsxText: {
|
|
284
|
+
type: 'boolean',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
additionalProperties: false,
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
messages: {
|
|
291
|
+
titleCase: 'Text "{{text}}" uses Title Case. Material Design 3 requires sentence case — only the first word and proper nouns should be capitalised. Consider "{{suggestion}}".',
|
|
292
|
+
allCaps: 'Text "{{text}}" is ALL CAPS. Material Design 3 requires sentence case — use "{{suggestion}}" instead.',
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
defaultOptions: [{}],
|
|
296
|
+
create(context, [options]) {
|
|
297
|
+
const propsToCheckSet = options.propsToCheck
|
|
298
|
+
? new Set(options.propsToCheck)
|
|
299
|
+
: DEFAULT_PROPS_TO_CHECK;
|
|
300
|
+
const ignoredWordsSet = new Set([
|
|
301
|
+
...DEFAULT_IGNORED_WORDS,
|
|
302
|
+
...(options.ignoredWords ?? []),
|
|
303
|
+
]);
|
|
304
|
+
const ignorePatternRegexes = (options.ignorePatterns ?? []).map((p) => new RegExp(p));
|
|
305
|
+
const allowListSet = new Set(options.allowList ?? []);
|
|
306
|
+
const checkJsxText = options.checkJsxText !== false;
|
|
307
|
+
/**
|
|
308
|
+
* Determine whether the text should be skipped before casing checks.
|
|
309
|
+
*/
|
|
310
|
+
function shouldSkip(text) {
|
|
311
|
+
if (allowListSet.has(text))
|
|
312
|
+
return true;
|
|
313
|
+
if (ignorePatternRegexes.some((re) => re.test(text)))
|
|
314
|
+
return true;
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Core checker. Reports on `reportNode` if `text` violates M3 sentence case.
|
|
319
|
+
*/
|
|
320
|
+
function checkText(text, reportNode) {
|
|
321
|
+
const checkable = extractCheckableText(text);
|
|
322
|
+
if (!checkable)
|
|
323
|
+
return;
|
|
324
|
+
if (shouldSkip(checkable))
|
|
325
|
+
return;
|
|
326
|
+
// ALL-CAPS check first (higher severity and different fix)
|
|
327
|
+
if (isAllCapsViolation(checkable, ignoredWordsSet)) {
|
|
328
|
+
const suggestion = buildSuggestionText(checkable, ignoredWordsSet);
|
|
329
|
+
context.report({
|
|
330
|
+
node: reportNode,
|
|
331
|
+
messageId: 'allCaps',
|
|
332
|
+
data: { text: checkable, suggestion },
|
|
333
|
+
suggest: [
|
|
334
|
+
{
|
|
335
|
+
messageId: 'allCaps',
|
|
336
|
+
data: { text: checkable, suggestion },
|
|
337
|
+
fix(fixer) {
|
|
338
|
+
if (reportNode.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
339
|
+
const raw = context.getSourceCode().getText(reportNode);
|
|
340
|
+
const quote = raw[0];
|
|
341
|
+
return fixer.replaceText(reportNode, `${quote}${suggestion}${quote}`);
|
|
342
|
+
}
|
|
343
|
+
if (reportNode.type === utils_1.AST_NODE_TYPES.JSXText) {
|
|
344
|
+
const original = reportNode.value;
|
|
345
|
+
const replaced = original.replace(checkable, suggestion);
|
|
346
|
+
return fixer.replaceText(reportNode, replaced);
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
// Title-Case check: validate each sentence segment independently
|
|
356
|
+
const sentences = splitIntoSentences(checkable);
|
|
357
|
+
const violatingWords = [];
|
|
358
|
+
sentences.forEach((sentence) => {
|
|
359
|
+
violatingWords.push(...titleCaseViolatingWords(sentence, ignoredWordsSet));
|
|
360
|
+
});
|
|
361
|
+
if (violatingWords.length > 0) {
|
|
362
|
+
const suggestion = buildSuggestionText(checkable, ignoredWordsSet);
|
|
363
|
+
context.report({
|
|
364
|
+
node: reportNode,
|
|
365
|
+
messageId: 'titleCase',
|
|
366
|
+
data: { text: checkable, suggestion },
|
|
367
|
+
suggest: [
|
|
368
|
+
{
|
|
369
|
+
messageId: 'titleCase',
|
|
370
|
+
data: { text: checkable, suggestion },
|
|
371
|
+
fix(fixer) {
|
|
372
|
+
if (reportNode.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
373
|
+
const raw = context.getSourceCode().getText(reportNode);
|
|
374
|
+
const quote = raw[0];
|
|
375
|
+
return fixer.replaceText(reportNode, `${quote}${suggestion}${quote}`);
|
|
376
|
+
}
|
|
377
|
+
if (reportNode.type === utils_1.AST_NODE_TYPES.JSXText) {
|
|
378
|
+
const original = reportNode.value;
|
|
379
|
+
const replaced = original.replace(checkable, suggestion);
|
|
380
|
+
return fixer.replaceText(reportNode, replaced);
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
// Check inline JSX text like <Button>Back To App</Button>
|
|
391
|
+
JSXText(node) {
|
|
392
|
+
if (!checkJsxText)
|
|
393
|
+
return;
|
|
394
|
+
checkText(node.value, node);
|
|
395
|
+
},
|
|
396
|
+
// Check string literals in JSX attribute values
|
|
397
|
+
JSXAttribute(node) {
|
|
398
|
+
// Determine the attribute name (handles both plain and namespaced names)
|
|
399
|
+
let attrName;
|
|
400
|
+
if (node.name.type === utils_1.AST_NODE_TYPES.JSXNamespacedName) {
|
|
401
|
+
attrName = `${node.name.namespace.name}:${node.name.name.name}`;
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
attrName = node.name.name;
|
|
405
|
+
}
|
|
406
|
+
if (!propsToCheckSet.has(attrName))
|
|
407
|
+
return;
|
|
408
|
+
const value = node.value;
|
|
409
|
+
if (!value)
|
|
410
|
+
return;
|
|
411
|
+
// <TextField label="Full Name" /> → value is a Literal
|
|
412
|
+
if (value.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
413
|
+
typeof value.value === 'string') {
|
|
414
|
+
checkText(String(value.value), value);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// <TextField label={"Full Name"} /> → value is JSXExpressionContainer
|
|
418
|
+
if (value.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer) {
|
|
419
|
+
const expr = value.expression;
|
|
420
|
+
if (expr.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
421
|
+
typeof expr.value === 'string') {
|
|
422
|
+
checkText(String(expr.value), expr);
|
|
423
|
+
}
|
|
424
|
+
// Template literals: skip — dynamic, cannot reliably check
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
//# sourceMappingURL=enforce-m3-sentence-case.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type MessageIds = 'noFalsyCheck' | 'noRawTypeof';
|
|
2
|
+
type Options = [
|
|
3
|
+
{
|
|
4
|
+
snapshotHooks?: string[];
|
|
5
|
+
guardFunctions?: string[];
|
|
6
|
+
excludeFiles?: string[];
|
|
7
|
+
}?
|
|
8
|
+
];
|
|
9
|
+
export declare const enforceSnapshotStateNarrowing: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
10
|
+
export {};
|