@isentinel/eslint-config 6.0.0-beta.35 → 6.0.0-beta.37

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.35";
12
+ var version = "6.0.0-beta.37";
13
13
  var package_default = {
14
14
  name: "@isentinel/eslint-config",
15
15
  version,
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,177 +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 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-manual-cleanup": "error",
3710
- "testing-library/no-promise-in-fire-event": "error",
3711
- "testing-library/no-render-in-lifecycle": "error",
3712
- "testing-library/no-unnecessary-act": "error",
3713
- "testing-library/no-wait-for-multiple-assertions": "error",
3714
- "testing-library/no-wait-for-side-effects": "error",
3715
- "testing-library/no-wait-for-snapshot": "error",
3716
- "testing-library/prefer-explicit-assert": "error",
3717
- "testing-library/prefer-find-by": "error",
3718
- "testing-library/prefer-presence-queries": "error",
3719
- "testing-library/prefer-query-by-disappearance": "error",
3720
- "testing-library/prefer-screen-queries": "error",
3721
- "testing-library/render-result-naming-convention": "error",
3722
- ...restrictedDomImport !== void 0 ? { "no-restricted-imports": restrictedDomImport } : {}
3723
- } : {},
3724
- ...stylistic !== false ? {
3725
- "flawless/jsx-shorthand-boolean": "warn",
3726
- "flawless/jsx-shorthand-fragment": "warn",
3727
- "flawless/prefer-destructuring-assignment": "error",
3728
- "flawless/react-namespace": "error",
3729
- "one-var": "off",
3730
- "react-jsx/no-useless-fragment": "warn",
3731
- "react-naming-convention/context-name": "error",
3732
- "react-naming-convention/ref-name": "error",
3733
- "react/use-state": "error",
3734
- "style/jsx-curly-brace-presence": ["error", {
3735
- children: "never",
3736
- propElementValues: "always",
3737
- props: "never"
3738
- }],
3739
- "style/jsx-newline": "error",
3740
- "style/jsx-self-closing-comp": "error",
3741
- "unicorn/filename-case": ["error", {
3742
- case: filenameCase,
3743
- ignore: ["^[A-Z0-9]+.md$"],
3744
- multipleFileExtensions: true
3745
- }]
3746
- } : {}
3747
- };
3748
- }
3749
- //#endregion
3750
3791
  //#region src/rules/small-rules.ts
3751
3792
  /**
3752
3793
  * Small-rules rules shared between the ESLint and oxlint factories.
@@ -3944,7 +3985,7 @@ function robloxRules({ stylistic = true } = {}) {
3944
3985
  //#region src/eslint/configs/roblox.ts
3945
3986
  init_globs();
3946
3987
  async function roblox(options = {}, formatLua = true) {
3947
- 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;
3948
3989
  const pluginSmallRules = loadSmallRulesPlugin();
3949
3990
  const [parserTs, pluginRobloxTs, pluginSentinel] = await Promise.all([
3950
3991
  interopDefault(import("@typescript-eslint/parser")),
@@ -3966,8 +4007,11 @@ async function roblox(options = {}, formatLua = true) {
3966
4007
  configName: "roblox",
3967
4008
  files: parserFiles,
3968
4009
  ignores,
4010
+ outOfProjectFiles,
3969
4011
  parser: parserTs,
3970
4012
  parserOptions,
4013
+ parserOptionsNonTypeAware,
4014
+ parserOptionsTypeAware,
3971
4015
  tsconfigPath,
3972
4016
  typeAware: usesTypeInformation
3973
4017
  });
@@ -15460,7 +15504,8 @@ init_adapters();
15460
15504
  const FUNCTIONALLY_TYPE_AWARE_RULES = /* @__PURE__ */ new Set([
15461
15505
  ...typeAwareJsPluginRules,
15462
15506
  ...optionallyTypeAwareRules,
15463
- "flawless/prefer-read-only-props"
15507
+ "flawless/prefer-read-only-props",
15508
+ "vitest/valid-title"
15464
15509
  ]);
15465
15510
  /**
15466
15511
  * Whether a plugin rule definition declares `meta.docs.requiresTypeChecking`,
@@ -19671,6 +19716,8 @@ async function isentinel(options, ...userConfigs) {
19671
19716
  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;
19672
19717
  const rootGlobs = mergeGlobs(GLOB_ROOT, customRootGlobs);
19673
19718
  const enableRoblox = options.roblox !== false;
19719
+ const reactOptions = resolveSubOptions(options, "react");
19720
+ const restrictedDomPackage = enableReact !== false && reactOptions.testing === true ? options.settings?.["testing-library"]?.domPackage : void 0;
19674
19721
  const oxlintMode = (() => {
19675
19722
  if (enableOxlint === false) return "off";
19676
19723
  return enableOxlint === "native" ? "native" : "full";
@@ -19872,6 +19919,16 @@ async function isentinel(options, ...userConfigs) {
19872
19919
  if (options.markdown !== false) composer = composer.setDefaultIgnores((previous) => {
19873
19920
  return [...previous, GLOB_MARKDOWN];
19874
19921
  });
19922
+ if (restrictedDomPackage !== void 0) composer = composer.onResolved((resolved) => {
19923
+ for (const config of resolved) {
19924
+ const restrictedImports = config.rules?.["no-restricted-imports"];
19925
+ if (restrictedImports === void 0) continue;
19926
+ config.rules = {
19927
+ ...config.rules,
19928
+ "no-restricted-imports": mergeRestrictedDomImportRule(restrictedImports, restrictedDomPackage)
19929
+ };
19930
+ }
19931
+ });
19875
19932
  if (autoRenamePlugins) composer = composer.renamePlugins(defaultPluginRenaming);
19876
19933
  if (oxlintMode !== "off") {
19877
19934
  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.35";
41
+ var version = "6.0.0-beta.37";
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.mjs CHANGED
@@ -5226,6 +5226,23 @@ const GLOB_EXCLUDE = [
5226
5226
  "**/scratch/**"
5227
5227
  ];
5228
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
5229
5246
  //#region src/prettier-config.ts
5230
5247
  /**
5231
5248
  * Synchronous resolution of the effective Prettier-shaped formatting settings
@@ -5330,15 +5347,15 @@ function parseJsonLike(contents) {
5330
5347
  return;
5331
5348
  }
5332
5349
  }
5333
- function isRecord$1(value) {
5350
+ function isRecord(value) {
5334
5351
  return typeof value === "object" && value !== null && !Array.isArray(value);
5335
5352
  }
5336
5353
  function readPackageJsonPrettierKey(filePath) {
5337
5354
  const contents = readFileOrUndefined(filePath);
5338
5355
  if (contents === void 0) return;
5339
5356
  const parsed = parseJsonLike(contents);
5340
- const prettierKey = isRecord$1(parsed) ? parsed["prettier"] : void 0;
5341
- return isRecord$1(prettierKey) ? prettierKey : void 0;
5357
+ const prettierKey = isRecord(parsed) ? parsed["prettier"] : void 0;
5358
+ return isRecord(prettierKey) ? prettierKey : void 0;
5342
5359
  }
5343
5360
  /**
5344
5361
  * Load a JS Prettier config. Node's `require` handles ESM entry points
@@ -5351,8 +5368,8 @@ function readPackageJsonPrettierKey(filePath) {
5351
5368
  function requireConfigModule(filePath) {
5352
5369
  try {
5353
5370
  const loaded = createRequire(filePath)(filePath);
5354
- const value = isRecord$1(loaded) && "default" in loaded ? loaded["default"] : loaded;
5355
- 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;
5356
5373
  } catch {
5357
5374
  return;
5358
5375
  }
@@ -5370,7 +5387,7 @@ function readConfigFile(filePath) {
5370
5387
  const contents = readFileOrUndefined(filePath);
5371
5388
  if (contents === void 0) return;
5372
5389
  const parsed = parseJsonLike(contents) ?? parseYamlLike(contents);
5373
- return isRecord$1(parsed) ? parsed : void 0;
5390
+ return isRecord(parsed) ? parsed : void 0;
5374
5391
  }
5375
5392
  function ancestorDirectories(cwd) {
5376
5393
  const directories = [];
@@ -5569,21 +5586,216 @@ function buildOxfmtOptions({ oxfmtConfigOptions = {}, oxfmtOptions, prettierOpti
5569
5586
  };
5570
5587
  }
5571
5588
  //#endregion
5572
- //#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.";
5573
5591
  /**
5574
- * Internal runtime type guards. Prefer these over `as` assertions so values
5575
- * crossing untyped boundaries (`JSON.parse`, dynamic `import`, plugin objects)
5576
- * 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.
5577
5597
  */
5598
+ function restrictedDomImportRule(domPackage) {
5599
+ if (domPackage === void 0) return;
5600
+ return ["error", { paths: [domImportRestriction(domPackage)] }];
5601
+ }
5578
5602
  /**
5579
- * Whether a value is a non-null, non-array object usable as a string-keyed
5580
- * record. Narrows `unknown` without an assertion.
5603
+ * Merge the direct DOM Testing Library restriction into an existing
5604
+ * `no-restricted-imports` entry.
5581
5605
  *
5582
- * @param value - The value to test.
5583
- * @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.
5584
5612
  */
5585
- function isRecord(value) {
5586
- 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);
5587
5799
  }
5588
5800
  createRequire(import.meta.url);
5589
5801
  /**
@@ -5639,7 +5851,7 @@ function resolveNodeMajor(settings, cwd = process.cwd()) {
5639
5851
  let manifest = {};
5640
5852
  try {
5641
5853
  const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
5642
- if (isRecord(parsed)) manifest = parsed;
5854
+ if (isRecord$1(parsed)) manifest = parsed;
5643
5855
  } catch {}
5644
5856
  const range = readNodeRange(manifest);
5645
5857
  if (range !== void 0) return parseNodeMajor(range);
@@ -5657,7 +5869,7 @@ function toSourceGlob(glob) {
5657
5869
  }
5658
5870
  function getOverrides(options, key) {
5659
5871
  const sub = resolveSubOptions(options, key);
5660
- if (!isRecord(sub)) return {
5872
+ if (!isRecord$1(sub)) return {
5661
5873
  overrides: {},
5662
5874
  overridesTypeAware: {}
5663
5875
  };
@@ -5828,7 +6040,7 @@ function resolveOxfmtConfigOptionsSync() {
5828
6040
  try {
5829
6041
  const content = fs.readFileSync(configPath, "utf-8");
5830
6042
  const parsed = JSON.parse(content);
5831
- if (!isRecord(parsed)) continue;
6043
+ if (!isRecord$1(parsed)) continue;
5832
6044
  const { $schema: _, ...config } = parsed;
5833
6045
  return config;
5834
6046
  } catch {
@@ -5882,7 +6094,7 @@ function parseNodeMajor(range) {
5882
6094
  function readSettingsNodeVersion(settings) {
5883
6095
  for (const key of ["n", "node"]) {
5884
6096
  const setting = settings?.[key];
5885
- const version = isRecord(setting) ? setting["version"] : void 0;
6097
+ const version = isRecord$1(setting) ? setting["version"] : void 0;
5886
6098
  if (typeof version === "string") return version;
5887
6099
  }
5888
6100
  }
@@ -5895,9 +6107,9 @@ function readSettingsNodeVersion(settings) {
5895
6107
  * @returns The declared range, or `undefined` when the manifest declares none.
5896
6108
  */
5897
6109
  function readNodeRange({ devEngines, engines }) {
5898
- if (isRecord(engines) && typeof engines["node"] === "string") return engines["node"];
5899
- const runtime = isRecord(devEngines) ? devEngines["runtime"] : void 0;
5900
- 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"];
5901
6113
  return typeof version === "string" ? version : void 0;
5902
6114
  }
5903
6115
  //#endregion
@@ -7492,7 +7704,7 @@ const require$1 = createRequire(import.meta.url);
7492
7704
  function oxlintOxfmt(options) {
7493
7705
  const { componentExts: componentExtensions = [], files: oxfmtFiles, oxfmtOptions = {} } = options ?? {};
7494
7706
  const configPrettier = require$1("eslint-config-prettier/flat");
7495
- const configPrettierRules = isRecord(configPrettier) && isRecord(configPrettier["rules"]) ? configPrettier["rules"] : {};
7707
+ const configPrettierRules = isRecord$1(configPrettier) && isRecord$1(configPrettier["rules"]) ? configPrettier["rules"] : {};
7496
7708
  const rulesToIgnore = /* @__PURE__ */ new Set(["curly", "style/quotes"]);
7497
7709
  const canonicalDisables = {};
7498
7710
  const prettierRuleNames = Object.keys(renameRules(configPrettierRules, defaultPluginRenaming));
@@ -7825,177 +8037,6 @@ function oxlintPromise() {
7825
8037
  });
7826
8038
  }
7827
8039
  //#endregion
7828
- //#region src/rules/react.ts
7829
- /**
7830
- * Build the direct DOM Testing Library import restriction shared by both
7831
- * engines.
7832
- *
7833
- * @param domPackage - Package name for DOM Testing Library.
7834
- * @returns The configured restriction, or `undefined` when no package is set.
7835
- */
7836
- function restrictedDomImportRule(domPackage) {
7837
- if (domPackage === void 0) return;
7838
- return ["error", { paths: [{
7839
- name: domPackage,
7840
- 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."
7841
- }] }];
7842
- }
7843
- /**
7844
- * React rules shared between the ESLint and oxlint factories.
7845
- *
7846
- * The type-aware react rules stay in the ESLint config (oxlint jsPlugins have
7847
- * no type information).
7848
- *
7849
- * @param options - Shared rule options.
7850
- * @returns The rule map.
7851
- */
7852
- function reactRules({ domPackage, filenameCase = "kebabCase", reactCompiler = true, stylistic = true, testing = false } = {}) {
7853
- const restrictedDomImport = testing ? restrictedDomImportRule(domPackage) : void 0;
7854
- return {
7855
- "flawless/no-unnecessary-use-callback": "error",
7856
- "flawless/no-unnecessary-use-memo": "error",
7857
- "flawless/purity": "error",
7858
- "small-rules/react-hooks-strict-return": "error",
7859
- ...reactCompiler ? {
7860
- "react/globals": "error",
7861
- "react/refs": "error"
7862
- } : {},
7863
- "react-jsx/no-children-prop": "warn",
7864
- "react-jsx/no-children-prop-with-children": "error",
7865
- "react-jsx/no-comment-textnodes": "off",
7866
- "react-jsx/no-key-after-spread": "error",
7867
- "react-jsx/no-leaked-dollar": "warn",
7868
- "react-jsx/no-leaked-semicolon": "warn",
7869
- "react/error-boundaries": "error",
7870
- "react/exhaustive-deps": "error",
7871
- "react/immutability": "error",
7872
- "react/no-access-state-in-setstate": "error",
7873
- "react/no-array-index-key": "warn",
7874
- "react/no-children-count": "warn",
7875
- "react/no-children-for-each": "warn",
7876
- "react/no-children-map": "warn",
7877
- "react/no-children-only": "warn",
7878
- "react/no-children-to-array": "warn",
7879
- "react/no-class-component": "error",
7880
- "react/no-clone-element": "warn",
7881
- "react/no-component-will-mount": "error",
7882
- "react/no-component-will-receive-props": "error",
7883
- "react/no-component-will-update": "error",
7884
- "react/no-create-ref": "error",
7885
- "react/no-direct-mutation-state": "error",
7886
- "react/no-duplicate-key": "error",
7887
- "react/no-forward-ref": "off",
7888
- "react/no-missing-component-display-name": "error",
7889
- "react/no-missing-context-display-name": "error",
7890
- "react/no-missing-key": "error",
7891
- "react/no-misused-capture-owner-stack": "off",
7892
- "react/no-nested-component-definitions": "warn",
7893
- "react/no-nested-lazy-component-declarations": "warn",
7894
- "react/no-set-state-in-component-did-mount": "warn",
7895
- "react/no-set-state-in-component-did-update": "warn",
7896
- "react/no-set-state-in-component-will-update": "warn",
7897
- "react/no-unnecessary-use-prefix": "error",
7898
- "react/no-unsafe-component-will-mount": "error",
7899
- "react/no-unsafe-component-will-receive-props": "error",
7900
- "react/no-unsafe-component-will-update": "error",
7901
- "react/no-unstable-context-value": "error",
7902
- "react/no-unstable-default-props": ["error", { safeDefaultProps: [
7903
- "Axes",
7904
- "BrickColor",
7905
- "CatalogSearchParams",
7906
- "CFrame",
7907
- "Color3",
7908
- "ColorSequence",
7909
- "CFrame",
7910
- "Content",
7911
- "DateTime",
7912
- "DockWidgetPluginGuiInfo",
7913
- "Enum",
7914
- "Faces",
7915
- "FloatCurveKey",
7916
- "Font",
7917
- "NumberRange",
7918
- "NumberSequence",
7919
- "NumberSequenceKeypoint",
7920
- "OverlapParams",
7921
- "Path2DControlPoint",
7922
- "PathWaypoint",
7923
- "PhysicalProperties",
7924
- "Ray",
7925
- "RaycastParams",
7926
- "Rect",
7927
- "Region3",
7928
- "Region3int16",
7929
- "RotationCurveKey",
7930
- "SecurityCapabilities",
7931
- "TweenInfo",
7932
- "UDim",
7933
- "UDim2",
7934
- "ValueCurveKey",
7935
- "Vector2",
7936
- "Vector3",
7937
- "Vector3int16"
7938
- ] }],
7939
- "react/no-unused-class-component-members": "off",
7940
- "react/no-unused-state": "error",
7941
- "react/no-use-context": "off",
7942
- "react/rules-of-hooks": "error",
7943
- "react/set-state-in-effect": "error",
7944
- "react/set-state-in-render": "error",
7945
- "react/static-components": "error",
7946
- "react/use-memo": "error",
7947
- "react/use-state": ["error", {
7948
- enforceAssignment: false,
7949
- enforceSetterName: false
7950
- }],
7951
- ...testing ? {
7952
- "testing-library/await-async-queries": "error",
7953
- "testing-library/await-async-utils": "error",
7954
- "testing-library/no-await-sync-events": ["error", { eventModules: ["fire-event"] }],
7955
- "testing-library/no-await-sync-queries": "error",
7956
- "testing-library/no-container": "error",
7957
- "testing-library/no-debugging-utils": "error",
7958
- "testing-library/no-manual-cleanup": "error",
7959
- "testing-library/no-promise-in-fire-event": "error",
7960
- "testing-library/no-render-in-lifecycle": "error",
7961
- "testing-library/no-unnecessary-act": "error",
7962
- "testing-library/no-wait-for-multiple-assertions": "error",
7963
- "testing-library/no-wait-for-side-effects": "error",
7964
- "testing-library/no-wait-for-snapshot": "error",
7965
- "testing-library/prefer-explicit-assert": "error",
7966
- "testing-library/prefer-find-by": "error",
7967
- "testing-library/prefer-presence-queries": "error",
7968
- "testing-library/prefer-query-by-disappearance": "error",
7969
- "testing-library/prefer-screen-queries": "error",
7970
- "testing-library/render-result-naming-convention": "error",
7971
- ...restrictedDomImport !== void 0 ? { "no-restricted-imports": restrictedDomImport } : {}
7972
- } : {},
7973
- ...stylistic !== false ? {
7974
- "flawless/jsx-shorthand-boolean": "warn",
7975
- "flawless/jsx-shorthand-fragment": "warn",
7976
- "flawless/prefer-destructuring-assignment": "error",
7977
- "flawless/react-namespace": "error",
7978
- "one-var": "off",
7979
- "react-jsx/no-useless-fragment": "warn",
7980
- "react-naming-convention/context-name": "error",
7981
- "react-naming-convention/ref-name": "error",
7982
- "react/use-state": "error",
7983
- "style/jsx-curly-brace-presence": ["error", {
7984
- children: "never",
7985
- propElementValues: "always",
7986
- props: "never"
7987
- }],
7988
- "style/jsx-newline": "error",
7989
- "style/jsx-self-closing-comp": "error",
7990
- "unicorn/filename-case": ["error", {
7991
- case: filenameCase,
7992
- ignore: ["^[A-Z0-9]+.md$"],
7993
- multipleFileExtensions: true
7994
- }]
7995
- } : {}
7996
- };
7997
- }
7998
- //#endregion
7999
8040
  //#region src/oxlint/configs/react.ts
8000
8041
  /**
8001
8042
  * React configs for oxlint: the shared react family plus oxlint's native
@@ -9179,6 +9220,10 @@ function isentinel(factoryOptions, ...userConfigs) {
9179
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;
9180
9221
  const rootGlobs = mergeGlobs(GLOB_ROOT, customRootGlobs);
9181
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;
9182
9227
  const nativeOnly = userJsPlugins === false;
9183
9228
  const robloxScopedFiles = typeof options.roblox === "object" && options.roblox.files !== void 0 ? options.roblox.files.flat() : void 0;
9184
9229
  const hasComplement = !enableRoblox || robloxScopedFiles !== void 0;
@@ -9289,15 +9334,12 @@ function isentinel(factoryOptions, ...userConfigs) {
9289
9334
  ...resolveSubOptions(options, "perfectionist"),
9290
9335
  type: projectType
9291
9336
  }), oxlintStylistic(stylisticOptions));
9292
- if (enableReact !== false) {
9293
- const reactOptions = typeof enableReact === "object" ? enableReact : {};
9294
- configs.push(oxlintReact({
9295
- roblox: enableRoblox,
9296
- settings: options.settings,
9297
- stylistic: stylisticOptions,
9298
- ...reactOptions
9299
- }));
9300
- }
9337
+ if (enableReact !== false) configs.push(oxlintReact({
9338
+ roblox: enableRoblox,
9339
+ settings: options.settings,
9340
+ stylistic: stylisticOptions,
9341
+ ...reactOptions
9342
+ }));
9301
9343
  if (enableSpellCheck !== false) configs.push(oxlintSpelling({
9302
9344
  ...resolveSubOptions(options, "spellCheck"),
9303
9345
  componentExts: componentExtensions,
@@ -9358,6 +9400,14 @@ function isentinel(factoryOptions, ...userConfigs) {
9358
9400
  for (const jsPlugin of fragmentJsPlugins) jsPlugins.set(jsPluginKey(jsPlugin), jsPlugin);
9359
9401
  if (config.settings) Object.assign(mergedSettings, config.settings);
9360
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
+ }
9361
9411
  override.files = override.files.map(anchorOxlintGlob);
9362
9412
  if (override.excludeFiles !== void 0) override.excludeFiles = override.excludeFiles.map(anchorOxlintGlob);
9363
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.35",
3
+ "version": "6.0.0-beta.37",
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.35"
146
+ "@isentinel/eslint-config": "6.0.0-beta.37"
147
147
  },
148
148
  "peerDependencies": {
149
149
  "@vitest/eslint-plugin": "^1.6.4",