@ashstack/lint 0.1.1 → 0.3.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 (104) hide show
  1. package/README.md +1 -1
  2. package/dist/core/index.js +1 -222
  3. package/dist/core/rules/core/components-tsx-only.js +1 -37
  4. package/dist/core/rules/core/hoist-intl.js +1 -32
  5. package/dist/core/rules/core/index.js +1 -18
  6. package/dist/core/rules/core/no-comments.js +1 -194
  7. package/dist/core/rules/core/no-naming-convention.js +1 -143
  8. package/dist/core/rules/core/no-packed-condition.d.ts +2 -0
  9. package/dist/core/rules/core/no-packed-condition.js +1 -0
  10. package/dist/core/rules/core/use-design-system.js +1 -147
  11. package/dist/core/rules/zod/index.js +1 -12
  12. package/dist/core/rules/zod/prefer-enum.js +1 -44
  13. package/dist/index.js +1 -3
  14. package/dist/lib/ast.js +1 -162
  15. package/dist/lib/detect.js +1 -42
  16. package/dist/lib/merge.js +1 -18
  17. package/dist/lib/module.js +1 -49
  18. package/dist/lib/types.js +1 -1
  19. package/dist/react/index.js +1 -126
  20. package/dist/react/rules/i18n/index.js +1 -16
  21. package/dist/react/rules/i18n/no-bare-attrs.js +1 -38
  22. package/dist/react/rules/i18n/no-bare-text.js +1 -26
  23. package/dist/react/rules/i18n/no-bare-toast.js +1 -38
  24. package/dist/react/rules/query/index.js +1 -20
  25. package/dist/react/rules/query/next-page-param-undefined.js +1 -26
  26. package/dist/react/rules/query/no-deprecated-filters.js +1 -63
  27. package/dist/react/rules/query/no-fetch-in-query-fn.js +1 -22
  28. package/dist/react/rules/query/no-inline-keys.js +1 -107
  29. package/dist/react/rules/query/require-destructured-hooks.js +1 -41
  30. package/dist/react/rules/query/shared.js +1 -10
  31. package/dist/react/rules/zustand/index.js +1 -12
  32. package/dist/react/rules/zustand/require-selector.js +1 -62
  33. package/dist/react-native/index.js +1 -113
  34. package/dist/react-native/rules/keyboard/index.js +1 -18
  35. package/dist/react-native/rules/legend-list/index.js +1 -51
  36. package/dist/react-native/rules/legend-list/no-flex-in-content-container.js +1 -77
  37. package/dist/react-native/rules/legend-list/no-index-key-extractor.js +1 -26
  38. package/dist/react-native/rules/legend-list/no-inline-data.js +1 -25
  39. package/dist/react-native/rules/legend-list/no-inline-extra-data.js +1 -21
  40. package/dist/react-native/rules/legend-list/no-inline-render-item-props.js +1 -35
  41. package/dist/react-native/rules/legend-list/no-mixed-children.js +1 -24
  42. package/dist/react-native/rules/legend-list/no-remount-key.js +1 -18
  43. package/dist/react-native/rules/legend-list/no-scrollview-map.js +1 -27
  44. package/dist/react-native/rules/legend-list/no-unsupported-props.js +1 -29
  45. package/dist/react-native/rules/legend-list/required-props.js +1 -23
  46. package/dist/react-native/rules/legend-list/shared.js +1 -25
  47. package/dist/react-native/rules/legend-list/typed-items-need-item-type.js +1 -49
  48. package/dist/react-native/rules/legend-state/index.js +1 -35
  49. package/dist/react-native/rules/legend-state/naming.js +1 -26
  50. package/dist/react-native/rules/legend-state/no-assignment.js +1 -31
  51. package/dist/react-native/rules/legend-state/no-nested-observable.js +1 -23
  52. package/dist/react-native/rules/legend-state/no-object-selector.js +1 -47
  53. package/dist/react-native/rules/legend-state/no-observable-in-component.js +1 -46
  54. package/dist/react-native/rules/legend-state/no-peek-in-selector.js +1 -67
  55. package/dist/react-native/rules/legend-state/no-react-mirror.js +1 -33
  56. package/dist/react-native/rules/legend-state/no-untracked-get-in-jsx.js +1 -52
  57. package/dist/react-native/rules/legend-state/shared.js +1 -23
  58. package/dist/react-native/rules/react-native/hoist-stateless-function.js +1 -59
  59. package/dist/react-native/rules/react-native/index.js +1 -50
  60. package/dist/react-native/rules/react-native/no-conditional-style-array.js +1 -23
  61. package/dist/react-native/rules/react-native/no-dynamic-import.js +1 -18
  62. package/dist/react-native/rules/react-native/no-keyboard-will-events.js +1 -18
  63. package/dist/react-native/rules/react-native/no-leaked-render.js +1 -43
  64. package/dist/react-native/rules/react-native/no-manual-memo.js +1 -87
  65. package/dist/react-native/rules/react-native/no-redundant-view-nesting.js +1 -36
  66. package/dist/react-native/rules/react-native/no-rn-image-network-source.js +1 -58
  67. package/dist/react-native/rules/react-native/no-rn-namespace-import.js +1 -38
  68. package/dist/react-native/rules/react-native/no-scroll-position-state.js +1 -26
  69. package/dist/react-native/rules/react-native/no-unlabeled-icon-pressable.js +1 -62
  70. package/dist/react-native/rules/reanimated/animated-reaction-safety.js +1 -110
  71. package/dist/react-native/rules/reanimated/animated-style-needs-animated-component.js +1 -72
  72. package/dist/react-native/rules/reanimated/animated-updater-purity.js +1 -38
  73. package/dist/react-native/rules/reanimated/gpu-properties-only.js +1 -80
  74. package/dist/react-native/rules/reanimated/hoist-layout-animation-builder.js +1 -43
  75. package/dist/react-native/rules/reanimated/index.js +1 -51
  76. package/dist/react-native/rules/reanimated/interpolate-needs-clamp.js +1 -41
  77. package/dist/react-native/rules/reanimated/no-react-state-from-continuous-worklet.js +1 -86
  78. package/dist/react-native/rules/reanimated/no-shared-value-dot-value.js +1 -112
  79. package/dist/react-native/rules/reanimated/prefer-lazy-shared-value-initializer.js +1 -24
  80. package/dist/react-native/rules/reanimated/schedule-on-rn-scope.js +1 -21
  81. package/dist/react-native/rules/reanimated/shared-value-usage.js +1 -105
  82. package/dist/react-native/rules/reanimated/shared.js +1 -13
  83. package/dist/react-native/rules/skia/canvas-opaque.js +1 -49
  84. package/dist/react-native/rules/skia/index.js +1 -21
  85. package/dist/react-native/rules/turbo-image/index.js +1 -14
  86. package/dist/react-native/rules/turbo-image/require-cache-policy.js +1 -21
  87. package/dist/react-native/rules/turbo-image/require-resize.js +1 -22
  88. package/dist/react-native/rules/unistyles/animated-theme.js +1 -37
  89. package/dist/react-native/rules/unistyles/content-container.js +1 -73
  90. package/dist/react-native/rules/unistyles/in-sheet.js +1 -116
  91. package/dist/react-native/rules/unistyles/index.js +1 -58
  92. package/dist/react-native/rules/unistyles/insets.js +1 -92
  93. package/dist/react-native/rules/unistyles/no-hardcoded-color.js +1 -30
  94. package/dist/react-native/rules/unistyles/no-hardcoded-spacing.js +1 -78
  95. package/dist/react-native/rules/unistyles/no-margin.js +1 -19
  96. package/dist/react-native/rules/unistyles/no-style-spread.js +1 -41
  97. package/dist/react-native/rules/unistyles/no-unused-styles.js +1 -99
  98. package/dist/react-native/rules/unistyles/rtl-style-call.js +1 -42
  99. package/dist/react-native/rules/unistyles/shared.js +1 -70
  100. package/dist/react-native/rules/unistyles/theme-screen-component.js +1 -22
  101. package/dist/react-native/rules/unistyles/theme-style-attr.js +1 -18
  102. package/dist/react-native/stylesheet.js +1 -44
  103. package/package.json +3 -4
  104. package/RULES.md +0 -2881
@@ -1,105 +1 @@
1
- import { closestAncestor, crossesFunctionBefore, FUNCTION_TYPES, isMemberCall, problem, receiverName, subtreeHas, } from "../../../lib/ast.js";
2
- import { isProducerCall } from "./shared.js";
3
- const MESSAGES = {
4
- renderRead: "Read this inside `useAnimatedStyle`/`useAnimatedProps`, or mirror non-visual state through an explicit callback. A `.get()` while JSX is evaluated is untracked, so the rendered value never updates.",
5
- renderWrite: "Move this write into an event handler, an effect, or an animation callback — render must stay pure.",
6
- destructure: "Keep the SharedValue object itself and read or write it with `.get()` / `.set(...)`; destructuring detaches the value from Reanimated reactivity.",
7
- nestedProperty: "Assign a new value with `.set(...)`, or use `.modify()` for a large object; mutating a property returned by `.get()` bypasses shared-value reactivity.",
8
- nestedCollection: "Assign a new collection with `.set(...)`, or mutate inside `.modify()`; mutating the collection returned by `.get()` bypasses shared-value reactivity.",
9
- };
10
- const MUTATING_METHODS = new Set([
11
- "push",
12
- "pop",
13
- "shift",
14
- "unshift",
15
- "splice",
16
- "sort",
17
- "reverse",
18
- "copyWithin",
19
- "fill",
20
- "set",
21
- "add",
22
- "delete",
23
- "clear",
24
- ]);
25
- const JSX_HOSTS = new Set(["JSXExpressionContainer", "JSXAttribute"]);
26
- /** True for `sv.get().push(...)` and friends — a mutation of the collection `get()` handed back. */
27
- const mutatesWhatGetReturned = (node) => {
28
- if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression")
29
- return false;
30
- const { object, property } = node.callee;
31
- return property.type === "Identifier" && MUTATING_METHODS.has(property.name) && isMemberCall(object, "get");
32
- };
33
- /** True when the call is evaluated as JSX is built, rather than inside a callback JSX only passes along. */
34
- const runsWhileJsxEvaluates = (node) => {
35
- const host = closestAncestor(node, JSX_HOSTS);
36
- if (!host)
37
- return false;
38
- if (crossesFunctionBefore(node, host, FUNCTION_TYPES))
39
- return false;
40
- return !subtreeHas(host, current => FUNCTION_TYPES.has(current.type));
41
- };
42
- export const sharedValueUsage = problem("Destructuring a shared value or mutating what its `get()` returned detaches it from Reanimated reactivity. Reading or writing one while JSX evaluates also makes render impure.", {
43
- createOnce(context) {
44
- const names = new Set();
45
- const candidates = [];
46
- return {
47
- before() {
48
- names.clear();
49
- candidates.length = 0;
50
- },
51
- VariableDeclarator(node) {
52
- const { id, init } = node;
53
- if (id.type === "Identifier" && isProducerCall(init)) {
54
- names.add(id.name);
55
- return;
56
- }
57
- if (id.type !== "ObjectPattern")
58
- return;
59
- if (init?.type !== "CallExpression")
60
- return;
61
- if (init.callee.type !== "Identifier" || init.callee.name !== "useSharedValue")
62
- return;
63
- const destructuresValue = id.properties.some(property => {
64
- if (property.type !== "Property")
65
- return false;
66
- const { key } = property;
67
- return key.type === "Identifier" && key.name === "value";
68
- });
69
- if (destructuresValue)
70
- context.report({ node, message: MESSAGES.destructure });
71
- },
72
- AssignmentExpression(node) {
73
- if (node.left.type !== "MemberExpression")
74
- return;
75
- if (!isMemberCall(node.left.object, "get"))
76
- return;
77
- const shared = receiverName(node.left.object);
78
- if (!shared)
79
- return;
80
- candidates.push({ node, shared, message: MESSAGES.nestedProperty });
81
- },
82
- CallExpression(node) {
83
- if (mutatesWhatGetReturned(node)) {
84
- context.report({ node, message: MESSAGES.nestedCollection });
85
- return;
86
- }
87
- const isGet = isMemberCall(node, "get") && node.arguments.length === 0;
88
- if (!isGet && !isMemberCall(node, "set"))
89
- return;
90
- const shared = receiverName(node);
91
- if (!shared)
92
- return;
93
- if (!runsWhileJsxEvaluates(node))
94
- return;
95
- candidates.push({ node, shared, message: isGet ? MESSAGES.renderRead : MESSAGES.renderWrite });
96
- },
97
- "Program:exit"() {
98
- for (const { node, shared, message } of candidates) {
99
- if (names.has(shared))
100
- context.report({ node, message });
101
- }
102
- },
103
- };
104
- },
105
- });
1
+ import{closestAncestor as p,crossesFunctionBefore as f,FUNCTION_TYPES as c,isMemberCall as u,problem as m,receiverName as d,subtreeHas as h}from"../../../lib/ast.js";import{isProducerCall as g}from"./shared.js";const n={renderRead:"Read this inside `useAnimatedStyle`/`useAnimatedProps`, or mirror non-visual state through an explicit callback. A `.get()` while JSX is evaluated is untracked, so the rendered value never updates.",renderWrite:"Move this write into an event handler, an effect, or an animation callback — render must stay pure.",destructure:"Keep the SharedValue object itself and read or write it with `.get()` / `.set(...)`; destructuring detaches the value from Reanimated reactivity.",nestedProperty:"Assign a new value with `.set(...)`, or use `.modify()` for a large object; mutating a property returned by `.get()` bypasses shared-value reactivity.",nestedCollection:"Assign a new collection with `.set(...)`, or mutate inside `.modify()`; mutating the collection returned by `.get()` bypasses shared-value reactivity."},y=new Set(["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill","set","add","delete","clear"]),b=new Set(["JSXExpressionContainer","JSXAttribute"]),v=(r)=>{if(r.type!=="CallExpression"||r.callee.type!=="MemberExpression")return!1;const{object:s,property:a}=r.callee;return a.type==="Identifier"&&y.has(a.name)&&u(s,"get")},S=(r)=>{const s=p(r,b);if(!s)return!1;if(f(r,s,c))return!1;return!h(s,(a)=>c.has(a.type))};export const sharedValueUsage=m("Destructuring a shared value or mutating what its `get()` returned detaches it from Reanimated reactivity. Reading or writing one while JSX evaluates also makes render impure.",{createOnce(r){const s=new Set,a=[];return{before(){s.clear();a.length=0},VariableDeclarator(e){const{id:t,init:i}=e;if(t.type==="Identifier"&&g(i)){s.add(t.name);return}if(t.type!=="ObjectPattern")return;if(i?.type!=="CallExpression")return;if(i.callee.type!=="Identifier"||i.callee.name!=="useSharedValue")return;if(t.properties.some((o)=>{if(o.type!=="Property")return!1;const{key:l}=o;return l.type==="Identifier"&&l.name==="value"}))r.report({node:e,message:n.destructure})},AssignmentExpression(e){if(e.left.type!=="MemberExpression")return;if(!u(e.left.object,"get"))return;const t=d(e.left.object);if(!t)return;a.push({node:e,shared:t,message:n.nestedProperty})},CallExpression(e){if(v(e)){r.report({node:e,message:n.nestedCollection});return}const t=u(e,"get")&&e.arguments.length===0;if(!t&&!u(e,"set"))return;const i=d(e);if(!i)return;if(!S(e))return;a.push({node:e,shared:i,message:t?n.renderRead:n.renderWrite})},"Program:exit"(){for(const{node:e,shared:t,message:i}of a)if(s.has(t))r.report({node:e,message:i})}}}});
@@ -1,13 +1 @@
1
- import { calleeName } from "../../../lib/ast.js";
2
- /** Calls that hand back a SharedValue. */
3
- const PRODUCERS = new Set([
4
- "useSharedValue",
5
- "useDerivedValue",
6
- "useScrollOffset",
7
- "useScrollViewOffset",
8
- "makeMutable",
9
- ]);
10
- /** `useSharedValue(...)` and friends, under a namespace or not — decides which locals hold a shared value. */
11
- export const isProducerCall = (node) => node?.type === "CallExpression" && PRODUCERS.has(calleeName(node));
12
- /** The hooks whose callback is an animated updater. */
13
- export const ANIMATED_STYLE_HOOKS = new Set(["useAnimatedStyle", "useAnimatedProps"]);
1
+ import{calleeName as s}from"../../../lib/ast.js";const t=new Set(["useSharedValue","useDerivedValue","useScrollOffset","useScrollViewOffset","makeMutable"]);export const isProducerCall=(e)=>e?.type==="CallExpression"&&t.has(s(e)),ANIMATED_STYLE_HOOKS=new Set(["useAnimatedStyle","useAnimatedProps"]);
@@ -1,49 +1 @@
1
- import { attributeName, gate, importedSpecifiers, problem, tagPath } from "../../../lib/ast.js";
2
- const MESSAGE = "Add an explicit `opaque` prop to this `<Canvas>`: `opaque={Platform.OS === 'android'}` for a fullscreen animated canvas, or `opaque={false}` when it needs transparency, view transforms, or ordinary stacking.";
3
- export const canvasOpaque = problem("Require an explicit `opaque` prop on a Skia `<Canvas>`. A fullscreen animated canvas wants it on; anything that needs transparency or a view transform wants it off.", {
4
- createOnce(context) {
5
- const canvasLocals = new Set();
6
- const namespaces = new Set();
7
- const elements = [];
8
- /** `<Canvas>` under its imported name, or `<Skia.Canvas>` under a namespace or default import. */
9
- const namesACanvas = (name) => {
10
- const path = tagPath(name);
11
- const dot = path.lastIndexOf(".");
12
- if (dot === -1)
13
- return canvasLocals.has(path);
14
- return namespaces.has(path.slice(0, dot)) && path.slice(dot + 1) === "Canvas";
15
- };
16
- return {
17
- before() {
18
- canvasLocals.clear();
19
- namespaces.clear();
20
- elements.length = 0;
21
- return gate(context, "react-native-skia");
22
- },
23
- ImportDeclaration(node) {
24
- for (const specifier of importedSpecifiers(node, "@shopify/react-native-skia")) {
25
- if (specifier.type === "ImportSpecifier") {
26
- const { imported, local } = specifier;
27
- if (imported.type === "Identifier" && imported.name === "Canvas")
28
- canvasLocals.add(local.name);
29
- continue;
30
- }
31
- if (specifier.type === "ImportNamespaceSpecifier" || specifier.type === "ImportDefaultSpecifier") {
32
- namespaces.add(specifier.local.name);
33
- }
34
- }
35
- },
36
- JSXOpeningElement(node) {
37
- if (!namesACanvas(node.name))
38
- return;
39
- const hasOpaque = node.attributes.some(attribute => attribute.type === "JSXAttribute" && attributeName(attribute) === "opaque");
40
- if (!hasOpaque)
41
- elements.push(node);
42
- },
43
- "Program:exit"() {
44
- for (const element of elements)
45
- context.report({ node: element, message: MESSAGE });
46
- },
47
- };
48
- },
49
- });
1
+ import{attributeName as p,gate as f,importedSpecifiers as m,problem as l,tagPath as u}from"../../../lib/ast.js";const d="Add an explicit `opaque` prop to this `<Canvas>`: `opaque={Platform.OS === 'android'}` for a fullscreen animated canvas, or `opaque={false}` when it needs transparency, view transforms, or ordinary stacking.";export const canvasOpaque=l("Require an explicit `opaque` prop on a Skia `<Canvas>`. A fullscreen animated canvas wants it on; anything that needs transparency or a view transform wants it off.",{createOnce(o){const n=new Set,r=new Set,s=[],i=(a)=>{const e=u(a),t=e.lastIndexOf(".");if(t===-1)return n.has(e);return r.has(e.slice(0,t))&&e.slice(t+1)==="Canvas"};return{before(){n.clear();r.clear();s.length=0;return f(o,"react-native-skia")},ImportDeclaration(a){for(const e of m(a,"@shopify/react-native-skia")){if(e.type==="ImportSpecifier"){const{imported:t,local:c}=e;if(t.type==="Identifier"&&t.name==="Canvas")n.add(c.name);continue}if(e.type==="ImportNamespaceSpecifier"||e.type==="ImportDefaultSpecifier")r.add(e.local.name)}},JSXOpeningElement(a){if(!i(a.name))return;if(!a.attributes.some((t)=>t.type==="JSXAttribute"&&p(t)==="opaque"))s.push(a)},"Program:exit"(){for(const a of s)o.report({node:a,message:d})}}}});
@@ -1,21 +1 @@
1
- import { defineModule } from "../../../lib/module.js";
2
- import { canvasOpaque } from "./canvas-opaque.js";
3
- export default defineModule({
4
- meta: { name: "@ashstack/skia" },
5
- url: import.meta.url,
6
- packages: ["@shopify/react-native-skia"],
7
- option: "skia",
8
- docsWhen: "auto-enabled when `@shopify/react-native-skia` is a dependency",
9
- rules: {
10
- "canvas-opaque": canvasOpaque,
11
- },
12
- restrictedImports: {
13
- paths: [
14
- {
15
- name: "@shopify/react-native-skia",
16
- importNames: ["usePathValue", "usePathInterpolation"],
17
- 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.",
18
- },
19
- ],
20
- },
21
- });
1
+ import{defineModule as e}from"../../../lib/module.js";import{canvasOpaque as a}from"./canvas-opaque.js";export default e({meta:{name:"@ashstack/skia"},url:import.meta.url,packages:["@shopify/react-native-skia"],option:"skia",docsWhen:"auto-enabled when `@shopify/react-native-skia` is a dependency",rules:{"canvas-opaque":a},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."}]}});
@@ -1,14 +1 @@
1
- import { defineModule } from "../../../lib/module.js";
2
- import { requireCachePolicy } from "./require-cache-policy.js";
3
- import { requireResize } from "./require-resize.js";
4
- export default defineModule({
5
- meta: { name: "@ashstack/turbo-image" },
6
- url: import.meta.url,
7
- packages: ["react-native-turbo-image"],
8
- option: "turboImage",
9
- docsWhen: "auto-enabled when `react-native-turbo-image` is a dependency",
10
- rules: {
11
- "require-resize": requireResize,
12
- "require-cache-policy": requireCachePolicy,
13
- },
14
- });
1
+ import{defineModule as e}from"../../../lib/module.js";import{requireCachePolicy as r}from"./require-cache-policy.js";import{requireResize as a}from"./require-resize.js";export default e({meta:{name:"@ashstack/turbo-image"},url:import.meta.url,packages:["react-native-turbo-image"],option:"turboImage",docsWhen:"auto-enabled when `react-native-turbo-image` is a dependency",rules:{"require-resize":a,"require-cache-policy":r}});
@@ -1,21 +1 @@
1
- import { attributeName, componentName, gate, problem } from "../../../lib/ast.js";
2
- const MESSAGE = 'Add `cachePolicy` to this TurboImage (normally `cachePolicy="dataCache"`), or it re-fetches over the network on every cold start.';
3
- export const requireCachePolicy = problem("Require `cachePolicy` on a TurboImage. Without one the image is re-fetched over the network on every cold start, so an already-scrolled feed costs its bandwidth again.", {
4
- createOnce(context) {
5
- return {
6
- before() {
7
- return gate(context, "TurboImage");
8
- },
9
- JSXOpeningElement(node) {
10
- if (componentName(node.name) !== "TurboImage")
11
- return;
12
- const { attributes } = node;
13
- if (attributes.some(attribute => attribute.type === "JSXSpreadAttribute"))
14
- return;
15
- if (attributes.some(attribute => attributeName(attribute) === "cachePolicy"))
16
- return;
17
- context.report({ node: node.name, message: MESSAGE });
18
- },
19
- };
20
- },
21
- });
1
+ import{attributeName as a,componentName as c,gate as n,problem as i}from"../../../lib/ast.js";const m='Add `cachePolicy` to this TurboImage (normally `cachePolicy="dataCache"`), or it re-fetches over the network on every cold start.';export const requireCachePolicy=i("Require `cachePolicy` on a TurboImage. Without one the image is re-fetched over the network on every cold start, so an already-scrolled feed costs its bandwidth again.",{createOnce(t){return{before(){return n(t,"TurboImage")},JSXOpeningElement(e){if(c(e.name)!=="TurboImage")return;const{attributes:o}=e;if(o.some((r)=>r.type==="JSXSpreadAttribute"))return;if(o.some((r)=>a(r)==="cachePolicy"))return;t.report({node:e.name,message:m})}}}});
@@ -1,22 +1 @@
1
- import { attributeName, componentName, gate, problem } from "../../../lib/ast.js";
2
- const MESSAGE = "Add `resize` to this TurboImage, set slightly BELOW the rendered width in points, so the native decoder downsamples before the bitmap reaches memory. Rounding down costs nothing visible; a value above the rendered width keeps the full-resolution decode (a 4000px photo in a 100pt avatar holds roughly 48MB of pixels).";
3
- export const requireResize = problem("Require `resize` on a TurboImage so the native decoder downsamples before the bitmap reaches memory. A full-resolution decode wastes tens of megabytes and stalls the first frame.", {
4
- createOnce(context) {
5
- return {
6
- before() {
7
- return gate(context, "TurboImage");
8
- },
9
- JSXOpeningElement(node) {
10
- if (componentName(node.name) !== "TurboImage")
11
- return;
12
- const { attributes } = node;
13
- if (attributes.some(attribute => attribute.type === "JSXSpreadAttribute"))
14
- return;
15
- const hasResize = attributes.some(attribute => attribute.type === "JSXAttribute" && attributeName(attribute) === "resize");
16
- if (hasResize)
17
- return;
18
- context.report({ node: node.name, message: MESSAGE });
19
- },
20
- };
21
- },
22
- });
1
+ import{attributeName as s,componentName as a,gate as i,problem as n}from"../../../lib/ast.js";const m="Add `resize` to this TurboImage, set slightly BELOW the rendered width in points, so the native decoder downsamples before the bitmap reaches memory. Rounding down costs nothing visible; a value above the rendered width keeps the full-resolution decode (a 4000px photo in a 100pt avatar holds roughly 48MB of pixels).";export const requireResize=n("Require `resize` on a TurboImage so the native decoder downsamples before the bitmap reaches memory. A full-resolution decode wastes tens of megabytes and stalls the first frame.",{createOnce(r){return{before(){return i(r,"TurboImage")},JSXOpeningElement(t){if(a(t.name)!=="TurboImage")return;const{attributes:o}=t;if(o.some((e)=>e.type==="JSXSpreadAttribute"))return;if(o.some((e)=>e.type==="JSXAttribute"&&s(e)==="resize"))return;r.report({node:t.name,message:m})}}}});
@@ -1,37 +1 @@
1
- import { calleeName, problem } from "../../../lib/ast.js";
2
- import { readsTheme } from "./shared.js";
3
- const WORKLET_HOOKS = new Set(["useAnimatedStyle", "useDerivedValue", "useAnimatedProps"]);
4
- const MESSAGE = "Use `useAnimatedTheme()` and read its shared value inside the Reanimated worklet so theme changes reach the UI thread.";
5
- export const animatedTheme = problem("A Reanimated worklet hook cannot see `useUnistyles()` theme changes on the UI thread. Read the shared value from `useAnimatedTheme()` there instead.", {
6
- createOnce(context) {
7
- let declaresTheme = false;
8
- const candidates = [];
9
- return {
10
- before() {
11
- declaresTheme = false;
12
- candidates.length = 0;
13
- },
14
- VariableDeclarator(node) {
15
- const { id, init } = node;
16
- if (id.type === "ObjectPattern" &&
17
- calleeName(init) === "useUnistyles" &&
18
- id.properties.some(property => property.type === "Property" && property.key.type === "Identifier" && property.key.name === "theme")) {
19
- declaresTheme = true;
20
- }
21
- },
22
- CallExpression(node) {
23
- if (!WORKLET_HOOKS.has(calleeName(node)))
24
- return;
25
- if (!readsTheme(node))
26
- return;
27
- candidates.push(node);
28
- },
29
- "Program:exit"() {
30
- if (!declaresTheme)
31
- return;
32
- for (const node of candidates)
33
- context.report({ node, message: MESSAGE });
34
- },
35
- };
36
- },
37
- });
1
+ import{calleeName as n,problem as m}from"../../../lib/ast.js";import{readsTheme as h}from"./shared.js";const d=new Set(["useAnimatedStyle","useDerivedValue","useAnimatedProps"]),l="Use `useAnimatedTheme()` and read its shared value inside the Reanimated worklet so theme changes reach the UI thread.";export const animatedTheme=m("A Reanimated worklet hook cannot see `useUnistyles()` theme changes on the UI thread. Read the shared value from `useAnimatedTheme()` there instead.",{createOnce(i){let t=!1;const a=[];return{before(){t=!1;a.length=0},VariableDeclarator(e){const{id:s,init:o}=e;if(s.type==="ObjectPattern"&&n(o)==="useUnistyles"&&s.properties.some((r)=>r.type==="Property"&&r.key.type==="Identifier"&&r.key.name==="theme"))t=!0},CallExpression(e){if(!d.has(n(e)))return;if(!h(e))return;a.push(e)},"Program:exit"(){if(!t)return;for(const e of a)i.report({node:e,message:l})}}}});
@@ -1,73 +1 @@
1
- import { calleeName, findInSubtree, gate, problem, subtreeHas, tagIdentifier } from "../../../lib/ast.js";
2
- import { isStyleSheetCreate, memberPath, propertyName, stylesObjectOf } from "./shared.js";
3
- const MESSAGES = {
4
- contentContainerRuntime: "Wrap this component with `withUnistyles`, or pass `contentContainerStyle` a plain hook-fed style. On a raw component `contentContainerStyle` never subscribes to Unistyles updates.",
5
- contentContainerTheme: "Wrap this component with `withUnistyles`, or pass `contentContainerStyle` a plain hook-fed style. On a raw component `contentContainerStyle` never subscribes to theme changes.",
6
- };
7
- export const contentContainer = problem("A raw component never subscribes its `contentContainerStyle` to theme or `rt` updates. Wrap the component with `withUnistyles` when the style depends on either.", {
8
- createOnce(context) {
9
- const sheetStyles = new Map();
10
- const wrapped = new Set();
11
- const attributes = [];
12
- return {
13
- before() {
14
- sheetStyles.clear();
15
- wrapped.clear();
16
- attributes.length = 0;
17
- return gate(context, "contentContainerStyle");
18
- },
19
- VariableDeclarator(node) {
20
- const { id, init } = node;
21
- if (id.type !== "Identifier")
22
- return;
23
- if (calleeName(init) === "withUnistyles") {
24
- wrapped.add(id.name);
25
- return;
26
- }
27
- if (init?.type !== "CallExpression" || !isStyleSheetCreate(init))
28
- return;
29
- const styles = stylesObjectOf(init.arguments[0]);
30
- if (!styles)
31
- return;
32
- const map = new Map();
33
- for (const property of styles.properties) {
34
- if (property.type !== "Property")
35
- continue;
36
- map.set(propertyName(property), property.value);
37
- }
38
- sheetStyles.set(id.name, map);
39
- },
40
- JSXAttribute(node) {
41
- if (node.name.type !== "JSXIdentifier" || node.name.name !== "contentContainerStyle")
42
- return;
43
- attributes.push(node);
44
- },
45
- "Program:exit"() {
46
- for (const attribute of attributes) {
47
- const reference = findInSubtree(attribute.value, current => current.type === "MemberExpression" &&
48
- current.object.type === "Identifier" &&
49
- sheetStyles.has(current.object.name));
50
- if (reference?.type !== "MemberExpression")
51
- continue;
52
- const owner = attribute.parent;
53
- if (owner.type === "JSXOpeningElement" && wrapped.has(tagIdentifier(owner.name)))
54
- continue;
55
- const { object, property } = reference;
56
- if (object.type !== "Identifier")
57
- continue;
58
- const definition = sheetStyles.get(object.name)?.get(property.type === "Identifier" ? property.name : "");
59
- if (!definition)
60
- continue;
61
- const usesRuntime = subtreeHas(definition, current => current.type === "MemberExpression" && memberPath(current).startsWith("rt."));
62
- if (usesRuntime) {
63
- context.report({ node: attribute, message: MESSAGES.contentContainerRuntime });
64
- continue;
65
- }
66
- const usesTheme = subtreeHas(definition, current => current.type === "MemberExpression" && memberPath(current).startsWith("theme."));
67
- if (usesTheme)
68
- context.report({ node: attribute, message: MESSAGES.contentContainerTheme });
69
- }
70
- },
71
- };
72
- },
73
- });
1
+ import{calleeName as f,findInSubtree as u,gate as b,problem as d,subtreeHas as y,tagIdentifier as S}from"../../../lib/ast.js";import{isStyleSheetCreate as w,memberPath as l,propertyName as C,stylesObjectOf as g}from"./shared.js";const h={contentContainerRuntime:"Wrap this component with `withUnistyles`, or pass `contentContainerStyle` a plain hook-fed style. On a raw component `contentContainerStyle` never subscribes to Unistyles updates.",contentContainerTheme:"Wrap this component with `withUnistyles`, or pass `contentContainerStyle` a plain hook-fed style. On a raw component `contentContainerStyle` never subscribes to theme changes."};export const contentContainer=d("A raw component never subscribes its `contentContainerStyle` to theme or `rt` updates. Wrap the component with `withUnistyles` when the style depends on either.",{createOnce(p){const a=new Map,c=new Set,m=[];return{before(){a.clear();c.clear();m.length=0;return b(p,"contentContainerStyle")},VariableDeclarator(e){const{id:r,init:n}=e;if(r.type!=="Identifier")return;if(f(n)==="withUnistyles"){c.add(r.name);return}if(n?.type!=="CallExpression"||!w(n))return;const s=g(n.arguments[0]);if(!s)return;const i=new Map;for(const o of s.properties){if(o.type!=="Property")continue;i.set(C(o),o.value)}a.set(r.name,i)},JSXAttribute(e){if(e.name.type!=="JSXIdentifier"||e.name.name!=="contentContainerStyle")return;m.push(e)},"Program:exit"(){for(const e of m){const r=u(e.value,(t)=>t.type==="MemberExpression"&&t.object.type==="Identifier"&&a.has(t.object.name));if(r?.type!=="MemberExpression")continue;const n=e.parent;if(n.type==="JSXOpeningElement"&&c.has(S(n.name)))continue;const{object:s,property:i}=r;if(s.type!=="Identifier")continue;const o=a.get(s.name)?.get(i.type==="Identifier"?i.name:"");if(!o)continue;if(y(o,(t)=>t.type==="MemberExpression"&&l(t).startsWith("rt."))){p.report({node:e,message:h.contentContainerRuntime});continue}if(y(o,(t)=>t.type==="MemberExpression"&&l(t).startsWith("theme.")))p.report({node:e,message:h.contentContainerTheme})}}}}});
@@ -1,116 +1 @@
1
- import { calleeName, subtreeHas } from "../../../lib/ast.js";
2
- import { inCreate, isStyleSheetCreate, memberPath, propertyName } from "./shared.js";
3
- const LOGICAL_SPACING = new Set(["paddingLeft", "paddingRight", "marginLeft", "marginRight"]);
4
- const LEGACY_SHADOW = new Set(["shadowColor", "shadowOffset", "shadowOpacity", "shadowRadius", "elevation"]);
5
- const MESSAGES = {
6
- logicalSpacing: "Use `paddingStart`/`paddingEnd` or `marginStart`/`marginEnd` here so the directional spacing mirrors in RTL.",
7
- rtlInSheet: "Use `rt.rtl` for RTL-dependent styles so Unistyles tracks the dependency natively.",
8
- screenDimensions: "Use reactive `rt.screen` inside `StyleSheet.create` instead of a `Dimensions.get` snapshot.",
9
- pixelRatio: "Use reactive `rt.pixelRatio` inside `StyleSheet.create`.",
10
- fontScale: "Use reactive `rt.fontScale` inside `StyleSheet.create`.",
11
- statusBar: "Use reactive `rt.statusBar.height` inside `StyleSheet.create`.",
12
- colorScheme: "Use the Unistyles theme or reactive `rt.colorScheme` inside `StyleSheet.create`.",
13
- fullRuntime: "Use the injected mini runtime (`rt`) inside `StyleSheet.create` instead of `UnistylesRuntime`, so the style recalculates on its own.",
14
- fullRuntimeDestructure: "Destructure from the injected mini runtime (`rt`) inside `StyleSheet.create`, not from `UnistylesRuntime`.",
15
- themeScreen: "Use reactive `rt.screen`, or Unistyles breakpoints, instead of the theme's module-initialization screen snapshot.",
16
- borderCurve: 'Add `borderCurve` (normally `"continuous"`) alongside `borderRadius`, or the corner renders as a circular arc rather than the squircle native iOS controls use.',
17
- legacyShadow: 'Use `boxShadow` with CSS syntax, e.g. `boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)"`, which renders on both platforms on the New Architecture, instead of the legacy `shadow*`/`elevation` props.',
18
- asConst: "Drop `as const` here — the typed `StyleSheet.create` already narrows the literal. If TS still widens the value to `string`, fix the `StyleSheet` import instead.",
19
- };
20
- export const inSheet = inCreate("Inside `StyleSheet.create`, require `rt` and theme values rather than Dimensions, PixelRatio, Appearance, I18nManager, StatusBar or `UnistylesRuntime`. It also covers logical spacing, `boxShadow`, `borderCurve` and a redundant `as const`.", (context, inside) => ({
21
- CallExpression(node) {
22
- if (!inside() || isStyleSheetCreate(node))
23
- return;
24
- const { callee } = node;
25
- const path = callee.type === "MemberExpression" ? memberPath(callee) : calleeName(node);
26
- if (path === "Dimensions.get")
27
- context.report({ node, message: MESSAGES.screenDimensions });
28
- else if (path === "PixelRatio.get")
29
- context.report({ node, message: MESSAGES.pixelRatio });
30
- else if (path === "PixelRatio.getFontScale")
31
- context.report({ node, message: MESSAGES.fontScale });
32
- else if (path === "Appearance.getColorScheme")
33
- context.report({ node, message: MESSAGES.colorScheme });
34
- else if (path === "useColorScheme")
35
- context.report({ node, message: MESSAGES.colorScheme });
36
- },
37
- MemberExpression(node) {
38
- if (!inside())
39
- return;
40
- const { parent } = node;
41
- if (parent.type === "MemberExpression" && parent.object === node)
42
- return;
43
- const path = memberPath(node);
44
- if (path === "I18nManager.isRTL") {
45
- context.report({ node, message: MESSAGES.rtlInSheet });
46
- return;
47
- }
48
- if (path === "StatusBar.currentHeight") {
49
- context.report({ node, message: MESSAGES.statusBar });
50
- return;
51
- }
52
- if (path.startsWith("UnistylesRuntime.")) {
53
- context.report({ node, message: MESSAGES.fullRuntime });
54
- return;
55
- }
56
- if (path.startsWith("theme.screen."))
57
- context.report({ node, message: MESSAGES.themeScreen });
58
- },
59
- VariableDeclarator(node) {
60
- if (!inside())
61
- return;
62
- const { id, init } = node;
63
- if (id.type !== "ObjectPattern")
64
- return;
65
- if (init?.type === "Identifier" && init.name === "UnistylesRuntime") {
66
- context.report({ node, message: MESSAGES.fullRuntimeDestructure });
67
- }
68
- },
69
- TSAsExpression(node) {
70
- if (!inside())
71
- return;
72
- const { typeAnnotation } = node;
73
- if (typeAnnotation.type !== "TSTypeReference")
74
- return;
75
- const { typeName } = typeAnnotation;
76
- if (typeName.type !== "Identifier" || typeName.name !== "const")
77
- return;
78
- context.report({ node, message: MESSAGES.asConst });
79
- },
80
- ObjectExpression(node) {
81
- if (!inside())
82
- return;
83
- let borderRadius = null;
84
- let hasBorderCurve = false;
85
- for (const property of node.properties) {
86
- if (property.type !== "Property")
87
- continue;
88
- const name = propertyName(property);
89
- if (name === "borderRadius")
90
- borderRadius = property;
91
- else if (name === "borderCurve")
92
- hasBorderCurve = true;
93
- }
94
- if (borderRadius && !hasBorderCurve)
95
- context.report({ node: borderRadius, message: MESSAGES.borderCurve });
96
- },
97
- Property(node) {
98
- if (!inside())
99
- return;
100
- const name = propertyName(node);
101
- if (LEGACY_SHADOW.has(name)) {
102
- context.report({ node, message: MESSAGES.legacyShadow });
103
- return;
104
- }
105
- if (!LOGICAL_SPACING.has(name))
106
- return;
107
- const usesInsets = subtreeHas(node, current => {
108
- if (current.type !== "MemberExpression")
109
- return false;
110
- const path = memberPath(current);
111
- return path === "rt.insets.left" || path === "rt.insets.right";
112
- });
113
- if (!usesInsets)
114
- context.report({ node, message: MESSAGES.logicalSpacing });
115
- },
116
- }));
1
+ import{calleeName as h,subtreeHas as p}from"../../../lib/ast.js";import{inCreate as m,isStyleSheetCreate as u,memberPath as l,propertyName as c}from"./shared.js";const S=new Set(["paddingLeft","paddingRight","marginLeft","marginRight"]),f=new Set(["shadowColor","shadowOffset","shadowOpacity","shadowRadius","elevation"]),i={logicalSpacing:"Use `paddingStart`/`paddingEnd` or `marginStart`/`marginEnd` here so the directional spacing mirrors in RTL.",rtlInSheet:"Use `rt.rtl` for RTL-dependent styles so Unistyles tracks the dependency natively.",screenDimensions:"Use reactive `rt.screen` inside `StyleSheet.create` instead of a `Dimensions.get` snapshot.",pixelRatio:"Use reactive `rt.pixelRatio` inside `StyleSheet.create`.",fontScale:"Use reactive `rt.fontScale` inside `StyleSheet.create`.",statusBar:"Use reactive `rt.statusBar.height` inside `StyleSheet.create`.",colorScheme:"Use the Unistyles theme or reactive `rt.colorScheme` inside `StyleSheet.create`.",fullRuntime:"Use the injected mini runtime (`rt`) inside `StyleSheet.create` instead of `UnistylesRuntime`, so the style recalculates on its own.",fullRuntimeDestructure:"Destructure from the injected mini runtime (`rt`) inside `StyleSheet.create`, not from `UnistylesRuntime`.",themeScreen:"Use reactive `rt.screen`, or Unistyles breakpoints, instead of the theme's module-initialization screen snapshot.",borderCurve:'Add `borderCurve` (normally `"continuous"`) alongside `borderRadius`, or the corner renders as a circular arc rather than the squircle native iOS controls use.',legacyShadow:'Use `boxShadow` with CSS syntax, e.g. `boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)"`, which renders on both platforms on the New Architecture, instead of the legacy `shadow*`/`elevation` props.',asConst:"Drop `as const` here — the typed `StyleSheet.create` already narrows the literal. If TS still widens the value to `string`, fix the `StyleSheet` import instead."};export const inSheet=m("Inside `StyleSheet.create`, require `rt` and theme values rather than Dimensions, PixelRatio, Appearance, I18nManager, StatusBar or `UnistylesRuntime`. It also covers logical spacing, `boxShadow`, `borderCurve` and a redundant `as const`.",(s,a)=>({CallExpression(e){if(!a()||u(e))return;const{callee:r}=e,t=r.type==="MemberExpression"?l(r):h(e);if(t==="Dimensions.get")s.report({node:e,message:i.screenDimensions});else if(t==="PixelRatio.get")s.report({node:e,message:i.pixelRatio});else if(t==="PixelRatio.getFontScale")s.report({node:e,message:i.fontScale});else if(t==="Appearance.getColorScheme")s.report({node:e,message:i.colorScheme});else if(t==="useColorScheme")s.report({node:e,message:i.colorScheme})},MemberExpression(e){if(!a())return;const{parent:r}=e;if(r.type==="MemberExpression"&&r.object===e)return;const t=l(e);if(t==="I18nManager.isRTL"){s.report({node:e,message:i.rtlInSheet});return}if(t==="StatusBar.currentHeight"){s.report({node:e,message:i.statusBar});return}if(t.startsWith("UnistylesRuntime.")){s.report({node:e,message:i.fullRuntime});return}if(t.startsWith("theme.screen."))s.report({node:e,message:i.themeScreen})},VariableDeclarator(e){if(!a())return;const{id:r,init:t}=e;if(r.type!=="ObjectPattern")return;if(t?.type==="Identifier"&&t.name==="UnistylesRuntime")s.report({node:e,message:i.fullRuntimeDestructure})},TSAsExpression(e){if(!a())return;const{typeAnnotation:r}=e;if(r.type!=="TSTypeReference")return;const{typeName:t}=r;if(t.type!=="Identifier"||t.name!=="const")return;s.report({node:e,message:i.asConst})},ObjectExpression(e){if(!a())return;let r=null,t=!1;for(const n of e.properties){if(n.type!=="Property")continue;const o=c(n);if(o==="borderRadius")r=n;else if(o==="borderCurve")t=!0}if(r&&!t)s.report({node:r,message:i.borderCurve})},Property(e){if(!a())return;const r=c(e);if(f.has(r)){s.report({node:e,message:i.legacyShadow});return}if(!S.has(r))return;if(!p(e,(n)=>{if(n.type!=="MemberExpression")return!1;const o=l(n);return o==="rt.insets.left"||o==="rt.insets.right"}))s.report({node:e,message:i.logicalSpacing})}}));
@@ -1,58 +1 @@
1
- import { defineModule } from "../../../lib/module.js";
2
- import { animatedTheme } from "./animated-theme.js";
3
- import { contentContainer } from "./content-container.js";
4
- import { inSheet } from "./in-sheet.js";
5
- import { insets } from "./insets.js";
6
- import { noHardcodedColor } from "./no-hardcoded-color.js";
7
- import { noHardcodedSpacing } from "./no-hardcoded-spacing.js";
8
- import { noMargin } from "./no-margin.js";
9
- import { noStyleSpread } from "./no-style-spread.js";
10
- import { noUnusedStyles } from "./no-unused-styles.js";
11
- import { rtlStyleCall } from "./rtl-style-call.js";
12
- import { themeScreenComponent } from "./theme-screen-component.js";
13
- import { themeStyleAttr } from "./theme-style-attr.js";
14
- export default defineModule({
15
- meta: { name: "@ashstack/unistyles" },
16
- rules: {
17
- "animated-theme": animatedTheme,
18
- "content-container": contentContainer,
19
- "in-sheet": inSheet,
20
- insets: insets,
21
- "no-hardcoded-color": noHardcodedColor,
22
- "no-hardcoded-spacing": noHardcodedSpacing,
23
- "no-margin": noMargin,
24
- "no-style-spread": noStyleSpread,
25
- "no-unused-styles": noUnusedStyles,
26
- "rtl-style-call": rtlStyleCall,
27
- "theme-screen-component": themeScreenComponent,
28
- "theme-style-attr": themeStyleAttr,
29
- },
30
- url: import.meta.url,
31
- packages: ["react-native-unistyles"],
32
- option: "unistyles",
33
- docsWhen: "auto-enabled when `react-native-unistyles` is a dependency",
34
- restrictedImports: {
35
- paths: [
36
- {
37
- name: "react-native",
38
- importNames: ["StyleSheet"],
39
- message: "Import `StyleSheet` from `react-native-unistyles` instead of `react-native`.",
40
- },
41
- {
42
- name: "react-native",
43
- importNames: ["useWindowDimensions", "Dimensions"],
44
- message: "Read screen size from `rt.screen` inside `StyleSheet.create`, or `UnistylesRuntime.screen` outside it, instead of the `Dimensions` APIs.",
45
- },
46
- {
47
- name: "react-native",
48
- importNames: ["SafeAreaView"],
49
- message: 'Set `contentInsetAdjustmentBehavior="automatic"` on the scroll view, or apply `rt.insets` from `react-native-unistyles`, instead of `SafeAreaView`.',
50
- },
51
- {
52
- name: "react-native-safe-area-context",
53
- importNames: ["useSafeAreaInsets", "useSafeAreaFrame", "SafeAreaView"],
54
- message: "Read safe-area values from `rt.insets`, or `UnistylesRuntime.insets` outside a stylesheet, in `react-native-unistyles`.",
55
- },
56
- ],
57
- },
58
- });
1
+ import{defineModule as e}from"../../../lib/module.js";import{animatedTheme as t}from"./animated-theme.js";import{contentContainer as n}from"./content-container.js";import{inSheet as a}from"./in-sheet.js";import{insets as r}from"./insets.js";import{noHardcodedColor as s}from"./no-hardcoded-color.js";import{noHardcodedSpacing as o}from"./no-hardcoded-spacing.js";import{noMargin as i}from"./no-margin.js";import{noStyleSpread as m}from"./no-style-spread.js";import{noUnusedStyles as l}from"./no-unused-styles.js";import{rtlStyleCall as c}from"./rtl-style-call.js";import{themeScreenComponent as d}from"./theme-screen-component.js";import{themeStyleAttr as p}from"./theme-style-attr.js";export default e({meta:{name:"@ashstack/unistyles"},rules:{"animated-theme":t,"content-container":n,"in-sheet":a,insets:r,"no-hardcoded-color":s,"no-hardcoded-spacing":o,"no-margin":i,"no-style-spread":m,"no-unused-styles":l,"rtl-style-call":c,"theme-screen-component":d,"theme-style-attr":p},url:import.meta.url,packages:["react-native-unistyles"],option:"unistyles",docsWhen:"auto-enabled when `react-native-unistyles` is a dependency",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`."}]}});