@ashstack/lint 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +17 -3
  2. package/dist/core/index.d.ts +7 -3
  3. package/dist/core/index.js +1 -1
  4. package/dist/core/rules/core/no-comments.js +1 -1
  5. package/dist/lib/ast.js +1 -1
  6. package/dist/lib/effect-plugin.js +1 -0
  7. package/dist/lib/module.js +1 -1
  8. package/dist/lib/registry.js +1 -0
  9. package/dist/lib/rule-types/core.d.ts +72 -0
  10. package/dist/lib/rule-types/core.js +1 -0
  11. package/dist/lib/rule-types/react-native.d.ts +333 -0
  12. package/dist/lib/rule-types/react-native.js +1 -0
  13. package/dist/lib/rule-types/react.d.ts +153 -0
  14. package/dist/lib/rule-types/react.js +1 -0
  15. package/dist/lib/types.d.ts +39 -11
  16. package/dist/modules.js +1 -0
  17. package/dist/react/index.d.ts +6 -1
  18. package/dist/react/index.js +1 -1
  19. package/dist/react/rules/query/next-page-param-undefined.js +1 -1
  20. package/dist/react/rules/query/no-fetch-in-query-fn.js +1 -1
  21. package/dist/react/rules/query/no-inline-keys.js +1 -1
  22. package/dist/react/rules/react/index.js +1 -0
  23. package/dist/react/rules/react/no-svg-without-title.js +1 -0
  24. package/dist/react/rules/react/no-unlabeled-icon-button.js +1 -0
  25. package/dist/react/rules/react/shared.js +1 -0
  26. package/dist/react/rules/tailwind/index.js +1 -0
  27. package/dist/react/rules/tailwind/prefer-cn.js +1 -0
  28. package/dist/react/rules/tailwind/shared.js +1 -0
  29. package/dist/react/rules/tailwind/use-logical-classes.js +1 -0
  30. package/dist/react/rules/tanstack-router/index.js +1 -0
  31. package/dist/react/rules/tanstack-router/no-search-casts.js +1 -0
  32. package/dist/react/rules/tanstack-router/require-selector.js +1 -0
  33. package/dist/react/rules/tanstack-router/shared.js +1 -0
  34. package/dist/react-native/index.d.ts +7 -1
  35. package/dist/react-native/index.js +1 -1
  36. package/dist/react-native/rules/legend-list/index.js +1 -1
  37. package/dist/react-native/rules/react-native/no-manual-memo.js +1 -1
  38. package/package.json +6 -10
  39. package/vendor/react-effect/LICENSE +21 -0
  40. package/vendor/react-effect/README.md +25 -0
  41. package/vendor/react-effect/index.mjs +559 -0
  42. package/dist/core/rules/core/components-tsx-only.d.ts +0 -2
  43. package/dist/core/rules/core/hoist-intl.d.ts +0 -2
  44. package/dist/core/rules/core/index.d.ts +0 -2
  45. package/dist/core/rules/core/no-comments.d.ts +0 -2
  46. package/dist/core/rules/core/no-naming-convention.d.ts +0 -2
  47. package/dist/core/rules/core/no-packed-condition.d.ts +0 -2
  48. package/dist/core/rules/core/use-design-system.d.ts +0 -2
  49. package/dist/core/rules/zod/index.d.ts +0 -2
  50. package/dist/core/rules/zod/prefer-enum.d.ts +0 -2
  51. package/dist/index.d.ts +0 -4
  52. package/dist/index.js +0 -1
  53. package/dist/lib/ast.d.ts +0 -44
  54. package/dist/lib/detect.d.ts +0 -9
  55. package/dist/lib/merge.d.ts +0 -9
  56. package/dist/lib/module.d.ts +0 -19
  57. package/dist/react/rules/i18n/index.d.ts +0 -2
  58. package/dist/react/rules/i18n/no-bare-attrs.d.ts +0 -2
  59. package/dist/react/rules/i18n/no-bare-text.d.ts +0 -2
  60. package/dist/react/rules/i18n/no-bare-toast.d.ts +0 -2
  61. package/dist/react/rules/query/index.d.ts +0 -2
  62. package/dist/react/rules/query/next-page-param-undefined.d.ts +0 -2
  63. package/dist/react/rules/query/no-deprecated-filters.d.ts +0 -2
  64. package/dist/react/rules/query/no-fetch-in-query-fn.d.ts +0 -2
  65. package/dist/react/rules/query/no-inline-keys.d.ts +0 -2
  66. package/dist/react/rules/query/require-destructured-hooks.d.ts +0 -2
  67. package/dist/react/rules/query/shared.d.ts +0 -2
  68. package/dist/react/rules/query/shared.js +0 -1
  69. package/dist/react/rules/zustand/index.d.ts +0 -2
  70. package/dist/react/rules/zustand/require-selector.d.ts +0 -2
  71. package/dist/react-native/rules/keyboard/index.d.ts +0 -2
  72. package/dist/react-native/rules/legend-list/index.d.ts +0 -2
  73. package/dist/react-native/rules/legend-list/no-flex-in-content-container.d.ts +0 -2
  74. package/dist/react-native/rules/legend-list/no-index-key-extractor.d.ts +0 -2
  75. package/dist/react-native/rules/legend-list/no-inline-data.d.ts +0 -2
  76. package/dist/react-native/rules/legend-list/no-inline-data.js +0 -1
  77. package/dist/react-native/rules/legend-list/no-inline-extra-data.d.ts +0 -2
  78. package/dist/react-native/rules/legend-list/no-inline-extra-data.js +0 -1
  79. package/dist/react-native/rules/legend-list/no-inline-render-item-props.d.ts +0 -2
  80. package/dist/react-native/rules/legend-list/no-mixed-children.d.ts +0 -2
  81. package/dist/react-native/rules/legend-list/no-remount-key.d.ts +0 -2
  82. package/dist/react-native/rules/legend-list/no-scrollview-map.d.ts +0 -2
  83. package/dist/react-native/rules/legend-list/no-unsupported-props.d.ts +0 -2
  84. package/dist/react-native/rules/legend-list/required-props.d.ts +0 -2
  85. package/dist/react-native/rules/legend-list/shared.d.ts +0 -19
  86. package/dist/react-native/rules/legend-list/typed-items-need-item-type.d.ts +0 -2
  87. package/dist/react-native/rules/legend-state/index.d.ts +0 -2
  88. package/dist/react-native/rules/legend-state/naming.d.ts +0 -2
  89. package/dist/react-native/rules/legend-state/no-assignment.d.ts +0 -2
  90. package/dist/react-native/rules/legend-state/no-nested-observable.d.ts +0 -2
  91. package/dist/react-native/rules/legend-state/no-object-selector.d.ts +0 -2
  92. package/dist/react-native/rules/legend-state/no-observable-in-component.d.ts +0 -2
  93. package/dist/react-native/rules/legend-state/no-peek-in-selector.d.ts +0 -2
  94. package/dist/react-native/rules/legend-state/no-react-mirror.d.ts +0 -2
  95. package/dist/react-native/rules/legend-state/no-untracked-get-in-jsx.d.ts +0 -2
  96. package/dist/react-native/rules/legend-state/shared.d.ts +0 -9
  97. package/dist/react-native/rules/react-native/hoist-stateless-function.d.ts +0 -2
  98. package/dist/react-native/rules/react-native/index.d.ts +0 -2
  99. package/dist/react-native/rules/react-native/no-conditional-style-array.d.ts +0 -2
  100. package/dist/react-native/rules/react-native/no-dynamic-import.d.ts +0 -2
  101. package/dist/react-native/rules/react-native/no-keyboard-will-events.d.ts +0 -2
  102. package/dist/react-native/rules/react-native/no-leaked-render.d.ts +0 -2
  103. package/dist/react-native/rules/react-native/no-manual-memo.d.ts +0 -2
  104. package/dist/react-native/rules/react-native/no-redundant-view-nesting.d.ts +0 -2
  105. package/dist/react-native/rules/react-native/no-rn-image-network-source.d.ts +0 -2
  106. package/dist/react-native/rules/react-native/no-rn-namespace-import.d.ts +0 -2
  107. package/dist/react-native/rules/react-native/no-scroll-position-state.d.ts +0 -2
  108. package/dist/react-native/rules/react-native/no-unlabeled-icon-pressable.d.ts +0 -2
  109. package/dist/react-native/rules/reanimated/animated-reaction-safety.d.ts +0 -2
  110. package/dist/react-native/rules/reanimated/animated-style-needs-animated-component.d.ts +0 -2
  111. package/dist/react-native/rules/reanimated/animated-updater-purity.d.ts +0 -2
  112. package/dist/react-native/rules/reanimated/gpu-properties-only.d.ts +0 -2
  113. package/dist/react-native/rules/reanimated/hoist-layout-animation-builder.d.ts +0 -2
  114. package/dist/react-native/rules/reanimated/index.d.ts +0 -2
  115. package/dist/react-native/rules/reanimated/interpolate-needs-clamp.d.ts +0 -2
  116. package/dist/react-native/rules/reanimated/no-react-state-from-continuous-worklet.d.ts +0 -2
  117. package/dist/react-native/rules/reanimated/no-shared-value-dot-value.d.ts +0 -2
  118. package/dist/react-native/rules/reanimated/prefer-lazy-shared-value-initializer.d.ts +0 -2
  119. package/dist/react-native/rules/reanimated/schedule-on-rn-scope.d.ts +0 -2
  120. package/dist/react-native/rules/reanimated/shared-value-usage.d.ts +0 -2
  121. package/dist/react-native/rules/reanimated/shared.d.ts +0 -5
  122. package/dist/react-native/rules/skia/canvas-opaque.d.ts +0 -2
  123. package/dist/react-native/rules/skia/index.d.ts +0 -2
  124. package/dist/react-native/rules/turbo-image/index.d.ts +0 -2
  125. package/dist/react-native/rules/turbo-image/require-cache-policy.d.ts +0 -2
  126. package/dist/react-native/rules/turbo-image/require-resize.d.ts +0 -2
  127. package/dist/react-native/rules/unistyles/animated-theme.d.ts +0 -2
  128. package/dist/react-native/rules/unistyles/content-container.d.ts +0 -2
  129. package/dist/react-native/rules/unistyles/in-sheet.d.ts +0 -2
  130. package/dist/react-native/rules/unistyles/index.d.ts +0 -2
  131. package/dist/react-native/rules/unistyles/insets.d.ts +0 -2
  132. package/dist/react-native/rules/unistyles/no-hardcoded-color.d.ts +0 -2
  133. package/dist/react-native/rules/unistyles/no-hardcoded-spacing.d.ts +0 -2
  134. package/dist/react-native/rules/unistyles/no-margin.d.ts +0 -2
  135. package/dist/react-native/rules/unistyles/no-style-spread.d.ts +0 -2
  136. package/dist/react-native/rules/unistyles/no-unused-styles.d.ts +0 -2
  137. package/dist/react-native/rules/unistyles/rtl-style-call.d.ts +0 -2
  138. package/dist/react-native/rules/unistyles/shared.d.ts +0 -16
  139. package/dist/react-native/rules/unistyles/theme-screen-component.d.ts +0 -2
  140. package/dist/react-native/rules/unistyles/theme-style-attr.d.ts +0 -2
  141. package/dist/react-native/stylesheet.d.ts +0 -11
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ashstack/lint
2
2
 
3
- Strict shared [oxlint](https://oxc.rs) config: 147 rules on plain TypeScript, 227 with React, 238 on React Native, over 70 of them custom-built. Three entries, each containing the one before it: `core()`, `react()`, `react-native()`.
3
+ Strict shared [oxlint](https://oxc.rs) config: <!-- rule-counts -->118 rules on plain TypeScript, 202 with React, 257 on React Native, 74 of them custom-built<!-- /rule-counts -->. Three entries, each containing the one before it: `core()`, `react()`, `react-native()`.
4
4
 
5
5
  Library-specific rules ship only when you depend on that library, so nothing fires about code you don't have.
6
6
 
@@ -14,7 +14,7 @@ bun add -d oxlint oxlint-tsgolint @ashstack/lint
14
14
 
15
15
  ```ts
16
16
  // oxlint.config.mts (JSON configs can't resolve npm packages, so TS config only)
17
- import { core } from "@ashstack/lint"; // or: react, reactNative
17
+ import { core } from "@ashstack/lint/core"; // or: /react, /react-native
18
18
  import { defineConfig } from "oxlint";
19
19
 
20
20
  export default defineConfig({
@@ -29,6 +29,20 @@ oxlint --type-aware --deny-warnings .
29
29
 
30
30
  Type-aware rules need the `oxlint-tsgolint` peer. Node 22.18+.
31
31
 
32
+ Import the entry you use. There is no package root to import from, so nothing
33
+ loads rule files you have no use for: `@ashstack/lint/core` loads 27 KB where
34
+ a root re-export of all three would load 137 KB. `react` and `react-native`
35
+ load most of it either way, since they configure most of the rules.
36
+
37
+ ```ts
38
+ import { core } from "@ashstack/lint/core";
39
+ import { react } from "@ashstack/lint/react";
40
+ import { reactNative } from "@ashstack/lint/react-native";
41
+ ```
42
+
43
+ Each entry also has a default export, and carries its own options type
44
+ (`CoreOptions`, `ReactOptions`, `ReactNativeOptions`).
45
+
32
46
  ## What each entry adds
33
47
 
34
48
  `core()` is a strict eslint / typescript / unicorn / promise / import base plus the `@ashstack/core/` convention rules. `react()` adds react, jsx-a11y, React Compiler and you-might-not-need-an-effect. `react-native()` adds the generic `@ashstack/react-native/` rules: leaked renders, view nesting, iOS-only keyboard events, remote images.
@@ -49,7 +63,7 @@ Import bans follow detection only; disable one by rule name.
49
63
 
50
64
  ## Opt-in rules
51
65
 
52
- Off by default, since they need a team decision first: `@ashstack/core/no-comments`, `use-design-system` and `components-tsx-only`.
66
+ <!-- opt-in -->Off by default, since they need a team decision first: `@ashstack/core/use-design-system` and `@ashstack/core/components-tsx-only`.<!-- /opt-in -->
53
67
 
54
68
  ## Rule reference
55
69
 
@@ -1,5 +1,4 @@
1
1
  import type { CoreOptions, OxlintConfig } from "../lib/types.js";
2
- export declare const coreModules: import("../lib/types.js").ModuleManifest[];
3
2
  /** Test code is a different kind of code: long tables of cases, and assertions that know more than the type does. */
4
3
  export declare const TEST_FILES: string[];
5
4
  /**
@@ -7,9 +6,14 @@ export declare const TEST_FILES: string[];
7
6
  * Library modules (zod) auto-enable when the consumer depends on the library;
8
7
  * pass an explicit boolean to force either way.
9
8
  *
10
- * Some of the enabled `typescript/` rules are type-aware (`no-floating-promises`,
11
- * `no-unnecessary-condition`, …): they only report when oxlint runs with
9
+ * Some of the enabled `typescript/` rules are type-aware (`no-unnecessary-condition`,
10
+ * `no-unsafe-type-assertion`, …): they only report when oxlint runs with
12
11
  * `--type-aware` and the `oxlint-tsgolint` peer installed.
12
+ *
13
+ * @see [every rule `core()` sets](https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#core)
13
14
  */
14
15
  declare const core: (options?: CoreOptions) => OxlintConfig;
16
+ export { core };
17
+ export type { CoreRuleId } from "../lib/rule-types/core.js";
18
+ export type { CoreOptions, ModuleManifest } from "../lib/types.js";
15
19
  export default core;
@@ -1 +1 @@
1
- import{composeModules as o}from"../lib/module.js";import n from"./rules/core/index.js";import s from"./rules/zod/index.js";export const coreModules=[n,s];const t={"array-callback-return":["error",{allowImplicit:!0}],"constructor-super":"error","default-param-last":"error",eqeqeq:["error","always",{null:"ignore"}],"for-direction":"error","getter-return":"error","func-style":["error","expression"],"no-array-constructor":"error","no-async-promise-executor":"error","no-case-declarations":"error","no-class-assign":"error","no-compare-neg-zero":"error","no-cond-assign":"error","no-const-assign":"error","no-constant-condition":"error","no-control-regex":"error","no-debugger":"error","no-dupe-class-members":"error","no-dupe-else-if":"error","no-dupe-keys":"error","no-duplicate-case":"error","no-empty":"error","no-empty-character-class":"error","no-empty-pattern":"error","no-ex-assign":"error","no-extra-boolean-cast":"error","no-fallthrough":"error","no-func-assign":"error","no-global-assign":"error","no-import-assign":"error","no-irregular-whitespace":"error","no-loss-of-precision":"error","no-misleading-character-class":"error","no-negated-condition":"error","no-nested-ternary":"error","no-new-native-nonconstructor":"error","no-nonoctal-decimal-escape":"error","no-obj-calls":"error","no-param-reassign":"error","no-prototype-builtins":"error","no-redeclare":"off","no-regex-spaces":"error","no-self-assign":"error","no-setter-return":"error","no-shadow-restricted-names":"error","no-sparse-arrays":"error","no-this-before-super":"error","no-undef":"off","no-unreachable":"error","no-unsafe-finally":"error","no-unsafe-negation":"error","no-unsafe-optional-chaining":"error","no-unused-labels":"error","no-unused-private-class-members":"error","no-useless-catch":"error","no-useless-constructor":"error","no-useless-escape":"error","no-var":"error","no-with":"error","object-shorthand":["error","methods"],"prefer-rest-params":"error","require-yield":"error","use-isnan":"error","valid-typeof":"error","no-unused-vars":["error",{args:"after-used",argsIgnorePattern:"^_",caughtErrors:"all",caughtErrorsIgnorePattern:"^_",varsIgnorePattern:"^_",destructuredArrayIgnorePattern:"^_",ignoreRestSiblings:!0,fix:{imports:"safe-fix"}}],"no-use-before-define":"off",complexity:["error",{max:12}],"max-depth":["error",3],"max-params":["error",4],"max-lines":["error",{max:300,skipBlankLines:!0,skipComments:!0}],"max-lines-per-function":["error",{max:250,skipBlankLines:!0,skipComments:!0}],"eslint/no-await-in-loop":"error","eslint/no-console":"off","eslint/no-constant-binary-expression":"off","eslint/no-control-regex":"error","eslint/no-else-return":"error","eslint/no-empty":"error","eslint/no-empty-function":"error","eslint/no-new":"off","eslint/no-self-compare":"error","eslint/no-shadow":"error","eslint/no-template-curly-in-string":"error","eslint/no-unassigned-vars":"off","eslint/no-underscore-dangle":"off","eslint/no-unmodified-loop-condition":"off","eslint/no-unused-expressions":"off","eslint/no-useless-concat":"off","eslint/preserve-caught-error":"off","eslint/prefer-arrow-callback":"error","eslint/prefer-const":"error","eslint/prefer-template":"error","typescript/consistent-type-imports":"error","typescript/dot-notation":"error","typescript/no-confusing-void-expression":"error","typescript/no-empty-object-type":["error",{allowInterfaces:"with-single-extends"}],"typescript/no-explicit-any":"error","typescript/no-extra-non-null-assertion":"error","typescript/no-extraneous-class":"error","typescript/no-floating-promises":"error","typescript/no-import-type-side-effects":"error","typescript/no-misused-new":"error","typescript/no-misused-promises":["error",{checksVoidReturn:!1}],"typescript/no-namespace":"error","typescript/no-non-null-asserted-optional-chain":"error","typescript/no-non-null-assertion":"error","typescript/no-unnecessary-boolean-literal-compare":"error","typescript/no-unnecessary-condition":"error","typescript/no-unnecessary-type-assertion":"error","typescript/no-unnecessary-type-constraint":"error","typescript/no-unsafe-declaration-merging":"error","typescript/no-unsafe-function-type":"error","typescript/no-unsafe-type-assertion":"error","typescript/no-wrapper-object-types":"error","typescript/prefer-as-const":"error","typescript/prefer-namespace-keyword":"error","typescript/prefer-nullish-coalescing":"error","typescript/require-await":"error","typescript/switch-exhaustiveness-check":["error",{considerDefaultExhaustiveForUnions:!0}],"import/no-commonjs":"off","import/no-cycle":"error","import/no-duplicates":"error","import/no-named-as-default":"off","import/no-named-as-default-member":"off","import/no-self-import":"off","import/no-unassigned-import":"off","unicorn/consistent-function-scoping":"off","unicorn/no-array-reverse":"off","unicorn/no-array-sort":"off","unicorn/no-empty-file":"off","unicorn/no-new-array":"off","unicorn/no-useless-fallback-in-spread":"off","unicorn/no-useless-spread":"off","unicorn/prefer-add-event-listener":"off","unicorn/prefer-array-flat-map":"error","unicorn/prefer-date-now":"error","unicorn/prefer-logical-operator-over-ternary":"error","unicorn/prefer-node-protocol":"error","unicorn/prefer-optional-catch-binding":"error","unicorn/require-module-specifiers":"off","unicorn/throw-new-error":"error","promise/always-return":"off","promise/no-callback-in-promise":"off","promise/no-multiple-resolved":"off","promise/no-promise-in-callback":"off","oxc/no-async-endpoint-handlers":"off"};export const TEST_FILES=["**/*.test.ts","**/*.test.tsx","**/*.spec.ts","**/*.spec.tsx","**/__tests__/**","**/tests/**"];const i=(e={})=>{const r=o(coreModules,e);return{plugins:["eslint","typescript","import","unicorn","promise","oxc"],jsPlugins:r.jsPlugins,env:{builtin:!0,es2024:!0},categories:{correctness:"error",suspicious:"error",perf:"error",pedantic:"off",style:"off",restriction:"off",nursery:"off"},rules:{...t,...r.rules},overrides:[{files:["**/*.tsx"],rules:{"func-style":"off"}},{files:["**/*.d.ts"],rules:{"typescript/no-explicit-any":"off"}},{files:TEST_FILES,rules:{"max-lines":"off","max-lines-per-function":"off","typescript/await-thenable":"off","typescript/no-non-null-assertion":"off","typescript/no-unsafe-type-assertion":"off","typescript/require-await":"off","typescript/unbound-method":"off"}}]}};export default i;
1
+ import{composeModules as o}from"../lib/module.js";import{coreRegistry as n}from"../lib/registry.js";const s={"array-callback-return":["error",{allowImplicit:!0}],"constructor-super":"error","default-param-last":"error",eqeqeq:["error","always",{null:"ignore"}],"for-direction":"error","getter-return":"error","func-style":["error","expression"],"no-array-constructor":"error","no-async-promise-executor":"error","no-case-declarations":"error","no-class-assign":"error","no-compare-neg-zero":"error","no-cond-assign":"error","no-const-assign":"error","no-constant-condition":"error","no-control-regex":"error","no-debugger":"error","no-dupe-class-members":"error","no-dupe-else-if":"error","no-dupe-keys":"error","no-duplicate-case":"error","no-empty":"error","no-empty-character-class":"error","no-empty-pattern":"error","no-ex-assign":"error","no-extra-boolean-cast":"error","no-fallthrough":"error","no-func-assign":"error","no-global-assign":"error","no-import-assign":"error","no-irregular-whitespace":"error","no-loss-of-precision":"error","no-misleading-character-class":"error","no-negated-condition":"error","no-nested-ternary":"error","no-new-native-nonconstructor":"error","no-nonoctal-decimal-escape":"error","no-obj-calls":"error","no-param-reassign":"error","no-prototype-builtins":"error","no-redeclare":"off","no-regex-spaces":"error","no-self-assign":"error","no-setter-return":"error","no-shadow-restricted-names":"error","no-sparse-arrays":"error","no-this-before-super":"error","no-undef":"off","no-unreachable":"error","no-unsafe-finally":"error","no-unsafe-negation":"error","no-unsafe-optional-chaining":"error","no-unused-labels":"error","no-unused-private-class-members":"error","no-useless-catch":"error","no-useless-constructor":"error","no-useless-escape":"error","no-var":"error","no-with":"error","object-shorthand":["error","methods"],"prefer-rest-params":"error","require-yield":"error","use-isnan":"error","valid-typeof":"error","no-unused-vars":["error",{args:"after-used",argsIgnorePattern:"^_",caughtErrors:"all",caughtErrorsIgnorePattern:"^_",varsIgnorePattern:"^_",destructuredArrayIgnorePattern:"^_",ignoreRestSiblings:!0,fix:{imports:"safe-fix"}}],"no-use-before-define":"off",complexity:["error",{max:12}],"max-depth":["error",3],"max-params":["error",4],"max-lines":["error",{max:300,skipBlankLines:!0,skipComments:!0}],"max-lines-per-function":["error",{max:250,skipBlankLines:!0,skipComments:!0}],"eslint/no-await-in-loop":"error","eslint/no-console":"off","eslint/no-constant-binary-expression":"off","eslint/no-control-regex":"error","eslint/no-else-return":"error","eslint/no-empty":"error","eslint/no-empty-function":"error","eslint/no-new":"off","eslint/no-self-compare":"error","eslint/no-shadow":"error","eslint/no-template-curly-in-string":"error","eslint/no-unassigned-vars":"off","eslint/no-underscore-dangle":"off","eslint/no-unmodified-loop-condition":"off","eslint/no-unused-expressions":"off","eslint/no-useless-concat":"off","eslint/preserve-caught-error":"off","eslint/prefer-arrow-callback":"error","eslint/prefer-const":"error","eslint/prefer-template":"error","typescript/consistent-type-imports":"error","typescript/dot-notation":"error","typescript/no-confusing-void-expression":"error","typescript/no-empty-object-type":["error",{allowInterfaces:"with-single-extends"}],"typescript/no-explicit-any":"error","typescript/no-extra-non-null-assertion":"error","typescript/no-extraneous-class":"error","typescript/no-floating-promises":"off","typescript/no-import-type-side-effects":"error","typescript/no-misused-new":"error","typescript/no-misused-promises":["error",{checksVoidReturn:!1}],"typescript/no-namespace":"error","typescript/no-non-null-asserted-optional-chain":"error","typescript/no-non-null-assertion":"error","typescript/no-unnecessary-boolean-literal-compare":"error","typescript/no-unnecessary-condition":"error","typescript/no-unnecessary-type-assertion":"error","typescript/no-unnecessary-type-constraint":"error","typescript/no-unsafe-declaration-merging":"error","typescript/no-unsafe-function-type":"error","typescript/no-unsafe-type-assertion":"error","typescript/no-wrapper-object-types":"error","typescript/prefer-as-const":"error","typescript/prefer-namespace-keyword":"error","typescript/prefer-nullish-coalescing":"error","typescript/require-await":"error","typescript/switch-exhaustiveness-check":["error",{considerDefaultExhaustiveForUnions:!0}],"import/no-commonjs":"off","import/no-cycle":"error","import/no-duplicates":"error","import/no-named-as-default":"off","import/no-named-as-default-member":"off","import/no-self-import":"off","import/no-unassigned-import":"off","unicorn/consistent-function-scoping":"off","unicorn/no-array-reverse":"off","unicorn/no-array-sort":"off","unicorn/no-empty-file":"off","unicorn/no-new-array":"off","unicorn/no-useless-fallback-in-spread":"off","unicorn/no-useless-spread":"off","unicorn/prefer-add-event-listener":"off","unicorn/prefer-array-flat-map":"error","unicorn/prefer-date-now":"error","unicorn/prefer-logical-operator-over-ternary":"error","unicorn/prefer-node-protocol":"error","unicorn/prefer-optional-catch-binding":"error","unicorn/require-module-specifiers":"off","unicorn/throw-new-error":"error","promise/always-return":"off","promise/no-callback-in-promise":"off","promise/no-multiple-resolved":"off","promise/no-promise-in-callback":"off","oxc/no-async-endpoint-handlers":"off"};export const TEST_FILES=["**/*.test.ts","**/*.test.tsx","**/*.spec.ts","**/*.spec.tsx","**/__tests__/**","**/tests/**"];const core=(e={})=>{const r=o(n,e);return{plugins:["eslint","typescript","import","unicorn","promise","oxc"],jsPlugins:r.jsPlugins,env:{builtin:!0,es2024:!0},categories:{correctness:"error",suspicious:"error",perf:"error",pedantic:"off",style:"off",restriction:"off",nursery:"off"},rules:{...s,...r.rules},overrides:[{files:["**/*.tsx"],rules:{"func-style":"off"}},{files:["**/*.d.ts"],rules:{"typescript/no-explicit-any":"off"}},{files:TEST_FILES,rules:{"max-lines":"off","max-lines-per-function":"off","typescript/await-thenable":"off","typescript/no-non-null-assertion":"off","typescript/no-unsafe-type-assertion":"off","typescript/require-await":"off","typescript/unbound-method":"off"}}]}};export{core};export default core;
@@ -1 +1 @@
1
- import{optionsOf as y}from"../../../lib/ast.js";const E=["@ts-expect-error","@ts-ignore","@ts-nocheck","@ts-check","@jsx","@jsxRuntime","@jsxImportSource","prettier-ignore","eslint-disable","eslint-enable","oxlint-disable","oxlint-enable","oxfmt-ignore","react-doctor-disable","@type","<reference","#__PURE__","@__PURE__","v8 ignore","c8 ignore","istanbul ignore","@vitest-environment","EXPECT_PASS","EXPECT_FAIL"],f=/^what:\s*(?<fact>[\s\S]+)$/i,u=new Set(["Block","MultiLine"]),v=new Set(["Shebang","Hashbang"]),m=10,p=120,C=2,D="Make the code say this, then delete the line — nearly every comment here is one rename or one refactor away from unnecessary. Reach for the move that fits: Rename the value, function or type to the plain word this sentence uses; Extract Function for the block it summarises, named after the claim it makes; Guard Clause for the nesting it walks the reader through; split the clever expression it decodes into named steps. You are done when the sentence's content is legible in an identifier, a signature or the control flow.",A=" The `// what:` hatch carries one kind of fact: one that outlives this code — a platform bug, an upstream contract, a number measured on a device. A fact about what this code does fails that test, and the refactor is still owed.",_=" This project runs with the `// what:` hatch turned off, so the refactor is the only way out: there is no fact this file may state in prose.",H=(e)=>D+(e?A:_),k="Attach this block to the declaration it documents, or Rename and Extract Function until the code reads without it. A `/** */` block earns its place as documentation of the symbol directly beneath it.",d={block:"Rewrite this as a single `// what: <fact>` line comment. If the fact needs a paragraph, name the pieces in code until it fits on one line.",shortFact:`Write the fact after \`what:\` (at least ${m} characters), or delete the comment.`,tooLong:`Trim this \`what:\` line under ${p} characters, moving what is left into names in the code.`,stacked:"Keep one `what:` line here — the single irreducible fact — and refactor what the others explain into named values and functions."},x=(e,o)=>`Delete \`what:\` comments from this file until at most ${e} remain (it has ${o}): move the logic each one annotates into a function whose name carries what the comment says.`,P=new Set(["ExportNamedDeclaration","ExportDefaultDeclaration","ExportAllDeclaration","FunctionDeclaration","VariableDeclaration","ClassDeclaration","MethodDefinition","PropertyDefinition","Property","TSInterfaceDeclaration","TSTypeAliasDeclaration","TSEnumDeclaration","TSEnumMember","TSModuleDeclaration","TSDeclareFunction","TSPropertySignature","TSMethodSignature","TSIndexSignature","TSCallSignatureDeclaration","TSConstructSignatureDeclaration","TSAbstractMethodDefinition","TSAbstractPropertyDefinition","AccessorProperty","ImportDeclaration"]),M=(e)=>(u.has(e.type)?e.value.replace(/^[*\s]+/,""):e.value).trim(),O=(e)=>E.some((o)=>e.startsWith(o)),F=(e)=>u.has(e.type)&&e.value.startsWith("*"),R=(e)=>{const o=[];for(const a of e.sourceCode.getAllComments()){if(v.has(a.type))continue;const c=M(a);if(O(c))continue;o.push({comment:a,body:c})}return o},I=(e,o)=>{let a=o;while(/\s/.test(e.charAt(a)))a+=1;return a},L=(e,o,a)=>{if(u.has(e.type))return d.block;if(a.trim().length<m)return d.shortFact;if(o.length>p)return d.tooLong;return null},N=(e,o,a)=>e.slice(o.end,a.start).trim()==="",g=(e)=>e.escapeHatch!==!1;export const noComments={meta:{type:"problem",docs:{description:'Disallow every comment that is neither a `// what: <fact>` line nor a tooling directive. The message names the refactoring that removes it: Rename, Extract Function, Guard Clause. Surviving `// what:` lines are held to one short line each, at most `budget` per file (default 2); `escapeHatch: false` removes the hatch entirely, so no prose survives at all. With `jsdoc: "allow"`, a `/** */` block documenting the declaration directly beneath it is kept, while a floating one still reports.'},schema:[{type:"object",properties:{jsdoc:{enum:["allow","report"]},escapeHatch:{type:"boolean"},budget:{type:"integer",minimum:0}},additionalProperties:!1}],defaultOff:!0},createOnce(e){const o=new Set,a=(t)=>{o.add(t.start)},c={};for(const t of P)c[t]=a;const l=(t,n)=>{e.report({node:t,message:n})},S=(t,n)=>{const{text:r}=e.sourceCode,s=g(n);for(const{comment:i,body:h}of t){if(s&&f.test(h))continue;if(n.jsdoc!=="allow"||!F(i))l(i,H(s));else if(!o.has(I(r,i.end)))l(i,k)}},T=(t)=>{const n=[];for(const{comment:r,body:s}of t){const i=s.match(f)?.groups?.fact;if(i===void 0)continue;const h=L(r,s,i);if(h===null)n.push(r);else l(r,h)}return n},w=(t)=>{const n=e.sourceCode.getText();for(const[r,s]of t.entries()){const i=t[r-1];if(i&&N(n,i,s))l(s,d.stacked)}},b=(t,n)=>{const r=n.budget??C,s=t[r];if(s)l(s,x(r,t.length))};return{...c,before(){o.clear();return!0},"Program:exit"(){const t=y(e,{}),n=R(e);S(n,t);if(!g(t))return;const r=T(n);w(r);b(r,t)}}}};
1
+ import{optionsOf as b}from"../../../lib/ast.js";const v=["@ts-expect-error","@ts-ignore","@ts-nocheck","@ts-check","@jsx","@jsxRuntime","@jsxImportSource","prettier-ignore","eslint-disable","eslint-enable","oxlint-disable","oxlint-enable","oxfmt-ignore","react-doctor-disable","@type","<reference","#__PURE__","@__PURE__","v8 ignore","c8 ignore","istanbul ignore","@vitest-environment","EXPECT_PASS","EXPECT_FAIL"],E=/^(?<kind>what|why):\s*(?<fact>[\s\S]+)$/i,m=new Set(["Block","MultiLine"]),k=new Set(["Shebang","Hashbang"]),u=10,f=120,C=2,D="Make the code say this, then delete the line — nearly every comment here is one rename or one refactor away from unnecessary. Reach for the move that fits: Rename the value, function or type to the plain word this sentence uses; Extract Function for the block it summarises, named after the claim it makes; Guard Clause for the nesting it walks the reader through; split the clever expression it decodes into named steps. You are done when the sentence's content is legible in an identifier, a signature or the control flow.",A=" The `// what:` hatch carries one kind of fact: one that outlives this code — a platform bug, an upstream contract, a number measured on a device. A fact about what this code does fails that test, and the refactor is still owed.",_=" This project runs with the `// what:` hatch turned off, so the refactor is the only way out. The one line left is `// why:`, which a kept `memo` requires.",x=(e)=>D+(e?A:_),H="Attach this block to the declaration it documents, or Rename and Extract Function until the code reads without it. A `/** */` block earns its place as documentation of the symbol directly beneath it.",d={block:"Rewrite this as a single `// what: <fact>` line comment. If the fact needs a paragraph, name the pieces in code until it fits on one line.",shortFact:`Write the fact after \`what:\` (at least ${u} characters), or delete the comment.`,tooLong:`Trim this \`what:\` line under ${f} characters, moving what is left into names in the code.`,stacked:"Keep one `what:` line here — the single irreducible fact — and refactor what the others explain into named values and functions."},M=(e,t)=>`Delete \`what:\` comments from this file until at most ${e} remain (it has ${t}): move the logic each one annotates into a function whose name carries what the comment says.`,P=new Set(["ExportNamedDeclaration","ExportDefaultDeclaration","ExportAllDeclaration","FunctionDeclaration","VariableDeclaration","ClassDeclaration","MethodDefinition","PropertyDefinition","Property","TSInterfaceDeclaration","TSTypeAliasDeclaration","TSEnumDeclaration","TSEnumMember","TSModuleDeclaration","TSDeclareFunction","TSPropertySignature","TSMethodSignature","TSIndexSignature","TSCallSignatureDeclaration","TSConstructSignatureDeclaration","TSAbstractMethodDefinition","TSAbstractPropertyDefinition","AccessorProperty","ImportDeclaration"]),O=(e)=>(m.has(e.type)?e.value.replace(/^[*\s]+/,""):e.value).trim(),R=(e)=>v.some((t)=>e.startsWith(t)),F=(e)=>m.has(e.type)&&e.value.startsWith("*"),I=(e)=>{const t=[];for(const o of e.sourceCode.getAllComments()){if(k.has(o.type))continue;const l=O(o);if(R(l))continue;t.push({comment:o,body:l})}return t},L=(e,t)=>{let o=t;while(/\s/.test(e.charAt(o)))o+=1;return o},p=(e,t)=>{const o=E.exec(e.body)?.groups;if(!o?.kind||!o.fact)return[];const l=o.kind.toLowerCase()==="why"?"why":"what";if(l==="what"&&!t)return[];return[{...e,kind:l,fact:o.fact}]},N=(e,t,o)=>{if(m.has(e.type))return d.block;if(o.trim().length<u)return d.shortFact;if(t.length>f)return d.tooLong;return null},j=(e,t,o)=>e.slice(t.end,o.start).trim()==="",w=(e)=>e.escapeHatch!==!1;export const noComments={meta:{type:"problem",docs:{description:'Disallow every comment that is neither a `// what: <fact>` line, a `// why:` marker, nor a tooling directive. The message names the refactoring that removes it: Rename, Extract Function, Guard Clause. Surviving `// what:` lines are held to one short line each, at most `budget` per file (default 2); `escapeHatch: false` removes that hatch, so no discretionary prose survives. A `// why:` line is the marker `@ashstack/react-native/no-manual-memo` requires above a kept `memo`: held to the same one-line shape, never counted against `budget`, and kept even with `escapeHatch: false`, so the two rules run together. With `jsdoc: "allow"`, a `/** */` block documenting the declaration directly beneath it is kept, while a floating one still reports.'},schema:[{type:"object",properties:{jsdoc:{enum:["allow","report"]},escapeHatch:{type:"boolean"},budget:{type:"integer",minimum:0}},additionalProperties:!1}]},createOnce(e){const t=new Set,o=(n)=>{t.add(n.start)},l={};for(const n of P)l[n]=o;const h=(n,r)=>{e.report({node:n,message:r})},g=(n,r)=>{const{text:s}=e.sourceCode,c=w(r);for(const a of n){const{comment:i}=a;if(p(a,c).length>0)continue;if(r.jsdoc!=="allow"||!F(i))h(i,x(c));else if(!t.has(L(s,i.end)))h(i,H)}},S=(n,r)=>{const s=[];for(const c of n)for(const a of p(c,r)){const i=N(a.comment,a.body,a.fact);if(i===null)s.push(a);else h(a.comment,i)}return s},T=(n)=>{const r=e.sourceCode.getText(),s=n.map((c)=>c.comment);for(const[c,a]of s.entries()){const i=s[c-1];if(i&&j(r,i,a))h(a,d.stacked)}},y=(n,r)=>{const s=r.budget??C,c=n.filter((i)=>i.kind==="what"),a=c[s];if(a)h(a.comment,M(s,c.length))};return{...l,before(){t.clear();return!0},"Program:exit"(){const n=b(e,{}),r=I(e);g(r,n);const s=S(r,w(n));T(s);y(s,n)}}}};
package/dist/lib/ast.js CHANGED
@@ -1 +1 @@
1
- const c=new Set(["parent","loc","range","start","end","type"]),o=(t,e)=>{for(const r of t){const n=findInSubtree(r,e);if(n)return n}return null},p=(t)=>{const e=[];for(const[r,n]of Object.entries(t)){if(c.has(r))continue;if(n!==null&&typeof n==="object")e.push(n)}return e};export const findInSubtree=(t,e)=>{if(t===null||typeof t!=="object")return null;if(Array.isArray(t))return o(t,e);const r=t;if(typeof r.type!=="string")return null;if(e(r))return r;return o(p(r),e)},subtreeHas=(t,e)=>findInSubtree(t,e)!==null,calleeName=(t)=>{if(t?.type!=="CallExpression")return"";const{callee:e}=t;if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name;return""},ancestors=function*(t){let e=t.parent;while(e){yield e;e=e.parent}},enclosingCall=(t,e)=>{for(const r of ancestors(t))if(r.type==="CallExpression"&&e.has(calleeName(r)))return r;return null},closestAncestor=(t,e)=>{for(const r of ancestors(t))if(e.has(r.type))return r;return null},hasAncestor=(t,e)=>{for(const r of ancestors(t))if(e(r))return!0;return!1},isWithin=(t,e)=>{for(const r of ancestors(t))if(r===e)return!0;return!1},crossesFunctionBefore=(t,e,r)=>{for(const n of ancestors(t)){if(n===e)return!1;if(r.has(n.type))return!0}return!1},isMemberCall=(t,e)=>t?.type==="CallExpression"&&t.callee.type==="MemberExpression"&&!t.callee.computed&&t.callee.property.type==="Identifier"&&t.callee.property.name===e,receiverName=(t)=>{if(t?.type!=="CallExpression"||t.callee.type!=="MemberExpression")return null;const{object:e}=t.callee;return e.type==="Identifier"?e.name:null},tagIdentifier=(t)=>{let e=t;while(e?.type==="JSXMemberExpression")e=e.property;if(e?.type==="JSXIdentifier"||e?.type==="Identifier")return e.name;return""},tagPath=(t)=>{if(t?.type==="JSXIdentifier"||t?.type==="Identifier")return t.name;if(t?.type!=="JSXMemberExpression")return"";const e=tagPath(t.object),r=tagPath(t.property);return e===""||r===""?"":`${e}.${r}`};const s=/^(?:Animated|Reanimated)/;export const componentName=(t)=>{const e=tagPath(t),r=e.lastIndexOf(".");if(r!==-1)return s.test(e.slice(0,r))?e.slice(r+1):"";const n=e.replace(s,"");return n===""?e:n},attributeName=(t)=>{if(t.type!=="JSXAttribute")return"";const{name:e}=t;return e.type==="JSXIdentifier"?e.name:e.name.name},importedSpecifiers=(t,e)=>t.type==="ImportDeclaration"&&t.source.value===e?[...t.specifiers]:[],FUNCTION_TYPES=new Set(["FunctionDeclaration","FunctionExpression","ArrowFunctionExpression"]),COMPONENT_OR_HOOK=/^(?:[A-Z]|use[A-Z])/,isFunction=(t)=>!!t&&FUNCTION_TYPES.has(t.type),gate=(t,...e)=>e.some((r)=>t.sourceCode.text.includes(r)),optionsOf=(t,e)=>t.options[0]??e,problem=(t,e)=>({...e,meta:{type:"problem",...e.meta,docs:{description:t}}});
1
+ const p=new Set(["parent","loc","range","start","end","type"]),o=(t,e)=>{for(const r of t){const n=findInSubtree(r,e);if(n)return n}return null},i=(t)=>{const e=[];for(const[r,n]of Object.entries(t)){if(p.has(r))continue;if(n!==null&&typeof n==="object")e.push(n)}return e};export const findInSubtree=(t,e)=>{if(t===null||typeof t!=="object")return null;if(Array.isArray(t))return o(t,e);const r=t;if(typeof r.type!=="string")return null;if(e(r))return r;return o(i(r),e)},subtreeHas=(t,e)=>findInSubtree(t,e)!==null,calleeName=(t)=>{if(t?.type!=="CallExpression")return"";const{callee:e}=t;if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name;return""},ancestors=function*(t){let e=t.parent;while(e){yield e;e=e.parent}},enclosingCall=(t,e)=>{for(const r of ancestors(t))if(r.type==="CallExpression"&&e.has(calleeName(r)))return r;return null},closestAncestor=(t,e)=>{for(const r of ancestors(t))if(e.has(r.type))return r;return null},hasAncestor=(t,e)=>{for(const r of ancestors(t))if(e(r))return!0;return!1},isWithin=(t,e)=>{for(const r of ancestors(t))if(r===e)return!0;return!1},crossesFunctionBefore=(t,e,r)=>{for(const n of ancestors(t)){if(n===e)return!1;if(r.has(n.type))return!0}return!1},isMemberCall=(t,e)=>t?.type==="CallExpression"&&t.callee.type==="MemberExpression"&&!t.callee.computed&&t.callee.property.type==="Identifier"&&t.callee.property.name===e,receiverName=(t)=>{if(t?.type!=="CallExpression"||t.callee.type!=="MemberExpression")return null;const{object:e}=t.callee;return e.type==="Identifier"?e.name:null},tagIdentifier=(t)=>{let e=t;while(e?.type==="JSXMemberExpression")e=e.property;if(e?.type==="JSXIdentifier"||e?.type==="Identifier")return e.name;return""},tagPath=(t)=>{if(t?.type==="JSXIdentifier"||t?.type==="Identifier")return t.name;if(t?.type!=="JSXMemberExpression")return"";const e=tagPath(t.object),r=tagPath(t.property);return e===""||r===""?"":`${e}.${r}`};const s=/^(?:Animated|Reanimated)/;export const componentName=(t)=>{const e=tagPath(t),r=e.lastIndexOf(".");if(r!==-1)return s.test(e.slice(0,r))?e.slice(r+1):"";const n=e.replace(s,"");return n===""?e:n},attributeName=(t)=>{if(t.type!=="JSXAttribute")return"";const{name:e}=t;return e.type==="JSXIdentifier"?e.name:e.name.name},importedSpecifiers=(t,e)=>t.type==="ImportDeclaration"&&t.source.value===e?[...t.specifiers]:[],importedNames=(t,e)=>{const r=[];for(const n of importedSpecifiers(t,e)){if(n.type!=="ImportSpecifier")continue;if(n.imported.type!=="Identifier")continue;r.push({imported:n.imported.name,local:n.local.name})}return r},propertyKeyName=(t)=>{if(t.type!=="Property"||t.computed)return"";const{key:e}=t;if(e.type==="Identifier")return e.name;return e.type==="Literal"?String(e.value):""},propertyValue=(t,e)=>{if(t?.type!=="ObjectExpression")return null;for(const r of t.properties)if(r.type==="Property"&&propertyKeyName(r)===e)return r.value;return null},FUNCTION_TYPES=new Set(["FunctionDeclaration","FunctionExpression","ArrowFunctionExpression"]),COMPONENT_OR_HOOK=/^(?:[A-Z]|use[A-Z])/,isFunction=(t)=>!!t&&FUNCTION_TYPES.has(t.type),gate=(t,...e)=>e.some((r)=>t.sourceCode.text.includes(r)),optionsOf=(t,e)=>t.options[0]??e,problem=(t,e)=>({...e,meta:{type:"problem",...e.meta,docs:{description:t}}});
@@ -0,0 +1 @@
1
+ import{fileURLToPath as e}from"node:url";export const EFFECT_NAMESPACE="@ashstack/effects",EFFECT_SPECIFIER=e(new URL("../../vendor/react-effect/index.mjs",import.meta.url)),effectPlugin={name:EFFECT_NAMESPACE,specifier:EFFECT_SPECIFIER},EFFECT_RULES={"@ashstack/effects/no-derived-state":"error","@ashstack/effects/no-chain-state-updates":"error","@ashstack/effects/no-event-handler":"error","@ashstack/effects/no-adjust-state-on-prop-change":"error","@ashstack/effects/no-reset-all-state-on-prop-change":"error","@ashstack/effects/no-pass-live-state-to-parent":"error","@ashstack/effects/no-pass-data-to-parent":"error","@ashstack/effects/no-external-store-subscription":"error","@ashstack/effects/no-initialize-state":"error"};
@@ -1 +1 @@
1
- import{fileURLToPath as d}from"node:url";import{detect as r}from"./detect.js";export const defineModule=(e)=>e;const shortName=(e)=>e.meta.name.slice(10),p=(e,t)=>r(e.option===void 0?void 0:t[e.option],e.packages),u=(e)=>{const t=[];for(const[o,s]of Object.entries(e.rules)){if(s.meta.defaultOff)continue;if(s.meta.packages&&!r(void 0,s.meta.packages))continue;t.push(`${e.meta.name}/${o}`)}return t},i=(e,t)=>{e.paths.push(...t?.paths??[]);e.patterns.push(...t?.patterns??[])};export const composeModules=(e,t,o=[])=>{const s={jsPlugins:[],rules:{},restricted:{paths:[],patterns:[]}};for(const n of e){if(!p(n,t))continue;const c=u(n);if(c.length>0)s.jsPlugins.push(d(n.url));for(const a of c)s.rules[a]="error";i(s.restricted,n.restrictedImports)}for(const n of o){if(!r(void 0,n.packages))continue;i(s.restricted,n.restrictedImports)}return s};export{shortName};
1
+ import{fileURLToPath as d}from"node:url";import{detect as r}from"./detect.js";export const defineModule=(e)=>e;const shortName=(e)=>e.meta.name.slice(10),p=(e,t)=>r(e.option===void 0?void 0:t[e.option],e.packages),u=(e)=>{const t=[];for(const[o,s]of Object.entries(e.rules)){if(s.defaultOff)continue;if(s.packages&&!r(void 0,s.packages))continue;t.push(`${e.meta.name}/${o}`)}return t},i=(e,t)=>{e.paths.push(...t?.paths??[]);e.patterns.push(...t?.patterns??[])};export const composeModules=(e,t,o=[])=>{const s={jsPlugins:[],rules:{},restricted:{paths:[],patterns:[]}};for(const n of e){if(!p(n,t))continue;const c=u(n);if(c.length>0)s.jsPlugins.push(d(n.url));for(const a of c)s.rules[a]="error";i(s.restricted,n.restrictedImports)}for(const n of o){if(!r(void 0,n.packages))continue;i(s.restricted,n.restrictedImports)}return s};export{shortName};
@@ -0,0 +1 @@
1
+ const e=(t)=>new URL(t,import.meta.url).href;export const coreRegistry=[{meta:{name:"@ashstack/core"},url:e("../core/rules/core/index.js"),rules:{"no-comments":{},"no-naming-convention":{},"no-packed-condition":{},"use-design-system":{defaultOff:!0},"components-tsx-only":{defaultOff:!0},"hoist-intl":{}}},{meta:{name:"@ashstack/zod"},url:e("../core/rules/zod/index.js"),option:"zod",packages:["zod"],rules:{"prefer-enum":{}}}],reactRegistry=[{meta:{name:"@ashstack/react"},url:e("../react/rules/react/index.js"),rules:{"no-unlabeled-icon-button":{},"no-svg-without-title":{}}},{meta:{name:"@ashstack/query"},url:e("../react/rules/query/index.js"),option:"query",packages:["@tanstack/react-query"],rules:{"no-inline-keys":{},"no-deprecated-filters":{},"require-destructured-hooks":{},"no-fetch-in-query-fn":{},"next-page-param-undefined":{}}},{meta:{name:"@ashstack/zustand"},url:e("../react/rules/zustand/index.js"),option:"zustand",packages:["zustand"],rules:{"require-selector":{}}},{meta:{name:"@ashstack/i18n"},url:e("../react/rules/i18n/index.js"),option:"i18n",packages:["i18next","react-i18next","@lingui/core","react-intl","use-intl","next-intl","expo-localization"],rules:{"no-bare-text":{},"no-bare-attrs":{},"no-bare-toast":{}}},{meta:{name:"@ashstack/tailwind"},url:e("../react/rules/tailwind/index.js"),option:"tailwind",packages:["tailwindcss"],rules:{"prefer-cn":{},"use-logical-classes":{}}},{meta:{name:"@ashstack/tanstack-router"},url:e("../react/rules/tanstack-router/index.js"),option:"tanstackRouter",packages:["@tanstack/react-router"],rules:{"require-selector":{},"no-search-casts":{}}}],reactNativeRegistry=[{meta:{name:"@ashstack/react-native"},url:e("../react-native/rules/react-native/index.js"),restrictedImports:{paths:[{name:"react",importNames:["forwardRef"],message:"Accept `ref` as a regular prop and delete the `forwardRef` wrapper — React 19 passes `ref` like any other prop."},{name:"react",importNames:["useContext"],message:"Call `use(Context)` instead of `useContext(Context)`; React 19 replaces the hook with it."}],patterns:[{group:["expo-linear-gradient","react-native-linear-gradient"],message:"Apply the gradient through the `experimental_backgroundImage` style property instead of a gradient component."}]},rules:{"no-keyboard-will-events":{},"no-scroll-position-state":{},"no-conditional-style-array":{},"no-leaked-render":{},"no-rn-image-network-source":{},"no-redundant-view-nesting":{},"no-rn-namespace-import":{},"no-unlabeled-icon-pressable":{},"hoist-stateless-function":{},"no-manual-memo":{packages:["babel-plugin-react-compiler","react-compiler-runtime","react-compiler-marker"]},"no-dynamic-import":{}}},{meta:{name:"@ashstack/unistyles"},url:e("../react-native/rules/unistyles/index.js"),option:"unistyles",packages:["react-native-unistyles"],restrictedImports:{paths:[{name:"react-native",importNames:["StyleSheet"],message:"Import `StyleSheet` from `react-native-unistyles` instead of `react-native`."},{name:"react-native",importNames:["useWindowDimensions","Dimensions"],message:"Read screen size from `rt.screen` inside `StyleSheet.create`, or `UnistylesRuntime.screen` outside it, instead of the `Dimensions` APIs."},{name:"react-native",importNames:["SafeAreaView"],message:'Set `contentInsetAdjustmentBehavior="automatic"` on the scroll view, or apply `rt.insets` from `react-native-unistyles`, instead of `SafeAreaView`.'},{name:"react-native-safe-area-context",importNames:["useSafeAreaInsets","useSafeAreaFrame","SafeAreaView"],message:"Read safe-area values from `rt.insets`, or `UnistylesRuntime.insets` outside a stylesheet, in `react-native-unistyles`."}]},rules:{"animated-theme":{},"content-container":{},"in-sheet":{},insets:{},"no-hardcoded-color":{},"no-hardcoded-spacing":{},"no-margin":{},"no-style-spread":{},"no-unused-styles":{},"rtl-style-call":{},"theme-screen-component":{},"theme-style-attr":{}}},{meta:{name:"@ashstack/legend-list"},url:e("../react-native/rules/legend-list/index.js"),option:"legendList",packages:["@legendapp/list"],restrictedImports:{paths:[{name:"react-native",importNames:["FlatList","SectionList","VirtualizedList"],message:"Render this list with `LegendList` from `@legendapp/list/react-native`; a `ScrollView` plus `map` is fine for fewer than 20 static items."},{name:"@shopify/flash-list",message:"Import `LegendList` from `@legendapp/list/react-native` — Legend List replaced FlashList in this stack."}],patterns:[{group:["@legendapp/list"],message:"Import from the platform entrypoint `@legendapp/list/react-native`, not the package root."}]},rules:{"required-props":{},"no-index-key-extractor":{},"no-remount-key":{},"no-inline-render-item-props":{},"no-mixed-children":{},"no-flex-in-content-container":{},"typed-items-need-item-type":{},"no-scrollview-map":{},"no-unsupported-props":{}}},{meta:{name:"@ashstack/legend-state"},url:e("../react-native/rules/legend-state/index.js"),option:"legendState",packages:["@legendapp/state"],restrictedImports:{paths:[{name:"@legendapp/state/react",importNames:["use$","useSelector"],message:"Use `useValue` from `@legendapp/state/react`; `use$` and `useSelector` are deprecated."}]},rules:{"no-assignment":{},naming:{},"no-nested-observable":{},"no-react-mirror":{},"no-untracked-get-in-jsx":{},"no-peek-in-selector":{},"no-object-selector":{},"no-observable-in-component":{}}},{meta:{name:"@ashstack/reanimated"},url:e("../react-native/rules/reanimated/index.js"),option:"reanimated",packages:["react-native-reanimated"],restrictedImports:{paths:[{name:"react-native",importNames:["Animated"],message:"Use `react-native-reanimated` instead of React Native's `Animated` API."},{name:"react-native-reanimated",importNames:["runOnJS","runOnUI"],message:"Use `scheduleOnRN` and `scheduleOnUI` from `react-native-worklets`; `runOnJS` and `runOnUI` are deprecated."},{name:"react-native-worklets",importNames:["runOnJS","runOnUI"],message:"Use `scheduleOnRN` and `scheduleOnUI` from `react-native-worklets`; `runOnJS` and `runOnUI` are deprecated."}]},rules:{"animated-reaction-safety":{},"animated-style-needs-animated-component":{},"animated-updater-purity":{},"gpu-properties-only":{},"hoist-layout-animation-builder":{},"interpolate-needs-clamp":{},"no-shared-value-dot-value":{},"no-react-state-from-continuous-worklet":{},"prefer-lazy-shared-value-initializer":{},"schedule-on-rn-scope":{},"shared-value-usage":{}}},{meta:{name:"@ashstack/turbo-image"},url:e("../react-native/rules/turbo-image/index.js"),option:"turboImage",packages:["react-native-turbo-image"],rules:{"require-resize":{},"require-cache-policy":{}}},{meta:{name:"@ashstack/skia"},url:e("../react-native/rules/skia/index.js"),option:"skia",packages:["@shopify/react-native-skia"],restrictedImports:{paths:[{name:"@shopify/react-native-skia",importNames:["usePathValue","usePathInterpolation"],message:"Keep one stable `SkPath` buffer and mutate it inside `useDerivedValue`, with consumers reading the driving shared value. These legacy path-value hooks self-dirty Reanimated mappers and re-record idle canvases."}]},rules:{"canvas-opaque":{}}},{meta:{name:"@ashstack/keyboard"},url:e("../react-native/rules/keyboard/index.js"),option:"keyboard",packages:["react-native-keyboard-controller"],restrictedImports:{paths:[{name:"react-native",importNames:["KeyboardAvoidingView"],message:"Import `KeyboardAvoidingView` from `react-native-keyboard-controller` — same props, and it tracks the keyboard per frame instead of waiting for Android's post-animation `keyboardDidShow`. For a scrolling form use its `KeyboardAwareScrollView`; to just shift a view, use `rt.insets.ime`."}]},rules:{}}];
@@ -0,0 +1,72 @@
1
+ import type { AllowWarnDeny } from "oxlint";
2
+ /** Every rule id `core()` adds on top of the entry below it. */
3
+ export type CoreRuleId = "@ashstack/core/no-comments" | "@ashstack/core/no-naming-convention" | "@ashstack/core/no-packed-condition" | "@ashstack/core/use-design-system" | "@ashstack/core/components-tsx-only" | "@ashstack/core/hoist-intl" | "@ashstack/zod/prefer-enum";
4
+ declare module "oxlint" {
5
+ interface DummyRuleMap {
6
+ /**
7
+ * Disallow every comment that is neither a `// what: <fact>` line, a `// why:` marker, nor a tooling directive. The message names the refactoring that removes it: Rename, Extract Function, Guard Clause. Surviving `// what:` lines are held to one short line each, at most `budget` per file (default 2); `escapeHatch: false` removes that hatch, so no discretionary prose survives. A `// why:` line is the marker `@ashstack/react-native/no-manual-memo` requires above a kept `memo`: held to the same one-line shape, never counted against `budget`, and kept even with `escapeHatch: false`, so the two rules run together. With `jsdoc: "allow"`, a `/** *\/` block documenting the declaration directly beneath it is kept, while a floating one still reports.
8
+ *
9
+ * @see https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#ashstackcoreno-comments
10
+ */
11
+ "@ashstack/core/no-comments"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, {
12
+ jsdoc?: "allow" | "report";
13
+ escapeHatch?: boolean;
14
+ budget?: number;
15
+ }];
16
+ /**
17
+ * Require a variable, object property, type member or enum member to use one of the casings allowed for its kind.
18
+ *
19
+ * @see https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#ashstackcoreno-naming-convention
20
+ */
21
+ "@ashstack/core/no-naming-convention"?: AllowWarnDeny | [AllowWarnDeny];
22
+ /**
23
+ * Require a condition to be split into named booleans once it holds too many boolean operators and comparisons. The option says how many, defaulting to 5.
24
+ *
25
+ * @see https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#ashstackcoreno-packed-condition
26
+ */
27
+ "@ashstack/core/no-packed-condition"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, number];
28
+ /**
29
+ * Disallow importing a raw primitive your design system already wraps. Wrapped components come from scanning the design-system directory, plus the explicit `use` map for names, paths and source modules the scan cannot infer. Files under the design-system directory (or `exempt`) are skipped.
30
+ *
31
+ * Off by default — opt in per project.
32
+ *
33
+ * @see https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#ashstackcoreuse-design-system
34
+ */
35
+ "@ashstack/core/use-design-system"?: AllowWarnDeny | [AllowWarnDeny] | [
36
+ AllowWarnDeny,
37
+ {
38
+ dir?: string;
39
+ alias?: string;
40
+ use?: Record<string, string | Array<string> | {
41
+ replaces: string | Array<string>;
42
+ from?: string;
43
+ path?: string;
44
+ reason?: string;
45
+ }>;
46
+ exempt?: Array<string>;
47
+ }
48
+ ];
49
+ /**
50
+ * Require every file under the components directory to render JSX or be a re-export barrel. `dir` says which directory, defaulting to `src/components`.
51
+ *
52
+ * Off by default — opt in per project.
53
+ *
54
+ * @see https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#ashstackcorecomponents-tsx-only
55
+ */
56
+ "@ashstack/core/components-tsx-only"?: AllowWarnDeny | [AllowWarnDeny] | [AllowWarnDeny, {
57
+ dir?: string;
58
+ }];
59
+ /**
60
+ * Disallow `new Intl.*` inside a function that renders JSX, unless the call already sits in `useMemo` or `useCallback`.
61
+ *
62
+ * @see https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#ashstackcorehoist-intl
63
+ */
64
+ "@ashstack/core/hoist-intl"?: AllowWarnDeny | [AllowWarnDeny];
65
+ /**
66
+ * Disallow `z.nativeEnum()` and any `z.union()` whose members are all `z.literal()` strings.
67
+ *
68
+ * @see https://github.com/AlshehriAli0/ashStack/blob/main/packages/lint/RULES.md#ashstackzodprefer-enum
69
+ */
70
+ "@ashstack/zod/prefer-enum"?: AllowWarnDeny | [AllowWarnDeny];
71
+ }
72
+ }
@@ -0,0 +1 @@
1
+ export{};