cloudcommerce 2.4.2 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test-apps.yml +2 -2
- package/CHANGELOG.md +28 -0
- package/action.yml +3 -3
- package/ecomplus-stores/barradoce/functions/many/package.json +3 -3
- package/ecomplus-stores/barradoce/functions/ssr/package.json +10 -8
- package/ecomplus-stores/barradoce/functions/ssr/src/components/Pagination.vue +4 -7
- package/ecomplus-stores/barradoce/functions/ssr/src/components/ProductDetails.vue +1 -1
- package/ecomplus-stores/barradoce/functions/ssr/src/layouts/PageHeader.astro +5 -4
- package/ecomplus-stores/barradoce/functions/ssr/src/main/content/Hero.astro +1 -1
- package/ecomplus-stores/barradoce/functions/ssr/src/main/content/Sections.astro +10 -7
- package/ecomplus-stores/barradoce/functions/ssr/src/pages/admin/index.astro +25 -0
- package/ecomplus-stores/barradoce/functions/with-apps/package.json +3 -3
- package/ecomplus-stores/barradoce/package.json +2 -2
- package/package.json +10 -10
- package/packages/api/package.json +1 -1
- package/packages/apps/affiliate-program/package.json +1 -1
- package/packages/apps/correios/package.json +2 -2
- package/packages/apps/custom-payment/package.json +1 -1
- package/packages/apps/custom-shipping/package.json +1 -1
- package/packages/apps/datafrete/package.json +1 -1
- package/packages/apps/discounts/package.json +1 -1
- package/packages/apps/emails/package.json +1 -1
- package/packages/apps/fb-conversions/package.json +1 -1
- package/packages/apps/flash-courier/package.json +1 -1
- package/packages/apps/frenet/package.json +1 -1
- package/packages/apps/galaxpay/package.json +1 -1
- package/packages/apps/google-analytics/package.json +1 -1
- package/packages/apps/jadlog/package.json +1 -1
- package/packages/apps/loyalty-points/package.json +1 -1
- package/packages/apps/mandae/package.json +1 -1
- package/packages/apps/melhor-envio/package.json +1 -1
- package/packages/apps/mercadopago/package.json +1 -1
- package/packages/apps/pagarme/package.json +1 -1
- package/packages/apps/pagarme-v5/package.json +1 -1
- package/packages/apps/paghiper/package.json +1 -1
- package/packages/apps/pix/package.json +1 -1
- package/packages/apps/tiny-erp/package.json +1 -1
- package/packages/apps/webhooks/package.json +1 -1
- package/packages/cli/ci/bunny-config-base.sh +2 -0
- package/packages/cli/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/emails/package.json +1 -1
- package/packages/eslint/package.json +3 -3
- package/packages/events/package.json +1 -1
- package/packages/feeds/package.json +1 -1
- package/packages/firebase/package.json +3 -3
- package/packages/i18n/package.json +1 -1
- package/packages/modules/package.json +1 -1
- package/packages/passport/package.json +1 -1
- package/packages/ssr/lib/lib/serve-storefront.js +1 -1
- package/packages/ssr/lib/lib/serve-storefront.js.map +1 -1
- package/packages/ssr/package.json +3 -3
- package/packages/ssr/src/lib/serve-storefront.ts +2 -2
- package/packages/storefront/astro.config.mjs +5 -1
- package/packages/storefront/client.d.ts +3 -0
- package/packages/storefront/config/astro/client-sf-directive.mjs +97 -0
- package/packages/storefront/config/astro/index.d.ts +9 -2
- package/packages/storefront/config/storefront.cms.js +3 -3
- package/packages/storefront/package.json +8 -9
- package/packages/storefront/src/decap-cms/create-preview-component.ts +135 -0
- package/packages/storefront/src/decap-cms/get-cms-config.ts +320 -12
- package/packages/storefront/src/lib/assets/decap-cms.css +81 -0
- package/packages/storefront/src/lib/components/Drawer.vue +8 -13
- package/packages/storefront/src/lib/components/QuantitySelector.vue +4 -9
- package/packages/storefront/src/lib/composables/use-pitch-bar.ts +3 -9
- package/packages/storefront/src/lib/composables/use-product-shelf.ts +4 -1
- package/packages/storefront/src/lib/content.d.ts +19 -4
- package/packages/storefront/src/lib/layouts/BaseBody.astro +13 -0
- package/packages/storefront/src/lib/layouts/BaseHead.astro +1 -1
- package/packages/storefront/src/lib/scripts/decap-cms.ts +37 -29
- package/packages/storefront/src/lib/ssr-context.ts +35 -11
- package/packages/storefront/src/lib/state/use-cms-preview.ts +88 -27
- package/packages/test-base/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/storefront/config/astro/context-directive.mjs +0 -46
- package/packages/storefront/src/decap-cms/gen-preview-container.ts +0 -51
- package/packages/storefront/src/decap-cms/preview/webcontainer.ts +0 -104
|
@@ -93,7 +93,7 @@ window._emitApiContext = (id = null) => {
|
|
|
93
93
|
url: $storefront.url,
|
|
94
94
|
apiContext: $storefront.apiContext,
|
|
95
95
|
});
|
|
96
|
-
console.
|
|
96
|
+
console.debug('[ctx] emit ' + id);
|
|
97
97
|
window.dispatchEvent(new Event('storefront:apiContext'));
|
|
98
98
|
window._emitedContextId = id;
|
|
99
99
|
window.__sfIds = {};
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import Deepmerge from '@fastify/deepmerge';
|
|
2
2
|
import afetch from '../../helpers/afetch';
|
|
3
3
|
import getCmsConfig from '../../decap-cms/get-cms-config';
|
|
4
|
-
import
|
|
4
|
+
import createPreviewComponent from '../../decap-cms/create-preview-component';
|
|
5
5
|
|
|
6
|
-
let cmsConfig: Record<string, any> =
|
|
7
|
-
let ghToken: string | undefined;
|
|
6
|
+
let cmsConfig: Record<string, any> = {};
|
|
8
7
|
const initCmsWithPreview = () => {
|
|
9
|
-
const {
|
|
8
|
+
const { createClass, h, CMS } = window as any;
|
|
10
9
|
CMS.init({ config: cmsConfig });
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
if (createClass && h) {
|
|
11
|
+
const Preview = createPreviewComponent({
|
|
12
|
+
createClass,
|
|
13
|
+
h,
|
|
14
|
+
cmsConfig,
|
|
15
|
+
});
|
|
16
|
+
cmsConfig.collections.forEach(({ name, files }) => {
|
|
17
|
+
const char = name.charAt(0);
|
|
18
|
+
if (char === '_' || char === '.') return;
|
|
19
|
+
if (files) {
|
|
20
|
+
files.forEach((file: Record<string, any>) => {
|
|
21
|
+
CMS.registerPreviewTemplate(file.name, Preview);
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
CMS.registerPreviewTemplate(name, Preview);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
13
28
|
};
|
|
14
29
|
|
|
15
30
|
const authAndInitCms = async () => {
|
|
@@ -18,8 +33,14 @@ const authAndInitCms = async () => {
|
|
|
18
33
|
sessionStorage,
|
|
19
34
|
ECOM_STORE_ID,
|
|
20
35
|
GIT_REPO,
|
|
36
|
+
CMS_CUSTOM_CONFIG,
|
|
21
37
|
CMS_SSO_URL = 'https://app.e-com.plus/pages/login?api_version=2',
|
|
22
38
|
} = window;
|
|
39
|
+
cmsConfig = getCmsConfig();
|
|
40
|
+
if (CMS_CUSTOM_CONFIG) {
|
|
41
|
+
const deepmerge = Deepmerge();
|
|
42
|
+
cmsConfig = deepmerge(cmsConfig, CMS_CUSTOM_CONFIG);
|
|
43
|
+
}
|
|
23
44
|
if (import.meta.env.DEV) {
|
|
24
45
|
cmsConfig.local_backend = true;
|
|
25
46
|
cmsConfig.backend = {
|
|
@@ -60,7 +81,7 @@ const authAndInitCms = async () => {
|
|
|
60
81
|
...cmsConfig.backend,
|
|
61
82
|
};
|
|
62
83
|
if (cmsConfig.backend.api_root?.startsWith('https://ecomplus.app/')) {
|
|
63
|
-
const res = await afetch('https://ecomplus.app/api/github-
|
|
84
|
+
const res = await afetch('https://ecomplus.app/api/github-installations', {
|
|
64
85
|
headers: {
|
|
65
86
|
'X-Store-ID': `${ECOM_STORE_ID}`,
|
|
66
87
|
Authorization: `Bearer ${ssoToken}`,
|
|
@@ -76,7 +97,7 @@ const authAndInitCms = async () => {
|
|
|
76
97
|
if (installation?.gh_token && installation.gh_token.charAt(0) !== '*') {
|
|
77
98
|
// Consume GitHub REST API directly
|
|
78
99
|
token = installation.gh_token as string;
|
|
79
|
-
ghToken = token;
|
|
100
|
+
// ghToken = token;
|
|
80
101
|
delete cmsConfig.backend.api_root;
|
|
81
102
|
cmsConfig.backend.repo = installation.repository;
|
|
82
103
|
cmsConfig.backend.name = 'github';
|
|
@@ -118,26 +139,13 @@ const authAndInitCms = async () => {
|
|
|
118
139
|
};
|
|
119
140
|
|
|
120
141
|
if (!import.meta.env.SSR) {
|
|
121
|
-
if (window.CMS_CUSTOM_CONFIG) {
|
|
122
|
-
const deepmerge = Deepmerge();
|
|
123
|
-
cmsConfig = deepmerge(cmsConfig, window.CMS_CUSTOM_CONFIG);
|
|
124
|
-
}
|
|
125
142
|
(window as any).CMS_MANUAL_INIT = true;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
(window as any).React = React;
|
|
135
|
-
(window as any).CMS = CMS;
|
|
136
|
-
authAndInitCms();
|
|
137
|
-
})
|
|
138
|
-
.catch((err) => {
|
|
139
|
-
console.error(err);
|
|
140
|
-
// eslint-disable-next-line
|
|
141
|
-
window.alert('Failed importing Decap CMS app or preview dependencies');
|
|
142
|
-
});
|
|
143
|
+
if (window.CMS) {
|
|
144
|
+
authAndInitCms();
|
|
145
|
+
} else {
|
|
146
|
+
const cmsScript = document.createElement('script');
|
|
147
|
+
cmsScript.src = 'https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js';
|
|
148
|
+
cmsScript.onload = authAndInitCms;
|
|
149
|
+
document.body.appendChild(cmsScript);
|
|
150
|
+
}
|
|
143
151
|
}
|
|
@@ -118,7 +118,27 @@ const loadRouteContext = async (
|
|
|
118
118
|
let urlPath = Astro.url.pathname;
|
|
119
119
|
const isPreview = urlPath.startsWith('/~preview');
|
|
120
120
|
if (isPreview) {
|
|
121
|
-
urlPath = urlPath.replace(
|
|
121
|
+
urlPath = urlPath.replace(/^\/~[^/]+\/?/, '/');
|
|
122
|
+
const getQueryContent = (filename: string) => {
|
|
123
|
+
const contentJson = Astro.url.searchParams.get(`content:${filename}`);
|
|
124
|
+
if (typeof contentJson === 'string') {
|
|
125
|
+
try {
|
|
126
|
+
const content = JSON.parse(contentJson);
|
|
127
|
+
return content;
|
|
128
|
+
} catch {
|
|
129
|
+
//
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
};
|
|
134
|
+
global.$storefrontCmsHandler = ({ filename, loadLocal }) => {
|
|
135
|
+
const content = getQueryContent(filename);
|
|
136
|
+
if (filename === 'settings') return content || loadLocal();
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
if (content) resolve(content);
|
|
139
|
+
loadLocal().then(resolve);
|
|
140
|
+
});
|
|
141
|
+
};
|
|
122
142
|
}
|
|
123
143
|
const isHomepage = urlPath === '/';
|
|
124
144
|
const isSearchPage = !isHomepage && (urlPath.startsWith('/s/') || urlPath === '/s');
|
|
@@ -131,7 +151,9 @@ const loadRouteContext = async (
|
|
|
131
151
|
}
|
|
132
152
|
const config = getConfig();
|
|
133
153
|
globalThis.$storefront.settings = config.settings;
|
|
134
|
-
let cmsContent: PageContent | null = {
|
|
154
|
+
let cmsContent: PageContent & { $filename?: `${string}/${string}` } | null = {
|
|
155
|
+
sections: [],
|
|
156
|
+
};
|
|
135
157
|
const apiState: {
|
|
136
158
|
categories?: CategoriesList,
|
|
137
159
|
brands?: BrandsList,
|
|
@@ -157,13 +179,14 @@ const loadRouteContext = async (
|
|
|
157
179
|
error: null,
|
|
158
180
|
};
|
|
159
181
|
const { slug } = Astro.params;
|
|
182
|
+
let contentFilename: `${string}/${string}` | undefined;
|
|
160
183
|
if (isHomepage) {
|
|
161
|
-
|
|
184
|
+
contentFilename = 'pages/home';
|
|
162
185
|
} else if (isSearchPage) {
|
|
163
|
-
|
|
186
|
+
contentFilename = 'pages/search';
|
|
164
187
|
} else if (slug && typeof slug === 'string') {
|
|
165
188
|
if (contentCollection) {
|
|
166
|
-
|
|
189
|
+
contentFilename = `${contentCollection}/${slug}`;
|
|
167
190
|
} else if (slug.startsWith('_api/') || slug === '_analytics') {
|
|
168
191
|
const err: any = new Error(`/${slug} route not implemented on SSR directly`);
|
|
169
192
|
Astro.response.status = 501;
|
|
@@ -177,10 +200,12 @@ const loadRouteContext = async (
|
|
|
177
200
|
Object.assign(apiContext, response.data);
|
|
178
201
|
const apiResource = apiContext.resource as
|
|
179
202
|
Exclude<typeof apiContext.resource, undefined>;
|
|
180
|
-
|
|
203
|
+
contentFilename = `pages/${apiResource}`;
|
|
204
|
+
config.getContent(contentFilename)
|
|
181
205
|
.then((_cmsContent) => {
|
|
182
206
|
if (cmsContent && _cmsContent) {
|
|
183
207
|
Object.assign(cmsContent, _cmsContent);
|
|
208
|
+
cmsContent.$filename = contentFilename;
|
|
184
209
|
}
|
|
185
210
|
})
|
|
186
211
|
.catch(console.warn);
|
|
@@ -212,6 +237,10 @@ const loadRouteContext = async (
|
|
|
212
237
|
}
|
|
213
238
|
}
|
|
214
239
|
}
|
|
240
|
+
if (contentFilename) {
|
|
241
|
+
cmsContent = await config.getContent(contentFilename);
|
|
242
|
+
if (cmsContent) cmsContent.$filename = contentFilename;
|
|
243
|
+
}
|
|
215
244
|
try {
|
|
216
245
|
(await Promise.all(apiPrefetchings)).forEach((response) => {
|
|
217
246
|
if (response) {
|
|
@@ -267,11 +296,6 @@ const loadRouteContext = async (
|
|
|
267
296
|
} else {
|
|
268
297
|
setResponseCache(Astro, 120, 180);
|
|
269
298
|
}
|
|
270
|
-
if (isPreview || urlPath.startsWith('/admin/')) {
|
|
271
|
-
// https://webcontainers.io/guides/configuring-headers#configuring-headers
|
|
272
|
-
Astro.response.headers.set('Cross-Origin-Embedder-Policy', 'require-corp');
|
|
273
|
-
Astro.response.headers.set('Cross-Origin-Opener-Policy', 'same-origin');
|
|
274
|
-
}
|
|
275
299
|
const routeContext = {
|
|
276
300
|
...config,
|
|
277
301
|
isHomepage,
|
|
@@ -1,38 +1,99 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { Ref, ShallowRef, ShallowReactive } from 'vue';
|
|
2
|
+
import type { ContentFilename, ContentData as _ContentData } from '@@sf/content';
|
|
3
|
+
import { watch, shallowRef } from 'vue';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
type ContentData<T extends ContentFilename> = NonNullable<_ContentData<T>>;
|
|
6
|
+
|
|
7
|
+
type SubfieldContentKeys<
|
|
8
|
+
T extends ContentFilename,
|
|
9
|
+
F extends undefined | keyof ContentData<T>,
|
|
10
|
+
> = F extends keyof ContentData<T> ? keyof ContentData<T>[F] : undefined;
|
|
11
|
+
|
|
12
|
+
type NestedContentData<
|
|
13
|
+
T extends ContentFilename,
|
|
14
|
+
F extends undefined | keyof ContentData<T>,
|
|
15
|
+
S extends SubfieldContentKeys<T, F>,
|
|
16
|
+
> = F extends keyof ContentData<T>
|
|
17
|
+
? S extends keyof ContentData<T>[F]
|
|
18
|
+
? ContentData<T>[F][S] : ContentData<T>[F]
|
|
19
|
+
: ContentData<T>;
|
|
20
|
+
|
|
21
|
+
export function useCmsPreview<
|
|
22
|
+
T extends ContentFilename,
|
|
23
|
+
F extends keyof ContentData<T>,
|
|
24
|
+
S extends SubfieldContentKeys<T, F>,
|
|
25
|
+
>(filename: T | [T] | [T, F] | [T, F, S]) {
|
|
26
|
+
let field: F | undefined;
|
|
27
|
+
let subfield: S | undefined;
|
|
28
|
+
if (Array.isArray(filename)) {
|
|
29
|
+
field = filename[1];
|
|
30
|
+
subfield = filename[2];
|
|
31
|
+
filename = filename[0];
|
|
32
|
+
}
|
|
33
|
+
const liveContent = shallowRef<NestedContentData<T, F, S> | undefined>();
|
|
34
|
+
if (!import.meta.env.SSR && window.$isCmsPreview) {
|
|
9
35
|
const id = btoa(JSON.stringify({ filename }));
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
);
|
|
36
|
+
const openMessage = {
|
|
37
|
+
type: 'open',
|
|
38
|
+
filename,
|
|
39
|
+
field,
|
|
40
|
+
subfield,
|
|
41
|
+
id,
|
|
42
|
+
};
|
|
43
|
+
window.parent.postMessage(openMessage, window.location.origin);
|
|
18
44
|
window.addEventListener('message', (event) => {
|
|
19
45
|
if (event.data.id === id) {
|
|
20
|
-
|
|
46
|
+
if (field) {
|
|
47
|
+
liveContent.value = subfield
|
|
48
|
+
? event.data.data[field][subfield]
|
|
49
|
+
: event.data.data[field];
|
|
50
|
+
} else {
|
|
51
|
+
liveContent.value = event.data.data;
|
|
52
|
+
}
|
|
21
53
|
}
|
|
22
54
|
});
|
|
23
55
|
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const useCmsPreview = <T extends ContentFilename>(filename: T) => {
|
|
27
|
-
const liveContent = shallowRef<NonNullable<ContentData<T>> | null>(null);
|
|
28
|
-
if (!import.meta.env.SSR && window.$isCmsPreview) {
|
|
29
|
-
getCmsUpdates(filename, (newData) => {
|
|
30
|
-
liveContent.value = newData;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
56
|
return { liveContent };
|
|
34
|
-
}
|
|
57
|
+
}
|
|
35
58
|
|
|
36
59
|
export default useCmsPreview;
|
|
37
60
|
|
|
38
|
-
export
|
|
61
|
+
export interface SectionPreviewProps {
|
|
62
|
+
cmsPreview?: {
|
|
63
|
+
contentFilename: `${string}/${string}`;
|
|
64
|
+
sectionIndex: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const useSectionPreview = (
|
|
69
|
+
{ cmsPreview }: SectionPreviewProps,
|
|
70
|
+
refs?: Record<string, Ref<any> | ShallowRef<any> | ShallowReactive<any>>,
|
|
71
|
+
) => {
|
|
72
|
+
if (cmsPreview) {
|
|
73
|
+
const { contentFilename, sectionIndex } = cmsPreview;
|
|
74
|
+
const {
|
|
75
|
+
liveContent,
|
|
76
|
+
} = useCmsPreview([contentFilename, 'sections', sectionIndex]);
|
|
77
|
+
if (refs) {
|
|
78
|
+
watch(liveContent, (data) => {
|
|
79
|
+
if (!data) return;
|
|
80
|
+
Object.keys(refs).forEach((key) => {
|
|
81
|
+
if (refs[key].value !== undefined) {
|
|
82
|
+
if (data[key] === undefined) return;
|
|
83
|
+
refs[key].value = data[key];
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!data[key]) return;
|
|
87
|
+
if (Array.isArray(refs[key])) {
|
|
88
|
+
refs[key].splice(0);
|
|
89
|
+
data[key].forEach((v: any) => refs[key].push(v));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
Object.assign(refs[key], data[key]);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { liveContent };
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hydrate on context script executed (`$storefront.apiContext` ready)
|
|
3
|
-
* Check event emits at BaseHead.astro and use-shared-data.ts
|
|
4
|
-
* @type {import('astro').ClientDirective}
|
|
5
|
-
*/
|
|
6
|
-
export default (load, opts) => {
|
|
7
|
-
const hy = async () => {
|
|
8
|
-
const hydrate = await load();
|
|
9
|
-
await hydrate();
|
|
10
|
-
};
|
|
11
|
-
const next = () => {
|
|
12
|
-
const arrOpts = Array.isArray(opts.value) ? opts.value : [opts.value];
|
|
13
|
-
for (let i = 0; i < arrOpts.length; i++) {
|
|
14
|
-
if (typeof arrOpts[i] === 'string' && arrOpts[i].startsWith('data:')) {
|
|
15
|
-
const field = arrOpts[i].substring(5);
|
|
16
|
-
if (!window.$storefront?.data?.[field]) {
|
|
17
|
-
window.addEventListener(
|
|
18
|
-
`storefront:data:${field}`,
|
|
19
|
-
next,
|
|
20
|
-
{ once: true },
|
|
21
|
-
);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (arrOpts.includes('idle')) {
|
|
27
|
-
if (typeof window.requestIdleCallback === 'function') {
|
|
28
|
-
setTimeout(() => window.requestIdleCallback(hy), 9);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
setTimeout(hy, 200);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
hy();
|
|
35
|
-
};
|
|
36
|
-
const id = window.$storefront?.apiContext?.doc._id || null;
|
|
37
|
-
if (window._firstLoadContextId === id && window._emitedContextId === id) {
|
|
38
|
-
console.log('[ctx] first load');
|
|
39
|
-
next();
|
|
40
|
-
document.addEventListener('astro:beforeload', () => {
|
|
41
|
-
delete window._firstLoadContextId;
|
|
42
|
-
}, { once: true });
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
window.addEventListener('storefront:apiContext', next, { once: true });
|
|
46
|
-
};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type NReact from 'react';
|
|
2
|
-
import { initWebcontainer } from './preview/webcontainer';
|
|
3
|
-
|
|
4
|
-
export const genPreviewContainer = ({ React, cmsConfig, ghToken }: {
|
|
5
|
-
React: typeof NReact,
|
|
6
|
-
cmsConfig: Record<string, any>,
|
|
7
|
-
ghToken?: string,
|
|
8
|
-
}) => {
|
|
9
|
-
const { repo } = cmsConfig.backend;
|
|
10
|
-
const cliTextareaId = 'webcontainerCli';
|
|
11
|
-
const serverIframeId = 'serverPreview';
|
|
12
|
-
// https://github.com/decaporg/decap-cms/issues/2183#issuecomment-997373169
|
|
13
|
-
return class Prevew extends React.Component {
|
|
14
|
-
render() {
|
|
15
|
-
const { entry } = (this as any).props;
|
|
16
|
-
console.log({ entry });
|
|
17
|
-
const slug = '';
|
|
18
|
-
const urlPath = `/~preview/${slug}`;
|
|
19
|
-
const html = `
|
|
20
|
-
<textarea id="${cliTextareaId}"></textarea>
|
|
21
|
-
<iframe
|
|
22
|
-
if="${serverIframeId}"
|
|
23
|
-
data-url="${urlPath}"
|
|
24
|
-
border="0"
|
|
25
|
-
width="100%"
|
|
26
|
-
height="100%"
|
|
27
|
-
style="border: 1px solid #EEE; height: calc(100vh - 30px)"
|
|
28
|
-
></iframe>`;
|
|
29
|
-
return React.createElement('div', {
|
|
30
|
-
dangerouslySetInnerHTML: { __html: html },
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
componentDidMount() {
|
|
34
|
-
const iframe = document.getElementById('preview-pane') as HTMLIFrameElement;
|
|
35
|
-
const cliTextarea = iframe.contentWindow!.document
|
|
36
|
-
.getElementById(cliTextareaId) as HTMLTextAreaElement;
|
|
37
|
-
const previewIframe = iframe.contentWindow!.document
|
|
38
|
-
.getElementById(serverIframeId) as HTMLIFrameElement;
|
|
39
|
-
initWebcontainer({ repo, ghToken, cliTextarea })
|
|
40
|
-
.then(({ webcontainerInstance, startDevServer }) => {
|
|
41
|
-
webcontainerInstance.on('server-ready', (port, url) => {
|
|
42
|
-
console.log({ port, url });
|
|
43
|
-
previewIframe.src = `${url}${previewIframe.dataset.url}`;
|
|
44
|
-
});
|
|
45
|
-
startDevServer();
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export default genPreviewContainer;
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { WebContainer } from '@webcontainer/api';
|
|
2
|
-
|
|
3
|
-
export const genContainerFiles = ({ repo, ghToken, repoDir }: {
|
|
4
|
-
repo: string,
|
|
5
|
-
ghToken?: string,
|
|
6
|
-
repoDir: string,
|
|
7
|
-
}) => ({
|
|
8
|
-
'git.js': {
|
|
9
|
-
file: {
|
|
10
|
-
contents: `
|
|
11
|
-
import fs from 'node:fs';
|
|
12
|
-
import { join as joinPath } from 'node:path';
|
|
13
|
-
import { clone, pull } from 'isomorphic-git';
|
|
14
|
-
import * as http from 'isomorphic-git/http/node/index.cjs';
|
|
15
|
-
const dir = joinPath(process.cwd(), '${repoDir}');
|
|
16
|
-
const isClone = process.argv.find((val) => val === '--clone');
|
|
17
|
-
const options = {
|
|
18
|
-
fs,
|
|
19
|
-
http,
|
|
20
|
-
dir,
|
|
21
|
-
singleBranch: true,
|
|
22
|
-
};
|
|
23
|
-
${(ghToken
|
|
24
|
-
? `
|
|
25
|
-
options.oauth2format = 'github';
|
|
26
|
-
options.token = '${ghToken}';`
|
|
27
|
-
: '')}
|
|
28
|
-
if (isClone) {
|
|
29
|
-
options.url = 'https://github.com/${repo}.git';
|
|
30
|
-
options.depth = 1;
|
|
31
|
-
clone(options);
|
|
32
|
-
} else {
|
|
33
|
-
pull(options);
|
|
34
|
-
}
|
|
35
|
-
`,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
'package.json': {
|
|
39
|
-
file: {
|
|
40
|
-
contents: `
|
|
41
|
-
{
|
|
42
|
-
"name": "git-app",
|
|
43
|
-
"version": "1.0.0",
|
|
44
|
-
"type": "module",
|
|
45
|
-
"private": true,
|
|
46
|
-
"description": "",
|
|
47
|
-
"author": "",
|
|
48
|
-
"license": "ISC",
|
|
49
|
-
"scripts": {
|
|
50
|
-
"git:clone": "node git.js --clone",
|
|
51
|
-
"git:pull": "node git.js --pull"
|
|
52
|
-
},
|
|
53
|
-
"dependencies": {
|
|
54
|
-
"isomorphic-git": "^1.25.3"
|
|
55
|
-
}
|
|
56
|
-
}`,
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
export const initWebcontainer = async ({ repo, ghToken, cliTextarea }: {
|
|
62
|
-
repo: string,
|
|
63
|
-
ghToken?: string,
|
|
64
|
-
cliTextarea: HTMLTextAreaElement,
|
|
65
|
-
}) => {
|
|
66
|
-
const webcontainerInstance = await WebContainer.boot();
|
|
67
|
-
const repoDir = 'store';
|
|
68
|
-
const files = genContainerFiles({ repo, ghToken, repoDir });
|
|
69
|
-
await webcontainerInstance.mount(files);
|
|
70
|
-
const exec = async (command: string, args: string[]) => {
|
|
71
|
-
const cliArgs = args.reduce((acc, opt) => `${acc} ${opt}`, '');
|
|
72
|
-
const cli = `$ ${command}${cliArgs}\n`;
|
|
73
|
-
cliTextarea.value += cli;
|
|
74
|
-
const proc = await webcontainerInstance.spawn(command, args);
|
|
75
|
-
if (import.meta.env.DEV || (window as any).DEBUG) {
|
|
76
|
-
proc.output.pipeTo(new WritableStream({
|
|
77
|
-
write(stdout) {
|
|
78
|
-
console.debug?.('webcontainer', { stdout });
|
|
79
|
-
},
|
|
80
|
-
}));
|
|
81
|
-
}
|
|
82
|
-
if (await proc.exit !== 0) {
|
|
83
|
-
throw new Error(`${command} failed`);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
await exec('npm', ['install']);
|
|
87
|
-
await exec('npm', ['run', 'git:clone']);
|
|
88
|
-
const ssrDir = `${repoDir}/functions/ssr`;
|
|
89
|
-
await exec('npm', ['--prefix', ssrDir, 'i']);
|
|
90
|
-
await webcontainerInstance.fs.writeFile(
|
|
91
|
-
`${ssrDir}/.env`,
|
|
92
|
-
`ECOM_STORE_ID=${window.ECOM_STORE_ID}\n`,
|
|
93
|
-
);
|
|
94
|
-
const startDevServer = async () => {
|
|
95
|
-
await exec('npm', ['--prefix', ssrDir, 'run', 'dev']);
|
|
96
|
-
// Keep restarting dev server (can crash)
|
|
97
|
-
startDevServer();
|
|
98
|
-
};
|
|
99
|
-
return {
|
|
100
|
-
webcontainerInstance,
|
|
101
|
-
webcontainerExec: exec,
|
|
102
|
-
startDevServer,
|
|
103
|
-
};
|
|
104
|
-
};
|