webstudio 0.167.0 → 0.173.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/README.md +1 -1
- package/bin.js +1 -1
- package/lib/cli.js +310 -153
- package/package.json +33 -26
- package/templates/cloudflare/functions/[[path]].ts +2 -0
- package/templates/cloudflare/package.json +4 -6
- package/templates/cloudflare/tsconfig.json +3 -18
- package/templates/defaults/app/route-templates/default-sitemap.tsx +1 -1
- package/templates/defaults/app/route-templates/html.tsx +76 -71
- package/templates/defaults/app/route-templates/redirect.tsx +6 -0
- package/templates/defaults/app/route-templates/xml.tsx +25 -10
- package/templates/defaults/package.json +14 -14
- package/templates/defaults/tsconfig.json +5 -6
- package/templates/internal/tsconfig.json +1 -21
- package/templates/netlify-edge-functions/package.json +1 -1
- package/templates/netlify-functions/package.json +1 -1
- package/templates/saas-helpers/tsconfig.json +3 -19
- package/templates/ssg/app/constants.mjs +13 -0
- package/templates/ssg/app/route-templates/html/+Head.tsx +92 -0
- package/templates/ssg/app/route-templates/html/+Page.tsx +22 -0
- package/templates/ssg/app/route-templates/html/+data.ts +37 -0
- package/templates/ssg/package.json +31 -0
- package/templates/ssg/pages/+config.ts +12 -0
- package/templates/ssg/public/favicon.ico +0 -0
- package/templates/ssg/renderer/+onRenderClient.tsx +30 -0
- package/templates/ssg/renderer/+onRenderHtml.tsx +29 -0
- package/templates/ssg/tsconfig.json +22 -0
- package/templates/ssg/vike.d.ts +26 -0
- package/templates/ssg/vite.config.ts +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webstudio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.173.0",
|
|
4
4
|
"description": "Webstudio CLI",
|
|
5
5
|
"author": "Webstudio <github@webstudio.is>",
|
|
6
6
|
"homepage": "https://webstudio.is",
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
"webstudio-cli": "./bin.js",
|
|
10
10
|
"webstudio": "./bin.js"
|
|
11
11
|
},
|
|
12
|
+
"imports": {
|
|
13
|
+
"#cli": {
|
|
14
|
+
"webstudio": "./src/cli.ts",
|
|
15
|
+
"default": "./lib/cli.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
12
18
|
"files": [
|
|
13
19
|
"lib/*",
|
|
14
20
|
"templates/*",
|
|
@@ -16,56 +22,57 @@
|
|
|
16
22
|
"!*.{test,stories}.*"
|
|
17
23
|
],
|
|
18
24
|
"license": "AGPL-3.0-or-later",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20.12"
|
|
27
|
+
},
|
|
19
28
|
"dependencies": {
|
|
20
29
|
"@clack/prompts": "^0.7.0",
|
|
21
|
-
"change-case": "^5.
|
|
30
|
+
"change-case": "^5.4.4",
|
|
22
31
|
"deepmerge": "^4.3.1",
|
|
23
32
|
"env-paths": "^3.0.0",
|
|
24
33
|
"execa": "^7.2.0",
|
|
25
|
-
"parse5": "7.1.2",
|
|
26
34
|
"p-limit": "^4.0.0",
|
|
35
|
+
"parse5": "7.1.2",
|
|
27
36
|
"picocolors": "^1.0.1",
|
|
28
37
|
"strip-indent": "^4.0.0",
|
|
29
|
-
"title-case": "^4.1.0",
|
|
30
38
|
"yargs": "^17.7.2",
|
|
31
39
|
"zod": "^3.22.4",
|
|
32
|
-
"@webstudio-is/http-client": "0.
|
|
33
|
-
"@webstudio-is/
|
|
34
|
-
"@webstudio-is/react
|
|
35
|
-
"@webstudio-is/
|
|
36
|
-
"@webstudio-is/
|
|
37
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
38
|
-
"@webstudio-is/sdk-components-react-remix": "0.
|
|
40
|
+
"@webstudio-is/http-client": "0.173.0",
|
|
41
|
+
"@webstudio-is/sdk": "0.173.0",
|
|
42
|
+
"@webstudio-is/sdk-components-react": "0.173.0",
|
|
43
|
+
"@webstudio-is/image": "0.173.0",
|
|
44
|
+
"@webstudio-is/react-sdk": "0.173.0",
|
|
45
|
+
"@webstudio-is/sdk-components-react-radix": "0.173.0",
|
|
46
|
+
"@webstudio-is/sdk-components-react-remix": "0.173.0"
|
|
39
47
|
},
|
|
40
48
|
"devDependencies": {
|
|
41
49
|
"@jest/globals": "^29.7.0",
|
|
42
50
|
"@netlify/remix-adapter": "^2.4.0",
|
|
43
51
|
"@netlify/remix-edge-adapter": "3.3.0",
|
|
44
|
-
"@remix-run/cloudflare": "^2.
|
|
45
|
-
"@remix-run/cloudflare-pages": "^2.
|
|
46
|
-
"@remix-run/dev": "^2.
|
|
47
|
-
"@remix-run/node": "^2.
|
|
48
|
-
"@remix-run/react": "^2.
|
|
49
|
-
"@remix-run/server-runtime": "^2.
|
|
52
|
+
"@remix-run/cloudflare": "^2.10.3",
|
|
53
|
+
"@remix-run/cloudflare-pages": "^2.10.3",
|
|
54
|
+
"@remix-run/dev": "^2.10.3",
|
|
55
|
+
"@remix-run/node": "^2.10.3",
|
|
56
|
+
"@remix-run/react": "^2.10.3",
|
|
57
|
+
"@remix-run/server-runtime": "^2.10.3",
|
|
50
58
|
"@types/node": "^20.12.7",
|
|
51
59
|
"@types/react": "^18.2.70",
|
|
52
60
|
"@types/react-dom": "^18.2.25",
|
|
53
61
|
"@types/yargs": "^17.0.32",
|
|
62
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
54
63
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
55
64
|
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
|
56
|
-
"typescript": "5.
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"@webstudio-is/
|
|
61
|
-
"@webstudio-is/jest-config": "1.0.7"
|
|
65
|
+
"typescript": "5.5.2",
|
|
66
|
+
"vike": "^0.4.180",
|
|
67
|
+
"vite": "^5.3.4",
|
|
68
|
+
"wrangler": "^3.63.2",
|
|
69
|
+
"@webstudio-is/form-handlers": "0.173.0",
|
|
70
|
+
"@webstudio-is/jest-config": "1.0.7",
|
|
71
|
+
"@webstudio-is/tsconfig": "1.0.7"
|
|
62
72
|
},
|
|
63
73
|
"scripts": {
|
|
64
74
|
"typecheck": "tsc",
|
|
65
|
-
"checks": "pnpm typecheck",
|
|
66
75
|
"build": "rm -rf lib && esbuild src/cli.ts --outdir=lib --bundle --format=esm --packages=external",
|
|
67
|
-
"local-run": "tsx --no-warnings ./src/bin.ts",
|
|
68
|
-
"dev": "esbuild src/cli.ts --watch --bundle --format=esm --packages=external --outdir=./lib",
|
|
69
76
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
|
|
70
77
|
}
|
|
71
78
|
}
|
|
@@ -4,4 +4,6 @@ import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
|
|
|
4
4
|
// @ts-ignore - the server build file is generated by `remix vite:build`
|
|
5
5
|
import * as build from "../build/server";
|
|
6
6
|
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
8
|
+
// @ts-ignore - the server build file is generated by `remix vite:build`
|
|
7
9
|
export const onRequest = createPagesFunctionHandler({ build });
|
|
@@ -12,13 +12,11 @@
|
|
|
12
12
|
"build-cf-types": "wrangler types"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@remix-run/cloudflare": "2.
|
|
16
|
-
"@remix-run/cloudflare-pages": "2.
|
|
17
|
-
"isbot": "^5.1.8"
|
|
15
|
+
"@remix-run/cloudflare": "2.10.3",
|
|
16
|
+
"@remix-run/cloudflare-pages": "2.10.3"
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
|
-
"@cloudflare/workers-types": "^4.
|
|
21
|
-
"wrangler": "^3.
|
|
22
|
-
"miniflare": "^3.20231030.4"
|
|
19
|
+
"@cloudflare/workers-types": "^4.20240620.0",
|
|
20
|
+
"wrangler": "^3.63.2"
|
|
23
21
|
}
|
|
24
22
|
}
|
|
@@ -8,26 +8,11 @@
|
|
|
8
8
|
"**/.client/**/*.tsx"
|
|
9
9
|
],
|
|
10
10
|
"compilerOptions": {
|
|
11
|
-
"lib": ["DOM", "DOM.Iterable", "ES2023"],
|
|
12
11
|
"types": [
|
|
13
12
|
"@remix-run/cloudflare",
|
|
14
13
|
"vite/client",
|
|
15
|
-
"@cloudflare/workers-types/2023-07-01"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"esModuleInterop": true,
|
|
19
|
-
"jsx": "react-jsx",
|
|
20
|
-
"module": "ESNext",
|
|
21
|
-
"moduleResolution": "Bundler",
|
|
22
|
-
"resolveJsonModule": true,
|
|
23
|
-
"target": "ES2022",
|
|
24
|
-
"strict": true,
|
|
25
|
-
"allowJs": true,
|
|
26
|
-
"skipLibCheck": true,
|
|
27
|
-
"forceConsistentCasingInFileNames": true,
|
|
28
|
-
"baseUrl": ".",
|
|
29
|
-
|
|
30
|
-
// Vite takes care of building everything, not tsc.
|
|
31
|
-
"noEmit": true
|
|
14
|
+
"@cloudflare/workers-types/2023-07-01",
|
|
15
|
+
"@webstudio-is/react-sdk/placeholder"
|
|
16
|
+
]
|
|
32
17
|
}
|
|
33
18
|
}
|
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
redirect,
|
|
11
11
|
} from "@remix-run/server-runtime";
|
|
12
12
|
import { useLoaderData } from "@remix-run/react";
|
|
13
|
+
import {
|
|
14
|
+
isLocalResource,
|
|
15
|
+
loadResource,
|
|
16
|
+
loadResources,
|
|
17
|
+
} from "@webstudio-is/sdk";
|
|
13
18
|
import { ReactSdkContext } from "@webstudio-is/react-sdk";
|
|
14
19
|
import {
|
|
15
20
|
n8nHandler,
|
|
@@ -20,21 +25,34 @@ import {
|
|
|
20
25
|
Page,
|
|
21
26
|
siteName,
|
|
22
27
|
favIconAsset,
|
|
23
|
-
socialImageAsset,
|
|
24
28
|
pageFontAssets,
|
|
25
29
|
pageBackgroundImageAssets,
|
|
26
|
-
} from "
|
|
30
|
+
} from "__CLIENT__";
|
|
27
31
|
import {
|
|
28
|
-
|
|
29
|
-
loadResources,
|
|
32
|
+
getResources,
|
|
30
33
|
getPageMeta,
|
|
31
34
|
getRemixParams,
|
|
32
35
|
projectId,
|
|
33
36
|
contactEmail,
|
|
34
|
-
} from "
|
|
37
|
+
} from "__SERVER__";
|
|
38
|
+
import { assetBaseUrl, imageBaseUrl, imageLoader } from "__CONSTANTS__";
|
|
39
|
+
import css from "__CSS__?url";
|
|
40
|
+
import { sitemap } from "__SITEMAP__";
|
|
41
|
+
|
|
42
|
+
const customFetch: typeof fetch = (input, init) => {
|
|
43
|
+
if (typeof input !== "string") {
|
|
44
|
+
return fetch(input, init);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (isLocalResource(input, "sitemap.xml")) {
|
|
48
|
+
// @todo: dynamic import sitemap ???
|
|
49
|
+
const response = new Response(JSON.stringify(sitemap));
|
|
50
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
51
|
+
return Promise.resolve(response);
|
|
52
|
+
}
|
|
35
53
|
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
return fetch(input, init);
|
|
55
|
+
};
|
|
38
56
|
|
|
39
57
|
export const loader = async (arg: LoaderFunctionArgs) => {
|
|
40
58
|
const url = new URL(arg.request.url);
|
|
@@ -52,7 +70,10 @@ export const loader = async (arg: LoaderFunctionArgs) => {
|
|
|
52
70
|
origin: url.origin,
|
|
53
71
|
};
|
|
54
72
|
|
|
55
|
-
const resources = await loadResources(
|
|
73
|
+
const resources = await loadResources(
|
|
74
|
+
customFetch,
|
|
75
|
+
getResources({ system }).data
|
|
76
|
+
);
|
|
56
77
|
const pageMeta = getPageMeta({ system, resources });
|
|
57
78
|
|
|
58
79
|
if (pageMeta.redirect) {
|
|
@@ -66,11 +87,14 @@ export const loader = async (arg: LoaderFunctionArgs) => {
|
|
|
66
87
|
// typecheck
|
|
67
88
|
arg.context.EXCLUDE_FROM_SEARCH satisfies boolean;
|
|
68
89
|
|
|
90
|
+
if (arg.context.EXCLUDE_FROM_SEARCH) {
|
|
91
|
+
pageMeta.excludePageFromSearch = arg.context.EXCLUDE_FROM_SEARCH;
|
|
92
|
+
}
|
|
93
|
+
|
|
69
94
|
return json(
|
|
70
95
|
{
|
|
71
96
|
host,
|
|
72
97
|
url: url.href,
|
|
73
|
-
excludeFromSearch: arg.context.EXCLUDE_FROM_SEARCH,
|
|
74
98
|
system,
|
|
75
99
|
resources,
|
|
76
100
|
pageMeta,
|
|
@@ -86,7 +110,7 @@ export const loader = async (arg: LoaderFunctionArgs) => {
|
|
|
86
110
|
);
|
|
87
111
|
};
|
|
88
112
|
|
|
89
|
-
export const headers: HeadersFunction = (
|
|
113
|
+
export const headers: HeadersFunction = () => {
|
|
90
114
|
return {
|
|
91
115
|
"Cache-Control": "public, max-age=0, must-revalidate",
|
|
92
116
|
};
|
|
@@ -134,7 +158,7 @@ export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
|
|
134
158
|
});
|
|
135
159
|
}
|
|
136
160
|
|
|
137
|
-
if (pageMeta.excludePageFromSearch
|
|
161
|
+
if (pageMeta.excludePageFromSearch) {
|
|
138
162
|
metas.push({
|
|
139
163
|
name: "robots",
|
|
140
164
|
content: "noindex, nofollow",
|
|
@@ -152,11 +176,11 @@ export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
|
|
152
176
|
});
|
|
153
177
|
}
|
|
154
178
|
|
|
155
|
-
if (
|
|
179
|
+
if (pageMeta.socialImageAssetName) {
|
|
156
180
|
metas.push({
|
|
157
181
|
property: "og:image",
|
|
158
182
|
content: `https://${data.host}${imageLoader({
|
|
159
|
-
src:
|
|
183
|
+
src: pageMeta.socialImageAssetName,
|
|
160
184
|
// Do not transform social image (not enough information do we need to do this)
|
|
161
185
|
format: "raw",
|
|
162
186
|
})}`,
|
|
@@ -186,24 +210,15 @@ export const links: LinksFunction = () => {
|
|
|
186
210
|
rel: "icon",
|
|
187
211
|
href: imageLoader({
|
|
188
212
|
src: favIconAsset.name,
|
|
189
|
-
width
|
|
213
|
+
// width,height must be multiple of 48 https://developers.google.com/search/docs/appearance/favicon-in-search
|
|
214
|
+
width: 144,
|
|
215
|
+
height: 144,
|
|
216
|
+
fit: "pad",
|
|
190
217
|
quality: 100,
|
|
191
218
|
format: "auto",
|
|
192
219
|
}),
|
|
193
220
|
type: undefined,
|
|
194
221
|
});
|
|
195
|
-
} else {
|
|
196
|
-
result.push({
|
|
197
|
-
rel: "icon",
|
|
198
|
-
href: "/favicon.ico",
|
|
199
|
-
type: "image/x-icon",
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
result.push({
|
|
203
|
-
rel: "shortcut icon",
|
|
204
|
-
href: "/favicon.ico",
|
|
205
|
-
type: "image/x-icon",
|
|
206
|
-
});
|
|
207
222
|
}
|
|
208
223
|
|
|
209
224
|
for (const asset of pageFontAssets) {
|
|
@@ -229,19 +244,6 @@ export const links: LinksFunction = () => {
|
|
|
229
244
|
const getRequestHost = (request: Request): string =>
|
|
230
245
|
request.headers.get("x-forwarded-host") || request.headers.get("host") || "";
|
|
231
246
|
|
|
232
|
-
const getMethod = (value: string | undefined) => {
|
|
233
|
-
if (value === undefined) {
|
|
234
|
-
return "post";
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
switch (value.toLowerCase()) {
|
|
238
|
-
case "get":
|
|
239
|
-
return "get";
|
|
240
|
-
default:
|
|
241
|
-
return "post";
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
|
|
245
247
|
export const action = async ({
|
|
246
248
|
request,
|
|
247
249
|
context,
|
|
@@ -249,14 +251,25 @@ export const action = async ({
|
|
|
249
251
|
{ success: true } | { success: false; errors: string[] }
|
|
250
252
|
> => {
|
|
251
253
|
try {
|
|
254
|
+
const pageUrl = new URL(request.url);
|
|
255
|
+
pageUrl.host = getRequestHost(request);
|
|
256
|
+
|
|
252
257
|
const formData = await request.formData();
|
|
253
258
|
|
|
254
|
-
const
|
|
259
|
+
const system = {
|
|
260
|
+
params: {},
|
|
261
|
+
search: {},
|
|
262
|
+
origin: pageUrl.origin,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const resourceName = formData.get(formIdFieldName);
|
|
255
266
|
|
|
256
|
-
if (
|
|
267
|
+
if (resourceName == null || typeof resourceName !== "string") {
|
|
257
268
|
throw new Error("No form id in FormData");
|
|
258
269
|
}
|
|
259
270
|
|
|
271
|
+
const resource = getResources({ system }).action.get(resourceName);
|
|
272
|
+
|
|
260
273
|
const formBotValue = formData.get(formBotFieldName);
|
|
261
274
|
|
|
262
275
|
if (formBotValue == null || typeof formBotValue !== "string") {
|
|
@@ -275,41 +288,32 @@ export const action = async ({
|
|
|
275
288
|
throw new Error(`Form bot value invalid ${formBotValue}`);
|
|
276
289
|
}
|
|
277
290
|
|
|
278
|
-
|
|
291
|
+
formData.delete(formIdFieldName);
|
|
292
|
+
formData.delete(formBotFieldName);
|
|
279
293
|
|
|
280
|
-
|
|
281
|
-
|
|
294
|
+
if (resource) {
|
|
295
|
+
const { ok, statusText } = await loadResource(fetch, {
|
|
296
|
+
...resource,
|
|
297
|
+
body: Object.fromEntries(formData),
|
|
298
|
+
});
|
|
299
|
+
if (ok) {
|
|
300
|
+
return { success: true };
|
|
301
|
+
}
|
|
302
|
+
return { success: false, errors: [statusText] };
|
|
303
|
+
}
|
|
282
304
|
|
|
283
305
|
if (contactEmail === undefined) {
|
|
284
306
|
throw new Error("Contact email not found");
|
|
285
307
|
}
|
|
286
308
|
|
|
287
|
-
const pageUrl = new URL(request.url);
|
|
288
|
-
pageUrl.host = getRequestHost(request);
|
|
289
|
-
|
|
290
|
-
if (action !== undefined) {
|
|
291
|
-
try {
|
|
292
|
-
// Test that action is full URL
|
|
293
|
-
new URL(action);
|
|
294
|
-
} catch {
|
|
295
|
-
throw new Error(
|
|
296
|
-
"Invalid action URL, must be valid http/https protocol"
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const formInfo = {
|
|
302
|
-
formData,
|
|
303
|
-
projectId,
|
|
304
|
-
action: action ?? null,
|
|
305
|
-
method: getMethod(method),
|
|
306
|
-
pageUrl: pageUrl.toString(),
|
|
307
|
-
toEmail: contactEmail,
|
|
308
|
-
fromEmail: pageUrl.hostname + "@webstudio.email",
|
|
309
|
-
} as const;
|
|
310
|
-
|
|
311
309
|
const result = await n8nHandler({
|
|
312
|
-
formInfo
|
|
310
|
+
formInfo: {
|
|
311
|
+
formId: [projectId, resourceName].join("--"),
|
|
312
|
+
formData,
|
|
313
|
+
pageUrl: pageUrl.toString(),
|
|
314
|
+
toEmail: contactEmail,
|
|
315
|
+
fromEmail: pageUrl.hostname + "@webstudio.email",
|
|
316
|
+
},
|
|
313
317
|
hookUrl: context.N8N_FORM_EMAIL_HOOK,
|
|
314
318
|
});
|
|
315
319
|
|
|
@@ -325,7 +329,7 @@ export const action = async ({
|
|
|
325
329
|
};
|
|
326
330
|
|
|
327
331
|
const Outlet = () => {
|
|
328
|
-
const { system, resources } = useLoaderData<typeof loader>();
|
|
332
|
+
const { system, resources, url } = useLoaderData<typeof loader>();
|
|
329
333
|
return (
|
|
330
334
|
<ReactSdkContext.Provider
|
|
331
335
|
value={{
|
|
@@ -335,7 +339,8 @@ const Outlet = () => {
|
|
|
335
339
|
resources,
|
|
336
340
|
}}
|
|
337
341
|
>
|
|
338
|
-
|
|
342
|
+
{/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */}
|
|
343
|
+
<Page key={url} system={system} />
|
|
339
344
|
</ReactSdkContext.Provider>
|
|
340
345
|
);
|
|
341
346
|
};
|
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
/* eslint-disable camelcase */
|
|
2
|
+
import { renderToString } from "react-dom/server";
|
|
2
3
|
import { type LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
|
|
4
|
+
import { isLocalResource, loadResources } from "@webstudio-is/sdk";
|
|
3
5
|
import { ReactSdkContext } from "@webstudio-is/react-sdk";
|
|
4
|
-
import { Page } from "
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
import { Page } from "__CLIENT__";
|
|
7
|
+
import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
|
|
8
|
+
import { assetBaseUrl, imageBaseUrl, imageLoader } from "__CONSTANTS__";
|
|
9
|
+
import { sitemap } from "__SITEMAP__";
|
|
10
|
+
|
|
11
|
+
const customFetch: typeof fetch = (input, init) => {
|
|
12
|
+
if (typeof input !== "string") {
|
|
13
|
+
return fetch(input, init);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (isLocalResource(input, "sitemap.xml")) {
|
|
17
|
+
// @todo: dynamic import sitemap ???
|
|
18
|
+
const response = new Response(JSON.stringify(sitemap));
|
|
19
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
20
|
+
return Promise.resolve(response);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return fetch(input, init);
|
|
24
|
+
};
|
|
13
25
|
|
|
14
26
|
export const loader = async (arg: LoaderFunctionArgs) => {
|
|
15
27
|
const url = new URL(arg.request.url);
|
|
@@ -28,7 +40,10 @@ export const loader = async (arg: LoaderFunctionArgs) => {
|
|
|
28
40
|
origin: url.origin,
|
|
29
41
|
};
|
|
30
42
|
|
|
31
|
-
const resources = await loadResources(
|
|
43
|
+
const resources = await loadResources(
|
|
44
|
+
customFetch,
|
|
45
|
+
getResources({ system }).data
|
|
46
|
+
);
|
|
32
47
|
const pageMeta = getPageMeta({ system, resources });
|
|
33
48
|
|
|
34
49
|
if (pageMeta.redirect) {
|
|
@@ -8,26 +8,26 @@
|
|
|
8
8
|
"typecheck": "tsc"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@remix-run/node": "2.
|
|
12
|
-
"@remix-run/react": "2.
|
|
13
|
-
"@remix-run/server-runtime": "2.
|
|
14
|
-
"@webstudio-is/react-sdk": "0.
|
|
15
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
16
|
-
"@webstudio-is/sdk-components-react-remix": "0.
|
|
17
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
18
|
-
"@webstudio-is/form-handlers": "0.
|
|
19
|
-
"@webstudio-is/image": "0.
|
|
20
|
-
"@webstudio-is/sdk": "0.
|
|
21
|
-
"isbot": "^5.1.
|
|
11
|
+
"@remix-run/node": "2.10.3",
|
|
12
|
+
"@remix-run/react": "2.10.3",
|
|
13
|
+
"@remix-run/server-runtime": "2.10.3",
|
|
14
|
+
"@webstudio-is/react-sdk": "0.173.0",
|
|
15
|
+
"@webstudio-is/sdk-components-react-radix": "0.173.0",
|
|
16
|
+
"@webstudio-is/sdk-components-react-remix": "0.173.0",
|
|
17
|
+
"@webstudio-is/sdk-components-react": "0.173.0",
|
|
18
|
+
"@webstudio-is/form-handlers": "0.173.0",
|
|
19
|
+
"@webstudio-is/image": "0.173.0",
|
|
20
|
+
"@webstudio-is/sdk": "0.173.0",
|
|
21
|
+
"isbot": "^5.1.13",
|
|
22
22
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
23
23
|
"react-dom": "18.3.0-canary-14898b6a9-20240318"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@remix-run/dev": "2.
|
|
26
|
+
"@remix-run/dev": "2.10.3",
|
|
27
27
|
"@types/react": "^18.2.70",
|
|
28
28
|
"@types/react-dom": "^18.2.25",
|
|
29
|
-
"typescript": "5.
|
|
30
|
-
"vite": "^5.
|
|
29
|
+
"typescript": "5.5.2",
|
|
30
|
+
"vite": "^5.3.4"
|
|
31
31
|
},
|
|
32
32
|
"engines": {
|
|
33
33
|
"node": ">=20.0.0"
|
|
@@ -2,21 +2,20 @@
|
|
|
2
2
|
"include": ["**/*.ts", "**/*.tsx", "**/*.mjs"],
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
5
|
-
"types": [
|
|
5
|
+
"types": [
|
|
6
|
+
"@remix-run/node",
|
|
7
|
+
"vite/client",
|
|
8
|
+
"@webstudio-is/react-sdk/placeholder"
|
|
9
|
+
],
|
|
6
10
|
"isolatedModules": true,
|
|
7
11
|
"esModuleInterop": true,
|
|
8
12
|
"jsx": "react-jsx",
|
|
9
13
|
"module": "ESNext",
|
|
10
14
|
"moduleResolution": "bundler",
|
|
11
|
-
"resolveJsonModule": true,
|
|
12
15
|
"target": "ES2022",
|
|
13
16
|
"strict": true,
|
|
14
17
|
"allowJs": true,
|
|
15
|
-
"checkJs": true,
|
|
16
18
|
"forceConsistentCasingInFileNames": true,
|
|
17
|
-
"allowImportingTsExtensions": true,
|
|
18
|
-
"baseUrl": ".",
|
|
19
|
-
// Remix takes care of building everything in `remix build`.
|
|
20
19
|
"noEmit": true,
|
|
21
20
|
"skipLibCheck": true
|
|
22
21
|
}
|
|
@@ -1,25 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"include": ["**/*.ts", "**/*.tsx", "**/*.mjs"],
|
|
3
2
|
"compilerOptions": {
|
|
4
|
-
"
|
|
5
|
-
"types": ["@remix-run/node", "vite/client"],
|
|
6
|
-
"isolatedModules": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"module": "ESNext",
|
|
10
|
-
"moduleResolution": "bundler",
|
|
11
|
-
"resolveJsonModule": true,
|
|
12
|
-
"target": "ES2022",
|
|
13
|
-
"strict": true,
|
|
14
|
-
"allowJs": true,
|
|
15
|
-
"checkJs": true,
|
|
16
|
-
"forceConsistentCasingInFileNames": true,
|
|
17
|
-
"allowImportingTsExtensions": true,
|
|
18
|
-
"baseUrl": ".",
|
|
19
|
-
"customConditions": ["webstudio"],
|
|
20
|
-
|
|
21
|
-
// Remix takes care of building everything in `remix build`.
|
|
22
|
-
"noEmit": true,
|
|
23
|
-
"skipLibCheck": true
|
|
3
|
+
"customConditions": ["webstudio"]
|
|
24
4
|
}
|
|
25
5
|
}
|
|
@@ -8,27 +8,11 @@
|
|
|
8
8
|
"**/.client/**/*.tsx"
|
|
9
9
|
],
|
|
10
10
|
"compilerOptions": {
|
|
11
|
-
"lib": ["DOM", "DOM.Iterable", "ES2023"],
|
|
12
11
|
"types": [
|
|
13
12
|
"@remix-run/cloudflare",
|
|
14
13
|
"vite/client",
|
|
15
|
-
"@cloudflare/workers-types/2023-07-01"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"esModuleInterop": true,
|
|
19
|
-
"jsx": "react-jsx",
|
|
20
|
-
"module": "ESNext",
|
|
21
|
-
"moduleResolution": "Bundler",
|
|
22
|
-
"resolveJsonModule": true,
|
|
23
|
-
"target": "ES2022",
|
|
24
|
-
"strict": true,
|
|
25
|
-
"allowJs": true,
|
|
26
|
-
"skipLibCheck": true,
|
|
27
|
-
"forceConsistentCasingInFileNames": true,
|
|
28
|
-
"baseUrl": ".",
|
|
29
|
-
"customConditions": ["webstudio"],
|
|
30
|
-
|
|
31
|
-
// Vite takes care of building everything, not tsc.
|
|
32
|
-
"noEmit": true
|
|
14
|
+
"@cloudflare/workers-types/2023-07-01",
|
|
15
|
+
"@webstudio-is/react-sdk/placeholder"
|
|
16
|
+
]
|
|
33
17
|
}
|
|
34
18
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* We use mjs extension as constants in this file is shared with the build script
|
|
3
|
+
* and we use `node --eval` to extract the constants.
|
|
4
|
+
*/
|
|
5
|
+
export const assetBaseUrl = "/assets/";
|
|
6
|
+
export const imageBaseUrl = "/assets/";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @type {import("@webstudio-is/image").ImageLoader}
|
|
10
|
+
*/
|
|
11
|
+
export const imageLoader = ({ src }) => {
|
|
12
|
+
return imageBaseUrl + src;
|
|
13
|
+
};
|