diff --git a/fixtures/react-router-docker/package.json b/fixtures/react-router-docker/package.json index 457abc5bcb64..e63f7ced0bd9 100644 --- a/fixtures/react-router-docker/package.json +++ b/fixtures/react-router-docker/package.json @@ -21,7 +21,7 @@ "@webstudio-is/sdk-components-react": "workspace:*", "@webstudio-is/sdk-components-react-radix": "workspace:*", "@webstudio-is/sdk-components-react-router": "workspace:*", - "h3": "^1.14.0", + "h3": "^1.15.0", "ipx": "^3.0.1", "isbot": "^5.1.22", "react": "18.3.0-canary-14898b6a9-20240318", diff --git a/fixtures/react-router-vercel/.gitignore b/fixtures/react-router-vercel/.gitignore new file mode 100644 index 000000000000..9b7c041f96ea --- /dev/null +++ b/fixtures/react-router-vercel/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +/node_modules/ + +# React Router +/.react-router/ +/build/ diff --git a/fixtures/react-router-vercel/.npmrc b/fixtures/react-router-vercel/.npmrc new file mode 100644 index 000000000000..43ce02f127ad --- /dev/null +++ b/fixtures/react-router-vercel/.npmrc @@ -0,0 +1,3 @@ +force=true +# to support using NODE_OPTIONS for windows tests +shell-emulator=true diff --git a/fixtures/react-router-vercel/.template/.npmrc b/fixtures/react-router-vercel/.template/.npmrc new file mode 100644 index 000000000000..43ce02f127ad --- /dev/null +++ b/fixtures/react-router-vercel/.template/.npmrc @@ -0,0 +1,3 @@ +force=true +# to support using NODE_OPTIONS for windows tests +shell-emulator=true diff --git a/fixtures/react-router-vercel/.template/package.json b/fixtures/react-router-vercel/.template/package.json new file mode 100644 index 000000000000..b21dce86c93a --- /dev/null +++ b/fixtures/react-router-vercel/.template/package.json @@ -0,0 +1,12 @@ +{ + "dependencies": { + "@webstudio-is/image": "workspace:*", + "@webstudio-is/react-sdk": "workspace:*", + "@webstudio-is/sdk": "workspace:*", + "@webstudio-is/sdk-components-animation": "workspace:*", + "@webstudio-is/sdk-components-react": "workspace:*", + "@webstudio-is/sdk-components-react-radix": "workspace:*", + "@webstudio-is/sdk-components-react-router": "workspace:*", + "webstudio": "workspace:*" + } +} diff --git a/fixtures/react-router-vercel/.template/tsconfig.json b/fixtures/react-router-vercel/.template/tsconfig.json new file mode 100644 index 000000000000..75cac78946fd --- /dev/null +++ b/fixtures/react-router-vercel/.template/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "customConditions": ["webstudio"] + } +} diff --git a/fixtures/react-router-vercel/.webstudio/config.json b/fixtures/react-router-vercel/.webstudio/config.json new file mode 100644 index 000000000000..3bad791718c2 --- /dev/null +++ b/fixtures/react-router-vercel/.webstudio/config.json @@ -0,0 +1,3 @@ +{ + "projectId": "d845c167-ea07-4875-b08d-83e97c09dcce" +} diff --git a/fixtures/react-router-vercel/.webstudio/data.json b/fixtures/react-router-vercel/.webstudio/data.json new file mode 100644 index 000000000000..7dc933cfb601 --- /dev/null +++ b/fixtures/react-router-vercel/.webstudio/data.json @@ -0,0 +1,500 @@ +{ + "build": { + "id": "f565d527-32e7-4731-bc71-aca9e9574587", + "projectId": "d845c167-ea07-4875-b08d-83e97c09dcce", + "version": 43, + "createdAt": "2025-01-04T11:01:50.091+00:00", + "updatedAt": "2025-01-04T11:01:50.091+00:00", + "pages": { + "meta": { + "siteName": "", + "faviconAssetId": "d0974db9300c1a3b0fb8b291dd9fabd45ad136478908394280af2f7087e3aecd", + "code": "" + }, + "homePage": { + "id": "9di_L14CzctvSruIoKVvE", + "name": "Home", + "title": "\"Home\"", + "rootInstanceId": "MMimeobf_zi4ZkRGXapju", + "systemDataSourceId": "2KT4-bRzToj9cAGAN_woK", + "meta": {}, + "path": "" + }, + "pages": [ + { + "id": "WPPAbLFyJD_02vhjRd8P4", + "name": "Another page", + "title": "\"Another page\"", + "history": ["/another-page"], + "rootInstanceId": "n_VBMr7klpx25buS0NV7R", + "systemDataSourceId": "tdXe9gFf83hSo9BLWU6xl", + "meta": { + "description": "\"\"", + "excludePageFromSearch": "true", + "language": "\"\"", + "socialImageUrl": "\"\"", + "status": "200", + "redirect": "\"\"", + "documentType": "html", + "custom": [ + { + "property": "", + "content": "\"\"" + } + ] + }, + "marketplace": { + "include": false + }, + "path": "/another-page" + } + ], + "folders": [ + { + "id": "root", + "name": "Root", + "slug": "", + "children": ["9di_L14CzctvSruIoKVvE", "WPPAbLFyJD_02vhjRd8P4"] + } + ] + }, + "breakpoints": [ + [ + "rKj-wYctg3-GnqL3WHN9I", + { + "id": "rKj-wYctg3-GnqL3WHN9I", + "label": "Base" + } + ], + [ + "yH9RXhqCyeaVkrOt8MzLc", + { + "id": "yH9RXhqCyeaVkrOt8MzLc", + "label": "Tablet", + "maxWidth": 991 + } + ], + [ + "8nSCZbeS002IVwkTdoIes", + { + "id": "8nSCZbeS002IVwkTdoIes", + "label": "Mobile landscape", + "maxWidth": 767 + } + ], + [ + "7gBD25KrrbBdJYNDlhPz7", + { + "id": "7gBD25KrrbBdJYNDlhPz7", + "label": "Mobile portrait", + "maxWidth": 479 + } + ] + ], + "styles": [ + [ + "7_QL45cpvP-zG8Hkgf4cr:rKj-wYctg3-GnqL3WHN9I:display:", + { + "breakpointId": "rKj-wYctg3-GnqL3WHN9I", + "styleSourceId": "7_QL45cpvP-zG8Hkgf4cr", + "property": "display", + "value": { + "type": "keyword", + "value": "flex" + } + } + ], + [ + "7_QL45cpvP-zG8Hkgf4cr:rKj-wYctg3-GnqL3WHN9I:alignItems:", + { + "breakpointId": "rKj-wYctg3-GnqL3WHN9I", + "styleSourceId": "7_QL45cpvP-zG8Hkgf4cr", + "property": "alignItems", + "value": { + "type": "keyword", + "value": "center" + } + } + ], + [ + "7_QL45cpvP-zG8Hkgf4cr:rKj-wYctg3-GnqL3WHN9I:justifyContent:", + { + "breakpointId": "rKj-wYctg3-GnqL3WHN9I", + "styleSourceId": "7_QL45cpvP-zG8Hkgf4cr", + "property": "justifyContent", + "value": { + "type": "keyword", + "value": "center" + } + } + ], + [ + "7_QL45cpvP-zG8Hkgf4cr:rKj-wYctg3-GnqL3WHN9I:flexDirection:", + { + "breakpointId": "rKj-wYctg3-GnqL3WHN9I", + "styleSourceId": "7_QL45cpvP-zG8Hkgf4cr", + "property": "flexDirection", + "value": { + "type": "keyword", + "value": "column" + } + } + ], + [ + "0KA68BwP9gdTzE1ESO2Zp:rKj-wYctg3-GnqL3WHN9I:marginBottom:", + { + "breakpointId": "rKj-wYctg3-GnqL3WHN9I", + "styleSourceId": "0KA68BwP9gdTzE1ESO2Zp", + "property": "marginBottom", + "value": { + "type": "unit", + "unit": "em", + "value": 1 + } + } + ], + [ + "mf2C07UBmGT7y_G4Du3yg:rKj-wYctg3-GnqL3WHN9I:width:", + { + "breakpointId": "rKj-wYctg3-GnqL3WHN9I", + "styleSourceId": "mf2C07UBmGT7y_G4Du3yg", + "property": "width", + "value": { + "type": "unit", + "unit": "px", + "value": 400 + } + } + ] + ], + "styleSources": [ + [ + "7_QL45cpvP-zG8Hkgf4cr", + { + "type": "local", + "id": "7_QL45cpvP-zG8Hkgf4cr" + } + ], + [ + "0KA68BwP9gdTzE1ESO2Zp", + { + "type": "local", + "id": "0KA68BwP9gdTzE1ESO2Zp" + } + ], + [ + "mf2C07UBmGT7y_G4Du3yg", + { + "type": "local", + "id": "mf2C07UBmGT7y_G4Du3yg" + } + ] + ], + "styleSourceSelections": [ + [ + "MMimeobf_zi4ZkRGXapju", + { + "instanceId": "MMimeobf_zi4ZkRGXapju", + "values": ["7_QL45cpvP-zG8Hkgf4cr"] + } + ], + [ + "BMJfjOzunWs8XkQgvvx1e", + { + "instanceId": "BMJfjOzunWs8XkQgvvx1e", + "values": ["0KA68BwP9gdTzE1ESO2Zp"] + } + ], + [ + "uHB3Fjb7-NELG-bnH7bXB", + { + "instanceId": "uHB3Fjb7-NELG-bnH7bXB", + "values": ["mf2C07UBmGT7y_G4Du3yg"] + } + ] + ], + "props": [ + [ + "1p34InvRgqoKVqeNZ1uBb", + { + "id": "1p34InvRgqoKVqeNZ1uBb", + "instanceId": "pjkZo5EiBqaeUXBcyHf_O", + "name": "href", + "type": "page", + "value": "WPPAbLFyJD_02vhjRd8P4" + } + ], + [ + "su3ag3OxH9WTBjJg5eIyg", + { + "id": "su3ag3OxH9WTBjJg5eIyg", + "instanceId": "uHB3Fjb7-NELG-bnH7bXB", + "name": "src", + "type": "asset", + "value": "1d8bf4398f643f5333d415091507d778aaed62f28883642636cbed0be156a0ee" + } + ], + [ + "vGCYpBBB1QUPIPPIdyexn", + { + "id": "vGCYpBBB1QUPIPPIdyexn", + "instanceId": "uHB3Fjb7-NELG-bnH7bXB", + "name": "width", + "type": "asset", + "value": "1d8bf4398f643f5333d415091507d778aaed62f28883642636cbed0be156a0ee" + } + ], + [ + "JKAGY7DWpciEl0UdnWuKL", + { + "id": "JKAGY7DWpciEl0UdnWuKL", + "instanceId": "uHB3Fjb7-NELG-bnH7bXB", + "name": "height", + "type": "asset", + "value": "1d8bf4398f643f5333d415091507d778aaed62f28883642636cbed0be156a0ee" + } + ], + [ + "CAkmmL8-JAgokmeopoFXh", + { + "id": "CAkmmL8-JAgokmeopoFXh", + "instanceId": "2sIE8GxbKRBaav_zdhaZ1", + "name": "src", + "type": "string", + "value": "https://picsum.photos/id/237/100/100.jpg?blur=4&grayscale" + } + ] + ], + "dataSources": [ + [ + "2KT4-bRzToj9cAGAN_woK", + { + "type": "parameter", + "id": "2KT4-bRzToj9cAGAN_woK", + "scopeInstanceId": "MMimeobf_zi4ZkRGXapju", + "name": "system" + } + ], + [ + "tdXe9gFf83hSo9BLWU6xl", + { + "type": "parameter", + "id": "tdXe9gFf83hSo9BLWU6xl", + "scopeInstanceId": "n_VBMr7klpx25buS0NV7R", + "name": "system" + } + ] + ], + "resources": [], + "instances": [ + [ + "MMimeobf_zi4ZkRGXapju", + { + "type": "instance", + "id": "MMimeobf_zi4ZkRGXapju", + "component": "Body", + "children": [ + { + "type": "id", + "value": "MYDt0guk1-vzc7yzqyN6A" + }, + { + "type": "id", + "value": "BMJfjOzunWs8XkQgvvx1e" + }, + { + "type": "id", + "value": "pjkZo5EiBqaeUXBcyHf_O" + }, + { + "type": "id", + "value": "uHB3Fjb7-NELG-bnH7bXB" + }, + { + "type": "id", + "value": "2sIE8GxbKRBaav_zdhaZ1" + } + ] + } + ], + [ + "MYDt0guk1-vzc7yzqyN6A", + { + "type": "instance", + "id": "MYDt0guk1-vzc7yzqyN6A", + "component": "Heading", + "label": "xD", + "children": [ + { + "type": "text", + "value": "Simple Project to test CLI" + } + ] + } + ], + [ + "BMJfjOzunWs8XkQgvvx1e", + { + "type": "instance", + "id": "BMJfjOzunWs8XkQgvvx1e", + "component": "Text", + "children": [ + { + "type": "text", + "value": "Please don't change directly in the fixture" + } + ] + } + ], + [ + "pjkZo5EiBqaeUXBcyHf_O", + { + "type": "instance", + "id": "pjkZo5EiBqaeUXBcyHf_O", + "component": "Link", + "children": [ + { + "type": "text", + "value": "Test another page link" + } + ] + } + ], + [ + "n_VBMr7klpx25buS0NV7R", + { + "type": "instance", + "id": "n_VBMr7klpx25buS0NV7R", + "component": "Body", + "children": [ + { + "type": "id", + "value": "wthNByqb3RPmheb-56VYI" + } + ] + } + ], + [ + "wthNByqb3RPmheb-56VYI", + { + "type": "instance", + "id": "wthNByqb3RPmheb-56VYI", + "component": "Heading", + "children": [ + { + "type": "text", + "value": "Another page" + } + ] + } + ], + [ + "uHB3Fjb7-NELG-bnH7bXB", + { + "type": "instance", + "id": "uHB3Fjb7-NELG-bnH7bXB", + "component": "Image", + "children": [] + } + ], + [ + "2sIE8GxbKRBaav_zdhaZ1", + { + "type": "instance", + "id": "2sIE8GxbKRBaav_zdhaZ1", + "component": "Image", + "children": [] + } + ] + ], + "deployment": { + "destination": "saas", + "domains": ["cli-basic-test-d0osr"], + "assetsDomain": "cli-basic-test-d0osr", + "excludeWstdDomainFromSearch": false + } + }, + "page": { + "id": "9di_L14CzctvSruIoKVvE", + "name": "Home", + "title": "\"Home\"", + "rootInstanceId": "MMimeobf_zi4ZkRGXapju", + "systemDataSourceId": "2KT4-bRzToj9cAGAN_woK", + "meta": {}, + "path": "" + }, + "pages": [ + { + "id": "9di_L14CzctvSruIoKVvE", + "name": "Home", + "title": "\"Home\"", + "rootInstanceId": "MMimeobf_zi4ZkRGXapju", + "systemDataSourceId": "2KT4-bRzToj9cAGAN_woK", + "meta": {}, + "path": "" + }, + { + "id": "WPPAbLFyJD_02vhjRd8P4", + "name": "Another page", + "title": "\"Another page\"", + "history": ["/another-page"], + "rootInstanceId": "n_VBMr7klpx25buS0NV7R", + "systemDataSourceId": "tdXe9gFf83hSo9BLWU6xl", + "meta": { + "description": "\"\"", + "excludePageFromSearch": "true", + "language": "\"\"", + "socialImageUrl": "\"\"", + "status": "200", + "redirect": "\"\"", + "documentType": "html", + "custom": [ + { + "property": "", + "content": "\"\"" + } + ] + }, + "marketplace": { + "include": false + }, + "path": "/another-page" + } + ], + "assets": [ + { + "id": "1d8bf4398f643f5333d415091507d778aaed62f28883642636cbed0be156a0ee", + "name": "iconly_svg_converted-converted_zMaMiAAutUl8XrITgz7d1.svg", + "description": null, + "projectId": "d845c167-ea07-4875-b08d-83e97c09dcce", + "size": 999, + "type": "image", + "format": "svg", + "createdAt": "2024-07-26T13:39:48.678+00:00", + "meta": { + "width": 14, + "height": 16 + } + }, + { + "id": "d0974db9300c1a3b0fb8b291dd9fabd45ad136478908394280af2f7087e3aecd", + "name": "147-1478573_cat-icon-png-black-cat-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg", + "description": null, + "projectId": "d845c167-ea07-4875-b08d-83e97c09dcce", + "size": 64701, + "type": "image", + "format": "jpg", + "createdAt": "2024-12-06T14:36:07.046+00:00", + "meta": { + "width": 820, + "height": 985 + } + } + ], + "user": { + "email": "hello@webstudio.is" + }, + "projectDomain": "cli-basic-test-d0osr", + "projectTitle": "cli-basic-test", + "origin": "https://main.development.webstudio.is" +} diff --git a/fixtures/react-router-vercel/app/__generated__/$resources.sitemap.xml.ts b/fixtures/react-router-vercel/app/__generated__/$resources.sitemap.xml.ts new file mode 100644 index 000000000000..181eac4621f3 --- /dev/null +++ b/fixtures/react-router-vercel/app/__generated__/$resources.sitemap.xml.ts @@ -0,0 +1,6 @@ +export const sitemap = [ + { + path: "/", + lastModified: "2025-01-04", + }, +]; diff --git a/fixtures/react-router-vercel/app/__generated__/[another-page]._index.server.tsx b/fixtures/react-router-vercel/app/__generated__/[another-page]._index.server.tsx new file mode 100644 index 000000000000..dc6510942c47 --- /dev/null +++ b/fixtures/react-router-vercel/app/__generated__/[another-page]._index.server.tsx @@ -0,0 +1,39 @@ +/* eslint-disable */ +/* This is a auto generated file for building the project */ + +import type { PageMeta } from "@webstudio-is/sdk"; +import type { System, ResourceRequest } from "@webstudio-is/sdk"; +export const getResources = (_props: { system: System }) => { + const _data = new Map([]); + const _action = new Map([]); + return { data: _data, action: _action }; +}; + +export const getPageMeta = ({ + system, + resources, +}: { + system: System; + resources: Record; +}): PageMeta => { + return { + title: "Another page", + description: "", + excludePageFromSearch: true, + language: "", + socialImageAssetName: undefined, + socialImageUrl: "", + status: 200, + redirect: "", + custom: [], + }; +}; + +type Params = Record; +export const getRemixParams = ({ ...params }: Params): Params => { + return params; +}; + +export const projectId = "d845c167-ea07-4875-b08d-83e97c09dcce"; + +export const contactEmail = "hello@webstudio.is"; diff --git a/fixtures/react-router-vercel/app/__generated__/[another-page]._index.tsx b/fixtures/react-router-vercel/app/__generated__/[another-page]._index.tsx new file mode 100644 index 000000000000..0f11ceedef83 --- /dev/null +++ b/fixtures/react-router-vercel/app/__generated__/[another-page]._index.tsx @@ -0,0 +1,37 @@ +/* eslint-disable */ +/* This is a auto generated file for building the project */ + +import { Fragment, useState } from "react"; +import type { FontAsset, ImageAsset } from "@webstudio-is/sdk"; +import { useResource, useVariableState } from "@webstudio-is/react-sdk/runtime"; +import { Body as Body } from "@webstudio-is/sdk-components-react-router"; +import { Heading as Heading } from "@webstudio-is/sdk-components-react"; + +export const siteName = ""; + +export const favIconAsset: ImageAsset | undefined = { + id: "d0974db9300c1a3b0fb8b291dd9fabd45ad136478908394280af2f7087e3aecd", + name: "147-1478573_cat-icon-png-black-cat-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg", + description: null, + projectId: "d845c167-ea07-4875-b08d-83e97c09dcce", + size: 64701, + type: "image", + format: "jpg", + createdAt: "2024-12-06T14:36:07.046+00:00", + meta: { width: 820, height: 985 }, +}; + +// Font assets on current page (can be preloaded) +export const pageFontAssets: FontAsset[] = []; + +export const pageBackgroundImageAssets: ImageAsset[] = []; + +const Page = ({}: { system: any }) => { + return ( + + {"Another page"} + + ); +}; + +export { Page }; diff --git a/fixtures/react-router-vercel/app/__generated__/_index.server.tsx b/fixtures/react-router-vercel/app/__generated__/_index.server.tsx new file mode 100644 index 000000000000..83d760a977b9 --- /dev/null +++ b/fixtures/react-router-vercel/app/__generated__/_index.server.tsx @@ -0,0 +1,39 @@ +/* eslint-disable */ +/* This is a auto generated file for building the project */ + +import type { PageMeta } from "@webstudio-is/sdk"; +import type { System, ResourceRequest } from "@webstudio-is/sdk"; +export const getResources = (_props: { system: System }) => { + const _data = new Map([]); + const _action = new Map([]); + return { data: _data, action: _action }; +}; + +export const getPageMeta = ({ + system, + resources, +}: { + system: System; + resources: Record; +}): PageMeta => { + return { + title: "Home", + description: undefined, + excludePageFromSearch: undefined, + language: undefined, + socialImageAssetName: undefined, + socialImageUrl: undefined, + status: undefined, + redirect: undefined, + custom: [], + }; +}; + +type Params = Record; +export const getRemixParams = ({ ...params }: Params): Params => { + return params; +}; + +export const projectId = "d845c167-ea07-4875-b08d-83e97c09dcce"; + +export const contactEmail = "hello@webstudio.is"; diff --git a/fixtures/react-router-vercel/app/__generated__/_index.tsx b/fixtures/react-router-vercel/app/__generated__/_index.tsx new file mode 100644 index 000000000000..9c073984cf91 --- /dev/null +++ b/fixtures/react-router-vercel/app/__generated__/_index.tsx @@ -0,0 +1,64 @@ +/* eslint-disable */ +/* This is a auto generated file for building the project */ + +import { Fragment, useState } from "react"; +import type { FontAsset, ImageAsset } from "@webstudio-is/sdk"; +import { useResource, useVariableState } from "@webstudio-is/react-sdk/runtime"; +import { + Body as Body, + Link as Link, +} from "@webstudio-is/sdk-components-react-router"; +import { + Heading as Heading, + Text as Text, + Image as Image, +} from "@webstudio-is/sdk-components-react"; + +export const siteName = ""; + +export const favIconAsset: ImageAsset | undefined = { + id: "d0974db9300c1a3b0fb8b291dd9fabd45ad136478908394280af2f7087e3aecd", + name: "147-1478573_cat-icon-png-black-cat-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg", + description: null, + projectId: "d845c167-ea07-4875-b08d-83e97c09dcce", + size: 64701, + type: "image", + format: "jpg", + createdAt: "2024-12-06T14:36:07.046+00:00", + meta: { width: 820, height: 985 }, +}; + +// Font assets on current page (can be preloaded) +export const pageFontAssets: FontAsset[] = []; + +export const pageBackgroundImageAssets: ImageAsset[] = []; + +export const CustomCode = () => { + return <>; +}; + +const Page = ({}: { system: any }) => { + return ( + + {"Simple Project to test CLI"} + + {"Please don't change directly in the fixture"} + + + {"Test another page link"} + + + + + ); +}; + +export { Page }; diff --git a/fixtures/react-router-vercel/app/__generated__/index.css b/fixtures/react-router-vercel/app/__generated__/index.css new file mode 100644 index 000000000000..dd85fdf13b99 --- /dev/null +++ b/fixtures/react-router-vercel/app/__generated__/index.css @@ -0,0 +1,118 @@ +@media all { + :root { + display: grid; + min-height: 100%; + font-family: Arial, Roboto, sans-serif; + font-size: 16px; + line-height: 1.2; + white-space: pre-wrap; + white-space-collapse: preserve; + } + :where(body.w-body) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + margin: 0; + } + :where(h1.w-heading) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + } + :where(h2.w-heading) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + } + :where(h3.w-heading) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + } + :where(h4.w-heading) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + } + :where(h5.w-heading) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + } + :where(h6.w-heading) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + } + :where(div.w-text) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + min-height: 1em; + } + :where(a.w-link) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + display: inline-block; + } + :where(img.w-image) { + box-sizing: border-box; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + outline-width: 1px; + max-width: 100%; + display: block; + height: auto; + } +} +@media all { + .c1jaw2zx { + display: flex; + } + .cbipm55 { + align-items: center; + } + .ctniqj4 { + justify-content: center; + } + .ctgx88l { + flex-direction: column; + } + .cn3rfux { + margin-bottom: 1em; + } + .c161qeci { + width: 400px; + } +} diff --git a/fixtures/react-router-vercel/app/constants.mjs b/fixtures/react-router-vercel/app/constants.mjs new file mode 100644 index 000000000000..5ebdad372c5a --- /dev/null +++ b/fixtures/react-router-vercel/app/constants.mjs @@ -0,0 +1,25 @@ +/** + * We use mjs extension as constants in this file is shared with the build script + * and we use `node --eval` to extract the constants. + */ +export const assetBaseUrl = "/assets/"; + +/** + * @type {import("@webstudio-is/image").ImageLoader} + */ +export const imageLoader = (props) => { + if (import.meta.env.DEV) { + return props.src; + } + + if (props.format === "raw") { + return props.src; + } + + // https://vercel.com/blog/build-your-own-web-framework#automatic-image-optimization + const searchParams = new URLSearchParams(); + searchParams.set("url", props.src); + searchParams.set("w", props.width.toString()); + searchParams.set("q", props.quality.toString()); + return `/_vercel/image?${searchParams}`; +}; diff --git a/fixtures/react-router-vercel/app/extension.ts b/fixtures/react-router-vercel/app/extension.ts new file mode 100644 index 000000000000..bffd05d48e17 --- /dev/null +++ b/fixtures/react-router-vercel/app/extension.ts @@ -0,0 +1,13 @@ +import { ResourceRequest } from "@webstudio-is/sdk"; + +declare module "react-router" { + interface AppLoadContext { + EXCLUDE_FROM_SEARCH: boolean; + getDefaultActionResource?: (options: { + url: URL; + projectId: string; + contactEmail: string; + formData: FormData; + }) => ResourceRequest; + } +} diff --git a/fixtures/react-router-vercel/app/root.tsx b/fixtures/react-router-vercel/app/root.tsx new file mode 100644 index 000000000000..aa2a8c416496 --- /dev/null +++ b/fixtures/react-router-vercel/app/root.tsx @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ + +import { Links, Meta, Outlet, useMatches } from "react-router"; +// @todo think about how to make __generated__ typeable +// @ts-ignore +import { CustomCode } from "./__generated__/_index"; + +const Root = () => { + // Get language from matches + const matches = useMatches(); + + const lastMatchWithLanguage = matches.findLast((match) => { + // @ts-ignore + const language = match?.data?.pageMeta?.language; + return language != null; + }); + + // @ts-ignore + const lang = lastMatchWithLanguage?.data?.pageMeta?.language ?? "en"; + + return ( + + + + + + + + + + + ); +}; + +export default Root; diff --git a/fixtures/react-router-vercel/app/routes.ts b/fixtures/react-router-vercel/app/routes.ts new file mode 100644 index 000000000000..4c05936cb638 --- /dev/null +++ b/fixtures/react-router-vercel/app/routes.ts @@ -0,0 +1,4 @@ +import { type RouteConfig } from "@react-router/dev/routes"; +import { flatRoutes } from "@react-router/fs-routes"; + +export default flatRoutes() satisfies RouteConfig; diff --git a/fixtures/react-router-vercel/app/routes/[another-page]._index.tsx b/fixtures/react-router-vercel/app/routes/[another-page]._index.tsx new file mode 100644 index 000000000000..e9d062f92306 --- /dev/null +++ b/fixtures/react-router-vercel/app/routes/[another-page]._index.tsx @@ -0,0 +1,295 @@ +import { + type MetaFunction, + type LinksFunction, + type LinkDescriptor, + type ActionFunctionArgs, + type LoaderFunctionArgs, + type HeadersFunction, + data, + redirect, + useLoaderData, +} from "react-router"; +import { + isLocalResource, + loadResource, + loadResources, + formIdFieldName, + formBotFieldName, +} from "@webstudio-is/sdk/runtime"; +import { + ReactSdkContext, + PageSettingsMeta, + PageSettingsTitle, +} from "@webstudio-is/react-sdk/runtime"; +import { + Page, + siteName, + favIconAsset, + pageFontAssets, + pageBackgroundImageAssets, +} from "../__generated__/[another-page]._index"; +import { + getResources, + getPageMeta, + getRemixParams, + projectId, + contactEmail, +} from "../__generated__/[another-page]._index.server"; +import { assetBaseUrl, imageLoader } from "../constants.mjs"; +import css from "../__generated__/index.css?url"; +import { sitemap } from "../__generated__/$resources.sitemap.xml"; + +const customFetch: typeof fetch = (input, init) => { + if (typeof input !== "string") { + return fetch(input, init); + } + + if (isLocalResource(input, "sitemap.xml")) { + // @todo: dynamic import sitemap ??? + const response = new Response(JSON.stringify(sitemap)); + response.headers.set("content-type", "application/json; charset=utf-8"); + return Promise.resolve(response); + } + + return fetch(input, init); +}; + +export const loader = async (arg: LoaderFunctionArgs) => { + const url = new URL(arg.request.url); + const host = + arg.request.headers.get("x-forwarded-host") || + arg.request.headers.get("host") || + ""; + url.host = host; + url.protocol = "https"; + + const params = getRemixParams(arg.params); + const system = { + params, + search: Object.fromEntries(url.searchParams), + origin: url.origin, + }; + + const resources = await loadResources( + customFetch, + getResources({ system }).data + ); + const pageMeta = getPageMeta({ system, resources }); + + if (pageMeta.redirect) { + const status = + pageMeta.status === 301 || pageMeta.status === 302 + ? pageMeta.status + : 302; + throw redirect(pageMeta.redirect, status); + } + + // typecheck + arg.context.EXCLUDE_FROM_SEARCH satisfies boolean; + + if (arg.context.EXCLUDE_FROM_SEARCH) { + pageMeta.excludePageFromSearch = arg.context.EXCLUDE_FROM_SEARCH; + } + + return data( + { + host, + url: url.href, + system, + resources, + pageMeta, + }, + // No way for current information to change, so add cache for 10 minutes + // In case of CRM Data, this should be set to 0 + { + status: pageMeta.status, + headers: { + "Cache-Control": "public, max-age=600", + }, + } + ); +}; + +export const headers: HeadersFunction = () => { + return { + "Cache-Control": "public, max-age=0, must-revalidate", + }; +}; + +export const meta: MetaFunction = ({ data }) => { + const metas: ReturnType = []; + if (data === undefined) { + return metas; + } + + const origin = `https://${data.host}`; + + if (siteName) { + metas.push({ + "script:ld+json": { + "@context": "https://schema.org", + "@type": "WebSite", + name: siteName, + url: origin, + }, + }); + } + + return metas; +}; + +export const links: LinksFunction = () => { + const result: LinkDescriptor[] = []; + + result.push({ + rel: "stylesheet", + href: css, + }); + + if (favIconAsset) { + result.push({ + rel: "icon", + href: imageLoader({ + src: `${assetBaseUrl}${favIconAsset.name}`, + // width,height must be multiple of 48 https://developers.google.com/search/docs/appearance/favicon-in-search + width: 144, + height: 144, + fit: "pad", + quality: 100, + format: "auto", + }), + type: undefined, + }); + } + + for (const asset of pageFontAssets) { + result.push({ + rel: "preload", + href: `${assetBaseUrl}${asset.name}`, + as: "font", + crossOrigin: "anonymous", + }); + } + + for (const backgroundImageAsset of pageBackgroundImageAssets) { + result.push({ + rel: "preload", + href: `${assetBaseUrl}${backgroundImageAsset.name}`, + as: "image", + }); + } + + return result; +}; + +const getRequestHost = (request: Request): string => + request.headers.get("x-forwarded-host") || request.headers.get("host") || ""; + +export const action = async ({ + request, + context, +}: ActionFunctionArgs): Promise< + { success: true } | { success: false; errors: string[] } +> => { + try { + const url = new URL(request.url); + url.host = getRequestHost(request); + + const formData = await request.formData(); + + const system = { + params: {}, + search: {}, + origin: url.origin, + }; + + const resourceName = formData.get(formIdFieldName); + let resource = + typeof resourceName === "string" + ? getResources({ system }).action.get(resourceName) + : undefined; + + const formBotValue = formData.get(formBotFieldName); + + if (formBotValue == null || typeof formBotValue !== "string") { + throw new Error("Form bot field not found"); + } + + const submitTime = parseInt(formBotValue, 16); + // Assumes that the difference between the server time and the form submission time, + // including any client-server time drift, is within a 5-minute range. + // Note: submitTime might be NaN because formBotValue can be any string used for logging purposes. + // Example: `formBotValue: jsdom`, or `formBotValue: headless-env` + if ( + Number.isNaN(submitTime) || + Math.abs(Date.now() - submitTime) > 1000 * 60 * 5 + ) { + throw new Error(`Form bot value invalid ${formBotValue}`); + } + + formData.delete(formIdFieldName); + formData.delete(formBotFieldName); + + if (resource) { + resource.headers.push({ + name: "Content-Type", + value: "application/json", + }); + resource.body = Object.fromEntries(formData); + } else { + if (contactEmail === undefined) { + throw new Error("Contact email not found"); + } + + resource = context.getDefaultActionResource?.({ + url, + projectId, + contactEmail, + formData, + }); + } + + if (resource === undefined) { + throw Error("Resource not found"); + } + const { ok, statusText } = await loadResource(fetch, resource); + if (ok) { + return { success: true }; + } + return { success: false, errors: [statusText] }; + } catch (error) { + console.error(error); + + return { + success: false, + errors: [error instanceof Error ? error.message : "Unknown error"], + }; + } +}; + +const Outlet = () => { + const { system, resources, url, pageMeta, host } = + useLoaderData(); + return ( + + {/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */} + + + {pageMeta.title} + + ); +}; + +export default Outlet; diff --git a/fixtures/react-router-vercel/app/routes/[robots.txt].tsx b/fixtures/react-router-vercel/app/routes/[robots.txt].tsx new file mode 100644 index 000000000000..cb8fe07b8f6f --- /dev/null +++ b/fixtures/react-router-vercel/app/routes/[robots.txt].tsx @@ -0,0 +1,24 @@ +import type { LoaderFunctionArgs } from "react-router"; + +export const loader = (arg: LoaderFunctionArgs) => { + const host = + arg.request.headers.get("x-forwarded-host") || + arg.request.headers.get("host") || + ""; + + return new Response( + ` +User-agent: * +Disallow: /api/ + +Sitemap: https://${host}/sitemap.xml + + `, + { + headers: { + "Content-Type": "text/plain", + }, + status: 200, + } + ); +}; diff --git a/fixtures/react-router-vercel/app/routes/[sitemap.xml]._index.tsx b/fixtures/react-router-vercel/app/routes/[sitemap.xml]._index.tsx new file mode 100644 index 000000000000..8362eebee742 --- /dev/null +++ b/fixtures/react-router-vercel/app/routes/[sitemap.xml]._index.tsx @@ -0,0 +1,34 @@ +import type { LoaderFunctionArgs } from "react-router"; +import { sitemap } from "../__generated__/$resources.sitemap.xml"; + +export const loader = (arg: LoaderFunctionArgs) => { + const host = + arg.request.headers.get("x-forwarded-host") || + arg.request.headers.get("host") || + ""; + + const urls = sitemap.map((page) => { + const url = new URL(`https://${host}${page.path}`); + + return ` + + ${url.href} + ${page.lastModified.split("T")[0]} + + `; + }); + + return new Response( + ` + +${urls.join("")} + + `, + { + headers: { + "Content-Type": "application/xml", + }, + status: 200, + } + ); +}; diff --git a/fixtures/react-router-vercel/app/routes/_index.tsx b/fixtures/react-router-vercel/app/routes/_index.tsx new file mode 100644 index 000000000000..5ecd622ce95e --- /dev/null +++ b/fixtures/react-router-vercel/app/routes/_index.tsx @@ -0,0 +1,295 @@ +import { + type MetaFunction, + type LinksFunction, + type LinkDescriptor, + type ActionFunctionArgs, + type LoaderFunctionArgs, + type HeadersFunction, + data, + redirect, + useLoaderData, +} from "react-router"; +import { + isLocalResource, + loadResource, + loadResources, + formIdFieldName, + formBotFieldName, +} from "@webstudio-is/sdk/runtime"; +import { + ReactSdkContext, + PageSettingsMeta, + PageSettingsTitle, +} from "@webstudio-is/react-sdk/runtime"; +import { + Page, + siteName, + favIconAsset, + pageFontAssets, + pageBackgroundImageAssets, +} from "../__generated__/_index"; +import { + getResources, + getPageMeta, + getRemixParams, + projectId, + contactEmail, +} from "../__generated__/_index.server"; +import { assetBaseUrl, imageLoader } from "../constants.mjs"; +import css from "../__generated__/index.css?url"; +import { sitemap } from "../__generated__/$resources.sitemap.xml"; + +const customFetch: typeof fetch = (input, init) => { + if (typeof input !== "string") { + return fetch(input, init); + } + + if (isLocalResource(input, "sitemap.xml")) { + // @todo: dynamic import sitemap ??? + const response = new Response(JSON.stringify(sitemap)); + response.headers.set("content-type", "application/json; charset=utf-8"); + return Promise.resolve(response); + } + + return fetch(input, init); +}; + +export const loader = async (arg: LoaderFunctionArgs) => { + const url = new URL(arg.request.url); + const host = + arg.request.headers.get("x-forwarded-host") || + arg.request.headers.get("host") || + ""; + url.host = host; + url.protocol = "https"; + + const params = getRemixParams(arg.params); + const system = { + params, + search: Object.fromEntries(url.searchParams), + origin: url.origin, + }; + + const resources = await loadResources( + customFetch, + getResources({ system }).data + ); + const pageMeta = getPageMeta({ system, resources }); + + if (pageMeta.redirect) { + const status = + pageMeta.status === 301 || pageMeta.status === 302 + ? pageMeta.status + : 302; + throw redirect(pageMeta.redirect, status); + } + + // typecheck + arg.context.EXCLUDE_FROM_SEARCH satisfies boolean; + + if (arg.context.EXCLUDE_FROM_SEARCH) { + pageMeta.excludePageFromSearch = arg.context.EXCLUDE_FROM_SEARCH; + } + + return data( + { + host, + url: url.href, + system, + resources, + pageMeta, + }, + // No way for current information to change, so add cache for 10 minutes + // In case of CRM Data, this should be set to 0 + { + status: pageMeta.status, + headers: { + "Cache-Control": "public, max-age=600", + }, + } + ); +}; + +export const headers: HeadersFunction = () => { + return { + "Cache-Control": "public, max-age=0, must-revalidate", + }; +}; + +export const meta: MetaFunction = ({ data }) => { + const metas: ReturnType = []; + if (data === undefined) { + return metas; + } + + const origin = `https://${data.host}`; + + if (siteName) { + metas.push({ + "script:ld+json": { + "@context": "https://schema.org", + "@type": "WebSite", + name: siteName, + url: origin, + }, + }); + } + + return metas; +}; + +export const links: LinksFunction = () => { + const result: LinkDescriptor[] = []; + + result.push({ + rel: "stylesheet", + href: css, + }); + + if (favIconAsset) { + result.push({ + rel: "icon", + href: imageLoader({ + src: `${assetBaseUrl}${favIconAsset.name}`, + // width,height must be multiple of 48 https://developers.google.com/search/docs/appearance/favicon-in-search + width: 144, + height: 144, + fit: "pad", + quality: 100, + format: "auto", + }), + type: undefined, + }); + } + + for (const asset of pageFontAssets) { + result.push({ + rel: "preload", + href: `${assetBaseUrl}${asset.name}`, + as: "font", + crossOrigin: "anonymous", + }); + } + + for (const backgroundImageAsset of pageBackgroundImageAssets) { + result.push({ + rel: "preload", + href: `${assetBaseUrl}${backgroundImageAsset.name}`, + as: "image", + }); + } + + return result; +}; + +const getRequestHost = (request: Request): string => + request.headers.get("x-forwarded-host") || request.headers.get("host") || ""; + +export const action = async ({ + request, + context, +}: ActionFunctionArgs): Promise< + { success: true } | { success: false; errors: string[] } +> => { + try { + const url = new URL(request.url); + url.host = getRequestHost(request); + + const formData = await request.formData(); + + const system = { + params: {}, + search: {}, + origin: url.origin, + }; + + const resourceName = formData.get(formIdFieldName); + let resource = + typeof resourceName === "string" + ? getResources({ system }).action.get(resourceName) + : undefined; + + const formBotValue = formData.get(formBotFieldName); + + if (formBotValue == null || typeof formBotValue !== "string") { + throw new Error("Form bot field not found"); + } + + const submitTime = parseInt(formBotValue, 16); + // Assumes that the difference between the server time and the form submission time, + // including any client-server time drift, is within a 5-minute range. + // Note: submitTime might be NaN because formBotValue can be any string used for logging purposes. + // Example: `formBotValue: jsdom`, or `formBotValue: headless-env` + if ( + Number.isNaN(submitTime) || + Math.abs(Date.now() - submitTime) > 1000 * 60 * 5 + ) { + throw new Error(`Form bot value invalid ${formBotValue}`); + } + + formData.delete(formIdFieldName); + formData.delete(formBotFieldName); + + if (resource) { + resource.headers.push({ + name: "Content-Type", + value: "application/json", + }); + resource.body = Object.fromEntries(formData); + } else { + if (contactEmail === undefined) { + throw new Error("Contact email not found"); + } + + resource = context.getDefaultActionResource?.({ + url, + projectId, + contactEmail, + formData, + }); + } + + if (resource === undefined) { + throw Error("Resource not found"); + } + const { ok, statusText } = await loadResource(fetch, resource); + if (ok) { + return { success: true }; + } + return { success: false, errors: [statusText] }; + } catch (error) { + console.error(error); + + return { + success: false, + errors: [error instanceof Error ? error.message : "Unknown error"], + }; + } +}; + +const Outlet = () => { + const { system, resources, url, pageMeta, host } = + useLoaderData(); + return ( + + {/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */} + + + {pageMeta.title} + + ); +}; + +export default Outlet; diff --git a/fixtures/react-router-vercel/package.json b/fixtures/react-router-vercel/package.json new file mode 100644 index 000000000000..177ccafba33c --- /dev/null +++ b/fixtures/react-router-vercel/package.json @@ -0,0 +1,43 @@ +{ + "name": "webstudio-react-router-vercel", + "scripts": { + "typecheck": "tsc", + "cli": "NODE_OPTIONS='--conditions=webstudio --import=tsx' webstudio", + "fixtures:link": "pnpm cli link --link https://p-d845c167-ea07-4875-b08d-83e97c09dcce-dot-${BUILDER_HOST:-main.development.webstudio.is}'?authToken=e9d1343f-9298-4fd3-a66e-f89a5af2dd93'", + "fixtures:sync": "pnpm cli sync --buildId f565d527-32e7-4731-bc71-aca9e9574587 && pnpm prettier --write ./.webstudio/", + "fixtures:build": "pnpm cli build --template vercel --template .template && pnpm prettier --write ./app/ ./package.json ./tsconfig.json", + "build": "react-router build", + "dev": "react-router dev", + "deploy": "npx vercel deploy" + }, + "dependencies": { + "@react-router/dev": "^7.1.5", + "@react-router/fs-routes": "^7.1.5", + "@react-router/node": "^7.1.5", + "@vercel/react-router": "^1.0.2", + "@webstudio-is/image": "workspace:*", + "@webstudio-is/react-sdk": "workspace:*", + "@webstudio-is/sdk": "workspace:*", + "@webstudio-is/sdk-components-animation": "workspace:*", + "@webstudio-is/sdk-components-react-radix": "workspace:*", + "@webstudio-is/sdk-components-react-router": "workspace:*", + "@webstudio-is/sdk-components-react": "workspace:*", + "isbot": "^5.1.22", + "react": "18.3.0-canary-14898b6a9-20240318", + "react-dom": "18.3.0-canary-14898b6a9-20240318", + "react-router": "^7.1.5", + "vite": "^5.4.11", + "webstudio": "workspace:*" + }, + "private": true, + "type": "module", + "sideEffects": false, + "devDependencies": { + "@types/react": "^18.2.70", + "@types/react-dom": "^18.2.25", + "typescript": "5.7.3" + }, + "engines": { + "node": ">=20.0.0" + } +} diff --git a/fixtures/react-router-vercel/public/assets/147-1478573_cat-icon-png-black-cat-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg b/fixtures/react-router-vercel/public/assets/147-1478573_cat-icon-png-black-cat-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg new file mode 100644 index 000000000000..9d5e2b88439a Binary files /dev/null and b/fixtures/react-router-vercel/public/assets/147-1478573_cat-icon-png-black-cat-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg differ diff --git a/fixtures/react-router-vercel/public/assets/iconly_svg_converted-converted_zMaMiAAutUl8XrITgz7d1.svg b/fixtures/react-router-vercel/public/assets/iconly_svg_converted-converted_zMaMiAAutUl8XrITgz7d1.svg new file mode 100644 index 000000000000..bc70d3a453d8 --- /dev/null +++ b/fixtures/react-router-vercel/public/assets/iconly_svg_converted-converted_zMaMiAAutUl8XrITgz7d1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fixtures/react-router-vercel/public/favicon.ico b/fixtures/react-router-vercel/public/favicon.ico new file mode 100644 index 000000000000..9cb8c815dc0a Binary files /dev/null and b/fixtures/react-router-vercel/public/favicon.ico differ diff --git a/fixtures/react-router-vercel/react-router.config.ts b/fixtures/react-router-vercel/react-router.config.ts new file mode 100644 index 000000000000..e002b06a1fe5 --- /dev/null +++ b/fixtures/react-router-vercel/react-router.config.ts @@ -0,0 +1,6 @@ +import type { Config } from "@react-router/dev/config"; +import { vercelPreset } from "@vercel/react-router/vite"; + +export default { + presets: [vercelPreset()], +} satisfies Config; diff --git a/fixtures/react-router-vercel/tsconfig.json b/fixtures/react-router-vercel/tsconfig.json new file mode 100644 index 000000000000..0744b14c56a2 --- /dev/null +++ b/fixtures/react-router-vercel/tsconfig.json @@ -0,0 +1,19 @@ +{ + "include": ["**/*.ts", "**/*.tsx", "**/*.mjs"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "types": ["vite/client", "@webstudio-is/react-sdk/placeholder"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "bundler", + "target": "ES2022", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "skipLibCheck": true, + "customConditions": ["webstudio"] + } +} diff --git a/fixtures/react-router-vercel/vercel.json b/fixtures/react-router-vercel/vercel.json new file mode 100644 index 000000000000..3c2f705ae161 --- /dev/null +++ b/fixtures/react-router-vercel/vercel.json @@ -0,0 +1,12 @@ +{ + "framework": "react-router", + "images": { + "domains": [], + "sizes": [ + 16, 32, 48, 64, 96, 128, 144, 256, 384, 640, 750, 828, 1080, 1200, 1920, + 2048, 3840 + ], + "minimumCacheTTL": 60, + "formats": ["image/webp", "image/avif"] + } +} diff --git a/fixtures/react-router-vercel/vite.config.ts b/fixtures/react-router-vercel/vite.config.ts new file mode 100644 index 000000000000..317d7dc0a1c6 --- /dev/null +++ b/fixtures/react-router-vercel/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import { reactRouter } from "@react-router/dev/vite"; + +export default defineConfig({ + plugins: [reactRouter()], +}); diff --git a/fixtures/ssg-netlify-by-project-id/package.json b/fixtures/ssg-netlify-by-project-id/package.json index 19e24e3ed316..57e3576cf539 100644 --- a/fixtures/ssg-netlify-by-project-id/package.json +++ b/fixtures/ssg-netlify-by-project-id/package.json @@ -40,6 +40,6 @@ "@webstudio-is/sdk-components-react-remix": "workspace:*", "react": "18.3.0-canary-14898b6a9-20240318", "react-dom": "18.3.0-canary-14898b6a9-20240318", - "vike": "^0.4.220" + "vike": "^0.4.222" } } diff --git a/fixtures/ssg/package.json b/fixtures/ssg/package.json index 6c9ad4d36fae..37cc0f953587 100644 --- a/fixtures/ssg/package.json +++ b/fixtures/ssg/package.json @@ -40,6 +40,6 @@ "@webstudio-is/sdk-components-react-remix": "workspace:*", "react": "18.3.0-canary-14898b6a9-20240318", "react-dom": "18.3.0-canary-14898b6a9-20240318", - "vike": "^0.4.220" + "vike": "^0.4.222" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index a7e44b6ecdde..3e945f70255f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -60,6 +60,7 @@ "@types/react": "^18.2.70", "@types/react-dom": "^18.2.25", "@types/yargs": "^17.0.33", + "@vercel/react-router": "^1.0.2", "@vitejs/plugin-react": "^4.3.4", "@webstudio-is/http-client": "workspace:*", "@webstudio-is/image": "workspace:*", @@ -71,14 +72,14 @@ "@webstudio-is/sdk-components-react-remix": "workspace:*", "@webstudio-is/sdk-components-react-router": "workspace:*", "@webstudio-is/tsconfig": "workspace:*", - "h3": "^1.14.0", + "h3": "^1.15.0", "ipx": "^3.0.1", "prettier": "3.4.2", "react": "18.3.0-canary-14898b6a9-20240318", "react-dom": "18.3.0-canary-14898b6a9-20240318", "react-router": "^7.1.5", "ts-expect": "^1.3.0", - "vike": "^0.4.220", + "vike": "^0.4.222", "vite": "^5.4.11", "vitest": "^3.0.4", "wrangler": "^3.63.2" diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts index 2b4fefbbd1e1..9570e2ac1f5f 100644 --- a/packages/cli/src/config.ts +++ b/packages/cli/src/config.ts @@ -66,6 +66,11 @@ export const PROJECT_TEMPLATES = [ { value: "vercel" as const, label: "Vercel", + expand: ["react-router", "react-router-vercel"], + }, + { + value: "vercel-legacy" as const, + label: "Vercel (legacy)", expand: ["defaults", "vercel"], }, { diff --git a/packages/cli/templates/react-router-docker/package.json b/packages/cli/templates/react-router-docker/package.json index b27445da844d..d2b1b80f9508 100644 --- a/packages/cli/templates/react-router-docker/package.json +++ b/packages/cli/templates/react-router-docker/package.json @@ -5,7 +5,7 @@ "dependencies": { "@react-router/node": "^7.1.5", "@react-router/serve": "^7.1.5", - "h3": "^1.14.0", + "h3": "^1.15.0", "ipx": "^3.0.1" } } diff --git a/packages/cli/templates/react-router-vercel/app/constants.mjs b/packages/cli/templates/react-router-vercel/app/constants.mjs new file mode 100644 index 000000000000..5ebdad372c5a --- /dev/null +++ b/packages/cli/templates/react-router-vercel/app/constants.mjs @@ -0,0 +1,25 @@ +/** + * We use mjs extension as constants in this file is shared with the build script + * and we use `node --eval` to extract the constants. + */ +export const assetBaseUrl = "/assets/"; + +/** + * @type {import("@webstudio-is/image").ImageLoader} + */ +export const imageLoader = (props) => { + if (import.meta.env.DEV) { + return props.src; + } + + if (props.format === "raw") { + return props.src; + } + + // https://vercel.com/blog/build-your-own-web-framework#automatic-image-optimization + const searchParams = new URLSearchParams(); + searchParams.set("url", props.src); + searchParams.set("w", props.width.toString()); + searchParams.set("q", props.quality.toString()); + return `/_vercel/image?${searchParams}`; +}; diff --git a/packages/cli/templates/react-router-vercel/package.json b/packages/cli/templates/react-router-vercel/package.json new file mode 100644 index 000000000000..b2b785b22e1b --- /dev/null +++ b/packages/cli/templates/react-router-vercel/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "deploy": "npx vercel deploy" + }, + "dependencies": { + "@react-router/node": "^7.1.5", + "@vercel/react-router": "^1.0.2" + } +} diff --git a/packages/cli/templates/react-router-vercel/react-router.config.ts b/packages/cli/templates/react-router-vercel/react-router.config.ts new file mode 100644 index 000000000000..e002b06a1fe5 --- /dev/null +++ b/packages/cli/templates/react-router-vercel/react-router.config.ts @@ -0,0 +1,6 @@ +import type { Config } from "@react-router/dev/config"; +import { vercelPreset } from "@vercel/react-router/vite"; + +export default { + presets: [vercelPreset()], +} satisfies Config; diff --git a/packages/cli/templates/react-router-vercel/vercel.json b/packages/cli/templates/react-router-vercel/vercel.json new file mode 100644 index 000000000000..3c2f705ae161 --- /dev/null +++ b/packages/cli/templates/react-router-vercel/vercel.json @@ -0,0 +1,12 @@ +{ + "framework": "react-router", + "images": { + "domains": [], + "sizes": [ + 16, 32, 48, 64, 96, 128, 144, 256, 384, 640, 750, 828, 1080, 1200, 1920, + 2048, 3840 + ], + "minimumCacheTTL": 60, + "formats": ["image/webp", "image/avif"] + } +} diff --git a/packages/cli/templates/ssg/package.json b/packages/cli/templates/ssg/package.json index cf65a6b75cce..0d2fa68ee9c0 100644 --- a/packages/cli/templates/ssg/package.json +++ b/packages/cli/templates/ssg/package.json @@ -16,7 +16,7 @@ "@webstudio-is/sdk-components-react-radix": "0.0.0-webstudio-version", "react": "18.3.0-canary-14898b6a9-20240318", "react-dom": "18.3.0-canary-14898b6a9-20240318", - "vike": "^0.4.220" + "vike": "^0.4.222" }, "devDependencies": { "@types/react": "^18.2.70", diff --git a/packages/sdk/src/schema/deployment.ts b/packages/sdk/src/schema/deployment.ts index 42047fb857c3..fc99d78223d6 100644 --- a/packages/sdk/src/schema/deployment.ts +++ b/packages/sdk/src/schema/deployment.ts @@ -4,6 +4,7 @@ export const Templates = z.enum([ "vanilla", "docker", "vercel", + "vercel-legacy", "netlify", "ssg", "ssg-netlify", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7555ebd3ee43..5036bc9fdc22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -510,8 +510,8 @@ importers: specifier: workspace:* version: link:../../packages/sdk-components-react-router h3: - specifier: ^1.14.0 - version: 1.14.0 + specifier: ^1.15.0 + version: 1.15.0 ipx: specifier: ^3.0.1 version: 3.0.1 @@ -608,6 +608,70 @@ importers: specifier: 5.7.3 version: 5.7.3 + fixtures/react-router-vercel: + dependencies: + '@react-router/dev': + specifier: ^7.1.5 + version: 7.1.5(@react-router/serve@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(@types/node@22.10.7)(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)) + '@react-router/fs-routes': + specifier: ^7.1.5 + version: 7.1.5(@react-router/dev@7.1.5(@react-router/serve@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(@types/node@22.10.7)(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)))(typescript@5.7.3) + '@react-router/node': + specifier: ^7.1.5 + version: 7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3) + '@vercel/react-router': + specifier: ^1.0.2 + version: 1.0.2(@react-router/dev@7.1.5(@react-router/serve@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(@types/node@22.10.7)(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)))(@react-router/node@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(isbot@5.1.22)(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318) + '@webstudio-is/image': + specifier: workspace:* + version: link:../../packages/image + '@webstudio-is/react-sdk': + specifier: workspace:* + version: link:../../packages/react-sdk + '@webstudio-is/sdk': + specifier: workspace:* + version: link:../../packages/sdk + '@webstudio-is/sdk-components-animation': + specifier: workspace:* + version: link:../../packages/sdk-components-animation + '@webstudio-is/sdk-components-react': + specifier: workspace:* + version: link:../../packages/sdk-components-react + '@webstudio-is/sdk-components-react-radix': + specifier: workspace:* + version: link:../../packages/sdk-components-react-radix + '@webstudio-is/sdk-components-react-router': + specifier: workspace:* + version: link:../../packages/sdk-components-react-router + isbot: + specifier: ^5.1.22 + version: 5.1.22 + react: + specifier: 18.3.0-canary-14898b6a9-20240318 + version: 18.3.0-canary-14898b6a9-20240318 + react-dom: + specifier: 18.3.0-canary-14898b6a9-20240318 + version: 18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318) + react-router: + specifier: ^7.1.5 + version: 7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318) + vite: + specifier: ^5.4.11 + version: 5.4.11(@types/node@22.10.7) + webstudio: + specifier: workspace:* + version: link:../../packages/cli + devDependencies: + '@types/react': + specifier: ^18.2.70 + version: 18.2.79 + '@types/react-dom': + specifier: ^18.2.25 + version: 18.2.25 + typescript: + specifier: 5.7.3 + version: 5.7.3 + fixtures/ssg: dependencies: '@webstudio-is/image': @@ -638,8 +702,8 @@ importers: specifier: 18.3.0-canary-14898b6a9-20240318 version: 18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318) vike: - specifier: ^0.4.220 - version: 0.4.220(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)) + specifier: ^0.4.222 + version: 0.4.222(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)) devDependencies: '@types/react': specifier: ^18.2.70 @@ -693,8 +757,8 @@ importers: specifier: 18.3.0-canary-14898b6a9-20240318 version: 18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318) vike: - specifier: ^0.4.220 - version: 0.4.220(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)) + specifier: ^0.4.222 + version: 0.4.222(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)) devDependencies: '@types/react': specifier: ^18.2.70 @@ -1053,6 +1117,9 @@ importers: '@types/yargs': specifier: ^17.0.33 version: 17.0.33 + '@vercel/react-router': + specifier: ^1.0.2 + version: 1.0.2(@react-router/dev@7.1.5(@react-router/serve@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(@types/node@22.10.7)(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)))(@react-router/node@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(isbot@5.1.22)(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318) '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.3.4(vite@5.4.11(@types/node@22.10.7)) @@ -1087,8 +1154,8 @@ importers: specifier: workspace:* version: link:../tsconfig h3: - specifier: ^1.14.0 - version: 1.14.0 + specifier: ^1.15.0 + version: 1.15.0 ipx: specifier: ^3.0.1 version: 3.0.1 @@ -1108,8 +1175,8 @@ importers: specifier: ^1.3.0 version: 1.3.0 vike: - specifier: ^0.4.220 - version: 0.4.220(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)) + specifier: ^0.4.222 + version: 0.4.222(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)) vite: specifier: ^5.4.11 version: 5.4.11(@types/node@22.10.7) @@ -2339,8 +2406,8 @@ packages: '@brillout/require-shim@0.1.2': resolution: {integrity: sha512-3I4LRHnVZXoSAsEoni5mosq9l6eiJED58d9V954W4CIZ88AUfYBanWGBGbJG3NztaRTpFHEA6wB3Hn93BmmJdg==} - '@brillout/vite-plugin-server-entry@0.5.8': - resolution: {integrity: sha512-Ug8CobLfbKJf3udr9v2PkzRkr69gn1jdE9k7u8n2Gizs/FhEM9Uudn3AsXXAikb4PONSGTqgj3CnEVchziDJQg==} + '@brillout/vite-plugin-server-entry@0.6.0': + resolution: {integrity: sha512-ELqCpB3l4dCZviobZKcHcNvuSPG86ZfBAHUYQWnf3K28PToBrXEagwPBEU/63ITcdpK9m7DOamQPcShtitTx5g==} '@bundled-es-modules/cookie@2.0.1': resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} @@ -5206,6 +5273,15 @@ packages: '@vanilla-extract/private@1.0.5': resolution: {integrity: sha512-6YXeOEKYTA3UV+RC8DeAjFk+/okoNz/h88R+McnzA2zpaVqTR/Ep+vszkWYlGBcMNO7vEkqbq5nT/JMMvhi+tw==} + '@vercel/react-router@1.0.2': + resolution: {integrity: sha512-DydjsjWCYiFNR18/KzxiDM8uJzt6iJJ4nNpma49+fGN5wo09APm+866wHQ/xuvUPpT+/ET4UzIa9tPBupuWS7Q==} + peerDependencies: + '@react-router/dev': '7' + '@react-router/node': '7' + isbot: '5' + react: 18.3.0-canary-14898b6a9-20240318 + react-dom: 18.3.0-canary-14898b6a9-20240318 + '@vercel/remix@2.15.2': resolution: {integrity: sha512-JZJmuM/pZl5zLACRHzq2uOamzTOeiKRnco0o07A+hUxTKemhS1tqc2R9PdQ6Mf/8ZcXY5ZxW5tah3zjBea6D4w==} engines: {node: '>=18.0.0'} @@ -5817,8 +5893,8 @@ packages: crossws@0.3.1: resolution: {integrity: sha512-HsZgeVYaG+b5zA+9PbIPGq4+J/CJynJuearykPsXx4V/eMhyQ5EDVg3Ak2FBZtVXCiOLu/U7IiwDHTr9MA+IKw==} - crossws@0.3.2: - resolution: {integrity: sha512-S2PpQHRcgYABOS2465b34wqTOn5dbLL+iSvyweJYGGFLDsKq88xrjDXUiEhfYkhWZq1HuS6of3okRHILbkrqxw==} + crossws@0.3.4: + resolution: {integrity: sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==} crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} @@ -6543,8 +6619,8 @@ packages: resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==} hasBin: true - h3@1.14.0: - resolution: {integrity: sha512-ao22eiONdgelqcnknw0iD645qW0s9NnrJHr5OBz4WOMdBdycfSas1EQf1wXRsm+PcB2Yoj43pjBPwqIpJQTeWg==} + h3@1.15.0: + resolution: {integrity: sha512-OsjX4JW8J4XGgCgEcad20pepFQWnuKH+OwkCJjogF3C+9AZ1iYdtB4hX6vAb5DskBiu5ljEXqApINjR8CqoCMQ==} has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -7594,6 +7670,9 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-mock-http@1.0.0: + resolution: {integrity: sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==} + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -8906,9 +8985,6 @@ packages: unenv-nightly@1.10.0-1717606461.a117952: resolution: {integrity: sha512-u3TfBX02WzbHTpaEfWEKwDijDSFAHcgXkayUZ+MVDrjhLFvgAJzFGTSTmwlEhwWi2exyRQey23ah9wELMM6etg==} - unenv@1.10.0: - resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} - unicode-properties@1.4.1: resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} @@ -9153,8 +9229,8 @@ packages: vfile@5.3.6: resolution: {integrity: sha512-ADBsmerdGBs2WYckrLBEmuETSPyTD4TuLxTrw0DvjirxW1ra4ZwkbzG8ndsv3Q57smvHxo677MHaQrY9yxH8cA==} - vike@0.4.220: - resolution: {integrity: sha512-9gB/Kha2tnHUyJpcJWMoL86hgW8NVxZsfolbvorTWIKkNsY77VpEUgLXiFnJCGBmim2p3PZFPxqxuxT9+VRfeA==} + vike@0.4.222: + resolution: {integrity: sha512-SeUvh/hDMUW0T0kAJIPmqfW3ijJwMa4KPJhB5sInOq5QjZQI4y0D67kYGm8kUE349NPsfw4hD7kV9TyYz7TT7w==} engines: {node: '>=18.0.0'} hasBin: true peerDependencies: @@ -9747,7 +9823,7 @@ snapshots: '@brillout/require-shim@0.1.2': {} - '@brillout/vite-plugin-server-entry@0.5.8': + '@brillout/vite-plugin-server-entry@0.6.0': dependencies: '@brillout/import': 0.2.3 '@brillout/picocolors': 1.0.15 @@ -12573,6 +12649,16 @@ snapshots: '@vanilla-extract/private@1.0.5': {} + '@vercel/react-router@1.0.2(@react-router/dev@7.1.5(@react-router/serve@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(@types/node@22.10.7)(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)))(@react-router/node@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(isbot@5.1.22)(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318)': + dependencies: + '@react-router/dev': 7.1.5(@react-router/serve@7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3))(@types/node@22.10.7)(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)) + '@react-router/node': 7.1.5(react-router@7.1.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(typescript@5.7.3) + '@vercel/static-config': 3.0.0 + isbot: 5.1.22 + react: 18.3.0-canary-14898b6a9-20240318 + react-dom: 18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318) + ts-morph: 12.0.0 + '@vercel/remix@2.15.2(@remix-run/dev@2.15.2(patch_hash=yortwzoeu3uj2blmdikhhw5byy)(@remix-run/react@2.15.2(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318)(typescript@5.7.3))(@remix-run/serve@2.15.2(typescript@5.7.3))(@types/node@22.10.7)(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)))(@remix-run/node@2.15.2(typescript@5.7.3))(@remix-run/server-runtime@2.15.2(typescript@5.7.3))(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318)': dependencies: '@remix-run/dev': 2.15.2(patch_hash=yortwzoeu3uj2blmdikhhw5byy)(@remix-run/react@2.15.2(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318)(typescript@5.7.3))(@remix-run/serve@2.15.2(typescript@5.7.3))(@types/node@22.10.7)(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.7))(wrangler@3.63.2(@cloudflare/workers-types@4.20240701.0)) @@ -13262,7 +13348,7 @@ snapshots: dependencies: uncrypto: 0.1.3 - crossws@0.3.2: + crossws@0.3.4: dependencies: uncrypto: 0.1.3 @@ -14189,18 +14275,18 @@ snapshots: pumpify: 1.5.1 through2: 2.0.5 - h3@1.14.0: + h3@1.15.0: dependencies: cookie-es: 1.2.2 - crossws: 0.3.2 + crossws: 0.3.4 defu: 6.1.4 destr: 2.0.3 iron-webcrypto: 1.2.1 + node-mock-http: 1.0.0 ohash: 1.1.4 radix3: 1.1.2 ufo: 1.5.4 uncrypto: 0.1.3 - unenv: 1.10.0 has-bigints@1.0.2: {} @@ -14372,7 +14458,7 @@ snapshots: defu: 6.1.4 destr: 2.0.3 etag: 1.8.1 - h3: 1.14.0 + h3: 1.15.0 image-meta: 0.2.1 listhen: 1.9.0 ofetch: 1.4.1 @@ -14691,7 +14777,7 @@ snapshots: crossws: 0.3.1 defu: 6.1.4 get-port-please: 3.1.2 - h3: 1.14.0 + h3: 1.15.0 http-shutdown: 1.2.2 jiti: 2.4.2 mlly: 1.7.4 @@ -15608,6 +15694,8 @@ snapshots: node-forge@1.3.1: {} + node-mock-http@1.0.0: {} + node-releases@2.0.19: {} normalize-package-data@2.5.0: @@ -16989,14 +17077,6 @@ snapshots: pathe: 1.1.2 ufo: 1.5.4 - unenv@1.10.0: - dependencies: - consola: 3.2.3 - defu: 6.1.4 - mime: 3.0.0 - node-fetch-native: 1.6.4 - pathe: 1.1.2 - unicode-properties@1.4.1: dependencies: base64-js: 1.5.1 @@ -17092,7 +17172,7 @@ snapshots: anymatch: 3.1.3 chokidar: 3.6.0 destr: 2.0.3 - h3: 1.14.0 + h3: 1.15.0 lru-cache: 10.4.3 node-fetch-native: 1.6.4 ofetch: 1.4.1 @@ -17208,13 +17288,13 @@ snapshots: unist-util-stringify-position: 3.0.3 vfile-message: 3.1.3 - vike@0.4.220(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)): + vike@0.4.222(react-streaming@0.3.42(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318))(vite@5.4.11(@types/node@22.10.7)): dependencies: '@brillout/import': 0.2.3 '@brillout/json-serializer': 0.5.15 '@brillout/picocolors': 1.0.15 '@brillout/require-shim': 0.1.2 - '@brillout/vite-plugin-server-entry': 0.5.8 + '@brillout/vite-plugin-server-entry': 0.6.0 acorn: 8.14.0 cac: 6.7.14 es-module-lexer: 1.6.0