eslint-plugin-zappie 0.2.0 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # eslint-plugin-zappie
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - afb46d4: Add storefront security lint rules, pass outbound hosts from `zappie.config.ts` to ESLint, and provide `safeRedirect` from `@zappie/sdk/redirect` for untrusted navigation targets.
8
+
3
9
  ## 0.2.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -17,28 +17,46 @@ import zappie from "eslint-plugin-zappie";
17
17
  export default [...zappie.configs.recommended];
18
18
  ```
19
19
 
20
- `recommended` turns every rule on as an error, except `verify-markers`, which warns. `strict` makes that one an error too.
20
+ `recommended` makes literal unlisted request hosts errors. Computed request
21
+ URLs use the companion `no-unlisted-fetch-dynamic` diagnostic at warning
22
+ severity because their hosts cannot be proved statically. `verify-markers`
23
+ also warns. `strict` makes both warning diagnostics errors.
21
24
 
22
25
  ## Rules
23
26
 
24
- | Rule | What it asks for |
25
- | ---------------------------- | ---------------------------------------------------------------- |
26
- | `zappie/no-direct-api` | Call Zappie through the SDK. |
27
- | `zappie/no-money-literals` | Use SDK prices and totals in your storefront. |
28
- | `zappie/use-format-money` | Format SDK Money with `format.money()` or `<Money>`. Fixable. |
29
- | `zappie/cart-via-sdk` | Keep your cart state in the SDK. |
30
- | `zappie/checkout-via-sdk` | Open checkout through the Zappie SDK. |
31
- | `zappie/no-custom-auth` | Sign customers in through the Zappie SDK. |
32
- | `zappie/no-tracking-scripts` | Forward analytics through Zappie. |
33
- | `zappie/overlay-primitive` | Use Overlay to keep dialogs and sheets in the URL. |
34
- | `zappie/verify-markers` | Mark shopping actions so verify can find them. Fixable. |
35
- | `zappie/no-lazy-overlay` | Load overlays with `deferred()`, not `React.lazy` or `Suspense`. |
27
+ | Rule | What it asks for |
28
+ | ---------------------------------- | ---------------------------------------------------------------- |
29
+ | `zappie/no-direct-api` | Call Zappie through the SDK. |
30
+ | `zappie/no-money-literals` | Use SDK prices and totals in your storefront. |
31
+ | `zappie/use-format-money` | Format SDK Money with `format.money()` or `<Money>`. Fixable. |
32
+ | `zappie/cart-via-sdk` | Keep your cart state in the SDK. |
33
+ | `zappie/checkout-via-sdk` | Open checkout through the Zappie SDK. |
34
+ | `zappie/no-custom-auth` | Sign customers in through the Zappie SDK. |
35
+ | `zappie/no-tracking-scripts` | Forward analytics through Zappie. |
36
+ | `zappie/overlay-primitive` | Use Overlay to keep dialogs and sheets in the URL. |
37
+ | `zappie/verify-markers` | Mark shopping actions so verify can find them. Fixable. |
38
+ | `zappie/no-lazy-overlay` | Load overlays with `deferred()`, not `React.lazy` or `Suspense`. |
39
+ | `zappie/no-unsafe-html` | Sanitize rich text before inserting HTML. |
40
+ | `zappie/no-eval` | Do not execute strings as code. |
41
+ | `zappie/no-external-scripts` | Ship scripts in the storefront bundle. |
42
+ | `zappie/safe-external-links` | Protect new tabs and validate redirect targets. Fixable. |
43
+ | `zappie/no-unlisted-fetch` | List external request hosts in `outbound.allow`. |
44
+ | `zappie/no-unlisted-fetch-dynamic` | Review computed request hosts. |
45
+ | `zappie/no-server-env-in-client` | Keep server environment values out of client bundles. |
36
46
 
37
47
  `recommended` also turns on the recommended `jsx-a11y` rules as the accessibility baseline. `zappie check` fails a project that turns any of them down.
38
48
 
39
49
  Typed rules use TypeScript's project service when it's available, and fall back to syntax checks when it isn't.
40
50
 
41
- The rules give early feedback. They aren't a security boundary: Zappie re-prices every order on the server and runs checkout on its own site.
51
+ The rules give early feedback. They aren't a security boundary. Server-side
52
+ validation, response headers and runtime network policy must still enforce the
53
+ same constraints.
54
+
55
+ The `zappie-internal` config disables `no-external-scripts` only for
56
+ `packages/foundations/src/branch-map/loader.ts`. That reviewed Google Maps
57
+ loader must create the provider script from a runtime browser key; general
58
+ storefront code does not get the exemption. Phase 3 CSP must allow its provider
59
+ host explicitly.
42
60
 
43
61
  ## Docs
44
62
 
package/dist/index.d.ts CHANGED
@@ -1,36 +1,88 @@
1
+ import * as _typescript_eslint_utils_json_schema from '@typescript-eslint/utils/json-schema';
1
2
  import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
2
3
  import { TSESLint } from '@typescript-eslint/utils';
3
4
 
4
- declare const cartViaSdk: _typescript_eslint_utils_ts_eslint.RuleModule<"cart", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
5
+ declare const cartViaSdk: _typescript_eslint_utils_ts_eslint.RuleModule<"cart", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
6
+ name: string;
7
+ };
5
8
 
6
- declare const checkoutViaSdk: _typescript_eslint_utils_ts_eslint.RuleModule<"checkout", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
9
+ declare const checkoutViaSdk: _typescript_eslint_utils_ts_eslint.RuleModule<"checkout", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
10
+ name: string;
11
+ };
7
12
 
8
- declare const imagePriority: TSESLint.RuleModule<"mapped" | "routeLimit", [], unknown, TSESLint.RuleListener>;
13
+ declare const imagePriority: TSESLint.RuleModule<"mapped" | "routeLimit", [], unknown, TSESLint.RuleListener> & {
14
+ name: string;
15
+ };
9
16
 
10
- declare const itemLinkPrefetch: _typescript_eslint_utils_ts_eslint.RuleModule<"prefetch", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
17
+ declare const itemLinkPrefetch: _typescript_eslint_utils_ts_eslint.RuleModule<"prefetch", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
18
+ name: string;
19
+ };
11
20
 
12
- type Options = [{
21
+ type Options$1 = [{
13
22
  serverFiles?: string[];
14
23
  }];
15
- declare const noCustomAuth: _typescript_eslint_utils_ts_eslint.RuleModule<"auth", Options, unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
24
+ declare const noCustomAuth: _typescript_eslint_utils_ts_eslint.RuleModule<"auth", Options$1, unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
25
+ name: string;
26
+ };
16
27
 
17
28
  type Message$1 = "direct" | "internal" | "storefront" | "checkout" | "customer";
18
- declare const noDirectApi: _typescript_eslint_utils_ts_eslint.RuleModule<Message$1, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
29
+ declare const noDirectApi: _typescript_eslint_utils_ts_eslint.RuleModule<Message$1, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
30
+ name: string;
31
+ };
19
32
 
20
- declare const noLazyOverlay: _typescript_eslint_utils_ts_eslint.RuleModule<"lazy" | "suspense", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
33
+ declare const noEval: _typescript_eslint_utils_ts_eslint.RuleModule<"eval", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
34
+ name: string;
35
+ };
21
36
 
22
- declare const noMoneyLiterals: _typescript_eslint_utils_ts_eslint.RuleModule<"arithmetic" | "literal" | "scale", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
37
+ declare const noExternalScripts: _typescript_eslint_utils_ts_eslint.RuleModule<"externalScript", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
38
+ name: string;
39
+ };
40
+
41
+ declare const noLazyOverlay: _typescript_eslint_utils_ts_eslint.RuleModule<"lazy" | "suspense", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
42
+ name: string;
43
+ };
23
44
 
24
- declare const noTrackingScripts: _typescript_eslint_utils_ts_eslint.RuleModule<"tracking", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
45
+ declare const noMoneyLiterals: _typescript_eslint_utils_ts_eslint.RuleModule<"arithmetic" | "literal" | "scale", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
46
+ name: string;
47
+ };
48
+
49
+ declare const noServerEnvInClient: _typescript_eslint_utils_ts_eslint.RuleModule<"serverEnv", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
50
+ name: string;
51
+ };
52
+
53
+ declare const noTrackingScripts: _typescript_eslint_utils_ts_eslint.RuleModule<"tracking", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
54
+ name: string;
55
+ };
56
+
57
+ type Options = {
58
+ dynamicOnly?: boolean;
59
+ };
60
+ declare const noUnlistedFetch: _typescript_eslint_utils_ts_eslint.RuleModule<"dynamicRequest" | "unlistedRequest", [Options], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
61
+ name: string;
62
+ };
63
+
64
+ declare const noUnsafeHtml: TSESLint.RuleModule<"unsafeHtml", [], unknown, TSESLint.RuleListener> & {
65
+ name: string;
66
+ };
25
67
 
26
68
  declare const overlayPrimitive: _typescript_eslint_utils_ts_eslint.RuleModule<"overlay", [{
27
69
  keys?: string[];
28
- }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
70
+ }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
71
+ name: string;
72
+ };
73
+
74
+ declare const safeExternalLinks: _typescript_eslint_utils_ts_eslint.RuleModule<"javascriptUrl" | "noopener" | "redirect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
75
+ name: string;
76
+ };
29
77
 
30
78
  type Message = "currency" | "direct" | "minor" | "useAlias";
31
- declare const useFormatMoney: _typescript_eslint_utils_ts_eslint.RuleModule<Message, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
79
+ declare const useFormatMoney: _typescript_eslint_utils_ts_eslint.RuleModule<Message, [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
80
+ name: string;
81
+ };
32
82
 
33
- declare const verifyMarkers: TSESLint.RuleModule<"marker", [], unknown, TSESLint.RuleListener>;
83
+ declare const verifyMarkers: TSESLint.RuleModule<"marker", [], unknown, TSESLint.RuleListener> & {
84
+ name: string;
85
+ };
34
86
 
35
87
  type ZappieFlatConfig = Record<string, unknown>;
36
88
  declare const plugin: {
@@ -39,24 +91,96 @@ declare const plugin: {
39
91
  version: string;
40
92
  };
41
93
  rules: {
42
- "no-direct-api": _typescript_eslint_utils_ts_eslint.RuleModule<"direct" | "internal" | "storefront" | "checkout" | "customer", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
43
- "no-tracking-scripts": _typescript_eslint_utils_ts_eslint.RuleModule<"tracking", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
44
- "checkout-via-sdk": _typescript_eslint_utils_ts_eslint.RuleModule<"checkout", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
94
+ "no-direct-api": _typescript_eslint_utils_ts_eslint.RuleModule<"direct" | "internal" | "storefront" | "checkout" | "customer", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
95
+ name: string;
96
+ };
97
+ "no-tracking-scripts": _typescript_eslint_utils_ts_eslint.RuleModule<"tracking", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
98
+ name: string;
99
+ };
100
+ "checkout-via-sdk": _typescript_eslint_utils_ts_eslint.RuleModule<"checkout", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
101
+ name: string;
102
+ };
45
103
  "no-custom-auth": _typescript_eslint_utils_ts_eslint.RuleModule<"auth", [{
46
104
  serverFiles?: string[];
47
- }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
48
- "no-money-literals": _typescript_eslint_utils_ts_eslint.RuleModule<"arithmetic" | "literal" | "scale", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
49
- "use-format-money": _typescript_eslint_utils_ts_eslint.RuleModule<"direct" | "currency" | "minor" | "useAlias", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
105
+ }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
106
+ name: string;
107
+ };
108
+ "no-money-literals": _typescript_eslint_utils_ts_eslint.RuleModule<"arithmetic" | "literal" | "scale", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
109
+ name: string;
110
+ };
111
+ "use-format-money": _typescript_eslint_utils_ts_eslint.RuleModule<"direct" | "currency" | "minor" | "useAlias", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
112
+ name: string;
113
+ };
50
114
  "overlay-primitive": _typescript_eslint_utils_ts_eslint.RuleModule<"overlay", [{
51
115
  keys?: string[];
52
- }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
53
- "cart-via-sdk": _typescript_eslint_utils_ts_eslint.RuleModule<"cart", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
54
- "verify-markers": _typescript_eslint_utils_ts_eslint.RuleModule<"marker", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
55
- "item-link-prefetch": _typescript_eslint_utils_ts_eslint.RuleModule<"prefetch", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
56
- "image-priority": _typescript_eslint_utils_ts_eslint.RuleModule<"mapped" | "routeLimit", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
57
- "no-lazy-overlay": _typescript_eslint_utils_ts_eslint.RuleModule<"lazy" | "suspense", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener>;
116
+ }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
117
+ name: string;
118
+ };
119
+ "cart-via-sdk": _typescript_eslint_utils_ts_eslint.RuleModule<"cart", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
120
+ name: string;
121
+ };
122
+ "verify-markers": _typescript_eslint_utils_ts_eslint.RuleModule<"marker", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
123
+ name: string;
124
+ };
125
+ "item-link-prefetch": _typescript_eslint_utils_ts_eslint.RuleModule<"prefetch", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
126
+ name: string;
127
+ };
128
+ "image-priority": _typescript_eslint_utils_ts_eslint.RuleModule<"mapped" | "routeLimit", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
129
+ name: string;
130
+ };
131
+ "no-lazy-overlay": _typescript_eslint_utils_ts_eslint.RuleModule<"lazy" | "suspense", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
132
+ name: string;
133
+ };
134
+ "no-unsafe-html": _typescript_eslint_utils_ts_eslint.RuleModule<"unsafeHtml", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
135
+ name: string;
136
+ };
137
+ "no-eval": _typescript_eslint_utils_ts_eslint.RuleModule<"eval", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
138
+ name: string;
139
+ };
140
+ "no-external-scripts": _typescript_eslint_utils_ts_eslint.RuleModule<"externalScript", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
141
+ name: string;
142
+ };
143
+ "safe-external-links": _typescript_eslint_utils_ts_eslint.RuleModule<"javascriptUrl" | "noopener" | "redirect", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
144
+ name: string;
145
+ };
146
+ "no-unlisted-fetch": _typescript_eslint_utils_ts_eslint.RuleModule<"dynamicRequest" | "unlistedRequest", [{
147
+ dynamicOnly?: boolean;
148
+ }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
149
+ name: string;
150
+ };
151
+ "no-unlisted-fetch-dynamic": {
152
+ meta: {
153
+ docs: {
154
+ url: string;
155
+ description?: string;
156
+ frozen?: boolean;
157
+ dialects?: string[] | undefined;
158
+ };
159
+ deprecated?: boolean | _typescript_eslint_utils_ts_eslint.DeprecatedInfo;
160
+ fixable?: "code" | "whitespace";
161
+ hasSuggestions?: boolean;
162
+ languages?: string[] | undefined;
163
+ messages: Record<"dynamicRequest" | "unlistedRequest", string>;
164
+ replacedBy?: readonly string[];
165
+ schema: _typescript_eslint_utils_json_schema.JSONSchema4 | readonly _typescript_eslint_utils_json_schema.JSONSchema4[];
166
+ type: "layout" | "problem" | "suggestion";
167
+ defaultOptions?: [{
168
+ dynamicOnly?: boolean;
169
+ }];
170
+ };
171
+ create(context: Readonly<_typescript_eslint_utils_ts_eslint.RuleContext<"dynamicRequest" | "unlistedRequest", [{
172
+ dynamicOnly?: boolean;
173
+ }]>>): _typescript_eslint_utils_ts_eslint.RuleListener;
174
+ defaultOptions?: [{
175
+ dynamicOnly?: boolean;
176
+ }];
177
+ name: string;
178
+ };
179
+ "no-server-env-in-client": _typescript_eslint_utils_ts_eslint.RuleModule<"serverEnv", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
180
+ name: string;
181
+ };
58
182
  };
59
183
  configs: Record<string, ZappieFlatConfig[]>;
60
184
  };
61
185
 
62
- export { type ZappieFlatConfig, cartViaSdk, checkoutViaSdk, plugin as default, imagePriority, itemLinkPrefetch, noCustomAuth, noDirectApi, noLazyOverlay, noMoneyLiterals, noTrackingScripts, overlayPrimitive, useFormatMoney, verifyMarkers };
186
+ export { type ZappieFlatConfig, cartViaSdk, checkoutViaSdk, plugin as default, imagePriority, itemLinkPrefetch, noCustomAuth, noDirectApi, noEval, noExternalScripts, noLazyOverlay, noMoneyLiterals, noServerEnvInClient, noTrackingScripts, noUnlistedFetch, noUnsafeHtml, overlayPrimitive, safeExternalLinks, useFormatMoney, verifyMarkers };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import De from"@typescript-eslint/parser";import ae from"eslint-plugin-jsx-a11y";import Pe from"eslint-plugin-react-hooks";import{ESLintUtils as pe}from"@typescript-eslint/utils";var p=pe.RuleCreator(e=>`https://docs.zappie.ai/lint/${e}`);var ce=/^(cart|basket|bag)(Items|Lines|Count|Total)?$/i,me=/cart|basket|bag/i;function b(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function le(e){return e.type==="Identifier"?e.name:e.type==="ArrayPattern"&&e.elements[0]?.type==="Identifier"?e.elements[0].name:void 0}function ue(e){let r=b(e.callee)==="useReducer"?e.arguments[1]:e.arguments[0];return!!(r&&r.type!=="SpreadElement"&&(r.type==="ArrayExpression"||r.type==="Literal"&&typeof r.value=="number"))}var O=p({name:"cart-via-sdk",meta:{type:"problem",docs:{description:"Keep your cart state in the SDK."},schema:[],messages:{cart:"Use useCart(), useCartLine() or the SDK client cart. Keep cart state and storage in the SDK."}},defaultOptions:[],create(e){return{CallExpression(r){let i=b(r.callee),t=r.arguments[0];["getItem","setItem","removeItem","open"].includes(i??"")&&t?.type==="Literal"&&typeof t.value=="string"&&me.test(t.value)&&e.report({node:r,messageId:"cart"})},VariableDeclarator(r){if(r.init?.type!=="CallExpression")return;let i=le(r.id),t=b(r.init.callee);i&&ce.test(i)&&["useState","useReducer","createContext"].includes(t??"")&&ue(r.init)&&e.report({node:r,messageId:"cart"}),i&&/cart/i.test(i)&&["create","createStore","atom"].includes(t??"")&&e.report({node:r,messageId:"cart"})}}}});import{minimatch as fe}from"minimatch";var de=/(?:api|accounts|checkout|edge)\.zappie\.ai|\/storefront\/v1|\/customer\/graphql/i,ye=/googletagmanager\.com|google-analytics\.com|connect\.facebook\.net|analytics\.tiktok\.com|static\.hotjar\.com|cdn\.segment\.com|plausible\.io|clarity\.ms/i,F=/checkout\.paystack\.com|flutterwave|(?:^|\.)stripe\.com/i;function d(e){if(e?.type==="Literal"&&typeof e.value=="string")return e.value;if(e?.type==="TemplateLiteral")return e.quasis.map(r=>r.value.cooked??r.value.raw).join("")}function l(e,r){return e.attributes.find(i=>i.type==="JSXAttribute"&&i.name.type==="JSXIdentifier"&&i.name.name.toLowerCase()===r.toLowerCase())}function u(e){if(e?.value){if(e.value.type==="Literal")return typeof e.value.value=="string"?e.value.value:void 0;if(e.value.type==="JSXExpressionContainer")return d(e.value.expression)}}var R=e=>de.test(e),J=e=>ye.test(e),X=e=>e==="@zappie/sdk/internal"||e.startsWith("@zappie/sdk/internal/");function D(e,r){let i=e.replaceAll("\\","/");return r.some(t=>fe(i,t,{matchBase:!0}))}var Se=new Set(["cc-number","cc-exp","cc-csc","cc-name"]),P=/card.?number|cvv|cvc|expiry/i;function ge(e){return e.startsWith("@paystack/")||e==="react-paystack"||e==="flutterwave-react-v3"||e.startsWith("@stripe/")}var K=p({name:"checkout-via-sdk",meta:{type:"problem",docs:{description:"Open checkout through the Zappie SDK."},schema:[],messages:{checkout:"Use createCheckout().open() from @zappie/sdk/checkout. Let checkout collect payment details."}},defaultOptions:[],create(e){return{ImportDeclaration(r){ge(r.source.value)&&e.report({node:r.source,messageId:"checkout"})},JSXOpeningElement(r){let i=r.name.type==="JSXIdentifier"?r.name.name.toLowerCase():"",t=u(l(r,"autocomplete")),n=u(l(r,"name")),a=u(l(r,"id")),s=u(l(r,i==="iframe"?"src":"href")),o=i==="input"&&(t!==void 0&&Se.has(t.toLowerCase())||n!==void 0&&P.test(n)||a!==void 0&&P.test(a)),c=(i==="iframe"||i==="a")&&s!==void 0&&F.test(s);(o||c)&&e.report({node:r,messageId:"checkout"})}}}});function he(e){let r=e;for(;r?.parent;){let i=r.parent;if((i.type==="ArrowFunctionExpression"||i.type==="FunctionExpression")&&i.parent?.type==="CallExpression"&&i.parent.callee.type==="MemberExpression"&&i.parent.callee.property.type==="Identifier"&&i.parent.callee.property.name==="map")return i;r=i}}function Te(e,r){let i=e.getText(r.parent);if(/\.slice\(\s*0\s*,\s*1\s*\)\.map/.test(i))return!0;let t=r.params[1];return t?.type!=="Identifier"?!1:new RegExp(`${t.name}\\s*(?:={2,3}\\s*0|<\\s*1)`).test(e.getText(r))}var U=p({name:"image-priority",meta:{type:"problem",docs:{description:"Reserve priority for the first phone screen."},schema:[],messages:{mapped:"Remove priority from repeated images or limit it to the first.",routeLimit:"Keep no more than two priority images in a route."}},defaultOptions:[],create(e){let r=[];return{JSXOpeningElement(i){let t=l(i,"priority"),n=t?.value?.type==="JSXExpressionContainer"&&t.value.expression.type==="Literal"&&t.value.expression.value===!1;if(i.name.type!=="JSXIdentifier"||i.name.name!=="Image"||!t||n)return;r.push(i);let a=he(i);a&&!Te(e.sourceCode,a)&&e.report({node:t,messageId:"mapped"})},"Program:exit"(){if(/(?:^|\/)routes?\/[^/]+\.[jt]sx$/.test(e.filename.replaceAll("\\","/"))&&r.length>2)for(let i of r.slice(2))e.report({node:i,messageId:"routeLimit"})}}}});function Ee(e){let r=e?.value?.type==="JSXExpressionContainer"?e.value.expression:void 0;if(r?.type==="CallExpression"&&r.callee.type==="Identifier"&&r.callee.name==="itemHref")return!0;let i=u(e);return i!==void 0&&/^\/item(?:\/|$)/.test(i)}var $=p({name:"item-link-prefetch",meta:{type:"problem",docs:{description:"Prefetch item routes before shoppers tap."},schema:[],messages:{prefetch:'Add prefetch="viewport" so this item route is ready to tap.'}},defaultOptions:[],create(e){let r=new Set;return{ImportDeclaration(i){if(["react-router","react-router-dom"].includes(i.source.value))for(let t of i.specifiers)t.type==="ImportSpecifier"&&t.imported.type==="Identifier"&&["Link","NavLink"].includes(t.imported.name)&&r.add(t.local.name)},JSXOpeningElement(i){i.name.type==="JSXIdentifier"&&r.has(i.name.name)&&Ee(l(i,"to"))&&!l(i,"prefetch")&&e.report({node:i,messageId:"prefetch"})}}}});var xe=["**/*.server.*","**/server/**"],I=/otp|verify.?code|access.?token|refresh.?token|id.?token/i,B=new Set(["localStorage","sessionStorage"]),ve=new Set(["window","globalThis","self"]);function V(e){return e.type==="Identifier"?B.has(e.name):e.type==="MemberExpression"&&!e.computed&&e.object.type==="Identifier"&&ve.has(e.object.name)&&e.property.type==="Identifier"&&B.has(e.property.name)}function ke(e){return!e.computed&&e.property.type==="Identifier"?e.property.name:d(e.property)}function be(e){if(e==="jose"||e==="jsonwebtoken")return"server";if(e==="next-auth"||e.startsWith("next-auth/")||e.startsWith("@auth/")||e==="better-auth"||e.startsWith("better-auth/")||e==="lucia"||e.startsWith("@clerk/")||e==="firebase/auth"||e.startsWith("@supabase/auth-"))return"all"}var q=p({name:"no-custom-auth",meta:{type:"problem",docs:{description:"Sign customers in through the Zappie SDK."},schema:[{type:"object",properties:{serverFiles:{type:"array",items:{type:"string"},uniqueItems:!0}},additionalProperties:!1}],messages:{auth:"Use createCustomer().signIn() from @zappie/sdk/customer to sign customers in."}},defaultOptions:[{}],create(e,[r]){let i=D(e.filename,r.serverFiles??xe);return{ImportDeclaration(t){let n=be(t.source.value);(n==="all"||n==="server"&&!i)&&e.report({node:t.source,messageId:"auth"})},JSXOpeningElement(t){if(t.name.type!=="JSXIdentifier"||t.name.name.toLowerCase()!=="input")return;let n=u(l(t,"type")),a=u(l(t,"autocomplete"));(n?.toLowerCase()==="password"||a!==void 0&&["one-time-code","current-password","new-password"].includes(a.toLowerCase()))&&e.report({node:t,messageId:"auth"})},CallExpression(t){if(t.callee.type!=="MemberExpression"||!V(t.callee.object))return;let n=t.arguments[0],a=n?.type==="Identifier"?n.name:d(n??null);a&&I.test(a)&&e.report({node:t,messageId:"auth"})},AssignmentExpression(t){if(t.left.type==="MemberExpression"&&V(t.left.object)){let s=ke(t.left);s&&I.test(s)&&e.report({node:t,messageId:"auth"});return}let n=t.left.type==="MemberExpression"&&t.left.object.type==="Identifier"&&t.left.object.name==="document"&&t.left.property.type==="Identifier"&&t.left.property.name==="cookie",a=d(t.right);n&&a&&I.test(a)&&e.report({node:t,messageId:"auth"})}}}});function Ie(e){return/checkout\.zappie\.ai|\/checkout(?:\/|$)/i.test(e)?"checkout":/accounts\.zappie\.ai|\/customer\/graphql/i.test(e)?"customer":/\/storefront\/v1/i.test(e)?"storefront":"direct"}var Z=p({name:"no-direct-api",meta:{type:"problem",docs:{description:"Call Zappie through the SDK."},schema:[],messages:{direct:"Call the matching public @zappie/sdk client to reach this Zappie endpoint.",internal:"Use a public SDK entry. Only Zappie-owned packages can import @zappie/sdk/internal.",storefront:"Use createZappie(config).store or .menu to read storefront data.",checkout:"Use createCheckout().open() from @zappie/sdk/checkout.",customer:"Use createCustomer() from @zappie/sdk/customer."}},defaultOptions:[],create(e){let r=i=>{if(i.parent.type==="ImportDeclaration")return;let t=d(i);t&&R(t)&&e.report({node:i,messageId:Ie(t)})};return{Literal:r,TemplateLiteral:r,ImportDeclaration(i){X(i.source.value)&&e.report({node:i.source,messageId:"internal"})}}}});var Le=new Set(["react"]),we="React",W=p({name:"no-lazy-overlay",meta:{type:"problem",docs:{description:"Load overlays with deferred() from @zappie/foundations, not React.lazy or Suspense."},schema:[],messages:{lazy:"Load this component with deferred() from @zappie/foundations. React.lazy suspends, and React holds a suspended reveal back for about 300 ms, so the overlay opens late.",suspense:"Remove Suspense and load the component with deferred() from @zappie/foundations. It renders as soon as its code arrives and never suspends."}},defaultOptions:[],create(e){let r=new Set,i=new Set,t=new Set([we]),n=(a,s,o)=>a.type==="Identifier"&&t.has(a.name)&&s.type==="Identifier"&&s.name===o;return{ImportDeclaration(a){if(Le.has(a.source.value))for(let s of a.specifiers){if(s.type!=="ImportSpecifier"){t.add(s.local.name);continue}let o=s.imported.type==="Identifier"?s.imported.name:s.imported.value;o==="lazy"?r.add(s.local.name):o==="Suspense"&&i.add(s.local.name)}},CallExpression(a){let s=a.callee;(s.type==="Identifier"&&r.has(s.name)||s.type==="MemberExpression"&&!s.computed&&n(s.object,s.property,"lazy"))&&e.report({node:a,messageId:"lazy"})},JSXOpeningElement(a){let s=a.name;(s.type==="JSXIdentifier"&&i.has(s.name)||s.type==="JSXMemberExpression"&&s.object.type==="JSXIdentifier"&&t.has(s.object.name)&&s.property.name==="Suspense")&&e.report({node:a,messageId:"suspense"})}}}});import{ESLintUtils as Ce}from"@typescript-eslint/utils";var ze=/[₦€£]\s?\d|\$\s?\d|NGN\s?\d|\d+(?:\.\d{2})?\s?(?:naira|NGN)/i;function g(e){return e.trim()!=="$1"&&ze.test(e)}function f(e){if(e.type==="MemberExpression"){if(!e.computed&&e.property.type==="Identifier")return e.property.name;if(e.computed&&e.property.type==="Literal")return typeof e.property.value=="string"?e.property.value:void 0}}var S=e=>f(e)==="amountMinor";function Me(e){return e.type==="MemberExpression"&&S(e)?e.object:void 0}function y(e){if(!(e.type!=="BinaryExpression"||e.operator!=="/"||e.right.type!=="Literal"||e.right.value!==100))return Me(e.left)}function h(e){try{return Ce.getParserServices(e,!0)}catch{return}}function Ne(e){return e.getName().includes("MoneyBrand")?(e.getDeclarations()??[]).some(r=>{let i=r.getSourceFile().fileName.replaceAll("\\","/");return i.endsWith("/packages/sdk/src/money.ts")||/\/node_modules\/@zappie\/sdk\/.*\.d\.ts$/.test(i)}):!1}function T(e,r){if(!e?.program)return!1;let i=e.program.getTypeChecker(),t=e.esTreeNodeToTSNodeMap.get(r),n=i.getTypeAtLocation(t);return(n.isUnionOrIntersection()?n.types:[n]).some(s=>s.getProperties().some(o=>Ne(o)))}function E(e){return e?.type!=="ObjectExpression"?!1:e.properties.some(r=>r.type==="Property"&&!r.computed&&(r.key.type==="Identifier"&&r.key.name==="style"||r.key.type==="Literal"&&r.key.value==="style")&&r.value.type==="Literal"&&r.value.value==="currency")}var _=p({name:"no-money-literals",meta:{type:"problem",docs:{description:"Use SDK prices and totals in your storefront."},schema:[],messages:{arithmetic:"Use cart.subtotal, line.lineSubtotal, quote.subtotal, quote.lines totals or quote.total. Let the SDK calculate amounts.",literal:"Render SDK Money with format.money() or <Money> instead of a hard-coded amount.",scale:"Use format.money() to display amountMinor. Currencies use 0, 2, 3 or 4 minor-unit digits."}},defaultOptions:[],create(e){let r=h(e),i=t=>S(t)||T(r,t);return{Literal(t){typeof t.value=="string"&&t.parent.type!=="ImportDeclaration"&&g(t.value)&&e.report({node:t,messageId:"literal"})},JSXText(t){g(t.value)&&e.report({node:t,messageId:"literal"})},BinaryExpression(t){["+","-","*","/"].includes(t.operator)&&(y(t)&&t.parent.type==="MemberExpression"&&t.parent.object===t&&f(t.parent)==="toFixed"||(i(t.left)||i(t.right))&&e.report({node:t,messageId:"arithmetic"}))},TemplateLiteral(t){for(let n of t.quasis)g(n.value.cooked??n.value.raw)&&e.report({node:n,messageId:"literal"})},CallExpression(t){t.callee.type==="MemberExpression"&&f(t.callee)==="toFixed"&&t.arguments[0]?.type==="Literal"&&t.arguments[0].value===2&&y(t.callee.object)&&e.report({node:t,messageId:"scale"})}}}});var je=["react-ga4","react-facebook-pixel","@vercel/analytics","posthog-js"];function Ae(e){return je.includes(e)||e.startsWith("@segment/")}var H=p({name:"no-tracking-scripts",meta:{type:"problem",docs:{description:"Forward analytics through Zappie."},schema:[],messages:{tracking:"Configure analytics.forward in zappie.config.ts to forward analytics. Remove the tracking script."}},defaultOptions:[],create(e){let r=i=>{if(i.parent.type==="ImportDeclaration")return;let t=d(i);t&&J(t)&&e.report({node:i,messageId:"tracking"})};return{Literal:r,TemplateLiteral:r,ImportDeclaration(i){Ae(i.source.value)&&e.report({node:i.source,messageId:"tracking"})},CallExpression(i){let t=i.callee,n=t.type==="Identifier"&&["gtag","fbq"].includes(t.name),a=t.type==="MemberExpression"&&!t.computed&&t.object.type==="Identifier"&&t.object.name==="dataLayer"&&t.property.type==="Identifier"&&t.property.name==="push";(n||a)&&e.report({node:i,messageId:"tracking"})}}}});var Oe=["modal","sheet","drawer","dialog","item","cart","cart_sheet","cart_edit","cart_clear","search","catalogue_search","catalogue_categories","product_note","modal_id"];function L(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function G(e,r){return e.type==="Literal"&&typeof e.value=="string"?[...r].some(i=>new RegExp(`[?&]${i}=`).test(e.value)):e.type==="TemplateLiteral"?e.quasis.some(i=>G({...i,type:"Literal",value:i.value.raw},r)):e.type==="ObjectExpression"?e.properties.some(i=>i.type==="Property"&&(i.key.type==="Identifier"&&r.has(i.key.name)||i.key.type==="Literal"&&typeof i.key.value=="string"&&r.has(i.key.value))):!1}var Q=p({name:"overlay-primitive",meta:{type:"problem",docs:{description:"Use Overlay to keep dialogs and sheets in the URL."},schema:[{type:"object",properties:{keys:{type:"array",items:{type:"string"}}},additionalProperties:!1}],messages:{overlay:"Use Overlay or useOverlay for dialogs and sheets in the URL. They handle Back, Forward and focus restoration."}},defaultOptions:[{}],create(e,[r]){let i=new Set([...Oe,...r.keys??[]]),t=new Set,n=a=>{let s=e.sourceCode.getText(a);return s.includes("useSearchParams(")||[...t].some(o=>new RegExp(`\\b${o}\\b`).test(s))};return{VariableDeclarator(a){a.init?.type==="CallExpression"&&L(a.init.callee)==="useSearchParams"?a.id.type==="Identifier"?t.add(a.id.name):a.id.type==="ArrayPattern"&&a.id.elements[0]?.type==="Identifier"&&t.add(a.id.elements[0].name):a.id.type==="Identifier"&&a.init&&n(a.init)&&t.add(a.id.name)},CallExpression(a){let s=L(a.callee);(s==="setSearchParams"||s==="navigate"||s==="pushState"||s==="replaceState"||a.callee.type==="CallExpression"&&L(a.callee.callee)==="useNavigate")&&a.arguments.some(c=>c.type!=="SpreadElement"&&G(c,i))&&e.report({node:a,messageId:"overlay"})},JSXOpeningElement(a){let s=a.name.type==="JSXIdentifier"&&a.name.name==="dialog",o=a.attributes.some(m=>m.type==="JSXAttribute"&&m.name.name==="role"&&m.value?.type==="Literal"&&m.value.value==="dialog"),c=a.attributes.find(m=>m.type==="JSXAttribute"&&m.name.name==="open");(s||o)&&c?.type==="JSXAttribute"&&c.value?.type==="JSXExpressionContainer"&&n(c.value.expression)&&e.report({node:a,messageId:"overlay"})}}}});function w(e){return e.body.find(r=>r.type==="ImportDeclaration"&&r.source.value==="@zappie/sdk")}function C(e){return e?.specifiers.find(r=>r.type==="ImportSpecifier"&&r.imported.type==="Identifier"&&r.imported.name==="format")}function Fe(e,r,i,t){let n=w(i);if(C(n)?.local.name===t)return;let a=t==="format"?"format":`format as ${t}`,s=n?.specifiers.filter(m=>m.type==="ImportSpecifier");if(n&&n.importKind!=="type"&&s?.length){let m=s.at(-1);if(m)return e.insertTextAfter(m,`, ${a}`)}let o=i.body[0],c=`import { ${a} } from "@zappie/sdk";
2
- `;return o?e.insertTextBefore(o,c):e.insertTextAfter(i,c)}function z(e,r,i,t,n,a){let s=[e.replaceText(t,`${a}.money(${r.getText(n)})`)],o=Fe(e,r,i,a);return o&&s.push(o),s}function Y(e,r,i){if(C(w(r)))return"imported";let t=e.getScope(i);for(;t;){if(t.variables.some(n=>n.name==="format"))return"conflict";t=t.upper}return"available"}function ee(e){return C(w(e))?.local.name}function te(e,r){let i=new Set,t=e.getScope(r);for(;t;){for(let n of t.variables)i.add(n.name);t=t.upper}for(let n of["zappieFormat","zappieMoneyFormat","formatMoneyValue"])if(!i.has(n))return n;return"zappieSdkFormat"}function re(e){let r=e.type==="NewExpression"||e.type==="CallExpression"?e.callee:void 0;return r?.type==="MemberExpression"&&r.object.type==="Identifier"&&r.object.name==="Intl"&&f(r)==="NumberFormat"}function Re(e){if(e.callee.type==="MemberExpression"){if(f(e.callee)==="toLocaleString")return y(e.callee.object);if(f(e.callee)==="format")return y(e.arguments[0]??e)}}function Je(e){let r=e.parent,i=r.type==="MemberExpression"?r.parent:void 0;return!!(i?.type==="CallExpression"&&i.callee===r&&f(r)==="format"&&y(i.arguments[0]??i))}var ie=p({name:"use-format-money",meta:{type:"problem",docs:{description:"Format SDK Money with format.money() or <Money>."},fixable:"code",hasSuggestions:!0,schema:[],messages:{currency:"Use format.money(). Intl currency defaults can differ from the API minor-unit digits.",direct:"Render SDK Money with format.money(value) or <Money value={value}>.",minor:"Use format.money(value). Dividing by 100 gives the wrong amount for currencies with 0, 3 or 4 minor-unit digits.",useAlias:"Import format with an unused alias and format this Money value."}},defaultOptions:[],create(e){let r=e.sourceCode,i=h(e),t,n=(s,o,c)=>{let m={node:s,messageId:"minor"};if(Y(r,t,s)==="conflict"){let v=te(r,s);e.report({...m,suggest:[{messageId:"useAlias",fix:k=>z(k,r,t,o,c,v)}]});return}e.report({...m,fix:v=>{let k=ee(t)??"format";return z(v,r,t,o,c,k)}})},a=s=>{let o=Re(s);return o?(n(s,s,o),!0):!1};return{Program(s){t=s},NewExpression(s){re(s)&&E(s.arguments[1])&&!Je(s)&&e.report({node:s,messageId:"currency"})},CallExpression(s){if(!a(s)){if(re(s)&&E(s.arguments[1])){e.report({node:s,messageId:"currency"});return}s.callee.type==="MemberExpression"&&f(s.callee)==="toLocaleString"&&s.arguments.some(E)&&e.report({node:s,messageId:"currency"})}},TemplateLiteral(s){for(let o of s.expressions){let c=y(o);c?n(o,o,c):S(o)&&e.report({node:o,messageId:"minor"})}},JSXExpressionContainer(s){s.parent.type==="JSXAttribute"||s.expression.type==="JSXEmptyExpression"||(y(s.expression)||S(s.expression)?e.report({node:s.expression,messageId:"minor"}):T(i,s.expression)&&e.report({node:s.expression,messageId:"direct"}))}}}});var M={add:{name:"data-zappie-action",text:'data-zappie-action="add-to-cart"'},checkout:{name:"data-zappie-checkout",text:"data-zappie-checkout"},signin:{name:"data-zappie-signin",text:"data-zappie-signin"}};function Xe(e,r){let i=r.value?e.getText(r.value):"";if(/\b(?:cart(?:\.actions)?\.add)\s*\(/.test(i))return"add";if(/\b(?:checkout|createCheckout\([^)]*\))\.open\s*\(/.test(i))return"checkout";if(/\b(?:auth|createCustomer\([^)]*\))\.signIn\s*\(/.test(i))return"signin"}var se=p({name:"verify-markers",meta:{type:"suggestion",docs:{description:"Mark shopping actions so verify can find them."},fixable:"code",schema:[],messages:{marker:"Add {{marker}} so zappie verify can find this shopping action."}},defaultOptions:[],create(e){return{JSXOpeningElement(r){let i=r.attributes.find(n=>n.type==="JSXAttribute"&&n.name.name==="onClick");if(!i)return;let t=Xe(e.sourceCode,i);!t||r.attributes.some(n=>n.type==="JSXAttribute"&&n.name.name===M[t].name)||e.report({node:i,messageId:"marker",data:{marker:M[t].text},fix:n=>n.insertTextAfter(r.name,` ${M[t].text}`)})}}}});var ne={"no-direct-api":Z,"no-tracking-scripts":H,"checkout-via-sdk":K,"no-custom-auth":q,"no-money-literals":_,"use-format-money":ie,"overlay-primitive":Q,"cart-via-sdk":O,"verify-markers":se,"item-link-prefetch":$,"image-priority":U,"no-lazy-overlay":W},oe=Object.fromEntries(Object.keys(ne).map(e=>[`zappie/${e}`,e==="verify-markers"?"warn":"error"])),Ke={...oe,"zappie/verify-markers":"error"},A={meta:{name:"eslint-plugin-zappie",version:"0.0.0"},rules:ne,configs:{}},x={name:"zappie/rules",files:["**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"],languageOptions:{parser:De},plugins:{zappie:A,"jsx-a11y":ae,"react-hooks":Pe},rules:{...ae.flatConfigs.recommended.rules,"react-hooks/rules-of-hooks":"error",...oe}},N={name:"zappie/search-dialogs",files:["**/search/**/*.{jsx,tsx}","**/*search*.{jsx,tsx}"],rules:{"jsx-a11y/no-autofocus":"off"}},j={name:"zappie/typed",files:["**/*.{ts,tsx,mts,cts}"],languageOptions:{parserOptions:{projectService:!0}}};A.configs={recommended:[x,j,N],strict:[{...x,rules:{...x.rules,...Ke}},j,N],"zappie-internal":[x,j,N,{name:"zappie/internal-sdk",files:["packages/sdk/src/**/*.{ts,tsx}"],rules:{"zappie/no-direct-api":"off"}},{name:"zappie/internal-auth-ui",files:["packages/foundations/src/otp/**/*.{ts,tsx}"],rules:{"zappie/no-custom-auth":"off"}},{name:"zappie/internal-money",files:["packages/sdk/src/money.ts","packages/sdk/src/internal/money-math.ts","packages/sdk/src/cart/branch.ts","packages/sdk/src/pricing/index.ts"],rules:{"zappie/no-money-literals":"off","zappie/use-format-money":"off"}},{name:"zappie/internal-overlay",files:["packages/foundations/src/overlay/**/*.{ts,tsx}"],rules:{"zappie/overlay-primitive":"off"}},{name:"zappie/internal-overlay-tests",files:["themes/base/src/cart/cart-review.test.tsx","themes/base/src/cart/core-cart.test.tsx","themes/base/src/cart/fulfilment.test.tsx","themes/base/src/cart/theme-class.test.tsx"],rules:{"zappie/overlay-primitive":"off"}}]};var Bt=A;export{O as cartViaSdk,K as checkoutViaSdk,Bt as default,U as imagePriority,$ as itemLinkPrefetch,q as noCustomAuth,Z as noDirectApi,W as noLazyOverlay,_ as noMoneyLiterals,H as noTrackingScripts,Q as overlayPrimitive,ie as useFormatMoney,se as verifyMarkers};
1
+ import St from"@typescript-eslint/parser";import Ce from"eslint-plugin-jsx-a11y";import gt from"eslint-plugin-react-hooks";import{ESLintUtils as Ne}from"@typescript-eslint/utils";var m=Ne.RuleCreator(e=>`https://docs.zappie.ai/lint/${e}`);var ze=/^(cart|basket|bag)(Items|Lines|Count|Total)?$/i,Re=/cart|basket|bag/i;function F(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function Ae(e){return e.type==="Identifier"?e.name:e.type==="ArrayPattern"&&e.elements[0]?.type==="Identifier"?e.elements[0].name:void 0}function Oe(e){let t=F(e.callee)==="useReducer"?e.arguments[1]:e.arguments[0];return!!(t&&t.type!=="SpreadElement"&&(t.type==="ArrayExpression"||t.type==="Literal"&&typeof t.value=="number"))}var Z=m({name:"cart-via-sdk",meta:{type:"problem",docs:{description:"Keep your cart state in the SDK."},schema:[],messages:{cart:"Use useCart(), useCartLine() or the SDK client cart. Keep cart state and storage in the SDK."}},defaultOptions:[],create(e){return{CallExpression(t){let s=F(t.callee),r=t.arguments[0];["getItem","setItem","removeItem","open"].includes(s??"")&&r?.type==="Literal"&&typeof r.value=="string"&&Re.test(r.value)&&e.report({node:t,messageId:"cart"})},VariableDeclarator(t){if(t.init?.type!=="CallExpression")return;let s=Ae(t.id),r=F(t.init.callee);s&&ze.test(s)&&["useState","useReducer","createContext"].includes(r??"")&&Oe(t.init)&&e.report({node:t,messageId:"cart"}),s&&/cart/i.test(s)&&["create","createStore","atom"].includes(r??"")&&e.report({node:t,messageId:"cart"})}}}});import{minimatch as Fe}from"minimatch";var Pe=/(?:api|accounts|checkout|edge)\.zappie\.ai|\/storefront\/v1|\/customer\/graphql/i,Xe=/googletagmanager\.com|google-analytics\.com|connect\.facebook\.net|analytics\.tiktok\.com|static\.hotjar\.com|cdn\.segment\.com|plausible\.io|clarity\.ms/i,G=/checkout\.paystack\.com|flutterwave|(?:^|\.)stripe\.com/i;function u(e){if(e?.type==="Literal"&&typeof e.value=="string")return e.value;if(e?.type==="TemplateLiteral")return e.quasis.map(t=>t.value.cooked??t.value.raw).join("")}function y(e){return!e.computed&&e.property.type==="Identifier"?e.property.name:u(e.property)}function h(e){return e.type==="TSAsExpression"||e.type==="TSTypeAssertion"||e.type==="TSNonNullExpression"?h(e.expression):e}function b(e,t,s){for(let r=e.getScope(t);r;r=r.upper){let c=r.set.get(s);if(c)return c}}function S(e,t){return e.attributes.find(s=>s.type==="JSXAttribute"&&s.name.type==="JSXIdentifier"&&s.name.name.toLowerCase()===t.toLowerCase())}function g(e){if(e?.value){if(e.value.type==="Literal")return typeof e.value.value=="string"?e.value.value:void 0;if(e.value.type==="JSXExpressionContainer")return u(e.value.expression)}}var M=e=>Pe.test(e),L=e=>Xe.test(e),Q=e=>e==="@zappie/sdk/internal"||e.startsWith("@zappie/sdk/internal/");function I(e,t){let s=e.replaceAll("\\","/");return t.some(r=>Fe(s,r,{matchBase:!0}))}var Je=new Set(["cc-number","cc-exp","cc-csc","cc-name"]),Y=/card.?number|cvv|cvc|expiry/i;function De(e){return e.startsWith("@paystack/")||e==="react-paystack"||e==="flutterwave-react-v3"||e.startsWith("@stripe/")}var ee=m({name:"checkout-via-sdk",meta:{type:"problem",docs:{description:"Open checkout through the Zappie SDK."},schema:[],messages:{checkout:"Use createCheckout().open() from @zappie/sdk/checkout. Let checkout collect payment details."}},defaultOptions:[],create(e){return{ImportDeclaration(t){De(t.source.value)&&e.report({node:t.source,messageId:"checkout"})},JSXOpeningElement(t){let s=t.name.type==="JSXIdentifier"?t.name.name.toLowerCase():"",r=g(S(t,"autocomplete")),c=g(S(t,"name")),p=g(S(t,"id")),i=g(S(t,s==="iframe"?"src":"href")),o=s==="input"&&(r!==void 0&&Je.has(r.toLowerCase())||c!==void 0&&Y.test(c)||p!==void 0&&Y.test(p)),n=(s==="iframe"||s==="a")&&i!==void 0&&G.test(i);(o||n)&&e.report({node:t,messageId:"checkout"})}}}});function Ue(e){let t=e;for(;t?.parent;){let s=t.parent;if((s.type==="ArrowFunctionExpression"||s.type==="FunctionExpression")&&s.parent?.type==="CallExpression"&&s.parent.callee.type==="MemberExpression"&&s.parent.callee.property.type==="Identifier"&&s.parent.callee.property.name==="map")return s;t=s}}function _e(e,t){let s=e.getText(t.parent);if(/\.slice\(\s*0\s*,\s*1\s*\)\.map/.test(s))return!0;let r=t.params[1];return r?.type!=="Identifier"?!1:new RegExp(`${r.name}\\s*(?:={2,3}\\s*0|<\\s*1)`).test(e.getText(t))}var te=m({name:"image-priority",meta:{type:"problem",docs:{description:"Reserve priority for the first phone screen."},schema:[],messages:{mapped:"Remove priority from repeated images or limit it to the first.",routeLimit:"Keep no more than two priority images in a route."}},defaultOptions:[],create(e){let t=[];return{JSXOpeningElement(s){let r=S(s,"priority"),c=r?.value?.type==="JSXExpressionContainer"&&r.value.expression.type==="Literal"&&r.value.expression.value===!1;if(s.name.type!=="JSXIdentifier"||s.name.name!=="Image"||!r||c)return;t.push(s);let p=Ue(s);p&&!_e(e.sourceCode,p)&&e.report({node:r,messageId:"mapped"})},"Program:exit"(){if(/(?:^|\/)routes?\/[^/]+\.[jt]sx$/.test(e.filename.replaceAll("\\","/"))&&t.length>2)for(let s of t.slice(2))e.report({node:s,messageId:"routeLimit"})}}}});function Be(e){let t=e?.value?.type==="JSXExpressionContainer"?e.value.expression:void 0;if(t?.type==="CallExpression"&&t.callee.type==="Identifier"&&t.callee.name==="itemHref")return!0;let s=g(e);return s!==void 0&&/^\/item(?:\/|$)/.test(s)}var re=m({name:"item-link-prefetch",meta:{type:"problem",docs:{description:"Prefetch item routes before shoppers tap."},schema:[],messages:{prefetch:'Add prefetch="viewport" so this item route is ready to tap.'}},defaultOptions:[],create(e){let t=new Set;return{ImportDeclaration(s){if(["react-router","react-router-dom"].includes(s.source.value))for(let r of s.specifiers)r.type==="ImportSpecifier"&&r.imported.type==="Identifier"&&["Link","NavLink"].includes(r.imported.name)&&t.add(r.local.name)},JSXOpeningElement(s){s.name.type==="JSXIdentifier"&&t.has(s.name.name)&&Be(S(s,"to"))&&!S(s,"prefetch")&&e.report({node:s,messageId:"prefetch"})}}}});var Ve=["**/*.server.*","**/server/**"],P=/otp|verify.?code|access.?token|refresh.?token|id.?token/i,ie=new Set(["localStorage","sessionStorage"]),qe=new Set(["window","globalThis","self"]);function se(e){return e.type==="Identifier"?ie.has(e.name):e.type==="MemberExpression"&&!e.computed&&e.object.type==="Identifier"&&qe.has(e.object.name)&&e.property.type==="Identifier"&&ie.has(e.property.name)}function Ke(e){return!e.computed&&e.property.type==="Identifier"?e.property.name:u(e.property)}function He(e){if(e==="jose"||e==="jsonwebtoken")return"server";if(e==="next-auth"||e.startsWith("next-auth/")||e.startsWith("@auth/")||e==="better-auth"||e.startsWith("better-auth/")||e==="lucia"||e.startsWith("@clerk/")||e==="firebase/auth"||e.startsWith("@supabase/auth-"))return"all"}var ne=m({name:"no-custom-auth",meta:{type:"problem",docs:{description:"Sign customers in through the Zappie SDK."},schema:[{type:"object",properties:{serverFiles:{type:"array",items:{type:"string"},uniqueItems:!0}},additionalProperties:!1}],messages:{auth:"Use createCustomer().signIn() from @zappie/sdk/customer to sign customers in."}},defaultOptions:[{}],create(e,[t]){let s=I(e.filename,t.serverFiles??Ve);return{ImportDeclaration(r){let c=He(r.source.value);(c==="all"||c==="server"&&!s)&&e.report({node:r.source,messageId:"auth"})},JSXOpeningElement(r){if(r.name.type!=="JSXIdentifier"||r.name.name.toLowerCase()!=="input")return;let c=g(S(r,"type")),p=g(S(r,"autocomplete"));(c?.toLowerCase()==="password"||p!==void 0&&["one-time-code","current-password","new-password"].includes(p.toLowerCase()))&&e.report({node:r,messageId:"auth"})},CallExpression(r){if(r.callee.type!=="MemberExpression"||!se(r.callee.object))return;let c=r.arguments[0],p=c?.type==="Identifier"?c.name:u(c??null);p&&P.test(p)&&e.report({node:r,messageId:"auth"})},AssignmentExpression(r){if(r.left.type==="MemberExpression"&&se(r.left.object)){let i=Ke(r.left);i&&P.test(i)&&e.report({node:r,messageId:"auth"});return}let c=r.left.type==="MemberExpression"&&r.left.object.type==="Identifier"&&r.left.object.name==="document"&&r.left.property.type==="Identifier"&&r.left.property.name==="cookie",p=u(r.right);c&&p&&P.test(p)&&e.report({node:r,messageId:"auth"})}}}});function $e(e){return/checkout\.zappie\.ai|\/checkout(?:\/|$)/i.test(e)?"checkout":/accounts\.zappie\.ai|\/customer\/graphql/i.test(e)?"customer":/\/storefront\/v1/i.test(e)?"storefront":"direct"}var ae=m({name:"no-direct-api",meta:{type:"problem",docs:{description:"Call Zappie through the SDK."},schema:[],messages:{direct:"Call the matching public @zappie/sdk client to reach this Zappie endpoint.",internal:"Use a public SDK entry. Only Zappie-owned packages can import @zappie/sdk/internal.",storefront:"Use createZappie(config).store or .menu to read storefront data.",checkout:"Use createCheckout().open() from @zappie/sdk/checkout.",customer:"Use createCustomer() from @zappie/sdk/customer."}},defaultOptions:[],create(e){let t=s=>{if(s.parent.type==="ImportDeclaration")return;let r=u(s);r&&M(r)&&e.report({node:s,messageId:$e(r)})};return{Literal:t,TemplateLiteral:t,ImportDeclaration(s){Q(s.source.value)&&e.report({node:s.source,messageId:"internal"})}}}});function X(e){if(e.type==="Identifier")return e.name;if(e.type==="SequenceExpression"){let t=e.expressions.at(-1);return t?X(t):void 0}if(e.type==="MemberExpression"&&e.object.type==="Identifier"&&["window","globalThis"].includes(e.object.name))return!e.computed&&e.property.type==="Identifier"?e.property.name:u(e.property)}var oe=m({name:"no-eval",meta:{type:"problem",docs:{description:"Do not execute strings as code."},schema:[],messages:{eval:"Remove code generated from a string. Ship executable code in the storefront bundle."}},defaultOptions:[],create(e){return{CallExpression(t){let s=X(t.callee);if(s==="eval"||s==="Function"){e.report({node:t,messageId:"eval"});return}["setTimeout","setInterval"].includes(s??"")&&u(t.arguments[0]??null)!==void 0&&e.report({node:t,messageId:"eval"})},NewExpression(t){X(t.callee)==="Function"&&e.report({node:t,messageId:"eval"})}}}});var pe=e=>!!(e&&/^(?:[a-z][a-z\d+.-]*:)?\/\//i.test(e)),We=e=>e.type==="Identifier"&&e.name==="document"||e.type==="MemberExpression"&&e.object.type==="Identifier"&&["window","globalThis"].includes(e.object.name)&&(!e.computed&&e.property.type==="Identifier"&&e.property.name==="document"||u(e.property)==="document"),ce=m({name:"no-external-scripts",meta:{type:"problem",docs:{description:"Ship scripts in the storefront bundle."},schema:[],messages:{externalScript:"Remove the external script. Install reviewed code as a package so it ships in the storefront bundle."}},defaultOptions:[],create(e){return{JSXOpeningElement(t){if(t.name.type!=="JSXIdentifier")return;let s=t.name.name.toLowerCase();if(s==="script"){let i=S(t,"src"),o=g(i),n=i?.value?.type==="JSXExpressionContainer"&&!(i.value.expression.type==="Literal"||i.value.expression.type==="TemplateLiteral"&&i.value.expression.expressions.length===0);i&&(n||o===void 0||pe(o)&&!L(o))&&e.report({node:t,messageId:"externalScript"});return}if(s!=="link")return;let r=g(S(t,"rel"))?.toLowerCase(),c=g(S(t,"as"))?.toLowerCase(),p=g(S(t,"href"));(r==="preload"&&c==="script"||r==="modulepreload")&&pe(p)&&!L(p??"")&&e.report({node:t,messageId:"externalScript"})},CallExpression(t){if(t.callee.type==="MemberExpression"&&!t.callee.computed&&We(t.callee.object)&&t.callee.property.type==="Identifier"&&t.callee.property.name==="createElement"&&u(t.arguments[0]??null)?.toLowerCase()==="script"){e.report({node:t,messageId:"externalScript"});return}t.callee.type==="MemberExpression"&&!t.callee.computed&&t.callee.object.type==="Identifier"&&t.callee.object.name==="React"&&t.callee.property.type==="Identifier"&&t.callee.property.name==="createElement"&&u(t.arguments[0]??null)?.toLowerCase()==="script"&&t.arguments[1]?.type==="ObjectExpression"&&t.arguments[1].properties.find(c=>c.type==="Property"&&(c.key.type==="Identifier"&&c.key.name==="src"||u(c.key)==="src"))&&e.report({node:t,messageId:"externalScript"})}}}});var Ze=new Set(["react"]),Ge="React",le=m({name:"no-lazy-overlay",meta:{type:"problem",docs:{description:"Load overlays with deferred() from @zappie/foundations, not React.lazy or Suspense."},schema:[],messages:{lazy:"Load this component with deferred() from @zappie/foundations. React.lazy suspends, and React holds a suspended reveal back for about 300 ms, so the overlay opens late.",suspense:"Remove Suspense and load the component with deferred() from @zappie/foundations. It renders as soon as its code arrives and never suspends."}},defaultOptions:[],create(e){let t=new Set,s=new Set,r=new Set([Ge]),c=(p,i,o)=>p.type==="Identifier"&&r.has(p.name)&&i.type==="Identifier"&&i.name===o;return{ImportDeclaration(p){if(Ze.has(p.source.value))for(let i of p.specifiers){if(i.type!=="ImportSpecifier"){r.add(i.local.name);continue}let o=i.imported.type==="Identifier"?i.imported.name:i.imported.value;o==="lazy"?t.add(i.local.name):o==="Suspense"&&s.add(i.local.name)}},CallExpression(p){let i=p.callee;(i.type==="Identifier"&&t.has(i.name)||i.type==="MemberExpression"&&!i.computed&&c(i.object,i.property,"lazy"))&&e.report({node:p,messageId:"lazy"})},JSXOpeningElement(p){let i=p.name;(i.type==="JSXIdentifier"&&s.has(i.name)||i.type==="JSXMemberExpression"&&i.object.type==="JSXIdentifier"&&r.has(i.object.name)&&i.property.name==="Suspense")&&e.report({node:p,messageId:"suspense"})}}}});import{ESLintUtils as Qe}from"@typescript-eslint/utils";var Ye=/[₦€£]\s?\d|\$\s?\d|NGN\s?\d|\d+(?:\.\d{2})?\s?(?:naira|NGN)/i;function N(e){return e.trim()!=="$1"&&Ye.test(e)}function T(e){if(e.type==="MemberExpression"){if(!e.computed&&e.property.type==="Identifier")return e.property.name;if(e.computed&&e.property.type==="Literal")return typeof e.property.value=="string"?e.property.value:void 0}}var k=e=>T(e)==="amountMinor";function et(e){return e.type==="MemberExpression"&&k(e)?e.object:void 0}function x(e){if(!(e.type!=="BinaryExpression"||e.operator!=="/"||e.right.type!=="Literal"||e.right.value!==100))return et(e.left)}function z(e){try{return Qe.getParserServices(e,!0)}catch{return}}function tt(e){return e.getName().includes("MoneyBrand")?(e.getDeclarations()??[]).some(t=>{let s=t.getSourceFile().fileName.replaceAll("\\","/");return s.endsWith("/packages/sdk/src/money.ts")||/\/node_modules\/@zappie\/sdk\/.*\.d\.ts$/.test(s)}):!1}function R(e,t){if(!e?.program)return!1;let s=e.program.getTypeChecker(),r=e.esTreeNodeToTSNodeMap.get(t),c=s.getTypeAtLocation(r);return(c.isUnionOrIntersection()?c.types:[c]).some(i=>i.getProperties().some(o=>tt(o)))}function A(e){return e?.type!=="ObjectExpression"?!1:e.properties.some(t=>t.type==="Property"&&!t.computed&&(t.key.type==="Identifier"&&t.key.name==="style"||t.key.type==="Literal"&&t.key.value==="style")&&t.value.type==="Literal"&&t.value.value==="currency")}var me=m({name:"no-money-literals",meta:{type:"problem",docs:{description:"Use SDK prices and totals in your storefront."},schema:[],messages:{arithmetic:"Use cart.subtotal, line.lineSubtotal, quote.subtotal, quote.lines totals or quote.total. Let the SDK calculate amounts.",literal:"Render SDK Money with format.money() or <Money> instead of a hard-coded amount.",scale:"Use format.money() to display amountMinor. Currencies use 0, 2, 3 or 4 minor-unit digits."}},defaultOptions:[],create(e){let t=z(e),s=r=>k(r)||R(t,r);return{Literal(r){typeof r.value=="string"&&r.parent.type!=="ImportDeclaration"&&N(r.value)&&e.report({node:r,messageId:"literal"})},JSXText(r){N(r.value)&&e.report({node:r,messageId:"literal"})},BinaryExpression(r){["+","-","*","/"].includes(r.operator)&&(x(r)&&r.parent.type==="MemberExpression"&&r.parent.object===r&&T(r.parent)==="toFixed"||(s(r.left)||s(r.right))&&e.report({node:r,messageId:"arithmetic"}))},TemplateLiteral(r){for(let c of r.quasis)N(c.value.cooked??c.value.raw)&&e.report({node:c,messageId:"literal"})},CallExpression(r){r.callee.type==="MemberExpression"&&T(r.callee)==="toFixed"&&r.arguments[0]?.type==="Literal"&&r.arguments[0].value===2&&x(r.callee.object)&&e.report({node:r,messageId:"scale"})}}}});var rt=["**/*.server.{js,jsx,ts,tsx,mjs,cjs,mts,cts}","**/app/.server/**","**/app/server.ts","**/test/**","**/tests/**","**/__tests__/**","**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs,mts,cts}","**/*.config.{js,ts,mjs,cjs,mts,cts}"],it=new Set(["MODE","DEV","PROD","SSR","BASE_URL"]);function ue(e){if(e.type==="FunctionDeclaration")return e.id?.name;if(e.type!=="FunctionExpression"&&e.type!=="ArrowFunctionExpression")return;let t=e.parent;if(t.type==="VariableDeclarator"&&t.id.type==="Identifier")return t.id.name;if(t.type==="Property")return t.key.type==="Identifier"?t.key.name:u(t.key)}function st(e){if(e.type==="MemberExpression"&&e.object.type==="Identifier"&&e.object.name==="process"&&y(e)==="env")return"process";if(e.type==="MemberExpression"&&e.object.type==="MetaProperty"&&e.object.meta.name==="import"&&e.object.property.name==="meta"&&y(e)==="env")return"vite"}function fe(e){return e.type!=="MemberExpression"||y(e)!=="cloudflare"?!1:e.object.type==="Identifier"&&e.object.name==="context"||e.object.type==="MemberExpression"&&y(e.object)==="context"}function de(e,t){return e==="process"?t!=="NODE_ENV":t===void 0||!t.startsWith("VITE_ZAPPIE_")&&!it.has(t)}var ye=m({name:"no-server-env-in-client",meta:{type:"problem",docs:{description:"Read server environment values only in server code."},schema:[],messages:{serverEnv:"Move this environment read into a loader, action, *.server.ts file, app/.server/ or app/server.ts so it cannot reach the client bundle."}},defaultOptions:[],create(e){let t=e.filename.replaceAll("\\","/");if(I(t,rt)||/(?:^|\/)(?:test|tests|__tests__)(?:\/|$)/.test(t)||/\.(?:test|spec|config)\.[cm]?[jt]sx?$/.test(t))return{};let s=new Map,r=new Set,c=n=>e.sourceCode.getAncestors(n).some(a=>["loader","action"].includes(ue(a)??"")),p=n=>{let a=st(n);return a||(n.type==="Identifier"?s.get(n.name):void 0)},i=n=>{if(n.type==="ObjectPattern")for(let a of n.properties)a.type==="Property"&&(a.key.type==="Identifier"&&a.key.name==="cloudflare"||u(a.key)==="cloudflare")&&a.value.type==="Identifier"&&r.add(a.value.name),a.type==="Property"&&(a.key.type==="Identifier"&&a.key.name==="context"||u(a.key)==="context")&&i(a.value)},o=n=>{if(!(c(n)||["loader","action"].includes(ue(n)??"")))for(let a of n.params)a.type!=="RestElement"&&i(a)};return{ArrowFunctionExpression:o,FunctionDeclaration:o,FunctionExpression:o,VariableDeclarator(n){if(c(n)||!n.init)return;let a=p(n.init);if(n.id.type==="Identifier"&&a){s.set(n.id.name,a);return}if(n.id.type==="ObjectPattern"&&a){for(let l of n.id.properties){let d=l.type==="Property"?l.key.type==="Identifier"?l.key.name:u(l.key):void 0;de(a,d)&&e.report({node:l,messageId:"serverEnv"})}return}if(n.id.type==="ObjectPattern"&&fe(n.init))for(let l of n.id.properties)l.type==="Property"&&(l.key.type==="Identifier"&&l.key.name==="env"||u(l.key)==="env")&&e.report({node:l,messageId:"serverEnv"})},MemberExpression(n){if(c(n))return;let a=p(n.object);if(a&&de(a,y(n))){e.report({node:n,messageId:"serverEnv"});return}y(n)==="env"&&(fe(n.object)||n.object.type==="Identifier"&&r.has(n.object.name))&&e.report({node:n,messageId:"serverEnv"})}}}});var nt=["react-ga4","react-facebook-pixel","@vercel/analytics","posthog-js"];function at(e){return nt.includes(e)||e.startsWith("@segment/")}var Se=m({name:"no-tracking-scripts",meta:{type:"problem",docs:{description:"Forward analytics through Zappie."},schema:[],messages:{tracking:"Configure analytics.forward in zappie.config.ts to forward analytics. Remove the tracking script."}},defaultOptions:[],create(e){let t=s=>{if(s.parent.type==="ImportDeclaration")return;let r=u(s);r&&L(r)&&e.report({node:s,messageId:"tracking"})};return{Literal:t,TemplateLiteral:t,ImportDeclaration(s){at(s.source.value)&&e.report({node:s.source,messageId:"tracking"})},CallExpression(s){let r=s.callee,c=r.type==="Identifier"&&["gtag","fbq"].includes(r.name),p=r.type==="MemberExpression"&&!r.computed&&r.object.type==="Identifier"&&r.object.name==="dataLayer"&&r.property.type==="Identifier"&&r.property.name==="push";(c||p)&&e.report({node:s,messageId:"tracking"})}}}});function ge(e){let t=u(e??null);if(t!==void 0)return t;if(e?.type==="NewExpression"&&e.callee.type==="Identifier"&&e.callee.name==="URL")return ge(e.arguments[0])}function ot(e){if(!(!/^(?:https?|wss?):\/\//i.test(e)&&!e.startsWith("//")))try{return new URL(e,"https://storefront.invalid").hostname.toLowerCase()}catch{return}}function pt(e,t){return t.some(s=>{let r=s.toLowerCase();return r.startsWith("*.")?e.endsWith(r.slice(1))&&e!==r.slice(2):e===r})}var C=m({name:"no-unlisted-fetch",meta:{type:"problem",docs:{description:"List every external request host in outbound.allow."},schema:[{type:"object",properties:{dynamicOnly:{type:"boolean"}},additionalProperties:!1}],messages:{dynamicRequest:"This request URL is computed. Review its possible hosts and list each external host in zappie.config.ts outbound.allow.",unlistedRequest:"Add {{host}} to zappie.config.ts outbound.allow, or remove this external request."}},defaultOptions:[{}],create(e,[t]){let s=e.filename.replaceAll("\\","/");if(I(s,["**/test/**","**/tests/**","**/__tests__/**","**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"])||/(?:^|\/)(?:test|tests|__tests__)(?:\/|$)/.test(s)||/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(s))return{};let r=e.settings?.zappie?.outboundAllow,c=Array.isArray(r)?r.filter(o=>typeof o=="string"):[],p=new Set,i=(o,n)=>{let a=ge(n);if(a===void 0){t.dynamicOnly&&e.report({node:o,messageId:"dynamicRequest"});return}if(t.dynamicOnly)return;let l=ot(a);!l||M(a)||pt(l,c)||e.report({node:o,messageId:"unlistedRequest",data:{host:l}})};return{VariableDeclarator(o){o.id.type==="Identifier"&&o.init?.type==="NewExpression"&&o.init.callee.type==="Identifier"&&o.init.callee.name==="XMLHttpRequest"&&p.add(o.id.name)},CallExpression(o){if(o.callee.type==="Identifier"&&o.callee.name==="fetch"||o.callee.type==="MemberExpression"&&y(o.callee)==="fetch"&&o.callee.object.type==="Identifier"&&["window","globalThis"].includes(o.callee.object.name)){i(o,o.arguments[0]);return}if(o.callee.type!=="MemberExpression")return;let a=y(o.callee);if(a==="sendBeacon"&&o.callee.object.type==="Identifier"&&o.callee.object.name==="navigator"){i(o,o.arguments[0]);return}a==="open"&&(o.callee.object.type==="Identifier"&&p.has(o.callee.object.name)||o.callee.object.type==="NewExpression"&&o.callee.object.callee.type==="Identifier"&&o.callee.object.callee.name==="XMLHttpRequest")&&i(o,o.arguments[1])},NewExpression(o){o.callee.type==="Identifier"&&["WebSocket","EventSource"].includes(o.callee.name)&&i(o,o.arguments[0])}}}}),Ee={...C,meta:{...C.meta,docs:{...C.meta.docs,url:"https://docs.zappie.ai/lint/no-unlisted-fetch-dynamic"}}};function ct(e){return e.properties.find(t=>t.type==="Property"&&!t.computed&&(t.key.type==="Identifier"&&t.key.name==="__html"||u(t.key)==="__html"))?.value}var Te=m({name:"no-unsafe-html",meta:{type:"problem",docs:{description:"Sanitize rich text before inserting HTML."},schema:[],messages:{unsafeHtml:"Pass the HTML straight through a sanitizer created with createSanitizeRichText from @zappie/foundations/rich-text."}},defaultOptions:[],create(e){let t=e.sourceCode,s=i=>!!b(t,i,i.name)?.defs.some(n=>n.type==="ImportBinding"&&n.node.type==="ImportSpecifier"&&n.node.imported.type==="Identifier"&&n.node.imported.name==="createSanitizeRichText"&&n.parent?.type==="ImportDeclaration"&&n.parent.source.value==="@zappie/foundations/rich-text"),r=i=>!!b(t,i,i.name)?.defs.some(n=>{if(n.type!=="Variable"||n.node.type!=="VariableDeclarator")return!1;let a=n.node.init;return a?.type==="CallExpression"&&a.callee.type==="Identifier"&&s(a.callee)}),c=(i,o=new Set)=>{if(!i||i.type==="SpreadElement")return!1;let n=h(i);if(n.type==="Literal")return n.value==="";if(n.type==="ConditionalExpression")return c(n.consequent,o)&&c(n.alternate,o);if(n.type==="LogicalExpression")return c(n.left,o)&&c(n.right,o);if(n.type==="CallExpression")return n.callee.type==="Identifier"&&r(n.callee)?!0:n.callee.type==="CallExpression"&&n.callee.callee.type==="Identifier"&&s(n.callee.callee);if(n.type!=="Identifier")return!1;let a=b(t,n,n.name);if(!a||o.has(a))return!1;let l=new Set(o).add(a);for(let d of a.defs){if(d.type!=="Variable"||d.node.type!=="VariableDeclarator")continue;if(d.node.id.type==="Identifier")return c(d.node.init,l);if(d.node.id.type!=="ArrayPattern"||d.node.id.elements[0]?.type!=="Identifier"||d.node.id.elements[0].name!==n.name||d.node.init?.type!=="CallExpression"||d.node.init.callee.type!=="Identifier"||d.node.init.callee.name!=="useState")continue;let E=d.node.init.arguments[0],f=d.node.id.elements[1];if(!c(E,l)||f?.type!=="Identifier")return!1;let $=b(t,f,f.name)?.references.filter(w=>w.identifier.parent.type==="CallExpression"&&w.identifier.parent.callee===w.identifier)??[];return $.length>0&&$.every(w=>{let W=w.identifier.parent;return W.type==="CallExpression"&&c(W.arguments[0],l)})}return!1},p=(i,o)=>{(o?.type!=="ObjectExpression"||!c(ct(o)))&&e.report({node:i,messageId:"unsafeHtml"})};return{JSXAttribute(i){if(i.name.type!=="JSXIdentifier")return;let o=i.name.name.toLowerCase();!["dangerouslysetinnerhtml","srcdoc"].includes(o)||i.value?.type!=="JSXExpressionContainer"||(o==="dangerouslysetinnerhtml"?p(i,i.value.expression):c(i.value.expression)||e.report({node:i,messageId:"unsafeHtml"}))},JSXSpreadAttribute(i){if(i.argument.type!=="ObjectExpression")return;let o=i.argument.properties.find(n=>n.type==="Property"&&(n.key.type==="Identifier"&&n.key.name==="dangerouslySetInnerHTML"||u(n.key)==="dangerouslySetInnerHTML"));o&&p(i,o.value)},JSXElement(i){if(i.openingElement.name.type==="JSXIdentifier"&&i.openingElement.name.name.toLowerCase()==="script")for(let o of i.children)o.type==="JSXExpressionContainer"&&o.expression.type!=="JSXEmptyExpression"&&e.report({node:o,messageId:"unsafeHtml"})},AssignmentExpression(i){i.left.type==="MemberExpression"&&["innerHTML","outerHTML"].includes(y(i.left)??"")&&!c(i.right)&&e.report({node:i,messageId:"unsafeHtml"})},CallExpression(i){if(i.callee.type==="MemberExpression"&&y(i.callee)==="insertAdjacentHTML"){c(i.arguments[1])||e.report({node:i,messageId:"unsafeHtml"});return}if(!(i.callee.type==="Identifier"&&i.callee.name==="createElement"||i.callee.type==="MemberExpression"&&y(i.callee)==="createElement"&&i.callee.object.type==="Identifier"&&i.callee.object.name==="React"))return;if(u(i.arguments[0]??null)?.toLowerCase()==="script")for(let l of i.arguments.slice(2))l.type!=="SpreadElement"&&e.report({node:l,messageId:"unsafeHtml"});let n=i.arguments[1];if(n?.type!=="ObjectExpression")return;let a=n.properties.find(l=>l.type==="Property"&&(l.key.type==="Identifier"&&l.key.name==="dangerouslySetInnerHTML"||u(l.key)==="dangerouslySetInnerHTML"));a&&p(i,a.value)}}}});var lt=["modal","sheet","drawer","dialog","item","cart","cart_sheet","cart_edit","cart_clear","search","catalogue_search","catalogue_categories","product_note","modal_id"];function J(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function he(e,t){return e.type==="Literal"&&typeof e.value=="string"?[...t].some(s=>new RegExp(`[?&]${s}=`).test(e.value)):e.type==="TemplateLiteral"?e.quasis.some(s=>he({...s,type:"Literal",value:s.value.raw},t)):e.type==="ObjectExpression"?e.properties.some(s=>s.type==="Property"&&(s.key.type==="Identifier"&&t.has(s.key.name)||s.key.type==="Literal"&&typeof s.key.value=="string"&&t.has(s.key.value))):!1}var xe=m({name:"overlay-primitive",meta:{type:"problem",docs:{description:"Use Overlay to keep dialogs and sheets in the URL."},schema:[{type:"object",properties:{keys:{type:"array",items:{type:"string"}}},additionalProperties:!1}],messages:{overlay:"Use Overlay or useOverlay for dialogs and sheets in the URL. They handle Back, Forward and focus restoration."}},defaultOptions:[{}],create(e,[t]){let s=new Set([...lt,...t.keys??[]]),r=new Set,c=p=>{let i=e.sourceCode.getText(p);return i.includes("useSearchParams(")||[...r].some(o=>new RegExp(`\\b${o}\\b`).test(i))};return{VariableDeclarator(p){p.init?.type==="CallExpression"&&J(p.init.callee)==="useSearchParams"?p.id.type==="Identifier"?r.add(p.id.name):p.id.type==="ArrayPattern"&&p.id.elements[0]?.type==="Identifier"&&r.add(p.id.elements[0].name):p.id.type==="Identifier"&&p.init&&c(p.init)&&r.add(p.id.name)},CallExpression(p){let i=J(p.callee);(i==="setSearchParams"||i==="navigate"||i==="pushState"||i==="replaceState"||p.callee.type==="CallExpression"&&J(p.callee.callee)==="useNavigate")&&p.arguments.some(n=>n.type!=="SpreadElement"&&he(n,s))&&e.report({node:p,messageId:"overlay"})},JSXOpeningElement(p){let i=p.name.type==="JSXIdentifier"&&p.name.name==="dialog",o=p.attributes.some(a=>a.type==="JSXAttribute"&&a.name.name==="role"&&a.value?.type==="Literal"&&a.value.value==="dialog"),n=p.attributes.find(a=>a.type==="JSXAttribute"&&a.name.name==="open");(i||o)&&n?.type==="JSXAttribute"&&n.value?.type==="JSXExpressionContainer"&&c(n.value.expression)&&e.report({node:p,messageId:"overlay"})}}}});function mt(e){return e.callee.type==="Identifier"?e.callee.name:e.callee.type==="MemberExpression"?y(e.callee):void 0}function j(e){return!e||e.type==="JSXEmptyExpression"?!1:e.type==="TSAsExpression"||e.type==="TSTypeAssertion"||e.type==="TSNonNullExpression"?j(e.expression):e.type==="ConditionalExpression"?j(e.consequent)||j(e.alternate):e.type==="LogicalExpression"?j(e.left)||j(e.right):u(e)?.toLowerCase()==="_blank"}var be=m({name:"safe-external-links",meta:{type:"problem",docs:{description:"Open links and redirects without exposing the site."},fixable:"code",schema:[],messages:{javascriptUrl:"Remove the javascript: URL. Use an event handler for storefront behavior.",noopener:'Add rel="noopener noreferrer" when target="_blank" opens a new tab.',redirect:"Pass this redirect target through safeRedirect(to, fallback) from @zappie/sdk/redirect."}},defaultOptions:[],create(e){let t=e.sourceCode,s=new Set,r=new Set,c=n=>!!b(t,n,n.name)?.defs.some(l=>l.type==="ImportBinding"&&l.node.type==="ImportSpecifier"&&l.node.imported.type==="Identifier"&&l.node.imported.name==="safeRedirect"&&l.parent?.type==="ImportDeclaration"&&l.parent.source.value==="@zappie/sdk/redirect"),p=n=>{if(!n||n.type==="SpreadElement")return!1;let a=h(n);return a.type==="Identifier"?s.has(a.name)&&!r.has(a.name):a.type==="MemberExpression"?a.object.type==="Identifier"&&a.object.name==="request"&&y(a)==="url"?!0:p(a.object):a.type==="CallExpression"?a.callee.type==="Identifier"&&c(a.callee)?!1:a.callee.type==="Identifier"&&a.callee.name==="safeRedirect"?a.arguments.some(p):!!(a.callee.type==="MemberExpression"&&y(a.callee)==="get"&&(a.callee.object.type==="MemberExpression"&&y(a.callee.object)==="searchParams"||a.callee.object.type==="Identifier"&&/searchParams/i.test(a.callee.object.name)||p(a.callee.object))):a.type==="NewExpression"?a.callee.type==="Identifier"&&a.callee.name==="URL"&&a.arguments.some(p):a.type==="ConditionalExpression"?p(a.consequent)||p(a.alternate):a.type==="LogicalExpression"?p(a.left)||p(a.right):a.type==="BinaryExpression"?p(a.left):a.type==="TemplateLiteral"?a.expressions.some(p):!1},i=(n,a)=>{if(a.type!=="Identifier")return!1;let l=E=>{let f=h(E);return f.type==="MemberExpression"&&y(f)==="origin"&&f.object.type==="Identifier"&&f.object.name===a.name},d=E=>{let f=h(E);return f.type==="MemberExpression"&&y(f)==="origin"&&(f.object.type==="Identifier"&&f.object.name==="location"||p(f.object))};return t.getAncestors(n).some(E=>{if(E.type!=="IfStatement"||!t.getAncestors(n).includes(E.consequent))return!1;let f=h(E.test);return f.type==="BinaryExpression"&&(f.operator==="==="||f.operator==="==")&&(l(f.left)&&d(f.right)||d(f.left)&&l(f.right))})};return{VariableDeclarator(n){n.id.type!=="Identifier"||!n.init||(n.init.type==="CallExpression"&&n.init.callee.type==="Identifier"&&c(n.init.callee)?r.add(n.id.name):p(n.init)&&s.add(n.id.name))},JSXOpeningElement(n){for(let f of["href","src","action","formAction"]){let v=g(S(n,f));/^javascript:/i.test((v??"").replace(/[\u0000-\u0020\u007f\s]/gu,""))&&e.report({node:S(n,f)??n,messageId:"javascriptUrl"})}let a=S(n,"target"),l=a?.value?.type==="JSXExpressionContainer"?a.value.expression:a?.value;if(!j(l))return;let d=S(n,"rel"),E=g(d);E?.toLowerCase().split(/\s+/).includes("noopener")||e.report({node:n,messageId:"noopener",fix(f){if(!d)return f.insertTextAfter(n.name,' rel="noopener noreferrer"');if(!d.value||d.value.type!=="Literal")return null;let v=new Set((E??"").split(/\s+/).filter(Boolean));return v.add("noopener"),v.add("noreferrer"),f.replaceText(d,`rel="${[...v].join(" ")}"`)}})},CallExpression:n=>{let a=mt(n);if(!["redirect","navigate","assign","replace"].includes(a??""))return;let l=n.arguments[0];l&&l.type!=="SpreadElement"&&p(l)&&!i(n,h(l))&&e.report({node:l,messageId:"redirect"})},AssignmentExpression(n){n.left.type==="MemberExpression"&&y(n.left)==="href"&&(n.left.object.type==="Identifier"&&n.left.object.name==="location"||n.left.object.type==="MemberExpression"&&y(n.left.object)==="location")&&p(n.right)&&e.report({node:n.right,messageId:"redirect"})}}}});function D(e){return e.body.find(t=>t.type==="ImportDeclaration"&&t.source.value==="@zappie/sdk")}function U(e){return e?.specifiers.find(t=>t.type==="ImportSpecifier"&&t.imported.type==="Identifier"&&t.imported.name==="format")}function ut(e,t,s,r){let c=D(s);if(U(c)?.local.name===r)return;let p=r==="format"?"format":`format as ${r}`,i=c?.specifiers.filter(a=>a.type==="ImportSpecifier");if(c&&c.importKind!=="type"&&i?.length){let a=i.at(-1);if(a)return e.insertTextAfter(a,`, ${p}`)}let o=s.body[0],n=`import { ${p} } from "@zappie/sdk";
2
+ `;return o?e.insertTextBefore(o,n):e.insertTextAfter(s,n)}function _(e,t,s,r,c,p){let i=[e.replaceText(r,`${p}.money(${t.getText(c)})`)],o=ut(e,t,s,p);return o&&i.push(o),i}function ve(e,t,s){if(U(D(t)))return"imported";let r=e.getScope(s);for(;r;){if(r.variables.some(c=>c.name==="format"))return"conflict";r=r.upper}return"available"}function Ie(e){return U(D(e))?.local.name}function ke(e,t){let s=new Set,r=e.getScope(t);for(;r;){for(let c of r.variables)s.add(c.name);r=r.upper}for(let c of["zappieFormat","zappieMoneyFormat","formatMoneyValue"])if(!s.has(c))return c;return"zappieSdkFormat"}function je(e){let t=e.type==="NewExpression"||e.type==="CallExpression"?e.callee:void 0;return t?.type==="MemberExpression"&&t.object.type==="Identifier"&&t.object.name==="Intl"&&T(t)==="NumberFormat"}function ft(e){if(e.callee.type==="MemberExpression"){if(T(e.callee)==="toLocaleString")return x(e.callee.object);if(T(e.callee)==="format")return x(e.arguments[0]??e)}}function dt(e){let t=e.parent,s=t.type==="MemberExpression"?t.parent:void 0;return!!(s?.type==="CallExpression"&&s.callee===t&&T(t)==="format"&&x(s.arguments[0]??s))}var we=m({name:"use-format-money",meta:{type:"problem",docs:{description:"Format SDK Money with format.money() or <Money>."},fixable:"code",hasSuggestions:!0,schema:[],messages:{currency:"Use format.money(). Intl currency defaults can differ from the API minor-unit digits.",direct:"Render SDK Money with format.money(value) or <Money value={value}>.",minor:"Use format.money(value). Dividing by 100 gives the wrong amount for currencies with 0, 3 or 4 minor-unit digits.",useAlias:"Import format with an unused alias and format this Money value."}},defaultOptions:[],create(e){let t=e.sourceCode,s=z(e),r,c=(i,o,n)=>{let a={node:i,messageId:"minor"};if(ve(t,r,i)==="conflict"){let d=ke(t,i);e.report({...a,suggest:[{messageId:"useAlias",fix:E=>_(E,t,r,o,n,d)}]});return}e.report({...a,fix:d=>{let E=Ie(r)??"format";return _(d,t,r,o,n,E)}})},p=i=>{let o=ft(i);return o?(c(i,i,o),!0):!1};return{Program(i){r=i},NewExpression(i){je(i)&&A(i.arguments[1])&&!dt(i)&&e.report({node:i,messageId:"currency"})},CallExpression(i){if(!p(i)){if(je(i)&&A(i.arguments[1])){e.report({node:i,messageId:"currency"});return}i.callee.type==="MemberExpression"&&T(i.callee)==="toLocaleString"&&i.arguments.some(A)&&e.report({node:i,messageId:"currency"})}},TemplateLiteral(i){for(let o of i.expressions){let n=x(o);n?c(o,o,n):k(o)&&e.report({node:o,messageId:"minor"})}},JSXExpressionContainer(i){i.parent.type==="JSXAttribute"||i.expression.type==="JSXEmptyExpression"||(x(i.expression)||k(i.expression)?e.report({node:i.expression,messageId:"minor"}):R(s,i.expression)&&e.report({node:i.expression,messageId:"direct"}))}}}});var B={add:{name:"data-zappie-action",text:'data-zappie-action="add-to-cart"'},checkout:{name:"data-zappie-checkout",text:"data-zappie-checkout"},signin:{name:"data-zappie-signin",text:"data-zappie-signin"}};function yt(e,t){let s=t.value?e.getText(t.value):"";if(/\b(?:cart(?:\.actions)?\.add)\s*\(/.test(s))return"add";if(/\b(?:checkout|createCheckout\([^)]*\))\.open\s*\(/.test(s))return"checkout";if(/\b(?:auth|createCustomer\([^)]*\))\.signIn\s*\(/.test(s))return"signin"}var Le=m({name:"verify-markers",meta:{type:"suggestion",docs:{description:"Mark shopping actions so verify can find them."},fixable:"code",schema:[],messages:{marker:"Add {{marker}} so zappie verify can find this shopping action."}},defaultOptions:[],create(e){return{JSXOpeningElement(t){let s=t.attributes.find(c=>c.type==="JSXAttribute"&&c.name.name==="onClick");if(!s)return;let r=yt(e.sourceCode,s);!r||t.attributes.some(c=>c.type==="JSXAttribute"&&c.name.name===B[r].name)||e.report({node:s,messageId:"marker",data:{marker:B[r].text},fix:c=>c.insertTextAfter(t.name,` ${B[r].text}`)})}}}});var Me={"no-direct-api":ae,"no-tracking-scripts":Se,"checkout-via-sdk":ee,"no-custom-auth":ne,"no-money-literals":me,"use-format-money":we,"overlay-primitive":xe,"cart-via-sdk":Z,"verify-markers":Le,"item-link-prefetch":re,"image-priority":te,"no-lazy-overlay":le,"no-unsafe-html":Te,"no-eval":oe,"no-external-scripts":ce,"safe-external-links":be,"no-unlisted-fetch":C,"no-unlisted-fetch-dynamic":Ee,"no-server-env-in-client":ye},K=Object.fromEntries(Object.keys(Me).map(e=>[`zappie/${e}`,e==="verify-markers"||e==="no-unlisted-fetch-dynamic"?"warn":"error"])),Et={...K,"zappie/verify-markers":"error","zappie/no-unlisted-fetch-dynamic":["error",{dynamicOnly:!0}]};K["zappie/no-unlisted-fetch-dynamic"]=["warn",{dynamicOnly:!0}];var H={meta:{name:"eslint-plugin-zappie",version:"0.0.0"},rules:Me,configs:{}},O={name:"zappie/rules",files:["**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"],languageOptions:{parser:St},plugins:{zappie:H,"jsx-a11y":Ce,"react-hooks":gt},rules:{...Ce.flatConfigs.recommended.rules,"react-hooks/rules-of-hooks":"error",...K}},V={name:"zappie/search-dialogs",files:["**/search/**/*.{jsx,tsx}","**/*search*.{jsx,tsx}"],rules:{"jsx-a11y/no-autofocus":"off"}},q={name:"zappie/typed",files:["**/*.{ts,tsx,mts,cts}"],languageOptions:{parserOptions:{projectService:!0}}};H.configs={recommended:[O,q,V],strict:[{...O,rules:{...O.rules,...Et}},q,V],"zappie-internal":[O,q,V,{name:"zappie/internal-sdk",files:["packages/sdk/src/**/*.{ts,tsx}"],rules:{"zappie/no-direct-api":"off","zappie/no-unlisted-fetch":"off","zappie/no-unlisted-fetch-dynamic":"off"}},{name:"zappie/internal-map-loader",files:["packages/foundations/src/branch-map/loader.ts"],rules:{"zappie/no-external-scripts":"off"}},{name:"zappie/internal-auth-ui",files:["packages/foundations/src/otp/**/*.{ts,tsx}"],rules:{"zappie/no-custom-auth":"off"}},{name:"zappie/internal-money",files:["packages/sdk/src/money.ts","packages/sdk/src/internal/money-math.ts","packages/sdk/src/cart/branch.ts","packages/sdk/src/pricing/index.ts"],rules:{"zappie/no-money-literals":"off","zappie/use-format-money":"off"}},{name:"zappie/internal-overlay",files:["packages/foundations/src/overlay/**/*.{ts,tsx}"],rules:{"zappie/overlay-primitive":"off"}},{name:"zappie/internal-overlay-tests",files:["themes/base/src/cart/cart-review.test.tsx","themes/base/src/cart/core-cart.test.tsx","themes/base/src/cart/fulfilment.test.tsx","themes/base/src/cart/theme-class.test.tsx"],rules:{"zappie/overlay-primitive":"off"}}]};var Vr=H;export{Z as cartViaSdk,ee as checkoutViaSdk,Vr as default,te as imagePriority,re as itemLinkPrefetch,ne as noCustomAuth,ae as noDirectApi,oe as noEval,ce as noExternalScripts,le as noLazyOverlay,me as noMoneyLiterals,ye as noServerEnvInClient,Se as noTrackingScripts,C as noUnlistedFetch,Te as noUnsafeHtml,xe as overlayPrimitive,be as safeExternalLinks,we as useFormatMoney,Le as verifyMarkers};
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/create-rule.ts","../src/rules/cart-via-sdk.ts","../src/matchers.ts","../src/rules/checkout-via-sdk.ts","../src/rules/image-priority.ts","../src/rules/item-link-prefetch.ts","../src/rules/no-custom-auth.ts","../src/rules/no-direct-api.ts","../src/rules/no-lazy-overlay.ts","../src/money-rule-utils.ts","../src/rules/no-money-literals.ts","../src/rules/no-tracking-scripts.ts","../src/rules/overlay-primitive.ts","../src/format-fix.ts","../src/rules/use-format-money.ts","../src/rules/verify-markers.ts"],"sourcesContent":["import parser from \"@typescript-eslint/parser\";\nimport jsxA11y from \"eslint-plugin-jsx-a11y\";\nimport reactHooks from \"eslint-plugin-react-hooks\";\nimport { cartViaSdk } from \"./rules/cart-via-sdk\";\nimport { checkoutViaSdk } from \"./rules/checkout-via-sdk\";\nimport { imagePriority } from \"./rules/image-priority\";\nimport { itemLinkPrefetch } from \"./rules/item-link-prefetch\";\nimport { noCustomAuth } from \"./rules/no-custom-auth\";\nimport { noDirectApi } from \"./rules/no-direct-api\";\nimport { noLazyOverlay } from \"./rules/no-lazy-overlay\";\nimport { noMoneyLiterals } from \"./rules/no-money-literals\";\nimport { noTrackingScripts } from \"./rules/no-tracking-scripts\";\nimport { overlayPrimitive } from \"./rules/overlay-primitive\";\nimport { useFormatMoney } from \"./rules/use-format-money\";\nimport { verifyMarkers } from \"./rules/verify-markers\";\n\nconst rules = {\n \"no-direct-api\": noDirectApi,\n \"no-tracking-scripts\": noTrackingScripts,\n \"checkout-via-sdk\": checkoutViaSdk,\n \"no-custom-auth\": noCustomAuth,\n \"no-money-literals\": noMoneyLiterals,\n \"use-format-money\": useFormatMoney,\n \"overlay-primitive\": overlayPrimitive,\n \"cart-via-sdk\": cartViaSdk,\n \"verify-markers\": verifyMarkers,\n \"item-link-prefetch\": itemLinkPrefetch,\n \"image-priority\": imagePriority,\n \"no-lazy-overlay\": noLazyOverlay,\n};\n\nexport type ZappieFlatConfig = Record<string, unknown>;\n\nconst enabled = Object.fromEntries(\n Object.keys(rules).map((name) => [\n `zappie/${name}`,\n name === \"verify-markers\" ? \"warn\" : \"error\",\n ]),\n);\nconst strictRules = { ...enabled, \"zappie/verify-markers\": \"error\" };\n\nconst plugin = {\n meta: { name: \"eslint-plugin-zappie\", version: \"0.0.0\" },\n rules,\n configs: {} as Record<string, ZappieFlatConfig[]>,\n};\n\nconst base = {\n name: \"zappie/rules\",\n files: [\"**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}\"],\n languageOptions: { parser },\n plugins: { zappie: plugin, \"jsx-a11y\": jsxA11y, \"react-hooks\": reactHooks },\n rules: {\n ...jsxA11y.flatConfigs.recommended.rules,\n \"react-hooks/rules-of-hooks\": \"error\",\n ...enabled,\n },\n};\nconst searchDialogs = {\n name: \"zappie/search-dialogs\",\n files: [\"**/search/**/*.{jsx,tsx}\", \"**/*search*.{jsx,tsx}\"],\n rules: { \"jsx-a11y/no-autofocus\": \"off\" },\n};\n\n// JavaScript files (eslint.config.mjs among them) usually sit outside the\n// tsconfig, where the project service refuses to parse them. They get the\n// syntactic money checks only.\nconst typed = {\n name: \"zappie/typed\",\n files: [\"**/*.{ts,tsx,mts,cts}\"],\n languageOptions: { parserOptions: { projectService: true } },\n};\n\nplugin.configs = {\n recommended: [base, typed, searchDialogs],\n strict: [\n { ...base, rules: { ...base.rules, ...strictRules } },\n typed,\n searchDialogs,\n ],\n \"zappie-internal\": [\n base,\n typed,\n searchDialogs,\n {\n name: \"zappie/internal-sdk\",\n files: [\"packages/sdk/src/**/*.{ts,tsx}\"],\n rules: { \"zappie/no-direct-api\": \"off\" },\n },\n {\n name: \"zappie/internal-auth-ui\",\n files: [\"packages/foundations/src/otp/**/*.{ts,tsx}\"],\n rules: { \"zappie/no-custom-auth\": \"off\" },\n },\n {\n name: \"zappie/internal-money\",\n files: [\n \"packages/sdk/src/money.ts\",\n \"packages/sdk/src/internal/money-math.ts\",\n // Its own arithmetic: importing money-math here splits it out of the core bundle.\n \"packages/sdk/src/cart/branch.ts\",\n // The starting-price rule compares and adds variant and choice prices.\n \"packages/sdk/src/pricing/index.ts\",\n ],\n rules: {\n \"zappie/no-money-literals\": \"off\",\n \"zappie/use-format-money\": \"off\",\n },\n },\n {\n name: \"zappie/internal-overlay\",\n files: [\"packages/foundations/src/overlay/**/*.{ts,tsx}\"],\n rules: { \"zappie/overlay-primitive\": \"off\" },\n },\n {\n name: \"zappie/internal-overlay-tests\",\n files: [\n \"themes/base/src/cart/cart-review.test.tsx\",\n \"themes/base/src/cart/core-cart.test.tsx\",\n \"themes/base/src/cart/fulfilment.test.tsx\",\n \"themes/base/src/cart/theme-class.test.tsx\",\n ],\n rules: { \"zappie/overlay-primitive\": \"off\" },\n },\n ],\n};\n\nexport {\n cartViaSdk,\n checkoutViaSdk,\n imagePriority,\n itemLinkPrefetch,\n noCustomAuth,\n noDirectApi,\n noLazyOverlay,\n noMoneyLiterals,\n noTrackingScripts,\n overlayPrimitive,\n useFormatMoney,\n verifyMarkers,\n};\nexport default plugin;\n","import { ESLintUtils } from \"@typescript-eslint/utils\";\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) => `https://docs.zappie.ai/lint/${name}`,\n);\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst cartName = /^(cart|basket|bag)(Items|Lines|Count|Total)?$/i;\nconst storageKey = /cart|basket|bag/i;\n\nfunction calleeName(node: TSESTree.Node): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"MemberExpression\" && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return undefined;\n}\n\nfunction variableName(node: TSESTree.BindingName): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n return node.type === \"ArrayPattern\" && node.elements[0]?.type === \"Identifier\"\n ? node.elements[0].name\n : undefined;\n}\n\nfunction cartInitial(node: TSESTree.CallExpression): boolean {\n const value =\n calleeName(node.callee) === \"useReducer\"\n ? node.arguments[1]\n : node.arguments[0];\n return Boolean(\n value &&\n value.type !== \"SpreadElement\" &&\n (value.type === \"ArrayExpression\" ||\n (value.type === \"Literal\" && typeof value.value === \"number\")),\n );\n}\n\nexport const cartViaSdk = createRule<[], \"cart\">({\n name: \"cart-via-sdk\",\n meta: {\n type: \"problem\",\n docs: { description: \"Keep your cart state in the SDK.\" },\n schema: [],\n messages: {\n cart: \"Use useCart(), useCartLine() or the SDK client cart. Keep cart state and storage in the SDK.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n CallExpression(node) {\n const method = calleeName(node.callee);\n const key = node.arguments[0];\n if (\n [\"getItem\", \"setItem\", \"removeItem\", \"open\"].includes(method ?? \"\") &&\n key?.type === \"Literal\" &&\n typeof key.value === \"string\" &&\n storageKey.test(key.value)\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n },\n VariableDeclarator(node) {\n if (node.init?.type !== \"CallExpression\") {\n return;\n }\n const id = variableName(node.id);\n const call = calleeName(node.init.callee);\n if (\n id &&\n cartName.test(id) &&\n [\"useState\", \"useReducer\", \"createContext\"].includes(call ?? \"\") &&\n cartInitial(node.init)\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n if (\n id &&\n /cart/i.test(id) &&\n [\"create\", \"createStore\", \"atom\"].includes(call ?? \"\")\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { minimatch } from \"minimatch\";\n\nconst endpointPattern =\n /(?:api|accounts|checkout|edge)\\.zappie\\.ai|\\/storefront\\/v1|\\/customer\\/graphql/i;\nconst trackingPattern =\n /googletagmanager\\.com|google-analytics\\.com|connect\\.facebook\\.net|analytics\\.tiktok\\.com|static\\.hotjar\\.com|cdn\\.segment\\.com|plausible\\.io|clarity\\.ms/i;\n\nexport const paymentHostPattern =\n /checkout\\.paystack\\.com|flutterwave|(?:^|\\.)stripe\\.com/i;\n\nexport function staticText(node: TSESTree.Node | null): string | undefined {\n if (node?.type === \"Literal\" && typeof node.value === \"string\") {\n return node.value;\n }\n if (node?.type === \"TemplateLiteral\") {\n return node.quasis\n .map((part) => part.value.cooked ?? part.value.raw)\n .join(\"\");\n }\n return undefined;\n}\n\nexport function jsxAttribute(\n node: TSESTree.JSXOpeningElement,\n name: string,\n): TSESTree.JSXAttribute | undefined {\n return node.attributes.find(\n (attribute): attribute is TSESTree.JSXAttribute =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.type === \"JSXIdentifier\" &&\n attribute.name.name.toLowerCase() === name.toLowerCase(),\n );\n}\n\nexport function jsxValue(node?: TSESTree.JSXAttribute): string | undefined {\n if (!node?.value) {\n return undefined;\n }\n if (node.value.type === \"Literal\") {\n return typeof node.value.value === \"string\" ? node.value.value : undefined;\n }\n if (node.value.type === \"JSXExpressionContainer\") {\n return staticText(node.value.expression);\n }\n return undefined;\n}\n\nexport const isZappieEndpoint = (value: string) => endpointPattern.test(value);\nexport const isTrackingEndpoint = (value: string) =>\n trackingPattern.test(value);\nexport const isInternalSdkImport = (value: string) =>\n value === \"@zappie/sdk/internal\" || value.startsWith(\"@zappie/sdk/internal/\");\n\nexport function isServerFile(filename: string, patterns: string[]): boolean {\n const path = filename.replaceAll(\"\\\\\", \"/\");\n return patterns.some((pattern) =>\n minimatch(path, pattern, { matchBase: true }),\n );\n}\n","import { createRule } from \"../create-rule\";\nimport { jsxAttribute, jsxValue, paymentHostPattern } from \"../matchers\";\n\nconst autocomplete = new Set([\"cc-number\", \"cc-exp\", \"cc-csc\", \"cc-name\"]);\nconst paymentField = /card.?number|cvv|cvc|expiry/i;\n\nfunction providerImport(value: string): boolean {\n return (\n value.startsWith(\"@paystack/\") ||\n value === \"react-paystack\" ||\n value === \"flutterwave-react-v3\" ||\n value.startsWith(\"@stripe/\")\n );\n}\n\nexport const checkoutViaSdk = createRule<[], \"checkout\">({\n name: \"checkout-via-sdk\",\n meta: {\n type: \"problem\",\n docs: { description: \"Open checkout through the Zappie SDK.\" },\n schema: [],\n messages: {\n checkout:\n \"Use createCheckout().open() from @zappie/sdk/checkout. Let checkout collect payment details.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n ImportDeclaration(node) {\n if (providerImport(node.source.value)) {\n context.report({ node: node.source, messageId: \"checkout\" });\n }\n },\n JSXOpeningElement(node) {\n const tag =\n node.name.type === \"JSXIdentifier\"\n ? node.name.name.toLowerCase()\n : \"\";\n const auto = jsxValue(jsxAttribute(node, \"autocomplete\"));\n const name = jsxValue(jsxAttribute(node, \"name\"));\n const id = jsxValue(jsxAttribute(node, \"id\"));\n const target = jsxValue(\n jsxAttribute(node, tag === \"iframe\" ? \"src\" : \"href\"),\n );\n const cardInput =\n tag === \"input\" &&\n ((auto !== undefined && autocomplete.has(auto.toLowerCase())) ||\n (name !== undefined && paymentField.test(name)) ||\n (id !== undefined && paymentField.test(id)));\n const providerTarget =\n (tag === \"iframe\" || tag === \"a\") &&\n target !== undefined &&\n paymentHostPattern.test(target);\n if (cardInput || providerTarget) {\n context.report({ node, messageId: \"checkout\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { jsxAttribute } from \"../matchers\";\n\ntype Callback = TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression;\n\nfunction mappedBy(node: TSESTree.Node) {\n let current: TSESTree.Node | undefined = node;\n while (current?.parent) {\n const parent: TSESTree.Node = current.parent;\n if (\n (parent.type === \"ArrowFunctionExpression\" ||\n parent.type === \"FunctionExpression\") &&\n parent.parent?.type === \"CallExpression\" &&\n parent.parent.callee.type === \"MemberExpression\" &&\n parent.parent.callee.property.type === \"Identifier\" &&\n parent.parent.callee.property.name === \"map\"\n ) {\n return parent;\n }\n current = parent;\n }\n}\n\nfunction firstOnly(source: Readonly<TSESLint.SourceCode>, callback: Callback) {\n const text = source.getText(callback.parent);\n if (/\\.slice\\(\\s*0\\s*,\\s*1\\s*\\)\\.map/.test(text)) {\n return true;\n }\n const index = callback.params[1];\n if (index?.type !== \"Identifier\") {\n return false;\n }\n const first = new RegExp(`${index.name}\\\\s*(?:={2,3}\\\\s*0|<\\\\s*1)`);\n return first.test(source.getText(callback));\n}\n\nexport const imagePriority = createRule<[], \"mapped\" | \"routeLimit\">({\n name: \"image-priority\",\n meta: {\n type: \"problem\",\n docs: { description: \"Reserve priority for the first phone screen.\" },\n schema: [],\n messages: {\n mapped: \"Remove priority from repeated images or limit it to the first.\",\n routeLimit: \"Keep no more than two priority images in a route.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const images: TSESTree.JSXOpeningElement[] = [];\n return {\n JSXOpeningElement(node) {\n const priority = jsxAttribute(node, \"priority\");\n const disabled =\n priority?.value?.type === \"JSXExpressionContainer\" &&\n priority.value.expression.type === \"Literal\" &&\n priority.value.expression.value === false;\n if (\n node.name.type !== \"JSXIdentifier\" ||\n node.name.name !== \"Image\" ||\n !priority ||\n disabled\n ) {\n return;\n }\n images.push(node);\n const callback = mappedBy(node);\n if (callback && !firstOnly(context.sourceCode, callback)) {\n context.report({ node: priority, messageId: \"mapped\" });\n }\n },\n \"Program:exit\"() {\n if (\n /(?:^|\\/)routes?\\/[^/]+\\.[jt]sx$/.test(\n context.filename.replaceAll(\"\\\\\", \"/\"),\n ) &&\n images.length > 2\n ) {\n for (const node of images.slice(2)) {\n context.report({ node, messageId: \"routeLimit\" });\n }\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { jsxAttribute, jsxValue } from \"../matchers\";\n\nfunction targetsItem(attribute?: TSESTree.JSXAttribute) {\n const expression =\n attribute?.value?.type === \"JSXExpressionContainer\"\n ? attribute.value.expression\n : undefined;\n if (\n expression?.type === \"CallExpression\" &&\n expression.callee.type === \"Identifier\" &&\n expression.callee.name === \"itemHref\"\n ) {\n return true;\n }\n const target = jsxValue(attribute);\n return target !== undefined && /^\\/item(?:\\/|$)/.test(target);\n}\n\nexport const itemLinkPrefetch = createRule<[], \"prefetch\">({\n name: \"item-link-prefetch\",\n meta: {\n type: \"problem\",\n docs: { description: \"Prefetch item routes before shoppers tap.\" },\n schema: [],\n messages: {\n prefetch: 'Add prefetch=\"viewport\" so this item route is ready to tap.',\n },\n },\n defaultOptions: [],\n create(context) {\n const links = new Set<string>();\n return {\n ImportDeclaration(node) {\n if (![\"react-router\", \"react-router-dom\"].includes(node.source.value)) {\n return;\n }\n for (const item of node.specifiers) {\n if (\n item.type === \"ImportSpecifier\" &&\n item.imported.type === \"Identifier\" &&\n [\"Link\", \"NavLink\"].includes(item.imported.name)\n ) {\n links.add(item.local.name);\n }\n }\n },\n JSXOpeningElement(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n links.has(node.name.name) &&\n targetsItem(jsxAttribute(node, \"to\")) &&\n !jsxAttribute(node, \"prefetch\")\n ) {\n context.report({ node, messageId: \"prefetch\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isServerFile, jsxAttribute, jsxValue, staticText } from \"../matchers\";\n\ntype Options = [{ serverFiles?: string[] }];\nconst defaults = [\"**/*.server.*\", \"**/server/**\"];\nconst authValue = /otp|verify.?code|access.?token|refresh.?token|id.?token/i;\nconst browserStorage = new Set([\"localStorage\", \"sessionStorage\"]);\nconst globals = new Set([\"window\", \"globalThis\", \"self\"]);\n\nfunction isStorage(node: TSESTree.Node): boolean {\n if (node.type === \"Identifier\") {\n return browserStorage.has(node.name);\n }\n return (\n node.type === \"MemberExpression\" &&\n !node.computed &&\n node.object.type === \"Identifier\" &&\n globals.has(node.object.name) &&\n node.property.type === \"Identifier\" &&\n browserStorage.has(node.property.name)\n );\n}\n\nfunction memberKey(node: TSESTree.MemberExpression): string | undefined {\n if (!node.computed && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return staticText(node.property);\n}\n\nfunction authImport(value: string): \"server\" | \"all\" | undefined {\n if (value === \"jose\" || value === \"jsonwebtoken\") {\n return \"server\";\n }\n if (\n value === \"next-auth\" ||\n value.startsWith(\"next-auth/\") ||\n value.startsWith(\"@auth/\") ||\n value === \"better-auth\" ||\n value.startsWith(\"better-auth/\") ||\n value === \"lucia\" ||\n value.startsWith(\"@clerk/\") ||\n value === \"firebase/auth\" ||\n value.startsWith(\"@supabase/auth-\")\n ) {\n return \"all\";\n }\n return undefined;\n}\n\nexport const noCustomAuth = createRule<Options, \"auth\">({\n name: \"no-custom-auth\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Sign customers in through the Zappie SDK.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n serverFiles: {\n type: \"array\",\n items: { type: \"string\" },\n uniqueItems: true,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n auth: \"Use createCustomer().signIn() from @zappie/sdk/customer to sign customers in.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const server = isServerFile(\n context.filename,\n options.serverFiles ?? defaults,\n );\n return {\n ImportDeclaration(node) {\n const kind = authImport(node.source.value);\n if (kind === \"all\" || (kind === \"server\" && !server)) {\n context.report({ node: node.source, messageId: \"auth\" });\n }\n },\n JSXOpeningElement(node) {\n if (\n node.name.type !== \"JSXIdentifier\" ||\n node.name.name.toLowerCase() !== \"input\"\n ) {\n return;\n }\n const type = jsxValue(jsxAttribute(node, \"type\"));\n const auto = jsxValue(jsxAttribute(node, \"autocomplete\"));\n if (\n type?.toLowerCase() === \"password\" ||\n (auto !== undefined &&\n [\"one-time-code\", \"current-password\", \"new-password\"].includes(\n auto.toLowerCase(),\n ))\n ) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n CallExpression(node) {\n if (\n node.callee.type !== \"MemberExpression\" ||\n !isStorage(node.callee.object)\n ) {\n return;\n }\n const first = node.arguments[0];\n const value =\n first?.type === \"Identifier\" ? first.name : staticText(first ?? null);\n if (value && authValue.test(value)) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n AssignmentExpression(node) {\n if (\n node.left.type === \"MemberExpression\" &&\n isStorage(node.left.object)\n ) {\n const key = memberKey(node.left);\n if (key && authValue.test(key)) {\n context.report({ node, messageId: \"auth\" });\n }\n return;\n }\n const cookie =\n node.left.type === \"MemberExpression\" &&\n node.left.object.type === \"Identifier\" &&\n node.left.object.name === \"document\" &&\n node.left.property.type === \"Identifier\" &&\n node.left.property.name === \"cookie\";\n const value = staticText(node.right);\n if (cookie && value && authValue.test(value)) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isInternalSdkImport, isZappieEndpoint, staticText } from \"../matchers\";\n\ntype Message = \"direct\" | \"internal\" | \"storefront\" | \"checkout\" | \"customer\";\n\nfunction message(value: string): Message {\n if (/checkout\\.zappie\\.ai|\\/checkout(?:\\/|$)/i.test(value)) {\n return \"checkout\";\n }\n if (/accounts\\.zappie\\.ai|\\/customer\\/graphql/i.test(value)) {\n return \"customer\";\n }\n if (/\\/storefront\\/v1/i.test(value)) {\n return \"storefront\";\n }\n return \"direct\";\n}\n\nexport const noDirectApi = createRule<[], Message>({\n name: \"no-direct-api\",\n meta: {\n type: \"problem\",\n docs: { description: \"Call Zappie through the SDK.\" },\n schema: [],\n messages: {\n direct:\n \"Call the matching public @zappie/sdk client to reach this Zappie endpoint.\",\n internal:\n \"Use a public SDK entry. Only Zappie-owned packages can import @zappie/sdk/internal.\",\n storefront:\n \"Use createZappie(config).store or .menu to read storefront data.\",\n checkout: \"Use createCheckout().open() from @zappie/sdk/checkout.\",\n customer: \"Use createCustomer() from @zappie/sdk/customer.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const inspect = (node: TSESTree.Literal | TSESTree.TemplateLiteral) => {\n if (node.parent.type === \"ImportDeclaration\") {\n return;\n }\n const value = staticText(node);\n if (value && isZappieEndpoint(value)) {\n context.report({ node, messageId: message(value) });\n }\n };\n return {\n Literal: inspect,\n TemplateLiteral: inspect,\n ImportDeclaration(node) {\n if (isInternalSdkImport(node.source.value)) {\n context.report({ node: node.source, messageId: \"internal\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst react = new Set([\"react\"]);\n\n// React's own name for its default and namespace import, even without an import (a global build).\nconst conventionalNamespace = \"React\";\n\nexport const noLazyOverlay = createRule<[], \"lazy\" | \"suspense\">({\n name: \"no-lazy-overlay\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Load overlays with deferred() from @zappie/foundations, not React.lazy or Suspense.\",\n },\n schema: [],\n messages: {\n lazy: \"Load this component with deferred() from @zappie/foundations. React.lazy suspends, and React holds a suspended reveal back for about 300 ms, so the overlay opens late.\",\n suspense:\n \"Remove Suspense and load the component with deferred() from @zappie/foundations. It renders as soon as its code arrives and never suspends.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const lazyNames = new Set<string>();\n const suspenseNames = new Set<string>();\n const namespaces = new Set<string>([conventionalNamespace]);\n\n const fromNamespace = (\n object: TSESTree.Node,\n property: TSESTree.Node,\n wanted: string,\n ) =>\n object.type === \"Identifier\" &&\n namespaces.has(object.name) &&\n property.type === \"Identifier\" &&\n property.name === wanted;\n\n return {\n ImportDeclaration(node) {\n if (!react.has(node.source.value)) {\n return;\n }\n for (const specifier of node.specifiers) {\n if (specifier.type !== \"ImportSpecifier\") {\n namespaces.add(specifier.local.name);\n continue;\n }\n const imported =\n specifier.imported.type === \"Identifier\"\n ? specifier.imported.name\n : specifier.imported.value;\n if (imported === \"lazy\") {\n lazyNames.add(specifier.local.name);\n } else if (imported === \"Suspense\") {\n suspenseNames.add(specifier.local.name);\n }\n }\n },\n CallExpression(node) {\n const callee = node.callee;\n if (\n (callee.type === \"Identifier\" && lazyNames.has(callee.name)) ||\n (callee.type === \"MemberExpression\" &&\n !callee.computed &&\n fromNamespace(callee.object, callee.property, \"lazy\"))\n ) {\n context.report({ node, messageId: \"lazy\" });\n }\n },\n JSXOpeningElement(node) {\n const name = node.name;\n if (\n (name.type === \"JSXIdentifier\" && suspenseNames.has(name.name)) ||\n (name.type === \"JSXMemberExpression\" &&\n name.object.type === \"JSXIdentifier\" &&\n namespaces.has(name.object.name) &&\n name.property.name === \"Suspense\")\n ) {\n context.report({ node, messageId: \"suspense\" });\n }\n },\n };\n },\n});\n","import {\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\nimport type ts from \"typescript\";\n\nconst moneyLiteralPattern =\n /[₦€£]\\s?\\d|\\$\\s?\\d|NGN\\s?\\d|\\d+(?:\\.\\d{2})?\\s?(?:naira|NGN)/i;\n\nexport function isMoneyLiteral(value: string): boolean {\n return value.trim() !== \"$1\" && moneyLiteralPattern.test(value);\n}\n\nexport function memberName(node: TSESTree.Node): string | undefined {\n if (node.type !== \"MemberExpression\") {\n return undefined;\n }\n if (!node.computed && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n if (node.computed && node.property.type === \"Literal\") {\n return typeof node.property.value === \"string\"\n ? node.property.value\n : undefined;\n }\n return undefined;\n}\n\nexport const isAmountMinor = (node: TSESTree.Node) =>\n memberName(node) === \"amountMinor\";\n\nexport function amountOwner(\n node: TSESTree.Node,\n): TSESTree.Expression | undefined {\n return node.type === \"MemberExpression\" && isAmountMinor(node)\n ? node.object\n : undefined;\n}\n\nexport function dividedMinorOwner(\n node: TSESTree.Node,\n): TSESTree.Expression | undefined {\n if (\n node.type !== \"BinaryExpression\" ||\n node.operator !== \"/\" ||\n node.right.type !== \"Literal\" ||\n node.right.value !== 100\n ) {\n return undefined;\n }\n return amountOwner(node.left);\n}\n\ntype Services = ReturnType<typeof ESLintUtils.getParserServices>;\n\nexport function parserServices<\n MessageIds extends string,\n Options extends readonly unknown[],\n>(context: TSESLint.RuleContext<MessageIds, Options>): Services | undefined {\n try {\n return ESLintUtils.getParserServices(context, true);\n } catch {\n return undefined;\n }\n}\n\nfunction declaredBySdkMoney(symbol: ts.Symbol): boolean {\n if (!symbol.getName().includes(\"MoneyBrand\")) {\n return false;\n }\n return (symbol.getDeclarations() ?? []).some((declaration) => {\n const path = declaration.getSourceFile().fileName.replaceAll(\"\\\\\", \"/\");\n return (\n path.endsWith(\"/packages/sdk/src/money.ts\") ||\n /\\/node_modules\\/@zappie\\/sdk\\/.*\\.d\\.ts$/.test(path)\n );\n });\n}\n\nexport function isSdkMoney(\n services: Services | undefined,\n node: TSESTree.Node,\n): boolean {\n if (!services?.program) {\n return false;\n }\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n const type = checker.getTypeAtLocation(tsNode);\n const types = type.isUnionOrIntersection() ? type.types : [type];\n return types.some((part) =>\n part.getProperties().some((property) => declaredBySdkMoney(property)),\n );\n}\n\nexport function currencyStyle(node: TSESTree.Node | undefined): boolean {\n if (node?.type !== \"ObjectExpression\") {\n return false;\n }\n return node.properties.some(\n (property) =>\n property.type === \"Property\" &&\n !property.computed &&\n ((property.key.type === \"Identifier\" && property.key.name === \"style\") ||\n (property.key.type === \"Literal\" && property.key.value === \"style\")) &&\n property.value.type === \"Literal\" &&\n property.value.value === \"currency\",\n );\n}\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n dividedMinorOwner,\n isAmountMinor,\n isMoneyLiteral,\n isSdkMoney,\n memberName,\n parserServices,\n} from \"../money-rule-utils\";\n\nexport const noMoneyLiterals = createRule<\n [],\n \"arithmetic\" | \"literal\" | \"scale\"\n>({\n name: \"no-money-literals\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Use SDK prices and totals in your storefront.\",\n },\n schema: [],\n messages: {\n arithmetic:\n \"Use cart.subtotal, line.lineSubtotal, quote.subtotal, quote.lines totals or quote.total. Let the SDK calculate amounts.\",\n literal:\n \"Render SDK Money with format.money() or <Money> instead of a hard-coded amount.\",\n scale:\n \"Use format.money() to display amountMinor. Currencies use 0, 2, 3 or 4 minor-unit digits.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const services = parserServices(context);\n const arithmetic = (node: TSESTree.Node) =>\n isAmountMinor(node) || isSdkMoney(services, node);\n return {\n Literal(node) {\n if (\n typeof node.value === \"string\" &&\n node.parent.type !== \"ImportDeclaration\" &&\n isMoneyLiteral(node.value)\n ) {\n context.report({ node, messageId: \"literal\" });\n }\n },\n JSXText(node) {\n if (isMoneyLiteral(node.value)) {\n context.report({ node, messageId: \"literal\" });\n }\n },\n BinaryExpression(node) {\n if (![\"+\", \"-\", \"*\", \"/\"].includes(node.operator)) {\n return;\n }\n if (\n dividedMinorOwner(node) &&\n node.parent.type === \"MemberExpression\" &&\n node.parent.object === node &&\n memberName(node.parent) === \"toFixed\"\n ) {\n return;\n }\n if (arithmetic(node.left) || arithmetic(node.right)) {\n context.report({ node, messageId: \"arithmetic\" });\n }\n },\n TemplateLiteral(node) {\n for (const quasi of node.quasis) {\n if (isMoneyLiteral(quasi.value.cooked ?? quasi.value.raw)) {\n context.report({ node: quasi, messageId: \"literal\" });\n }\n }\n },\n CallExpression(node) {\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"toFixed\" &&\n node.arguments[0]?.type === \"Literal\" &&\n node.arguments[0].value === 2 &&\n dividedMinorOwner(node.callee.object)\n ) {\n context.report({ node, messageId: \"scale\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isTrackingEndpoint, staticText } from \"../matchers\";\n\nconst imports = [\n \"react-ga4\",\n \"react-facebook-pixel\",\n \"@vercel/analytics\",\n \"posthog-js\",\n];\n\nfunction imported(value: string): boolean {\n return imports.includes(value) || value.startsWith(\"@segment/\");\n}\n\nexport const noTrackingScripts = createRule<[], \"tracking\">({\n name: \"no-tracking-scripts\",\n meta: {\n type: \"problem\",\n docs: { description: \"Forward analytics through Zappie.\" },\n schema: [],\n messages: {\n tracking:\n \"Configure analytics.forward in zappie.config.ts to forward analytics. Remove the tracking script.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const inspect = (node: TSESTree.Literal | TSESTree.TemplateLiteral) => {\n if (node.parent.type === \"ImportDeclaration\") {\n return;\n }\n const value = staticText(node);\n if (value && isTrackingEndpoint(value)) {\n context.report({ node, messageId: \"tracking\" });\n }\n };\n return {\n Literal: inspect,\n TemplateLiteral: inspect,\n ImportDeclaration(node) {\n if (imported(node.source.value)) {\n context.report({ node: node.source, messageId: \"tracking\" });\n }\n },\n CallExpression(node) {\n const callee = node.callee;\n const direct =\n callee.type === \"Identifier\" && [\"gtag\", \"fbq\"].includes(callee.name);\n const dataLayer =\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"dataLayer\" &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"push\";\n if (direct || dataLayer) {\n context.report({ node, messageId: \"tracking\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst defaults = [\n \"modal\",\n \"sheet\",\n \"drawer\",\n \"dialog\",\n \"item\",\n \"cart\",\n \"cart_sheet\",\n \"cart_edit\",\n \"cart_clear\",\n \"search\",\n \"catalogue_search\",\n \"catalogue_categories\",\n \"product_note\",\n \"modal_id\",\n];\n\nfunction name(node: TSESTree.Node): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"MemberExpression\" && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return undefined;\n}\n\nfunction carriesKey(node: TSESTree.Node, keys: Set<string>): boolean {\n if (node.type === \"Literal\" && typeof node.value === \"string\") {\n return [...keys].some((key) => new RegExp(`[?&]${key}=`).test(node.value));\n }\n if (node.type === \"TemplateLiteral\") {\n return node.quasis.some((part) =>\n carriesKey(\n { ...part, type: \"Literal\", value: part.value.raw } as never,\n keys,\n ),\n );\n }\n if (node.type === \"ObjectExpression\") {\n return node.properties.some(\n (property) =>\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" && keys.has(property.key.name)) ||\n (property.key.type === \"Literal\" &&\n typeof property.key.value === \"string\" &&\n keys.has(property.key.value))),\n );\n }\n return false;\n}\n\nexport const overlayPrimitive = createRule<[{ keys?: string[] }], \"overlay\">({\n name: \"overlay-primitive\",\n meta: {\n type: \"problem\",\n docs: { description: \"Use Overlay to keep dialogs and sheets in the URL.\" },\n schema: [\n {\n type: \"object\",\n properties: { keys: { type: \"array\", items: { type: \"string\" } } },\n additionalProperties: false,\n },\n ],\n messages: {\n overlay:\n \"Use Overlay or useOverlay for dialogs and sheets in the URL. They handle Back, Forward and focus restoration.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const keys = new Set([...defaults, ...(options.keys ?? [])]);\n const searchValues = new Set<string>();\n const derivedFromSearch = (node: TSESTree.Node) => {\n const text = context.sourceCode.getText(node);\n return (\n text.includes(\"useSearchParams(\") ||\n [...searchValues].some((value) =>\n new RegExp(`\\\\b${value}\\\\b`).test(text),\n )\n );\n };\n return {\n VariableDeclarator(node) {\n if (\n node.init?.type === \"CallExpression\" &&\n name(node.init.callee) === \"useSearchParams\"\n ) {\n if (node.id.type === \"Identifier\") {\n searchValues.add(node.id.name);\n } else if (\n node.id.type === \"ArrayPattern\" &&\n node.id.elements[0]?.type === \"Identifier\"\n ) {\n searchValues.add(node.id.elements[0].name);\n }\n } else if (\n node.id.type === \"Identifier\" &&\n node.init &&\n derivedFromSearch(node.init)\n ) {\n searchValues.add(node.id.name);\n }\n },\n CallExpression(node) {\n const callee = name(node.callee);\n const navigation =\n callee === \"setSearchParams\" ||\n callee === \"navigate\" ||\n callee === \"pushState\" ||\n callee === \"replaceState\" ||\n (node.callee.type === \"CallExpression\" &&\n name(node.callee.callee) === \"useNavigate\");\n if (\n navigation &&\n node.arguments.some(\n (argument) =>\n argument.type !== \"SpreadElement\" && carriesKey(argument, keys),\n )\n ) {\n context.report({ node, messageId: \"overlay\" });\n }\n },\n JSXOpeningElement(node) {\n const dialog =\n node.name.type === \"JSXIdentifier\" && node.name.name === \"dialog\";\n const role = node.attributes.some(\n (attribute) =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === \"role\" &&\n attribute.value?.type === \"Literal\" &&\n attribute.value.value === \"dialog\",\n );\n const open = node.attributes.find(\n (attribute) =>\n attribute.type === \"JSXAttribute\" && attribute.name.name === \"open\",\n );\n if (\n (dialog || role) &&\n open?.type === \"JSXAttribute\" &&\n open.value?.type === \"JSXExpressionContainer\" &&\n derivedFromSearch(open.value.expression)\n ) {\n context.report({ node, messageId: \"overlay\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\n\ntype Fixer = TSESLint.RuleFixer;\ntype Fix = TSESLint.RuleFix;\ntype Source = Readonly<TSESLint.SourceCode>;\n\nfunction sdkImport(\n program: TSESTree.Program,\n): TSESTree.ImportDeclaration | undefined {\n return program.body.find(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === \"ImportDeclaration\" && node.source.value === \"@zappie/sdk\",\n );\n}\n\nfunction importedFormat(\n declaration: TSESTree.ImportDeclaration | undefined,\n): TSESTree.ImportSpecifier | undefined {\n return declaration?.specifiers.find(\n (specifier): specifier is TSESTree.ImportSpecifier =>\n specifier.type === \"ImportSpecifier\" &&\n specifier.imported.type === \"Identifier\" &&\n specifier.imported.name === \"format\",\n );\n}\n\nfunction importFix(\n fixer: Fixer,\n source: Source,\n program: TSESTree.Program,\n local: string,\n): Fix | undefined {\n const declaration = sdkImport(program);\n if (importedFormat(declaration)?.local.name === local) {\n return undefined;\n }\n const importedName = local === \"format\" ? \"format\" : `format as ${local}`;\n const named = declaration?.specifiers.filter(\n (specifier) => specifier.type === \"ImportSpecifier\",\n );\n if (declaration && declaration.importKind !== \"type\" && named?.length) {\n const last = named.at(-1);\n if (last) {\n return fixer.insertTextAfter(last, `, ${importedName}`);\n }\n }\n const first = program.body[0];\n const text = `import { ${importedName} } from \"@zappie/sdk\";\\n`;\n return first\n ? fixer.insertTextBefore(first, text)\n : fixer.insertTextAfter(program, text);\n}\n\nexport function formatFixes(\n fixer: Fixer,\n source: Source,\n program: TSESTree.Program,\n replace: TSESTree.Node,\n owner: TSESTree.Expression,\n local: string,\n): Fix[] {\n const fixes = [\n fixer.replaceText(replace, `${local}.money(${source.getText(owner)})`),\n ];\n const addition = importFix(fixer, source, program, local);\n if (addition) {\n fixes.push(addition);\n }\n return fixes;\n}\n\nexport function formatBinding(\n source: Source,\n program: TSESTree.Program,\n node: TSESTree.Node,\n): \"available\" | \"conflict\" | \"imported\" {\n if (importedFormat(sdkImport(program))) {\n return \"imported\";\n }\n let scope: TSESLint.Scope.Scope | null = source.getScope(node);\n while (scope) {\n if (scope.variables.some((variable) => variable.name === \"format\")) {\n return \"conflict\";\n }\n scope = scope.upper;\n }\n return \"available\";\n}\n\nexport function importedFormatLocal(\n program: TSESTree.Program,\n): string | undefined {\n return importedFormat(sdkImport(program))?.local.name;\n}\n\nexport function availableAlias(source: Source, node: TSESTree.Node): string {\n const names = new Set<string>();\n let scope: TSESLint.Scope.Scope | null = source.getScope(node);\n while (scope) {\n for (const variable of scope.variables) {\n names.add(variable.name);\n }\n scope = scope.upper;\n }\n for (const name of [\n \"zappieFormat\",\n \"zappieMoneyFormat\",\n \"formatMoneyValue\",\n ]) {\n if (!names.has(name)) {\n return name;\n }\n }\n return \"zappieSdkFormat\";\n}\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n availableAlias,\n formatBinding,\n formatFixes,\n importedFormatLocal,\n} from \"../format-fix\";\nimport {\n currencyStyle,\n dividedMinorOwner,\n isAmountMinor,\n isSdkMoney,\n memberName,\n parserServices,\n} from \"../money-rule-utils\";\n\ntype Message = \"currency\" | \"direct\" | \"minor\" | \"useAlias\";\n\nfunction intlNumberFormat(node: TSESTree.Node): boolean {\n const callee =\n node.type === \"NewExpression\" || node.type === \"CallExpression\"\n ? node.callee\n : undefined;\n return Boolean(\n callee?.type === \"MemberExpression\" &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"Intl\" &&\n memberName(callee) === \"NumberFormat\",\n );\n}\n\nfunction formattedMinor(node: TSESTree.CallExpression) {\n if (node.callee.type !== \"MemberExpression\") {\n return undefined;\n }\n if (memberName(node.callee) === \"toLocaleString\") {\n return dividedMinorOwner(node.callee.object);\n }\n if (memberName(node.callee) === \"format\") {\n return dividedMinorOwner(node.arguments[0] ?? node);\n }\n return undefined;\n}\n\nfunction formatsDividedMinor(node: TSESTree.NewExpression): boolean {\n const member = node.parent;\n const call = member.type === \"MemberExpression\" ? member.parent : undefined;\n return Boolean(\n call?.type === \"CallExpression\" &&\n call.callee === member &&\n memberName(member) === \"format\" &&\n dividedMinorOwner(call.arguments[0] ?? call),\n );\n}\n\nexport const useFormatMoney = createRule<[], Message>({\n name: \"use-format-money\",\n meta: {\n type: \"problem\",\n docs: { description: \"Format SDK Money with format.money() or <Money>.\" },\n fixable: \"code\",\n hasSuggestions: true,\n schema: [],\n messages: {\n currency:\n \"Use format.money(). Intl currency defaults can differ from the API minor-unit digits.\",\n direct:\n \"Render SDK Money with format.money(value) or <Money value={value}>.\",\n minor:\n \"Use format.money(value). Dividing by 100 gives the wrong amount for currencies with 0, 3 or 4 minor-unit digits.\",\n useAlias:\n \"Import format with an unused alias and format this Money value.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const source = context.sourceCode;\n const services = parserServices(context);\n let program: TSESTree.Program;\n const reportFix = (\n node: TSESTree.Node,\n replace: TSESTree.Node,\n owner: TSESTree.Expression,\n ) => {\n const data = { node, messageId: \"minor\" as const };\n const binding = formatBinding(source, program, node);\n if (binding === \"conflict\") {\n const alias = availableAlias(source, node);\n context.report({\n ...data,\n suggest: [\n {\n messageId: \"useAlias\",\n fix: (fixer) =>\n formatFixes(fixer, source, program, replace, owner, alias),\n },\n ],\n });\n return;\n }\n context.report({\n ...data,\n fix: (fixer) => {\n const local = importedFormatLocal(program) ?? \"format\";\n return formatFixes(fixer, source, program, replace, owner, local);\n },\n });\n };\n const inspectFormatter = (node: TSESTree.CallExpression) => {\n const owner = formattedMinor(node);\n if (owner) {\n reportFix(node, node, owner);\n return true;\n }\n return false;\n };\n return {\n Program(node) {\n program = node;\n },\n NewExpression(node) {\n if (\n intlNumberFormat(node) &&\n currencyStyle(node.arguments[1]) &&\n !formatsDividedMinor(node)\n ) {\n context.report({ node, messageId: \"currency\" });\n }\n },\n CallExpression(node) {\n if (inspectFormatter(node)) {\n return;\n }\n if (intlNumberFormat(node) && currencyStyle(node.arguments[1])) {\n context.report({ node, messageId: \"currency\" });\n return;\n }\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"toLocaleString\" &&\n node.arguments.some(currencyStyle)\n ) {\n context.report({ node, messageId: \"currency\" });\n }\n },\n TemplateLiteral(node) {\n for (const expression of node.expressions) {\n const owner = dividedMinorOwner(expression);\n if (owner) {\n reportFix(expression, expression, owner);\n } else if (isAmountMinor(expression)) {\n context.report({ node: expression, messageId: \"minor\" });\n }\n }\n },\n JSXExpressionContainer(node) {\n if (\n node.parent.type === \"JSXAttribute\" ||\n node.expression.type === \"JSXEmptyExpression\"\n ) {\n return;\n }\n if (\n dividedMinorOwner(node.expression) ||\n isAmountMinor(node.expression)\n ) {\n context.report({ node: node.expression, messageId: \"minor\" });\n } else if (isSdkMoney(services, node.expression)) {\n context.report({ node: node.expression, messageId: \"direct\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\ntype Kind = \"add\" | \"checkout\" | \"signin\";\nconst marker: Record<Kind, { name: string; text: string }> = {\n add: { name: \"data-zappie-action\", text: 'data-zappie-action=\"add-to-cart\"' },\n checkout: { name: \"data-zappie-checkout\", text: \"data-zappie-checkout\" },\n signin: { name: \"data-zappie-signin\", text: \"data-zappie-signin\" },\n};\n\nfunction handlerKind(\n source: Readonly<TSESLint.SourceCode>,\n node: TSESTree.JSXAttribute,\n): Kind | undefined {\n const text = node.value ? source.getText(node.value) : \"\";\n if (/\\b(?:cart(?:\\.actions)?\\.add)\\s*\\(/.test(text)) {\n return \"add\";\n }\n if (/\\b(?:checkout|createCheckout\\([^)]*\\))\\.open\\s*\\(/.test(text)) {\n return \"checkout\";\n }\n if (/\\b(?:auth|createCustomer\\([^)]*\\))\\.signIn\\s*\\(/.test(text)) {\n return \"signin\";\n }\n return undefined;\n}\n\nexport const verifyMarkers = createRule<[], \"marker\">({\n name: \"verify-markers\",\n meta: {\n type: \"suggestion\",\n docs: { description: \"Mark shopping actions so verify can find them.\" },\n fixable: \"code\",\n schema: [],\n messages: {\n marker: \"Add {{marker}} so zappie verify can find this shopping action.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n JSXOpeningElement(node) {\n const click = node.attributes.find(\n (attribute): attribute is TSESTree.JSXAttribute =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === \"onClick\",\n );\n if (!click) {\n return;\n }\n const kind = handlerKind(context.sourceCode, click);\n if (\n !kind ||\n node.attributes.some(\n (attribute) =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === marker[kind].name,\n )\n ) {\n return;\n }\n context.report({\n node: click,\n messageId: \"marker\",\n data: { marker: marker[kind].text },\n fix: (fixer) =>\n fixer.insertTextAfter(node.name, ` ${marker[kind].text}`),\n });\n },\n };\n },\n});\n"],"mappings":"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","names":["parser","jsxA11y","reactHooks","ESLintUtils","createRule","name","cartName","storageKey","calleeName","node","variableName","cartInitial","value","cartViaSdk","createRule","context","method","key","id","call","minimatch","endpointPattern","trackingPattern","paymentHostPattern","staticText","node","part","jsxAttribute","name","attribute","jsxValue","isZappieEndpoint","value","isTrackingEndpoint","isInternalSdkImport","isServerFile","filename","patterns","path","pattern","autocomplete","paymentField","providerImport","value","checkoutViaSdk","createRule","context","node","tag","auto","jsxValue","jsxAttribute","name","id","target","cardInput","providerTarget","paymentHostPattern","mappedBy","node","current","parent","firstOnly","source","callback","text","index","imagePriority","createRule","context","images","priority","jsxAttribute","disabled","targetsItem","attribute","expression","target","jsxValue","itemLinkPrefetch","createRule","context","links","node","item","jsxAttribute","defaults","authValue","browserStorage","globals","isStorage","node","memberKey","staticText","authImport","value","noCustomAuth","createRule","context","options","server","isServerFile","kind","type","jsxValue","jsxAttribute","auto","first","key","cookie","message","value","noDirectApi","createRule","context","inspect","node","staticText","isZappieEndpoint","isInternalSdkImport","react","conventionalNamespace","noLazyOverlay","createRule","context","lazyNames","suspenseNames","namespaces","fromNamespace","object","property","wanted","node","specifier","imported","callee","name","ESLintUtils","moneyLiteralPattern","isMoneyLiteral","value","memberName","node","isAmountMinor","amountOwner","dividedMinorOwner","parserServices","context","declaredBySdkMoney","symbol","declaration","path","isSdkMoney","services","checker","tsNode","type","part","property","currencyStyle","noMoneyLiterals","createRule","context","services","parserServices","arithmetic","node","isAmountMinor","isSdkMoney","isMoneyLiteral","dividedMinorOwner","memberName","quasi","imports","imported","value","noTrackingScripts","createRule","context","inspect","node","staticText","isTrackingEndpoint","callee","direct","dataLayer","defaults","name","node","carriesKey","keys","key","part","property","overlayPrimitive","createRule","context","options","searchValues","derivedFromSearch","text","value","callee","argument","dialog","role","attribute","open","sdkImport","program","node","importedFormat","declaration","specifier","importFix","fixer","source","local","importedName","named","last","first","text","formatFixes","replace","owner","fixes","addition","formatBinding","scope","variable","importedFormatLocal","availableAlias","names","name","intlNumberFormat","node","callee","memberName","formattedMinor","dividedMinorOwner","formatsDividedMinor","member","call","useFormatMoney","createRule","context","source","services","parserServices","program","reportFix","replace","owner","data","formatBinding","alias","availableAlias","fixer","formatFixes","local","importedFormatLocal","inspectFormatter","currencyStyle","expression","isAmountMinor","isSdkMoney","marker","handlerKind","source","node","text","verifyMarkers","createRule","context","click","attribute","kind","fixer","rules","noDirectApi","noTrackingScripts","checkoutViaSdk","noCustomAuth","noMoneyLiterals","useFormatMoney","overlayPrimitive","cartViaSdk","verifyMarkers","itemLinkPrefetch","imagePriority","noLazyOverlay","enabled","name","strictRules","plugin","base","parser","jsxA11y","reactHooks","searchDialogs","typed","src_default","plugin"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/create-rule.ts","../src/rules/cart-via-sdk.ts","../src/matchers.ts","../src/rules/checkout-via-sdk.ts","../src/rules/image-priority.ts","../src/rules/item-link-prefetch.ts","../src/rules/no-custom-auth.ts","../src/rules/no-direct-api.ts","../src/rules/no-eval.ts","../src/rules/no-external-scripts.ts","../src/rules/no-lazy-overlay.ts","../src/money-rule-utils.ts","../src/rules/no-money-literals.ts","../src/rules/no-server-env-in-client.ts","../src/rules/no-tracking-scripts.ts","../src/rules/no-unlisted-fetch.ts","../src/rules/no-unsafe-html.ts","../src/rules/overlay-primitive.ts","../src/rules/safe-external-links.ts","../src/format-fix.ts","../src/rules/use-format-money.ts","../src/rules/verify-markers.ts"],"sourcesContent":["import parser from \"@typescript-eslint/parser\";\nimport jsxA11y from \"eslint-plugin-jsx-a11y\";\nimport reactHooks from \"eslint-plugin-react-hooks\";\nimport { cartViaSdk } from \"./rules/cart-via-sdk\";\nimport { checkoutViaSdk } from \"./rules/checkout-via-sdk\";\nimport { imagePriority } from \"./rules/image-priority\";\nimport { itemLinkPrefetch } from \"./rules/item-link-prefetch\";\nimport { noCustomAuth } from \"./rules/no-custom-auth\";\nimport { noDirectApi } from \"./rules/no-direct-api\";\nimport { noEval } from \"./rules/no-eval\";\nimport { noExternalScripts } from \"./rules/no-external-scripts\";\nimport { noLazyOverlay } from \"./rules/no-lazy-overlay\";\nimport { noMoneyLiterals } from \"./rules/no-money-literals\";\nimport { noServerEnvInClient } from \"./rules/no-server-env-in-client\";\nimport { noTrackingScripts } from \"./rules/no-tracking-scripts\";\nimport {\n noUnlistedFetch,\n noUnlistedFetchDynamic,\n} from \"./rules/no-unlisted-fetch\";\nimport { noUnsafeHtml } from \"./rules/no-unsafe-html\";\nimport { overlayPrimitive } from \"./rules/overlay-primitive\";\nimport { safeExternalLinks } from \"./rules/safe-external-links\";\nimport { useFormatMoney } from \"./rules/use-format-money\";\nimport { verifyMarkers } from \"./rules/verify-markers\";\n\nconst rules = {\n \"no-direct-api\": noDirectApi,\n \"no-tracking-scripts\": noTrackingScripts,\n \"checkout-via-sdk\": checkoutViaSdk,\n \"no-custom-auth\": noCustomAuth,\n \"no-money-literals\": noMoneyLiterals,\n \"use-format-money\": useFormatMoney,\n \"overlay-primitive\": overlayPrimitive,\n \"cart-via-sdk\": cartViaSdk,\n \"verify-markers\": verifyMarkers,\n \"item-link-prefetch\": itemLinkPrefetch,\n \"image-priority\": imagePriority,\n \"no-lazy-overlay\": noLazyOverlay,\n \"no-unsafe-html\": noUnsafeHtml,\n \"no-eval\": noEval,\n \"no-external-scripts\": noExternalScripts,\n \"safe-external-links\": safeExternalLinks,\n \"no-unlisted-fetch\": noUnlistedFetch,\n \"no-unlisted-fetch-dynamic\": noUnlistedFetchDynamic,\n \"no-server-env-in-client\": noServerEnvInClient,\n};\n\nexport type ZappieFlatConfig = Record<string, unknown>;\n\nconst enabled: Record<string, unknown> = Object.fromEntries(\n Object.keys(rules).map((name) => [\n `zappie/${name}`,\n name === \"verify-markers\" || name === \"no-unlisted-fetch-dynamic\"\n ? \"warn\"\n : \"error\",\n ]),\n);\nconst strictRules = {\n ...enabled,\n \"zappie/verify-markers\": \"error\",\n \"zappie/no-unlisted-fetch-dynamic\": [\"error\", { dynamicOnly: true }],\n};\nenabled[\"zappie/no-unlisted-fetch-dynamic\"] = [\"warn\", { dynamicOnly: true }];\n\nconst plugin = {\n meta: { name: \"eslint-plugin-zappie\", version: \"0.0.0\" },\n rules,\n configs: {} as Record<string, ZappieFlatConfig[]>,\n};\n\nconst base = {\n name: \"zappie/rules\",\n files: [\"**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}\"],\n languageOptions: { parser },\n plugins: { zappie: plugin, \"jsx-a11y\": jsxA11y, \"react-hooks\": reactHooks },\n rules: {\n ...jsxA11y.flatConfigs.recommended.rules,\n \"react-hooks/rules-of-hooks\": \"error\",\n ...enabled,\n },\n};\nconst searchDialogs = {\n name: \"zappie/search-dialogs\",\n files: [\"**/search/**/*.{jsx,tsx}\", \"**/*search*.{jsx,tsx}\"],\n rules: { \"jsx-a11y/no-autofocus\": \"off\" },\n};\n\n// JavaScript files (eslint.config.mjs among them) usually sit outside the\n// tsconfig, where the project service refuses to parse them. They get the\n// syntactic money checks only.\nconst typed = {\n name: \"zappie/typed\",\n files: [\"**/*.{ts,tsx,mts,cts}\"],\n languageOptions: { parserOptions: { projectService: true } },\n};\n\nplugin.configs = {\n recommended: [base, typed, searchDialogs],\n strict: [\n { ...base, rules: { ...base.rules, ...strictRules } },\n typed,\n searchDialogs,\n ],\n \"zappie-internal\": [\n base,\n typed,\n searchDialogs,\n {\n name: \"zappie/internal-sdk\",\n files: [\"packages/sdk/src/**/*.{ts,tsx}\"],\n rules: {\n \"zappie/no-direct-api\": \"off\",\n \"zappie/no-unlisted-fetch\": \"off\",\n \"zappie/no-unlisted-fetch-dynamic\": \"off\",\n },\n },\n {\n name: \"zappie/internal-map-loader\",\n files: [\"packages/foundations/src/branch-map/loader.ts\"],\n rules: { \"zappie/no-external-scripts\": \"off\" },\n },\n {\n name: \"zappie/internal-auth-ui\",\n files: [\"packages/foundations/src/otp/**/*.{ts,tsx}\"],\n rules: { \"zappie/no-custom-auth\": \"off\" },\n },\n {\n name: \"zappie/internal-money\",\n files: [\n \"packages/sdk/src/money.ts\",\n \"packages/sdk/src/internal/money-math.ts\",\n // Its own arithmetic: importing money-math here splits it out of the core bundle.\n \"packages/sdk/src/cart/branch.ts\",\n // The starting-price rule compares and adds variant and choice prices.\n \"packages/sdk/src/pricing/index.ts\",\n ],\n rules: {\n \"zappie/no-money-literals\": \"off\",\n \"zappie/use-format-money\": \"off\",\n },\n },\n {\n name: \"zappie/internal-overlay\",\n files: [\"packages/foundations/src/overlay/**/*.{ts,tsx}\"],\n rules: { \"zappie/overlay-primitive\": \"off\" },\n },\n {\n name: \"zappie/internal-overlay-tests\",\n files: [\n \"themes/base/src/cart/cart-review.test.tsx\",\n \"themes/base/src/cart/core-cart.test.tsx\",\n \"themes/base/src/cart/fulfilment.test.tsx\",\n \"themes/base/src/cart/theme-class.test.tsx\",\n ],\n rules: { \"zappie/overlay-primitive\": \"off\" },\n },\n ],\n};\n\nexport {\n cartViaSdk,\n checkoutViaSdk,\n imagePriority,\n itemLinkPrefetch,\n noCustomAuth,\n noDirectApi,\n noEval,\n noExternalScripts,\n noLazyOverlay,\n noMoneyLiterals,\n noServerEnvInClient,\n noTrackingScripts,\n noUnlistedFetch,\n noUnsafeHtml,\n overlayPrimitive,\n safeExternalLinks,\n useFormatMoney,\n verifyMarkers,\n};\nexport default plugin;\n","import { ESLintUtils } from \"@typescript-eslint/utils\";\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) => `https://docs.zappie.ai/lint/${name}`,\n);\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst cartName = /^(cart|basket|bag)(Items|Lines|Count|Total)?$/i;\nconst storageKey = /cart|basket|bag/i;\n\nfunction calleeName(node: TSESTree.Node): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"MemberExpression\" && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return undefined;\n}\n\nfunction variableName(node: TSESTree.BindingName): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n return node.type === \"ArrayPattern\" && node.elements[0]?.type === \"Identifier\"\n ? node.elements[0].name\n : undefined;\n}\n\nfunction cartInitial(node: TSESTree.CallExpression): boolean {\n const value =\n calleeName(node.callee) === \"useReducer\"\n ? node.arguments[1]\n : node.arguments[0];\n return Boolean(\n value &&\n value.type !== \"SpreadElement\" &&\n (value.type === \"ArrayExpression\" ||\n (value.type === \"Literal\" && typeof value.value === \"number\")),\n );\n}\n\nexport const cartViaSdk = createRule<[], \"cart\">({\n name: \"cart-via-sdk\",\n meta: {\n type: \"problem\",\n docs: { description: \"Keep your cart state in the SDK.\" },\n schema: [],\n messages: {\n cart: \"Use useCart(), useCartLine() or the SDK client cart. Keep cart state and storage in the SDK.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n CallExpression(node) {\n const method = calleeName(node.callee);\n const key = node.arguments[0];\n if (\n [\"getItem\", \"setItem\", \"removeItem\", \"open\"].includes(method ?? \"\") &&\n key?.type === \"Literal\" &&\n typeof key.value === \"string\" &&\n storageKey.test(key.value)\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n },\n VariableDeclarator(node) {\n if (node.init?.type !== \"CallExpression\") {\n return;\n }\n const id = variableName(node.id);\n const call = calleeName(node.init.callee);\n if (\n id &&\n cartName.test(id) &&\n [\"useState\", \"useReducer\", \"createContext\"].includes(call ?? \"\") &&\n cartInitial(node.init)\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n if (\n id &&\n /cart/i.test(id) &&\n [\"create\", \"createStore\", \"atom\"].includes(call ?? \"\")\n ) {\n context.report({ node, messageId: \"cart\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { minimatch } from \"minimatch\";\n\nconst endpointPattern =\n /(?:api|accounts|checkout|edge)\\.zappie\\.ai|\\/storefront\\/v1|\\/customer\\/graphql/i;\nconst trackingPattern =\n /googletagmanager\\.com|google-analytics\\.com|connect\\.facebook\\.net|analytics\\.tiktok\\.com|static\\.hotjar\\.com|cdn\\.segment\\.com|plausible\\.io|clarity\\.ms/i;\n\nexport const paymentHostPattern =\n /checkout\\.paystack\\.com|flutterwave|(?:^|\\.)stripe\\.com/i;\n\nexport function staticText(node: TSESTree.Node | null): string | undefined {\n if (node?.type === \"Literal\" && typeof node.value === \"string\") {\n return node.value;\n }\n if (node?.type === \"TemplateLiteral\") {\n return node.quasis\n .map((part) => part.value.cooked ?? part.value.raw)\n .join(\"\");\n }\n return undefined;\n}\n\nexport function memberName(\n node: TSESTree.MemberExpression,\n): string | undefined {\n return !node.computed && node.property.type === \"Identifier\"\n ? node.property.name\n : staticText(node.property);\n}\n\nexport function unwrapExpression(\n node: TSESTree.Expression,\n): TSESTree.Expression {\n return node.type === \"TSAsExpression\" ||\n node.type === \"TSTypeAssertion\" ||\n node.type === \"TSNonNullExpression\"\n ? unwrapExpression(node.expression)\n : node;\n}\n\nexport function findVariable(\n source: Readonly<TSESLint.SourceCode>,\n node: TSESTree.Node,\n name: string,\n): TSESLint.Scope.Variable | undefined {\n for (\n let scope: TSESLint.Scope.Scope | null = source.getScope(node);\n scope;\n scope = scope.upper\n ) {\n const found = scope.set.get(name);\n if (found) return found;\n }\n}\n\nexport function jsxAttribute(\n node: TSESTree.JSXOpeningElement,\n name: string,\n): TSESTree.JSXAttribute | undefined {\n return node.attributes.find(\n (attribute): attribute is TSESTree.JSXAttribute =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.type === \"JSXIdentifier\" &&\n attribute.name.name.toLowerCase() === name.toLowerCase(),\n );\n}\n\nexport function jsxValue(node?: TSESTree.JSXAttribute): string | undefined {\n if (!node?.value) {\n return undefined;\n }\n if (node.value.type === \"Literal\") {\n return typeof node.value.value === \"string\" ? node.value.value : undefined;\n }\n if (node.value.type === \"JSXExpressionContainer\") {\n return staticText(node.value.expression);\n }\n return undefined;\n}\n\nexport const isZappieEndpoint = (value: string) => endpointPattern.test(value);\nexport const isTrackingEndpoint = (value: string) =>\n trackingPattern.test(value);\nexport const isInternalSdkImport = (value: string) =>\n value === \"@zappie/sdk/internal\" || value.startsWith(\"@zappie/sdk/internal/\");\n\nexport function isServerFile(filename: string, patterns: string[]): boolean {\n const path = filename.replaceAll(\"\\\\\", \"/\");\n return patterns.some((pattern) =>\n minimatch(path, pattern, { matchBase: true }),\n );\n}\n","import { createRule } from \"../create-rule\";\nimport { jsxAttribute, jsxValue, paymentHostPattern } from \"../matchers\";\n\nconst autocomplete = new Set([\"cc-number\", \"cc-exp\", \"cc-csc\", \"cc-name\"]);\nconst paymentField = /card.?number|cvv|cvc|expiry/i;\n\nfunction providerImport(value: string): boolean {\n return (\n value.startsWith(\"@paystack/\") ||\n value === \"react-paystack\" ||\n value === \"flutterwave-react-v3\" ||\n value.startsWith(\"@stripe/\")\n );\n}\n\nexport const checkoutViaSdk = createRule<[], \"checkout\">({\n name: \"checkout-via-sdk\",\n meta: {\n type: \"problem\",\n docs: { description: \"Open checkout through the Zappie SDK.\" },\n schema: [],\n messages: {\n checkout:\n \"Use createCheckout().open() from @zappie/sdk/checkout. Let checkout collect payment details.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n ImportDeclaration(node) {\n if (providerImport(node.source.value)) {\n context.report({ node: node.source, messageId: \"checkout\" });\n }\n },\n JSXOpeningElement(node) {\n const tag =\n node.name.type === \"JSXIdentifier\"\n ? node.name.name.toLowerCase()\n : \"\";\n const auto = jsxValue(jsxAttribute(node, \"autocomplete\"));\n const name = jsxValue(jsxAttribute(node, \"name\"));\n const id = jsxValue(jsxAttribute(node, \"id\"));\n const target = jsxValue(\n jsxAttribute(node, tag === \"iframe\" ? \"src\" : \"href\"),\n );\n const cardInput =\n tag === \"input\" &&\n ((auto !== undefined && autocomplete.has(auto.toLowerCase())) ||\n (name !== undefined && paymentField.test(name)) ||\n (id !== undefined && paymentField.test(id)));\n const providerTarget =\n (tag === \"iframe\" || tag === \"a\") &&\n target !== undefined &&\n paymentHostPattern.test(target);\n if (cardInput || providerTarget) {\n context.report({ node, messageId: \"checkout\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { jsxAttribute } from \"../matchers\";\n\ntype Callback = TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression;\n\nfunction mappedBy(node: TSESTree.Node) {\n let current: TSESTree.Node | undefined = node;\n while (current?.parent) {\n const parent: TSESTree.Node = current.parent;\n if (\n (parent.type === \"ArrowFunctionExpression\" ||\n parent.type === \"FunctionExpression\") &&\n parent.parent?.type === \"CallExpression\" &&\n parent.parent.callee.type === \"MemberExpression\" &&\n parent.parent.callee.property.type === \"Identifier\" &&\n parent.parent.callee.property.name === \"map\"\n ) {\n return parent;\n }\n current = parent;\n }\n}\n\nfunction firstOnly(source: Readonly<TSESLint.SourceCode>, callback: Callback) {\n const text = source.getText(callback.parent);\n if (/\\.slice\\(\\s*0\\s*,\\s*1\\s*\\)\\.map/.test(text)) {\n return true;\n }\n const index = callback.params[1];\n if (index?.type !== \"Identifier\") {\n return false;\n }\n const first = new RegExp(`${index.name}\\\\s*(?:={2,3}\\\\s*0|<\\\\s*1)`);\n return first.test(source.getText(callback));\n}\n\nexport const imagePriority = createRule<[], \"mapped\" | \"routeLimit\">({\n name: \"image-priority\",\n meta: {\n type: \"problem\",\n docs: { description: \"Reserve priority for the first phone screen.\" },\n schema: [],\n messages: {\n mapped: \"Remove priority from repeated images or limit it to the first.\",\n routeLimit: \"Keep no more than two priority images in a route.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const images: TSESTree.JSXOpeningElement[] = [];\n return {\n JSXOpeningElement(node) {\n const priority = jsxAttribute(node, \"priority\");\n const disabled =\n priority?.value?.type === \"JSXExpressionContainer\" &&\n priority.value.expression.type === \"Literal\" &&\n priority.value.expression.value === false;\n if (\n node.name.type !== \"JSXIdentifier\" ||\n node.name.name !== \"Image\" ||\n !priority ||\n disabled\n ) {\n return;\n }\n images.push(node);\n const callback = mappedBy(node);\n if (callback && !firstOnly(context.sourceCode, callback)) {\n context.report({ node: priority, messageId: \"mapped\" });\n }\n },\n \"Program:exit\"() {\n if (\n /(?:^|\\/)routes?\\/[^/]+\\.[jt]sx$/.test(\n context.filename.replaceAll(\"\\\\\", \"/\"),\n ) &&\n images.length > 2\n ) {\n for (const node of images.slice(2)) {\n context.report({ node, messageId: \"routeLimit\" });\n }\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { jsxAttribute, jsxValue } from \"../matchers\";\n\nfunction targetsItem(attribute?: TSESTree.JSXAttribute) {\n const expression =\n attribute?.value?.type === \"JSXExpressionContainer\"\n ? attribute.value.expression\n : undefined;\n if (\n expression?.type === \"CallExpression\" &&\n expression.callee.type === \"Identifier\" &&\n expression.callee.name === \"itemHref\"\n ) {\n return true;\n }\n const target = jsxValue(attribute);\n return target !== undefined && /^\\/item(?:\\/|$)/.test(target);\n}\n\nexport const itemLinkPrefetch = createRule<[], \"prefetch\">({\n name: \"item-link-prefetch\",\n meta: {\n type: \"problem\",\n docs: { description: \"Prefetch item routes before shoppers tap.\" },\n schema: [],\n messages: {\n prefetch: 'Add prefetch=\"viewport\" so this item route is ready to tap.',\n },\n },\n defaultOptions: [],\n create(context) {\n const links = new Set<string>();\n return {\n ImportDeclaration(node) {\n if (![\"react-router\", \"react-router-dom\"].includes(node.source.value)) {\n return;\n }\n for (const item of node.specifiers) {\n if (\n item.type === \"ImportSpecifier\" &&\n item.imported.type === \"Identifier\" &&\n [\"Link\", \"NavLink\"].includes(item.imported.name)\n ) {\n links.add(item.local.name);\n }\n }\n },\n JSXOpeningElement(node) {\n if (\n node.name.type === \"JSXIdentifier\" &&\n links.has(node.name.name) &&\n targetsItem(jsxAttribute(node, \"to\")) &&\n !jsxAttribute(node, \"prefetch\")\n ) {\n context.report({ node, messageId: \"prefetch\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isServerFile, jsxAttribute, jsxValue, staticText } from \"../matchers\";\n\ntype Options = [{ serverFiles?: string[] }];\nconst defaults = [\"**/*.server.*\", \"**/server/**\"];\nconst authValue = /otp|verify.?code|access.?token|refresh.?token|id.?token/i;\nconst browserStorage = new Set([\"localStorage\", \"sessionStorage\"]);\nconst globals = new Set([\"window\", \"globalThis\", \"self\"]);\n\nfunction isStorage(node: TSESTree.Node): boolean {\n if (node.type === \"Identifier\") {\n return browserStorage.has(node.name);\n }\n return (\n node.type === \"MemberExpression\" &&\n !node.computed &&\n node.object.type === \"Identifier\" &&\n globals.has(node.object.name) &&\n node.property.type === \"Identifier\" &&\n browserStorage.has(node.property.name)\n );\n}\n\nfunction memberKey(node: TSESTree.MemberExpression): string | undefined {\n if (!node.computed && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return staticText(node.property);\n}\n\nfunction authImport(value: string): \"server\" | \"all\" | undefined {\n if (value === \"jose\" || value === \"jsonwebtoken\") {\n return \"server\";\n }\n if (\n value === \"next-auth\" ||\n value.startsWith(\"next-auth/\") ||\n value.startsWith(\"@auth/\") ||\n value === \"better-auth\" ||\n value.startsWith(\"better-auth/\") ||\n value === \"lucia\" ||\n value.startsWith(\"@clerk/\") ||\n value === \"firebase/auth\" ||\n value.startsWith(\"@supabase/auth-\")\n ) {\n return \"all\";\n }\n return undefined;\n}\n\nexport const noCustomAuth = createRule<Options, \"auth\">({\n name: \"no-custom-auth\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Sign customers in through the Zappie SDK.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n serverFiles: {\n type: \"array\",\n items: { type: \"string\" },\n uniqueItems: true,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n auth: \"Use createCustomer().signIn() from @zappie/sdk/customer to sign customers in.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const server = isServerFile(\n context.filename,\n options.serverFiles ?? defaults,\n );\n return {\n ImportDeclaration(node) {\n const kind = authImport(node.source.value);\n if (kind === \"all\" || (kind === \"server\" && !server)) {\n context.report({ node: node.source, messageId: \"auth\" });\n }\n },\n JSXOpeningElement(node) {\n if (\n node.name.type !== \"JSXIdentifier\" ||\n node.name.name.toLowerCase() !== \"input\"\n ) {\n return;\n }\n const type = jsxValue(jsxAttribute(node, \"type\"));\n const auto = jsxValue(jsxAttribute(node, \"autocomplete\"));\n if (\n type?.toLowerCase() === \"password\" ||\n (auto !== undefined &&\n [\"one-time-code\", \"current-password\", \"new-password\"].includes(\n auto.toLowerCase(),\n ))\n ) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n CallExpression(node) {\n if (\n node.callee.type !== \"MemberExpression\" ||\n !isStorage(node.callee.object)\n ) {\n return;\n }\n const first = node.arguments[0];\n const value =\n first?.type === \"Identifier\" ? first.name : staticText(first ?? null);\n if (value && authValue.test(value)) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n AssignmentExpression(node) {\n if (\n node.left.type === \"MemberExpression\" &&\n isStorage(node.left.object)\n ) {\n const key = memberKey(node.left);\n if (key && authValue.test(key)) {\n context.report({ node, messageId: \"auth\" });\n }\n return;\n }\n const cookie =\n node.left.type === \"MemberExpression\" &&\n node.left.object.type === \"Identifier\" &&\n node.left.object.name === \"document\" &&\n node.left.property.type === \"Identifier\" &&\n node.left.property.name === \"cookie\";\n const value = staticText(node.right);\n if (cookie && value && authValue.test(value)) {\n context.report({ node, messageId: \"auth\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isInternalSdkImport, isZappieEndpoint, staticText } from \"../matchers\";\n\ntype Message = \"direct\" | \"internal\" | \"storefront\" | \"checkout\" | \"customer\";\n\nfunction message(value: string): Message {\n if (/checkout\\.zappie\\.ai|\\/checkout(?:\\/|$)/i.test(value)) {\n return \"checkout\";\n }\n if (/accounts\\.zappie\\.ai|\\/customer\\/graphql/i.test(value)) {\n return \"customer\";\n }\n if (/\\/storefront\\/v1/i.test(value)) {\n return \"storefront\";\n }\n return \"direct\";\n}\n\nexport const noDirectApi = createRule<[], Message>({\n name: \"no-direct-api\",\n meta: {\n type: \"problem\",\n docs: { description: \"Call Zappie through the SDK.\" },\n schema: [],\n messages: {\n direct:\n \"Call the matching public @zappie/sdk client to reach this Zappie endpoint.\",\n internal:\n \"Use a public SDK entry. Only Zappie-owned packages can import @zappie/sdk/internal.\",\n storefront:\n \"Use createZappie(config).store or .menu to read storefront data.\",\n checkout: \"Use createCheckout().open() from @zappie/sdk/checkout.\",\n customer: \"Use createCustomer() from @zappie/sdk/customer.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const inspect = (node: TSESTree.Literal | TSESTree.TemplateLiteral) => {\n if (node.parent.type === \"ImportDeclaration\") {\n return;\n }\n const value = staticText(node);\n if (value && isZappieEndpoint(value)) {\n context.report({ node, messageId: message(value) });\n }\n };\n return {\n Literal: inspect,\n TemplateLiteral: inspect,\n ImportDeclaration(node) {\n if (isInternalSdkImport(node.source.value)) {\n context.report({ node: node.source, messageId: \"internal\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { staticText } from \"../matchers\";\n\nfunction globalCallName(node: TSESTree.Expression): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"SequenceExpression\") {\n const last = node.expressions.at(-1);\n return last ? globalCallName(last) : undefined;\n }\n if (\n node.type === \"MemberExpression\" &&\n node.object.type === \"Identifier\" &&\n [\"window\", \"globalThis\"].includes(node.object.name)\n ) {\n if (!node.computed && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return staticText(node.property);\n }\n return undefined;\n}\n\nexport const noEval = createRule<[], \"eval\">({\n name: \"no-eval\",\n meta: {\n type: \"problem\",\n docs: { description: \"Do not execute strings as code.\" },\n schema: [],\n messages: {\n eval: \"Remove code generated from a string. Ship executable code in the storefront bundle.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n CallExpression(node) {\n const name = globalCallName(node.callee);\n if (name === \"eval\" || name === \"Function\") {\n context.report({ node, messageId: \"eval\" });\n return;\n }\n if (\n [\"setTimeout\", \"setInterval\"].includes(name ?? \"\") &&\n staticText(node.arguments[0] ?? null) !== undefined\n ) {\n context.report({ node, messageId: \"eval\" });\n }\n },\n NewExpression(node) {\n if (globalCallName(node.callee) === \"Function\") {\n context.report({ node, messageId: \"eval\" });\n }\n },\n };\n },\n});\n","import { createRule } from \"../create-rule\";\nimport {\n isTrackingEndpoint,\n jsxAttribute,\n jsxValue,\n staticText,\n} from \"../matchers\";\n\nconst external = (value?: string) =>\n Boolean(value && /^(?:[a-z][a-z\\d+.-]*:)?\\/\\//i.test(value));\n\nconst documentObject = (\n node: import(\"@typescript-eslint/utils\").TSESTree.Node,\n) =>\n (node.type === \"Identifier\" && node.name === \"document\") ||\n (node.type === \"MemberExpression\" &&\n node.object.type === \"Identifier\" &&\n [\"window\", \"globalThis\"].includes(node.object.name) &&\n ((!node.computed &&\n node.property.type === \"Identifier\" &&\n node.property.name === \"document\") ||\n staticText(node.property) === \"document\"));\n\nexport const noExternalScripts = createRule<[], \"externalScript\">({\n name: \"no-external-scripts\",\n meta: {\n type: \"problem\",\n docs: { description: \"Ship scripts in the storefront bundle.\" },\n schema: [],\n messages: {\n externalScript:\n \"Remove the external script. Install reviewed code as a package so it ships in the storefront bundle.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n JSXOpeningElement(node) {\n if (node.name.type !== \"JSXIdentifier\") {\n return;\n }\n const element = node.name.name.toLowerCase();\n if (element === \"script\") {\n const attribute = jsxAttribute(node, \"src\");\n const src = jsxValue(attribute);\n const dynamic =\n attribute?.value?.type === \"JSXExpressionContainer\" &&\n !(\n attribute.value.expression.type === \"Literal\" ||\n (attribute.value.expression.type === \"TemplateLiteral\" &&\n attribute.value.expression.expressions.length === 0)\n );\n if (\n attribute &&\n (dynamic ||\n src === undefined ||\n (external(src) && !isTrackingEndpoint(src)))\n ) {\n context.report({ node, messageId: \"externalScript\" });\n }\n return;\n }\n if (element !== \"link\") {\n return;\n }\n const rel = jsxValue(jsxAttribute(node, \"rel\"))?.toLowerCase();\n const as = jsxValue(jsxAttribute(node, \"as\"))?.toLowerCase();\n const href = jsxValue(jsxAttribute(node, \"href\"));\n if (\n ((rel === \"preload\" && as === \"script\") || rel === \"modulepreload\") &&\n external(href) &&\n !isTrackingEndpoint(href ?? \"\")\n ) {\n context.report({ node, messageId: \"externalScript\" });\n }\n },\n CallExpression(node) {\n if (\n node.callee.type === \"MemberExpression\" &&\n !node.callee.computed &&\n documentObject(node.callee.object) &&\n node.callee.property.type === \"Identifier\" &&\n node.callee.property.name === \"createElement\" &&\n staticText(node.arguments[0] ?? null)?.toLowerCase() === \"script\"\n ) {\n context.report({ node, messageId: \"externalScript\" });\n return;\n }\n const reactCreateElement =\n node.callee.type === \"MemberExpression\" &&\n !node.callee.computed &&\n node.callee.object.type === \"Identifier\" &&\n node.callee.object.name === \"React\" &&\n node.callee.property.type === \"Identifier\" &&\n node.callee.property.name === \"createElement\";\n if (\n reactCreateElement &&\n staticText(node.arguments[0] ?? null)?.toLowerCase() === \"script\" &&\n node.arguments[1]?.type === \"ObjectExpression\"\n ) {\n const src = node.arguments[1].properties.find(\n (property) =>\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" &&\n property.key.name === \"src\") ||\n staticText(property.key) === \"src\"),\n );\n if (src) {\n context.report({ node, messageId: \"externalScript\" });\n }\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst react = new Set([\"react\"]);\n\n// React's own name for its default and namespace import, even without an import (a global build).\nconst conventionalNamespace = \"React\";\n\nexport const noLazyOverlay = createRule<[], \"lazy\" | \"suspense\">({\n name: \"no-lazy-overlay\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Load overlays with deferred() from @zappie/foundations, not React.lazy or Suspense.\",\n },\n schema: [],\n messages: {\n lazy: \"Load this component with deferred() from @zappie/foundations. React.lazy suspends, and React holds a suspended reveal back for about 300 ms, so the overlay opens late.\",\n suspense:\n \"Remove Suspense and load the component with deferred() from @zappie/foundations. It renders as soon as its code arrives and never suspends.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const lazyNames = new Set<string>();\n const suspenseNames = new Set<string>();\n const namespaces = new Set<string>([conventionalNamespace]);\n\n const fromNamespace = (\n object: TSESTree.Node,\n property: TSESTree.Node,\n wanted: string,\n ) =>\n object.type === \"Identifier\" &&\n namespaces.has(object.name) &&\n property.type === \"Identifier\" &&\n property.name === wanted;\n\n return {\n ImportDeclaration(node) {\n if (!react.has(node.source.value)) {\n return;\n }\n for (const specifier of node.specifiers) {\n if (specifier.type !== \"ImportSpecifier\") {\n namespaces.add(specifier.local.name);\n continue;\n }\n const imported =\n specifier.imported.type === \"Identifier\"\n ? specifier.imported.name\n : specifier.imported.value;\n if (imported === \"lazy\") {\n lazyNames.add(specifier.local.name);\n } else if (imported === \"Suspense\") {\n suspenseNames.add(specifier.local.name);\n }\n }\n },\n CallExpression(node) {\n const callee = node.callee;\n if (\n (callee.type === \"Identifier\" && lazyNames.has(callee.name)) ||\n (callee.type === \"MemberExpression\" &&\n !callee.computed &&\n fromNamespace(callee.object, callee.property, \"lazy\"))\n ) {\n context.report({ node, messageId: \"lazy\" });\n }\n },\n JSXOpeningElement(node) {\n const name = node.name;\n if (\n (name.type === \"JSXIdentifier\" && suspenseNames.has(name.name)) ||\n (name.type === \"JSXMemberExpression\" &&\n name.object.type === \"JSXIdentifier\" &&\n namespaces.has(name.object.name) &&\n name.property.name === \"Suspense\")\n ) {\n context.report({ node, messageId: \"suspense\" });\n }\n },\n };\n },\n});\n","import {\n ESLintUtils,\n type TSESLint,\n type TSESTree,\n} from \"@typescript-eslint/utils\";\nimport type ts from \"typescript\";\n\nconst moneyLiteralPattern =\n /[₦€£]\\s?\\d|\\$\\s?\\d|NGN\\s?\\d|\\d+(?:\\.\\d{2})?\\s?(?:naira|NGN)/i;\n\nexport function isMoneyLiteral(value: string): boolean {\n return value.trim() !== \"$1\" && moneyLiteralPattern.test(value);\n}\n\nexport function memberName(node: TSESTree.Node): string | undefined {\n if (node.type !== \"MemberExpression\") {\n return undefined;\n }\n if (!node.computed && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n if (node.computed && node.property.type === \"Literal\") {\n return typeof node.property.value === \"string\"\n ? node.property.value\n : undefined;\n }\n return undefined;\n}\n\nexport const isAmountMinor = (node: TSESTree.Node) =>\n memberName(node) === \"amountMinor\";\n\nexport function amountOwner(\n node: TSESTree.Node,\n): TSESTree.Expression | undefined {\n return node.type === \"MemberExpression\" && isAmountMinor(node)\n ? node.object\n : undefined;\n}\n\nexport function dividedMinorOwner(\n node: TSESTree.Node,\n): TSESTree.Expression | undefined {\n if (\n node.type !== \"BinaryExpression\" ||\n node.operator !== \"/\" ||\n node.right.type !== \"Literal\" ||\n node.right.value !== 100\n ) {\n return undefined;\n }\n return amountOwner(node.left);\n}\n\ntype Services = ReturnType<typeof ESLintUtils.getParserServices>;\n\nexport function parserServices<\n MessageIds extends string,\n Options extends readonly unknown[],\n>(context: TSESLint.RuleContext<MessageIds, Options>): Services | undefined {\n try {\n return ESLintUtils.getParserServices(context, true);\n } catch {\n return undefined;\n }\n}\n\nfunction declaredBySdkMoney(symbol: ts.Symbol): boolean {\n if (!symbol.getName().includes(\"MoneyBrand\")) {\n return false;\n }\n return (symbol.getDeclarations() ?? []).some((declaration) => {\n const path = declaration.getSourceFile().fileName.replaceAll(\"\\\\\", \"/\");\n return (\n path.endsWith(\"/packages/sdk/src/money.ts\") ||\n /\\/node_modules\\/@zappie\\/sdk\\/.*\\.d\\.ts$/.test(path)\n );\n });\n}\n\nexport function isSdkMoney(\n services: Services | undefined,\n node: TSESTree.Node,\n): boolean {\n if (!services?.program) {\n return false;\n }\n const checker = services.program.getTypeChecker();\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n const type = checker.getTypeAtLocation(tsNode);\n const types = type.isUnionOrIntersection() ? type.types : [type];\n return types.some((part) =>\n part.getProperties().some((property) => declaredBySdkMoney(property)),\n );\n}\n\nexport function currencyStyle(node: TSESTree.Node | undefined): boolean {\n if (node?.type !== \"ObjectExpression\") {\n return false;\n }\n return node.properties.some(\n (property) =>\n property.type === \"Property\" &&\n !property.computed &&\n ((property.key.type === \"Identifier\" && property.key.name === \"style\") ||\n (property.key.type === \"Literal\" && property.key.value === \"style\")) &&\n property.value.type === \"Literal\" &&\n property.value.value === \"currency\",\n );\n}\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n dividedMinorOwner,\n isAmountMinor,\n isMoneyLiteral,\n isSdkMoney,\n memberName,\n parserServices,\n} from \"../money-rule-utils\";\n\nexport const noMoneyLiterals = createRule<\n [],\n \"arithmetic\" | \"literal\" | \"scale\"\n>({\n name: \"no-money-literals\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Use SDK prices and totals in your storefront.\",\n },\n schema: [],\n messages: {\n arithmetic:\n \"Use cart.subtotal, line.lineSubtotal, quote.subtotal, quote.lines totals or quote.total. Let the SDK calculate amounts.\",\n literal:\n \"Render SDK Money with format.money() or <Money> instead of a hard-coded amount.\",\n scale:\n \"Use format.money() to display amountMinor. Currencies use 0, 2, 3 or 4 minor-unit digits.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const services = parserServices(context);\n const arithmetic = (node: TSESTree.Node) =>\n isAmountMinor(node) || isSdkMoney(services, node);\n return {\n Literal(node) {\n if (\n typeof node.value === \"string\" &&\n node.parent.type !== \"ImportDeclaration\" &&\n isMoneyLiteral(node.value)\n ) {\n context.report({ node, messageId: \"literal\" });\n }\n },\n JSXText(node) {\n if (isMoneyLiteral(node.value)) {\n context.report({ node, messageId: \"literal\" });\n }\n },\n BinaryExpression(node) {\n if (![\"+\", \"-\", \"*\", \"/\"].includes(node.operator)) {\n return;\n }\n if (\n dividedMinorOwner(node) &&\n node.parent.type === \"MemberExpression\" &&\n node.parent.object === node &&\n memberName(node.parent) === \"toFixed\"\n ) {\n return;\n }\n if (arithmetic(node.left) || arithmetic(node.right)) {\n context.report({ node, messageId: \"arithmetic\" });\n }\n },\n TemplateLiteral(node) {\n for (const quasi of node.quasis) {\n if (isMoneyLiteral(quasi.value.cooked ?? quasi.value.raw)) {\n context.report({ node: quasi, messageId: \"literal\" });\n }\n }\n },\n CallExpression(node) {\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"toFixed\" &&\n node.arguments[0]?.type === \"Literal\" &&\n node.arguments[0].value === 2 &&\n dividedMinorOwner(node.callee.object)\n ) {\n context.report({ node, messageId: \"scale\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isServerFile, memberName, staticText } from \"../matchers\";\n\nconst serverFiles = [\n \"**/*.server.{js,jsx,ts,tsx,mjs,cjs,mts,cts}\",\n \"**/app/.server/**\",\n \"**/app/server.ts\",\n \"**/test/**\",\n \"**/tests/**\",\n \"**/__tests__/**\",\n \"**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs,mts,cts}\",\n \"**/*.config.{js,ts,mjs,cjs,mts,cts}\",\n];\nconst vitePublic = new Set([\"MODE\", \"DEV\", \"PROD\", \"SSR\", \"BASE_URL\"]);\ntype EnvKind = \"process\" | \"vite\";\n\nfunction namedFunction(node: TSESTree.Node): string | undefined {\n if (node.type === \"FunctionDeclaration\") {\n return node.id?.name;\n }\n if (\n node.type !== \"FunctionExpression\" &&\n node.type !== \"ArrowFunctionExpression\"\n ) {\n return undefined;\n }\n const parent = node.parent;\n if (parent.type === \"VariableDeclarator\" && parent.id.type === \"Identifier\") {\n return parent.id.name;\n }\n if (parent.type === \"Property\") {\n if (parent.key.type === \"Identifier\") {\n return parent.key.name;\n }\n return staticText(parent.key);\n }\n return undefined;\n}\n\nfunction directEnv(node: TSESTree.Node): EnvKind | undefined {\n if (\n node.type === \"MemberExpression\" &&\n node.object.type === \"Identifier\" &&\n node.object.name === \"process\" &&\n memberName(node) === \"env\"\n ) {\n return \"process\";\n }\n if (\n node.type === \"MemberExpression\" &&\n node.object.type === \"MetaProperty\" &&\n node.object.meta.name === \"import\" &&\n node.object.property.name === \"meta\" &&\n memberName(node) === \"env\"\n ) {\n return \"vite\";\n }\n return undefined;\n}\n\nfunction contextCloudflare(node: TSESTree.Node): boolean {\n if (node.type !== \"MemberExpression\" || memberName(node) !== \"cloudflare\") {\n return false;\n }\n return (\n (node.object.type === \"Identifier\" && node.object.name === \"context\") ||\n (node.object.type === \"MemberExpression\" &&\n memberName(node.object) === \"context\")\n );\n}\n\nfunction forbidden(kind: EnvKind, key: string | undefined): boolean {\n if (kind === \"process\") {\n return key !== \"NODE_ENV\";\n }\n return (\n key === undefined ||\n (!key.startsWith(\"VITE_ZAPPIE_\") && !vitePublic.has(key))\n );\n}\n\nexport const noServerEnvInClient = createRule<[], \"serverEnv\">({\n name: \"no-server-env-in-client\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Read server environment values only in server code.\",\n },\n schema: [],\n messages: {\n serverEnv:\n \"Move this environment read into a loader, action, *.server.ts file, app/.server/ or app/server.ts so it cannot reach the client bundle.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const filename = context.filename.replaceAll(\"\\\\\", \"/\");\n if (\n isServerFile(filename, serverFiles) ||\n /(?:^|\\/)(?:test|tests|__tests__)(?:\\/|$)/.test(filename) ||\n /\\.(?:test|spec|config)\\.[cm]?[jt]sx?$/.test(filename)\n ) {\n return {};\n }\n const aliases = new Map<string, EnvKind>();\n const cloudflareAliases = new Set<string>();\n const serverBody = (node: TSESTree.Node) =>\n context.sourceCode\n .getAncestors(node)\n .some((ancestor) =>\n [\"loader\", \"action\"].includes(namedFunction(ancestor) ?? \"\"),\n );\n const kindOf = (node: TSESTree.Node): EnvKind | undefined => {\n const direct = directEnv(node);\n if (direct) {\n return direct;\n }\n return node.type === \"Identifier\" ? aliases.get(node.name) : undefined;\n };\n const recordCloudflarePattern = (pattern: TSESTree.BindingName) => {\n if (pattern.type !== \"ObjectPattern\") {\n return;\n }\n for (const property of pattern.properties) {\n if (\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" &&\n property.key.name === \"cloudflare\") ||\n staticText(property.key) === \"cloudflare\") &&\n property.value.type === \"Identifier\"\n ) {\n cloudflareAliases.add(property.value.name);\n }\n if (\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" &&\n property.key.name === \"context\") ||\n staticText(property.key) === \"context\")\n ) {\n recordCloudflarePattern(property.value as TSESTree.BindingName);\n }\n }\n };\n const inspectParameters = (\n node:\n | TSESTree.ArrowFunctionExpression\n | TSESTree.FunctionDeclaration\n | TSESTree.FunctionExpression,\n ) => {\n if (\n serverBody(node) ||\n [\"loader\", \"action\"].includes(namedFunction(node) ?? \"\")\n ) {\n return;\n }\n for (const parameter of node.params) {\n if (parameter.type !== \"RestElement\") {\n recordCloudflarePattern(parameter as TSESTree.BindingName);\n }\n }\n };\n return {\n ArrowFunctionExpression: inspectParameters,\n FunctionDeclaration: inspectParameters,\n FunctionExpression: inspectParameters,\n VariableDeclarator(node) {\n if (serverBody(node) || !node.init) {\n return;\n }\n const kind = kindOf(node.init);\n if (node.id.type === \"Identifier\" && kind) {\n aliases.set(node.id.name, kind);\n return;\n }\n if (node.id.type === \"ObjectPattern\" && kind) {\n for (const property of node.id.properties) {\n const key =\n property.type === \"Property\"\n ? property.key.type === \"Identifier\"\n ? property.key.name\n : staticText(property.key)\n : undefined;\n if (forbidden(kind, key)) {\n context.report({ node: property, messageId: \"serverEnv\" });\n }\n }\n return;\n }\n if (node.id.type === \"ObjectPattern\" && contextCloudflare(node.init)) {\n for (const property of node.id.properties) {\n if (\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" &&\n property.key.name === \"env\") ||\n staticText(property.key) === \"env\")\n ) {\n context.report({ node: property, messageId: \"serverEnv\" });\n }\n }\n }\n },\n MemberExpression(node) {\n if (serverBody(node)) {\n return;\n }\n const kind = kindOf(node.object);\n if (kind && forbidden(kind, memberName(node))) {\n context.report({ node, messageId: \"serverEnv\" });\n return;\n }\n if (\n memberName(node) === \"env\" &&\n (contextCloudflare(node.object) ||\n (node.object.type === \"Identifier\" &&\n cloudflareAliases.has(node.object.name)))\n ) {\n context.report({ node, messageId: \"serverEnv\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport { isTrackingEndpoint, staticText } from \"../matchers\";\n\nconst imports = [\n \"react-ga4\",\n \"react-facebook-pixel\",\n \"@vercel/analytics\",\n \"posthog-js\",\n];\n\nfunction imported(value: string): boolean {\n return imports.includes(value) || value.startsWith(\"@segment/\");\n}\n\nexport const noTrackingScripts = createRule<[], \"tracking\">({\n name: \"no-tracking-scripts\",\n meta: {\n type: \"problem\",\n docs: { description: \"Forward analytics through Zappie.\" },\n schema: [],\n messages: {\n tracking:\n \"Configure analytics.forward in zappie.config.ts to forward analytics. Remove the tracking script.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const inspect = (node: TSESTree.Literal | TSESTree.TemplateLiteral) => {\n if (node.parent.type === \"ImportDeclaration\") {\n return;\n }\n const value = staticText(node);\n if (value && isTrackingEndpoint(value)) {\n context.report({ node, messageId: \"tracking\" });\n }\n };\n return {\n Literal: inspect,\n TemplateLiteral: inspect,\n ImportDeclaration(node) {\n if (imported(node.source.value)) {\n context.report({ node: node.source, messageId: \"tracking\" });\n }\n },\n CallExpression(node) {\n const callee = node.callee;\n const direct =\n callee.type === \"Identifier\" && [\"gtag\", \"fbq\"].includes(callee.name);\n const dataLayer =\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"dataLayer\" &&\n callee.property.type === \"Identifier\" &&\n callee.property.name === \"push\";\n if (direct || dataLayer) {\n context.report({ node, messageId: \"tracking\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n isServerFile,\n isZappieEndpoint,\n memberName,\n staticText,\n} from \"../matchers\";\n\ntype Settings = { zappie?: { outboundAllow?: unknown } };\n\nfunction literal(node: TSESTree.Node | null | undefined): string | undefined {\n const text = staticText(node ?? null);\n if (text !== undefined) return text;\n if (\n node?.type === \"NewExpression\" &&\n node.callee.type === \"Identifier\" &&\n node.callee.name === \"URL\"\n ) {\n return literal(node.arguments[0]);\n }\n return undefined;\n}\n\nfunction hostname(value: string): string | undefined {\n if (!/^(?:https?|wss?):\\/\\//i.test(value) && !value.startsWith(\"//\")) {\n return undefined;\n }\n try {\n return new URL(value, \"https://storefront.invalid\").hostname.toLowerCase();\n } catch {\n return undefined;\n }\n}\n\nfunction allowed(host: string, entries: string[]): boolean {\n return entries.some((entry) => {\n const candidate = entry.toLowerCase();\n return candidate.startsWith(\"*.\")\n ? host.endsWith(candidate.slice(1)) && host !== candidate.slice(2)\n : host === candidate;\n });\n}\n\ntype Options = { dynamicOnly?: boolean };\n\nexport const noUnlistedFetch = createRule<\n [Options],\n \"dynamicRequest\" | \"unlistedRequest\"\n>({\n name: \"no-unlisted-fetch\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"List every external request host in outbound.allow.\",\n },\n schema: [\n {\n type: \"object\",\n properties: { dynamicOnly: { type: \"boolean\" } },\n additionalProperties: false,\n },\n ],\n messages: {\n dynamicRequest:\n \"This request URL is computed. Review its possible hosts and list each external host in zappie.config.ts outbound.allow.\",\n unlistedRequest:\n \"Add {{host}} to zappie.config.ts outbound.allow, or remove this external request.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const filename = context.filename.replaceAll(\"\\\\\", \"/\");\n if (\n isServerFile(filename, [\n \"**/test/**\",\n \"**/tests/**\",\n \"**/__tests__/**\",\n \"**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs,mts,cts}\",\n ]) ||\n /(?:^|\\/)(?:test|tests|__tests__)(?:\\/|$)/.test(filename) ||\n /\\.(?:test|spec)\\.[cm]?[jt]sx?$/.test(filename)\n ) {\n return {};\n }\n const configured = (context.settings as Settings | undefined)?.zappie\n ?.outboundAllow;\n const allow = Array.isArray(configured)\n ? configured.filter((value): value is string => typeof value === \"string\")\n : [];\n const xhr = new Set<string>();\n const inspect = (node: TSESTree.Node, target?: TSESTree.Node) => {\n const value = literal(target);\n if (value === undefined) {\n if (options.dynamicOnly) {\n context.report({ node, messageId: \"dynamicRequest\" });\n }\n return;\n }\n if (options.dynamicOnly) {\n return;\n }\n const host = hostname(value);\n if (!host || isZappieEndpoint(value) || allowed(host, allow)) {\n return;\n }\n context.report({ node, messageId: \"unlistedRequest\", data: { host } });\n };\n return {\n VariableDeclarator(node) {\n if (\n node.id.type === \"Identifier\" &&\n node.init?.type === \"NewExpression\" &&\n node.init.callee.type === \"Identifier\" &&\n node.init.callee.name === \"XMLHttpRequest\"\n ) {\n xhr.add(node.id.name);\n }\n },\n CallExpression(node) {\n const fetchCall =\n (node.callee.type === \"Identifier\" && node.callee.name === \"fetch\") ||\n (node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"fetch\" &&\n node.callee.object.type === \"Identifier\" &&\n [\"window\", \"globalThis\"].includes(node.callee.object.name));\n if (fetchCall) {\n inspect(node, node.arguments[0]);\n return;\n }\n if (node.callee.type !== \"MemberExpression\") {\n return;\n }\n const name = memberName(node.callee);\n if (\n name === \"sendBeacon\" &&\n node.callee.object.type === \"Identifier\" &&\n node.callee.object.name === \"navigator\"\n ) {\n inspect(node, node.arguments[0]);\n return;\n }\n if (\n name === \"open\" &&\n ((node.callee.object.type === \"Identifier\" &&\n xhr.has(node.callee.object.name)) ||\n (node.callee.object.type === \"NewExpression\" &&\n node.callee.object.callee.type === \"Identifier\" &&\n node.callee.object.callee.name === \"XMLHttpRequest\"))\n ) {\n inspect(node, node.arguments[1]);\n }\n },\n NewExpression(node) {\n if (\n node.callee.type === \"Identifier\" &&\n [\"WebSocket\", \"EventSource\"].includes(node.callee.name)\n ) {\n inspect(node, node.arguments[0]);\n }\n },\n };\n },\n});\n\nexport const noUnlistedFetchDynamic = {\n ...noUnlistedFetch,\n meta: {\n ...noUnlistedFetch.meta,\n docs: {\n ...noUnlistedFetch.meta.docs,\n url: \"https://docs.zappie.ai/lint/no-unlisted-fetch-dynamic\",\n },\n },\n};\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n findVariable,\n memberName,\n staticText,\n unwrapExpression,\n} from \"../matchers\";\n\nfunction objectHtml(node: TSESTree.ObjectExpression) {\n return node.properties.find(\n (property): property is TSESTree.Property =>\n property.type === \"Property\" &&\n !property.computed &&\n ((property.key.type === \"Identifier\" && property.key.name === \"__html\") ||\n staticText(property.key) === \"__html\"),\n )?.value;\n}\n\nexport const noUnsafeHtml = createRule<[], \"unsafeHtml\">({\n name: \"no-unsafe-html\",\n meta: {\n type: \"problem\",\n docs: { description: \"Sanitize rich text before inserting HTML.\" },\n schema: [],\n messages: {\n unsafeHtml:\n \"Pass the HTML straight through a sanitizer created with createSanitizeRichText from @zappie/foundations/rich-text.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const source = context.sourceCode;\n const isFactory = (node: TSESTree.Identifier): boolean => {\n const binding = findVariable(source, node, node.name);\n return Boolean(\n binding?.defs.some(\n (definition) =>\n definition.type === \"ImportBinding\" &&\n definition.node.type === \"ImportSpecifier\" &&\n definition.node.imported.type === \"Identifier\" &&\n definition.node.imported.name === \"createSanitizeRichText\" &&\n definition.parent?.type === \"ImportDeclaration\" &&\n definition.parent.source.value === \"@zappie/foundations/rich-text\",\n ),\n );\n };\n const isSanitizer = (node: TSESTree.Identifier): boolean => {\n const binding = findVariable(source, node, node.name);\n return Boolean(\n binding?.defs.some((definition) => {\n if (\n definition.type !== \"Variable\" ||\n definition.node.type !== \"VariableDeclarator\"\n ) {\n return false;\n }\n const init = definition.node.init;\n return (\n init?.type === \"CallExpression\" &&\n init.callee.type === \"Identifier\" &&\n isFactory(init.callee)\n );\n }),\n );\n };\n const sanitized = (\n input: TSESTree.Node | null | undefined,\n seen = new Set<TSESLint.Scope.Variable>(),\n ): boolean => {\n if (!input || input.type === \"SpreadElement\") {\n return false;\n }\n const node = unwrapExpression(input as TSESTree.Expression);\n if (node.type === \"Literal\") {\n return node.value === \"\";\n }\n if (node.type === \"ConditionalExpression\") {\n return (\n sanitized(node.consequent, seen) && sanitized(node.alternate, seen)\n );\n }\n if (node.type === \"LogicalExpression\") {\n return sanitized(node.left, seen) && sanitized(node.right, seen);\n }\n if (node.type === \"CallExpression\") {\n if (node.callee.type === \"Identifier\" && isSanitizer(node.callee)) {\n return true;\n }\n return (\n node.callee.type === \"CallExpression\" &&\n node.callee.callee.type === \"Identifier\" &&\n isFactory(node.callee.callee)\n );\n }\n if (node.type !== \"Identifier\") {\n return false;\n }\n const binding = findVariable(source, node, node.name);\n if (!binding || seen.has(binding)) {\n return false;\n }\n const nextSeen = new Set(seen).add(binding);\n for (const definition of binding.defs) {\n if (\n definition.type !== \"Variable\" ||\n definition.node.type !== \"VariableDeclarator\"\n ) {\n continue;\n }\n if (definition.node.id.type === \"Identifier\") {\n return sanitized(definition.node.init, nextSeen);\n }\n if (\n definition.node.id.type !== \"ArrayPattern\" ||\n definition.node.id.elements[0]?.type !== \"Identifier\" ||\n definition.node.id.elements[0].name !== node.name ||\n definition.node.init?.type !== \"CallExpression\" ||\n definition.node.init.callee.type !== \"Identifier\" ||\n definition.node.init.callee.name !== \"useState\"\n ) {\n continue;\n }\n const initial = definition.node.init.arguments[0];\n const setter = definition.node.id.elements[1];\n if (!sanitized(initial, nextSeen) || setter?.type !== \"Identifier\") {\n return false;\n }\n const setterBinding = findVariable(source, setter, setter.name);\n const updates =\n setterBinding?.references.filter(\n (reference) =>\n reference.identifier.parent.type === \"CallExpression\" &&\n reference.identifier.parent.callee === reference.identifier,\n ) ?? [];\n return (\n updates.length > 0 &&\n updates.every((reference) => {\n const call = reference.identifier.parent;\n return (\n call.type === \"CallExpression\" &&\n sanitized(call.arguments[0], nextSeen)\n );\n })\n );\n }\n return false;\n };\n const reportHtmlObject = (\n node: TSESTree.Node,\n value: TSESTree.Node | null | undefined,\n ) => {\n if (value?.type !== \"ObjectExpression\" || !sanitized(objectHtml(value))) {\n context.report({ node, messageId: \"unsafeHtml\" });\n }\n };\n return {\n JSXAttribute(node) {\n if (node.name.type !== \"JSXIdentifier\") {\n return;\n }\n const name = node.name.name.toLowerCase();\n if (\n ![\"dangerouslysetinnerhtml\", \"srcdoc\"].includes(name) ||\n node.value?.type !== \"JSXExpressionContainer\"\n ) {\n return;\n }\n if (name === \"dangerouslysetinnerhtml\") {\n reportHtmlObject(node, node.value.expression);\n } else if (!sanitized(node.value.expression)) {\n context.report({ node, messageId: \"unsafeHtml\" });\n }\n },\n JSXSpreadAttribute(node) {\n if (node.argument.type !== \"ObjectExpression\") {\n return;\n }\n const dangerous = node.argument.properties.find(\n (property): property is TSESTree.Property =>\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" &&\n property.key.name === \"dangerouslySetInnerHTML\") ||\n staticText(property.key) === \"dangerouslySetInnerHTML\"),\n );\n if (dangerous) {\n reportHtmlObject(node, dangerous.value);\n }\n },\n JSXElement(node) {\n if (\n node.openingElement.name.type === \"JSXIdentifier\" &&\n node.openingElement.name.name.toLowerCase() === \"script\"\n ) {\n for (const child of node.children) {\n if (\n child.type === \"JSXExpressionContainer\" &&\n child.expression.type !== \"JSXEmptyExpression\"\n ) {\n context.report({ node: child, messageId: \"unsafeHtml\" });\n }\n }\n }\n },\n AssignmentExpression(node) {\n if (\n node.left.type === \"MemberExpression\" &&\n [\"innerHTML\", \"outerHTML\"].includes(memberName(node.left) ?? \"\") &&\n !sanitized(node.right)\n ) {\n context.report({ node, messageId: \"unsafeHtml\" });\n }\n },\n CallExpression(node) {\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"insertAdjacentHTML\"\n ) {\n if (!sanitized(node.arguments[1])) {\n context.report({ node, messageId: \"unsafeHtml\" });\n }\n return;\n }\n const createElement =\n (node.callee.type === \"Identifier\" &&\n node.callee.name === \"createElement\") ||\n (node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"createElement\" &&\n node.callee.object.type === \"Identifier\" &&\n node.callee.object.name === \"React\");\n if (!createElement) {\n return;\n }\n if (staticText(node.arguments[0] ?? null)?.toLowerCase() === \"script\") {\n for (const child of node.arguments.slice(2)) {\n if (child.type !== \"SpreadElement\") {\n context.report({ node: child, messageId: \"unsafeHtml\" });\n }\n }\n }\n const props = node.arguments[1];\n if (props?.type !== \"ObjectExpression\") {\n return;\n }\n const dangerous = props.properties.find(\n (property): property is TSESTree.Property =>\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" &&\n property.key.name === \"dangerouslySetInnerHTML\") ||\n staticText(property.key) === \"dangerouslySetInnerHTML\"),\n );\n if (dangerous) {\n reportHtmlObject(node, dangerous.value);\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\nconst defaults = [\n \"modal\",\n \"sheet\",\n \"drawer\",\n \"dialog\",\n \"item\",\n \"cart\",\n \"cart_sheet\",\n \"cart_edit\",\n \"cart_clear\",\n \"search\",\n \"catalogue_search\",\n \"catalogue_categories\",\n \"product_note\",\n \"modal_id\",\n];\n\nfunction name(node: TSESTree.Node): string | undefined {\n if (node.type === \"Identifier\") {\n return node.name;\n }\n if (node.type === \"MemberExpression\" && node.property.type === \"Identifier\") {\n return node.property.name;\n }\n return undefined;\n}\n\nfunction carriesKey(node: TSESTree.Node, keys: Set<string>): boolean {\n if (node.type === \"Literal\" && typeof node.value === \"string\") {\n return [...keys].some((key) => new RegExp(`[?&]${key}=`).test(node.value));\n }\n if (node.type === \"TemplateLiteral\") {\n return node.quasis.some((part) =>\n carriesKey(\n { ...part, type: \"Literal\", value: part.value.raw } as never,\n keys,\n ),\n );\n }\n if (node.type === \"ObjectExpression\") {\n return node.properties.some(\n (property) =>\n property.type === \"Property\" &&\n ((property.key.type === \"Identifier\" && keys.has(property.key.name)) ||\n (property.key.type === \"Literal\" &&\n typeof property.key.value === \"string\" &&\n keys.has(property.key.value))),\n );\n }\n return false;\n}\n\nexport const overlayPrimitive = createRule<[{ keys?: string[] }], \"overlay\">({\n name: \"overlay-primitive\",\n meta: {\n type: \"problem\",\n docs: { description: \"Use Overlay to keep dialogs and sheets in the URL.\" },\n schema: [\n {\n type: \"object\",\n properties: { keys: { type: \"array\", items: { type: \"string\" } } },\n additionalProperties: false,\n },\n ],\n messages: {\n overlay:\n \"Use Overlay or useOverlay for dialogs and sheets in the URL. They handle Back, Forward and focus restoration.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const keys = new Set([...defaults, ...(options.keys ?? [])]);\n const searchValues = new Set<string>();\n const derivedFromSearch = (node: TSESTree.Node) => {\n const text = context.sourceCode.getText(node);\n return (\n text.includes(\"useSearchParams(\") ||\n [...searchValues].some((value) =>\n new RegExp(`\\\\b${value}\\\\b`).test(text),\n )\n );\n };\n return {\n VariableDeclarator(node) {\n if (\n node.init?.type === \"CallExpression\" &&\n name(node.init.callee) === \"useSearchParams\"\n ) {\n if (node.id.type === \"Identifier\") {\n searchValues.add(node.id.name);\n } else if (\n node.id.type === \"ArrayPattern\" &&\n node.id.elements[0]?.type === \"Identifier\"\n ) {\n searchValues.add(node.id.elements[0].name);\n }\n } else if (\n node.id.type === \"Identifier\" &&\n node.init &&\n derivedFromSearch(node.init)\n ) {\n searchValues.add(node.id.name);\n }\n },\n CallExpression(node) {\n const callee = name(node.callee);\n const navigation =\n callee === \"setSearchParams\" ||\n callee === \"navigate\" ||\n callee === \"pushState\" ||\n callee === \"replaceState\" ||\n (node.callee.type === \"CallExpression\" &&\n name(node.callee.callee) === \"useNavigate\");\n if (\n navigation &&\n node.arguments.some(\n (argument) =>\n argument.type !== \"SpreadElement\" && carriesKey(argument, keys),\n )\n ) {\n context.report({ node, messageId: \"overlay\" });\n }\n },\n JSXOpeningElement(node) {\n const dialog =\n node.name.type === \"JSXIdentifier\" && node.name.name === \"dialog\";\n const role = node.attributes.some(\n (attribute) =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === \"role\" &&\n attribute.value?.type === \"Literal\" &&\n attribute.value.value === \"dialog\",\n );\n const open = node.attributes.find(\n (attribute) =>\n attribute.type === \"JSXAttribute\" && attribute.name.name === \"open\",\n );\n if (\n (dialog || role) &&\n open?.type === \"JSXAttribute\" &&\n open.value?.type === \"JSXExpressionContainer\" &&\n derivedFromSearch(open.value.expression)\n ) {\n context.report({ node, messageId: \"overlay\" });\n }\n },\n };\n },\n});\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n findVariable,\n jsxAttribute,\n jsxValue,\n memberName,\n staticText,\n unwrapExpression,\n} from \"../matchers\";\n\nfunction callName(node: TSESTree.CallExpression): string | undefined {\n if (node.callee.type === \"Identifier\") {\n return node.callee.name;\n }\n return node.callee.type === \"MemberExpression\"\n ? memberName(node.callee)\n : undefined;\n}\n\nfunction possibleBlank(node: TSESTree.Node | null | undefined): boolean {\n if (!node || node.type === \"JSXEmptyExpression\") {\n return false;\n }\n if (\n node.type === \"TSAsExpression\" ||\n node.type === \"TSTypeAssertion\" ||\n node.type === \"TSNonNullExpression\"\n ) {\n return possibleBlank(node.expression);\n }\n if (node.type === \"ConditionalExpression\") {\n return possibleBlank(node.consequent) || possibleBlank(node.alternate);\n }\n if (node.type === \"LogicalExpression\") {\n return possibleBlank(node.left) || possibleBlank(node.right);\n }\n return staticText(node)?.toLowerCase() === \"_blank\";\n}\n\nexport const safeExternalLinks = createRule<\n [],\n \"javascriptUrl\" | \"noopener\" | \"redirect\"\n>({\n name: \"safe-external-links\",\n meta: {\n type: \"problem\",\n docs: {\n description: \"Open links and redirects without exposing the site.\",\n },\n fixable: \"code\",\n schema: [],\n messages: {\n javascriptUrl:\n \"Remove the javascript: URL. Use an event handler for storefront behavior.\",\n noopener:\n 'Add rel=\"noopener noreferrer\" when target=\"_blank\" opens a new tab.',\n redirect:\n \"Pass this redirect target through safeRedirect(to, fallback) from @zappie/sdk/redirect.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const source = context.sourceCode;\n const tainted = new Set<string>();\n const safe = new Set<string>();\n const importedSafeRedirect = (node: TSESTree.Identifier): boolean => {\n const binding = findVariable(source, node, node.name);\n return Boolean(\n binding?.defs.some(\n (definition) =>\n definition.type === \"ImportBinding\" &&\n definition.node.type === \"ImportSpecifier\" &&\n definition.node.imported.type === \"Identifier\" &&\n definition.node.imported.name === \"safeRedirect\" &&\n definition.parent?.type === \"ImportDeclaration\" &&\n definition.parent.source.value === \"@zappie/sdk/redirect\",\n ),\n );\n };\n const fromRequest = (input: TSESTree.Node | null | undefined): boolean => {\n if (!input || input.type === \"SpreadElement\") {\n return false;\n }\n const node = unwrapExpression(input as TSESTree.Expression);\n if (node.type === \"Identifier\") {\n return tainted.has(node.name) && !safe.has(node.name);\n }\n if (node.type === \"MemberExpression\") {\n if (\n node.object.type === \"Identifier\" &&\n node.object.name === \"request\" &&\n memberName(node) === \"url\"\n ) {\n return true;\n }\n return fromRequest(node.object);\n }\n if (node.type === \"CallExpression\") {\n if (\n node.callee.type === \"Identifier\" &&\n importedSafeRedirect(node.callee)\n ) {\n return false;\n }\n if (\n node.callee.type === \"Identifier\" &&\n node.callee.name === \"safeRedirect\"\n ) {\n return node.arguments.some(fromRequest);\n }\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"get\" &&\n ((node.callee.object.type === \"MemberExpression\" &&\n memberName(node.callee.object) === \"searchParams\") ||\n (node.callee.object.type === \"Identifier\" &&\n /searchParams/i.test(node.callee.object.name)) ||\n fromRequest(node.callee.object))\n ) {\n return true;\n }\n return false;\n }\n if (node.type === \"NewExpression\") {\n return (\n node.callee.type === \"Identifier\" &&\n node.callee.name === \"URL\" &&\n node.arguments.some(fromRequest)\n );\n }\n if (node.type === \"ConditionalExpression\") {\n return fromRequest(node.consequent) || fromRequest(node.alternate);\n }\n if (node.type === \"LogicalExpression\") {\n return fromRequest(node.left) || fromRequest(node.right);\n }\n if (node.type === \"BinaryExpression\") {\n return fromRequest(node.left);\n }\n if (node.type === \"TemplateLiteral\") {\n return node.expressions.some(fromRequest);\n }\n return false;\n };\n const sameOriginGuard = (node: TSESTree.Node, target: TSESTree.Node) => {\n if (target.type !== \"Identifier\") {\n return false;\n }\n const targetOrigin = (input: TSESTree.Node) => {\n const expression = unwrapExpression(input as TSESTree.Expression);\n return (\n expression.type === \"MemberExpression\" &&\n memberName(expression) === \"origin\" &&\n expression.object.type === \"Identifier\" &&\n expression.object.name === target.name\n );\n };\n const trustedOrigin = (input: TSESTree.Node) => {\n const expression = unwrapExpression(input as TSESTree.Expression);\n return (\n expression.type === \"MemberExpression\" &&\n memberName(expression) === \"origin\" &&\n ((expression.object.type === \"Identifier\" &&\n expression.object.name === \"location\") ||\n fromRequest(expression.object))\n );\n };\n return source.getAncestors(node).some((ancestor) => {\n if (\n ancestor.type !== \"IfStatement\" ||\n !source.getAncestors(node).includes(ancestor.consequent)\n ) {\n return false;\n }\n const test = unwrapExpression(ancestor.test);\n return (\n test.type === \"BinaryExpression\" &&\n (test.operator === \"===\" || test.operator === \"==\") &&\n ((targetOrigin(test.left) && trustedOrigin(test.right)) ||\n (trustedOrigin(test.left) && targetOrigin(test.right)))\n );\n });\n };\n const redirectTarget = (node: TSESTree.CallExpression) => {\n const name = callName(node);\n if (![\"redirect\", \"navigate\", \"assign\", \"replace\"].includes(name ?? \"\")) {\n return;\n }\n const target = node.arguments[0];\n if (\n target &&\n target.type !== \"SpreadElement\" &&\n fromRequest(target) &&\n !sameOriginGuard(node, unwrapExpression(target))\n ) {\n context.report({ node: target, messageId: \"redirect\" });\n }\n };\n return {\n VariableDeclarator(node) {\n if (node.id.type !== \"Identifier\" || !node.init) {\n return;\n }\n if (\n node.init.type === \"CallExpression\" &&\n node.init.callee.type === \"Identifier\" &&\n importedSafeRedirect(node.init.callee)\n ) {\n safe.add(node.id.name);\n } else if (fromRequest(node.init)) {\n tainted.add(node.id.name);\n }\n },\n JSXOpeningElement(node) {\n for (const name of [\"href\", \"src\", \"action\", \"formAction\"]) {\n const value = jsxValue(jsxAttribute(node, name));\n if (\n /^javascript:/i.test(\n (value ?? \"\").replace(/[\\u0000-\\u0020\\u007f\\s]/gu, \"\"),\n )\n ) {\n context.report({\n node: jsxAttribute(node, name) ?? node,\n messageId: \"javascriptUrl\",\n });\n }\n }\n const targetAttribute = jsxAttribute(node, \"target\");\n const targetExpression =\n targetAttribute?.value?.type === \"JSXExpressionContainer\"\n ? targetAttribute.value.expression\n : targetAttribute?.value;\n if (!possibleBlank(targetExpression)) {\n return;\n }\n const relAttribute = jsxAttribute(node, \"rel\");\n const rel = jsxValue(relAttribute);\n if (rel?.toLowerCase().split(/\\s+/).includes(\"noopener\")) {\n return;\n }\n context.report({\n node,\n messageId: \"noopener\",\n fix(fixer) {\n if (!relAttribute) {\n return fixer.insertTextAfter(\n node.name,\n ' rel=\"noopener noreferrer\"',\n );\n }\n if (!relAttribute.value || relAttribute.value.type !== \"Literal\") {\n return null;\n }\n const tokens = new Set((rel ?? \"\").split(/\\s+/).filter(Boolean));\n tokens.add(\"noopener\");\n tokens.add(\"noreferrer\");\n return fixer.replaceText(\n relAttribute,\n `rel=\"${[...tokens].join(\" \")}\"`,\n );\n },\n });\n },\n CallExpression: redirectTarget,\n AssignmentExpression(node) {\n if (\n node.left.type === \"MemberExpression\" &&\n memberName(node.left) === \"href\" &&\n ((node.left.object.type === \"Identifier\" &&\n node.left.object.name === \"location\") ||\n (node.left.object.type === \"MemberExpression\" &&\n memberName(node.left.object) === \"location\")) &&\n fromRequest(node.right)\n ) {\n context.report({ node: node.right, messageId: \"redirect\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\n\ntype Fixer = TSESLint.RuleFixer;\ntype Fix = TSESLint.RuleFix;\ntype Source = Readonly<TSESLint.SourceCode>;\n\nfunction sdkImport(\n program: TSESTree.Program,\n): TSESTree.ImportDeclaration | undefined {\n return program.body.find(\n (node): node is TSESTree.ImportDeclaration =>\n node.type === \"ImportDeclaration\" && node.source.value === \"@zappie/sdk\",\n );\n}\n\nfunction importedFormat(\n declaration: TSESTree.ImportDeclaration | undefined,\n): TSESTree.ImportSpecifier | undefined {\n return declaration?.specifiers.find(\n (specifier): specifier is TSESTree.ImportSpecifier =>\n specifier.type === \"ImportSpecifier\" &&\n specifier.imported.type === \"Identifier\" &&\n specifier.imported.name === \"format\",\n );\n}\n\nfunction importFix(\n fixer: Fixer,\n source: Source,\n program: TSESTree.Program,\n local: string,\n): Fix | undefined {\n const declaration = sdkImport(program);\n if (importedFormat(declaration)?.local.name === local) {\n return undefined;\n }\n const importedName = local === \"format\" ? \"format\" : `format as ${local}`;\n const named = declaration?.specifiers.filter(\n (specifier) => specifier.type === \"ImportSpecifier\",\n );\n if (declaration && declaration.importKind !== \"type\" && named?.length) {\n const last = named.at(-1);\n if (last) {\n return fixer.insertTextAfter(last, `, ${importedName}`);\n }\n }\n const first = program.body[0];\n const text = `import { ${importedName} } from \"@zappie/sdk\";\\n`;\n return first\n ? fixer.insertTextBefore(first, text)\n : fixer.insertTextAfter(program, text);\n}\n\nexport function formatFixes(\n fixer: Fixer,\n source: Source,\n program: TSESTree.Program,\n replace: TSESTree.Node,\n owner: TSESTree.Expression,\n local: string,\n): Fix[] {\n const fixes = [\n fixer.replaceText(replace, `${local}.money(${source.getText(owner)})`),\n ];\n const addition = importFix(fixer, source, program, local);\n if (addition) {\n fixes.push(addition);\n }\n return fixes;\n}\n\nexport function formatBinding(\n source: Source,\n program: TSESTree.Program,\n node: TSESTree.Node,\n): \"available\" | \"conflict\" | \"imported\" {\n if (importedFormat(sdkImport(program))) {\n return \"imported\";\n }\n let scope: TSESLint.Scope.Scope | null = source.getScope(node);\n while (scope) {\n if (scope.variables.some((variable) => variable.name === \"format\")) {\n return \"conflict\";\n }\n scope = scope.upper;\n }\n return \"available\";\n}\n\nexport function importedFormatLocal(\n program: TSESTree.Program,\n): string | undefined {\n return importedFormat(sdkImport(program))?.local.name;\n}\n\nexport function availableAlias(source: Source, node: TSESTree.Node): string {\n const names = new Set<string>();\n let scope: TSESLint.Scope.Scope | null = source.getScope(node);\n while (scope) {\n for (const variable of scope.variables) {\n names.add(variable.name);\n }\n scope = scope.upper;\n }\n for (const name of [\n \"zappieFormat\",\n \"zappieMoneyFormat\",\n \"formatMoneyValue\",\n ]) {\n if (!names.has(name)) {\n return name;\n }\n }\n return \"zappieSdkFormat\";\n}\n","import type { TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\nimport {\n availableAlias,\n formatBinding,\n formatFixes,\n importedFormatLocal,\n} from \"../format-fix\";\nimport {\n currencyStyle,\n dividedMinorOwner,\n isAmountMinor,\n isSdkMoney,\n memberName,\n parserServices,\n} from \"../money-rule-utils\";\n\ntype Message = \"currency\" | \"direct\" | \"minor\" | \"useAlias\";\n\nfunction intlNumberFormat(node: TSESTree.Node): boolean {\n const callee =\n node.type === \"NewExpression\" || node.type === \"CallExpression\"\n ? node.callee\n : undefined;\n return Boolean(\n callee?.type === \"MemberExpression\" &&\n callee.object.type === \"Identifier\" &&\n callee.object.name === \"Intl\" &&\n memberName(callee) === \"NumberFormat\",\n );\n}\n\nfunction formattedMinor(node: TSESTree.CallExpression) {\n if (node.callee.type !== \"MemberExpression\") {\n return undefined;\n }\n if (memberName(node.callee) === \"toLocaleString\") {\n return dividedMinorOwner(node.callee.object);\n }\n if (memberName(node.callee) === \"format\") {\n return dividedMinorOwner(node.arguments[0] ?? node);\n }\n return undefined;\n}\n\nfunction formatsDividedMinor(node: TSESTree.NewExpression): boolean {\n const member = node.parent;\n const call = member.type === \"MemberExpression\" ? member.parent : undefined;\n return Boolean(\n call?.type === \"CallExpression\" &&\n call.callee === member &&\n memberName(member) === \"format\" &&\n dividedMinorOwner(call.arguments[0] ?? call),\n );\n}\n\nexport const useFormatMoney = createRule<[], Message>({\n name: \"use-format-money\",\n meta: {\n type: \"problem\",\n docs: { description: \"Format SDK Money with format.money() or <Money>.\" },\n fixable: \"code\",\n hasSuggestions: true,\n schema: [],\n messages: {\n currency:\n \"Use format.money(). Intl currency defaults can differ from the API minor-unit digits.\",\n direct:\n \"Render SDK Money with format.money(value) or <Money value={value}>.\",\n minor:\n \"Use format.money(value). Dividing by 100 gives the wrong amount for currencies with 0, 3 or 4 minor-unit digits.\",\n useAlias:\n \"Import format with an unused alias and format this Money value.\",\n },\n },\n defaultOptions: [],\n create(context) {\n const source = context.sourceCode;\n const services = parserServices(context);\n let program: TSESTree.Program;\n const reportFix = (\n node: TSESTree.Node,\n replace: TSESTree.Node,\n owner: TSESTree.Expression,\n ) => {\n const data = { node, messageId: \"minor\" as const };\n const binding = formatBinding(source, program, node);\n if (binding === \"conflict\") {\n const alias = availableAlias(source, node);\n context.report({\n ...data,\n suggest: [\n {\n messageId: \"useAlias\",\n fix: (fixer) =>\n formatFixes(fixer, source, program, replace, owner, alias),\n },\n ],\n });\n return;\n }\n context.report({\n ...data,\n fix: (fixer) => {\n const local = importedFormatLocal(program) ?? \"format\";\n return formatFixes(fixer, source, program, replace, owner, local);\n },\n });\n };\n const inspectFormatter = (node: TSESTree.CallExpression) => {\n const owner = formattedMinor(node);\n if (owner) {\n reportFix(node, node, owner);\n return true;\n }\n return false;\n };\n return {\n Program(node) {\n program = node;\n },\n NewExpression(node) {\n if (\n intlNumberFormat(node) &&\n currencyStyle(node.arguments[1]) &&\n !formatsDividedMinor(node)\n ) {\n context.report({ node, messageId: \"currency\" });\n }\n },\n CallExpression(node) {\n if (inspectFormatter(node)) {\n return;\n }\n if (intlNumberFormat(node) && currencyStyle(node.arguments[1])) {\n context.report({ node, messageId: \"currency\" });\n return;\n }\n if (\n node.callee.type === \"MemberExpression\" &&\n memberName(node.callee) === \"toLocaleString\" &&\n node.arguments.some(currencyStyle)\n ) {\n context.report({ node, messageId: \"currency\" });\n }\n },\n TemplateLiteral(node) {\n for (const expression of node.expressions) {\n const owner = dividedMinorOwner(expression);\n if (owner) {\n reportFix(expression, expression, owner);\n } else if (isAmountMinor(expression)) {\n context.report({ node: expression, messageId: \"minor\" });\n }\n }\n },\n JSXExpressionContainer(node) {\n if (\n node.parent.type === \"JSXAttribute\" ||\n node.expression.type === \"JSXEmptyExpression\"\n ) {\n return;\n }\n if (\n dividedMinorOwner(node.expression) ||\n isAmountMinor(node.expression)\n ) {\n context.report({ node: node.expression, messageId: \"minor\" });\n } else if (isSdkMoney(services, node.expression)) {\n context.report({ node: node.expression, messageId: \"direct\" });\n }\n },\n };\n },\n});\n","import type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { createRule } from \"../create-rule\";\n\ntype Kind = \"add\" | \"checkout\" | \"signin\";\nconst marker: Record<Kind, { name: string; text: string }> = {\n add: { name: \"data-zappie-action\", text: 'data-zappie-action=\"add-to-cart\"' },\n checkout: { name: \"data-zappie-checkout\", text: \"data-zappie-checkout\" },\n signin: { name: \"data-zappie-signin\", text: \"data-zappie-signin\" },\n};\n\nfunction handlerKind(\n source: Readonly<TSESLint.SourceCode>,\n node: TSESTree.JSXAttribute,\n): Kind | undefined {\n const text = node.value ? source.getText(node.value) : \"\";\n if (/\\b(?:cart(?:\\.actions)?\\.add)\\s*\\(/.test(text)) {\n return \"add\";\n }\n if (/\\b(?:checkout|createCheckout\\([^)]*\\))\\.open\\s*\\(/.test(text)) {\n return \"checkout\";\n }\n if (/\\b(?:auth|createCustomer\\([^)]*\\))\\.signIn\\s*\\(/.test(text)) {\n return \"signin\";\n }\n return undefined;\n}\n\nexport const verifyMarkers = createRule<[], \"marker\">({\n name: \"verify-markers\",\n meta: {\n type: \"suggestion\",\n docs: { description: \"Mark shopping actions so verify can find them.\" },\n fixable: \"code\",\n schema: [],\n messages: {\n marker: \"Add {{marker}} so zappie verify can find this shopping action.\",\n },\n },\n defaultOptions: [],\n create(context) {\n return {\n JSXOpeningElement(node) {\n const click = node.attributes.find(\n (attribute): attribute is TSESTree.JSXAttribute =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === \"onClick\",\n );\n if (!click) {\n return;\n }\n const kind = handlerKind(context.sourceCode, click);\n if (\n !kind ||\n node.attributes.some(\n (attribute) =>\n attribute.type === \"JSXAttribute\" &&\n attribute.name.name === marker[kind].name,\n )\n ) {\n return;\n }\n context.report({\n node: click,\n messageId: \"marker\",\n data: { marker: marker[kind].text },\n fix: (fixer) =>\n fixer.insertTextAfter(node.name, ` ${marker[kind].text}`),\n });\n },\n };\n },\n});\n"],"mappings":"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","names":["parser","jsxA11y","reactHooks","ESLintUtils","createRule","name","cartName","storageKey","calleeName","node","variableName","cartInitial","value","cartViaSdk","createRule","context","method","key","id","call","minimatch","endpointPattern","trackingPattern","paymentHostPattern","staticText","node","part","memberName","unwrapExpression","findVariable","source","name","scope","found","jsxAttribute","attribute","jsxValue","isZappieEndpoint","value","isTrackingEndpoint","isInternalSdkImport","isServerFile","filename","patterns","path","pattern","autocomplete","paymentField","providerImport","value","checkoutViaSdk","createRule","context","node","tag","auto","jsxValue","jsxAttribute","name","id","target","cardInput","providerTarget","paymentHostPattern","mappedBy","node","current","parent","firstOnly","source","callback","text","index","imagePriority","createRule","context","images","priority","jsxAttribute","disabled","targetsItem","attribute","expression","target","jsxValue","itemLinkPrefetch","createRule","context","links","node","item","jsxAttribute","defaults","authValue","browserStorage","globals","isStorage","node","memberKey","staticText","authImport","value","noCustomAuth","createRule","context","options","server","isServerFile","kind","type","jsxValue","jsxAttribute","auto","first","key","cookie","message","value","noDirectApi","createRule","context","inspect","node","staticText","isZappieEndpoint","isInternalSdkImport","globalCallName","node","last","staticText","noEval","createRule","context","name","external","value","documentObject","node","staticText","noExternalScripts","createRule","context","element","attribute","jsxAttribute","src","jsxValue","dynamic","isTrackingEndpoint","rel","as","href","property","react","conventionalNamespace","noLazyOverlay","createRule","context","lazyNames","suspenseNames","namespaces","fromNamespace","object","property","wanted","node","specifier","imported","callee","name","ESLintUtils","moneyLiteralPattern","isMoneyLiteral","value","memberName","node","isAmountMinor","amountOwner","dividedMinorOwner","parserServices","context","declaredBySdkMoney","symbol","declaration","path","isSdkMoney","services","checker","tsNode","type","part","property","currencyStyle","noMoneyLiterals","createRule","context","services","parserServices","arithmetic","node","isAmountMinor","isSdkMoney","isMoneyLiteral","dividedMinorOwner","memberName","quasi","serverFiles","vitePublic","namedFunction","node","parent","staticText","directEnv","memberName","contextCloudflare","forbidden","kind","key","noServerEnvInClient","createRule","context","filename","isServerFile","aliases","cloudflareAliases","serverBody","ancestor","kindOf","direct","recordCloudflarePattern","pattern","property","inspectParameters","parameter","imports","imported","value","noTrackingScripts","createRule","context","inspect","node","staticText","isTrackingEndpoint","callee","direct","dataLayer","literal","node","text","staticText","hostname","value","allowed","host","entries","entry","candidate","noUnlistedFetch","createRule","context","options","filename","isServerFile","configured","allow","xhr","inspect","target","isZappieEndpoint","memberName","name","noUnlistedFetchDynamic","objectHtml","node","property","staticText","noUnsafeHtml","createRule","context","source","isFactory","findVariable","definition","isSanitizer","init","sanitized","input","seen","unwrapExpression","binding","nextSeen","initial","setter","updates","reference","call","reportHtmlObject","value","name","dangerous","child","memberName","props","defaults","name","node","carriesKey","keys","key","part","property","overlayPrimitive","createRule","context","options","searchValues","derivedFromSearch","text","value","callee","argument","dialog","role","attribute","open","callName","node","memberName","possibleBlank","staticText","safeExternalLinks","createRule","context","source","tainted","safe","importedSafeRedirect","findVariable","definition","fromRequest","input","unwrapExpression","sameOriginGuard","target","targetOrigin","expression","trustedOrigin","ancestor","test","name","value","jsxValue","jsxAttribute","targetAttribute","targetExpression","relAttribute","rel","fixer","tokens","sdkImport","program","node","importedFormat","declaration","specifier","importFix","fixer","source","local","importedName","named","last","first","text","formatFixes","replace","owner","fixes","addition","formatBinding","scope","variable","importedFormatLocal","availableAlias","names","name","intlNumberFormat","node","callee","memberName","formattedMinor","dividedMinorOwner","formatsDividedMinor","member","call","useFormatMoney","createRule","context","source","services","parserServices","program","reportFix","replace","owner","data","formatBinding","alias","availableAlias","fixer","formatFixes","local","importedFormatLocal","inspectFormatter","currencyStyle","expression","isAmountMinor","isSdkMoney","marker","handlerKind","source","node","text","verifyMarkers","createRule","context","click","attribute","kind","fixer","rules","noDirectApi","noTrackingScripts","checkoutViaSdk","noCustomAuth","noMoneyLiterals","useFormatMoney","overlayPrimitive","cartViaSdk","verifyMarkers","itemLinkPrefetch","imagePriority","noLazyOverlay","noUnsafeHtml","noEval","noExternalScripts","safeExternalLinks","noUnlistedFetch","noUnlistedFetchDynamic","noServerEnvInClient","enabled","name","strictRules","plugin","base","parser","jsxA11y","reactHooks","searchDialogs","typed","src_default","plugin"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-zappie",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Flat ESLint rules for Zappie storefront projects.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,15 +29,15 @@
29
29
  "eslint": "^9.0.0"
30
30
  },
31
31
  "dependencies": {
32
- "@typescript-eslint/parser": "8.48.0",
33
- "@typescript-eslint/utils": "8.48.0",
32
+ "@typescript-eslint/parser": "8.71.2",
33
+ "@typescript-eslint/utils": "8.71.2",
34
34
  "eslint-plugin-jsx-a11y": "6.10.2",
35
- "eslint-plugin-react-hooks": "7.0.1",
36
- "minimatch": "^10.2.5"
35
+ "eslint-plugin-react-hooks": "7.1.1",
36
+ "minimatch": "^10.2.6"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/eslint-plugin-jsx-a11y": "^6.10.1",
40
- "@typescript-eslint/rule-tester": "8.48.0"
40
+ "@typescript-eslint/rule-tester": "8.71.2"
41
41
  },
42
42
  "publishConfig": {
43
43
  "access": "public",