webstudio 0.111.0 → 0.111.1-0d68965.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cli.js CHANGED
@@ -357,6 +357,11 @@ var prebuild = async (options) => {
357
357
  const spinner = ora2("Scaffolding the project files");
358
358
  spinner.start();
359
359
  spinner.text = "Generating files";
360
+ const appRoot = "app";
361
+ const generatedDir = join4(appRoot, "__generated__");
362
+ await rm(generatedDir, { recursive: true, force: true });
363
+ const routesDir = join4(appRoot, "routes");
364
+ await rm(routesDir, { recursive: true, force: true });
360
365
  await copyTemplates();
361
366
  if (options.template !== void 0) {
362
367
  await copyTemplates(options.template);
@@ -456,6 +461,12 @@ var prebuild = async (options) => {
456
461
  }
457
462
  const assetsToDownload = [];
458
463
  const fontAssets = [];
464
+ const imageAssets = [];
465
+ for (const asset of siteData.assets) {
466
+ if (asset.type === "image") {
467
+ imageAssets.push(asset);
468
+ }
469
+ }
459
470
  const appDomain = options.preview ? "wstd.work" : "wstd.io";
460
471
  const assetBuildUrl = `https://${domain}.${appDomain}/cgi/asset/`;
461
472
  const temporaryDir = await mkdtemp(join4(tmpdir(), "webstudio-"));
@@ -466,8 +477,6 @@ var prebuild = async (options) => {
466
477
  for (const asset of siteData.assets) {
467
478
  if (asset.type === "image") {
468
479
  const imageSrc = imageLoader({
469
- width: 16,
470
- quality: 100,
471
480
  src: asset.name,
472
481
  format: "raw"
473
482
  });
@@ -493,11 +502,6 @@ var prebuild = async (options) => {
493
502
  }
494
503
  }
495
504
  spinner.text = "Generating routes and pages";
496
- const appRoot = "app";
497
- const generatedDir = join4(appRoot, "__generated__");
498
- await rm(generatedDir, { recursive: true, force: true });
499
- const routesDir = join4(appRoot, "routes");
500
- await rm(routesDir, { recursive: true, force: true });
501
505
  const routeFileTemplate = await readFile4(
502
506
  normalize(
503
507
  join4(
@@ -556,6 +560,7 @@ var prebuild = async (options) => {
556
560
  }
557
561
  const pageData = siteDataByPage[pathName];
558
562
  const renderedPageData = {
563
+ site: siteData.build.pages.meta,
559
564
  page: pageData.page
560
565
  };
561
566
  const rootInstanceId = pageData.page.rootInstanceId;
@@ -583,9 +588,10 @@ var prebuild = async (options) => {
583
588
 
584
589
  import { type ReactNode, useState } from "react";
585
590
  import type { PageData } from "~/routes/_index";
586
- import type { Asset } from "@webstudio-is/sdk";
591
+ import type { Asset, ImageAsset, SiteMeta } from "@webstudio-is/sdk";
587
592
  ${componentImports}
588
593
  export const fontAssets: Asset[] = ${JSON.stringify(fontAssets)}
594
+ export const imageAssets: ImageAsset[] = ${JSON.stringify(imageAssets)}
589
595
  export const pageData: PageData = ${JSON.stringify(renderedPageData)};
590
596
  export const user: { email: string | null } | undefined = ${JSON.stringify(
591
597
  siteData.user
@@ -785,7 +791,7 @@ import makeCLI from "yargs";
785
791
  // package.json
786
792
  var package_default = {
787
793
  name: "webstudio",
788
- version: "0.111.0",
794
+ version: "0.111.1-0d68965.0",
789
795
  description: "Webstudio CLI",
790
796
  author: "Webstudio <github@webstudio.is>",
791
797
  homepage: "https://webstudio.is",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webstudio",
3
- "version": "0.111.0",
3
+ "version": "0.111.1-0d68965.0",
4
4
  "description": "Webstudio CLI",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -28,21 +28,21 @@
28
28
  "title-case": "^4.1.0",
29
29
  "yargs": "^17.7.2",
30
30
  "zod": "^3.21.4",
31
- "@webstudio-is/http-client": "0.111.0",
32
- "@webstudio-is/image": "0.111.0",
33
- "@webstudio-is/react-sdk": "0.111.0",
34
- "@webstudio-is/sdk": "0.111.0",
35
- "@webstudio-is/sdk-components-react": "0.111.0",
36
- "@webstudio-is/sdk-components-react-radix": "0.111.0",
37
- "@webstudio-is/sdk-components-react-remix": "0.111.0"
31
+ "@webstudio-is/http-client": "0.111.1-0d68965.0",
32
+ "@webstudio-is/image": "0.111.1-0d68965.0",
33
+ "@webstudio-is/react-sdk": "0.111.1-0d68965.0",
34
+ "@webstudio-is/sdk": "0.111.1-0d68965.0",
35
+ "@webstudio-is/sdk-components-react": "0.111.1-0d68965.0",
36
+ "@webstudio-is/sdk-components-react-radix": "0.111.1-0d68965.0",
37
+ "@webstudio-is/sdk-components-react-remix": "0.111.1-0d68965.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^18.17.1",
41
41
  "@types/prompts": "^2.4.5",
42
42
  "tsx": "^3.12.8",
43
43
  "typescript": "5.2.2",
44
- "@webstudio-is/form-handlers": "0.111.0",
45
- "@webstudio-is/tsconfig": "1.0.7"
44
+ "@webstudio-is/form-handlers": "0.111.1-0d68965.0",
45
+ "@webstudio-is/tsconfig": "1.0.8-0d68965.0"
46
46
  },
47
47
  "scripts": {
48
48
  "typecheck": "tsc",
@@ -0,0 +1,9 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-ignore
3
+ import { AppLoadContext } from "@remix-run/server-runtime";
4
+
5
+ declare module "@remix-run/server-runtime" {
6
+ interface AppLoadContext {
7
+ EXCLUDE_FROM_SEARCH: boolean;
8
+ }
9
+ }
@@ -0,0 +1,25 @@
1
+ import type { LoaderArgs } from "@remix-run/server-runtime";
2
+
3
+ export const loader = (arg: LoaderArgs) => {
4
+ const host =
5
+ arg.request.headers.get("x-forwarded-host") ||
6
+ arg.request.headers.get("host") ||
7
+ "";
8
+
9
+ return new Response(
10
+ `
11
+ User-agent: *
12
+ Disallow: /api/
13
+
14
+ # Uncomment
15
+ # Sitemap: https://${host}/sitemap.xml
16
+
17
+ `,
18
+ {
19
+ headers: {
20
+ "Content-Type": "text/plain",
21
+ },
22
+ status: 200,
23
+ }
24
+ );
25
+ };
@@ -1,2 +1,3 @@
1
1
  /// <reference types="@remix-run/dev" />
2
2
  /// <reference types="@remix-run/node" />
3
+
@@ -4,9 +4,10 @@ import {
4
4
  type LinksFunction,
5
5
  type LinkDescriptor,
6
6
  type ActionArgs,
7
+ type LoaderArgs,
7
8
  json,
8
9
  } from "@remix-run/server-runtime";
9
- import type { Page as PageType } from "@webstudio-is/sdk";
10
+ import type { Page as PageType, SiteMeta } from "@webstudio-is/sdk";
10
11
  import { ReactSdkContext } from "@webstudio-is/react-sdk";
11
12
  import { n8nHandler, getFormId } from "@webstudio-is/form-handlers";
12
13
  import { Scripts, ScrollRestoration } from "@remix-run/react";
@@ -18,34 +19,122 @@ import {
18
19
  pagesPaths,
19
20
  formsProperties,
20
21
  Page,
22
+ imageAssets,
21
23
  } from "../__generated__/index";
22
24
  import css from "../__generated__/index.css";
23
25
  import { assetBaseUrl, imageBaseUrl, imageLoader } from "~/constants.mjs";
24
26
 
25
27
  export type PageData = {
28
+ site?: SiteMeta;
26
29
  page: PageType;
27
30
  };
28
31
 
29
- export const meta: V2_ServerRuntimeMetaFunction = () => {
30
- const { page } = pageData;
31
- const metas: ReturnType<V2_ServerRuntimeMetaFunction> = [
32
- { title: page?.title || "Webstudio" },
33
- ];
34
- for (const [name, value] of Object.entries(page?.meta ?? {})) {
35
- if (name.startsWith("og:")) {
36
- metas.push({
37
- property: name,
38
- content: value,
39
- });
40
- continue;
41
- }
32
+ export const loader = async (arg: LoaderArgs) => {
33
+ const host =
34
+ arg.request.headers.get("x-forwarded-host") ||
35
+ arg.request.headers.get("host") ||
36
+ "";
37
+
38
+ const url = new URL(arg.request.url);
39
+ url.host = host;
40
+ url.protocol = "https";
41
+
42
+ // typecheck
43
+ arg.context.EXCLUDE_FROM_SEARCH satisfies boolean;
44
+
45
+ return json(
46
+ {
47
+ host,
48
+ url: url.href,
49
+ excludeFromSearch: arg.context.EXCLUDE_FROM_SEARCH,
50
+ },
51
+ // No way for current information to change, so add cache for 10 minutes
52
+ // In case of CRM Data, this should be set to 0
53
+ { headers: { "Cache-Control": "public, max-age=600" } }
54
+ );
55
+ };
56
+
57
+ export const headers = () => {
58
+ return {
59
+ "Cache-Control": "public, max-age=0, must-revalidate",
60
+ };
61
+ };
62
+
63
+ export const meta: V2_ServerRuntimeMetaFunction<typeof loader> = ({ data }) => {
64
+ const { page, site } = pageData;
65
+
66
+ const metas: ReturnType<V2_ServerRuntimeMetaFunction> = [];
67
+
68
+ if (data?.url) {
69
+ metas.push({
70
+ property: "og:url",
71
+ content: data.url,
72
+ });
73
+ }
74
+
75
+ if (page.title) {
76
+ metas.push({ title: page.title });
42
77
 
43
78
  metas.push({
44
- name,
45
- content: value,
79
+ property: "og:title",
80
+ content: page.title,
46
81
  });
47
82
  }
48
83
 
84
+ metas.push({ property: "og:type", content: "website" });
85
+
86
+ const origin = `https://${data?.host}`;
87
+
88
+ if (site?.siteName) {
89
+ metas.push({
90
+ property: "og:site_name",
91
+ content: site.siteName,
92
+ });
93
+ metas.push({
94
+ "script:ld+json": {
95
+ "@context": "https://schema.org",
96
+ "@type": "WebSite",
97
+ name: site.siteName,
98
+ url: origin,
99
+ },
100
+ });
101
+ }
102
+
103
+ if (page.meta.excludePageFromSearch || data?.excludeFromSearch) {
104
+ metas.push({
105
+ name: "robots",
106
+ content: "noindex",
107
+ });
108
+ }
109
+
110
+ if (page.meta.description) {
111
+ metas.push({
112
+ name: "description",
113
+ content: page.meta.description,
114
+ });
115
+ metas.push({
116
+ property: "og:description",
117
+ content: page.meta.description,
118
+ });
119
+ }
120
+
121
+ if (page.meta.socialImageAssetId) {
122
+ const imageAsset = imageAssets.find(
123
+ (asset) => asset.id === page.meta.socialImageAssetId
124
+ );
125
+
126
+ if (imageAsset) {
127
+ metas.push({
128
+ property: "og:image",
129
+ content: `https://${data?.host}${imageLoader({
130
+ src: imageAsset.name,
131
+ // Do not transform social image (not enough information do we need to do this)
132
+ format: "raw",
133
+ })}`,
134
+ });
135
+ }
136
+ }
137
+
49
138
  return metas;
50
139
  };
51
140
 
@@ -57,6 +146,39 @@ export const links: LinksFunction = () => {
57
146
  href: css,
58
147
  });
59
148
 
149
+ const { site } = pageData;
150
+
151
+ if (site?.faviconAssetId) {
152
+ const imageAsset = imageAssets.find(
153
+ (asset) => asset.id === site.faviconAssetId
154
+ );
155
+
156
+ if (imageAsset) {
157
+ result.push({
158
+ rel: "icon",
159
+ href: imageLoader({
160
+ src: imageAsset.name,
161
+ width: 128,
162
+ quality: 100,
163
+ format: "auto",
164
+ }),
165
+ type: undefined,
166
+ });
167
+ }
168
+ } else {
169
+ result.push({
170
+ rel: "icon",
171
+ href: "/favicon.ico",
172
+ type: "image/x-icon",
173
+ });
174
+
175
+ result.push({
176
+ rel: "shortcut icon",
177
+ href: "/favicon.ico",
178
+ type: "image/x-icon",
179
+ });
180
+ }
181
+
60
182
  for (const asset of fontAssets) {
61
183
  if (asset.type === "font") {
62
184
  result.push({
@@ -8,18 +8,22 @@ export const imageBaseUrl = "/assets/";
8
8
  /**
9
9
  * @type {import("@webstudio-is/image").ImageLoader}
10
10
  */
11
- export const imageLoader = ({ quality, src, width }) => {
11
+ export const imageLoader = (props) => {
12
12
  if (process.env.NODE_ENV !== "production") {
13
- return imageBaseUrl + src;
13
+ return imageBaseUrl + props.src;
14
+ }
15
+
16
+ if (props.format === "raw") {
17
+ return imageBaseUrl + props.src;
14
18
  }
15
19
 
16
20
  // https://vercel.com/blog/build-your-own-web-framework#automatic-image-optimization
17
21
  return (
18
22
  "/_vercel/image?url=" +
19
- encodeURIComponent(imageBaseUrl + src) +
23
+ encodeURIComponent(imageBaseUrl + props.src) +
20
24
  "&w=" +
21
- width +
25
+ props.width +
22
26
  "&q=" +
23
- quality
27
+ props.quality
24
28
  );
25
29
  };