@isentinel/eslint-config 6.0.0-beta.34 → 6.0.0-beta.36

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/cli.mjs CHANGED
@@ -9,7 +9,7 @@ import path from "node:path";
9
9
  import { existsSync, readFileSync } from "fs";
10
10
  import { execSync } from "node:child_process";
11
11
  //#region package.json
12
- var version = "6.0.0-beta.34";
12
+ var version = "6.0.0-beta.36";
13
13
  var package_default = {
14
14
  name: "@isentinel/eslint-config",
15
15
  version,
package/dist/index.d.mts CHANGED
@@ -26933,7 +26933,7 @@ type ConfigSettings = NonNullable<Linter.Config["settings"]> & {
26933
26933
  * Package name for DOM Testing Library.
26934
26934
  *
26935
26935
  * When React testing support is enabled, importing this package is
26936
- * forbidden in favor of `@packages/react-testing-library-lua`.
26936
+ * forbidden in favor of `react-testing-library-lua`.
26937
26937
  */
26938
26938
  domPackage?: string;
26939
26939
  };
package/dist/index.mjs CHANGED
@@ -682,6 +682,218 @@ function isRecord(value) {
682
682
  return typeof value === "object" && value !== null && !Array.isArray(value);
683
683
  }
684
684
  //#endregion
685
+ //#region src/rules/react.ts
686
+ const DOM_IMPORT_MESSAGE = "Import from react-testing-library-lua instead; it re-exports the DOM utilities, and eslint-plugin-testing-library only detects one module per file.";
687
+ /**
688
+ * Build the direct DOM Testing Library import restriction shared by both
689
+ * engines.
690
+ *
691
+ * @param domPackage - Package name for DOM Testing Library.
692
+ * @returns The configured restriction, or `undefined` when no package is set.
693
+ */
694
+ function restrictedDomImportRule(domPackage) {
695
+ if (domPackage === void 0) return;
696
+ return ["error", { paths: [domImportRestriction(domPackage)] }];
697
+ }
698
+ /**
699
+ * Merge the direct DOM Testing Library restriction into an existing
700
+ * `no-restricted-imports` entry.
701
+ *
702
+ * The existing entry owns the severity and any duplicate restriction. An
703
+ * explicit disabled entry remains disabled.
704
+ *
705
+ * @param rule - Existing rule entry.
706
+ * @param domPackage - Package name for DOM Testing Library.
707
+ * @returns The composed rule entry.
708
+ */
709
+ function mergeRestrictedDomImportRule(rule, domPackage) {
710
+ if (domPackage === void 0 || rule === "off" || rule === 0) return rule;
711
+ if (rule === void 0) return restrictedDomImportRule(domPackage);
712
+ const parts = Array.isArray(rule) ? [...rule] : [rule];
713
+ const [severity, ...options] = parts;
714
+ if (severity === "off" || severity === 0) return rule;
715
+ if (severity !== "error" && severity !== "warn" && severity !== 1 && severity !== 2) return rule;
716
+ const [firstOption] = options;
717
+ if (options.length === 1 && isObjectStyleRestriction(firstOption)) {
718
+ const paths = Array.isArray(firstOption["paths"]) ? firstOption["paths"] : [];
719
+ if (paths.some((path) => isDomImportRestriction(path, domPackage))) return rule;
720
+ return [severity, {
721
+ ...firstOption,
722
+ paths: [...paths, domImportRestriction(domPackage)]
723
+ }];
724
+ }
725
+ if (options.some((option) => isDomImportRestriction(option, domPackage))) return rule;
726
+ if (options.length === 0) return [severity, { paths: [domImportRestriction(domPackage)] }];
727
+ return [...parts, domImportRestriction(domPackage)];
728
+ }
729
+ /**
730
+ * React rules shared between the ESLint and oxlint factories.
731
+ *
732
+ * The type-aware react rules stay in the ESLint config (oxlint jsPlugins have
733
+ * no type information).
734
+ *
735
+ * @param options - Shared rule options.
736
+ * @returns The rule map.
737
+ */
738
+ function reactRules({ domPackage, filenameCase = "kebabCase", reactCompiler = true, stylistic = true, testing = false } = {}) {
739
+ const restrictedDomImport = testing ? restrictedDomImportRule(domPackage) : void 0;
740
+ return {
741
+ "flawless/no-unnecessary-use-callback": "error",
742
+ "flawless/no-unnecessary-use-memo": "error",
743
+ "flawless/purity": "error",
744
+ "small-rules/react-hooks-strict-return": "error",
745
+ ...reactCompiler ? {
746
+ "react/globals": "error",
747
+ "react/refs": "error"
748
+ } : {},
749
+ "react-jsx/no-children-prop": "warn",
750
+ "react-jsx/no-children-prop-with-children": "error",
751
+ "react-jsx/no-comment-textnodes": "off",
752
+ "react-jsx/no-key-after-spread": "error",
753
+ "react-jsx/no-leaked-dollar": "warn",
754
+ "react-jsx/no-leaked-semicolon": "warn",
755
+ "react/error-boundaries": "error",
756
+ "react/exhaustive-deps": "error",
757
+ "react/immutability": "error",
758
+ "react/no-access-state-in-setstate": "error",
759
+ "react/no-array-index-key": "warn",
760
+ "react/no-children-count": "warn",
761
+ "react/no-children-for-each": "warn",
762
+ "react/no-children-map": "warn",
763
+ "react/no-children-only": "warn",
764
+ "react/no-children-to-array": "warn",
765
+ "react/no-class-component": "error",
766
+ "react/no-clone-element": "warn",
767
+ "react/no-component-will-mount": "error",
768
+ "react/no-component-will-receive-props": "error",
769
+ "react/no-component-will-update": "error",
770
+ "react/no-create-ref": "error",
771
+ "react/no-direct-mutation-state": "error",
772
+ "react/no-duplicate-key": "error",
773
+ "react/no-forward-ref": "off",
774
+ "react/no-missing-component-display-name": "error",
775
+ "react/no-missing-context-display-name": "error",
776
+ "react/no-missing-key": "error",
777
+ "react/no-misused-capture-owner-stack": "off",
778
+ "react/no-nested-component-definitions": "warn",
779
+ "react/no-nested-lazy-component-declarations": "warn",
780
+ "react/no-set-state-in-component-did-mount": "warn",
781
+ "react/no-set-state-in-component-did-update": "warn",
782
+ "react/no-set-state-in-component-will-update": "warn",
783
+ "react/no-unnecessary-use-prefix": "error",
784
+ "react/no-unsafe-component-will-mount": "error",
785
+ "react/no-unsafe-component-will-receive-props": "error",
786
+ "react/no-unsafe-component-will-update": "error",
787
+ "react/no-unstable-context-value": "error",
788
+ "react/no-unstable-default-props": ["error", { safeDefaultProps: [
789
+ "Axes",
790
+ "BrickColor",
791
+ "CatalogSearchParams",
792
+ "CFrame",
793
+ "Color3",
794
+ "ColorSequence",
795
+ "CFrame",
796
+ "Content",
797
+ "DateTime",
798
+ "DockWidgetPluginGuiInfo",
799
+ "Enum",
800
+ "Faces",
801
+ "FloatCurveKey",
802
+ "Font",
803
+ "NumberRange",
804
+ "NumberSequence",
805
+ "NumberSequenceKeypoint",
806
+ "OverlapParams",
807
+ "Path2DControlPoint",
808
+ "PathWaypoint",
809
+ "PhysicalProperties",
810
+ "Ray",
811
+ "RaycastParams",
812
+ "Rect",
813
+ "Region3",
814
+ "Region3int16",
815
+ "RotationCurveKey",
816
+ "SecurityCapabilities",
817
+ "TweenInfo",
818
+ "UDim",
819
+ "UDim2",
820
+ "ValueCurveKey",
821
+ "Vector2",
822
+ "Vector3",
823
+ "Vector3int16"
824
+ ] }],
825
+ "react/no-unused-class-component-members": "off",
826
+ "react/no-unused-state": "error",
827
+ "react/no-use-context": "off",
828
+ "react/rules-of-hooks": "error",
829
+ "react/set-state-in-effect": "error",
830
+ "react/set-state-in-render": "error",
831
+ "react/static-components": "error",
832
+ "react/use-memo": "error",
833
+ "react/use-state": ["error", {
834
+ enforceAssignment: false,
835
+ enforceSetterName: false
836
+ }],
837
+ ...testing ? {
838
+ "testing-library/await-async-queries": "error",
839
+ "testing-library/await-async-utils": "error",
840
+ "testing-library/no-await-sync-events": ["error", { eventModules: ["fire-event"] }],
841
+ "testing-library/no-await-sync-queries": "error",
842
+ "testing-library/no-container": "error",
843
+ "testing-library/no-debugging-utils": "error",
844
+ "testing-library/no-manual-cleanup": "error",
845
+ "testing-library/no-promise-in-fire-event": "error",
846
+ "testing-library/no-render-in-lifecycle": "error",
847
+ "testing-library/no-unnecessary-act": "error",
848
+ "testing-library/no-wait-for-multiple-assertions": "error",
849
+ "testing-library/no-wait-for-side-effects": "error",
850
+ "testing-library/no-wait-for-snapshot": "error",
851
+ "testing-library/prefer-explicit-assert": "error",
852
+ "testing-library/prefer-find-by": "error",
853
+ "testing-library/prefer-presence-queries": "error",
854
+ "testing-library/prefer-query-by-disappearance": "error",
855
+ "testing-library/prefer-screen-queries": "error",
856
+ "testing-library/render-result-naming-convention": "error",
857
+ ...restrictedDomImport !== void 0 ? { "no-restricted-imports": restrictedDomImport } : {}
858
+ } : {},
859
+ ...stylistic !== false ? {
860
+ "flawless/jsx-shorthand-boolean": "warn",
861
+ "flawless/jsx-shorthand-fragment": "warn",
862
+ "flawless/prefer-destructuring-assignment": "error",
863
+ "flawless/react-namespace": "error",
864
+ "one-var": "off",
865
+ "react-jsx/no-useless-fragment": "warn",
866
+ "react-naming-convention/context-name": "error",
867
+ "react-naming-convention/ref-name": "error",
868
+ "react/use-state": "error",
869
+ "style/jsx-curly-brace-presence": ["error", {
870
+ children: "never",
871
+ propElementValues: "always",
872
+ props: "never"
873
+ }],
874
+ "style/jsx-newline": "error",
875
+ "style/jsx-self-closing-comp": "error",
876
+ "unicorn/filename-case": ["error", {
877
+ case: filenameCase,
878
+ ignore: ["^[A-Z0-9]+.md$"],
879
+ multipleFileExtensions: true
880
+ }]
881
+ } : {}
882
+ };
883
+ }
884
+ function domImportRestriction(domPackage) {
885
+ return {
886
+ name: domPackage,
887
+ message: DOM_IMPORT_MESSAGE
888
+ };
889
+ }
890
+ function isDomImportRestriction(value, domPackage) {
891
+ return value === domPackage || isRecord(value) && "name" in value && value["name"] === domPackage;
892
+ }
893
+ function isObjectStyleRestriction(value) {
894
+ return isRecord(value) && !("name" in value);
895
+ }
896
+ //#endregion
685
897
  //#region src/utils.ts
686
898
  const require$2 = createRequire(import.meta.url);
687
899
  /**
@@ -3576,179 +3788,6 @@ function promise() {
3576
3788
  }];
3577
3789
  }
3578
3790
  //#endregion
3579
- //#region src/rules/react.ts
3580
- /**
3581
- * Build the direct DOM Testing Library import restriction shared by both
3582
- * engines.
3583
- *
3584
- * @param domPackage - Package name for DOM Testing Library.
3585
- * @returns The configured restriction, or `undefined` when no package is set.
3586
- */
3587
- function restrictedDomImportRule(domPackage) {
3588
- if (domPackage === void 0) return;
3589
- return ["error", { paths: [{
3590
- name: domPackage,
3591
- message: "Import from @packages/react-testing-library-lua instead; it re-exports the DOM utilities, and eslint-plugin-testing-library only detects one module per file."
3592
- }] }];
3593
- }
3594
- /**
3595
- * React rules shared between the ESLint and oxlint factories.
3596
- *
3597
- * The type-aware react rules stay in the ESLint config (oxlint jsPlugins have
3598
- * no type information).
3599
- *
3600
- * @param options - Shared rule options.
3601
- * @returns The rule map.
3602
- */
3603
- function reactRules({ domPackage, filenameCase = "kebabCase", reactCompiler = true, stylistic = true, testing = false } = {}) {
3604
- const restrictedDomImport = testing ? restrictedDomImportRule(domPackage) : void 0;
3605
- return {
3606
- "flawless/no-unnecessary-use-callback": "error",
3607
- "flawless/no-unnecessary-use-memo": "error",
3608
- "flawless/purity": "error",
3609
- "small-rules/react-hooks-strict-return": "error",
3610
- ...reactCompiler ? {
3611
- "react/globals": "error",
3612
- "react/refs": "error"
3613
- } : {},
3614
- "react-jsx/no-children-prop": "warn",
3615
- "react-jsx/no-children-prop-with-children": "error",
3616
- "react-jsx/no-comment-textnodes": "off",
3617
- "react-jsx/no-key-after-spread": "error",
3618
- "react-jsx/no-leaked-dollar": "warn",
3619
- "react-jsx/no-leaked-semicolon": "warn",
3620
- "react/error-boundaries": "error",
3621
- "react/exhaustive-deps": "error",
3622
- "react/immutability": "error",
3623
- "react/no-access-state-in-setstate": "error",
3624
- "react/no-array-index-key": "warn",
3625
- "react/no-children-count": "warn",
3626
- "react/no-children-for-each": "warn",
3627
- "react/no-children-map": "warn",
3628
- "react/no-children-only": "warn",
3629
- "react/no-children-to-array": "warn",
3630
- "react/no-class-component": "error",
3631
- "react/no-clone-element": "warn",
3632
- "react/no-component-will-mount": "error",
3633
- "react/no-component-will-receive-props": "error",
3634
- "react/no-component-will-update": "error",
3635
- "react/no-create-ref": "error",
3636
- "react/no-direct-mutation-state": "error",
3637
- "react/no-duplicate-key": "error",
3638
- "react/no-forward-ref": "off",
3639
- "react/no-missing-component-display-name": "error",
3640
- "react/no-missing-context-display-name": "error",
3641
- "react/no-missing-key": "error",
3642
- "react/no-misused-capture-owner-stack": "off",
3643
- "react/no-nested-component-definitions": "warn",
3644
- "react/no-nested-lazy-component-declarations": "warn",
3645
- "react/no-set-state-in-component-did-mount": "warn",
3646
- "react/no-set-state-in-component-did-update": "warn",
3647
- "react/no-set-state-in-component-will-update": "warn",
3648
- "react/no-unnecessary-use-prefix": "error",
3649
- "react/no-unsafe-component-will-mount": "error",
3650
- "react/no-unsafe-component-will-receive-props": "error",
3651
- "react/no-unsafe-component-will-update": "error",
3652
- "react/no-unstable-context-value": "error",
3653
- "react/no-unstable-default-props": ["error", { safeDefaultProps: [
3654
- "Axes",
3655
- "BrickColor",
3656
- "CatalogSearchParams",
3657
- "CFrame",
3658
- "Color3",
3659
- "ColorSequence",
3660
- "CFrame",
3661
- "Content",
3662
- "DateTime",
3663
- "DockWidgetPluginGuiInfo",
3664
- "Enum",
3665
- "Faces",
3666
- "FloatCurveKey",
3667
- "Font",
3668
- "NumberRange",
3669
- "NumberSequence",
3670
- "NumberSequenceKeypoint",
3671
- "OverlapParams",
3672
- "Path2DControlPoint",
3673
- "PathWaypoint",
3674
- "PhysicalProperties",
3675
- "Ray",
3676
- "RaycastParams",
3677
- "Rect",
3678
- "Region3",
3679
- "Region3int16",
3680
- "RotationCurveKey",
3681
- "SecurityCapabilities",
3682
- "TweenInfo",
3683
- "UDim",
3684
- "UDim2",
3685
- "ValueCurveKey",
3686
- "Vector2",
3687
- "Vector3",
3688
- "Vector3int16"
3689
- ] }],
3690
- "react/no-unused-class-component-members": "off",
3691
- "react/no-unused-state": "error",
3692
- "react/no-use-context": "off",
3693
- "react/rules-of-hooks": "error",
3694
- "react/set-state-in-effect": "error",
3695
- "react/set-state-in-render": "error",
3696
- "react/static-components": "error",
3697
- "react/use-memo": "error",
3698
- "react/use-state": ["error", {
3699
- enforceAssignment: false,
3700
- enforceSetterName: false
3701
- }],
3702
- ...testing ? {
3703
- "testing-library/await-async-queries": "error",
3704
- "testing-library/await-async-utils": "error",
3705
- "testing-library/no-await-sync-events": ["error", { eventModules: ["fire-event"] }],
3706
- "testing-library/no-await-sync-queries": "error",
3707
- "testing-library/no-container": "error",
3708
- "testing-library/no-debugging-utils": "error",
3709
- "testing-library/no-global-regexp-flag-in-query": "error",
3710
- "testing-library/no-manual-cleanup": "error",
3711
- "testing-library/no-node-access": "error",
3712
- "testing-library/no-promise-in-fire-event": "error",
3713
- "testing-library/no-render-in-lifecycle": "error",
3714
- "testing-library/no-unnecessary-act": "error",
3715
- "testing-library/no-wait-for-multiple-assertions": "error",
3716
- "testing-library/no-wait-for-side-effects": "error",
3717
- "testing-library/no-wait-for-snapshot": "error",
3718
- "testing-library/prefer-explicit-assert": "error",
3719
- "testing-library/prefer-find-by": "error",
3720
- "testing-library/prefer-presence-queries": "error",
3721
- "testing-library/prefer-query-by-disappearance": "error",
3722
- "testing-library/prefer-screen-queries": "error",
3723
- "testing-library/render-result-naming-convention": "error",
3724
- ...restrictedDomImport !== void 0 ? { "no-restricted-imports": restrictedDomImport } : {}
3725
- } : {},
3726
- ...stylistic !== false ? {
3727
- "flawless/jsx-shorthand-boolean": "warn",
3728
- "flawless/jsx-shorthand-fragment": "warn",
3729
- "flawless/prefer-destructuring-assignment": "error",
3730
- "flawless/react-namespace": "error",
3731
- "one-var": "off",
3732
- "react-jsx/no-useless-fragment": "warn",
3733
- "react-naming-convention/context-name": "error",
3734
- "react-naming-convention/ref-name": "error",
3735
- "react/use-state": "error",
3736
- "style/jsx-curly-brace-presence": ["error", {
3737
- children: "never",
3738
- propElementValues: "always",
3739
- props: "never"
3740
- }],
3741
- "style/jsx-newline": "error",
3742
- "style/jsx-self-closing-comp": "error",
3743
- "unicorn/filename-case": ["error", {
3744
- case: filenameCase,
3745
- ignore: ["^[A-Z0-9]+.md$"],
3746
- multipleFileExtensions: true
3747
- }]
3748
- } : {}
3749
- };
3750
- }
3751
- //#endregion
3752
3791
  //#region src/rules/small-rules.ts
3753
3792
  /**
3754
3793
  * Small-rules rules shared between the ESLint and oxlint factories.
@@ -3946,7 +3985,7 @@ function robloxRules({ stylistic = true } = {}) {
3946
3985
  //#region src/eslint/configs/roblox.ts
3947
3986
  init_globs();
3948
3987
  async function roblox(options = {}, formatLua = true) {
3949
- const { componentExts: componentExtensions = [], overrides = {}, overridesTypeAware = {}, parserOptions = {}, stylistic = true, typeAware = true } = options;
3988
+ const { componentExts: componentExtensions = [], outOfProjectFiles, overrides = {}, overridesTypeAware = {}, parserOptions = {}, parserOptionsNonTypeAware = {}, parserOptionsTypeAware = {}, stylistic = true, typeAware = true } = options;
3950
3989
  const pluginSmallRules = loadSmallRulesPlugin();
3951
3990
  const [parserTs, pluginRobloxTs, pluginSentinel] = await Promise.all([
3952
3991
  interopDefault(import("@typescript-eslint/parser")),
@@ -3968,8 +4007,11 @@ async function roblox(options = {}, formatLua = true) {
3968
4007
  configName: "roblox",
3969
4008
  files: parserFiles,
3970
4009
  ignores,
4010
+ outOfProjectFiles,
3971
4011
  parser: parserTs,
3972
4012
  parserOptions,
4013
+ parserOptionsNonTypeAware,
4014
+ parserOptionsTypeAware,
3973
4015
  tsconfigPath,
3974
4016
  typeAware: usesTypeInformation
3975
4017
  });
@@ -19673,6 +19715,8 @@ async function isentinel(options, ...userConfigs) {
19673
19715
  const { autoRenamePlugins = true, componentExts: componentExtensions = [], e18e: enableE18e = true, eslintPlugin: enableEslintPlugin = false, formatters, gitignore: enableGitignore = true, jsdoc: enableJsdoc = true, jsonc: enableJsonc = true, jsx: enableJsx = true, markdown: enableMarkdown = true, oxlint: enableOxlint = false, pnpm: enableCatalogs = findUpSync("pnpm-workspace.yaml") !== void 0, react: enableReact = false, root: customRootGlobs, spellCheck: enableSpellCheck, toml: enableToml = true, yaml: enableYaml = true } = options;
19674
19716
  const rootGlobs = mergeGlobs(GLOB_ROOT, customRootGlobs);
19675
19717
  const enableRoblox = options.roblox !== false;
19718
+ const reactOptions = resolveSubOptions(options, "react");
19719
+ const restrictedDomPackage = enableReact !== false && reactOptions.testing === true ? options.settings?.["testing-library"]?.domPackage : void 0;
19676
19720
  const oxlintMode = (() => {
19677
19721
  if (enableOxlint === false) return "off";
19678
19722
  return enableOxlint === "native" ? "native" : "full";
@@ -19874,6 +19918,16 @@ async function isentinel(options, ...userConfigs) {
19874
19918
  if (options.markdown !== false) composer = composer.setDefaultIgnores((previous) => {
19875
19919
  return [...previous, GLOB_MARKDOWN];
19876
19920
  });
19921
+ if (restrictedDomPackage !== void 0) composer = composer.onResolved((resolved) => {
19922
+ for (const config of resolved) {
19923
+ const restrictedImports = config.rules?.["no-restricted-imports"];
19924
+ if (restrictedImports === void 0) continue;
19925
+ config.rules = {
19926
+ ...config.rules,
19927
+ "no-restricted-imports": mergeRestrictedDomImportRule(restrictedImports, restrictedDomPackage)
19928
+ };
19929
+ }
19930
+ });
19877
19931
  if (autoRenamePlugins) composer = composer.renamePlugins(defaultPluginRenaming);
19878
19932
  if (oxlintMode !== "off") {
19879
19933
  const { dropOxlintCoveredRules, warnDeadMappedRules, warnMissingTsgolint } = await Promise.resolve().then(() => (init_oxlint_drop(), oxlint_drop_exports));
package/dist/lint-cli.mjs CHANGED
@@ -38,7 +38,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
38
38
  }) : target, mod));
39
39
  //#endregion
40
40
  //#region package.json
41
- var version = "6.0.0-beta.34";
41
+ var version = "6.0.0-beta.36";
42
42
  //#endregion
43
43
  //#region src/lint-cli/lib/cli/types.ts
44
44
  /**
@@ -776,7 +776,7 @@ function composeOxlintCommand(options, context) {
776
776
  if (options.agents) args.push("--format", "agent");
777
777
  if (context.oxlintTypeAware) args.push("--type-aware");
778
778
  if (options.fix) args.push("--fix");
779
- args.push(...options.oxlintArgs, ...context.paths);
779
+ args.push("--no-error-on-unmatched-pattern", ...options.oxlintArgs, ...context.paths);
780
780
  return {
781
781
  args,
782
782
  bin: "oxlint",
@@ -3738,17 +3738,21 @@ function withoutIgnored(files, ignored) {
3738
3738
  };
3739
3739
  }
3740
3740
  /**
3741
- * Restrict explicit lint targets to the ones oxlint could lint. Oxlint only
3742
- * handles the TS/JS family, and exits non-zero with "No files found to lint"
3743
- * when every path it was handed resolves to nothing — so a hook run over a
3744
- * `package.json`-only change would fail on a file oxlint was never going to
3745
- * lint. A path is kept when its extension is type-aware, when it has no
3746
- * extension, or when it is an existing directory (either may hold TS/JS files).
3741
+ * Restrict explicit lint targets to the ones oxlint could lint, so a run with
3742
+ * nothing for oxlint to do spawns no oxlint child at all. Oxlint only handles
3743
+ * the TS/JS family: a path is kept when its extension is type-aware, when it
3744
+ * has no extension, or when it is an existing directory (either may hold TS/JS
3745
+ * files).
3747
3746
  *
3748
3747
  * Surviving targets are then dropped when oxlint's own ignore matching would
3749
3748
  * skip them (see {@link gitIgnoredTargets}): a hook stages a `.gitignore`d yet
3750
3749
  * git-tracked file (a committed generated `*.d.ts`, say), which passes the
3751
- * extension test but is the exact all-ignored set oxlint exits non-zero on.
3750
+ * extension test but is a path oxlint refuses.
3751
+ *
3752
+ * This is a spawn-avoidance filter, not a correctness one — the config
3753
+ * `ignores` an all-ignored set usually comes from are invisible to git.
3754
+ * Tolerating that set is `--no-error-on-unmatched-pattern`'s job (see
3755
+ * `composeOxlintCommand`).
3752
3756
  *
3753
3757
  * @param cwd - The working directory to resolve targets against.
3754
3758
  * @param targets - The explicit lint target paths.
package/dist/oxlint.d.mts CHANGED
@@ -24305,7 +24305,7 @@ type ConfigSettings = NonNullable<Linter.Config["settings"]> & {
24305
24305
  * Package name for DOM Testing Library.
24306
24306
  *
24307
24307
  * When React testing support is enabled, importing this package is
24308
- * forbidden in favor of `@packages/react-testing-library-lua`.
24308
+ * forbidden in favor of `react-testing-library-lua`.
24309
24309
  */
24310
24310
  domPackage?: string;
24311
24311
  };
package/dist/oxlint.mjs CHANGED
@@ -3462,9 +3462,7 @@ const enabledPresetRuleNames = /* @__PURE__ */ new Set([
3462
3462
  "testing-library/no-await-sync-queries",
3463
3463
  "testing-library/no-container",
3464
3464
  "testing-library/no-debugging-utils",
3465
- "testing-library/no-global-regexp-flag-in-query",
3466
3465
  "testing-library/no-manual-cleanup",
3467
- "testing-library/no-node-access",
3468
3466
  "testing-library/no-promise-in-fire-event",
3469
3467
  "testing-library/no-render-in-lifecycle",
3470
3468
  "testing-library/no-unnecessary-act",
@@ -5228,6 +5226,23 @@ const GLOB_EXCLUDE = [
5228
5226
  "**/scratch/**"
5229
5227
  ];
5230
5228
  //#endregion
5229
+ //#region src/guards.ts
5230
+ /**
5231
+ * Internal runtime type guards. Prefer these over `as` assertions so values
5232
+ * crossing untyped boundaries (`JSON.parse`, dynamic `import`, plugin objects)
5233
+ * are validated at runtime rather than asserted away.
5234
+ */
5235
+ /**
5236
+ * Whether a value is a non-null, non-array object usable as a string-keyed
5237
+ * record. Narrows `unknown` without an assertion.
5238
+ *
5239
+ * @param value - The value to test.
5240
+ * @returns Whether the value is a plain object.
5241
+ */
5242
+ function isRecord$1(value) {
5243
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5244
+ }
5245
+ //#endregion
5231
5246
  //#region src/prettier-config.ts
5232
5247
  /**
5233
5248
  * Synchronous resolution of the effective Prettier-shaped formatting settings
@@ -5332,15 +5347,15 @@ function parseJsonLike(contents) {
5332
5347
  return;
5333
5348
  }
5334
5349
  }
5335
- function isRecord$1(value) {
5350
+ function isRecord(value) {
5336
5351
  return typeof value === "object" && value !== null && !Array.isArray(value);
5337
5352
  }
5338
5353
  function readPackageJsonPrettierKey(filePath) {
5339
5354
  const contents = readFileOrUndefined(filePath);
5340
5355
  if (contents === void 0) return;
5341
5356
  const parsed = parseJsonLike(contents);
5342
- const prettierKey = isRecord$1(parsed) ? parsed["prettier"] : void 0;
5343
- return isRecord$1(prettierKey) ? prettierKey : void 0;
5357
+ const prettierKey = isRecord(parsed) ? parsed["prettier"] : void 0;
5358
+ return isRecord(prettierKey) ? prettierKey : void 0;
5344
5359
  }
5345
5360
  /**
5346
5361
  * Load a JS Prettier config. Node's `require` handles ESM entry points
@@ -5353,8 +5368,8 @@ function readPackageJsonPrettierKey(filePath) {
5353
5368
  function requireConfigModule(filePath) {
5354
5369
  try {
5355
5370
  const loaded = createRequire(filePath)(filePath);
5356
- const value = isRecord$1(loaded) && "default" in loaded ? loaded["default"] : loaded;
5357
- return isRecord$1(value) ? value : void 0;
5371
+ const value = isRecord(loaded) && "default" in loaded ? loaded["default"] : loaded;
5372
+ return isRecord(value) ? value : void 0;
5358
5373
  } catch {
5359
5374
  return;
5360
5375
  }
@@ -5372,7 +5387,7 @@ function readConfigFile(filePath) {
5372
5387
  const contents = readFileOrUndefined(filePath);
5373
5388
  if (contents === void 0) return;
5374
5389
  const parsed = parseJsonLike(contents) ?? parseYamlLike(contents);
5375
- return isRecord$1(parsed) ? parsed : void 0;
5390
+ return isRecord(parsed) ? parsed : void 0;
5376
5391
  }
5377
5392
  function ancestorDirectories(cwd) {
5378
5393
  const directories = [];
@@ -5571,21 +5586,216 @@ function buildOxfmtOptions({ oxfmtConfigOptions = {}, oxfmtOptions, prettierOpti
5571
5586
  };
5572
5587
  }
5573
5588
  //#endregion
5574
- //#region src/guards.ts
5589
+ //#region src/rules/react.ts
5590
+ const DOM_IMPORT_MESSAGE = "Import from react-testing-library-lua instead; it re-exports the DOM utilities, and eslint-plugin-testing-library only detects one module per file.";
5575
5591
  /**
5576
- * Internal runtime type guards. Prefer these over `as` assertions so values
5577
- * crossing untyped boundaries (`JSON.parse`, dynamic `import`, plugin objects)
5578
- * are validated at runtime rather than asserted away.
5592
+ * Build the direct DOM Testing Library import restriction shared by both
5593
+ * engines.
5594
+ *
5595
+ * @param domPackage - Package name for DOM Testing Library.
5596
+ * @returns The configured restriction, or `undefined` when no package is set.
5579
5597
  */
5598
+ function restrictedDomImportRule(domPackage) {
5599
+ if (domPackage === void 0) return;
5600
+ return ["error", { paths: [domImportRestriction(domPackage)] }];
5601
+ }
5580
5602
  /**
5581
- * Whether a value is a non-null, non-array object usable as a string-keyed
5582
- * record. Narrows `unknown` without an assertion.
5603
+ * Merge the direct DOM Testing Library restriction into an existing
5604
+ * `no-restricted-imports` entry.
5583
5605
  *
5584
- * @param value - The value to test.
5585
- * @returns Whether the value is a plain object.
5606
+ * The existing entry owns the severity and any duplicate restriction. An
5607
+ * explicit disabled entry remains disabled.
5608
+ *
5609
+ * @param rule - Existing rule entry.
5610
+ * @param domPackage - Package name for DOM Testing Library.
5611
+ * @returns The composed rule entry.
5586
5612
  */
5587
- function isRecord(value) {
5588
- return typeof value === "object" && value !== null && !Array.isArray(value);
5613
+ function mergeRestrictedDomImportRule(rule, domPackage) {
5614
+ if (domPackage === void 0 || rule === "off" || rule === 0) return rule;
5615
+ if (rule === void 0) return restrictedDomImportRule(domPackage);
5616
+ const parts = Array.isArray(rule) ? [...rule] : [rule];
5617
+ const [severity, ...options] = parts;
5618
+ if (severity === "off" || severity === 0) return rule;
5619
+ if (severity !== "error" && severity !== "warn" && severity !== 1 && severity !== 2) return rule;
5620
+ const [firstOption] = options;
5621
+ if (options.length === 1 && isObjectStyleRestriction(firstOption)) {
5622
+ const paths = Array.isArray(firstOption["paths"]) ? firstOption["paths"] : [];
5623
+ if (paths.some((path) => isDomImportRestriction(path, domPackage))) return rule;
5624
+ return [severity, {
5625
+ ...firstOption,
5626
+ paths: [...paths, domImportRestriction(domPackage)]
5627
+ }];
5628
+ }
5629
+ if (options.some((option) => isDomImportRestriction(option, domPackage))) return rule;
5630
+ if (options.length === 0) return [severity, { paths: [domImportRestriction(domPackage)] }];
5631
+ return [...parts, domImportRestriction(domPackage)];
5632
+ }
5633
+ /**
5634
+ * React rules shared between the ESLint and oxlint factories.
5635
+ *
5636
+ * The type-aware react rules stay in the ESLint config (oxlint jsPlugins have
5637
+ * no type information).
5638
+ *
5639
+ * @param options - Shared rule options.
5640
+ * @returns The rule map.
5641
+ */
5642
+ function reactRules({ domPackage, filenameCase = "kebabCase", reactCompiler = true, stylistic = true, testing = false } = {}) {
5643
+ const restrictedDomImport = testing ? restrictedDomImportRule(domPackage) : void 0;
5644
+ return {
5645
+ "flawless/no-unnecessary-use-callback": "error",
5646
+ "flawless/no-unnecessary-use-memo": "error",
5647
+ "flawless/purity": "error",
5648
+ "small-rules/react-hooks-strict-return": "error",
5649
+ ...reactCompiler ? {
5650
+ "react/globals": "error",
5651
+ "react/refs": "error"
5652
+ } : {},
5653
+ "react-jsx/no-children-prop": "warn",
5654
+ "react-jsx/no-children-prop-with-children": "error",
5655
+ "react-jsx/no-comment-textnodes": "off",
5656
+ "react-jsx/no-key-after-spread": "error",
5657
+ "react-jsx/no-leaked-dollar": "warn",
5658
+ "react-jsx/no-leaked-semicolon": "warn",
5659
+ "react/error-boundaries": "error",
5660
+ "react/exhaustive-deps": "error",
5661
+ "react/immutability": "error",
5662
+ "react/no-access-state-in-setstate": "error",
5663
+ "react/no-array-index-key": "warn",
5664
+ "react/no-children-count": "warn",
5665
+ "react/no-children-for-each": "warn",
5666
+ "react/no-children-map": "warn",
5667
+ "react/no-children-only": "warn",
5668
+ "react/no-children-to-array": "warn",
5669
+ "react/no-class-component": "error",
5670
+ "react/no-clone-element": "warn",
5671
+ "react/no-component-will-mount": "error",
5672
+ "react/no-component-will-receive-props": "error",
5673
+ "react/no-component-will-update": "error",
5674
+ "react/no-create-ref": "error",
5675
+ "react/no-direct-mutation-state": "error",
5676
+ "react/no-duplicate-key": "error",
5677
+ "react/no-forward-ref": "off",
5678
+ "react/no-missing-component-display-name": "error",
5679
+ "react/no-missing-context-display-name": "error",
5680
+ "react/no-missing-key": "error",
5681
+ "react/no-misused-capture-owner-stack": "off",
5682
+ "react/no-nested-component-definitions": "warn",
5683
+ "react/no-nested-lazy-component-declarations": "warn",
5684
+ "react/no-set-state-in-component-did-mount": "warn",
5685
+ "react/no-set-state-in-component-did-update": "warn",
5686
+ "react/no-set-state-in-component-will-update": "warn",
5687
+ "react/no-unnecessary-use-prefix": "error",
5688
+ "react/no-unsafe-component-will-mount": "error",
5689
+ "react/no-unsafe-component-will-receive-props": "error",
5690
+ "react/no-unsafe-component-will-update": "error",
5691
+ "react/no-unstable-context-value": "error",
5692
+ "react/no-unstable-default-props": ["error", { safeDefaultProps: [
5693
+ "Axes",
5694
+ "BrickColor",
5695
+ "CatalogSearchParams",
5696
+ "CFrame",
5697
+ "Color3",
5698
+ "ColorSequence",
5699
+ "CFrame",
5700
+ "Content",
5701
+ "DateTime",
5702
+ "DockWidgetPluginGuiInfo",
5703
+ "Enum",
5704
+ "Faces",
5705
+ "FloatCurveKey",
5706
+ "Font",
5707
+ "NumberRange",
5708
+ "NumberSequence",
5709
+ "NumberSequenceKeypoint",
5710
+ "OverlapParams",
5711
+ "Path2DControlPoint",
5712
+ "PathWaypoint",
5713
+ "PhysicalProperties",
5714
+ "Ray",
5715
+ "RaycastParams",
5716
+ "Rect",
5717
+ "Region3",
5718
+ "Region3int16",
5719
+ "RotationCurveKey",
5720
+ "SecurityCapabilities",
5721
+ "TweenInfo",
5722
+ "UDim",
5723
+ "UDim2",
5724
+ "ValueCurveKey",
5725
+ "Vector2",
5726
+ "Vector3",
5727
+ "Vector3int16"
5728
+ ] }],
5729
+ "react/no-unused-class-component-members": "off",
5730
+ "react/no-unused-state": "error",
5731
+ "react/no-use-context": "off",
5732
+ "react/rules-of-hooks": "error",
5733
+ "react/set-state-in-effect": "error",
5734
+ "react/set-state-in-render": "error",
5735
+ "react/static-components": "error",
5736
+ "react/use-memo": "error",
5737
+ "react/use-state": ["error", {
5738
+ enforceAssignment: false,
5739
+ enforceSetterName: false
5740
+ }],
5741
+ ...testing ? {
5742
+ "testing-library/await-async-queries": "error",
5743
+ "testing-library/await-async-utils": "error",
5744
+ "testing-library/no-await-sync-events": ["error", { eventModules: ["fire-event"] }],
5745
+ "testing-library/no-await-sync-queries": "error",
5746
+ "testing-library/no-container": "error",
5747
+ "testing-library/no-debugging-utils": "error",
5748
+ "testing-library/no-manual-cleanup": "error",
5749
+ "testing-library/no-promise-in-fire-event": "error",
5750
+ "testing-library/no-render-in-lifecycle": "error",
5751
+ "testing-library/no-unnecessary-act": "error",
5752
+ "testing-library/no-wait-for-multiple-assertions": "error",
5753
+ "testing-library/no-wait-for-side-effects": "error",
5754
+ "testing-library/no-wait-for-snapshot": "error",
5755
+ "testing-library/prefer-explicit-assert": "error",
5756
+ "testing-library/prefer-find-by": "error",
5757
+ "testing-library/prefer-presence-queries": "error",
5758
+ "testing-library/prefer-query-by-disappearance": "error",
5759
+ "testing-library/prefer-screen-queries": "error",
5760
+ "testing-library/render-result-naming-convention": "error",
5761
+ ...restrictedDomImport !== void 0 ? { "no-restricted-imports": restrictedDomImport } : {}
5762
+ } : {},
5763
+ ...stylistic !== false ? {
5764
+ "flawless/jsx-shorthand-boolean": "warn",
5765
+ "flawless/jsx-shorthand-fragment": "warn",
5766
+ "flawless/prefer-destructuring-assignment": "error",
5767
+ "flawless/react-namespace": "error",
5768
+ "one-var": "off",
5769
+ "react-jsx/no-useless-fragment": "warn",
5770
+ "react-naming-convention/context-name": "error",
5771
+ "react-naming-convention/ref-name": "error",
5772
+ "react/use-state": "error",
5773
+ "style/jsx-curly-brace-presence": ["error", {
5774
+ children: "never",
5775
+ propElementValues: "always",
5776
+ props: "never"
5777
+ }],
5778
+ "style/jsx-newline": "error",
5779
+ "style/jsx-self-closing-comp": "error",
5780
+ "unicorn/filename-case": ["error", {
5781
+ case: filenameCase,
5782
+ ignore: ["^[A-Z0-9]+.md$"],
5783
+ multipleFileExtensions: true
5784
+ }]
5785
+ } : {}
5786
+ };
5787
+ }
5788
+ function domImportRestriction(domPackage) {
5789
+ return {
5790
+ name: domPackage,
5791
+ message: DOM_IMPORT_MESSAGE
5792
+ };
5793
+ }
5794
+ function isDomImportRestriction(value, domPackage) {
5795
+ return value === domPackage || isRecord$1(value) && "name" in value && value["name"] === domPackage;
5796
+ }
5797
+ function isObjectStyleRestriction(value) {
5798
+ return isRecord$1(value) && !("name" in value);
5589
5799
  }
5590
5800
  createRequire(import.meta.url);
5591
5801
  /**
@@ -5641,7 +5851,7 @@ function resolveNodeMajor(settings, cwd = process.cwd()) {
5641
5851
  let manifest = {};
5642
5852
  try {
5643
5853
  const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
5644
- if (isRecord(parsed)) manifest = parsed;
5854
+ if (isRecord$1(parsed)) manifest = parsed;
5645
5855
  } catch {}
5646
5856
  const range = readNodeRange(manifest);
5647
5857
  if (range !== void 0) return parseNodeMajor(range);
@@ -5659,7 +5869,7 @@ function toSourceGlob(glob) {
5659
5869
  }
5660
5870
  function getOverrides(options, key) {
5661
5871
  const sub = resolveSubOptions(options, key);
5662
- if (!isRecord(sub)) return {
5872
+ if (!isRecord$1(sub)) return {
5663
5873
  overrides: {},
5664
5874
  overridesTypeAware: {}
5665
5875
  };
@@ -5830,7 +6040,7 @@ function resolveOxfmtConfigOptionsSync() {
5830
6040
  try {
5831
6041
  const content = fs.readFileSync(configPath, "utf-8");
5832
6042
  const parsed = JSON.parse(content);
5833
- if (!isRecord(parsed)) continue;
6043
+ if (!isRecord$1(parsed)) continue;
5834
6044
  const { $schema: _, ...config } = parsed;
5835
6045
  return config;
5836
6046
  } catch {
@@ -5884,7 +6094,7 @@ function parseNodeMajor(range) {
5884
6094
  function readSettingsNodeVersion(settings) {
5885
6095
  for (const key of ["n", "node"]) {
5886
6096
  const setting = settings?.[key];
5887
- const version = isRecord(setting) ? setting["version"] : void 0;
6097
+ const version = isRecord$1(setting) ? setting["version"] : void 0;
5888
6098
  if (typeof version === "string") return version;
5889
6099
  }
5890
6100
  }
@@ -5897,9 +6107,9 @@ function readSettingsNodeVersion(settings) {
5897
6107
  * @returns The declared range, or `undefined` when the manifest declares none.
5898
6108
  */
5899
6109
  function readNodeRange({ devEngines, engines }) {
5900
- if (isRecord(engines) && typeof engines["node"] === "string") return engines["node"];
5901
- const runtime = isRecord(devEngines) ? devEngines["runtime"] : void 0;
5902
- const version = (Array.isArray(runtime) ? runtime : [runtime]).find((entry) => isRecord(entry) && entry["name"] === "node")?.["version"];
6110
+ if (isRecord$1(engines) && typeof engines["node"] === "string") return engines["node"];
6111
+ const runtime = isRecord$1(devEngines) ? devEngines["runtime"] : void 0;
6112
+ const version = (Array.isArray(runtime) ? runtime : [runtime]).find((entry) => isRecord$1(entry) && entry["name"] === "node")?.["version"];
5903
6113
  return typeof version === "string" ? version : void 0;
5904
6114
  }
5905
6115
  //#endregion
@@ -7494,7 +7704,7 @@ const require$1 = createRequire(import.meta.url);
7494
7704
  function oxlintOxfmt(options) {
7495
7705
  const { componentExts: componentExtensions = [], files: oxfmtFiles, oxfmtOptions = {} } = options ?? {};
7496
7706
  const configPrettier = require$1("eslint-config-prettier/flat");
7497
- const configPrettierRules = isRecord(configPrettier) && isRecord(configPrettier["rules"]) ? configPrettier["rules"] : {};
7707
+ const configPrettierRules = isRecord$1(configPrettier) && isRecord$1(configPrettier["rules"]) ? configPrettier["rules"] : {};
7498
7708
  const rulesToIgnore = /* @__PURE__ */ new Set(["curly", "style/quotes"]);
7499
7709
  const canonicalDisables = {};
7500
7710
  const prettierRuleNames = Object.keys(renameRules(configPrettierRules, defaultPluginRenaming));
@@ -7827,179 +8037,6 @@ function oxlintPromise() {
7827
8037
  });
7828
8038
  }
7829
8039
  //#endregion
7830
- //#region src/rules/react.ts
7831
- /**
7832
- * Build the direct DOM Testing Library import restriction shared by both
7833
- * engines.
7834
- *
7835
- * @param domPackage - Package name for DOM Testing Library.
7836
- * @returns The configured restriction, or `undefined` when no package is set.
7837
- */
7838
- function restrictedDomImportRule(domPackage) {
7839
- if (domPackage === void 0) return;
7840
- return ["error", { paths: [{
7841
- name: domPackage,
7842
- message: "Import from @packages/react-testing-library-lua instead; it re-exports the DOM utilities, and eslint-plugin-testing-library only detects one module per file."
7843
- }] }];
7844
- }
7845
- /**
7846
- * React rules shared between the ESLint and oxlint factories.
7847
- *
7848
- * The type-aware react rules stay in the ESLint config (oxlint jsPlugins have
7849
- * no type information).
7850
- *
7851
- * @param options - Shared rule options.
7852
- * @returns The rule map.
7853
- */
7854
- function reactRules({ domPackage, filenameCase = "kebabCase", reactCompiler = true, stylistic = true, testing = false } = {}) {
7855
- const restrictedDomImport = testing ? restrictedDomImportRule(domPackage) : void 0;
7856
- return {
7857
- "flawless/no-unnecessary-use-callback": "error",
7858
- "flawless/no-unnecessary-use-memo": "error",
7859
- "flawless/purity": "error",
7860
- "small-rules/react-hooks-strict-return": "error",
7861
- ...reactCompiler ? {
7862
- "react/globals": "error",
7863
- "react/refs": "error"
7864
- } : {},
7865
- "react-jsx/no-children-prop": "warn",
7866
- "react-jsx/no-children-prop-with-children": "error",
7867
- "react-jsx/no-comment-textnodes": "off",
7868
- "react-jsx/no-key-after-spread": "error",
7869
- "react-jsx/no-leaked-dollar": "warn",
7870
- "react-jsx/no-leaked-semicolon": "warn",
7871
- "react/error-boundaries": "error",
7872
- "react/exhaustive-deps": "error",
7873
- "react/immutability": "error",
7874
- "react/no-access-state-in-setstate": "error",
7875
- "react/no-array-index-key": "warn",
7876
- "react/no-children-count": "warn",
7877
- "react/no-children-for-each": "warn",
7878
- "react/no-children-map": "warn",
7879
- "react/no-children-only": "warn",
7880
- "react/no-children-to-array": "warn",
7881
- "react/no-class-component": "error",
7882
- "react/no-clone-element": "warn",
7883
- "react/no-component-will-mount": "error",
7884
- "react/no-component-will-receive-props": "error",
7885
- "react/no-component-will-update": "error",
7886
- "react/no-create-ref": "error",
7887
- "react/no-direct-mutation-state": "error",
7888
- "react/no-duplicate-key": "error",
7889
- "react/no-forward-ref": "off",
7890
- "react/no-missing-component-display-name": "error",
7891
- "react/no-missing-context-display-name": "error",
7892
- "react/no-missing-key": "error",
7893
- "react/no-misused-capture-owner-stack": "off",
7894
- "react/no-nested-component-definitions": "warn",
7895
- "react/no-nested-lazy-component-declarations": "warn",
7896
- "react/no-set-state-in-component-did-mount": "warn",
7897
- "react/no-set-state-in-component-did-update": "warn",
7898
- "react/no-set-state-in-component-will-update": "warn",
7899
- "react/no-unnecessary-use-prefix": "error",
7900
- "react/no-unsafe-component-will-mount": "error",
7901
- "react/no-unsafe-component-will-receive-props": "error",
7902
- "react/no-unsafe-component-will-update": "error",
7903
- "react/no-unstable-context-value": "error",
7904
- "react/no-unstable-default-props": ["error", { safeDefaultProps: [
7905
- "Axes",
7906
- "BrickColor",
7907
- "CatalogSearchParams",
7908
- "CFrame",
7909
- "Color3",
7910
- "ColorSequence",
7911
- "CFrame",
7912
- "Content",
7913
- "DateTime",
7914
- "DockWidgetPluginGuiInfo",
7915
- "Enum",
7916
- "Faces",
7917
- "FloatCurveKey",
7918
- "Font",
7919
- "NumberRange",
7920
- "NumberSequence",
7921
- "NumberSequenceKeypoint",
7922
- "OverlapParams",
7923
- "Path2DControlPoint",
7924
- "PathWaypoint",
7925
- "PhysicalProperties",
7926
- "Ray",
7927
- "RaycastParams",
7928
- "Rect",
7929
- "Region3",
7930
- "Region3int16",
7931
- "RotationCurveKey",
7932
- "SecurityCapabilities",
7933
- "TweenInfo",
7934
- "UDim",
7935
- "UDim2",
7936
- "ValueCurveKey",
7937
- "Vector2",
7938
- "Vector3",
7939
- "Vector3int16"
7940
- ] }],
7941
- "react/no-unused-class-component-members": "off",
7942
- "react/no-unused-state": "error",
7943
- "react/no-use-context": "off",
7944
- "react/rules-of-hooks": "error",
7945
- "react/set-state-in-effect": "error",
7946
- "react/set-state-in-render": "error",
7947
- "react/static-components": "error",
7948
- "react/use-memo": "error",
7949
- "react/use-state": ["error", {
7950
- enforceAssignment: false,
7951
- enforceSetterName: false
7952
- }],
7953
- ...testing ? {
7954
- "testing-library/await-async-queries": "error",
7955
- "testing-library/await-async-utils": "error",
7956
- "testing-library/no-await-sync-events": ["error", { eventModules: ["fire-event"] }],
7957
- "testing-library/no-await-sync-queries": "error",
7958
- "testing-library/no-container": "error",
7959
- "testing-library/no-debugging-utils": "error",
7960
- "testing-library/no-global-regexp-flag-in-query": "error",
7961
- "testing-library/no-manual-cleanup": "error",
7962
- "testing-library/no-node-access": "error",
7963
- "testing-library/no-promise-in-fire-event": "error",
7964
- "testing-library/no-render-in-lifecycle": "error",
7965
- "testing-library/no-unnecessary-act": "error",
7966
- "testing-library/no-wait-for-multiple-assertions": "error",
7967
- "testing-library/no-wait-for-side-effects": "error",
7968
- "testing-library/no-wait-for-snapshot": "error",
7969
- "testing-library/prefer-explicit-assert": "error",
7970
- "testing-library/prefer-find-by": "error",
7971
- "testing-library/prefer-presence-queries": "error",
7972
- "testing-library/prefer-query-by-disappearance": "error",
7973
- "testing-library/prefer-screen-queries": "error",
7974
- "testing-library/render-result-naming-convention": "error",
7975
- ...restrictedDomImport !== void 0 ? { "no-restricted-imports": restrictedDomImport } : {}
7976
- } : {},
7977
- ...stylistic !== false ? {
7978
- "flawless/jsx-shorthand-boolean": "warn",
7979
- "flawless/jsx-shorthand-fragment": "warn",
7980
- "flawless/prefer-destructuring-assignment": "error",
7981
- "flawless/react-namespace": "error",
7982
- "one-var": "off",
7983
- "react-jsx/no-useless-fragment": "warn",
7984
- "react-naming-convention/context-name": "error",
7985
- "react-naming-convention/ref-name": "error",
7986
- "react/use-state": "error",
7987
- "style/jsx-curly-brace-presence": ["error", {
7988
- children: "never",
7989
- propElementValues: "always",
7990
- props: "never"
7991
- }],
7992
- "style/jsx-newline": "error",
7993
- "style/jsx-self-closing-comp": "error",
7994
- "unicorn/filename-case": ["error", {
7995
- case: filenameCase,
7996
- ignore: ["^[A-Z0-9]+.md$"],
7997
- multipleFileExtensions: true
7998
- }]
7999
- } : {}
8000
- };
8001
- }
8002
- //#endregion
8003
8040
  //#region src/oxlint/configs/react.ts
8004
8041
  /**
8005
8042
  * React configs for oxlint: the shared react family plus oxlint's native
@@ -9183,6 +9220,10 @@ function isentinel(factoryOptions, ...userConfigs) {
9183
9220
  const { categories, componentExts: componentExtensions = [], e18e: enableE18e = true, env, eslintPlugin: enableEslintPlugin = false, formatters, gitignore: enableGitignore = true, globals, ignores, jsdoc: enableJsdoc = true, jsPlugins: userJsPlugins, jsx: enableJsx = true, options: linterOptions, oxc: enableOxc = true, react: enableReact = false, root: customRootGlobs, rules = {}, spellCheck: enableSpellCheck, test: enableTest = false, warnDroppedOverrides: enableDroppedOverrideWarning = true } = options;
9184
9221
  const rootGlobs = mergeGlobs(GLOB_ROOT, customRootGlobs);
9185
9222
  const enableRoblox = options.roblox !== false;
9223
+ const reactOptions = resolveSubOptions(options, "react");
9224
+ const testingLibrarySettings = options.settings?.["testing-library"];
9225
+ const configuredDomPackage = isRecord$1(testingLibrarySettings) && typeof testingLibrarySettings["domPackage"] === "string" ? testingLibrarySettings["domPackage"] : void 0;
9226
+ const restrictedDomPackage = enableReact !== false && reactOptions.testing === true ? configuredDomPackage : void 0;
9186
9227
  const nativeOnly = userJsPlugins === false;
9187
9228
  const robloxScopedFiles = typeof options.roblox === "object" && options.roblox.files !== void 0 ? options.roblox.files.flat() : void 0;
9188
9229
  const hasComplement = !enableRoblox || robloxScopedFiles !== void 0;
@@ -9293,15 +9334,12 @@ function isentinel(factoryOptions, ...userConfigs) {
9293
9334
  ...resolveSubOptions(options, "perfectionist"),
9294
9335
  type: projectType
9295
9336
  }), oxlintStylistic(stylisticOptions));
9296
- if (enableReact !== false) {
9297
- const reactOptions = typeof enableReact === "object" ? enableReact : {};
9298
- configs.push(oxlintReact({
9299
- roblox: enableRoblox,
9300
- settings: options.settings,
9301
- stylistic: stylisticOptions,
9302
- ...reactOptions
9303
- }));
9304
- }
9337
+ if (enableReact !== false) configs.push(oxlintReact({
9338
+ roblox: enableRoblox,
9339
+ settings: options.settings,
9340
+ stylistic: stylisticOptions,
9341
+ ...reactOptions
9342
+ }));
9305
9343
  if (enableSpellCheck !== false) configs.push(oxlintSpelling({
9306
9344
  ...resolveSubOptions(options, "spellCheck"),
9307
9345
  componentExts: componentExtensions,
@@ -9362,6 +9400,14 @@ function isentinel(factoryOptions, ...userConfigs) {
9362
9400
  for (const jsPlugin of fragmentJsPlugins) jsPlugins.set(jsPluginKey(jsPlugin), jsPlugin);
9363
9401
  if (config.settings) Object.assign(mergedSettings, config.settings);
9364
9402
  const { name: _name, settings: _settings, ...override } = config;
9403
+ const restrictedImports = override.rules?.["no-restricted-imports"];
9404
+ if (restrictedDomPackage !== void 0 && restrictedImports !== void 0) {
9405
+ const mergedRestrictedImports = mergeRestrictedDomImportRule(restrictedImports, restrictedDomPackage);
9406
+ override.rules = {
9407
+ ...override.rules,
9408
+ "no-restricted-imports": mergedRestrictedImports
9409
+ };
9410
+ }
9365
9411
  override.files = override.files.map(anchorOxlintGlob);
9366
9412
  if (override.excludeFiles !== void 0) override.excludeFiles = override.excludeFiles.map(anchorOxlintGlob);
9367
9413
  overrides.push(override);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isentinel/eslint-config",
3
- "version": "6.0.0-beta.34",
3
+ "version": "6.0.0-beta.36",
4
4
  "description": "iSentinel's ESLint config",
5
5
  "keywords": [
6
6
  "eslint-config",
@@ -143,7 +143,7 @@
143
143
  "typescript": "6.0.3",
144
144
  "unplugin-unused": "0.5.7",
145
145
  "vitest": "4.1.10",
146
- "@isentinel/eslint-config": "6.0.0-beta.34"
146
+ "@isentinel/eslint-config": "6.0.0-beta.36"
147
147
  },
148
148
  "peerDependencies": {
149
149
  "@vitest/eslint-plugin": "^1.6.4",