webstudio 0.167.0 → 0.173.0

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