eslint-plugin-zappie 0.3.1 → 0.3.2

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,15 @@
1
1
  # eslint-plugin-zappie
2
2
 
3
+ ## 0.3.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 88f52b6: `zappie/no-server-env-in-client` allows a direct `process.env.NEXT_PUBLIC_ZAPPIE_*` read in client code, as it allows `import.meta.env.VITE_ZAPPIE_*`. Next.js inlines those values into the client build. A destructured or aliased read is still reported, because Next.js doesn't inline it.
8
+ - a151b0b: Menu pages stay cacheable.
9
+
10
+ - `eslint-plugin-zappie`: `zappie/cacheable-menu-routes` reports a menu, item or category route, or the root route, whose `loader` or `headers` reads the customer's session: `getCustomer()`, `getSession()`, `zappieFetch()`, `openSealedValue()`, `.get()` on a session store, the `Cookie` or `Authorization` header (also through a destructured `headers`), or `/_zappie/session` and `/_zappie/api`. It checks `middleware` too. It finds menu pages by file name, not by URL, so list a menu page with another file name in the `routes` option. It warns in `recommended` and is an error in `strict`.
11
+ - `@zappie/starter`: menu and item pages send `public, s-maxage=30, stale-while-revalidate=300`, `Vary: Accept-Encoding, X-Zappie-Draft, X-Zappie-Review` and an `ETag` from the menu version, a hash of the store details and the build. They render in full before their headers are sent, so a render error or a rejected deferred value makes them `private, no-store`. Cart, checkout, order, account and sign-in pages, previews, error pages, responses that set a cookie and `/_zappie/*` send `private, no-store`. A route's own `headers()` wins unless the response sets a cookie or the page fails to render.
12
+
3
13
  ## 0.3.1
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -20,29 +20,30 @@ export default [...zappie.configs.recommended];
20
20
  `recommended` makes literal unlisted request hosts errors. Computed request
21
21
  URLs use the companion `no-unlisted-fetch-dynamic` diagnostic at warning
22
22
  severity because their hosts cannot be proved statically. `verify-markers`
23
- also warns. `strict` makes both warning diagnostics errors.
23
+ and `cacheable-menu-routes` also warn. `strict` makes every warning an error.
24
24
 
25
25
  ## Rules
26
26
 
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. |
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. |
46
+ | `zappie/cacheable-menu-routes` | Keep the session out of menu page loaders so caches can keep them. |
46
47
 
47
48
  `recommended` also turns on the recommended `jsx-a11y` rules as the accessibility baseline. `zappie check` fails a project that turns any of them down.
48
49
 
package/dist/index.d.ts CHANGED
@@ -2,6 +2,13 @@ import * as _typescript_eslint_utils_json_schema from '@typescript-eslint/utils/
2
2
  import * as _typescript_eslint_utils_ts_eslint from '@typescript-eslint/utils/ts-eslint';
3
3
  import { TSESLint } from '@typescript-eslint/utils';
4
4
 
5
+ type Options$2 = [{
6
+ routes?: string[];
7
+ }];
8
+ declare const cacheableMenuRoutes: TSESLint.RuleModule<"sessionRead", Options$2, unknown, TSESLint.RuleListener> & {
9
+ name: string;
10
+ };
11
+
5
12
  declare const cartViaSdk: _typescript_eslint_utils_ts_eslint.RuleModule<"cart", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
6
13
  name: string;
7
14
  };
@@ -179,8 +186,13 @@ declare const plugin: {
179
186
  "no-server-env-in-client": _typescript_eslint_utils_ts_eslint.RuleModule<"serverEnv", [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
180
187
  name: string;
181
188
  };
189
+ "cacheable-menu-routes": _typescript_eslint_utils_ts_eslint.RuleModule<"sessionRead", [{
190
+ routes?: string[];
191
+ }], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
192
+ name: string;
193
+ };
182
194
  };
183
195
  configs: Record<string, ZappieFlatConfig[]>;
184
196
  };
185
197
 
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 };
198
+ export { type ZappieFlatConfig, cacheableMenuRoutes, 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 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};
1
+ import At from"@typescript-eslint/parser";import De from"eslint-plugin-jsx-a11y";import Ot from"eslint-plugin-react-hooks";import{minimatch as Be}from"minimatch";import{ESLintUtils as Je}from"@typescript-eslint/utils";var m=Je.RuleCreator(e=>`https://docs.zappie.ai/lint/${e}`);import{minimatch as _e}from"minimatch";var Ue=/(?:api|accounts|checkout|edge)\.zappie\.ai|\/storefront\/v1|\/customer\/graphql/i,Ve=/googletagmanager\.com|google-analytics\.com|connect\.facebook\.net|analytics\.tiktok\.com|static\.hotjar\.com|cdn\.segment\.com|plausible\.io|clarity\.ms/i,Q=/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 d(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,i){for(let r=e.getScope(t);r;r=r.upper){let c=r.set.get(i);if(c)return c}}function S(e,t){return e.attributes.find(i=>i.type==="JSXAttribute"&&i.name.type==="JSXIdentifier"&&i.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 N=e=>Ue.test(e),L=e=>Ve.test(e),Y=e=>e==="@zappie/sdk/internal"||e.startsWith("@zappie/sdk/internal/");function I(e,t){let i=e.replaceAll("\\","/");return t.some(r=>_e(i,r,{matchBase:!0}))}var F="{_index,index,home,menu,item,items,category,categories,catalogue,collection,collections,product,products}",M="{js,jsx,ts,tsx}",$e=[`**/app/root.${M}`,`**/app/routes/${F}.${M}`,`**/app/routes/${F}.*.${M}`,`**/app/routes/${F}/**/*.${M}`],qe=new Set(["loader","headers","middleware","unstable_middleware"]),He=new Set(["getCustomer","zappieFetch","getSession","requireCustomer","openSealedValue"]),ee=/session/i,Ke=new Set(["cookie","authorization"]),te=/\/_zappie\/(?:session|api\/)/,re=e=>e.parent?.type==="Program"||e.parent?.type==="ExportNamedDeclaration";function We(e){if(e.type==="FunctionDeclaration"&&re(e))return e.id?.name;if(e.type==="VariableDeclarator"&&e.id.type==="Identifier"&&re(e.parent))return e.id.name}function se(e,t){return b(e,t,t.name)?.defs[0]}function Ze(e,t){if(t.type==="MemberExpression")return d(t)==="headers";if(t.type!=="Identifier")return!1;if(t.name==="headers")return!0;let i=se(e,t);if(!i)return!1;let r=i.name;for(;r.parent?.type==="AssignmentPattern";)r=r.parent;return r.parent?.type==="Property"&&r.parent.value===r&&!r.parent.computed&&r.parent.key.type==="Identifier"&&r.parent.key.name==="headers"?!0:i.node.type==="VariableDeclarator"&&i.node.id===i.name&&i.node.init?.type==="MemberExpression"&&d(i.node.init)==="headers"}var Ge=e=>e?.type==="TSTypeReference"&&e.typeName.type==="Identifier"&&e.typeName.name.endsWith("SessionStore"),ie=e=>e?.type==="CallExpression"&&e.callee.type==="Identifier"&&/SessionStore$/.test(e.callee.name);function Qe(e,t){if(ie(t))return!0;if(t.type==="MemberExpression")return ee.test(d(t)??"");if(t.type!=="Identifier")return!1;if(ee.test(t.name))return!0;let i=se(e,t);if(!i)return!1;let r=i.name;return Ge(r.typeAnnotation?.typeAnnotation)||i.node.type==="VariableDeclarator"&&i.node.id===r&&ie(i.node.init)}function Ye(e){return e.callee.type==="Identifier"?e.callee.name:e.callee.type==="MemberExpression"?d(e.callee):void 0}var ne=m({name:"cacheable-menu-routes",meta:{type:"suggestion",docs:{description:"Keep the customer's session out of menu page loaders, so shared caches can keep those pages."},schema:[{type:"object",properties:{routes:{type:"array",items:{type:"string"}}},additionalProperties:!1}],messages:{sessionRead:"This menu page reads the customer's session on the server, so no shared cache can keep it. Show customer details in the browser with useCustomer() from @zappie/sdk/customer/react."}},defaultOptions:[{}],create(e,[t]){let i=e.filename.replaceAll("\\","/");if(!(t.routes??$e).some(a=>Be(i,a,{dot:!0})))return{};let r=a=>e.sourceCode.getAncestors(a).some(s=>qe.has(We(s)??"")),c=a=>{r(a)&&e.report({node:a,messageId:"sessionRead"})};return{CallExpression(a){let s=Ye(a);if(s&&He.has(s)){c(a);return}if(s!=="get"||a.callee.type!=="MemberExpression")return;let p=a.callee.object,n=u(a.arguments[0]??null)?.toLowerCase();(n!==void 0&&Ke.has(n)&&Ze(e.sourceCode,p)||Qe(e.sourceCode,p))&&c(a)},Literal(a){typeof a.value=="string"&&te.test(a.value)&&c(a)},TemplateLiteral(a){te.test(u(a)??"")&&c(a)}}}});var et=/^(cart|basket|bag)(Items|Lines|Count|Total)?$/i,tt=/cart|basket|bag/i;function D(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function rt(e){return e.type==="Identifier"?e.name:e.type==="ArrayPattern"&&e.elements[0]?.type==="Identifier"?e.elements[0].name:void 0}function it(e){let t=D(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 ae=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 i=D(t.callee),r=t.arguments[0];["getItem","setItem","removeItem","open"].includes(i??"")&&r?.type==="Literal"&&typeof r.value=="string"&&tt.test(r.value)&&e.report({node:t,messageId:"cart"})},VariableDeclarator(t){if(t.init?.type!=="CallExpression")return;let i=rt(t.id),r=D(t.init.callee);i&&et.test(i)&&["useState","useReducer","createContext"].includes(r??"")&&it(t.init)&&e.report({node:t,messageId:"cart"}),i&&/cart/i.test(i)&&["create","createStore","atom"].includes(r??"")&&e.report({node:t,messageId:"cart"})}}}});var st=new Set(["cc-number","cc-exp","cc-csc","cc-name"]),oe=/card.?number|cvv|cvc|expiry/i;function nt(e){return e.startsWith("@paystack/")||e==="react-paystack"||e==="flutterwave-react-v3"||e.startsWith("@stripe/")}var pe=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){nt(t.source.value)&&e.report({node:t.source,messageId:"checkout"})},JSXOpeningElement(t){let i=t.name.type==="JSXIdentifier"?t.name.name.toLowerCase():"",r=g(S(t,"autocomplete")),c=g(S(t,"name")),a=g(S(t,"id")),s=g(S(t,i==="iframe"?"src":"href")),p=i==="input"&&(r!==void 0&&st.has(r.toLowerCase())||c!==void 0&&oe.test(c)||a!==void 0&&oe.test(a)),n=(i==="iframe"||i==="a")&&s!==void 0&&Q.test(s);(p||n)&&e.report({node:t,messageId:"checkout"})}}}});function at(e){let t=e;for(;t?.parent;){let i=t.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;t=i}}function ot(e,t){let i=e.getText(t.parent);if(/\.slice\(\s*0\s*,\s*1\s*\)\.map/.test(i))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 ce=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(i){let r=S(i,"priority"),c=r?.value?.type==="JSXExpressionContainer"&&r.value.expression.type==="Literal"&&r.value.expression.value===!1;if(i.name.type!=="JSXIdentifier"||i.name.name!=="Image"||!r||c)return;t.push(i);let a=at(i);a&&!ot(e.sourceCode,a)&&e.report({node:r,messageId:"mapped"})},"Program:exit"(){if(/(?:^|\/)routes?\/[^/]+\.[jt]sx$/.test(e.filename.replaceAll("\\","/"))&&t.length>2)for(let i of t.slice(2))e.report({node:i,messageId:"routeLimit"})}}}});function pt(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 i=g(e);return i!==void 0&&/^\/item(?:\/|$)/.test(i)}var le=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(i){if(["react-router","react-router-dom"].includes(i.source.value))for(let r of i.specifiers)r.type==="ImportSpecifier"&&r.imported.type==="Identifier"&&["Link","NavLink"].includes(r.imported.name)&&t.add(r.local.name)},JSXOpeningElement(i){i.name.type==="JSXIdentifier"&&t.has(i.name.name)&&pt(S(i,"to"))&&!S(i,"prefetch")&&e.report({node:i,messageId:"prefetch"})}}}});var ct=["**/*.server.*","**/server/**"],X=/otp|verify.?code|access.?token|refresh.?token|id.?token/i,me=new Set(["localStorage","sessionStorage"]),lt=new Set(["window","globalThis","self"]);function ue(e){return e.type==="Identifier"?me.has(e.name):e.type==="MemberExpression"&&!e.computed&&e.object.type==="Identifier"&&lt.has(e.object.name)&&e.property.type==="Identifier"&&me.has(e.property.name)}function mt(e){return!e.computed&&e.property.type==="Identifier"?e.property.name:u(e.property)}function ut(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 fe=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 i=I(e.filename,t.serverFiles??ct);return{ImportDeclaration(r){let c=ut(r.source.value);(c==="all"||c==="server"&&!i)&&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")),a=g(S(r,"autocomplete"));(c?.toLowerCase()==="password"||a!==void 0&&["one-time-code","current-password","new-password"].includes(a.toLowerCase()))&&e.report({node:r,messageId:"auth"})},CallExpression(r){if(r.callee.type!=="MemberExpression"||!ue(r.callee.object))return;let c=r.arguments[0],a=c?.type==="Identifier"?c.name:u(c??null);a&&X.test(a)&&e.report({node:r,messageId:"auth"})},AssignmentExpression(r){if(r.left.type==="MemberExpression"&&ue(r.left.object)){let s=mt(r.left);s&&X.test(s)&&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",a=u(r.right);c&&a&&X.test(a)&&e.report({node:r,messageId:"auth"})}}}});function ft(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 de=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=i=>{if(i.parent.type==="ImportDeclaration")return;let r=u(i);r&&N(r)&&e.report({node:i,messageId:ft(r)})};return{Literal:t,TemplateLiteral:t,ImportDeclaration(i){Y(i.source.value)&&e.report({node:i.source,messageId:"internal"})}}}});function J(e){if(e.type==="Identifier")return e.name;if(e.type==="SequenceExpression"){let t=e.expressions.at(-1);return t?J(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 ye=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 i=J(t.callee);if(i==="eval"||i==="Function"){e.report({node:t,messageId:"eval"});return}["setTimeout","setInterval"].includes(i??"")&&u(t.arguments[0]??null)!==void 0&&e.report({node:t,messageId:"eval"})},NewExpression(t){J(t.callee)==="Function"&&e.report({node:t,messageId:"eval"})}}}});var Se=e=>!!(e&&/^(?:[a-z][a-z\d+.-]*:)?\/\//i.test(e)),dt=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"),ge=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 i=t.name.name.toLowerCase();if(i==="script"){let s=S(t,"src"),p=g(s),n=s?.value?.type==="JSXExpressionContainer"&&!(s.value.expression.type==="Literal"||s.value.expression.type==="TemplateLiteral"&&s.value.expression.expressions.length===0);s&&(n||p===void 0||Se(p)&&!L(p))&&e.report({node:t,messageId:"externalScript"});return}if(i!=="link")return;let r=g(S(t,"rel"))?.toLowerCase(),c=g(S(t,"as"))?.toLowerCase(),a=g(S(t,"href"));(r==="preload"&&c==="script"||r==="modulepreload")&&Se(a)&&!L(a??"")&&e.report({node:t,messageId:"externalScript"})},CallExpression(t){if(t.callee.type==="MemberExpression"&&!t.callee.computed&&dt(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 yt=new Set(["react"]),St="React",Ee=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,i=new Set,r=new Set([St]),c=(a,s,p)=>a.type==="Identifier"&&r.has(a.name)&&s.type==="Identifier"&&s.name===p;return{ImportDeclaration(a){if(yt.has(a.source.value))for(let s of a.specifiers){if(s.type!=="ImportSpecifier"){r.add(s.local.name);continue}let p=s.imported.type==="Identifier"?s.imported.name:s.imported.value;p==="lazy"?t.add(s.local.name):p==="Suspense"&&i.add(s.local.name)}},CallExpression(a){let s=a.callee;(s.type==="Identifier"&&t.has(s.name)||s.type==="MemberExpression"&&!s.computed&&c(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"&&r.has(s.object.name)&&s.property.name==="Suspense")&&e.report({node:a,messageId:"suspense"})}}}});import{ESLintUtils as gt}from"@typescript-eslint/utils";var Et=/[₦€£]\s?\d|\$\s?\d|NGN\s?\d|\d+(?:\.\d{2})?\s?(?:naira|NGN)/i;function R(e){return e.trim()!=="$1"&&Et.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 Tt(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 Tt(e.left)}function z(e){try{return gt.getParserServices(e,!0)}catch{return}}function ht(e){return e.getName().includes("MoneyBrand")?(e.getDeclarations()??[]).some(t=>{let i=t.getSourceFile().fileName.replaceAll("\\","/");return i.endsWith("/packages/sdk/src/money.ts")||/\/node_modules\/@zappie\/sdk\/.*\.d\.ts$/.test(i)}):!1}function A(e,t){if(!e?.program)return!1;let i=e.program.getTypeChecker(),r=e.esTreeNodeToTSNodeMap.get(t),c=i.getTypeAtLocation(r);return(c.isUnionOrIntersection()?c.types:[c]).some(s=>s.getProperties().some(p=>ht(p)))}function O(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 Te=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),i=r=>k(r)||A(t,r);return{Literal(r){typeof r.value=="string"&&r.parent.type!=="ImportDeclaration"&&R(r.value)&&e.report({node:r,messageId:"literal"})},JSXText(r){R(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"||(i(r.left)||i(r.right))&&e.report({node:r,messageId:"arithmetic"}))},TemplateLiteral(r){for(let c of r.quasis)R(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 bt=["**/*.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}"],xt=new Set(["MODE","DEV","PROD","SSR","BASE_URL"]);function he(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 be(e){if(e.type==="MemberExpression"&&e.object.type==="Identifier"&&e.object.name==="process"&&d(e)==="env")return"process";if(e.type==="MemberExpression"&&e.object.type==="MetaProperty"&&e.object.meta.name==="import"&&e.object.property.name==="meta"&&d(e)==="env")return"vite"}function xe(e){return e.type!=="MemberExpression"||d(e)!=="cloudflare"?!1:e.object.type==="Identifier"&&e.object.name==="context"||e.object.type==="MemberExpression"&&d(e.object)==="context"}function ve(e,t,i=!1){return e==="process"?t!=="NODE_ENV"&&!(i&&t?.startsWith("NEXT_PUBLIC_ZAPPIE_")):t===void 0||!t.startsWith("VITE_ZAPPIE_")&&!xt.has(t)}var Ie=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,bt)||/(?:^|\/)(?:test|tests|__tests__)(?:\/|$)/.test(t)||/\.(?:test|spec|config)\.[cm]?[jt]sx?$/.test(t))return{};let i=new Map,r=new Set,c=n=>e.sourceCode.getAncestors(n).some(o=>["loader","action"].includes(he(o)??"")),a=n=>{let o=be(n);return o||(n.type==="Identifier"?i.get(n.name):void 0)},s=n=>{if(n.type==="ObjectPattern")for(let o of n.properties)o.type==="Property"&&(o.key.type==="Identifier"&&o.key.name==="cloudflare"||u(o.key)==="cloudflare")&&o.value.type==="Identifier"&&r.add(o.value.name),o.type==="Property"&&(o.key.type==="Identifier"&&o.key.name==="context"||u(o.key)==="context")&&s(o.value)},p=n=>{if(!(c(n)||["loader","action"].includes(he(n)??"")))for(let o of n.params)o.type!=="RestElement"&&s(o)};return{ArrowFunctionExpression:p,FunctionDeclaration:p,FunctionExpression:p,VariableDeclarator(n){if(c(n)||!n.init)return;let o=a(n.init);if(n.id.type==="Identifier"&&o){i.set(n.id.name,o);return}if(n.id.type==="ObjectPattern"&&o){for(let l of n.id.properties){let y=l.type==="Property"?l.key.type==="Identifier"?l.key.name:u(l.key):void 0;ve(o,y)&&e.report({node:l,messageId:"serverEnv"})}return}if(n.id.type==="ObjectPattern"&&xe(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 o=a(n.object);if(o&&ve(o,d(n),be(n.object)===o)){e.report({node:n,messageId:"serverEnv"});return}d(n)==="env"&&(xe(n.object)||n.object.type==="Identifier"&&r.has(n.object.name))&&e.report({node:n,messageId:"serverEnv"})}}}});var vt=["react-ga4","react-facebook-pixel","@vercel/analytics","posthog-js"];function It(e){return vt.includes(e)||e.startsWith("@segment/")}var ke=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=i=>{if(i.parent.type==="ImportDeclaration")return;let r=u(i);r&&L(r)&&e.report({node:i,messageId:"tracking"})};return{Literal:t,TemplateLiteral:t,ImportDeclaration(i){It(i.source.value)&&e.report({node:i.source,messageId:"tracking"})},CallExpression(i){let r=i.callee,c=r.type==="Identifier"&&["gtag","fbq"].includes(r.name),a=r.type==="MemberExpression"&&!r.computed&&r.object.type==="Identifier"&&r.object.name==="dataLayer"&&r.property.type==="Identifier"&&r.property.name==="push";(c||a)&&e.report({node:i,messageId:"tracking"})}}}});function je(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 je(e.arguments[0])}function kt(e){if(!(!/^(?:https?|wss?):\/\//i.test(e)&&!e.startsWith("//")))try{return new URL(e,"https://storefront.invalid").hostname.toLowerCase()}catch{return}}function jt(e,t){return t.some(i=>{let r=i.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 i=e.filename.replaceAll("\\","/");if(I(i,["**/test/**","**/tests/**","**/__tests__/**","**/*.{test,spec}.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"])||/(?:^|\/)(?:test|tests|__tests__)(?:\/|$)/.test(i)||/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(i))return{};let r=e.settings?.zappie?.outboundAllow,c=Array.isArray(r)?r.filter(p=>typeof p=="string"):[],a=new Set,s=(p,n)=>{let o=je(n);if(o===void 0){t.dynamicOnly&&e.report({node:p,messageId:"dynamicRequest"});return}if(t.dynamicOnly)return;let l=kt(o);!l||N(o)||jt(l,c)||e.report({node:p,messageId:"unlistedRequest",data:{host:l}})};return{VariableDeclarator(p){p.id.type==="Identifier"&&p.init?.type==="NewExpression"&&p.init.callee.type==="Identifier"&&p.init.callee.name==="XMLHttpRequest"&&a.add(p.id.name)},CallExpression(p){if(p.callee.type==="Identifier"&&p.callee.name==="fetch"||p.callee.type==="MemberExpression"&&d(p.callee)==="fetch"&&p.callee.object.type==="Identifier"&&["window","globalThis"].includes(p.callee.object.name)){s(p,p.arguments[0]);return}if(p.callee.type!=="MemberExpression")return;let o=d(p.callee);if(o==="sendBeacon"&&p.callee.object.type==="Identifier"&&p.callee.object.name==="navigator"){s(p,p.arguments[0]);return}o==="open"&&(p.callee.object.type==="Identifier"&&a.has(p.callee.object.name)||p.callee.object.type==="NewExpression"&&p.callee.object.callee.type==="Identifier"&&p.callee.object.callee.name==="XMLHttpRequest")&&s(p,p.arguments[1])},NewExpression(p){p.callee.type==="Identifier"&&["WebSocket","EventSource"].includes(p.callee.name)&&s(p,p.arguments[0])}}}}),we={...C,meta:{...C.meta,docs:{...C.meta.docs,url:"https://docs.zappie.ai/lint/no-unlisted-fetch-dynamic"}}};function wt(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 Le=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,i=s=>!!b(t,s,s.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=s=>!!b(t,s,s.name)?.defs.some(n=>{if(n.type!=="Variable"||n.node.type!=="VariableDeclarator")return!1;let o=n.node.init;return o?.type==="CallExpression"&&o.callee.type==="Identifier"&&i(o.callee)}),c=(s,p=new Set)=>{if(!s||s.type==="SpreadElement")return!1;let n=h(s);if(n.type==="Literal")return n.value==="";if(n.type==="ConditionalExpression")return c(n.consequent,p)&&c(n.alternate,p);if(n.type==="LogicalExpression")return c(n.left,p)&&c(n.right,p);if(n.type==="CallExpression")return n.callee.type==="Identifier"&&r(n.callee)?!0:n.callee.type==="CallExpression"&&n.callee.callee.type==="Identifier"&&i(n.callee.callee);if(n.type!=="Identifier")return!1;let o=b(t,n,n.name);if(!o||p.has(o))return!1;let l=new Set(p).add(o);for(let y of o.defs){if(y.type!=="Variable"||y.node.type!=="VariableDeclarator")continue;if(y.node.id.type==="Identifier")return c(y.node.init,l);if(y.node.id.type!=="ArrayPattern"||y.node.id.elements[0]?.type!=="Identifier"||y.node.id.elements[0].name!==n.name||y.node.init?.type!=="CallExpression"||y.node.init.callee.type!=="Identifier"||y.node.init.callee.name!=="useState")continue;let E=y.node.init.arguments[0],f=y.node.id.elements[1];if(!c(E,l)||f?.type!=="Identifier")return!1;let Z=b(t,f,f.name)?.references.filter(w=>w.identifier.parent.type==="CallExpression"&&w.identifier.parent.callee===w.identifier)??[];return Z.length>0&&Z.every(w=>{let G=w.identifier.parent;return G.type==="CallExpression"&&c(G.arguments[0],l)})}return!1},a=(s,p)=>{(p?.type!=="ObjectExpression"||!c(wt(p)))&&e.report({node:s,messageId:"unsafeHtml"})};return{JSXAttribute(s){if(s.name.type!=="JSXIdentifier")return;let p=s.name.name.toLowerCase();!["dangerouslysetinnerhtml","srcdoc"].includes(p)||s.value?.type!=="JSXExpressionContainer"||(p==="dangerouslysetinnerhtml"?a(s,s.value.expression):c(s.value.expression)||e.report({node:s,messageId:"unsafeHtml"}))},JSXSpreadAttribute(s){if(s.argument.type!=="ObjectExpression")return;let p=s.argument.properties.find(n=>n.type==="Property"&&(n.key.type==="Identifier"&&n.key.name==="dangerouslySetInnerHTML"||u(n.key)==="dangerouslySetInnerHTML"));p&&a(s,p.value)},JSXElement(s){if(s.openingElement.name.type==="JSXIdentifier"&&s.openingElement.name.name.toLowerCase()==="script")for(let p of s.children)p.type==="JSXExpressionContainer"&&p.expression.type!=="JSXEmptyExpression"&&e.report({node:p,messageId:"unsafeHtml"})},AssignmentExpression(s){s.left.type==="MemberExpression"&&["innerHTML","outerHTML"].includes(d(s.left)??"")&&!c(s.right)&&e.report({node:s,messageId:"unsafeHtml"})},CallExpression(s){if(s.callee.type==="MemberExpression"&&d(s.callee)==="insertAdjacentHTML"){c(s.arguments[1])||e.report({node:s,messageId:"unsafeHtml"});return}if(!(s.callee.type==="Identifier"&&s.callee.name==="createElement"||s.callee.type==="MemberExpression"&&d(s.callee)==="createElement"&&s.callee.object.type==="Identifier"&&s.callee.object.name==="React"))return;if(u(s.arguments[0]??null)?.toLowerCase()==="script")for(let l of s.arguments.slice(2))l.type!=="SpreadElement"&&e.report({node:l,messageId:"unsafeHtml"});let n=s.arguments[1];if(n?.type!=="ObjectExpression")return;let o=n.properties.find(l=>l.type==="Property"&&(l.key.type==="Identifier"&&l.key.name==="dangerouslySetInnerHTML"||u(l.key)==="dangerouslySetInnerHTML"));o&&a(s,o.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 _(e){if(e.type==="Identifier")return e.name;if(e.type==="MemberExpression"&&e.property.type==="Identifier")return e.property.name}function Ce(e,t){return e.type==="Literal"&&typeof e.value=="string"?[...t].some(i=>new RegExp(`[?&]${i}=`).test(e.value)):e.type==="TemplateLiteral"?e.quasis.some(i=>Ce({...i,type:"Literal",value:i.value.raw},t)):e.type==="ObjectExpression"?e.properties.some(i=>i.type==="Property"&&(i.key.type==="Identifier"&&t.has(i.key.name)||i.key.type==="Literal"&&typeof i.key.value=="string"&&t.has(i.key.value))):!1}var Ne=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 i=new Set([...Lt,...t.keys??[]]),r=new Set,c=a=>{let s=e.sourceCode.getText(a);return s.includes("useSearchParams(")||[...r].some(p=>new RegExp(`\\b${p}\\b`).test(s))};return{VariableDeclarator(a){a.init?.type==="CallExpression"&&_(a.init.callee)==="useSearchParams"?a.id.type==="Identifier"?r.add(a.id.name):a.id.type==="ArrayPattern"&&a.id.elements[0]?.type==="Identifier"&&r.add(a.id.elements[0].name):a.id.type==="Identifier"&&a.init&&c(a.init)&&r.add(a.id.name)},CallExpression(a){let s=_(a.callee);(s==="setSearchParams"||s==="navigate"||s==="pushState"||s==="replaceState"||a.callee.type==="CallExpression"&&_(a.callee.callee)==="useNavigate")&&a.arguments.some(n=>n.type!=="SpreadElement"&&Ce(n,i))&&e.report({node:a,messageId:"overlay"})},JSXOpeningElement(a){let s=a.name.type==="JSXIdentifier"&&a.name.name==="dialog",p=a.attributes.some(o=>o.type==="JSXAttribute"&&o.name.name==="role"&&o.value?.type==="Literal"&&o.value.value==="dialog"),n=a.attributes.find(o=>o.type==="JSXAttribute"&&o.name.name==="open");(s||p)&&n?.type==="JSXAttribute"&&n.value?.type==="JSXExpressionContainer"&&c(n.value.expression)&&e.report({node:a,messageId:"overlay"})}}}});function Ct(e){return e.callee.type==="Identifier"?e.callee.name:e.callee.type==="MemberExpression"?d(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 Me=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,i=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"),a=n=>{if(!n||n.type==="SpreadElement")return!1;let o=h(n);return o.type==="Identifier"?i.has(o.name)&&!r.has(o.name):o.type==="MemberExpression"?o.object.type==="Identifier"&&o.object.name==="request"&&d(o)==="url"?!0:a(o.object):o.type==="CallExpression"?o.callee.type==="Identifier"&&c(o.callee)?!1:o.callee.type==="Identifier"&&o.callee.name==="safeRedirect"?o.arguments.some(a):!!(o.callee.type==="MemberExpression"&&d(o.callee)==="get"&&(o.callee.object.type==="MemberExpression"&&d(o.callee.object)==="searchParams"||o.callee.object.type==="Identifier"&&/searchParams/i.test(o.callee.object.name)||a(o.callee.object))):o.type==="NewExpression"?o.callee.type==="Identifier"&&o.callee.name==="URL"&&o.arguments.some(a):o.type==="ConditionalExpression"?a(o.consequent)||a(o.alternate):o.type==="LogicalExpression"?a(o.left)||a(o.right):o.type==="BinaryExpression"?a(o.left):o.type==="TemplateLiteral"?o.expressions.some(a):!1},s=(n,o)=>{if(o.type!=="Identifier")return!1;let l=E=>{let f=h(E);return f.type==="MemberExpression"&&d(f)==="origin"&&f.object.type==="Identifier"&&f.object.name===o.name},y=E=>{let f=h(E);return f.type==="MemberExpression"&&d(f)==="origin"&&(f.object.type==="Identifier"&&f.object.name==="location"||a(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)&&y(f.right)||y(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):a(n.init)&&i.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 o=S(n,"target"),l=o?.value?.type==="JSXExpressionContainer"?o.value.expression:o?.value;if(!j(l))return;let y=S(n,"rel"),E=g(y);E?.toLowerCase().split(/\s+/).includes("noopener")||e.report({node:n,messageId:"noopener",fix(f){if(!y)return f.insertTextAfter(n.name,' rel="noopener noreferrer"');if(!y.value||y.value.type!=="Literal")return null;let v=new Set((E??"").split(/\s+/).filter(Boolean));return v.add("noopener"),v.add("noreferrer"),f.replaceText(y,`rel="${[...v].join(" ")}"`)}})},CallExpression:n=>{let o=Ct(n);if(!["redirect","navigate","assign","replace"].includes(o??""))return;let l=n.arguments[0];l&&l.type!=="SpreadElement"&&a(l)&&!s(n,h(l))&&e.report({node:l,messageId:"redirect"})},AssignmentExpression(n){n.left.type==="MemberExpression"&&d(n.left)==="href"&&(n.left.object.type==="Identifier"&&n.left.object.name==="location"||n.left.object.type==="MemberExpression"&&d(n.left.object)==="location")&&a(n.right)&&e.report({node:n.right,messageId:"redirect"})}}}});function U(e){return e.body.find(t=>t.type==="ImportDeclaration"&&t.source.value==="@zappie/sdk")}function V(e){return e?.specifiers.find(t=>t.type==="ImportSpecifier"&&t.imported.type==="Identifier"&&t.imported.name==="format")}function Nt(e,t,i,r){let c=U(i);if(V(c)?.local.name===r)return;let a=r==="format"?"format":`format as ${r}`,s=c?.specifiers.filter(o=>o.type==="ImportSpecifier");if(c&&c.importKind!=="type"&&s?.length){let o=s.at(-1);if(o)return e.insertTextAfter(o,`, ${a}`)}let p=i.body[0],n=`import { ${a} } from "@zappie/sdk";
2
+ `;return p?e.insertTextBefore(p,n):e.insertTextAfter(i,n)}function B(e,t,i,r,c,a){let s=[e.replaceText(r,`${a}.money(${t.getText(c)})`)],p=Nt(e,t,i,a);return p&&s.push(p),s}function Re(e,t,i){if(V(U(t)))return"imported";let r=e.getScope(i);for(;r;){if(r.variables.some(c=>c.name==="format"))return"conflict";r=r.upper}return"available"}function ze(e){return V(U(e))?.local.name}function Ae(e,t){let i=new Set,r=e.getScope(t);for(;r;){for(let c of r.variables)i.add(c.name);r=r.upper}for(let c of["zappieFormat","zappieMoneyFormat","formatMoneyValue"])if(!i.has(c))return c;return"zappieSdkFormat"}function Oe(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 Mt(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 Rt(e){let t=e.parent,i=t.type==="MemberExpression"?t.parent:void 0;return!!(i?.type==="CallExpression"&&i.callee===t&&T(t)==="format"&&x(i.arguments[0]??i))}var Pe=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,i=z(e),r,c=(s,p,n)=>{let o={node:s,messageId:"minor"};if(Re(t,r,s)==="conflict"){let y=Ae(t,s);e.report({...o,suggest:[{messageId:"useAlias",fix:E=>B(E,t,r,p,n,y)}]});return}e.report({...o,fix:y=>{let E=ze(r)??"format";return B(y,t,r,p,n,E)}})},a=s=>{let p=Mt(s);return p?(c(s,s,p),!0):!1};return{Program(s){r=s},NewExpression(s){Oe(s)&&O(s.arguments[1])&&!Rt(s)&&e.report({node:s,messageId:"currency"})},CallExpression(s){if(!a(s)){if(Oe(s)&&O(s.arguments[1])){e.report({node:s,messageId:"currency"});return}s.callee.type==="MemberExpression"&&T(s.callee)==="toLocaleString"&&s.arguments.some(O)&&e.report({node:s,messageId:"currency"})}},TemplateLiteral(s){for(let p of s.expressions){let n=x(p);n?c(p,p,n):k(p)&&e.report({node:p,messageId:"minor"})}},JSXExpressionContainer(s){s.parent.type==="JSXAttribute"||s.expression.type==="JSXEmptyExpression"||(x(s.expression)||k(s.expression)?e.report({node:s.expression,messageId:"minor"}):A(i,s.expression)&&e.report({node:s.expression,messageId:"direct"}))}}}});var $={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 zt(e,t){let i=t.value?e.getText(t.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 Fe=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 i=t.attributes.find(c=>c.type==="JSXAttribute"&&c.name.name==="onClick");if(!i)return;let r=zt(e.sourceCode,i);!r||t.attributes.some(c=>c.type==="JSXAttribute"&&c.name.name===$[r].name)||e.report({node:i,messageId:"marker",data:{marker:$[r].text},fix:c=>c.insertTextAfter(t.name,` ${$[r].text}`)})}}}});var Xe={"no-direct-api":de,"no-tracking-scripts":ke,"checkout-via-sdk":pe,"no-custom-auth":fe,"no-money-literals":Te,"use-format-money":Pe,"overlay-primitive":Ne,"cart-via-sdk":ae,"verify-markers":Fe,"item-link-prefetch":le,"image-priority":ce,"no-lazy-overlay":Ee,"no-unsafe-html":Le,"no-eval":ye,"no-external-scripts":ge,"safe-external-links":Me,"no-unlisted-fetch":C,"no-unlisted-fetch-dynamic":we,"no-server-env-in-client":Ie,"cacheable-menu-routes":ne},K=Object.fromEntries(Object.keys(Xe).map(e=>[`zappie/${e}`,e==="verify-markers"||e==="no-unlisted-fetch-dynamic"||e==="cacheable-menu-routes"?"warn":"error"])),Pt={...K,"zappie/verify-markers":"error","zappie/cacheable-menu-routes":"error","zappie/no-unlisted-fetch-dynamic":["error",{dynamicOnly:!0}]};K["zappie/no-unlisted-fetch-dynamic"]=["warn",{dynamicOnly:!0}];var W={meta:{name:"eslint-plugin-zappie",version:"0.0.0"},rules:Xe,configs:{}},P={name:"zappie/rules",files:["**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts}"],languageOptions:{parser:At},plugins:{zappie:W,"jsx-a11y":De,"react-hooks":Ot},rules:{...De.flatConfigs.recommended.rules,"react-hooks/rules-of-hooks":"error",...K}},q={name:"zappie/search-dialogs",files:["**/search/**/*.{jsx,tsx}","**/*search*.{jsx,tsx}"],rules:{"jsx-a11y/no-autofocus":"off"}},H={name:"zappie/typed",files:["**/*.{ts,tsx,mts,cts}"],languageOptions:{parserOptions:{projectService:!0}}};W.configs={recommended:[P,H,q],strict:[{...P,rules:{...P.rules,...Pt}},H,q],"zappie-internal":[P,H,q,{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 fi=W;export{ne as cacheableMenuRoutes,ae as cartViaSdk,pe as checkoutViaSdk,fi as default,ce as imagePriority,le as itemLinkPrefetch,fe as noCustomAuth,de as noDirectApi,ye as noEval,ge as noExternalScripts,Ee as noLazyOverlay,Te as noMoneyLiterals,Ie as noServerEnvInClient,ke as noTrackingScripts,C as noUnlistedFetch,Le as noUnsafeHtml,Ne as overlayPrimitive,Me as safeExternalLinks,Pe as useFormatMoney,Fe 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-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"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/rules/cacheable-menu-routes.ts","../src/create-rule.ts","../src/matchers.ts","../src/rules/cart-via-sdk.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 { cacheableMenuRoutes } from \"./rules/cacheable-menu-routes\";\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 \"cacheable-menu-routes\": cacheableMenuRoutes,\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\" ||\n name === \"no-unlisted-fetch-dynamic\" ||\n name === \"cacheable-menu-routes\"\n ? \"warn\"\n : \"error\",\n ]),\n);\nconst strictRules = {\n ...enabled,\n \"zappie/verify-markers\": \"error\",\n \"zappie/cacheable-menu-routes\": \"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 cacheableMenuRoutes,\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 type { TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport { minimatch } from \"minimatch\";\nimport { createRule } from \"../create-rule\";\nimport { findVariable, memberName, staticText } from \"../matchers\";\n\nconst pages =\n \"{_index,index,home,menu,item,items,category,categories,catalogue,collection,collections,product,products}\";\nconst extensions = \"{js,jsx,ts,tsx}\";\n// The root route's loader runs for every page, the menu pages included.\nconst menuRoutes = [\n `**/app/root.${extensions}`,\n `**/app/routes/${pages}.${extensions}`,\n `**/app/routes/${pages}.*.${extensions}`,\n `**/app/routes/${pages}/**/*.${extensions}`,\n];\nconst serverExports = new Set([\n \"loader\",\n \"headers\",\n \"middleware\",\n \"unstable_middleware\",\n]);\nconst sessionCalls = new Set([\n \"getCustomer\",\n \"zappieFetch\",\n \"getSession\",\n \"requireCustomer\",\n \"openSealedValue\",\n]);\nconst sessionStoreName = /session/i;\nconst sessionHeaders = new Set([\"cookie\", \"authorization\"]);\nconst sessionRoutes = /\\/_zappie\\/(?:session|api\\/)/;\n\ntype Options = [{ routes?: string[] }];\n\nconst topLevel = (node: TSESTree.Node) =>\n node.parent?.type === \"Program\" ||\n node.parent?.type === \"ExportNamedDeclaration\";\n\n// A route module's own loader, headers or middleware, so a `const loader`\n// nested in another function isn't one.\nfunction exportedName(node: TSESTree.Node): string | undefined {\n if (node.type === \"FunctionDeclaration\" && topLevel(node)) {\n return node.id?.name;\n }\n if (\n node.type === \"VariableDeclarator\" &&\n node.id.type === \"Identifier\" &&\n topLevel(node.parent)\n ) {\n return node.id.name;\n }\n return undefined;\n}\n\ntype Source = Readonly<TSESLint.SourceCode>;\n\nfunction definitionOf(source: Source, node: TSESTree.Identifier) {\n return findVariable(source, node, node.name)?.defs[0];\n}\n\n// `headers.get()` after `const { headers } = request`, `{ headers: h }`,\n// `({ request: { headers } })` or `const h = request.headers`.\nfunction isHeaders(source: Source, node: TSESTree.Node): boolean {\n if (node.type === \"MemberExpression\") {\n return memberName(node) === \"headers\";\n }\n if (node.type !== \"Identifier\") {\n return false;\n }\n if (node.name === \"headers\") {\n return true;\n }\n const definition = definitionOf(source, node);\n if (!definition) {\n return false;\n }\n let binding: TSESTree.Node = definition.name;\n while (binding.parent?.type === \"AssignmentPattern\") {\n binding = binding.parent;\n }\n if (\n binding.parent?.type === \"Property\" &&\n binding.parent.value === binding &&\n !binding.parent.computed &&\n binding.parent.key.type === \"Identifier\" &&\n binding.parent.key.name === \"headers\"\n ) {\n return true;\n }\n return (\n definition.node.type === \"VariableDeclarator\" &&\n definition.node.id === definition.name &&\n definition.node.init?.type === \"MemberExpression\" &&\n memberName(definition.node.init) === \"headers\"\n );\n}\n\nconst storeType = (annotation: TSESTree.TypeNode | undefined) =>\n annotation?.type === \"TSTypeReference\" &&\n annotation.typeName.type === \"Identifier\" &&\n annotation.typeName.name.endsWith(\"SessionStore\");\n\nconst createsStore = (node: TSESTree.Node | null | undefined) =>\n node?.type === \"CallExpression\" &&\n node.callee.type === \"Identifier\" &&\n /SessionStore$/.test(node.callee.name);\n\n// `.get()` on a SessionStore: one named for sessions, created by a\n// `*SessionStore()` factory, or typed as a SessionStore.\nfunction isSessionStore(source: Source, node: TSESTree.Node): boolean {\n if (createsStore(node)) {\n return true;\n }\n if (node.type === \"MemberExpression\") {\n return sessionStoreName.test(memberName(node) ?? \"\");\n }\n if (node.type !== \"Identifier\") {\n return false;\n }\n if (sessionStoreName.test(node.name)) {\n return true;\n }\n const definition = definitionOf(source, node);\n if (!definition) {\n return false;\n }\n const declared = definition.name as TSESTree.Identifier;\n return (\n storeType(declared.typeAnnotation?.typeAnnotation) ||\n (definition.node.type === \"VariableDeclarator\" &&\n definition.node.id === declared &&\n createsStore(definition.node.init))\n );\n}\n\nfunction calleeName(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\nexport const cacheableMenuRoutes = createRule<Options, \"sessionRead\">({\n name: \"cacheable-menu-routes\",\n meta: {\n type: \"suggestion\",\n docs: {\n description:\n \"Keep the customer's session out of menu page loaders, so shared caches can keep those pages.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n routes: { type: \"array\", items: { type: \"string\" } },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n sessionRead:\n \"This menu page reads the customer's session on the server, so no shared cache can keep it. Show customer details in the browser with useCustomer() from @zappie/sdk/customer/react.\",\n },\n },\n defaultOptions: [{}],\n create(context, [options]) {\n const filename = context.filename.replaceAll(\"\\\\\", \"/\");\n // dot: a project inside a dot directory, such as a worktree, still matches.\n if (\n !(options.routes ?? menuRoutes).some((pattern) =>\n minimatch(filename, pattern, { dot: true }),\n )\n ) {\n return {};\n }\n const inServerExport = (node: TSESTree.Node) =>\n context.sourceCode\n .getAncestors(node)\n .some((ancestor) => serverExports.has(exportedName(ancestor) ?? \"\"));\n const report = (node: TSESTree.Node) => {\n if (inServerExport(node)) {\n context.report({ node, messageId: \"sessionRead\" });\n }\n };\n return {\n CallExpression(node) {\n const name = calleeName(node);\n if (name && sessionCalls.has(name)) {\n report(node);\n return;\n }\n if (name !== \"get\" || node.callee.type !== \"MemberExpression\") {\n return;\n }\n const target = node.callee.object;\n const header = staticText(node.arguments[0] ?? null)?.toLowerCase();\n if (\n (header !== undefined &&\n sessionHeaders.has(header) &&\n isHeaders(context.sourceCode, target)) ||\n isSessionStore(context.sourceCode, target)\n ) {\n report(node);\n }\n },\n Literal(node) {\n if (typeof node.value === \"string\" && sessionRoutes.test(node.value)) {\n report(node);\n }\n },\n TemplateLiteral(node) {\n if (sessionRoutes.test(staticText(node) ?? \"\")) {\n report(node);\n }\n },\n };\n },\n});\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 { 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 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 { 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\n// Next.js inlines a NEXT_PUBLIC_ value into the client build only where the code reads\n// process.env.NAME directly, so a destructured or aliased read stays forbidden.\nfunction forbidden(\n kind: EnvKind,\n key: string | undefined,\n direct = false,\n): boolean {\n if (kind === \"process\") {\n return (\n key !== \"NODE_ENV\" && !(direct && key?.startsWith(\"NEXT_PUBLIC_ZAPPIE_\"))\n );\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 (\n kind &&\n forbidden(kind, memberName(node), directEnv(node.object) === kind)\n ) {\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","minimatch","ESLintUtils","createRule","name","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","pages","extensions","menuRoutes","serverExports","sessionCalls","sessionStoreName","sessionHeaders","sessionRoutes","topLevel","node","exportedName","definitionOf","source","findVariable","isHeaders","memberName","definition","binding","storeType","annotation","createsStore","isSessionStore","declared","calleeName","cacheableMenuRoutes","createRule","context","options","filename","pattern","minimatch","inServerExport","ancestor","report","name","target","header","staticText","cartName","storageKey","calleeName","node","variableName","cartInitial","value","cartViaSdk","createRule","context","method","key","id","call","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","direct","noServerEnvInClient","createRule","context","filename","isServerFile","aliases","cloudflareAliases","serverBody","ancestor","kindOf","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","cacheableMenuRoutes","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.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Flat ESLint rules for Zappie storefront projects.",
5
5
  "license": "MIT",
6
6
  "repository": {