webstudio 0.267.0 → 0.269.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/lib/cli.js +628 -50
- package/package.json +15 -14
- package/templates/cloudflare/package.json +1 -0
- package/templates/defaults/app/redirect-url.ts +21 -0
- package/templates/defaults/app/root.tsx +9 -0
- package/templates/defaults/app/route-templates/html.tsx +56 -12
- package/templates/defaults/app/route-templates/redirect.tsx +4 -3
- package/templates/defaults/app/route-templates/text.tsx +107 -0
- package/templates/defaults/app/route-templates/xml.tsx +28 -2
- package/templates/defaults/package.json +8 -7
- package/templates/internal/package.json +2 -1
- package/templates/react-router/app/redirect-url.ts +21 -0
- package/templates/react-router/app/root.tsx +15 -1
- package/templates/react-router/app/route-templates/html.tsx +56 -12
- package/templates/react-router/app/route-templates/redirect.tsx +4 -3
- package/templates/react-router/app/route-templates/text.tsx +107 -0
- package/templates/react-router/app/route-templates/xml.tsx +28 -2
- package/templates/react-router/package.json +8 -7
- package/templates/react-router-cloudflare/package.json +1 -0
- package/templates/ssg/app/route-templates/html/+Page.tsx +34 -11
- package/templates/ssg/package.json +6 -6
- package/templates/ssg/renderer/+onRenderClient.tsx +3 -2
- package/templates/ssg/public/favicon.ico +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { type LoaderFunctionArgs, redirect } from "react-router";
|
|
2
|
+
import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
|
|
3
|
+
import { authenticateRequest } from "@webstudio-is/wsauth";
|
|
4
|
+
import { projectDomain } from "__CLIENT__";
|
|
5
|
+
import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
|
|
6
|
+
import { sitemap } from "__SITEMAP__";
|
|
7
|
+
import { assets } from "__ASSETS__";
|
|
8
|
+
import { authRoutes } from "__AUTH__";
|
|
9
|
+
|
|
10
|
+
const authenticateProductionRequest = (request: Request) => {
|
|
11
|
+
const host =
|
|
12
|
+
request.headers.get("x-forwarded-host") ||
|
|
13
|
+
request.headers.get("host") ||
|
|
14
|
+
"";
|
|
15
|
+
|
|
16
|
+
const requestHost = host.split(":")[0];
|
|
17
|
+
if (
|
|
18
|
+
projectDomain !== undefined &&
|
|
19
|
+
(requestHost === projectDomain ||
|
|
20
|
+
requestHost.startsWith(`${projectDomain}.`))
|
|
21
|
+
) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return authenticateRequest(request, authRoutes);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const customFetch: typeof fetch = (input, init) => {
|
|
29
|
+
if (typeof input !== "string") {
|
|
30
|
+
return fetch(input, init);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (isLocalResource(input, "sitemap.xml")) {
|
|
34
|
+
const response = new Response(JSON.stringify(sitemap));
|
|
35
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
36
|
+
return Promise.resolve(response);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (isLocalResource(input, "current-date")) {
|
|
40
|
+
const now = new Date();
|
|
41
|
+
const startOfDay = new Date(
|
|
42
|
+
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
|
|
43
|
+
);
|
|
44
|
+
const data = {
|
|
45
|
+
iso: startOfDay.toISOString(),
|
|
46
|
+
year: startOfDay.getUTCFullYear(),
|
|
47
|
+
month: startOfDay.getUTCMonth() + 1,
|
|
48
|
+
day: startOfDay.getUTCDate(),
|
|
49
|
+
timestamp: startOfDay.getTime(),
|
|
50
|
+
};
|
|
51
|
+
const response = new Response(JSON.stringify(data));
|
|
52
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
53
|
+
return Promise.resolve(response);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isLocalResource(input, "assets")) {
|
|
57
|
+
const response = new Response(JSON.stringify(assets));
|
|
58
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
59
|
+
return Promise.resolve(response);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return fetch(input, init);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const loader = async (arg: LoaderFunctionArgs) => {
|
|
66
|
+
const authRoute = authenticateProductionRequest(arg.request);
|
|
67
|
+
|
|
68
|
+
const url = new URL(arg.request.url);
|
|
69
|
+
const host =
|
|
70
|
+
arg.request.headers.get("x-forwarded-host") ||
|
|
71
|
+
arg.request.headers.get("host") ||
|
|
72
|
+
"";
|
|
73
|
+
url.host = host;
|
|
74
|
+
url.protocol = "https";
|
|
75
|
+
|
|
76
|
+
const params = getRemixParams(arg.params);
|
|
77
|
+
|
|
78
|
+
const system = {
|
|
79
|
+
params,
|
|
80
|
+
search: Object.fromEntries(url.searchParams),
|
|
81
|
+
origin: url.origin,
|
|
82
|
+
pathname: url.pathname,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const resources = await loadResources(
|
|
86
|
+
customFetch,
|
|
87
|
+
getResources({ system }).data
|
|
88
|
+
);
|
|
89
|
+
const pageMeta = getPageMeta({ system, resources });
|
|
90
|
+
|
|
91
|
+
if (pageMeta.redirect) {
|
|
92
|
+
const status =
|
|
93
|
+
pageMeta.status === 301 || pageMeta.status === 302
|
|
94
|
+
? pageMeta.status
|
|
95
|
+
: 302;
|
|
96
|
+
return redirect(pageMeta.redirect, status);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return new Response(pageMeta.content ?? "", {
|
|
100
|
+
status: pageMeta.status,
|
|
101
|
+
headers: {
|
|
102
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
103
|
+
"Cache-Control":
|
|
104
|
+
authRoute === undefined ? "public, max-age=600" : "private, no-store",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
};
|
|
@@ -1,15 +1,35 @@
|
|
|
1
1
|
import { renderToString } from "react-dom/server";
|
|
2
2
|
import { type LoaderFunctionArgs, redirect } from "react-router";
|
|
3
3
|
import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
|
|
4
|
+
import { authenticateRequest } from "@webstudio-is/wsauth";
|
|
4
5
|
import {
|
|
5
6
|
ReactSdkContext,
|
|
6
7
|
xmlNodeTagSuffix,
|
|
7
8
|
} from "@webstudio-is/react-sdk/runtime";
|
|
8
|
-
import { Page, breakpoints } from "__CLIENT__";
|
|
9
|
+
import { Page, breakpoints, projectDomain } from "__CLIENT__";
|
|
9
10
|
import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
|
|
10
11
|
import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
|
|
11
12
|
import { sitemap } from "__SITEMAP__";
|
|
12
13
|
import { assets } from "__ASSETS__";
|
|
14
|
+
import { authRoutes } from "__AUTH__";
|
|
15
|
+
|
|
16
|
+
const authenticateProductionRequest = (request: Request) => {
|
|
17
|
+
const host =
|
|
18
|
+
request.headers.get("x-forwarded-host") ||
|
|
19
|
+
request.headers.get("host") ||
|
|
20
|
+
"";
|
|
21
|
+
|
|
22
|
+
const requestHost = host.split(":")[0];
|
|
23
|
+
if (
|
|
24
|
+
projectDomain !== undefined &&
|
|
25
|
+
(requestHost === projectDomain ||
|
|
26
|
+
requestHost.startsWith(`${projectDomain}.`))
|
|
27
|
+
) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return authenticateRequest(request, authRoutes);
|
|
32
|
+
};
|
|
13
33
|
|
|
14
34
|
const customFetch: typeof fetch = (input, init) => {
|
|
15
35
|
if (typeof input !== "string") {
|
|
@@ -51,6 +71,8 @@ const customFetch: typeof fetch = (input, init) => {
|
|
|
51
71
|
};
|
|
52
72
|
|
|
53
73
|
export const loader = async (arg: LoaderFunctionArgs) => {
|
|
74
|
+
const authRoute = authenticateProductionRequest(arg.request);
|
|
75
|
+
|
|
54
76
|
const url = new URL(arg.request.url);
|
|
55
77
|
const host =
|
|
56
78
|
arg.request.headers.get("x-forwarded-host") ||
|
|
@@ -108,6 +130,10 @@ export const loader = async (arg: LoaderFunctionArgs) => {
|
|
|
108
130
|
text = text.replaceAll(xmlNodeTagSuffix, "");
|
|
109
131
|
|
|
110
132
|
return new Response(`<?xml version="1.0" encoding="UTF-8"?>\n${text}`, {
|
|
111
|
-
headers: {
|
|
133
|
+
headers: {
|
|
134
|
+
"Content-Type": "application/xml",
|
|
135
|
+
"Cache-Control":
|
|
136
|
+
authRoute === undefined ? "public, max-age=600" : "private, no-store",
|
|
137
|
+
},
|
|
112
138
|
});
|
|
113
139
|
};
|
|
@@ -10,13 +10,14 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@react-router/dev": "^7.5.3",
|
|
12
12
|
"@react-router/fs-routes": "^7.5.3",
|
|
13
|
-
"@webstudio-is/image": "0.
|
|
14
|
-
"@webstudio-is/react-sdk": "0.
|
|
15
|
-
"@webstudio-is/sdk": "0.
|
|
16
|
-
"@webstudio-is/sdk-components-animation": "0.
|
|
17
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
18
|
-
"@webstudio-is/sdk-components-react-router": "0.
|
|
19
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
13
|
+
"@webstudio-is/image": "0.269.0",
|
|
14
|
+
"@webstudio-is/react-sdk": "0.269.0",
|
|
15
|
+
"@webstudio-is/sdk": "0.269.0",
|
|
16
|
+
"@webstudio-is/sdk-components-animation": "0.269.0",
|
|
17
|
+
"@webstudio-is/sdk-components-react-radix": "0.269.0",
|
|
18
|
+
"@webstudio-is/sdk-components-react-router": "0.269.0",
|
|
19
|
+
"@webstudio-is/sdk-components-react": "0.269.0",
|
|
20
|
+
"@webstudio-is/wsauth": "0.269.0",
|
|
20
21
|
"isbot": "^5.1.25",
|
|
21
22
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
22
23
|
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
|
@@ -1,26 +1,49 @@
|
|
|
1
|
+
import { type ComponentProps, memo, useMemo } from "react";
|
|
1
2
|
import type { PageContext } from "vike/types";
|
|
2
3
|
import {
|
|
3
4
|
PageSettingsMeta,
|
|
4
5
|
PageSettingsTitle,
|
|
5
6
|
ReactSdkContext,
|
|
6
7
|
} from "@webstudio-is/react-sdk/runtime";
|
|
8
|
+
import { LinkCurrentUrlContext } from "@webstudio-is/sdk-components-react";
|
|
7
9
|
import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
|
|
8
10
|
import { Page, breakpoints, siteName } from "__CLIENT__";
|
|
9
11
|
|
|
12
|
+
const getPageKey = (url: string) => {
|
|
13
|
+
const { origin, pathname, search } = new URL(url);
|
|
14
|
+
return `${origin}${pathname}${search}`;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const PageBoundary = memo(
|
|
18
|
+
({ pageKey, system }: ComponentProps<typeof Page> & { pageKey: string }) => {
|
|
19
|
+
// Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages
|
|
20
|
+
return <Page key={pageKey} system={system} />;
|
|
21
|
+
},
|
|
22
|
+
// Vike can rerender the current page during client-side navigation and
|
|
23
|
+
// hash-only URL updates. Keep the generated page out of that render path,
|
|
24
|
+
// but let actual page URL changes remount it.
|
|
25
|
+
(prevProps, nextProps) => prevProps.pageKey === nextProps.pageKey
|
|
26
|
+
);
|
|
27
|
+
|
|
10
28
|
const PageComponent = ({ data }: { data: PageContext["data"] }) => {
|
|
11
29
|
const { system, resources, url, pageMeta } = data;
|
|
30
|
+
const pageKey = getPageKey(url);
|
|
31
|
+
const sdkContext = useMemo(
|
|
32
|
+
() => ({
|
|
33
|
+
imageLoader,
|
|
34
|
+
assetBaseUrl,
|
|
35
|
+
resources,
|
|
36
|
+
breakpoints,
|
|
37
|
+
onError: console.error,
|
|
38
|
+
}),
|
|
39
|
+
[resources]
|
|
40
|
+
);
|
|
41
|
+
|
|
12
42
|
return (
|
|
13
|
-
<ReactSdkContext.Provider
|
|
14
|
-
value={
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
resources,
|
|
18
|
-
breakpoints,
|
|
19
|
-
onError: console.error,
|
|
20
|
-
}}
|
|
21
|
-
>
|
|
22
|
-
{/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */}
|
|
23
|
-
<Page key={url} system={system} />
|
|
43
|
+
<ReactSdkContext.Provider value={sdkContext}>
|
|
44
|
+
<LinkCurrentUrlContext.Provider value={url}>
|
|
45
|
+
<PageBoundary pageKey={pageKey} system={system} />
|
|
46
|
+
</LinkCurrentUrlContext.Provider>
|
|
24
47
|
<PageSettingsMeta
|
|
25
48
|
url={url}
|
|
26
49
|
pageMeta={pageMeta}
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"typecheck": "tsgo --noEmit"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@webstudio-is/image": "0.
|
|
12
|
-
"@webstudio-is/react-sdk": "0.
|
|
13
|
-
"@webstudio-is/sdk": "0.
|
|
14
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
15
|
-
"@webstudio-is/sdk-components-animation": "0.
|
|
16
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
11
|
+
"@webstudio-is/image": "0.269.0",
|
|
12
|
+
"@webstudio-is/react-sdk": "0.269.0",
|
|
13
|
+
"@webstudio-is/sdk": "0.269.0",
|
|
14
|
+
"@webstudio-is/sdk-components-react": "0.269.0",
|
|
15
|
+
"@webstudio-is/sdk-components-animation": "0.269.0",
|
|
16
|
+
"@webstudio-is/sdk-components-react-radix": "0.269.0",
|
|
17
17
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
18
18
|
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
|
19
19
|
"vike": "^0.4.229"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Root,
|
|
1
|
+
import { type Root, hydrateRoot } from "react-dom/client";
|
|
2
2
|
import type { OnRenderClientSync } from "vike/types";
|
|
3
3
|
|
|
4
4
|
let root: Root;
|
|
@@ -19,7 +19,8 @@ export const onRenderClient: OnRenderClientSync = (pageContext) => {
|
|
|
19
19
|
</>
|
|
20
20
|
);
|
|
21
21
|
if (root === undefined) {
|
|
22
|
-
root =
|
|
22
|
+
root = hydrateRoot(document.documentElement, htmlContent);
|
|
23
|
+
return;
|
|
23
24
|
}
|
|
24
25
|
document.documentElement.lang = lang;
|
|
25
26
|
root.render(htmlContent);
|
|
Binary file
|