@gem-sdk/pages 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +7 -0
  2. package/dist/cjs/components/ErrorBoundary.js +32 -0
  3. package/dist/cjs/components/ErrorFallback.js +9 -0
  4. package/dist/cjs/components/FacebookPixel.js +41 -0
  5. package/dist/cjs/components/GoogleAnalytic.js +43 -0
  6. package/dist/cjs/components/TikTokPixel.js +20 -0
  7. package/dist/cjs/components/builder/Toolbox.js +200 -0
  8. package/dist/cjs/index.js +62 -0
  9. package/dist/cjs/layouts/main.js +15 -0
  10. package/dist/cjs/libs/api/get-builder-props.js +33 -0
  11. package/dist/cjs/libs/api/get-collection-props.js +64 -0
  12. package/dist/cjs/libs/api/get-home-page-props-v2.js +127 -0
  13. package/dist/cjs/libs/api/get-home-page-props.js +135 -0
  14. package/dist/cjs/libs/api/get-preview-props.js +18 -0
  15. package/dist/cjs/libs/api/get-product-props.js +71 -0
  16. package/dist/cjs/libs/api/get-static-page-props-preview.js +133 -0
  17. package/dist/cjs/libs/api/get-static-page-props-v2.js +130 -0
  18. package/dist/cjs/libs/api/get-static-page-props.js +132 -0
  19. package/dist/cjs/libs/fetcher.js +71 -0
  20. package/dist/cjs/libs/get-layout.js +10 -0
  21. package/dist/cjs/libs/get-storefront-api.js +12 -0
  22. package/dist/cjs/libs/getStaticPaths.js +10 -0
  23. package/dist/cjs/libs/google-fonts.js +68 -0
  24. package/dist/cjs/libs/helpers/gen-css.js +102 -0
  25. package/dist/cjs/libs/helpers/generate-manifres.js +5 -0
  26. package/dist/cjs/libs/helpers/get-fallback.js +29 -0
  27. package/dist/cjs/libs/helpers/normalize.js +87 -0
  28. package/dist/cjs/libs/helpers/parse-json.js +18 -0
  29. package/dist/cjs/pages/404.js +11 -0
  30. package/dist/cjs/pages/500.js +19 -0
  31. package/dist/cjs/pages/builder.js +26 -0
  32. package/dist/cjs/pages/collection-detail.js +21 -0
  33. package/dist/cjs/pages/preview.js +20 -0
  34. package/dist/cjs/pages/product-detail.js +21 -0
  35. package/dist/cjs/pages/static-v2.js +17 -0
  36. package/dist/cjs/pages/static.js +19 -0
  37. package/dist/esm/components/ErrorBoundary.js +30 -0
  38. package/dist/esm/components/ErrorFallback.js +7 -0
  39. package/dist/esm/components/FacebookPixel.js +39 -0
  40. package/dist/esm/components/GoogleAnalytic.js +41 -0
  41. package/dist/esm/components/TikTokPixel.js +18 -0
  42. package/dist/esm/components/builder/Toolbox.js +196 -0
  43. package/dist/esm/index.js +28 -0
  44. package/dist/esm/layouts/main.js +11 -0
  45. package/dist/esm/libs/api/get-builder-props.js +31 -0
  46. package/dist/esm/libs/api/get-collection-props.js +62 -0
  47. package/dist/esm/libs/api/get-home-page-props-v2.js +125 -0
  48. package/dist/esm/libs/api/get-home-page-props.js +133 -0
  49. package/dist/esm/libs/api/get-preview-props.js +16 -0
  50. package/dist/esm/libs/api/get-product-props.js +69 -0
  51. package/dist/esm/libs/api/get-static-page-props-preview.js +131 -0
  52. package/dist/esm/libs/api/get-static-page-props-v2.js +128 -0
  53. package/dist/esm/libs/api/get-static-page-props.js +130 -0
  54. package/dist/esm/libs/fetcher.js +68 -0
  55. package/dist/esm/libs/get-layout.js +8 -0
  56. package/dist/esm/libs/get-storefront-api.js +10 -0
  57. package/dist/esm/libs/getStaticPaths.js +8 -0
  58. package/dist/esm/libs/google-fonts.js +64 -0
  59. package/dist/esm/libs/helpers/gen-css.js +100 -0
  60. package/dist/esm/libs/helpers/generate-manifres.js +3 -0
  61. package/dist/esm/libs/helpers/get-fallback.js +27 -0
  62. package/dist/esm/libs/helpers/normalize.js +83 -0
  63. package/dist/esm/libs/helpers/parse-json.js +15 -0
  64. package/dist/esm/pages/404.js +9 -0
  65. package/dist/esm/pages/500.js +17 -0
  66. package/dist/esm/pages/builder.js +24 -0
  67. package/dist/esm/pages/collection-detail.js +17 -0
  68. package/dist/esm/pages/preview.js +18 -0
  69. package/dist/esm/pages/product-detail.js +17 -0
  70. package/dist/esm/pages/static-v2.js +15 -0
  71. package/dist/esm/pages/static.js +15 -0
  72. package/dist/types/index.d.ts +173 -0
  73. package/package.json +45 -0
@@ -0,0 +1,133 @@
1
+ import { PublishedThemePagesDocument, StorePropertyDocument, prefetchQueries } from '@gem-sdk/core';
2
+ import { ShopMetaDocument } from '@gem-sdk/adapter-shopify';
3
+ import { getFontFromGlobalStyle } from '../google-fonts.js';
4
+ import { genCSS } from '../helpers/gen-css.js';
5
+ import { generateManifest } from '../helpers/generate-manifres.js';
6
+ import { parseBuilderTemplate } from '../helpers/normalize.js';
7
+ import { parseJson, serializableJson } from '../helpers/parse-json.js';
8
+
9
+ const getHomePageProps = (fetcher, shopifyFetcher) => async () => {
10
+ const pageType = 'STATIC';
11
+ const variables = {
12
+ slugType: pageType,
13
+ };
14
+ // const theme = await fetcher<PublishedThemePagesQueryResponse, PublishedThemePagesQueryVariables>([
15
+ // PublishedThemePagesDocument,
16
+ // variables,
17
+ // ]);
18
+ const [theme, storeProperty, shopifyMeta] = await Promise.allSettled([
19
+ fetcher([
20
+ PublishedThemePagesDocument,
21
+ variables,
22
+ ]),
23
+ fetcher([StorePropertyDocument]),
24
+ shopifyFetcher([ShopMetaDocument]),
25
+ ]);
26
+ if (theme.status === 'rejected') {
27
+ return {
28
+ pageType,
29
+ };
30
+ }
31
+ const dataBuilder = theme.value.publishedThemePages?.[0];
32
+ const homeTemplate = parseBuilderTemplate(dataBuilder);
33
+ const fontStyle = await getFontFromGlobalStyle(dataBuilder?.pageStyle?.data);
34
+ const queries = prefetchQueries(homeTemplate);
35
+ const datas = await Promise.allSettled(queries.map(({ query, variables, func }) => {
36
+ if (func) {
37
+ return func(fetcher, variables);
38
+ }
39
+ if (query)
40
+ return fetcher([query, variables]);
41
+ return {};
42
+ }));
43
+ const fallback = queries.reduce((acc, { key }, index) => {
44
+ const res = datas[index];
45
+ if (res?.status === 'fulfilled') {
46
+ return {
47
+ ...acc,
48
+ [key]: res.value,
49
+ };
50
+ }
51
+ return acc;
52
+ }, {});
53
+ const description = dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-description')?.value;
54
+ const thumbnail = parseJson(dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-thumbnail')?.value);
55
+ const shopMeta = shopifyMeta.status === 'fulfilled' ? shopifyMeta.value : undefined;
56
+ const shopData = storeProperty.status === 'fulfilled' ? storeProperty.value : undefined;
57
+ const favicon = shopData?.storeProperty?.favicon ?? '/favicon/favicon-32x32.png';
58
+ const seo = {
59
+ defaultTitle: shopMeta?.shop.name,
60
+ title: dataBuilder?.name ?? 'Home',
61
+ description: description ?? shopMeta?.shop.description,
62
+ openGraph: {
63
+ site_name: shopMeta?.shop.name,
64
+ locale: shopMeta?.localization.country.isoCode,
65
+ title: dataBuilder?.name ?? shopMeta?.shop.name,
66
+ description: description ?? shopMeta?.shop.description,
67
+ images: thumbnail ? [thumbnail] : [],
68
+ },
69
+ additionalMetaTags: [
70
+ {
71
+ name: 'theme-color',
72
+ content: '#000000',
73
+ },
74
+ ],
75
+ canonical: '/',
76
+ additionalLinkTags: [
77
+ {
78
+ rel: 'icon',
79
+ sizes: '32x32',
80
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/32x32/`,
81
+ },
82
+ {
83
+ rel: 'icon',
84
+ sizes: '16x16',
85
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/16x16/`,
86
+ },
87
+ {
88
+ rel: 'apple-touch-icon',
89
+ sizes: '180x180',
90
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/180x180/`,
91
+ },
92
+ {
93
+ rel: 'manifest',
94
+ href: generateManifest({
95
+ theme_color: '#000000',
96
+ background_color: '#ffffff',
97
+ display: 'standalone',
98
+ scope: '/',
99
+ start_url: '/',
100
+ name: shopMeta?.shop.name,
101
+ short_name: shopMeta?.shop.name,
102
+ description: shopMeta?.shop.description,
103
+ icons: [
104
+ {
105
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/192x192/`,
106
+ sizes: '192x192',
107
+ type: 'image/png',
108
+ purpose: 'any maskable',
109
+ },
110
+ {
111
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/512x512/`,
112
+ sizes: '512x512',
113
+ type: 'image/png',
114
+ },
115
+ ],
116
+ }),
117
+ },
118
+ ],
119
+ };
120
+ return serializableJson({
121
+ themeStyle: genCSS(dataBuilder?.pageStyle?.data),
122
+ fontStyle,
123
+ builderData: homeTemplate,
124
+ pageType,
125
+ swr: { fallback },
126
+ currency: shopMeta?.localization.country.currency.isoCode ?? null,
127
+ locale: shopMeta?.localization.country.isoCode ?? null,
128
+ swatches: parseJson(shopData?.storeProperty?.swatchesConfig),
129
+ seo,
130
+ });
131
+ };
132
+
133
+ export { getHomePageProps };
@@ -0,0 +1,16 @@
1
+ import { getCollectionProps } from './get-collection-props.js';
2
+ import { getProductProps } from './get-product-props.js';
3
+ import { getStaticPageProps } from './get-static-page-props.js';
4
+
5
+ const getPreviewProps = (fetcher, shopifyFetcher) => async (pageType, slug) => {
6
+ switch (pageType) {
7
+ case 'COLLECTION':
8
+ return getCollectionProps(fetcher)(slug);
9
+ case 'PRODUCT':
10
+ return getProductProps(fetcher)(slug);
11
+ default:
12
+ return getStaticPageProps(fetcher, shopifyFetcher)(slug);
13
+ }
14
+ };
15
+
16
+ export { getPreviewProps };
@@ -0,0 +1,69 @@
1
+ import { getProductBySlug, PublishedThemePagesDocument, prefetchQueries } from '@gem-sdk/core';
2
+ import { genCSS } from '../helpers/gen-css.js';
3
+ import { parseBuilderTemplate } from '../helpers/normalize.js';
4
+ import { serializableJson } from '../helpers/parse-json.js';
5
+
6
+ const getProductProps = (fetcher) => async (handle) => {
7
+ const pageType = 'PRODUCT';
8
+ const variables = {
9
+ slug: handle,
10
+ slugType: pageType,
11
+ };
12
+ const [product, theme] = await Promise.all([
13
+ getProductBySlug(fetcher, handle),
14
+ fetcher([
15
+ PublishedThemePagesDocument,
16
+ variables,
17
+ ]),
18
+ ]);
19
+ const dataBuilder = theme.publishedThemePages?.[0];
20
+ if (!product || !dataBuilder) {
21
+ throw new Error('Product not found');
22
+ }
23
+ const productTemplate = parseBuilderTemplate(dataBuilder);
24
+ const queries = prefetchQueries(productTemplate);
25
+ const datas = await Promise.allSettled(queries.map(({ query, variables, func }) => {
26
+ if (func) {
27
+ return func(fetcher, variables);
28
+ }
29
+ if (query)
30
+ return fetcher([query, variables]);
31
+ return {};
32
+ }));
33
+ const fallback = queries.reduce((acc, { key }, index) => {
34
+ const res = datas[index];
35
+ if (res?.status === 'fulfilled') {
36
+ return {
37
+ ...acc,
38
+ [key]: res.value,
39
+ };
40
+ }
41
+ return acc;
42
+ }, {});
43
+ return serializableJson({
44
+ builderData: productTemplate,
45
+ themeStyle: genCSS(dataBuilder.pageStyle?.data),
46
+ pageType,
47
+ swr: { fallback },
48
+ seo: {
49
+ title: product.title,
50
+ description: product.description,
51
+ openGraph: {
52
+ images: product.featuredImage?.src
53
+ ? [
54
+ {
55
+ url: product.featuredImage.src,
56
+ alt: product.featuredImage?.alt,
57
+ height: product.featuredImage?.height,
58
+ width: product.featuredImage?.width,
59
+ type: product.featuredImage?.contentType,
60
+ },
61
+ ]
62
+ : [],
63
+ },
64
+ },
65
+ product,
66
+ });
67
+ };
68
+
69
+ export { getProductProps };
@@ -0,0 +1,131 @@
1
+ import { PreviewPageDocument, StorePropertyDocument } from '@gem-sdk/core';
2
+ import { ShopMetaDocument } from '@gem-sdk/adapter-shopify';
3
+ import { captureException } from '@sentry/nextjs';
4
+ import { getFontFromGlobalStyle } from '../google-fonts.js';
5
+ import { genCSS } from '../helpers/gen-css.js';
6
+ import { generateManifest } from '../helpers/generate-manifres.js';
7
+ import { getFallbackV2 } from '../helpers/get-fallback.js';
8
+ import { parseBuilderTemplateV2 } from '../helpers/normalize.js';
9
+ import { parseJson, serializableJson } from '../helpers/parse-json.js';
10
+
11
+ const getStaticPagePropsPreview = (fetcher, shopifyFetcher) => async (slug) => {
12
+ try {
13
+ const pageType = 'STATIC';
14
+ const variables = {
15
+ handleURL: slug,
16
+ pageType,
17
+ };
18
+ const [theme, storeProperty, shopifyMeta] = await Promise.allSettled([
19
+ fetcher([
20
+ PreviewPageDocument,
21
+ variables,
22
+ 'previewPage',
23
+ ]),
24
+ fetcher([StorePropertyDocument]),
25
+ shopifyFetcher([ShopMetaDocument]),
26
+ ]);
27
+ if (theme.status === 'rejected') {
28
+ throw new Error(theme.reason?.[0]);
29
+ }
30
+ const dataBuilder = theme.value.previewPage;
31
+ if (!dataBuilder) {
32
+ throw new Error(`No data builder found for slug: /preview/${slug}`);
33
+ }
34
+ const pageTemplate = parseBuilderTemplateV2(dataBuilder);
35
+ const [fontStyle, fallback] = await Promise.all([
36
+ getFontFromGlobalStyle(dataBuilder?.pageStyle?.data),
37
+ getFallbackV2(fetcher, pageTemplate),
38
+ ]);
39
+ const mobileOnly = dataBuilder.isMobile ?? false;
40
+ const description = dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-description')?.value;
41
+ const thumbnail = parseJson(dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-thumbnail')?.value);
42
+ const shopMeta = shopifyMeta.status === 'fulfilled' ? shopifyMeta.value : undefined;
43
+ const shopData = storeProperty.status === 'fulfilled' ? storeProperty.value : undefined;
44
+ const favicon = shopData?.storeProperty?.favicon ?? '/favicon/favicon-32x32.png';
45
+ const seo = {
46
+ defaultTitle: shopMeta?.shop.name,
47
+ title: dataBuilder?.name,
48
+ description: description ?? shopMeta?.shop.description,
49
+ openGraph: {
50
+ site_name: shopMeta?.shop.name,
51
+ locale: shopMeta?.localization.country.isoCode,
52
+ title: dataBuilder?.name ?? shopMeta?.shop.name,
53
+ description: description ?? shopMeta?.shop.description,
54
+ images: thumbnail ? [thumbnail] : [],
55
+ },
56
+ nofollow: true,
57
+ noindex: true,
58
+ canonical: `/preview/${slug}`,
59
+ additionalMetaTags: [
60
+ {
61
+ name: 'theme-color',
62
+ content: '#000000',
63
+ },
64
+ ],
65
+ additionalLinkTags: [
66
+ {
67
+ rel: 'icon',
68
+ sizes: '32x32',
69
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/32x32/`,
70
+ },
71
+ {
72
+ rel: 'icon',
73
+ sizes: '16x16',
74
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/16x16/`,
75
+ },
76
+ {
77
+ rel: 'apple-touch-icon',
78
+ sizes: '180x180',
79
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/180x180/`,
80
+ },
81
+ {
82
+ rel: 'manifest',
83
+ href: generateManifest({
84
+ theme_color: '#000000',
85
+ background_color: '#ffffff',
86
+ display: 'standalone',
87
+ scope: '/',
88
+ start_url: '/',
89
+ name: shopMeta?.shop.name,
90
+ short_name: shopMeta?.shop.name,
91
+ description: shopMeta?.shop.description,
92
+ icons: [
93
+ {
94
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/192x192/`,
95
+ sizes: '192x192',
96
+ type: 'image/png',
97
+ purpose: 'any maskable',
98
+ },
99
+ {
100
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/512x512/`,
101
+ sizes: '512x512',
102
+ type: 'image/png',
103
+ },
104
+ ],
105
+ }),
106
+ },
107
+ ],
108
+ };
109
+ return serializableJson({
110
+ themeStyle: genCSS(dataBuilder?.pageStyle?.data, mobileOnly),
111
+ fontStyle,
112
+ builderData: pageTemplate,
113
+ pageType,
114
+ currency: shopMeta?.localization.country.currency.isoCode ?? null,
115
+ locale: shopMeta?.localization.country.isoCode ?? null,
116
+ swr: { fallback },
117
+ swatches: parseJson(shopData?.storeProperty?.swatchesConfig),
118
+ seo,
119
+ mobileOnly,
120
+ gaTrackingId: dataBuilder.themePageAnalytic?.gaTrackingID ?? null,
121
+ facebookPixelId: dataBuilder.themePageAnalytic?.fbPixelID ?? null,
122
+ tiktokPixelId: dataBuilder.themePageAnalytic?.tiktokPixelID ?? null,
123
+ });
124
+ }
125
+ catch (err) {
126
+ captureException(err);
127
+ throw err;
128
+ }
129
+ };
130
+
131
+ export { getStaticPagePropsPreview };
@@ -0,0 +1,128 @@
1
+ import { PublishedThemePagesDocument, StorePropertyDocument } from '@gem-sdk/core';
2
+ import { ShopMetaDocument } from '@gem-sdk/adapter-shopify';
3
+ import { getFontFromGlobalStyle } from '../google-fonts.js';
4
+ import { genCSS } from '../helpers/gen-css.js';
5
+ import { generateManifest } from '../helpers/generate-manifres.js';
6
+ import { getFallbackV2 } from '../helpers/get-fallback.js';
7
+ import { parseBuilderTemplateV2 } from '../helpers/normalize.js';
8
+ import { parseJson, serializableJson } from '../helpers/parse-json.js';
9
+ import { captureException } from '@sentry/nextjs';
10
+
11
+ const getStaticPagePropsV2 = (fetcher, shopifyFetcher) => async (slug) => {
12
+ try {
13
+ const pageType = 'STATIC';
14
+ const variables = {
15
+ slug,
16
+ slugType: pageType,
17
+ };
18
+ const [theme, storeProperty, shopifyMeta] = await Promise.allSettled([
19
+ fetcher([
20
+ PublishedThemePagesDocument,
21
+ variables,
22
+ ]),
23
+ fetcher([StorePropertyDocument]),
24
+ shopifyFetcher([ShopMetaDocument]),
25
+ ]);
26
+ if (theme.status === 'rejected') {
27
+ throw new Error(theme.reason?.[0]);
28
+ }
29
+ const dataBuilder = theme.value.publishedThemePages?.[0];
30
+ if (!dataBuilder) {
31
+ throw new Error(`No data builder found for slug: /${slug}`);
32
+ }
33
+ const pageTemplate = parseBuilderTemplateV2(dataBuilder);
34
+ const [fontStyle, fallback] = await Promise.all([
35
+ getFontFromGlobalStyle(dataBuilder?.pageStyle?.data),
36
+ getFallbackV2(fetcher, pageTemplate),
37
+ ]);
38
+ const mobileOnly = dataBuilder.isMobile ?? false;
39
+ const description = dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-description')?.value;
40
+ const thumbnail = parseJson(dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-thumbnail')?.value);
41
+ const shopMeta = shopifyMeta.status === 'fulfilled' ? shopifyMeta.value : undefined;
42
+ const shopData = storeProperty.status === 'fulfilled' ? storeProperty.value : undefined;
43
+ const favicon = shopData?.storeProperty?.favicon ?? '/favicon/favicon-32x32.png';
44
+ const seo = {
45
+ defaultTitle: shopMeta?.shop.name,
46
+ title: dataBuilder?.name,
47
+ description: description ?? shopMeta?.shop.description,
48
+ openGraph: {
49
+ site_name: shopMeta?.shop.name,
50
+ locale: shopMeta?.localization.country.isoCode,
51
+ title: dataBuilder?.name ?? shopMeta?.shop.name,
52
+ description: description ?? shopMeta?.shop.description,
53
+ images: thumbnail ? [thumbnail] : [],
54
+ },
55
+ canonical: `/${slug}`,
56
+ additionalMetaTags: [
57
+ {
58
+ name: 'theme-color',
59
+ content: '#000000',
60
+ },
61
+ ],
62
+ additionalLinkTags: [
63
+ {
64
+ rel: 'icon',
65
+ sizes: '32x32',
66
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/32x32/`,
67
+ },
68
+ {
69
+ rel: 'icon',
70
+ sizes: '16x16',
71
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/16x16/`,
72
+ },
73
+ {
74
+ rel: 'apple-touch-icon',
75
+ sizes: '180x180',
76
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/180x180/`,
77
+ },
78
+ {
79
+ rel: 'manifest',
80
+ href: generateManifest({
81
+ theme_color: '#000000',
82
+ background_color: '#ffffff',
83
+ display: 'standalone',
84
+ scope: '/',
85
+ start_url: '/',
86
+ name: shopMeta?.shop.name,
87
+ short_name: shopMeta?.shop.name,
88
+ description: shopMeta?.shop.description,
89
+ icons: [
90
+ {
91
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/192x192/`,
92
+ sizes: '192x192',
93
+ type: 'image/png',
94
+ purpose: 'any maskable',
95
+ },
96
+ {
97
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/512x512/`,
98
+ sizes: '512x512',
99
+ type: 'image/png',
100
+ },
101
+ ],
102
+ }),
103
+ },
104
+ ],
105
+ };
106
+ return serializableJson({
107
+ themeStyle: genCSS(dataBuilder?.pageStyle?.data, mobileOnly),
108
+ fontStyle,
109
+ builderData: pageTemplate,
110
+ pageType,
111
+ currency: shopMeta?.localization.country.currency.isoCode ?? null,
112
+ locale: shopMeta?.localization.country.isoCode ?? null,
113
+ swr: { fallback },
114
+ swatches: parseJson(shopData?.storeProperty?.swatchesConfig),
115
+ seo,
116
+ mobileOnly,
117
+ gaTrackingId: dataBuilder.themePageAnalytic?.gaTrackingID ?? null,
118
+ facebookPixelId: dataBuilder.themePageAnalytic?.fbPixelID ?? null,
119
+ tiktokPixelId: dataBuilder.themePageAnalytic?.tiktokPixelID ?? null,
120
+ });
121
+ }
122
+ catch (err) {
123
+ captureException(err);
124
+ throw err;
125
+ }
126
+ };
127
+
128
+ export { getStaticPagePropsV2 };
@@ -0,0 +1,130 @@
1
+ import { PublishedThemePagesDocument, StorePropertyDocument, prefetchQueries } from '@gem-sdk/core';
2
+ import { ShopMetaDocument } from '@gem-sdk/adapter-shopify';
3
+ import { getFontFromGlobalStyle } from '../google-fonts.js';
4
+ import { genCSS } from '../helpers/gen-css.js';
5
+ import { generateManifest } from '../helpers/generate-manifres.js';
6
+ import { parseBuilderTemplate } from '../helpers/normalize.js';
7
+ import { parseJson, serializableJson } from '../helpers/parse-json.js';
8
+
9
+ const getStaticPageProps = (fetcher, shopifyFetcher) => async (slug) => {
10
+ const pageType = 'STATIC';
11
+ const variables = {
12
+ slug,
13
+ slugType: pageType,
14
+ };
15
+ const [theme, storeProperty, shopifyMeta] = await Promise.allSettled([
16
+ fetcher([
17
+ PublishedThemePagesDocument,
18
+ variables,
19
+ ]),
20
+ fetcher([StorePropertyDocument]),
21
+ shopifyFetcher([ShopMetaDocument]),
22
+ ]);
23
+ if (theme.status === 'rejected') {
24
+ return {
25
+ pageType,
26
+ };
27
+ }
28
+ const dataBuilder = theme.value.publishedThemePages?.[0];
29
+ const pageTemplate = parseBuilderTemplate(dataBuilder);
30
+ const fontStyle = await getFontFromGlobalStyle(dataBuilder?.pageStyle?.data);
31
+ const queries = prefetchQueries(pageTemplate);
32
+ const datas = await Promise.allSettled(queries.map(({ query, variables, func }) => {
33
+ if (func) {
34
+ return func(fetcher, variables);
35
+ }
36
+ if (query)
37
+ return fetcher([query, variables]);
38
+ return {};
39
+ }));
40
+ const fallback = queries.reduce((acc, { key }, index) => {
41
+ const res = datas[index];
42
+ if (res?.status === 'fulfilled') {
43
+ return {
44
+ ...acc,
45
+ [key]: res.value,
46
+ };
47
+ }
48
+ return acc;
49
+ }, {});
50
+ const description = dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-description')?.value;
51
+ const thumbnail = parseJson(dataBuilder?.themePageDataSEO?.find((item) => item?.key === 'global-meta-thumbnail')?.value);
52
+ const shopMeta = shopifyMeta.status === 'fulfilled' ? shopifyMeta.value : undefined;
53
+ const shopData = storeProperty.status === 'fulfilled' ? storeProperty.value : undefined;
54
+ const favicon = shopData?.storeProperty?.favicon ?? '/favicon/favicon-32x32.png';
55
+ const seo = {
56
+ defaultTitle: shopMeta?.shop.name ?? '',
57
+ title: dataBuilder?.name ?? '',
58
+ description: description ?? shopMeta?.shop.description ?? '',
59
+ openGraph: {
60
+ site_name: shopMeta?.shop.name ?? '',
61
+ locale: shopMeta?.localization.country.isoCode ?? '',
62
+ title: dataBuilder?.name ?? shopMeta?.shop.name ?? '',
63
+ description: description ?? shopMeta?.shop.description ?? '',
64
+ images: thumbnail ? [thumbnail] : [],
65
+ },
66
+ additionalMetaTags: [
67
+ {
68
+ name: 'theme-color',
69
+ content: '#000000',
70
+ },
71
+ ],
72
+ canonical: `/${slug}`,
73
+ additionalLinkTags: [
74
+ {
75
+ rel: 'icon',
76
+ sizes: '32x32',
77
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/32x32/`,
78
+ },
79
+ {
80
+ rel: 'icon',
81
+ sizes: '16x16',
82
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/16x16/`,
83
+ },
84
+ {
85
+ rel: 'apple-touch-icon',
86
+ sizes: '180x180',
87
+ href: `${favicon}-/crop/1:1/center/-/smart_resize/180x180/`,
88
+ },
89
+ {
90
+ rel: 'manifest',
91
+ href: generateManifest({
92
+ theme_color: '#000000',
93
+ background_color: '#ffffff',
94
+ display: 'standalone',
95
+ scope: '/',
96
+ start_url: '/',
97
+ name: shopMeta?.shop.name,
98
+ short_name: shopMeta?.shop.name,
99
+ description: shopMeta?.shop.description,
100
+ icons: [
101
+ {
102
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/192x192/`,
103
+ sizes: '192x192',
104
+ type: 'image/png',
105
+ purpose: 'any maskable',
106
+ },
107
+ {
108
+ src: `${favicon}-/crop/1:1/center/-/smart_resize/512x512/`,
109
+ sizes: '512x512',
110
+ type: 'image/png',
111
+ },
112
+ ],
113
+ }),
114
+ },
115
+ ],
116
+ };
117
+ return serializableJson({
118
+ themeStyle: genCSS(dataBuilder?.pageStyle?.data),
119
+ fontStyle,
120
+ builderData: pageTemplate,
121
+ pageType,
122
+ currency: shopMeta?.localization.country.currency.isoCode ?? null,
123
+ locale: shopMeta?.localization.country.isoCode ?? null,
124
+ swr: { fallback },
125
+ swatches: parseJson(shopData?.storeProperty?.swatchesConfig),
126
+ seo,
127
+ });
128
+ };
129
+
130
+ export { getStaticPageProps };
@@ -0,0 +1,68 @@
1
+ import { getStorefrontApi } from './get-storefront-api.js';
2
+
3
+ const createFetcher = (token) => {
4
+ const shopToken = token || process.env.NEXT_PUBLIC_SHOP_TOKEN;
5
+ return async (args) => {
6
+ const [query, variables, operationName] = args;
7
+ if (!shopToken) {
8
+ throw new Error('shopToken is not defined');
9
+ }
10
+ if (!process.env.NEXT_PUBLIC_API_URL) {
11
+ throw new Error('NEXT_PUBLIC_API_URL is not defined');
12
+ }
13
+ const headers = {
14
+ 'Content-Type': 'application/json',
15
+ 'X-GemX-Shop-Token': shopToken,
16
+ };
17
+ return fetch(process.env.NEXT_PUBLIC_API_URL, {
18
+ method: 'POST',
19
+ headers,
20
+ body: JSON.stringify({
21
+ query,
22
+ variables,
23
+ operationName,
24
+ }),
25
+ })
26
+ .then((res) => res.json())
27
+ .then((res) => {
28
+ if (res.errors) {
29
+ return Promise.reject(res.errors);
30
+ }
31
+ return res.data;
32
+ });
33
+ };
34
+ };
35
+ const createShopifyFetcher = (storefrontToken, handle) => {
36
+ const token = storefrontToken ?? process.env.NEXT_PUBLIC_STOREFRONT_TOKEN;
37
+ const storefrontHandle = handle ?? process.env.NEXT_PUBLIC_STOREFRONT_HANDLE;
38
+ return async (args) => {
39
+ if (!token) {
40
+ throw new Error('NEXT_PUBLIC_STOREFRONT_TOKEN is not defined');
41
+ }
42
+ if (!storefrontHandle) {
43
+ throw new Error('NEXT_PUBLIC_STOREFRONT_HANDLE is not defined');
44
+ }
45
+ const headers = {
46
+ 'Content-Type': 'application/json',
47
+ 'X-Shopify-Storefront-Access-Token': token,
48
+ };
49
+ const [query, variables] = args;
50
+ return fetch(getStorefrontApi(storefrontHandle), {
51
+ method: 'POST',
52
+ headers,
53
+ body: JSON.stringify({
54
+ query,
55
+ variables,
56
+ }),
57
+ })
58
+ .then((res) => res.json())
59
+ .then((res) => {
60
+ if (res.errors) {
61
+ return Promise.reject(res.errors);
62
+ }
63
+ return res.data;
64
+ });
65
+ };
66
+ };
67
+
68
+ export { createFetcher, createShopifyFetcher };
@@ -0,0 +1,8 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import MainLayout from '../layouts/main.js';
3
+
4
+ const getLayout = (page) => {
5
+ return jsx(MainLayout, { children: page });
6
+ };
7
+
8
+ export { getLayout };