Skip to content

Commit 20b9227

Browse files
authored
Merge pull request #163 from contentstack/feature/MKT-17479-Snyk-Fixes
snyk fixes
2 parents 69501a0 + 75a9c43 commit 20b9227

23 files changed

Lines changed: 4368 additions & 4161 deletions

CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
* @contentstack/tso-integration-pr-reviewers
1+
* @contentstack/marketplace-solutions-pr-reviewers
22

33
.github/workflows/sca-scan.yml @contentstack/security-admin
44

app/components/Footer.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,12 @@ const MenuSection = ({
7979
export function Footer(fetchdata: any) {
8080
const footerMetaObject = fetchdata?.footerMetaObject;
8181
const footerData = footerMetaObject?.metaobjects?.nodes?.[0]?.fields;
82-
const sortedFooterData = footerData.sort((a: any, b: any) => {
83-
return MENU_ORDER.indexOf(a.key) - MENU_ORDER.indexOf(b.key);
84-
});
82+
const sortedFooterData = Array.isArray(footerData)
83+
? [...footerData].sort(
84+
(a: any, b: any) =>
85+
MENU_ORDER.indexOf(a.key) - MENU_ORDER.indexOf(b.key),
86+
)
87+
: [];
8588

8689
return (
8790
<footer className="footer-wrapper">

app/components/Header.tsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function HeaderMenu({
3939
primaryDomainUrl: HeaderQuery['shop']['primaryDomain']['url'];
4040
viewport: Viewport;
4141
}) {
42-
const {publicStoreDomain} = useRootLoaderData();
42+
const publicStoreDomain = useRootLoaderData()?.publicStoreDomain ?? '';
4343
const className = `header-menu-${viewport}`;
4444

4545
function closeAside(event: React.MouseEvent<HTMLAnchorElement>) {
@@ -65,13 +65,24 @@ export function HeaderMenu({
6565
{(menu || FALLBACK_HEADER_MENU).items.map((item) => {
6666
if (!item.url) return null;
6767

68-
// if the url is internal, we strip the domain
69-
const url =
68+
const isStoreHost =
7069
item.url.includes('myshopify.com') ||
71-
item.url.includes(publicStoreDomain) ||
72-
item.url.includes(primaryDomainUrl)
73-
? new URL(item.url).pathname
74-
: item.url;
70+
(publicStoreDomain !== '' && item.url.includes(publicStoreDomain)) ||
71+
(primaryDomainUrl !== '' && item.url.includes(primaryDomainUrl));
72+
73+
// Relative menu paths (e.g. /collections) are already fine for <NavLink to>.
74+
// new URL(relative) throws without a base; only parse absolute store URLs.
75+
let url = item.url;
76+
if (
77+
isStoreHost &&
78+
(item.url.startsWith('http://') || item.url.startsWith('https://'))
79+
) {
80+
try {
81+
url = new URL(item.url).pathname;
82+
} catch {
83+
/* keep original */
84+
}
85+
}
7586
return (
7687
<NavLink
7788
className="header-menu-item"

app/components/Layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ export type LayoutProps = {
2020
footer: Promise<FooterQuery>;
2121
header: HeaderQuery;
2222
isLoggedIn: boolean;
23-
fetchData: any;
24-
footerMetaObject: any;
23+
/** Passed through on error boundaries; unused by layout chrome. */
24+
fetchData?: unknown;
25+
footerMetaObject?: unknown;
2526
};
2627

2728
export function Layout({

app/entry.server.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type {EntryContext} from '@shopify/remix-oxygen';
22
import {RemixServer} from '@remix-run/react';
3-
import isbot from 'isbot';
4-
import {renderToReadableStream} from 'react-dom/server';
3+
import {isbot} from 'isbot';
4+
import {renderToReadableStream} from 'react-dom/server.browser';
55
import {createContentSecurityPolicy} from '@shopify/hydrogen';
66

77
export default async function handleRequest(
@@ -12,19 +12,18 @@ export default async function handleRequest(
1212
) {
1313
const {nonce, header, NonceProvider} = createContentSecurityPolicy();
1414

15-
//Sanitize URL to prevent open redirect
15+
// Sanitize URL (path + query only)
1616
const url = new URL(request.url);
17-
const safeUrl = `${url.pathname}${url.search}`;
17+
const safeUrl = new URL(`${url.pathname}${url.search}`, url.origin);
1818

1919
const body = await renderToReadableStream(
2020
<NonceProvider>
21-
<RemixServer context={remixContext} url={safeUrl} />
21+
<RemixServer context={remixContext} url={safeUrl.toString()} />
2222
</NonceProvider>,
2323
{
2424
nonce,
2525
signal: request.signal,
2626
onError(error) {
27-
// eslint-disable-next-line no-console
2827
console.error(error);
2928
responseStatusCode = 500;
3029
},

app/root.tsx

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
import {useNonce} from '@shopify/hydrogen';
2-
import {
3-
defer,
4-
type SerializeFrom,
5-
type LoaderFunctionArgs,
6-
} from '@shopify/remix-oxygen';
2+
import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
73
import {
84
Links,
95
Meta,
106
Outlet,
117
Scripts,
12-
LiveReload,
13-
useMatches,
8+
useRouteLoaderData,
149
useRouteError,
1510
useLoaderData,
1611
ScrollRestoration,
1712
isRouteErrorResponse,
1813
type ShouldRevalidateFunction,
1914
} from '@remix-run/react';
2015
import type {CustomerAccessToken} from '@shopify/hydrogen/storefront-api-types';
16+
import type {
17+
CartApiQueryFragment,
18+
FooterQuery,
19+
HeaderQuery,
20+
RootMetaObjectQuery,
21+
} from 'storefrontapi.generated';
2122
import favicon from '../public/favicon.svg';
22-
import resetStyles from './styles/reset.css';
23-
import appStyles from './styles/app.css';
23+
import './styles/reset.css';
24+
import './styles/app.css';
25+
2426
import {Layout} from '~/components/Layout';
2527

2628
/**
@@ -46,9 +48,8 @@ export const shouldRevalidate: ShouldRevalidateFunction = ({
4648

4749
export function links() {
4850
return [
49-
...(cssBundleHref ? [{rel: 'stylesheet', href: cssBundleHref}] : []),
50-
{rel: 'stylesheet', href: resetStyles},
51-
{rel: 'stylesheet', href: appStyles},
51+
{ rel: 'stylesheet', href: '/app/styles/reset.css' },
52+
{ rel: 'stylesheet', href: '/app/styles/app.css' },
5253
{
5354
rel: 'preconnect',
5455
href: 'https://cdn.shopify.com',
@@ -57,13 +58,34 @@ export function links() {
5758
rel: 'preconnect',
5859
href: 'https://shop.app',
5960
},
60-
{rel: 'icon', type: 'image/svg+xml', href: favicon},
61+
{ rel: 'icon', type: 'image/svg+xml', href: favicon },
6162
];
6263
}
6364

65+
66+
/**
67+
* Remix `SerializeFrom` loses top-level keys on `defer()` payloads; this matches runtime loader data.
68+
*/
69+
export type RootLoaderClientData = {
70+
cart: Promise<CartApiQueryFragment | null>;
71+
footer: Promise<FooterQuery>;
72+
header: HeaderQuery;
73+
isLoggedIn: boolean;
74+
publicStoreDomain: string;
75+
footerMetaObject: RootMetaObjectQuery;
76+
};
77+
78+
const FALLBACK_HEADER: HeaderQuery = {
79+
shop: {
80+
id: 'gid://shopify/Shop/0',
81+
name: '',
82+
description: '',
83+
primaryDomain: {url: ''},
84+
},
85+
};
86+
6487
export const useRootLoaderData = () => {
65-
const [root] = useMatches();
66-
return root?.data as SerializeFrom<typeof loader>;
88+
return useRouteLoaderData('root') as unknown as RootLoaderClientData | undefined;
6789
};
6890

6991
export async function loader({context}: LoaderFunctionArgs) {
@@ -112,7 +134,7 @@ export async function loader({context}: LoaderFunctionArgs) {
112134

113135
export default function App() {
114136
const nonce = useNonce();
115-
const data = useLoaderData<typeof loader>();
137+
const data = useLoaderData<typeof loader>() as unknown as RootLoaderClientData;
116138
return (
117139
<html lang="en">
118140
<head>
@@ -122,12 +144,17 @@ export default function App() {
122144
<Links />
123145
</head>
124146
<body>
125-
<Layout footerMetaObject={data?.footerMetaObject} {...data}>
147+
<Layout
148+
cart={data.cart}
149+
footer={data.footer}
150+
header={data.header}
151+
isLoggedIn={data.isLoggedIn}
152+
footerMetaObject={data.footerMetaObject}
153+
>
126154
<Outlet />
127155
</Layout>
128156
<ScrollRestoration nonce={nonce} />
129157
<Scripts nonce={nonce} />
130-
<LiveReload nonce={nonce} />
131158
</body>
132159
</html>
133160
);
@@ -156,7 +183,14 @@ export function ErrorBoundary() {
156183
<Links />
157184
</head>
158185
<body>
159-
<Layout {...rootData}>
186+
<Layout
187+
fetchData={rootData}
188+
cart={rootData?.cart ?? Promise.resolve(null)}
189+
footer={rootData?.footer ?? Promise.resolve({} as FooterQuery)}
190+
header={rootData?.header ?? FALLBACK_HEADER}
191+
isLoggedIn={rootData?.isLoggedIn ?? false}
192+
footerMetaObject={rootData?.footerMetaObject}
193+
>
160194
<div className="route-error">
161195
<h1>Oops</h1>
162196
<h2>{errorStatus}</h2>
@@ -169,7 +203,6 @@ export function ErrorBoundary() {
169203
</Layout>
170204
<ScrollRestoration nonce={nonce} />
171205
<Scripts nonce={nonce} />
172-
<LiveReload nonce={nonce} />
173206
</body>
174207
</html>
175208
);
@@ -282,7 +315,7 @@ const FOOTER_QUERY = `#graphql
282315
` as const;
283316

284317
const FOOTER_META_OBJECT_QUERY = `#graphql
285-
query MetaObject($country: CountryCode, $language: LanguageCode)
318+
query RootMetaObject($country: CountryCode, $language: LanguageCode)
286319
@inContext(country: $country, language: $language) {
287320
metaobjects(first: 100, type: "footer") {
288321
nodes {

app/routes/($locale)._index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ function RecommendedProducts({
162162
<>
163163
{bannerField?.key === 'heading' && (
164164
<h5 className="page-banner-heading">
165-
{bannerField?.value}
165+
{bannerField?.value} :
166166
</h5>
167167
)}
168168
{bannerField?.key === 'title' && (
@@ -1092,7 +1092,7 @@ const COLLECTIONS_QUERY = `#graphql
10921092
` as const;
10931093

10941094
const NEW_ARRIVALS_QUERY = `#graphql
1095-
query FeaturedCollection($country: CountryCode, $language: LanguageCode)
1095+
query FeaturedCollectionNewArrivals($country: CountryCode, $language: LanguageCode)
10961096
@inContext(country: $country, language: $language) {
10971097
collection(handle: "new-arrivals") {
10981098
handle
@@ -1116,7 +1116,7 @@ query FeaturedCollection($country: CountryCode, $language: LanguageCode)
11161116
}` as const;
11171117

11181118
const TOP_CATEGORIES_QUERY = `#graphql
1119-
query FeaturedCollection($country: CountryCode, $language: LanguageCode)
1119+
query FeaturedCollectionTopCategories($country: CountryCode, $language: LanguageCode)
11201120
@inContext(country: $country, language: $language) {
11211121
collections(first:8, reverse:true) {
11221122
edges {
@@ -1148,7 +1148,7 @@ const TOP_CATEGORIES_QUERY = `#graphql
11481148
` as const;
11491149

11501150
const BEST_SELLER_QUERY = `#graphql
1151-
query FeaturedCollection($country: CountryCode, $language: LanguageCode)
1151+
query FeaturedCollectionBestSeller($country: CountryCode, $language: LanguageCode)
11521152
@inContext(country: $country, language: $language) {
11531153
collection(handle: "womens-fashion") {
11541154
handle
@@ -1172,7 +1172,7 @@ query FeaturedCollection($country: CountryCode, $language: LanguageCode)
11721172
}` as const;
11731173

11741174
const META_OBJECT_QUERY = `#graphql
1175-
query MetaObject($country: CountryCode, $language: LanguageCode)
1175+
query MetaObjectHome($country: CountryCode, $language: LanguageCode)
11761176
@inContext(country: $country, language: $language) {
11771177
metaobjects(first: 100, type: "home") {
11781178
nodes {

app/routes/($locale).about.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function RecommendedProducts({metaObject}: {metaObject: any}) {
5858
}
5959

6060
const META_OBJECT_QUERY = `#graphql
61-
query MetaObject($country: CountryCode, $language: LanguageCode)
61+
query MetaObjectAbout($country: CountryCode, $language: LanguageCode)
6262
@inContext(country: $country, language: $language) {
6363
metaobjects(first: 100, type: "about_us") {
6464
nodes {

app/routes/($locale).collections._index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ const COLLECTIONS_QUERY = `#graphql
114114
height
115115
}
116116
}
117-
query StoreCollections(
117+
query StoreCollectionsIndex(
118118
$country: CountryCode
119119
$endCursor: String
120120
$first: Int
@@ -142,7 +142,7 @@ const COLLECTIONS_QUERY = `#graphql
142142
` as const;
143143

144144
const HEADING_QUERY = `#graphql
145-
query MetaObject($country: CountryCode, $language: LanguageCode)
145+
query MetaObjectCollectionsPageHeading($country: CountryCode, $language: LanguageCode)
146146
@inContext(country: $country, language: $language) {
147147
metaobjects(first: 100, type: "product_page_contents") {
148148
nodes {

app/routes/($locale).collections.all.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ function RecommendedProducts({
183183
}
184184

185185
const RECOMMENDED_PRODUCTS_QUERY = `#graphql
186-
fragment Product on Product {
186+
fragment CollectionProduct on Product {
187187
id
188188
title
189189
handle
@@ -214,7 +214,7 @@ const RECOMMENDED_PRODUCTS_QUERY = `#graphql
214214
}
215215
}
216216
}
217-
query RecommendedProducts ( $country: CountryCode
217+
query RecommendedProductsAllPage ( $country: CountryCode
218218
$endCursor: String
219219
$first: Int
220220
$language: LanguageCode
@@ -229,7 +229,7 @@ const RECOMMENDED_PRODUCTS_QUERY = `#graphql
229229
reverse: true
230230
) {
231231
nodes {
232-
...Product
232+
...CollectionProduct
233233
}
234234
pageInfo {
235235
hasNextPage
@@ -242,7 +242,7 @@ const RECOMMENDED_PRODUCTS_QUERY = `#graphql
242242
` as const;
243243

244244
const HEADING_QUERY = `#graphql
245-
query MetaObject($country: CountryCode, $language: LanguageCode)
245+
query MetaObjectAllProductsPageHeading($country: CountryCode, $language: LanguageCode)
246246
@inContext(country: $country, language: $language) {
247247
metaobjects(first: 100, type: "product_page_contents") {
248248
nodes {

0 commit comments

Comments
 (0)