From 1810b1a286d4e2bd8e7bb807c6c3a612de2b21b4 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 4 Feb 2025 12:46:16 +0700 Subject: [PATCH 1/9] feat: add new vercel template based on react-router --- fixtures/react-router-vercel/.gitignore | 6 + fixtures/react-router-vercel/.npmrc | 3 + fixtures/react-router-vercel/.template/.npmrc | 3 + .../.template/package.json | 12 + .../.template/tsconfig.json | 5 + .../.webstudio/config.json | 3 + .../react-router-vercel/.webstudio/data.json | 500 ++++++++++++++++++ .../__generated__/$resources.sitemap.xml.ts | 6 + .../[another-page]._index.server.tsx | 39 ++ .../__generated__/[another-page]._index.tsx | 37 ++ .../app/__generated__/_index.server.tsx | 39 ++ .../app/__generated__/_index.tsx | 64 +++ .../app/__generated__/index.css | 118 +++++ .../react-router-vercel/app/constants.mjs | 25 + fixtures/react-router-vercel/app/extension.ts | 13 + fixtures/react-router-vercel/app/root.tsx | 35 ++ fixtures/react-router-vercel/app/routes.ts | 4 + .../app/routes/[another-page]._index.tsx | 295 +++++++++++ .../app/routes/[robots.txt].tsx | 24 + .../app/routes/[sitemap.xml]._index.tsx | 34 ++ .../react-router-vercel/app/routes/_index.tsx | 295 +++++++++++ fixtures/react-router-vercel/package.json | 43 ++ ...at-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg | Bin 0 -> 64701 bytes ...verted-converted_zMaMiAAutUl8XrITgz7d1.svg | 1 + .../react-router-vercel/public/favicon.ico | Bin 0 -> 15086 bytes fixtures/react-router-vercel/tsconfig.json | 19 + fixtures/react-router-vercel/vercel.json | 11 + fixtures/react-router-vercel/vite.config.ts | 6 + packages/cli/src/config.ts | 5 + .../react-router-vercel/app/constants.mjs | 25 + .../react-router-vercel/package.json | 9 + .../templates/react-router-vercel/vercel.json | 11 + packages/sdk/src/schema/deployment.ts | 1 + pnpm-lock.yaml | 6 + 34 files changed, 1697 insertions(+) create mode 100644 fixtures/react-router-vercel/.gitignore create mode 100644 fixtures/react-router-vercel/.npmrc create mode 100644 fixtures/react-router-vercel/.template/.npmrc create mode 100644 fixtures/react-router-vercel/.template/package.json create mode 100644 fixtures/react-router-vercel/.template/tsconfig.json create mode 100644 fixtures/react-router-vercel/.webstudio/config.json create mode 100644 fixtures/react-router-vercel/.webstudio/data.json create mode 100644 fixtures/react-router-vercel/app/__generated__/$resources.sitemap.xml.ts create mode 100644 fixtures/react-router-vercel/app/__generated__/[another-page]._index.server.tsx create mode 100644 fixtures/react-router-vercel/app/__generated__/[another-page]._index.tsx create mode 100644 fixtures/react-router-vercel/app/__generated__/_index.server.tsx create mode 100644 fixtures/react-router-vercel/app/__generated__/_index.tsx create mode 100644 fixtures/react-router-vercel/app/__generated__/index.css create mode 100644 fixtures/react-router-vercel/app/constants.mjs create mode 100644 fixtures/react-router-vercel/app/extension.ts create mode 100644 fixtures/react-router-vercel/app/root.tsx create mode 100644 fixtures/react-router-vercel/app/routes.ts create mode 100644 fixtures/react-router-vercel/app/routes/[another-page]._index.tsx create mode 100644 fixtures/react-router-vercel/app/routes/[robots.txt].tsx create mode 100644 fixtures/react-router-vercel/app/routes/[sitemap.xml]._index.tsx create mode 100644 fixtures/react-router-vercel/app/routes/_index.tsx create mode 100644 fixtures/react-router-vercel/package.json create mode 100644 fixtures/react-router-vercel/public/assets/147-1478573_cat-icon-png-black-cat-png-icon.png_ZJ6-qJjk1RlFzuYwyCXdp.jpeg create mode 100644 fixtures/react-router-vercel/public/assets/iconly_svg_converted-converted_zMaMiAAutUl8XrITgz7d1.svg create mode 100644 fixtures/react-router-vercel/public/favicon.ico create mode 100644 fixtures/react-router-vercel/tsconfig.json create mode 100644 fixtures/react-router-vercel/vercel.json create mode 100644 fixtures/react-router-vercel/vite.config.ts create mode 100644 packages/cli/templates/react-router-vercel/app/constants.mjs create mode 100644 packages/cli/templates/react-router-vercel/package.json create mode 100644 packages/cli/templates/react-router-vercel/vercel.json 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..8bfaf110674a --- /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 (process.env.NODE_ENV !== "production") { + 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..5b2592d970a2 --- /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", + "start": "npx vercel dev", + "deploy": "npx vercel deploy" + }, + "dependencies": { + "webstudio": "workspace:*", + "@react-router/dev": "^7.1.5", + "@react-router/fs-routes": "^7.1.5", + "@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", + "@react-router/node": "^7.1.5" + }, + "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 0000000000000000000000000000000000000000..9d5e2b88439ace5f8de36737ff779f8b70599d56 GIT binary patch literal 64701 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 mRMneXO=C?}CjJ_Z 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 \ 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 0000000000000000000000000000000000000000..9cb8c815dc0abbc6b9038edea292275bf56e2537 GIT binary patch literal 15086 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 NPS;av>ny9R_rKT{U=08O literal 0 HcmV?d00001 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..94ff3b2f352e --- /dev/null +++ b/fixtures/react-router-vercel/vercel.json @@ -0,0 +1,11 @@ +{ + "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/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-vercel/app/constants.mjs b/packages/cli/templates/react-router-vercel/app/constants.mjs new file mode 100644 index 000000000000..8bfaf110674a --- /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 (process.env.NODE_ENV !== "production") { + 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..093014386bb9 --- /dev/null +++ b/packages/cli/templates/react-router-vercel/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "start": "npx vercel dev", + "deploy": "npx vercel deploy" + }, + "dependencies": { + "@react-router/node": "^7.1.5" + } +} 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..94ff3b2f352e --- /dev/null +++ b/packages/cli/templates/react-router-vercel/vercel.json @@ -0,0 +1,11 @@ +{ + "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/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..293550da74b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -608,6 +608,12 @@ importers: specifier: 5.7.3 version: 5.7.3 + fixtures/react-router-vercel: + dependencies: + webstudio: + specifier: workspace:* + version: link:../../packages/cli + fixtures/ssg: dependencies: '@webstudio-is/image': From a49e016744980c46841670d5facfbd460c1a5cb9 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 4 Feb 2025 13:15:21 +0700 Subject: [PATCH 2/9] lockfile --- pnpm-lock.yaml | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 293550da74b2..2490a7980a6f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -610,9 +610,64 @@ importers: 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) + '@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: From 63154829148c5b0f541d20d96d0f387734f2d9a2 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 4 Feb 2025 22:03:30 +0700 Subject: [PATCH 3/9] Add framework vite to vercel --- fixtures/react-router-vercel/vercel.json | 1 + packages/cli/templates/react-router-vercel/vercel.json | 1 + 2 files changed, 2 insertions(+) diff --git a/fixtures/react-router-vercel/vercel.json b/fixtures/react-router-vercel/vercel.json index 94ff3b2f352e..5c4c20a0873a 100644 --- a/fixtures/react-router-vercel/vercel.json +++ b/fixtures/react-router-vercel/vercel.json @@ -1,4 +1,5 @@ { + "framework": "vite", "images": { "domains": [], "sizes": [ diff --git a/packages/cli/templates/react-router-vercel/vercel.json b/packages/cli/templates/react-router-vercel/vercel.json index 94ff3b2f352e..5c4c20a0873a 100644 --- a/packages/cli/templates/react-router-vercel/vercel.json +++ b/packages/cli/templates/react-router-vercel/vercel.json @@ -1,4 +1,5 @@ { + "framework": "vite", "images": { "domains": [], "sizes": [ From 2f298231533aa203b4b783e9823367e9e064d939 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 12 Feb 2025 17:02:14 +0800 Subject: [PATCH 4/9] Add vercel preset --- fixtures/react-router-vercel/package.json | 3 ++- .../react-router.config.ts | 6 +++++ packages/cli/package.json | 1 + .../react-router-vercel/package.json | 3 ++- .../react-router.config.ts | 6 +++++ pnpm-lock.yaml | 22 +++++++++++++++++++ 6 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 fixtures/react-router-vercel/react-router.config.ts create mode 100644 packages/cli/templates/react-router-vercel/react-router.config.ts diff --git a/fixtures/react-router-vercel/package.json b/fixtures/react-router-vercel/package.json index 5b2592d970a2..723ec1ea837f 100644 --- a/fixtures/react-router-vercel/package.json +++ b/fixtures/react-router-vercel/package.json @@ -27,7 +27,8 @@ "react-dom": "18.3.0-canary-14898b6a9-20240318", "react-router": "^7.1.5", "vite": "^5.4.11", - "@react-router/node": "^7.1.5" + "@react-router/node": "^7.1.5", + "@vercel/react-router": "^1.0.2" }, "private": true, "type": "module", 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/packages/cli/package.json b/packages/cli/package.json index a7e44b6ecdde..c799769ffce5 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:*", diff --git a/packages/cli/templates/react-router-vercel/package.json b/packages/cli/templates/react-router-vercel/package.json index 093014386bb9..041e2ecbb38f 100644 --- a/packages/cli/templates/react-router-vercel/package.json +++ b/packages/cli/templates/react-router-vercel/package.json @@ -4,6 +4,7 @@ "deploy": "npx vercel deploy" }, "dependencies": { - "@react-router/node": "^7.1.5" + "@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/pnpm-lock.yaml b/pnpm-lock.yaml index 2490a7980a6f..baa7bbab58fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1114,6 +1114,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)) @@ -5267,6 +5270,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'} @@ -12634,6 +12646,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)) From 213310ea1179c923b30a43a6731c654817d47e9a Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 12 Feb 2025 17:05:03 +0800 Subject: [PATCH 5/9] Update lockfile --- pnpm-lock.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index baa7bbab58fb..220a967f2b6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -619,6 +619,9 @@ importers: '@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 From 08106d5d0b82dfdc43dea6d1f9d3920ded7afa51 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 12 Feb 2025 17:36:17 +0800 Subject: [PATCH 6/9] Switch to import.meta.env.DEV --- fixtures/react-router-vercel/app/constants.mjs | 2 +- packages/cli/templates/react-router-vercel/app/constants.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fixtures/react-router-vercel/app/constants.mjs b/fixtures/react-router-vercel/app/constants.mjs index 8bfaf110674a..5ebdad372c5a 100644 --- a/fixtures/react-router-vercel/app/constants.mjs +++ b/fixtures/react-router-vercel/app/constants.mjs @@ -8,7 +8,7 @@ export const assetBaseUrl = "/assets/"; * @type {import("@webstudio-is/image").ImageLoader} */ export const imageLoader = (props) => { - if (process.env.NODE_ENV !== "production") { + if (import.meta.env.DEV) { return props.src; } diff --git a/packages/cli/templates/react-router-vercel/app/constants.mjs b/packages/cli/templates/react-router-vercel/app/constants.mjs index 8bfaf110674a..5ebdad372c5a 100644 --- a/packages/cli/templates/react-router-vercel/app/constants.mjs +++ b/packages/cli/templates/react-router-vercel/app/constants.mjs @@ -8,7 +8,7 @@ export const assetBaseUrl = "/assets/"; * @type {import("@webstudio-is/image").ImageLoader} */ export const imageLoader = (props) => { - if (process.env.NODE_ENV !== "production") { + if (import.meta.env.DEV) { return props.src; } From 042a34545e197e76d96f64acbfdbe4142b900a56 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Thu, 13 Feb 2025 13:32:09 +0800 Subject: [PATCH 7/9] Remove confusing start script from vercel template --- fixtures/react-router-vercel/package.json | 7 +++---- packages/cli/templates/react-router-vercel/package.json | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/fixtures/react-router-vercel/package.json b/fixtures/react-router-vercel/package.json index 723ec1ea837f..177ccafba33c 100644 --- a/fixtures/react-router-vercel/package.json +++ b/fixtures/react-router-vercel/package.json @@ -8,13 +8,13 @@ "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", - "start": "npx vercel dev", "deploy": "npx vercel deploy" }, "dependencies": { - "webstudio": "workspace:*", "@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:*", @@ -27,8 +27,7 @@ "react-dom": "18.3.0-canary-14898b6a9-20240318", "react-router": "^7.1.5", "vite": "^5.4.11", - "@react-router/node": "^7.1.5", - "@vercel/react-router": "^1.0.2" + "webstudio": "workspace:*" }, "private": true, "type": "module", diff --git a/packages/cli/templates/react-router-vercel/package.json b/packages/cli/templates/react-router-vercel/package.json index 041e2ecbb38f..b2b785b22e1b 100644 --- a/packages/cli/templates/react-router-vercel/package.json +++ b/packages/cli/templates/react-router-vercel/package.json @@ -1,6 +1,5 @@ { "scripts": { - "start": "npx vercel dev", "deploy": "npx vercel deploy" }, "dependencies": { From 5844e2859335d5dd7b2ff53e0c09b356a882d53c Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Thu, 13 Feb 2025 13:45:24 +0800 Subject: [PATCH 8/9] Bump deps --- fixtures/react-router-docker/package.json | 2 +- .../ssg-netlify-by-project-id/package.json | 2 +- fixtures/ssg/package.json | 2 +- packages/cli/package.json | 4 +- .../react-router-docker/package.json | 2 +- packages/cli/templates/ssg/package.json | 2 +- pnpm-lock.yaml | 72 +++++++++---------- 7 files changed, 40 insertions(+), 46 deletions(-) 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/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 c799769ffce5..3e945f70255f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -72,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/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/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/pnpm-lock.yaml b/pnpm-lock.yaml index 220a967f2b6d..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 @@ -702,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 @@ -757,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 @@ -1154,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 @@ -1175,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) @@ -2406,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==} @@ -5893,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==} @@ -6619,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==} @@ -7670,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==} @@ -8982,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==} @@ -9229,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: @@ -9823,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 @@ -13348,7 +13348,7 @@ snapshots: dependencies: uncrypto: 0.1.3 - crossws@0.3.2: + crossws@0.3.4: dependencies: uncrypto: 0.1.3 @@ -14275,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: {} @@ -14458,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 @@ -14777,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 @@ -15694,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: @@ -17075,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 @@ -17178,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 @@ -17294,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 From 4ee3d341db8e901123a927c1158b728f3acf1344 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Thu, 13 Feb 2025 16:33:03 +0800 Subject: [PATCH 9/9] Specify react-router framework explicitly --- fixtures/react-router-vercel/vercel.json | 2 +- packages/cli/templates/react-router-vercel/vercel.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fixtures/react-router-vercel/vercel.json b/fixtures/react-router-vercel/vercel.json index 5c4c20a0873a..3c2f705ae161 100644 --- a/fixtures/react-router-vercel/vercel.json +++ b/fixtures/react-router-vercel/vercel.json @@ -1,5 +1,5 @@ { - "framework": "vite", + "framework": "react-router", "images": { "domains": [], "sizes": [ diff --git a/packages/cli/templates/react-router-vercel/vercel.json b/packages/cli/templates/react-router-vercel/vercel.json index 5c4c20a0873a..3c2f705ae161 100644 --- a/packages/cli/templates/react-router-vercel/vercel.json +++ b/packages/cli/templates/react-router-vercel/vercel.json @@ -1,5 +1,5 @@ { - "framework": "vite", + "framework": "react-router", "images": { "domains": [], "sizes": [