diff --git a/fixtures/webstudio-features/.webstudio/data.json b/fixtures/webstudio-features/.webstudio/data.json
index f5473c7fd0ea..126e64e5e4a1 100644
--- a/fixtures/webstudio-features/.webstudio/data.json
+++ b/fixtures/webstudio-features/.webstudio/data.json
@@ -1,10 +1,10 @@
{
"build": {
- "id": "f0dfc2e7-240a-4542-ad28-a4cb68b2d9db",
+ "id": "043bda3f-0d77-4656-aa9c-652c1a1f272e",
"projectId": "cddc1d44-af37-4cb6-a430-d300cf6f932d",
- "version": 583,
- "createdAt": "2025-04-14T09:45:34.257+00:00",
- "updatedAt": "2025-04-14T09:45:34.257+00:00",
+ "version": 593,
+ "createdAt": "2025-05-15T22:04:05.511+00:00",
+ "updatedAt": "2025-05-15T22:04:05.511+00:00",
"pages": {
"meta": {
"siteName": "KittyGuardedZone",
@@ -2165,6 +2165,18 @@
"value": "center"
}
}
+ ],
+ [
+ "U6kV-2LTnN9kR4jWfLb1c:UoTkWyaFuTYJihS3MFYK5:display:",
+ {
+ "breakpointId": "UoTkWyaFuTYJihS3MFYK5",
+ "styleSourceId": "U6kV-2LTnN9kR4jWfLb1c",
+ "property": "display",
+ "value": {
+ "type": "keyword",
+ "value": "block"
+ }
+ }
]
],
"styleSources": [
@@ -2482,6 +2494,13 @@
"type": "local",
"id": "hEVSzkOEBn_3dNyo65PAM"
}
+ ],
+ [
+ "U6kV-2LTnN9kR4jWfLb1c",
+ {
+ "type": "local",
+ "id": "U6kV-2LTnN9kR4jWfLb1c"
+ }
]
],
"styleSourceSelections": [
@@ -2799,6 +2818,13 @@
"instanceId": "g__o13UKOkD0KImnp-sWp",
"values": ["hEVSzkOEBn_3dNyo65PAM"]
}
+ ],
+ [
+ "Ol5bklKKxyJaS7Q3jcCfD",
+ {
+ "instanceId": "Ol5bklKKxyJaS7Q3jcCfD",
+ "values": ["U6kV-2LTnN9kR4jWfLb1c"]
+ }
]
],
"props": [
@@ -2812,16 +2838,6 @@
"value": "cd939c56-bcdd-4e64-bd9c-567a9bccd3da"
}
],
- [
- "SYK4hpLQ9tHnESKDtPvI9",
- {
- "id": "SYK4hpLQ9tHnESKDtPvI9",
- "instanceId": "l9AI_pShC-BH4ibxK6kNT",
- "name": "href",
- "type": "string",
- "value": "https://github.com/"
- }
- ],
[
"_x-dxwbTQ-XBLRuYQE9Pm",
{
@@ -4111,6 +4127,16 @@
"type": "boolean",
"value": true
}
+ ],
+ [
+ "qG9NyGQuq0KVR0zvf76Er",
+ {
+ "id": "qG9NyGQuq0KVR0zvf76Er",
+ "instanceId": "Ol5bklKKxyJaS7Q3jcCfD",
+ "name": "href",
+ "type": "string",
+ "value": "https://github.com/"
+ }
]
],
"dataSources": [
@@ -4432,7 +4458,7 @@
},
{
"type": "id",
- "value": "l9AI_pShC-BH4ibxK6kNT"
+ "value": "Ol5bklKKxyJaS7Q3jcCfD"
},
{
"type": "id",
@@ -4508,20 +4534,6 @@
"children": []
}
],
- [
- "l9AI_pShC-BH4ibxK6kNT",
- {
- "type": "instance",
- "id": "l9AI_pShC-BH4ibxK6kNT",
- "component": "Link",
- "children": [
- {
- "type": "text",
- "value": "Click here to adore more kittens"
- }
- ]
- }
- ],
[
"uKWGyE9JY3cPwY-xI9vk6",
{
@@ -6728,6 +6740,21 @@
}
]
}
+ ],
+ [
+ "Ol5bklKKxyJaS7Q3jcCfD",
+ {
+ "type": "instance",
+ "id": "Ol5bklKKxyJaS7Q3jcCfD",
+ "component": "ws:element",
+ "tag": "a",
+ "children": [
+ {
+ "type": "text",
+ "value": "Click here to adore more kittens"
+ }
+ ]
+ }
]
],
"deployment": {
diff --git a/fixtures/webstudio-features/app/__generated__/$resources.sitemap.xml.ts b/fixtures/webstudio-features/app/__generated__/$resources.sitemap.xml.ts
index d33e6cb8fb0f..4adf5d883e30 100644
--- a/fixtures/webstudio-features/app/__generated__/$resources.sitemap.xml.ts
+++ b/fixtures/webstudio-features/app/__generated__/$resources.sitemap.xml.ts
@@ -1,26 +1,26 @@
export const sitemap = [
{
path: "/",
- lastModified: "2025-04-14",
+ lastModified: "2025-05-15",
},
{
path: "/_route_with_symbols_",
- lastModified: "2025-04-14",
+ lastModified: "2025-05-15",
},
{
path: "/form",
- lastModified: "2025-04-14",
+ lastModified: "2025-05-15",
},
{
path: "/heading-with-id",
- lastModified: "2025-04-14",
+ lastModified: "2025-05-15",
},
{
path: "/resources",
- lastModified: "2025-04-14",
+ lastModified: "2025-05-15",
},
{
path: "/nested/nested-page",
- lastModified: "2025-04-14",
+ lastModified: "2025-05-15",
},
];
diff --git a/fixtures/webstudio-features/app/__generated__/[_route_with_symbols_]._index.tsx b/fixtures/webstudio-features/app/__generated__/[_route_with_symbols_]._index.tsx
index 9c281b2bb89a..72512f300034 100644
--- a/fixtures/webstudio-features/app/__generated__/[_route_with_symbols_]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[_route_with_symbols_]._index.tsx
@@ -8,7 +8,7 @@ import { Image as Image } from "@webstudio-is/sdk-components-react";
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[animations]._index.tsx b/fixtures/webstudio-features/app/__generated__/[animations]._index.tsx
index 98b20e7fd504..967cff4e83ea 100644
--- a/fixtures/webstudio-features/app/__generated__/[animations]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[animations]._index.tsx
@@ -15,7 +15,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[class-names]._index.tsx b/fixtures/webstudio-features/app/__generated__/[class-names]._index.tsx
index 0d81948c412c..0251b4eba87e 100644
--- a/fixtures/webstudio-features/app/__generated__/[class-names]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[class-names]._index.tsx
@@ -8,7 +8,7 @@ import { Box as Box } from "@webstudio-is/sdk-components-react";
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[content-block]._index.tsx b/fixtures/webstudio-features/app/__generated__/[content-block]._index.tsx
index 5281a7759002..b4c83bb60bcc 100644
--- a/fixtures/webstudio-features/app/__generated__/[content-block]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[content-block]._index.tsx
@@ -12,7 +12,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[duration]._index.tsx b/fixtures/webstudio-features/app/__generated__/[duration]._index.tsx
index f8072156dff5..09871db730b7 100644
--- a/fixtures/webstudio-features/app/__generated__/[duration]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[duration]._index.tsx
@@ -9,7 +9,7 @@ import { Heading as Heading } from "@webstudio-is/sdk-components-react";
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[expressions]._index.tsx b/fixtures/webstudio-features/app/__generated__/[expressions]._index.tsx
index 8aedb43c32a7..7308a5640b92 100644
--- a/fixtures/webstudio-features/app/__generated__/[expressions]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[expressions]._index.tsx
@@ -12,7 +12,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[form]._index.tsx b/fixtures/webstudio-features/app/__generated__/[form]._index.tsx
index 9709fe9b2d67..5318e790eb3d 100644
--- a/fixtures/webstudio-features/app/__generated__/[form]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[form]._index.tsx
@@ -17,7 +17,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[head-tag]._index.tsx b/fixtures/webstudio-features/app/__generated__/[head-tag]._index.tsx
index 6a7000811dbb..9320d5efca81 100644
--- a/fixtures/webstudio-features/app/__generated__/[head-tag]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[head-tag]._index.tsx
@@ -17,7 +17,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[heading-with-id]._index.tsx b/fixtures/webstudio-features/app/__generated__/[heading-with-id]._index.tsx
index ac381b1d1436..5b5e3f7cfe25 100644
--- a/fixtures/webstudio-features/app/__generated__/[heading-with-id]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[heading-with-id]._index.tsx
@@ -8,7 +8,7 @@ import { Heading as Heading } from "@webstudio-is/sdk-components-react";
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[nested].[nested-page]._index.tsx b/fixtures/webstudio-features/app/__generated__/[nested].[nested-page]._index.tsx
index 02aaaaa3b7f2..f8c3fc682356 100644
--- a/fixtures/webstudio-features/app/__generated__/[nested].[nested-page]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[nested].[nested-page]._index.tsx
@@ -8,7 +8,7 @@ import { Heading as Heading } from "@webstudio-is/sdk-components-react";
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[radix]._index.tsx b/fixtures/webstudio-features/app/__generated__/[radix]._index.tsx
index f9999e091f25..4a99e3b1a539 100644
--- a/fixtures/webstudio-features/app/__generated__/[radix]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[radix]._index.tsx
@@ -19,7 +19,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[resources]._index.tsx b/fixtures/webstudio-features/app/__generated__/[resources]._index.tsx
index 53250ff8d846..6e40d8fb3fc6 100644
--- a/fixtures/webstudio-features/app/__generated__/[resources]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[resources]._index.tsx
@@ -11,7 +11,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[sitemap.xml]._index.tsx b/fixtures/webstudio-features/app/__generated__/[sitemap.xml]._index.tsx
index c53f64fd0d5b..06acae7bf9db 100644
--- a/fixtures/webstudio-features/app/__generated__/[sitemap.xml]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[sitemap.xml]._index.tsx
@@ -10,7 +10,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/[text-duration]._index.tsx b/fixtures/webstudio-features/app/__generated__/[text-duration]._index.tsx
index f46671b99871..0f13b7814636 100644
--- a/fixtures/webstudio-features/app/__generated__/[text-duration]._index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/[text-duration]._index.tsx
@@ -12,7 +12,7 @@ import { Heading as Heading } from "@webstudio-is/sdk-components-react";
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
diff --git a/fixtures/webstudio-features/app/__generated__/_index.tsx b/fixtures/webstudio-features/app/__generated__/_index.tsx
index 36fa9cc5d0d9..6248c70bf485 100644
--- a/fixtures/webstudio-features/app/__generated__/_index.tsx
+++ b/fixtures/webstudio-features/app/__generated__/_index.tsx
@@ -6,6 +6,7 @@ import { useResource, useVariableState } from "@webstudio-is/react-sdk/runtime";
import {
Body as Body,
Link as Link,
+ Link as Link_1,
} from "@webstudio-is/sdk-components-react-router";
import {
Heading as Heading,
@@ -17,7 +18,7 @@ import {
export const projectId = "cddc1d44-af37-4cb6-a430-d300cf6f932d";
-export const lastPublished = "2025-04-14T09:45:34.257Z";
+export const lastPublished = "2025-05-15T22:04:05.511Z";
export const siteName = "KittyGuardedZone";
@@ -72,9 +73,9 @@ const Page = (_props: { system: any }) => {
"a little kitten painted in black and white gouache with a thick brush"
}
-
+
{"Click here to adore more kittens"}
-
+
{" or "}
diff --git a/fixtures/webstudio-features/app/__generated__/index.css b/fixtures/webstudio-features/app/__generated__/index.css
index 6a94668cb011..4d4b55ef0a86 100644
--- a/fixtures/webstudio-features/app/__generated__/index.css
+++ b/fixtures/webstudio-features/app/__generated__/index.css
@@ -8,6 +8,14 @@
white-space: pre-wrap;
white-space-collapse: preserve;
}
+ a.w-element {
+ box-sizing: border-box;
+ border-top-width: 1px;
+ border-right-width: 1px;
+ border-bottom-width: 1px;
+ border-left-width: 1px;
+ outline-width: 1px;
+ }
body.w-body {
box-sizing: border-box;
border-top-width: 1px;
diff --git a/fixtures/webstudio-features/package.json b/fixtures/webstudio-features/package.json
index 915298800832..b50c50a4c8ee 100644
--- a/fixtures/webstudio-features/package.json
+++ b/fixtures/webstudio-features/package.json
@@ -6,7 +6,7 @@
"dev": "react-router dev",
"cli": "NODE_OPTIONS='--conditions=webstudio --import=tsx' webstudio",
"fixtures:link": "pnpm cli link --link https://p-cddc1d44-af37-4cb6-a430-d300cf6f932d-dot-${BUILDER_HOST:-main.development.webstudio.is}'?authToken=1cdc6026-dd5b-4624-b89b-9bd45e9bcc3d'",
- "fixtures:sync": "pnpm cli sync --buildId f0dfc2e7-240a-4542-ad28-a4cb68b2d9db && pnpm prettier --write ./.webstudio/",
+ "fixtures:sync": "pnpm cli sync --buildId 043bda3f-0d77-4656-aa9c-652c1a1f272e && pnpm prettier --write ./.webstudio/",
"fixtures:build": "pnpm cli build --template react-router --template ./.template && pnpm prettier --write ./app/ ./package.json ./tsconfig.json"
},
"private": true,
diff --git a/packages/cli/src/framework-react-router.ts b/packages/cli/src/framework-react-router.ts
index a956faec58be..0d007e43a246 100644
--- a/packages/cli/src/framework-react-router.ts
+++ b/packages/cli/src/framework-react-router.ts
@@ -4,7 +4,6 @@ import type { WsComponentMeta } from "@webstudio-is/sdk";
import { generateRemixRoute } from "@webstudio-is/react-sdk";
import * as baseComponentMetas from "@webstudio-is/sdk-components-react/metas";
import * as animationComponentMetas from "@webstudio-is/sdk-components-animation/metas";
-import * as reactRouterComponentMetas from "@webstudio-is/sdk-components-react-router/metas";
import * as radixComponentMetas from "@webstudio-is/sdk-components-react-radix/metas";
import type { Framework } from "./framework";
@@ -31,37 +30,36 @@ export const createFramework = async (): Promise => {
// cleanup route templates after reading to not bloat generated code
await rm(routeTemplatesDir, { recursive: true, force: true });
- const radixComponentNamespacedMetas: Record = {};
+ const base = "@webstudio-is/sdk-components-react";
+ const reactRouter = "@webstudio-is/sdk-components-react-router";
+ const reactRadix = "@webstudio-is/sdk-components-react-radix";
+ const animation = "@webstudio-is/sdk-components-animation";
+ const components: Record = {};
+ const metas: Record = {};
+ for (const [name, meta] of Object.entries(baseComponentMetas)) {
+ components[name] = `${base}:${name}`;
+ metas[name] = meta;
+ }
+ for (const name of ["Body", "Link", "RichTextLink", "Form", "RemixForm"]) {
+ components[name] = `${reactRouter}:${name}`;
+ }
for (const [name, meta] of Object.entries(radixComponentMetas)) {
- const namespace = "@webstudio-is/sdk-components-react-radix";
- radixComponentNamespacedMetas[`${namespace}:${name}`] = meta;
+ components[`${reactRadix}:${name}`] = `${reactRadix}:${name}`;
+ metas[`${reactRadix}:${name}`] = meta;
}
-
- const animationComponentNamespacedMetas: Record = {};
for (const [name, meta] of Object.entries(animationComponentMetas)) {
- const namespace = "@webstudio-is/sdk-components-animation";
- animationComponentNamespacedMetas[`${namespace}:${name}`] = meta;
+ components[`${animation}:${name}`] = `${animation}:${name}`;
+ metas[`${animation}:${name}`] = meta;
}
return {
- components: [
- {
- source: "@webstudio-is/sdk-components-react",
- metas: baseComponentMetas,
- },
- {
- source: "@webstudio-is/sdk-components-animation",
- metas: animationComponentNamespacedMetas,
- },
- {
- source: "@webstudio-is/sdk-components-react-radix",
- metas: radixComponentNamespacedMetas,
- },
- {
- source: "@webstudio-is/sdk-components-react-router",
- metas: reactRouterComponentMetas,
- },
- ],
+ metas,
+ components,
+ tags: {
+ body: `${reactRouter}:Body`,
+ a: `${reactRouter}:Link`,
+ form: `${reactRouter}:RemixForm`,
+ },
html: ({ pagePath }: { pagePath: string }) => [
{
file: join("app", "routes", `${generateRemixRoute(pagePath)}.tsx`),
diff --git a/packages/cli/src/framework-remix.ts b/packages/cli/src/framework-remix.ts
index debb68cd6aa0..d03a527c2b09 100644
--- a/packages/cli/src/framework-remix.ts
+++ b/packages/cli/src/framework-remix.ts
@@ -4,7 +4,6 @@ import type { WsComponentMeta } from "@webstudio-is/sdk";
import { generateRemixRoute } from "@webstudio-is/react-sdk";
import * as baseComponentMetas from "@webstudio-is/sdk-components-react/metas";
import * as animationComponentMetas from "@webstudio-is/sdk-components-animation/metas";
-import * as remixComponentMetas from "@webstudio-is/sdk-components-react-remix/metas";
import * as radixComponentMetas from "@webstudio-is/sdk-components-react-radix/metas";
import type { Framework } from "./framework";
@@ -31,37 +30,36 @@ export const createFramework = async (): Promise => {
// cleanup route templates after reading to not bloat generated code
await rm(routeTemplatesDir, { recursive: true, force: true });
- const radixComponentNamespacedMetas: Record = {};
+ const base = "@webstudio-is/sdk-components-react";
+ const remix = "@webstudio-is/sdk-components-react-remix";
+ const reactRadix = "@webstudio-is/sdk-components-react-radix";
+ const animation = "@webstudio-is/sdk-components-animation";
+ const components: Record = {};
+ const metas: Record = {};
+ for (const [name, meta] of Object.entries(baseComponentMetas)) {
+ components[name] = `${base}:${name}`;
+ metas[name] = meta;
+ }
+ for (const name of ["Body", "Link", "RichTextLink", "Form", "RemixForm"]) {
+ components[name] = `${remix}:${name}`;
+ }
for (const [name, meta] of Object.entries(radixComponentMetas)) {
- const namespace = "@webstudio-is/sdk-components-react-radix";
- radixComponentNamespacedMetas[`${namespace}:${name}`] = meta;
+ components[`${reactRadix}:${name}`] = `${reactRadix}:${name}`;
+ metas[`${reactRadix}:${name}`] = meta;
}
-
- const animationComponentNamespacedMetas: Record = {};
for (const [name, meta] of Object.entries(animationComponentMetas)) {
- const namespace = "@webstudio-is/sdk-components-animation";
- animationComponentNamespacedMetas[`${namespace}:${name}`] = meta;
+ components[`${animation}:${name}`] = `${animation}:${name}`;
+ metas[`${animation}:${name}`] = meta;
}
return {
- components: [
- {
- source: "@webstudio-is/sdk-components-react",
- metas: baseComponentMetas,
- },
- {
- source: "@webstudio-is/sdk-components-animation",
- metas: animationComponentNamespacedMetas,
- },
- {
- source: "@webstudio-is/sdk-components-react-radix",
- metas: radixComponentNamespacedMetas,
- },
- {
- source: "@webstudio-is/sdk-components-react-remix",
- metas: remixComponentMetas,
- },
- ],
+ metas,
+ components,
+ tags: {
+ body: `${remix}:Body`,
+ a: `${remix}:Link`,
+ form: `${remix}:RemixForm`,
+ },
html: ({ pagePath }: { pagePath: string }) => [
{
file: join("app", "routes", `${generateRemixRoute(pagePath)}.tsx`),
diff --git a/packages/cli/src/framework-vike-ssg.ts b/packages/cli/src/framework-vike-ssg.ts
index d9f747a0c9f8..148c59ea7be0 100644
--- a/packages/cli/src/framework-vike-ssg.ts
+++ b/packages/cli/src/framework-vike-ssg.ts
@@ -32,33 +32,28 @@ export const createFramework = async (): Promise => {
// cleanup route templates after reading to not bloat generated code
await rm(routeTemplatesDir, { recursive: true, force: true });
- const radixComponentNamespacedMetas: Record = {};
+ const base = "@webstudio-is/sdk-components-react";
+ const reactRadix = "@webstudio-is/sdk-components-react-radix";
+ const animation = "@webstudio-is/sdk-components-animation";
+ const components: Record = {};
+ const metas: Record = {};
+ for (const [name, meta] of Object.entries(baseComponentMetas)) {
+ components[name] = `${base}:${name}`;
+ metas[name] = meta;
+ }
for (const [name, meta] of Object.entries(radixComponentMetas)) {
- const namespace = "@webstudio-is/sdk-components-react-radix";
- radixComponentNamespacedMetas[`${namespace}:${name}`] = meta;
+ components[`${reactRadix}:${name}`] = `${reactRadix}:${name}`;
+ metas[`${reactRadix}:${name}`] = meta;
}
-
- const animationComponentNamespacedMetas: Record = {};
for (const [name, meta] of Object.entries(animationComponentMetas)) {
- const namespace = "@webstudio-is/sdk-components-animation";
- animationComponentNamespacedMetas[`${namespace}:${name}`] = meta;
+ components[`${animation}:${name}`] = `${animation}:${name}`;
+ metas[`${animation}:${name}`] = meta;
}
return {
- components: [
- {
- source: "@webstudio-is/sdk-components-react",
- metas: baseComponentMetas,
- },
- {
- source: "@webstudio-is/sdk-components-animation",
- metas: animationComponentNamespacedMetas,
- },
- {
- source: "@webstudio-is/sdk-components-react-radix",
- metas: radixComponentNamespacedMetas,
- },
- ],
+ metas,
+ components,
+ tags: {},
html: ({ pagePath }: { pagePath: string }) => {
// ignore dynamic pages in static export
if (isPathnamePattern(pagePath)) {
diff --git a/packages/cli/src/framework.ts b/packages/cli/src/framework.ts
index a8d89c0a7517..8871d519d840 100644
--- a/packages/cli/src/framework.ts
+++ b/packages/cli/src/framework.ts
@@ -1,17 +1,17 @@
import type { WsComponentMeta } from "@webstudio-is/sdk";
-type FrameworkComponentEntry = {
- source: string;
- metas: Record;
-};
-
type FrameworkTemplateEntry = {
file: string;
template: string;
};
export type Framework = {
- components: FrameworkComponentEntry[];
+ // instance.component: WsComponentMeta
+ metas: Record;
+ // instance.component: "importSource:importSpecifier"
+ components: Record;
+ // instance.tag: "importSource:importSpecifier"
+ tags: Record;
html: (params: { pagePath: string }) => FrameworkTemplateEntry[];
xml: (params: { pagePath: string }) => FrameworkTemplateEntry[];
redirect: (params: { pagePath: string }) => FrameworkTemplateEntry[];
diff --git a/packages/cli/src/prebuild.ts b/packages/cli/src/prebuild.ts
index a91a1efbfd5b..29f777cc52d8 100644
--- a/packages/cli/src/prebuild.ts
+++ b/packages/cli/src/prebuild.ts
@@ -36,7 +36,6 @@ import {
createScope,
findTreeInstanceIds,
getPagePath,
- parseComponentName,
generateResources,
generatePageMeta,
getStaticSiteMapXml,
@@ -46,6 +45,7 @@ import {
SYSTEM_VARIABLE_ID,
generateCss,
ROOT_INSTANCE_ID,
+ elementComponent,
} from "@webstudio-is/sdk";
import type { Data } from "@webstudio-is/http-client";
import { LOCAL_DATA_FILE } from "./config";
@@ -285,16 +285,6 @@ export const prebuild = async (options: {
);
}
- // collect all possible component metas
- const metas = new Map();
- const componentSources = new Map();
- for (const entry of framework.components) {
- for (const [componentName, meta] of Object.entries(entry.metas)) {
- metas.set(componentName, meta);
- componentSources.set(componentName, entry.source);
- }
- }
-
const usedMetas = new Map(
Object.entries(coreMetas)
);
@@ -325,7 +315,7 @@ export const prebuild = async (options: {
for (const [_instanceId, instance] of siteData.build.instances) {
if (pageInstanceSet.has(instance.id)) {
instances.push([instance.id, instance]);
- const meta = metas.get(instance.component);
+ const meta = framework.metas[instance.component];
if (meta) {
usedMetas.set(instance.component, meta);
}
@@ -518,38 +508,36 @@ export const prebuild = async (options: {
}
}
- const pageComponents = new Set();
+ // generate component imports
+ // Map>
+ const imports = new Map>();
for (const instance of instances.values()) {
- pageComponents.add(instance.component);
- }
- const namespaces = new Map<
- string,
- Set<[shortName: string, componentName: string]>
- >();
- for (const component of pageComponents) {
- const namespace = componentSources.get(component);
- if (namespace === undefined) {
+ let descriptor = framework.components[instance.component];
+ let id = instance.component;
+ if (instance.component === elementComponent && instance.tag) {
+ descriptor = framework.tags[instance.tag];
+ id = descriptor;
+ }
+ if (descriptor === undefined) {
continue;
}
- if (namespaces.has(namespace) === false) {
- namespaces.set(
- namespace,
- new Set<[shortName: string, componentName: string]>()
- );
+ const [importSource, importSpecifier] = descriptor.split(":");
+ let specifiers = imports.get(importSource);
+ if (specifiers === undefined) {
+ specifiers = new Map();
+ imports.set(importSource, specifiers);
}
- const [_namespace, shortName] = parseComponentName(component);
- namespaces.get(namespace)?.add([shortName, component]);
+ specifiers.set(id, importSpecifier);
}
-
- let componentImports = "";
- for (const [namespace, componentsSet] of namespaces.entries()) {
- const specifiers = Array.from(componentsSet)
+ let importsString = "";
+ for (const [importSource, specifiers] of imports) {
+ const specifiersString = Array.from(specifiers)
.map(
- ([shortName, component]) =>
- `${shortName} as ${scope.getName(component, shortName)}`
+ ([id, importSpecifier]) =>
+ `${importSpecifier} as ${scope.getName(id, importSpecifier)}`
)
.join(", ");
- componentImports += `import { ${specifiers} } from "${namespace}";\n`;
+ importsString += `import { ${specifiersString} } from "${importSource}";\n`;
}
const pageFontAssets = fontAssetsByPage[page.id];
@@ -588,6 +576,7 @@ export const prebuild = async (options: {
dataSources,
classesMap: classes,
metas: usedMetas,
+ tagsOverrides: framework.tags,
});
const projectMeta = siteData.build.pages.meta;
@@ -612,7 +601,7 @@ export const prebuild = async (options: {
import { Fragment, useState } from "react";
import { useResource, useVariableState } from "@webstudio-is/react-sdk/runtime";
- ${componentImports}
+ ${importsString}
export const projectId = "${siteData.build.projectId}";
diff --git a/packages/react-sdk/src/component-generator.test.tsx b/packages/react-sdk/src/component-generator.test.tsx
index 798cd9310fdd..8f1520e4190f 100644
--- a/packages/react-sdk/src/component-generator.test.tsx
+++ b/packages/react-sdk/src/component-generator.test.tsx
@@ -1518,3 +1518,37 @@ test("ignore props similar to standard attributes on react components without ta
)
);
});
+
+test("overrides some element tags with provided components", () => {
+ expect(
+ generateWebstudioComponent({
+ classesMap: new Map(),
+ scope: createScope(),
+ name: "Page",
+ rootInstanceId: "bodyId",
+ parameters: [],
+ metas: new Map([["HeadSlot", { icon: "" }]]),
+ tagsOverrides: {
+ body: "namespace:Body",
+ a: "namespace:Link",
+ },
+ ...renderData(
+
+
+
+
+ ),
+ })
+ ).toEqual(
+ validateJSX(
+ clear(`
+ const Page = () => {
+ return
+
+
+
+ }
+ `)
+ )
+ );
+});
diff --git a/packages/react-sdk/src/component-generator.ts b/packages/react-sdk/src/component-generator.ts
index a2c009990f18..aa62450d6e3a 100644
--- a/packages/react-sdk/src/component-generator.ts
+++ b/packages/react-sdk/src/component-generator.ts
@@ -145,6 +145,7 @@ export const generateJsxElement = ({
context = "jsx",
scope,
metas,
+ tagsOverrides,
instance,
props,
dataSources,
@@ -156,6 +157,10 @@ export const generateJsxElement = ({
context?: "expression" | "jsx";
scope: Scope;
metas: Map;
+ /**
+ * Record
+ */
+ tagsOverrides?: Record;
instance: Instance;
props: Props;
dataSources: DataSources;
@@ -280,18 +285,20 @@ export const generateJsxElement = ({
generatedElement += `)}\n`;
} else if (instance.component === blockComponent) {
generatedElement += children;
- } else if (instance.component === elementComponent) {
- const tagName = instance.tag ?? "div";
- if (instance.children.length === 0) {
- generatedElement += `<${tagName}${generatedProps} />\n`;
+ } else {
+ let componentVariable;
+ if (instance.component === elementComponent) {
+ componentVariable = instance.tag ?? "div";
+ // replace html tag with component if available
+ const componentDescriptor = tagsOverrides?.[componentVariable];
+ if (componentDescriptor !== undefined) {
+ const [_importSource, importSpecifier] = componentDescriptor.split(":");
+ componentVariable = scope.getName(componentDescriptor, importSpecifier);
+ }
} else {
- generatedElement += `<${tagName}${generatedProps}>\n`;
- generatedElement += children;
- generatedElement += `${tagName}>\n`;
+ const [_namespace, shortName] = parseComponentName(instance.component);
+ componentVariable = scope.getName(instance.component, shortName);
}
- } else {
- const [_namespace, shortName] = parseComponentName(instance.component);
- const componentVariable = scope.getName(instance.component, shortName);
if (instance.children.length === 0) {
generatedElement += `<${componentVariable}${generatedProps} />\n`;
} else {
@@ -335,6 +342,7 @@ export const generateJsxElement = ({
export const generateJsxChildren = ({
scope,
metas,
+ tagsOverrides,
children,
instances,
props,
@@ -346,6 +354,8 @@ export const generateJsxChildren = ({
}: {
scope: Scope;
metas: Map;
+ // Record
+ tagsOverrides?: Record;
children: Instance["children"];
instances: Instances;
props: Props;
@@ -389,6 +399,7 @@ export const generateJsxChildren = ({
context: "jsx",
scope,
metas,
+ tagsOverrides,
instance,
props,
dataSources,
@@ -399,6 +410,7 @@ export const generateJsxChildren = ({
classesMap,
scope,
metas,
+ tagsOverrides,
children: instance.children,
instances,
props,
@@ -424,6 +436,7 @@ export const generateWebstudioComponent = ({
props,
dataSources,
metas,
+ tagsOverrides,
classesMap,
}: {
scope: Scope;
@@ -435,6 +448,10 @@ export const generateWebstudioComponent = ({
dataSources: DataSources;
classesMap: Map>;
metas: Map;
+ /**
+ * Record
+ */
+ tagsOverrides?: Record;
}) => {
const instance = instances.get(rootInstanceId);
const indexesWithinAncestors = getIndexesWithinAncestors(metas, instances, [
@@ -449,6 +466,7 @@ export const generateWebstudioComponent = ({
context: "expression",
scope,
metas,
+ tagsOverrides,
instance,
props,
dataSources,
@@ -458,6 +476,7 @@ export const generateWebstudioComponent = ({
children: generateJsxChildren({
scope,
metas,
+ tagsOverrides,
children: instance.children,
instances,
props,
diff --git a/packages/sdk-components-react-remix/package.json b/packages/sdk-components-react-remix/package.json
index 6932a7ba0ae0..21335deab7f5 100644
--- a/packages/sdk-components-react-remix/package.json
+++ b/packages/sdk-components-react-remix/package.json
@@ -17,20 +17,10 @@
"webstudio": "./src/components.ts",
"types": "./lib/types/components.d.ts",
"import": "./lib/components.js"
- },
- "./metas": {
- "webstudio": "./src/metas.ts",
- "types": "./lib/types/metas.d.ts",
- "import": "./lib/metas.js"
- },
- "./props": {
- "webstudio": "./src/props.ts",
- "types": "./lib/types/props.d.ts",
- "import": "./lib/props.js"
}
},
"scripts": {
- "build": "vite build --config ../../vite.sdk-components.config.ts",
+ "build": "rm -rf lib && esbuild src/components.ts --outdir=lib --bundle --format=esm --packages=external",
"dts": "tsc --project tsconfig.dts.json",
"typecheck": "tsc"
},
diff --git a/packages/sdk-components-react-remix/src/hooks.ts b/packages/sdk-components-react-remix/src/hooks.ts
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/packages/sdk-components-react-remix/src/metas.ts b/packages/sdk-components-react-remix/src/metas.ts
deleted file mode 100644
index 4dee41be644c..000000000000
--- a/packages/sdk-components-react-remix/src/metas.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// this lets CLI detect which components are overriden
-// without importing components runtime
-export {
- Body,
- Link,
- RichTextLink,
- Form,
- RemixForm,
-} from "@webstudio-is/sdk-components-react/metas";
diff --git a/packages/sdk-components-react-remix/src/remix-form.tsx b/packages/sdk-components-react-remix/src/remix-form.tsx
index 214834a498fc..0dead67cfea7 100644
--- a/packages/sdk-components-react-remix/src/remix-form.tsx
+++ b/packages/sdk-components-react-remix/src/remix-form.tsx
@@ -9,10 +9,13 @@ export const RemixForm = forwardRef<
Pick & {
// Remix's default behavior includes method values in both uppercase and lowercase,
// resulting in our UI displaying a list that encompasses both variants.
- method?: Lowercase>;
+ method?: Lowercase> | "dialog";
action?: string;
}
->(({ action, ...props }, ref) => {
+>(({ action, method, ...props }, ref) => {
+ if (method === "dialog") {
+ return ;
+ }
// remix casts action to relative url
if (
action === undefined ||
@@ -22,6 +25,7 @@ export const RemixForm = forwardRef<
return (