@hublo/sentinel 1.1.0-alpha.1 → 1.1.0-alpha.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.
- package/dist/bin/sentinel.js +1 -1
- package/dist/{chunk-RLMXN2ZJ.js → chunk-EZCRU6XE.js} +533 -25
- package/dist/index.js +1 -1
- package/oxlint/react.json +570 -0
- package/package.json +2 -1
- package/plugins/hublo.js +88 -0
package/dist/bin/sentinel.js
CHANGED
|
@@ -187,8 +187,204 @@ var NEST_DISABLED = [
|
|
|
187
187
|
reason: "the single occurrence is a regex whose whole purpose is stripping ASCII control characters before XML output, which the rule cannot distinguish from an accident"
|
|
188
188
|
}
|
|
189
189
|
];
|
|
190
|
+
var REACT_DISABLED = [
|
|
191
|
+
// The jest suite is the single largest group: enabling it turned 135 errors into a
|
|
192
|
+
// wall nobody would read on day one. Adoption has to be a lateral move first.
|
|
193
|
+
{
|
|
194
|
+
rule: "jest/expect-expect",
|
|
195
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
rule: "jest/no-alias-methods",
|
|
199
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
rule: "jest/no-commented-out-tests",
|
|
203
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
rule: "jest/no-conditional-expect",
|
|
207
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
rule: "jest/no-deprecated-functions",
|
|
211
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
rule: "jest/no-disabled-tests",
|
|
215
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
rule: "jest/no-done-callback",
|
|
219
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
rule: "jest/no-export",
|
|
223
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
rule: "jest/no-focused-tests",
|
|
227
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
rule: "jest/no-identical-title",
|
|
231
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
rule: "jest/no-interpolation-in-snapshots",
|
|
235
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
rule: "jest/no-jasmine-globals",
|
|
239
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
rule: "jest/no-mocks-import",
|
|
243
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
rule: "jest/no-standalone-expect",
|
|
247
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
rule: "jest/no-test-prefixes",
|
|
251
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
rule: "jest/prefer-to-be",
|
|
255
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
rule: "jest/prefer-to-contain",
|
|
259
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
rule: "jest/prefer-to-have-length",
|
|
263
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
rule: "jest/valid-describe-callback",
|
|
267
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
rule: "jest/valid-expect",
|
|
271
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
rule: "jest/valid-expect-in-promise",
|
|
275
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
rule: "jest/valid-title",
|
|
279
|
+
reason: "pre-existing test-suite debt; enabling it on adoption would surface a wall of findings and make a lateral move look like a regression"
|
|
280
|
+
},
|
|
281
|
+
// TanStack Query rules the codebase has never satisfied.
|
|
282
|
+
{
|
|
283
|
+
rule: "@tanstack/query/exhaustive-deps",
|
|
284
|
+
reason: "the codebase predates these rules and has never satisfied them; enabling them is its own piece of work, not part of swapping linters"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
rule: "@tanstack/query/infinite-query-property-order",
|
|
288
|
+
reason: "the codebase predates these rules and has never satisfied them; enabling them is its own piece of work, not part of swapping linters"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
rule: "@tanstack/query/mutation-property-order",
|
|
292
|
+
reason: "the codebase predates these rules and has never satisfied them; enabling them is its own piece of work, not part of swapping linters"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
rule: "@tanstack/query/no-rest-destructuring",
|
|
296
|
+
reason: "the codebase predates these rules and has never satisfied them; enabling them is its own piece of work, not part of swapping linters"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
rule: "@tanstack/query/no-unstable-deps",
|
|
300
|
+
reason: "the codebase predates these rules and has never satisfied them; enabling them is its own piece of work, not part of swapping linters"
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
rule: "@tanstack/query/no-void-query-fn",
|
|
304
|
+
reason: "the codebase predates these rules and has never satisfied them; enabling them is its own piece of work, not part of swapping linters"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
rule: "@tanstack/query/stable-query-client",
|
|
308
|
+
reason: "the codebase predates these rules and has never satisfied them; enabling them is its own piece of work, not part of swapping linters"
|
|
309
|
+
},
|
|
310
|
+
// typescript-eslint rules carrying real, pre-existing debt.
|
|
311
|
+
{
|
|
312
|
+
rule: "typescript/no-empty-function",
|
|
313
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
rule: "typescript/no-explicit-any",
|
|
317
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
rule: "typescript/no-unnecessary-type-assertion",
|
|
321
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
rule: "typescript/no-unsafe-argument",
|
|
325
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
rule: "typescript/no-unsafe-assignment",
|
|
329
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
rule: "typescript/no-unsafe-call",
|
|
333
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
rule: "typescript/no-unsafe-enum-comparison",
|
|
337
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
rule: "typescript/no-unsafe-member-access",
|
|
341
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
rule: "typescript/no-unsafe-return",
|
|
345
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
rule: "typescript/no-unused-vars",
|
|
349
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
rule: "typescript/only-throw-error",
|
|
353
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
rule: "typescript/require-await",
|
|
357
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
358
|
+
},
|
|
359
|
+
// One-offs, each for its own reason.
|
|
360
|
+
{
|
|
361
|
+
rule: "no-console",
|
|
362
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
rule: "no-unsafe-optional-chaining",
|
|
366
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
rule: "react/jsx-curly-brace-presence",
|
|
370
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
rule: "styled-components-a11y/label-has-for",
|
|
374
|
+
reason: "pre-existing debt measured during the proof of concept; parked so adoption stays non-breaking"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
rule: "jsx-a11y/img-redundant-alt",
|
|
378
|
+
reason: 'one real finding, a decorative image whose title is already rendered as a heading beside it, so the fix is alt="". A genuine accessibility issue rather than a false positive: parked only so swapping linters stays a lateral move, and worth switching back on as soon as it is fixed'
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
rule: "hublo/no-palette-common",
|
|
382
|
+
reason: "the house rule finds 3 real violations; they are fixed as their own change rather than inside a linter swap, so the rule ships visible-but-off and can be switched on once they are"
|
|
383
|
+
}
|
|
384
|
+
];
|
|
190
385
|
var BY_FLAVOUR = {
|
|
191
|
-
nest: NEST_DISABLED
|
|
386
|
+
nest: NEST_DISABLED,
|
|
387
|
+
react: REACT_DISABLED
|
|
192
388
|
};
|
|
193
389
|
function disabledRulesFor(flavour) {
|
|
194
390
|
return BY_FLAVOUR[flavour] ?? [];
|
|
@@ -393,6 +589,261 @@ var nestLayer = {
|
|
|
393
589
|
};
|
|
394
590
|
var nestRules = { ...lintBase, ...nestLayer };
|
|
395
591
|
|
|
592
|
+
// src/roles/lint/flavours/react.ts
|
|
593
|
+
var reactLayer = {
|
|
594
|
+
// ESLint core (42)
|
|
595
|
+
curly: ["error", "all"],
|
|
596
|
+
"for-direction": "error",
|
|
597
|
+
"no-async-promise-executor": "error",
|
|
598
|
+
"no-case-declarations": "error",
|
|
599
|
+
"no-compare-neg-zero": "error",
|
|
600
|
+
"no-cond-assign": ["error", "except-parens"],
|
|
601
|
+
"no-constant-binary-expression": "error",
|
|
602
|
+
"no-constant-condition": ["error", { checkLoops: "allExceptWhileTrue" }],
|
|
603
|
+
"no-control-regex": "error",
|
|
604
|
+
"no-debugger": "error",
|
|
605
|
+
"no-delete-var": "error",
|
|
606
|
+
"no-dupe-else-if": "error",
|
|
607
|
+
"no-duplicate-case": "error",
|
|
608
|
+
"no-empty": ["error", { allowEmptyCatch: false }],
|
|
609
|
+
"no-empty-character-class": "error",
|
|
610
|
+
"no-empty-pattern": ["error", { allowObjectPatternsAsParameters: false }],
|
|
611
|
+
"no-empty-static-block": "error",
|
|
612
|
+
"no-ex-assign": "error",
|
|
613
|
+
"no-extra-boolean-cast": ["error", {}],
|
|
614
|
+
"no-fallthrough": ["error", { allowEmptyCase: false, reportUnusedFallthroughComment: false }],
|
|
615
|
+
"no-global-assign": ["error", { exceptions: [] }],
|
|
616
|
+
"no-invalid-regexp": ["error", {}],
|
|
617
|
+
"no-irregular-whitespace": [
|
|
618
|
+
"error",
|
|
619
|
+
{
|
|
620
|
+
skipComments: false,
|
|
621
|
+
skipJSXText: false,
|
|
622
|
+
skipRegExps: false,
|
|
623
|
+
skipStrings: true,
|
|
624
|
+
skipTemplates: false
|
|
625
|
+
}
|
|
626
|
+
],
|
|
627
|
+
"no-loss-of-precision": "error",
|
|
628
|
+
"no-misleading-character-class": "error",
|
|
629
|
+
"no-nonoctal-decimal-escape": "error",
|
|
630
|
+
"no-prototype-builtins": "error",
|
|
631
|
+
"no-regex-spaces": "error",
|
|
632
|
+
"no-restricted-imports": [
|
|
633
|
+
"error",
|
|
634
|
+
{
|
|
635
|
+
patterns: [
|
|
636
|
+
{
|
|
637
|
+
group: ["next/*"],
|
|
638
|
+
message: "Importing from next/* is not allowed in the admin project."
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
group: ["storybook/*"],
|
|
642
|
+
message: "Importing from storybook/* is not allowed in the admin project."
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
group: ["@nestjs/*"],
|
|
646
|
+
message: "Importing from @nestjs/* is not allowed in the admin project."
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
group: ["next-i18next"],
|
|
650
|
+
message: "Importing from next-i18next is not allowed in the admin project."
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
group: ["@hublo/style/colors", "@hublo/style/colors/*"],
|
|
654
|
+
message: "Importing runtime colors directly is not allowed. Use the MUI theme, theme-derived CSS variables, or an explicit product-specific mapping."
|
|
655
|
+
}
|
|
656
|
+
]
|
|
657
|
+
}
|
|
658
|
+
],
|
|
659
|
+
"no-self-assign": ["error", { props: true }],
|
|
660
|
+
"no-shadow-restricted-names": ["error", { reportGlobalThis: false }],
|
|
661
|
+
"no-sparse-arrays": "error",
|
|
662
|
+
"no-unexpected-multiline": "error",
|
|
663
|
+
"no-unsafe-finally": "error",
|
|
664
|
+
"no-unused-labels": "error",
|
|
665
|
+
"no-unused-private-class-members": "error",
|
|
666
|
+
"no-useless-backreference": "error",
|
|
667
|
+
"no-useless-catch": "error",
|
|
668
|
+
"no-useless-escape": ["error", { allowRegexCharacters: [] }],
|
|
669
|
+
"require-yield": "error",
|
|
670
|
+
"use-isnan": ["error", { enforceForIndexOf: false, enforceForSwitchCase: true }],
|
|
671
|
+
"valid-typeof": ["error", { requireStringLiterals: false }],
|
|
672
|
+
// eslint-plugin-styled-components-a11y (hosted) (32)
|
|
673
|
+
"styled-components-a11y/alt-text": "error",
|
|
674
|
+
"styled-components-a11y/anchor-has-content": "error",
|
|
675
|
+
"styled-components-a11y/anchor-is-valid": "error",
|
|
676
|
+
"styled-components-a11y/aria-activedescendant-has-tabindex": "error",
|
|
677
|
+
"styled-components-a11y/aria-props": "error",
|
|
678
|
+
"styled-components-a11y/aria-proptypes": "error",
|
|
679
|
+
"styled-components-a11y/aria-role": "error",
|
|
680
|
+
"styled-components-a11y/aria-unsupported-elements": "error",
|
|
681
|
+
"styled-components-a11y/autocomplete-valid": "error",
|
|
682
|
+
"styled-components-a11y/click-events-have-key-events": "error",
|
|
683
|
+
"styled-components-a11y/control-has-associated-label": [
|
|
684
|
+
"off",
|
|
685
|
+
{
|
|
686
|
+
ignoreElements: ["audio", "canvas", "embed", "input", "textarea", "tr", "video"],
|
|
687
|
+
ignoreRoles: [
|
|
688
|
+
"grid",
|
|
689
|
+
"listbox",
|
|
690
|
+
"menu",
|
|
691
|
+
"menubar",
|
|
692
|
+
"radiogroup",
|
|
693
|
+
"row",
|
|
694
|
+
"tablist",
|
|
695
|
+
"toolbar",
|
|
696
|
+
"tree",
|
|
697
|
+
"treegrid"
|
|
698
|
+
],
|
|
699
|
+
includeRoles: ["alert", "dialog"]
|
|
700
|
+
}
|
|
701
|
+
],
|
|
702
|
+
"styled-components-a11y/heading-has-content": "error",
|
|
703
|
+
"styled-components-a11y/html-has-lang": "error",
|
|
704
|
+
"styled-components-a11y/iframe-has-title": "error",
|
|
705
|
+
"styled-components-a11y/img-redundant-alt": "error",
|
|
706
|
+
"styled-components-a11y/interactive-supports-focus": [
|
|
707
|
+
"error",
|
|
708
|
+
{
|
|
709
|
+
tabbable: [
|
|
710
|
+
"button",
|
|
711
|
+
"checkbox",
|
|
712
|
+
"link",
|
|
713
|
+
"progressbar",
|
|
714
|
+
"searchbox",
|
|
715
|
+
"slider",
|
|
716
|
+
"spinbutton",
|
|
717
|
+
"switch",
|
|
718
|
+
"textbox"
|
|
719
|
+
]
|
|
720
|
+
}
|
|
721
|
+
],
|
|
722
|
+
"styled-components-a11y/label-has-associated-control": "error",
|
|
723
|
+
"styled-components-a11y/media-has-caption": "error",
|
|
724
|
+
"styled-components-a11y/mouse-events-have-key-events": "error",
|
|
725
|
+
"styled-components-a11y/no-access-key": "error",
|
|
726
|
+
"styled-components-a11y/no-autofocus": "error",
|
|
727
|
+
"styled-components-a11y/no-distracting-elements": "error",
|
|
728
|
+
"styled-components-a11y/no-interactive-element-to-noninteractive-role": "error",
|
|
729
|
+
"styled-components-a11y/no-noninteractive-element-interactions": [
|
|
730
|
+
"error",
|
|
731
|
+
{ body: ["onError", "onLoad"], iframe: ["onError", "onLoad"], img: ["onError", "onLoad"] }
|
|
732
|
+
],
|
|
733
|
+
"styled-components-a11y/no-noninteractive-element-to-interactive-role": "error",
|
|
734
|
+
"styled-components-a11y/no-noninteractive-tabindex": "error",
|
|
735
|
+
"styled-components-a11y/no-redundant-roles": "error",
|
|
736
|
+
"styled-components-a11y/no-static-element-interactions": "error",
|
|
737
|
+
"styled-components-a11y/role-has-required-aria-props": "error",
|
|
738
|
+
"styled-components-a11y/role-supports-aria-props": "error",
|
|
739
|
+
"styled-components-a11y/scope": "error",
|
|
740
|
+
"styled-components-a11y/tabindex-no-positive": "error",
|
|
741
|
+
// eslint-plugin-jsx-a11y (31)
|
|
742
|
+
"jsx-a11y/alt-text": "error",
|
|
743
|
+
"jsx-a11y/anchor-has-content": "error",
|
|
744
|
+
"jsx-a11y/anchor-is-valid": "error",
|
|
745
|
+
"jsx-a11y/aria-activedescendant-has-tabindex": "error",
|
|
746
|
+
"jsx-a11y/aria-props": "error",
|
|
747
|
+
"jsx-a11y/aria-proptypes": "error",
|
|
748
|
+
"jsx-a11y/aria-role": "error",
|
|
749
|
+
"jsx-a11y/aria-unsupported-elements": "error",
|
|
750
|
+
"jsx-a11y/autocomplete-valid": "error",
|
|
751
|
+
"jsx-a11y/click-events-have-key-events": "error",
|
|
752
|
+
"jsx-a11y/heading-has-content": "error",
|
|
753
|
+
"jsx-a11y/html-has-lang": "error",
|
|
754
|
+
"jsx-a11y/iframe-has-title": "error",
|
|
755
|
+
"jsx-a11y/img-redundant-alt": "error",
|
|
756
|
+
"jsx-a11y/interactive-supports-focus": [
|
|
757
|
+
"error",
|
|
758
|
+
{
|
|
759
|
+
tabbable: [
|
|
760
|
+
"button",
|
|
761
|
+
"checkbox",
|
|
762
|
+
"link",
|
|
763
|
+
"progressbar",
|
|
764
|
+
"searchbox",
|
|
765
|
+
"slider",
|
|
766
|
+
"spinbutton",
|
|
767
|
+
"switch",
|
|
768
|
+
"textbox"
|
|
769
|
+
]
|
|
770
|
+
}
|
|
771
|
+
],
|
|
772
|
+
"jsx-a11y/label-has-associated-control": "error",
|
|
773
|
+
"jsx-a11y/media-has-caption": "error",
|
|
774
|
+
"jsx-a11y/mouse-events-have-key-events": "error",
|
|
775
|
+
"jsx-a11y/no-access-key": "error",
|
|
776
|
+
"jsx-a11y/no-autofocus": "error",
|
|
777
|
+
"jsx-a11y/no-distracting-elements": "error",
|
|
778
|
+
"jsx-a11y/no-interactive-element-to-noninteractive-role": "error",
|
|
779
|
+
"jsx-a11y/no-noninteractive-element-interactions": [
|
|
780
|
+
"error",
|
|
781
|
+
{ body: ["onError", "onLoad"], iframe: ["onError", "onLoad"], img: ["onError", "onLoad"] }
|
|
782
|
+
],
|
|
783
|
+
"jsx-a11y/no-noninteractive-element-to-interactive-role": "error",
|
|
784
|
+
"jsx-a11y/no-noninteractive-tabindex": "error",
|
|
785
|
+
"jsx-a11y/no-redundant-roles": "error",
|
|
786
|
+
"jsx-a11y/no-static-element-interactions": "error",
|
|
787
|
+
"jsx-a11y/role-has-required-aria-props": "error",
|
|
788
|
+
"jsx-a11y/role-supports-aria-props": "error",
|
|
789
|
+
"jsx-a11y/scope": "error",
|
|
790
|
+
"jsx-a11y/tabindex-no-positive": "error",
|
|
791
|
+
// typescript-eslint (18)
|
|
792
|
+
"typescript/await-thenable": "error",
|
|
793
|
+
"typescript/no-array-delete": "error",
|
|
794
|
+
"typescript/no-base-to-string": "error",
|
|
795
|
+
"typescript/no-duplicate-type-constituents": "error",
|
|
796
|
+
"typescript/no-floating-promises": ["error", { ignoreVoid: true }],
|
|
797
|
+
"typescript/no-for-in-array": "error",
|
|
798
|
+
"typescript/no-implied-eval": "error",
|
|
799
|
+
"typescript/no-inferrable-types": "error",
|
|
800
|
+
"typescript/no-misused-promises": ["error", { checksVoidReturn: { attributes: false } }],
|
|
801
|
+
"typescript/no-non-null-assertion": "warn",
|
|
802
|
+
"typescript/no-redundant-type-constituents": "error",
|
|
803
|
+
"typescript/no-require-imports": "error",
|
|
804
|
+
"typescript/no-unsafe-unary-minus": "error",
|
|
805
|
+
"typescript/prefer-promise-reject-errors": "error",
|
|
806
|
+
"typescript/restrict-plus-operands": "error",
|
|
807
|
+
"typescript/restrict-template-expressions": "error",
|
|
808
|
+
"typescript/switch-exhaustiveness-check": "warn",
|
|
809
|
+
"typescript/unbound-method": "error",
|
|
810
|
+
// eslint-plugin-react-hooks (2)
|
|
811
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
812
|
+
"react-hooks/rules-of-hooks": "error",
|
|
813
|
+
// eslint-plugin-no-barrel-files (hosted) (1)
|
|
814
|
+
"no-barrel-files/no-barrel-files": "warn",
|
|
815
|
+
// @nx/eslint-plugin (hosted) (1)
|
|
816
|
+
"nx/enforce-module-boundaries": [
|
|
817
|
+
"error",
|
|
818
|
+
{
|
|
819
|
+
enforceBuildableLibDependency: true,
|
|
820
|
+
allow: [],
|
|
821
|
+
depConstraints: [{ sourceTag: "*", onlyDependOnLibsWithTags: ["*"] }]
|
|
822
|
+
}
|
|
823
|
+
],
|
|
824
|
+
// eslint-plugin-import — aliased, oxlint reserves `import` (1)
|
|
825
|
+
"import-js/order": [
|
|
826
|
+
"error",
|
|
827
|
+
{
|
|
828
|
+
"newlines-between": "always",
|
|
829
|
+
groups: ["builtin", "external", "internal", ["parent", "index"], "sibling"],
|
|
830
|
+
alphabetize: { order: "asc" },
|
|
831
|
+
pathGroups: [
|
|
832
|
+
{ pattern: "@nestjs/**", group: "external" },
|
|
833
|
+
{
|
|
834
|
+
pattern: "{@admin/**,@authentication/**,@featureToggles/**,@institution/**,@hublo/**,@network/**,@mission/**,@notification/**,@shared/**,@talent/**,@worker/**,@contract/**,@config/**,@front/**}",
|
|
835
|
+
group: "internal",
|
|
836
|
+
position: "before"
|
|
837
|
+
}
|
|
838
|
+
],
|
|
839
|
+
pathGroupsExcludedImportTypes: ["builtin"]
|
|
840
|
+
}
|
|
841
|
+
],
|
|
842
|
+
// eslint-plugin-react-refresh (hosted) (1)
|
|
843
|
+
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }]
|
|
844
|
+
};
|
|
845
|
+
var reactRules = { ...lintBase, ...reactLayer };
|
|
846
|
+
|
|
396
847
|
// src/roles/lint/presets.ts
|
|
397
848
|
var TS_EXTENSIONS = [".ts", ".cts", ".mts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
398
849
|
var IMPORT_SETTINGS = {
|
|
@@ -418,8 +869,26 @@ var BASE_PRESET = {
|
|
|
418
869
|
env: { es2022: true },
|
|
419
870
|
rules: lintBase
|
|
420
871
|
};
|
|
872
|
+
var REACT_PRESET = {
|
|
873
|
+
plugins: ["typescript", "react", "jsx-a11y", "jest", "import", "unicorn", "oxc"],
|
|
874
|
+
jsPlugins: [
|
|
875
|
+
"eslint-plugin-no-barrel-files",
|
|
876
|
+
{ name: "nx", specifier: "@nx/eslint-plugin" },
|
|
877
|
+
{ name: "import-js", specifier: "eslint-plugin-import" },
|
|
878
|
+
{ name: "@tanstack/query", specifier: "@tanstack/eslint-plugin-query" },
|
|
879
|
+
{ name: "styled-components-a11y", specifier: "eslint-plugin-styled-components-a11y" },
|
|
880
|
+
{ name: "react-refresh", specifier: "eslint-plugin-react-refresh" },
|
|
881
|
+
// Ships inside sentinel, resolved relative to THIS preset file. Oxlint has no
|
|
882
|
+
// `no-restricted-syntax`, so the Hublo house rules are a real rule instead.
|
|
883
|
+
"../plugins/hublo.js"
|
|
884
|
+
],
|
|
885
|
+
env: { browser: true, es2022: true, node: true, jest: true },
|
|
886
|
+
settings: IMPORT_SETTINGS,
|
|
887
|
+
rules: reactRules
|
|
888
|
+
};
|
|
421
889
|
var BY_FLAVOUR2 = {
|
|
422
890
|
nest: NEST_PRESET,
|
|
891
|
+
react: REACT_PRESET,
|
|
423
892
|
// `node` ships the base alone, deliberately. It is the honest answer for a module that
|
|
424
893
|
// belongs to no stack, and it keeps `--init` with no named target working everywhere
|
|
425
894
|
// rather than blocking a whole workspace sweep on one unshipped flavour. It is thin (21
|
|
@@ -437,17 +906,43 @@ function lintPresetFor(flavour) {
|
|
|
437
906
|
// src/roles/lint/rename-suppressions.ts
|
|
438
907
|
import { readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
439
908
|
import { join as join2, relative } from "path";
|
|
440
|
-
var SOURCE_EXTENSIONS = [
|
|
909
|
+
var SOURCE_EXTENSIONS = [
|
|
910
|
+
".ts",
|
|
911
|
+
".tsx",
|
|
912
|
+
".js",
|
|
913
|
+
".jsx",
|
|
914
|
+
".mjs",
|
|
915
|
+
".cjs",
|
|
916
|
+
".mts",
|
|
917
|
+
".cts",
|
|
918
|
+
".svelte",
|
|
919
|
+
".vue"
|
|
920
|
+
];
|
|
441
921
|
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".nx"]);
|
|
442
922
|
var SUPPRESSION = /(eslint-disable(?:-next-line|-line)?)\s+([^\n*]+?)(?:\s*--.*)?(?:\s*\*\/)?\s*$/;
|
|
443
|
-
function renamedPrefixes(jsPlugins) {
|
|
923
|
+
function renamedPrefixes(jsPlugins, declared = {}) {
|
|
444
924
|
const renames = /* @__PURE__ */ new Map();
|
|
445
925
|
for (const plugin of jsPlugins) {
|
|
446
926
|
if (typeof plugin === "string") continue;
|
|
447
927
|
const original = plugin.specifier.replace(/^eslint-plugin-/, "").replace(/\/eslint-plugin$/, "");
|
|
448
928
|
if (original !== plugin.name) renames.set(`${original}/`, `${plugin.name}/`);
|
|
449
929
|
}
|
|
450
|
-
|
|
930
|
+
const slash = (value) => value.endsWith("/") ? value : `${value}/`;
|
|
931
|
+
for (const [from, to] of Object.entries(declared)) renames.set(slash(from), slash(to));
|
|
932
|
+
return collapseChains(renames);
|
|
933
|
+
}
|
|
934
|
+
function collapseChains(renames) {
|
|
935
|
+
const collapsed = /* @__PURE__ */ new Map();
|
|
936
|
+
for (const [from, to] of renames) {
|
|
937
|
+
let target = to;
|
|
938
|
+
for (let hop = 0; hop < renames.size; hop += 1) {
|
|
939
|
+
const next = renames.get(target);
|
|
940
|
+
if (next === void 0 || next === target) break;
|
|
941
|
+
target = next;
|
|
942
|
+
}
|
|
943
|
+
collapsed.set(from, target);
|
|
944
|
+
}
|
|
945
|
+
return collapsed;
|
|
451
946
|
}
|
|
452
947
|
function* sourceFiles(dir) {
|
|
453
948
|
let entries;
|
|
@@ -684,6 +1179,14 @@ function oxlintSearchPath(cwd) {
|
|
|
684
1179
|
|
|
685
1180
|
// src/roles/lint/adapters/oxlint/oxlint.adapter.ts
|
|
686
1181
|
var MAX_FIX_PASSES = 3;
|
|
1182
|
+
function summariseByPlugin(rules) {
|
|
1183
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1184
|
+
for (const name of deferredRuleNames(rules)) {
|
|
1185
|
+
const plugin = name.includes("/") ? name.split("/")[0] ?? name : "core";
|
|
1186
|
+
counts.set(plugin, (counts.get(plugin) ?? 0) + 1);
|
|
1187
|
+
}
|
|
1188
|
+
return [...counts.entries()].sort((left, right) => right[1] - left[1]).map(([plugin, count]) => `${count} ${plugin}`).join(", ");
|
|
1189
|
+
}
|
|
687
1190
|
var OxlintAdapter = class extends BaseAdapter {
|
|
688
1191
|
target = "lint";
|
|
689
1192
|
runner = "oxlint";
|
|
@@ -735,10 +1238,15 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
735
1238
|
}
|
|
736
1239
|
const renames = findSuppressionRenames(
|
|
737
1240
|
context.cwd,
|
|
738
|
-
renamedPrefixes(
|
|
1241
|
+
renamedPrefixes(
|
|
1242
|
+
lintPresetFor(context.flavour).jsPlugins,
|
|
1243
|
+
lintPresetFor(context.flavour).renames
|
|
1244
|
+
)
|
|
739
1245
|
);
|
|
1246
|
+
const deleting = new Set(eslintConfigs);
|
|
1247
|
+
const rewritten = renames.filter((entry) => !deleting.has(entry.file));
|
|
740
1248
|
const byFile = /* @__PURE__ */ new Map();
|
|
741
|
-
for (const entry of
|
|
1249
|
+
for (const entry of rewritten) {
|
|
742
1250
|
const list = byFile.get(entry.file) ?? [];
|
|
743
1251
|
list.push({ from: entry.from, to: entry.to });
|
|
744
1252
|
byFile.set(entry.file, list);
|
|
@@ -751,15 +1259,15 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
751
1259
|
];
|
|
752
1260
|
if (disabled.length > 0) {
|
|
753
1261
|
notes.push(
|
|
754
|
-
`${disabled.length} rule(s) not enforced by this preset
|
|
1262
|
+
`${disabled.length} rule(s) not enforced by this preset (${summariseByPlugin(disabled)}). Run --inspect --lint for each rule and why.`
|
|
755
1263
|
);
|
|
756
1264
|
}
|
|
757
1265
|
if (eslintConfigs.length > 0) {
|
|
758
1266
|
notes.push(`removed ${eslintConfigs.join(", ")} and pointed the nx lint target at oxlint`);
|
|
759
1267
|
}
|
|
760
|
-
if (
|
|
1268
|
+
if (rewritten.length > 0) {
|
|
761
1269
|
notes.push(
|
|
762
|
-
`renamed ${
|
|
1270
|
+
`renamed ${rewritten.length} suppression comment(s) whose plugin oxlint registers under a different name (${[...new Set(rewritten.map((entry) => entry.rule))].join(", ")}); left alone they would stop suppressing anything, silently`
|
|
763
1271
|
);
|
|
764
1272
|
}
|
|
765
1273
|
notes.push("run `pnpm install`, then `pnpm run lint`");
|
|
@@ -816,35 +1324,35 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
816
1324
|
return { ok: false, code: 1 };
|
|
817
1325
|
}
|
|
818
1326
|
this.announceDisabled(ctx.flavour);
|
|
819
|
-
const
|
|
820
|
-
if (
|
|
821
|
-
args.push("--type-aware");
|
|
822
|
-
} else {
|
|
1327
|
+
const typeAware = canRunTypeAware(ctx.cwd);
|
|
1328
|
+
if (!typeAware) {
|
|
823
1329
|
process.stderr.write(
|
|
824
1330
|
`${palette(process.stderr).warn("sentinel lint(oxlint): oxlint-tsgolint is not installed, so type-aware rules will NOT run")}
|
|
825
1331
|
`
|
|
826
1332
|
);
|
|
827
1333
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
let code = 1;
|
|
832
|
-
for (let pass = 0; pass < passes; pass += 1) {
|
|
833
|
-
const result = spawnSync(oxlint, args, {
|
|
1334
|
+
const env = { ...process.env, PATH: oxlintPath(ctx.cwd, process.env) };
|
|
1335
|
+
const lint = (extra) => {
|
|
1336
|
+
const result = spawnSync(oxlint, ["-c", LINT_CONFIG_FILE, ...extra, "."], {
|
|
834
1337
|
cwd: ctx.cwd,
|
|
835
1338
|
stdio: "inherit",
|
|
836
|
-
env
|
|
1339
|
+
env
|
|
837
1340
|
});
|
|
838
1341
|
if (result.error) {
|
|
839
1342
|
process.stderr.write(
|
|
840
1343
|
`sentinel lint(oxlint): could not run oxlint (${result.error.message})
|
|
841
1344
|
`
|
|
842
1345
|
);
|
|
843
|
-
return
|
|
1346
|
+
return 1;
|
|
1347
|
+
}
|
|
1348
|
+
return result.status ?? 1;
|
|
1349
|
+
};
|
|
1350
|
+
if (ctx.fix) {
|
|
1351
|
+
for (let pass = 0; pass < MAX_FIX_PASSES; pass += 1) {
|
|
1352
|
+
if (lint(["--fix"]) === 0) break;
|
|
844
1353
|
}
|
|
845
|
-
code = result.status ?? 1;
|
|
846
|
-
if (code === 0) break;
|
|
847
1354
|
}
|
|
1355
|
+
const code = lint(typeAware ? ["--type-aware"] : []);
|
|
848
1356
|
return { ok: code === 0, code };
|
|
849
1357
|
}
|
|
850
1358
|
/** The module's resolved lint configuration, and what it deliberately does not enforce. */
|
|
@@ -915,7 +1423,7 @@ var OxlintAdapter = class extends BaseAdapter {
|
|
|
915
1423
|
if (disabled.length === 0) return;
|
|
916
1424
|
process.stderr.write(
|
|
917
1425
|
`${palette(process.stderr).warn(
|
|
918
|
-
`sentinel lint(oxlint): ${disabled.length} rule(s) not enforced by default (${
|
|
1426
|
+
`sentinel lint(oxlint): ${disabled.length} rule(s) not enforced by default (${summariseByPlugin(disabled)}); run --inspect --lint for each rule and why`
|
|
919
1427
|
)}
|
|
920
1428
|
`
|
|
921
1429
|
);
|
|
@@ -1551,4 +2059,4 @@ export {
|
|
|
1551
2059
|
detectFramework,
|
|
1552
2060
|
dispatch
|
|
1553
2061
|
};
|
|
1554
|
-
//# sourceMappingURL=chunk-
|
|
2062
|
+
//# sourceMappingURL=chunk-EZCRU6XE.js.map
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": [
|
|
4
|
+
"typescript",
|
|
5
|
+
"react",
|
|
6
|
+
"jsx-a11y",
|
|
7
|
+
"jest",
|
|
8
|
+
"import",
|
|
9
|
+
"unicorn",
|
|
10
|
+
"oxc"
|
|
11
|
+
],
|
|
12
|
+
"env": {
|
|
13
|
+
"browser": true,
|
|
14
|
+
"es2022": true,
|
|
15
|
+
"node": true,
|
|
16
|
+
"jest": true
|
|
17
|
+
},
|
|
18
|
+
"categories": {
|
|
19
|
+
"correctness": "off",
|
|
20
|
+
"suspicious": "off",
|
|
21
|
+
"pedantic": "off",
|
|
22
|
+
"perf": "off",
|
|
23
|
+
"style": "off",
|
|
24
|
+
"restriction": "off",
|
|
25
|
+
"nursery": "off"
|
|
26
|
+
},
|
|
27
|
+
"ignorePatterns": [
|
|
28
|
+
"**/dist/**",
|
|
29
|
+
"**/node_modules/**",
|
|
30
|
+
"**/coverage/**",
|
|
31
|
+
"**/*.mock.ts"
|
|
32
|
+
],
|
|
33
|
+
"rules": {
|
|
34
|
+
"typescript/ban-ts-comment": "warn",
|
|
35
|
+
"typescript/no-array-constructor": "warn",
|
|
36
|
+
"typescript/no-duplicate-enum-values": "warn",
|
|
37
|
+
"typescript/no-empty-object-type": "warn",
|
|
38
|
+
"typescript/no-extra-non-null-assertion": "warn",
|
|
39
|
+
"typescript/no-misused-new": "warn",
|
|
40
|
+
"typescript/no-namespace": "warn",
|
|
41
|
+
"typescript/no-non-null-asserted-optional-chain": "warn",
|
|
42
|
+
"typescript/no-this-alias": "warn",
|
|
43
|
+
"typescript/no-unnecessary-type-constraint": "warn",
|
|
44
|
+
"typescript/no-unsafe-declaration-merging": "warn",
|
|
45
|
+
"typescript/no-unsafe-function-type": "warn",
|
|
46
|
+
"typescript/no-unused-expressions": [
|
|
47
|
+
"warn",
|
|
48
|
+
{
|
|
49
|
+
"allowShortCircuit": false,
|
|
50
|
+
"allowTaggedTemplates": false,
|
|
51
|
+
"allowTernary": false
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"typescript/no-wrapper-object-types": "warn",
|
|
55
|
+
"typescript/prefer-as-const": "warn",
|
|
56
|
+
"typescript/prefer-namespace-keyword": "warn",
|
|
57
|
+
"typescript/triple-slash-reference": "warn",
|
|
58
|
+
"no-var": "warn",
|
|
59
|
+
"prefer-const": [
|
|
60
|
+
"warn",
|
|
61
|
+
{
|
|
62
|
+
"destructuring": "any",
|
|
63
|
+
"ignoreReadBeforeAssign": false
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"prefer-rest-params": "warn",
|
|
67
|
+
"prefer-spread": "warn",
|
|
68
|
+
"curly": [
|
|
69
|
+
"error",
|
|
70
|
+
"all"
|
|
71
|
+
],
|
|
72
|
+
"for-direction": "error",
|
|
73
|
+
"no-async-promise-executor": "error",
|
|
74
|
+
"no-case-declarations": "error",
|
|
75
|
+
"no-compare-neg-zero": "error",
|
|
76
|
+
"no-cond-assign": [
|
|
77
|
+
"error",
|
|
78
|
+
"except-parens"
|
|
79
|
+
],
|
|
80
|
+
"no-constant-binary-expression": "error",
|
|
81
|
+
"no-constant-condition": [
|
|
82
|
+
"error",
|
|
83
|
+
{
|
|
84
|
+
"checkLoops": "allExceptWhileTrue"
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"no-control-regex": "error",
|
|
88
|
+
"no-debugger": "error",
|
|
89
|
+
"no-delete-var": "error",
|
|
90
|
+
"no-dupe-else-if": "error",
|
|
91
|
+
"no-duplicate-case": "error",
|
|
92
|
+
"no-empty": [
|
|
93
|
+
"error",
|
|
94
|
+
{
|
|
95
|
+
"allowEmptyCatch": false
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"no-empty-character-class": "error",
|
|
99
|
+
"no-empty-pattern": [
|
|
100
|
+
"error",
|
|
101
|
+
{
|
|
102
|
+
"allowObjectPatternsAsParameters": false
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"no-empty-static-block": "error",
|
|
106
|
+
"no-ex-assign": "error",
|
|
107
|
+
"no-extra-boolean-cast": [
|
|
108
|
+
"error",
|
|
109
|
+
{}
|
|
110
|
+
],
|
|
111
|
+
"no-fallthrough": [
|
|
112
|
+
"error",
|
|
113
|
+
{
|
|
114
|
+
"allowEmptyCase": false,
|
|
115
|
+
"reportUnusedFallthroughComment": false
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
"no-global-assign": [
|
|
119
|
+
"error",
|
|
120
|
+
{
|
|
121
|
+
"exceptions": []
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"no-invalid-regexp": [
|
|
125
|
+
"error",
|
|
126
|
+
{}
|
|
127
|
+
],
|
|
128
|
+
"no-irregular-whitespace": [
|
|
129
|
+
"error",
|
|
130
|
+
{
|
|
131
|
+
"skipComments": false,
|
|
132
|
+
"skipJSXText": false,
|
|
133
|
+
"skipRegExps": false,
|
|
134
|
+
"skipStrings": true,
|
|
135
|
+
"skipTemplates": false
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"no-loss-of-precision": "error",
|
|
139
|
+
"no-misleading-character-class": "error",
|
|
140
|
+
"no-nonoctal-decimal-escape": "error",
|
|
141
|
+
"no-prototype-builtins": "error",
|
|
142
|
+
"no-regex-spaces": "error",
|
|
143
|
+
"no-restricted-imports": [
|
|
144
|
+
"error",
|
|
145
|
+
{
|
|
146
|
+
"patterns": [
|
|
147
|
+
{
|
|
148
|
+
"group": [
|
|
149
|
+
"next/*"
|
|
150
|
+
],
|
|
151
|
+
"message": "Importing from next/* is not allowed in the admin project."
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"group": [
|
|
155
|
+
"storybook/*"
|
|
156
|
+
],
|
|
157
|
+
"message": "Importing from storybook/* is not allowed in the admin project."
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"group": [
|
|
161
|
+
"@nestjs/*"
|
|
162
|
+
],
|
|
163
|
+
"message": "Importing from @nestjs/* is not allowed in the admin project."
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"group": [
|
|
167
|
+
"next-i18next"
|
|
168
|
+
],
|
|
169
|
+
"message": "Importing from next-i18next is not allowed in the admin project."
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"group": [
|
|
173
|
+
"@hublo/style/colors",
|
|
174
|
+
"@hublo/style/colors/*"
|
|
175
|
+
],
|
|
176
|
+
"message": "Importing runtime colors directly is not allowed. Use the MUI theme, theme-derived CSS variables, or an explicit product-specific mapping."
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
"no-self-assign": [
|
|
182
|
+
"error",
|
|
183
|
+
{
|
|
184
|
+
"props": true
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
"no-shadow-restricted-names": [
|
|
188
|
+
"error",
|
|
189
|
+
{
|
|
190
|
+
"reportGlobalThis": false
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
"no-sparse-arrays": "error",
|
|
194
|
+
"no-unexpected-multiline": "error",
|
|
195
|
+
"no-unsafe-finally": "error",
|
|
196
|
+
"no-unused-labels": "error",
|
|
197
|
+
"no-unused-private-class-members": "error",
|
|
198
|
+
"no-useless-backreference": "error",
|
|
199
|
+
"no-useless-catch": "error",
|
|
200
|
+
"no-useless-escape": [
|
|
201
|
+
"error",
|
|
202
|
+
{
|
|
203
|
+
"allowRegexCharacters": []
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
"require-yield": "error",
|
|
207
|
+
"use-isnan": [
|
|
208
|
+
"error",
|
|
209
|
+
{
|
|
210
|
+
"enforceForIndexOf": false,
|
|
211
|
+
"enforceForSwitchCase": true
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
"valid-typeof": [
|
|
215
|
+
"error",
|
|
216
|
+
{
|
|
217
|
+
"requireStringLiterals": false
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
"styled-components-a11y/alt-text": "error",
|
|
221
|
+
"styled-components-a11y/anchor-has-content": "error",
|
|
222
|
+
"styled-components-a11y/anchor-is-valid": "error",
|
|
223
|
+
"styled-components-a11y/aria-activedescendant-has-tabindex": "error",
|
|
224
|
+
"styled-components-a11y/aria-props": "error",
|
|
225
|
+
"styled-components-a11y/aria-proptypes": "error",
|
|
226
|
+
"styled-components-a11y/aria-role": "error",
|
|
227
|
+
"styled-components-a11y/aria-unsupported-elements": "error",
|
|
228
|
+
"styled-components-a11y/autocomplete-valid": "error",
|
|
229
|
+
"styled-components-a11y/click-events-have-key-events": "error",
|
|
230
|
+
"styled-components-a11y/control-has-associated-label": [
|
|
231
|
+
"off",
|
|
232
|
+
{
|
|
233
|
+
"ignoreElements": [
|
|
234
|
+
"audio",
|
|
235
|
+
"canvas",
|
|
236
|
+
"embed",
|
|
237
|
+
"input",
|
|
238
|
+
"textarea",
|
|
239
|
+
"tr",
|
|
240
|
+
"video"
|
|
241
|
+
],
|
|
242
|
+
"ignoreRoles": [
|
|
243
|
+
"grid",
|
|
244
|
+
"listbox",
|
|
245
|
+
"menu",
|
|
246
|
+
"menubar",
|
|
247
|
+
"radiogroup",
|
|
248
|
+
"row",
|
|
249
|
+
"tablist",
|
|
250
|
+
"toolbar",
|
|
251
|
+
"tree",
|
|
252
|
+
"treegrid"
|
|
253
|
+
],
|
|
254
|
+
"includeRoles": [
|
|
255
|
+
"alert",
|
|
256
|
+
"dialog"
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
],
|
|
260
|
+
"styled-components-a11y/heading-has-content": "error",
|
|
261
|
+
"styled-components-a11y/html-has-lang": "error",
|
|
262
|
+
"styled-components-a11y/iframe-has-title": "error",
|
|
263
|
+
"styled-components-a11y/img-redundant-alt": "error",
|
|
264
|
+
"styled-components-a11y/interactive-supports-focus": [
|
|
265
|
+
"error",
|
|
266
|
+
{
|
|
267
|
+
"tabbable": [
|
|
268
|
+
"button",
|
|
269
|
+
"checkbox",
|
|
270
|
+
"link",
|
|
271
|
+
"progressbar",
|
|
272
|
+
"searchbox",
|
|
273
|
+
"slider",
|
|
274
|
+
"spinbutton",
|
|
275
|
+
"switch",
|
|
276
|
+
"textbox"
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
],
|
|
280
|
+
"styled-components-a11y/label-has-associated-control": "error",
|
|
281
|
+
"styled-components-a11y/media-has-caption": "error",
|
|
282
|
+
"styled-components-a11y/mouse-events-have-key-events": "error",
|
|
283
|
+
"styled-components-a11y/no-access-key": "error",
|
|
284
|
+
"styled-components-a11y/no-autofocus": "error",
|
|
285
|
+
"styled-components-a11y/no-distracting-elements": "error",
|
|
286
|
+
"styled-components-a11y/no-interactive-element-to-noninteractive-role": "error",
|
|
287
|
+
"styled-components-a11y/no-noninteractive-element-interactions": [
|
|
288
|
+
"error",
|
|
289
|
+
{
|
|
290
|
+
"body": [
|
|
291
|
+
"onError",
|
|
292
|
+
"onLoad"
|
|
293
|
+
],
|
|
294
|
+
"iframe": [
|
|
295
|
+
"onError",
|
|
296
|
+
"onLoad"
|
|
297
|
+
],
|
|
298
|
+
"img": [
|
|
299
|
+
"onError",
|
|
300
|
+
"onLoad"
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
],
|
|
304
|
+
"styled-components-a11y/no-noninteractive-element-to-interactive-role": "error",
|
|
305
|
+
"styled-components-a11y/no-noninteractive-tabindex": "error",
|
|
306
|
+
"styled-components-a11y/no-redundant-roles": "error",
|
|
307
|
+
"styled-components-a11y/no-static-element-interactions": "error",
|
|
308
|
+
"styled-components-a11y/role-has-required-aria-props": "error",
|
|
309
|
+
"styled-components-a11y/role-supports-aria-props": "error",
|
|
310
|
+
"styled-components-a11y/scope": "error",
|
|
311
|
+
"styled-components-a11y/tabindex-no-positive": "error",
|
|
312
|
+
"jsx-a11y/alt-text": "error",
|
|
313
|
+
"jsx-a11y/anchor-has-content": "error",
|
|
314
|
+
"jsx-a11y/anchor-is-valid": "error",
|
|
315
|
+
"jsx-a11y/aria-activedescendant-has-tabindex": "error",
|
|
316
|
+
"jsx-a11y/aria-props": "error",
|
|
317
|
+
"jsx-a11y/aria-proptypes": "error",
|
|
318
|
+
"jsx-a11y/aria-role": "error",
|
|
319
|
+
"jsx-a11y/aria-unsupported-elements": "error",
|
|
320
|
+
"jsx-a11y/autocomplete-valid": "error",
|
|
321
|
+
"jsx-a11y/click-events-have-key-events": "error",
|
|
322
|
+
"jsx-a11y/heading-has-content": "error",
|
|
323
|
+
"jsx-a11y/html-has-lang": "error",
|
|
324
|
+
"jsx-a11y/iframe-has-title": "error",
|
|
325
|
+
"jsx-a11y/img-redundant-alt": "off",
|
|
326
|
+
"jsx-a11y/interactive-supports-focus": [
|
|
327
|
+
"error",
|
|
328
|
+
{
|
|
329
|
+
"tabbable": [
|
|
330
|
+
"button",
|
|
331
|
+
"checkbox",
|
|
332
|
+
"link",
|
|
333
|
+
"progressbar",
|
|
334
|
+
"searchbox",
|
|
335
|
+
"slider",
|
|
336
|
+
"spinbutton",
|
|
337
|
+
"switch",
|
|
338
|
+
"textbox"
|
|
339
|
+
]
|
|
340
|
+
}
|
|
341
|
+
],
|
|
342
|
+
"jsx-a11y/label-has-associated-control": "error",
|
|
343
|
+
"jsx-a11y/media-has-caption": "error",
|
|
344
|
+
"jsx-a11y/mouse-events-have-key-events": "error",
|
|
345
|
+
"jsx-a11y/no-access-key": "error",
|
|
346
|
+
"jsx-a11y/no-autofocus": "error",
|
|
347
|
+
"jsx-a11y/no-distracting-elements": "error",
|
|
348
|
+
"jsx-a11y/no-interactive-element-to-noninteractive-role": "error",
|
|
349
|
+
"jsx-a11y/no-noninteractive-element-interactions": [
|
|
350
|
+
"error",
|
|
351
|
+
{
|
|
352
|
+
"body": [
|
|
353
|
+
"onError",
|
|
354
|
+
"onLoad"
|
|
355
|
+
],
|
|
356
|
+
"iframe": [
|
|
357
|
+
"onError",
|
|
358
|
+
"onLoad"
|
|
359
|
+
],
|
|
360
|
+
"img": [
|
|
361
|
+
"onError",
|
|
362
|
+
"onLoad"
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
],
|
|
366
|
+
"jsx-a11y/no-noninteractive-element-to-interactive-role": "error",
|
|
367
|
+
"jsx-a11y/no-noninteractive-tabindex": "error",
|
|
368
|
+
"jsx-a11y/no-redundant-roles": "error",
|
|
369
|
+
"jsx-a11y/no-static-element-interactions": "error",
|
|
370
|
+
"jsx-a11y/role-has-required-aria-props": "error",
|
|
371
|
+
"jsx-a11y/role-supports-aria-props": "error",
|
|
372
|
+
"jsx-a11y/scope": "error",
|
|
373
|
+
"jsx-a11y/tabindex-no-positive": "error",
|
|
374
|
+
"typescript/await-thenable": "error",
|
|
375
|
+
"typescript/no-array-delete": "error",
|
|
376
|
+
"typescript/no-base-to-string": "error",
|
|
377
|
+
"typescript/no-duplicate-type-constituents": "error",
|
|
378
|
+
"typescript/no-floating-promises": [
|
|
379
|
+
"error",
|
|
380
|
+
{
|
|
381
|
+
"ignoreVoid": true
|
|
382
|
+
}
|
|
383
|
+
],
|
|
384
|
+
"typescript/no-for-in-array": "error",
|
|
385
|
+
"typescript/no-implied-eval": "error",
|
|
386
|
+
"typescript/no-inferrable-types": "error",
|
|
387
|
+
"typescript/no-misused-promises": [
|
|
388
|
+
"error",
|
|
389
|
+
{
|
|
390
|
+
"checksVoidReturn": {
|
|
391
|
+
"attributes": false
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
],
|
|
395
|
+
"typescript/no-non-null-assertion": "warn",
|
|
396
|
+
"typescript/no-redundant-type-constituents": "error",
|
|
397
|
+
"typescript/no-require-imports": "error",
|
|
398
|
+
"typescript/no-unsafe-unary-minus": "error",
|
|
399
|
+
"typescript/prefer-promise-reject-errors": "error",
|
|
400
|
+
"typescript/restrict-plus-operands": "error",
|
|
401
|
+
"typescript/restrict-template-expressions": "error",
|
|
402
|
+
"typescript/switch-exhaustiveness-check": "warn",
|
|
403
|
+
"typescript/unbound-method": "error",
|
|
404
|
+
"react-hooks/exhaustive-deps": "warn",
|
|
405
|
+
"react-hooks/rules-of-hooks": "error",
|
|
406
|
+
"no-barrel-files/no-barrel-files": "warn",
|
|
407
|
+
"nx/enforce-module-boundaries": [
|
|
408
|
+
"error",
|
|
409
|
+
{
|
|
410
|
+
"enforceBuildableLibDependency": true,
|
|
411
|
+
"allow": [],
|
|
412
|
+
"depConstraints": [
|
|
413
|
+
{
|
|
414
|
+
"sourceTag": "*",
|
|
415
|
+
"onlyDependOnLibsWithTags": [
|
|
416
|
+
"*"
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
],
|
|
422
|
+
"import-js/order": [
|
|
423
|
+
"error",
|
|
424
|
+
{
|
|
425
|
+
"newlines-between": "always",
|
|
426
|
+
"groups": [
|
|
427
|
+
"builtin",
|
|
428
|
+
"external",
|
|
429
|
+
"internal",
|
|
430
|
+
[
|
|
431
|
+
"parent",
|
|
432
|
+
"index"
|
|
433
|
+
],
|
|
434
|
+
"sibling"
|
|
435
|
+
],
|
|
436
|
+
"alphabetize": {
|
|
437
|
+
"order": "asc"
|
|
438
|
+
},
|
|
439
|
+
"pathGroups": [
|
|
440
|
+
{
|
|
441
|
+
"pattern": "@nestjs/**",
|
|
442
|
+
"group": "external"
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
"pattern": "{@admin/**,@authentication/**,@featureToggles/**,@institution/**,@hublo/**,@network/**,@mission/**,@notification/**,@shared/**,@talent/**,@worker/**,@contract/**,@config/**,@front/**}",
|
|
446
|
+
"group": "internal",
|
|
447
|
+
"position": "before"
|
|
448
|
+
}
|
|
449
|
+
],
|
|
450
|
+
"pathGroupsExcludedImportTypes": [
|
|
451
|
+
"builtin"
|
|
452
|
+
]
|
|
453
|
+
}
|
|
454
|
+
],
|
|
455
|
+
"react-refresh/only-export-components": [
|
|
456
|
+
"warn",
|
|
457
|
+
{
|
|
458
|
+
"allowConstantExport": true
|
|
459
|
+
}
|
|
460
|
+
],
|
|
461
|
+
"jest/expect-expect": "off",
|
|
462
|
+
"jest/no-alias-methods": "off",
|
|
463
|
+
"jest/no-commented-out-tests": "off",
|
|
464
|
+
"jest/no-conditional-expect": "off",
|
|
465
|
+
"jest/no-deprecated-functions": "off",
|
|
466
|
+
"jest/no-disabled-tests": "off",
|
|
467
|
+
"jest/no-done-callback": "off",
|
|
468
|
+
"jest/no-export": "off",
|
|
469
|
+
"jest/no-focused-tests": "off",
|
|
470
|
+
"jest/no-identical-title": "off",
|
|
471
|
+
"jest/no-interpolation-in-snapshots": "off",
|
|
472
|
+
"jest/no-jasmine-globals": "off",
|
|
473
|
+
"jest/no-mocks-import": "off",
|
|
474
|
+
"jest/no-standalone-expect": "off",
|
|
475
|
+
"jest/no-test-prefixes": "off",
|
|
476
|
+
"jest/prefer-to-be": "off",
|
|
477
|
+
"jest/prefer-to-contain": "off",
|
|
478
|
+
"jest/prefer-to-have-length": "off",
|
|
479
|
+
"jest/valid-describe-callback": "off",
|
|
480
|
+
"jest/valid-expect": "off",
|
|
481
|
+
"jest/valid-expect-in-promise": "off",
|
|
482
|
+
"jest/valid-title": "off",
|
|
483
|
+
"@tanstack/query/exhaustive-deps": "off",
|
|
484
|
+
"@tanstack/query/infinite-query-property-order": "off",
|
|
485
|
+
"@tanstack/query/mutation-property-order": "off",
|
|
486
|
+
"@tanstack/query/no-rest-destructuring": "off",
|
|
487
|
+
"@tanstack/query/no-unstable-deps": "off",
|
|
488
|
+
"@tanstack/query/no-void-query-fn": "off",
|
|
489
|
+
"@tanstack/query/stable-query-client": "off",
|
|
490
|
+
"typescript/no-empty-function": "off",
|
|
491
|
+
"typescript/no-explicit-any": "off",
|
|
492
|
+
"typescript/no-unnecessary-type-assertion": "off",
|
|
493
|
+
"typescript/no-unsafe-argument": "off",
|
|
494
|
+
"typescript/no-unsafe-assignment": "off",
|
|
495
|
+
"typescript/no-unsafe-call": "off",
|
|
496
|
+
"typescript/no-unsafe-enum-comparison": "off",
|
|
497
|
+
"typescript/no-unsafe-member-access": "off",
|
|
498
|
+
"typescript/no-unsafe-return": "off",
|
|
499
|
+
"typescript/no-unused-vars": "off",
|
|
500
|
+
"typescript/only-throw-error": "off",
|
|
501
|
+
"typescript/require-await": "off",
|
|
502
|
+
"no-console": "off",
|
|
503
|
+
"no-unsafe-optional-chaining": "off",
|
|
504
|
+
"react/jsx-curly-brace-presence": "off",
|
|
505
|
+
"styled-components-a11y/label-has-for": "off",
|
|
506
|
+
"hublo/no-palette-common": "off"
|
|
507
|
+
},
|
|
508
|
+
"jsPlugins": [
|
|
509
|
+
"eslint-plugin-no-barrel-files",
|
|
510
|
+
{
|
|
511
|
+
"name": "nx",
|
|
512
|
+
"specifier": "@nx/eslint-plugin"
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
"name": "import-js",
|
|
516
|
+
"specifier": "eslint-plugin-import"
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"name": "@tanstack/query",
|
|
520
|
+
"specifier": "@tanstack/eslint-plugin-query"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"name": "styled-components-a11y",
|
|
524
|
+
"specifier": "eslint-plugin-styled-components-a11y"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
"name": "react-refresh",
|
|
528
|
+
"specifier": "eslint-plugin-react-refresh"
|
|
529
|
+
},
|
|
530
|
+
"../plugins/hublo.js"
|
|
531
|
+
],
|
|
532
|
+
"settings": {
|
|
533
|
+
"import/extensions": [
|
|
534
|
+
".ts",
|
|
535
|
+
".cts",
|
|
536
|
+
".mts",
|
|
537
|
+
".tsx",
|
|
538
|
+
".js",
|
|
539
|
+
".jsx",
|
|
540
|
+
".mjs",
|
|
541
|
+
".cjs"
|
|
542
|
+
],
|
|
543
|
+
"import/external-module-folders": [
|
|
544
|
+
"node_modules",
|
|
545
|
+
"node_modules/@types"
|
|
546
|
+
],
|
|
547
|
+
"import/parsers": {
|
|
548
|
+
"@typescript-eslint/parser": [
|
|
549
|
+
".ts",
|
|
550
|
+
".cts",
|
|
551
|
+
".mts",
|
|
552
|
+
".tsx"
|
|
553
|
+
]
|
|
554
|
+
},
|
|
555
|
+
"import/resolver": {
|
|
556
|
+
"node": {
|
|
557
|
+
"extensions": [
|
|
558
|
+
".ts",
|
|
559
|
+
".cts",
|
|
560
|
+
".mts",
|
|
561
|
+
".tsx",
|
|
562
|
+
".js",
|
|
563
|
+
".jsx",
|
|
564
|
+
".mjs",
|
|
565
|
+
".cjs"
|
|
566
|
+
]
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hublo/sentinel",
|
|
3
|
-
"version": "1.1.0-alpha.
|
|
3
|
+
"version": "1.1.0-alpha.2",
|
|
4
4
|
"description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"files": [
|
|
37
37
|
"dist",
|
|
38
38
|
"oxlint",
|
|
39
|
+
"plugins",
|
|
39
40
|
"!dist/**/*.map"
|
|
40
41
|
],
|
|
41
42
|
"dependencies": {
|
package/plugins/hublo.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local oxlint plugin: Hublo house rules that have no equivalent anywhere else.
|
|
3
|
+
*
|
|
4
|
+
* oxlint has no `no-restricted-syntax`, so the eight AST selectors that used to live in
|
|
5
|
+
* `restricted-syntax.cjs` cannot be carried across as configuration. They are expressed
|
|
6
|
+
* here as a real rule instead, which is what oxlint's JS plugin API is for.
|
|
7
|
+
*
|
|
8
|
+
* It ships INSIDE sentinel rather than being copied into each module. A house rule is code,
|
|
9
|
+
* not configuration: a per-module copy drifts, and drifting house rules are the problem
|
|
10
|
+
* sentinel exists to remove. The cost is that `@hublo/sentinel` carries a rule about Hublo's
|
|
11
|
+
* colour palette, which is honest for a package named after the company.
|
|
12
|
+
*
|
|
13
|
+
* The original selectors, and where each is handled below:
|
|
14
|
+
* 1-2 MemberExpression[object.property.name='palette'][property.name='common'] theme.palette.common
|
|
15
|
+
* …[computed=true][property.value='common'] theme.palette['common']
|
|
16
|
+
* 3-4 MemberExpression[object.name='palette']… palette.common
|
|
17
|
+
* 5-6 Property[key.name|key.value=/<colour props>/] > Literal[value=/^common\./] { color: 'common.white' }
|
|
18
|
+
* 7-8 JSXAttribute[name.name=/<colour props>/] > Literal | JSXExpressionContainer color="common.white"
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const MESSAGE =
|
|
22
|
+
'Do not use palette.common or common.* color paths in product code. Use semantic palette tokens instead.'
|
|
23
|
+
|
|
24
|
+
/** The MUI props that take a colour path, so `common.*` in them is the same mistake. */
|
|
25
|
+
const COLOUR_PROPS =
|
|
26
|
+
/^(color|bgcolor|background|backgroundColor|borderColor|borderTopColor|borderRightColor|borderBottomColor|borderLeftColor|outlineColor|fill|stroke|textDecorationColor|caretColor|accentColor)$/
|
|
27
|
+
|
|
28
|
+
/** `common.something` as a string value, e.g. `{ color: 'common.white' }`. */
|
|
29
|
+
const isCommonPath = (node) =>
|
|
30
|
+
node?.type === 'Literal' && typeof node.value === 'string' && /^common\./.test(node.value)
|
|
31
|
+
|
|
32
|
+
const plugin = {
|
|
33
|
+
meta: { name: 'hublo' },
|
|
34
|
+
rules: {
|
|
35
|
+
'no-palette-common': {
|
|
36
|
+
meta: {
|
|
37
|
+
docs: {
|
|
38
|
+
description: 'Ban palette.common and common.* colour paths in product code.',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
create(context) {
|
|
42
|
+
const report = (node) => context.report({ node, message: MESSAGE })
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
// 1-4: reaching `common` off a palette, by property or by computed key.
|
|
46
|
+
MemberExpression(node) {
|
|
47
|
+
const onPalette =
|
|
48
|
+
node.object?.property?.name === 'palette' || node.object?.name === 'palette'
|
|
49
|
+
if (!onPalette) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
const reachesCommon =
|
|
53
|
+
node.property?.name === 'common' ||
|
|
54
|
+
(node.computed && node.property?.value === 'common')
|
|
55
|
+
if (reachesCommon) {
|
|
56
|
+
report(node)
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// 5-6: `{ color: 'common.white' }` in an object literal, key written either way.
|
|
61
|
+
Property(node) {
|
|
62
|
+
const key = node.key?.name ?? node.key?.value
|
|
63
|
+
if (typeof key === 'string' && COLOUR_PROPS.test(key) && isCommonPath(node.value)) {
|
|
64
|
+
report(node.value)
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// 7-8: `color="common.white"` and `color={'common.white'}` on a JSX element.
|
|
69
|
+
JSXAttribute(node) {
|
|
70
|
+
const name = node.name?.name
|
|
71
|
+
if (typeof name !== 'string' || !COLOUR_PROPS.test(name)) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
const value = node.value
|
|
75
|
+
if (isCommonPath(value)) {
|
|
76
|
+
return report(value)
|
|
77
|
+
}
|
|
78
|
+
if (value?.type === 'JSXExpressionContainer' && isCommonPath(value.expression)) {
|
|
79
|
+
report(value.expression)
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default plugin
|