webstudio 0.238.0 → 0.252.2
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/README.md +2 -2
- package/lib/cli.js +280 -40
- package/package.json +14 -13
- package/templates/defaults/app/route-templates/html.tsx +22 -10
- package/templates/defaults/app/route-templates/xml.tsx +7 -0
- package/templates/defaults/package.json +9 -9
- package/templates/react-router/app/route-templates/html.tsx +22 -10
- package/templates/react-router/app/route-templates/xml.tsx +7 -0
- package/templates/react-router/package.json +9 -9
- package/templates/react-router-cloudflare/package.json +1 -1
- package/templates/ssg/app/route-templates/html/+Head.tsx +1 -41
- package/templates/ssg/app/route-templates/html/+Page.tsx +15 -3
- package/templates/ssg/app/route-templates/html/+data.ts +7 -9
- package/templates/ssg/package.json +8 -8
- package/templates/ssg/renderer/+onRenderClient.tsx +1 -5
package/README.md
CHANGED
|
@@ -16,10 +16,10 @@ To install Node.js using NVM, first install NVM by running:
|
|
|
16
16
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Once NVM is installed, you can install Node.js version
|
|
19
|
+
Once NVM is installed, you can install Node.js version 22 by running:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
nvm install
|
|
22
|
+
nvm install 22
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
Verify your Node.js installation by checking its version:
|
package/lib/cli.js
CHANGED
|
@@ -24,6 +24,7 @@ import { pathToFileURL, fileURLToPath } from "node:url";
|
|
|
24
24
|
import pLimit from "p-limit";
|
|
25
25
|
import merge from "deepmerge";
|
|
26
26
|
import hash from "@emotion/hash";
|
|
27
|
+
import "warn-once";
|
|
27
28
|
import { parseExpressionAt } from "acorn";
|
|
28
29
|
import { simple } from "acorn-walk";
|
|
29
30
|
import reservedIdentifiers from "reserved-identifiers";
|
|
@@ -1437,12 +1438,16 @@ var MediaRule = (_c = class {
|
|
|
1437
1438
|
return "";
|
|
1438
1439
|
}
|
|
1439
1440
|
let conditionText = "";
|
|
1440
|
-
const { minWidth, maxWidth } = this.options;
|
|
1441
|
-
if (
|
|
1442
|
-
conditionText = ` and (
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
|
-
|
|
1441
|
+
const { minWidth, maxWidth, condition } = this.options;
|
|
1442
|
+
if (condition !== void 0) {
|
|
1443
|
+
conditionText = ` and (${condition})`;
|
|
1444
|
+
} else {
|
|
1445
|
+
if (minWidth !== void 0) {
|
|
1446
|
+
conditionText = ` and (min-width: ${minWidth}px)`;
|
|
1447
|
+
}
|
|
1448
|
+
if (maxWidth !== void 0) {
|
|
1449
|
+
conditionText += ` and (max-width: ${maxWidth}px)`;
|
|
1450
|
+
}
|
|
1446
1451
|
}
|
|
1447
1452
|
return `@media ${__privateGet(this, _mediaType)}${conditionText} {
|
|
1448
1453
|
${rules.join(
|
|
@@ -1492,6 +1497,21 @@ var FontFaceRule = (_d = class {
|
|
|
1492
1497
|
}
|
|
1493
1498
|
}, _cached = new WeakMap(), _options = new WeakMap(), _d);
|
|
1494
1499
|
var compareMedia = (optionA, optionB) => {
|
|
1500
|
+
if (optionA.condition !== void 0 && optionB.condition !== void 0) {
|
|
1501
|
+
return optionA.condition.localeCompare(optionB.condition);
|
|
1502
|
+
}
|
|
1503
|
+
if (optionA.condition !== void 0) {
|
|
1504
|
+
if (optionB.minWidth === void 0 && optionB.maxWidth === void 0) {
|
|
1505
|
+
return 1;
|
|
1506
|
+
}
|
|
1507
|
+
return -1;
|
|
1508
|
+
}
|
|
1509
|
+
if (optionB.condition !== void 0) {
|
|
1510
|
+
if (optionA.minWidth === void 0 && optionA.maxWidth === void 0) {
|
|
1511
|
+
return -1;
|
|
1512
|
+
}
|
|
1513
|
+
return 1;
|
|
1514
|
+
}
|
|
1495
1515
|
if (optionA.minWidth === void 0 && optionA.maxWidth === void 0) {
|
|
1496
1516
|
return -1;
|
|
1497
1517
|
}
|
|
@@ -1660,7 +1680,7 @@ var StyleSheet = (_f = class {
|
|
|
1660
1680
|
var StyleSheetRegular = class extends StyleSheet {
|
|
1661
1681
|
};
|
|
1662
1682
|
var createRegularStyleSheet = (options) => {
|
|
1663
|
-
const element = new StyleElement(options == null ? void 0 : options.name);
|
|
1683
|
+
const element = (options == null ? void 0 : options.element) ?? new StyleElement(options == null ? void 0 : options.name);
|
|
1664
1684
|
return new StyleSheetRegular(element);
|
|
1665
1685
|
};
|
|
1666
1686
|
var generateAtomic = (sheet, options) => {
|
|
@@ -1783,7 +1803,13 @@ var ImageAsset = z.object({
|
|
|
1783
1803
|
meta: ImageMeta,
|
|
1784
1804
|
type: z.literal("image")
|
|
1785
1805
|
});
|
|
1786
|
-
var
|
|
1806
|
+
var FileAsset = z.object({
|
|
1807
|
+
...baseAsset,
|
|
1808
|
+
format: z.string(),
|
|
1809
|
+
meta: z.object({}),
|
|
1810
|
+
type: z.literal("file")
|
|
1811
|
+
});
|
|
1812
|
+
var Asset = z.union([FontAsset, ImageAsset, FileAsset]);
|
|
1787
1813
|
z.map(AssetId, Asset);
|
|
1788
1814
|
var MIN_TITLE_LENGTH = 2;
|
|
1789
1815
|
var PageId = z.string();
|
|
@@ -2303,14 +2329,23 @@ var Breakpoint = z.object({
|
|
|
2303
2329
|
id: BreakpointId,
|
|
2304
2330
|
label: z.string(),
|
|
2305
2331
|
minWidth: z.number().optional(),
|
|
2306
|
-
maxWidth: z.number().optional()
|
|
2307
|
-
|
|
2332
|
+
maxWidth: z.number().optional(),
|
|
2333
|
+
condition: z.string().optional()
|
|
2334
|
+
}).transform((data) => {
|
|
2335
|
+
if (data.condition !== void 0 && data.condition.trim() === "") {
|
|
2336
|
+
return { ...data, condition: void 0 };
|
|
2337
|
+
}
|
|
2338
|
+
return data;
|
|
2339
|
+
}).refine(({ minWidth, maxWidth, condition }) => {
|
|
2340
|
+
if (condition !== void 0) {
|
|
2341
|
+
return minWidth === void 0 && maxWidth === void 0;
|
|
2342
|
+
}
|
|
2308
2343
|
return (
|
|
2309
2344
|
// Either min or max width have to be defined
|
|
2310
2345
|
minWidth !== void 0 && maxWidth === void 0 || minWidth === void 0 && maxWidth !== void 0 || // This is a base breakpoint
|
|
2311
2346
|
minWidth === void 0 && maxWidth === void 0
|
|
2312
2347
|
);
|
|
2313
|
-
}, "Either minWidth or
|
|
2348
|
+
}, "Either minWidth, maxWidth, or condition should be defined, but not both");
|
|
2314
2349
|
z.map(BreakpointId, Breakpoint);
|
|
2315
2350
|
var StyleSourceId = z.string();
|
|
2316
2351
|
var StyleSourceToken = z.object({
|
|
@@ -2617,6 +2652,170 @@ z.object({
|
|
|
2617
2652
|
initialProps: z.array(z.string()).optional(),
|
|
2618
2653
|
props: z.record(PropMeta).optional()
|
|
2619
2654
|
});
|
|
2655
|
+
var ALLOWED_FILE_TYPES = {
|
|
2656
|
+
// Documents
|
|
2657
|
+
pdf: "application/pdf",
|
|
2658
|
+
doc: "application/msword",
|
|
2659
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
2660
|
+
xls: "application/vnd.ms-excel",
|
|
2661
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
2662
|
+
csv: "text/csv",
|
|
2663
|
+
ppt: "application/vnd.ms-powerpoint",
|
|
2664
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
2665
|
+
// Code
|
|
2666
|
+
txt: "text/plain",
|
|
2667
|
+
md: "text/markdown",
|
|
2668
|
+
js: "text/javascript",
|
|
2669
|
+
css: "text/css",
|
|
2670
|
+
json: "application/json",
|
|
2671
|
+
html: "text/html",
|
|
2672
|
+
xml: "application/xml",
|
|
2673
|
+
// Archives
|
|
2674
|
+
zip: "application/zip",
|
|
2675
|
+
rar: "application/vnd.rar",
|
|
2676
|
+
// Audio
|
|
2677
|
+
mp3: "audio/mpeg",
|
|
2678
|
+
wav: "audio/wav",
|
|
2679
|
+
ogg: "audio/ogg",
|
|
2680
|
+
m4a: "audio/mp4",
|
|
2681
|
+
// Video
|
|
2682
|
+
mp4: "video/mp4",
|
|
2683
|
+
mov: "video/quicktime",
|
|
2684
|
+
avi: "video/x-msvideo",
|
|
2685
|
+
webm: "video/webm",
|
|
2686
|
+
// Images
|
|
2687
|
+
// Note: Cloudflare Image Resizing supports: jpg, jpeg, png, gif, webp, svg, avif
|
|
2688
|
+
// Other formats (bmp, ico, tif, tiff) are served as-is without optimization
|
|
2689
|
+
jpg: "image/jpeg",
|
|
2690
|
+
jpeg: "image/jpeg",
|
|
2691
|
+
png: "image/png",
|
|
2692
|
+
gif: "image/gif",
|
|
2693
|
+
svg: "image/svg+xml",
|
|
2694
|
+
webp: "image/webp",
|
|
2695
|
+
avif: "image/avif",
|
|
2696
|
+
ico: "image/vnd.microsoft.icon",
|
|
2697
|
+
// Used for favicons
|
|
2698
|
+
bmp: "image/bmp",
|
|
2699
|
+
// Served without optimization
|
|
2700
|
+
tif: "image/tiff",
|
|
2701
|
+
// Served without optimization
|
|
2702
|
+
tiff: "image/tiff",
|
|
2703
|
+
// Served without optimization
|
|
2704
|
+
// Fonts
|
|
2705
|
+
woff: "font/woff",
|
|
2706
|
+
woff2: "font/woff2",
|
|
2707
|
+
ttf: "font/ttf",
|
|
2708
|
+
otf: "font/otf"
|
|
2709
|
+
};
|
|
2710
|
+
new Set(
|
|
2711
|
+
Object.keys(ALLOWED_FILE_TYPES)
|
|
2712
|
+
);
|
|
2713
|
+
var MIME_CATEGORIES = [
|
|
2714
|
+
"image",
|
|
2715
|
+
"video",
|
|
2716
|
+
"audio",
|
|
2717
|
+
"font",
|
|
2718
|
+
"text",
|
|
2719
|
+
"application"
|
|
2720
|
+
];
|
|
2721
|
+
var FILE_EXTENSIONS_BY_CATEGORY = (() => {
|
|
2722
|
+
const categories = Object.fromEntries(
|
|
2723
|
+
MIME_CATEGORIES.map((category) => [category, []])
|
|
2724
|
+
);
|
|
2725
|
+
Object.entries(ALLOWED_FILE_TYPES).forEach(([ext, mimeType]) => {
|
|
2726
|
+
const [category] = mimeType.split("/");
|
|
2727
|
+
if (category in categories) {
|
|
2728
|
+
categories[category].push(ext);
|
|
2729
|
+
}
|
|
2730
|
+
});
|
|
2731
|
+
return categories;
|
|
2732
|
+
})();
|
|
2733
|
+
var extensionToMime = new Map(
|
|
2734
|
+
Object.entries(ALLOWED_FILE_TYPES).map(([ext, mime]) => [`.${ext}`, mime])
|
|
2735
|
+
);
|
|
2736
|
+
var mimeTypes = new Set(extensionToMime.values());
|
|
2737
|
+
/* @__PURE__ */ new Set([
|
|
2738
|
+
...mimeTypes.values(),
|
|
2739
|
+
...MIME_CATEGORIES.map((category) => `${category}/*`)
|
|
2740
|
+
]);
|
|
2741
|
+
var IMAGE_EXTENSIONS = FILE_EXTENSIONS_BY_CATEGORY.image;
|
|
2742
|
+
IMAGE_EXTENSIONS.map(
|
|
2743
|
+
(ext) => ALLOWED_FILE_TYPES[ext]
|
|
2744
|
+
);
|
|
2745
|
+
var VIDEO_EXTENSIONS = FILE_EXTENSIONS_BY_CATEGORY.video;
|
|
2746
|
+
VIDEO_EXTENSIONS.map(
|
|
2747
|
+
(ext) => ALLOWED_FILE_TYPES[ext]
|
|
2748
|
+
);
|
|
2749
|
+
var FONT_EXTENSIONS = FILE_EXTENSIONS_BY_CATEGORY.font;
|
|
2750
|
+
var detectAssetType = (fileName) => {
|
|
2751
|
+
var _a2;
|
|
2752
|
+
const ext = (_a2 = fileName.split(".").pop()) == null ? void 0 : _a2.toLowerCase();
|
|
2753
|
+
if (!ext) {
|
|
2754
|
+
return "file";
|
|
2755
|
+
}
|
|
2756
|
+
if (IMAGE_EXTENSIONS.includes(ext)) {
|
|
2757
|
+
return "image";
|
|
2758
|
+
}
|
|
2759
|
+
if (FONT_EXTENSIONS.includes(ext)) {
|
|
2760
|
+
return "font";
|
|
2761
|
+
}
|
|
2762
|
+
if (VIDEO_EXTENSIONS.includes(ext)) {
|
|
2763
|
+
return "video";
|
|
2764
|
+
}
|
|
2765
|
+
return "file";
|
|
2766
|
+
};
|
|
2767
|
+
var getAssetUrl = (asset, origin) => {
|
|
2768
|
+
let path;
|
|
2769
|
+
const assetType = detectAssetType(asset.name);
|
|
2770
|
+
if (assetType === "image") {
|
|
2771
|
+
path = `/cgi/image/${asset.name}?format=raw`;
|
|
2772
|
+
} else {
|
|
2773
|
+
path = `/cgi/asset/${asset.name}?format=raw`;
|
|
2774
|
+
}
|
|
2775
|
+
return new URL(path, origin);
|
|
2776
|
+
};
|
|
2777
|
+
var extractImageMetadata = (asset) => {
|
|
2778
|
+
if (asset.type !== "image") {
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
if (asset.meta.width && asset.meta.height) {
|
|
2782
|
+
return {
|
|
2783
|
+
width: asset.meta.width,
|
|
2784
|
+
height: asset.meta.height
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
var extractFontMetadata = (asset) => {
|
|
2789
|
+
if (asset.type !== "font") {
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const metadata = {
|
|
2793
|
+
family: asset.meta.family
|
|
2794
|
+
};
|
|
2795
|
+
if ("style" in asset.meta) {
|
|
2796
|
+
metadata.style = asset.meta.style;
|
|
2797
|
+
metadata.weight = asset.meta.weight;
|
|
2798
|
+
}
|
|
2799
|
+
return metadata;
|
|
2800
|
+
};
|
|
2801
|
+
var extractFileMetadata = (_asset) => {
|
|
2802
|
+
return;
|
|
2803
|
+
};
|
|
2804
|
+
var metadataExtractors = {
|
|
2805
|
+
image: extractImageMetadata,
|
|
2806
|
+
font: extractFontMetadata,
|
|
2807
|
+
file: extractFileMetadata
|
|
2808
|
+
};
|
|
2809
|
+
var toRuntimeAsset = (asset, origin) => {
|
|
2810
|
+
const extractor = metadataExtractors[asset.type];
|
|
2811
|
+
const metadata = extractor(asset);
|
|
2812
|
+
const url = getAssetUrl(asset, origin);
|
|
2813
|
+
const relativeUrl = url.pathname + url.search;
|
|
2814
|
+
return {
|
|
2815
|
+
url: relativeUrl,
|
|
2816
|
+
...metadata
|
|
2817
|
+
};
|
|
2818
|
+
};
|
|
2620
2819
|
var normalize_css_exports = {};
|
|
2621
2820
|
__export(normalize_css_exports, {
|
|
2622
2821
|
a: () => a$7,
|
|
@@ -3171,6 +3370,16 @@ var collectionMeta = {
|
|
|
3171
3370
|
required: true,
|
|
3172
3371
|
control: "json",
|
|
3173
3372
|
type: "json"
|
|
3373
|
+
},
|
|
3374
|
+
item: {
|
|
3375
|
+
required: false,
|
|
3376
|
+
control: "text",
|
|
3377
|
+
type: "string"
|
|
3378
|
+
},
|
|
3379
|
+
itemKey: {
|
|
3380
|
+
required: false,
|
|
3381
|
+
control: "text",
|
|
3382
|
+
type: "string"
|
|
3174
3383
|
}
|
|
3175
3384
|
}
|
|
3176
3385
|
};
|
|
@@ -4199,6 +4408,18 @@ var isAttributeNameSafe = (attributeName) => {
|
|
|
4199
4408
|
illegalAttributeNameCache.set(attributeName, true);
|
|
4200
4409
|
return false;
|
|
4201
4410
|
};
|
|
4411
|
+
var generateCollectionIterationCode = ({
|
|
4412
|
+
dataExpression,
|
|
4413
|
+
keyVariable,
|
|
4414
|
+
itemVariable
|
|
4415
|
+
}) => {
|
|
4416
|
+
return `Object.entries(
|
|
4417
|
+
// @ts-ignore
|
|
4418
|
+
${dataExpression} ?? {}
|
|
4419
|
+
).map(([_key, ${itemVariable}]: any) => {
|
|
4420
|
+
const ${keyVariable} = Array.isArray(${dataExpression}) ? Number(_key) : _key;
|
|
4421
|
+
return`;
|
|
4422
|
+
};
|
|
4202
4423
|
var standardAttributesToReactProps = {
|
|
4203
4424
|
"accept-charset": "acceptCharset",
|
|
4204
4425
|
accesskey: "accessKey",
|
|
@@ -4427,6 +4648,7 @@ ${tagProperty}=${JSON.stringify(instance.tag)}`;
|
|
|
4427
4648
|
let conditionValue;
|
|
4428
4649
|
let collectionDataValue;
|
|
4429
4650
|
let collectionItemValue;
|
|
4651
|
+
let collectionItemKeyValue;
|
|
4430
4652
|
let classNameValue;
|
|
4431
4653
|
for (const prop of props.values()) {
|
|
4432
4654
|
if (prop.instanceId !== instance.id) {
|
|
@@ -4462,6 +4684,9 @@ ${tagProperty}=${JSON.stringify(instance.tag)}`;
|
|
|
4462
4684
|
if (prop.name === "item") {
|
|
4463
4685
|
collectionItemValue = propValue;
|
|
4464
4686
|
}
|
|
4687
|
+
if (prop.name === "itemKey") {
|
|
4688
|
+
collectionItemKeyValue = propValue;
|
|
4689
|
+
}
|
|
4465
4690
|
continue;
|
|
4466
4691
|
}
|
|
4467
4692
|
if (name2 === "className" && propValue !== void 0) {
|
|
@@ -4493,14 +4718,23 @@ ${name2}={${propValue}}`;
|
|
|
4493
4718
|
return "";
|
|
4494
4719
|
}
|
|
4495
4720
|
const indexVariable = scope.getName(`${instance.id}-index`, "index");
|
|
4496
|
-
|
|
4721
|
+
const keyVariable = collectionItemKeyValue ?? indexVariable;
|
|
4722
|
+
generatedElement += `{${generateCollectionIterationCode({
|
|
4723
|
+
dataExpression: collectionDataValue,
|
|
4724
|
+
keyVariable,
|
|
4725
|
+
itemVariable: collectionItemValue
|
|
4726
|
+
})} (
|
|
4497
4727
|
`;
|
|
4498
|
-
generatedElement += `<Fragment key={${
|
|
4728
|
+
generatedElement += `<Fragment key={${keyVariable}}>
|
|
4499
4729
|
`;
|
|
4500
4730
|
generatedElement += children;
|
|
4501
4731
|
generatedElement += `</Fragment>
|
|
4502
4732
|
`;
|
|
4503
|
-
generatedElement += `)
|
|
4733
|
+
generatedElement += `)
|
|
4734
|
+
`;
|
|
4735
|
+
generatedElement += `})
|
|
4736
|
+
`;
|
|
4737
|
+
generatedElement += `}
|
|
4504
4738
|
`;
|
|
4505
4739
|
} else if (instance.component === blockComponent) {
|
|
4506
4740
|
generatedElement += children;
|
|
@@ -5437,7 +5671,8 @@ const o$v = {
|
|
|
5437
5671
|
type: "string",
|
|
5438
5672
|
control: "file",
|
|
5439
5673
|
label: "Source",
|
|
5440
|
-
required: false
|
|
5674
|
+
required: false,
|
|
5675
|
+
accept: "image/*"
|
|
5441
5676
|
}
|
|
5442
5677
|
}
|
|
5443
5678
|
};
|
|
@@ -8358,29 +8593,19 @@ Please check webstudio --help for more details`
|
|
|
8358
8593
|
const assetsToDownload = [];
|
|
8359
8594
|
if (options.assets === true) {
|
|
8360
8595
|
const assetOrigin = siteData.origin;
|
|
8596
|
+
if (!assetOrigin) {
|
|
8597
|
+
console.warn("Warning: Asset origin is not defined in project data.");
|
|
8598
|
+
}
|
|
8361
8599
|
for (const asset of siteData.assets) {
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
(
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
assetBaseUrl
|
|
8369
|
-
)
|
|
8370
|
-
)
|
|
8371
|
-
);
|
|
8372
|
-
}
|
|
8373
|
-
if (asset.type === "font") {
|
|
8374
|
-
assetsToDownload.push(
|
|
8375
|
-
limit(
|
|
8376
|
-
() => downloadAsset(
|
|
8377
|
-
`${assetOrigin}/cgi/asset/${asset.name}`,
|
|
8378
|
-
asset.name,
|
|
8379
|
-
assetBaseUrl
|
|
8380
|
-
)
|
|
8600
|
+
assetsToDownload.push(
|
|
8601
|
+
limit(
|
|
8602
|
+
() => downloadAsset(
|
|
8603
|
+
getAssetUrl(asset, assetOrigin || "").href,
|
|
8604
|
+
asset.name,
|
|
8605
|
+
assetBaseUrl
|
|
8381
8606
|
)
|
|
8382
|
-
)
|
|
8383
|
-
|
|
8607
|
+
)
|
|
8608
|
+
);
|
|
8384
8609
|
}
|
|
8385
8610
|
}
|
|
8386
8611
|
const assets = new Map(siteData.assets.map((asset) => [asset.id, asset]));
|
|
@@ -8590,6 +8815,9 @@ Please check webstudio --help for more details`
|
|
|
8590
8815
|
const content = template.replaceAll("__CONSTANTS__", importFrom("./app/constants.mjs", file)).replaceAll(
|
|
8591
8816
|
"__SITEMAP__",
|
|
8592
8817
|
importFrom(`./app/__generated__/$resources.sitemap.xml`, file)
|
|
8818
|
+
).replaceAll(
|
|
8819
|
+
"__ASSETS__",
|
|
8820
|
+
importFrom(`./app/__generated__/$resources.assets`, file)
|
|
8593
8821
|
).replaceAll(
|
|
8594
8822
|
"__CLIENT__",
|
|
8595
8823
|
importFrom(`./app/__generated__/${generatedBasename}`, file)
|
|
@@ -8620,6 +8848,18 @@ Please check webstudio --help for more details`
|
|
|
8620
8848
|
)};
|
|
8621
8849
|
`
|
|
8622
8850
|
);
|
|
8851
|
+
const assetsById = Object.fromEntries(
|
|
8852
|
+
siteData.assets.map((asset) => [
|
|
8853
|
+
asset.id,
|
|
8854
|
+
toRuntimeAsset(asset, "https://placeholder.local")
|
|
8855
|
+
])
|
|
8856
|
+
);
|
|
8857
|
+
await createFileIfNotExists(
|
|
8858
|
+
join(generatedDir, "$resources.assets.ts"),
|
|
8859
|
+
`
|
|
8860
|
+
export const assets = ${JSON.stringify(assetsById, null, 2)};
|
|
8861
|
+
`
|
|
8862
|
+
);
|
|
8623
8863
|
const redirects = (_k = siteData.build.pages) == null ? void 0 : _k.redirects;
|
|
8624
8864
|
if (redirects !== void 0 && redirects.length > 0) {
|
|
8625
8865
|
for (const redirect of redirects) {
|
|
@@ -8795,7 +9035,7 @@ const getDeploymentInstructions = (deployTarget) => {
|
|
|
8795
9035
|
}
|
|
8796
9036
|
};
|
|
8797
9037
|
const name = "webstudio";
|
|
8798
|
-
const version = "0.
|
|
9038
|
+
const version = "0.252.2";
|
|
8799
9039
|
const description = "Webstudio CLI";
|
|
8800
9040
|
const author = "Webstudio <github@webstudio.is>";
|
|
8801
9041
|
const homepage = "https://webstudio.is";
|
|
@@ -8803,10 +9043,10 @@ const type = "module";
|
|
|
8803
9043
|
const bin = { "webstudio-cli": "./bin.js", "webstudio": "./bin.js" };
|
|
8804
9044
|
const imports = { "#cli": { "webstudio": "./src/cli.ts", "default": "./lib/cli.js" } };
|
|
8805
9045
|
const files = ["lib/*", "templates/*", "bin.js", "!*.{test,stories}.*"];
|
|
8806
|
-
const scripts = { "typecheck": "
|
|
9046
|
+
const scripts = { "typecheck": "tsgo --noEmit", "build": "rm -rf lib && vite build", "test": "vitest run" };
|
|
8807
9047
|
const license = "AGPL-3.0-or-later";
|
|
8808
|
-
const engines = { "node": ">=
|
|
8809
|
-
const dependencies = { "@clack/prompts": "^0.10.0", "@emotion/hash": "^0.9.2", "acorn": "^8.14.1", "acorn-walk": "^8.3.4", "change-case": "^5.4.4", "deepmerge": "^4.3.1", "env-paths": "^3.0.0", "nanoid": "^5.1.5", "p-limit": "^6.2.0", "parse5": "7.3.0", "picocolors": "^1.1.1", "reserved-identifiers": "^1.0.0", "tinyexec": "^0.3.2", "yargs": "^17.7.2", "zod": "^3.24.2" };
|
|
9048
|
+
const engines = { "node": ">=22" };
|
|
9049
|
+
const dependencies = { "@clack/prompts": "^0.10.0", "@emotion/hash": "^0.9.2", "acorn": "^8.14.1", "acorn-walk": "^8.3.4", "change-case": "^5.4.4", "deepmerge": "^4.3.1", "env-paths": "^3.0.0", "nanoid": "^5.1.5", "p-limit": "^6.2.0", "parse5": "7.3.0", "picocolors": "^1.1.1", "reserved-identifiers": "^1.0.0", "tinyexec": "^0.3.2", "warn-once": "^0.1.1", "yargs": "^17.7.2", "zod": "^3.24.2" };
|
|
8810
9050
|
const devDependencies = { "@cloudflare/vite-plugin": "^1.1.0", "@netlify/vite-plugin-react-router": "^1.0.1", "@react-router/dev": "^7.5.3", "@react-router/fs-routes": "^7.5.3", "@remix-run/cloudflare": "^2.16.5", "@remix-run/cloudflare-pages": "^2.16.5", "@remix-run/dev": "^2.16.5", "@remix-run/node": "^2.16.5", "@remix-run/react": "^2.16.5", "@remix-run/server-runtime": "^2.16.5", "@types/react": "^18.2.70", "@types/react-dom": "^18.2.25", "@types/yargs": "^17.0.33", "@vercel/react-router": "^1.1.0", "@vitejs/plugin-react": "^4.4.1", "@webstudio-is/css-engine": "workspace:*", "@webstudio-is/http-client": "workspace:*", "@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-remix": "workspace:*", "@webstudio-is/sdk-components-react-router": "workspace:*", "@webstudio-is/tsconfig": "workspace:*", "h3": "^1.15.1", "ipx": "^3.0.3", "isbot": "^5.1.25", "prettier": "3.5.3", "react": "18.3.0-canary-14898b6a9-20240318", "react-dom": "18.3.0-canary-14898b6a9-20240318", "react-router": "^7.5.3", "ts-expect": "^1.3.0", "vike": "^0.4.229", "vite": "^6.3.4", "vitest": "^3.1.2", "wrangler": "^3.63.2" };
|
|
8811
9051
|
const packageJson = {
|
|
8812
9052
|
name,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webstudio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.252.2",
|
|
4
4
|
"description": "Webstudio CLI",
|
|
5
5
|
"author": "Webstudio <github@webstudio.is>",
|
|
6
6
|
"homepage": "https://webstudio.is",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"license": "AGPL-3.0-or-later",
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
26
|
+
"node": ">=22"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@clack/prompts": "^0.10.0",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"picocolors": "^1.1.1",
|
|
40
40
|
"reserved-identifiers": "^1.0.0",
|
|
41
41
|
"tinyexec": "^0.3.2",
|
|
42
|
+
"warn-once": "^0.1.1",
|
|
42
43
|
"yargs": "^17.7.2",
|
|
43
44
|
"zod": "^3.24.2"
|
|
44
45
|
},
|
|
@@ -70,20 +71,20 @@
|
|
|
70
71
|
"vite": "^6.3.4",
|
|
71
72
|
"vitest": "^3.1.2",
|
|
72
73
|
"wrangler": "^3.63.2",
|
|
73
|
-
"@webstudio-is/css-engine": "0.
|
|
74
|
-
"@webstudio-is/http-client": "0.
|
|
75
|
-
"@webstudio-is/
|
|
76
|
-
"@webstudio-is/
|
|
77
|
-
"@webstudio-is/
|
|
78
|
-
"@webstudio-is/sdk-components-animation": "0.
|
|
79
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
80
|
-
"@webstudio-is/sdk-components-react-
|
|
81
|
-
"@webstudio-is/sdk-components-react
|
|
82
|
-
"@webstudio-is/sdk-components-react-
|
|
74
|
+
"@webstudio-is/css-engine": "0.252.2",
|
|
75
|
+
"@webstudio-is/http-client": "0.252.2",
|
|
76
|
+
"@webstudio-is/image": "0.252.2",
|
|
77
|
+
"@webstudio-is/sdk": "0.252.2",
|
|
78
|
+
"@webstudio-is/react-sdk": "0.252.2",
|
|
79
|
+
"@webstudio-is/sdk-components-animation": "0.252.2",
|
|
80
|
+
"@webstudio-is/sdk-components-react-radix": "0.252.2",
|
|
81
|
+
"@webstudio-is/sdk-components-react-remix": "0.252.2",
|
|
82
|
+
"@webstudio-is/sdk-components-react": "0.252.2",
|
|
83
|
+
"@webstudio-is/sdk-components-react-router": "0.252.2",
|
|
83
84
|
"@webstudio-is/tsconfig": "1.0.7"
|
|
84
85
|
},
|
|
85
86
|
"scripts": {
|
|
86
|
-
"typecheck": "
|
|
87
|
+
"typecheck": "tsgo --noEmit",
|
|
87
88
|
"build": "rm -rf lib && vite build",
|
|
88
89
|
"test": "vitest run"
|
|
89
90
|
}
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
import * as constants from "__CONSTANTS__";
|
|
42
42
|
import css from "__CSS__?url";
|
|
43
43
|
import { sitemap } from "__SITEMAP__";
|
|
44
|
+
import { assets } from "__ASSETS__";
|
|
44
45
|
|
|
45
46
|
const customFetch: typeof fetch = (input, init) => {
|
|
46
47
|
if (typeof input !== "string") {
|
|
@@ -72,6 +73,12 @@ const customFetch: typeof fetch = (input, init) => {
|
|
|
72
73
|
return Promise.resolve(response);
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
if (isLocalResource(input, "assets")) {
|
|
77
|
+
const response = new Response(JSON.stringify(assets));
|
|
78
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
79
|
+
return Promise.resolve(response);
|
|
80
|
+
}
|
|
81
|
+
|
|
75
82
|
return cachedFetch(projectId, input, init);
|
|
76
83
|
};
|
|
77
84
|
|
|
@@ -238,16 +245,21 @@ export const action = async ({
|
|
|
238
245
|
throw new Error("Form bot field not found");
|
|
239
246
|
}
|
|
240
247
|
|
|
241
|
-
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
// Skip timestamp validation for Brave browser
|
|
249
|
+
// Brave Shields blocks matchMedia fingerprinting detection used in bot protection
|
|
250
|
+
// See: https://github.com/brave/brave-browser/issues/46541
|
|
251
|
+
if (formBotValue !== "brave") {
|
|
252
|
+
const submitTime = parseInt(formBotValue, 16);
|
|
253
|
+
// Assumes that the difference between the server time and the form submission time,
|
|
254
|
+
// including any client-server time drift, is within a 5-minute range.
|
|
255
|
+
// Note: submitTime might be NaN because formBotValue can be any string used for logging purposes.
|
|
256
|
+
// Example: `formBotValue: jsdom`, or `formBotValue: headless-env`
|
|
257
|
+
if (
|
|
258
|
+
Number.isNaN(submitTime) ||
|
|
259
|
+
Math.abs(Date.now() - submitTime) > 1000 * 60 * 5
|
|
260
|
+
) {
|
|
261
|
+
throw new Error(`Form bot value invalid ${formBotValue}`);
|
|
262
|
+
}
|
|
251
263
|
}
|
|
252
264
|
|
|
253
265
|
formData.delete(formIdFieldName);
|
|
@@ -9,6 +9,7 @@ import { Page, breakpoints } from "__CLIENT__";
|
|
|
9
9
|
import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
|
|
10
10
|
import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
|
|
11
11
|
import { sitemap } from "__SITEMAP__";
|
|
12
|
+
import { assets } from "__ASSETS__";
|
|
12
13
|
|
|
13
14
|
const customFetch: typeof fetch = (input, init) => {
|
|
14
15
|
if (typeof input !== "string") {
|
|
@@ -40,6 +41,12 @@ const customFetch: typeof fetch = (input, init) => {
|
|
|
40
41
|
return Promise.resolve(response);
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
if (isLocalResource(input, "assets")) {
|
|
45
|
+
const response = new Response(JSON.stringify(assets));
|
|
46
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
47
|
+
return Promise.resolve(response);
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
return fetch(input, init);
|
|
44
51
|
};
|
|
45
52
|
|
|
@@ -5,19 +5,19 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "remix vite:build",
|
|
7
7
|
"dev": "remix vite:dev",
|
|
8
|
-
"typecheck": "
|
|
8
|
+
"typecheck": "tsgo --noEmit"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@remix-run/node": "2.16.5",
|
|
12
12
|
"@remix-run/react": "2.16.5",
|
|
13
13
|
"@remix-run/server-runtime": "2.16.5",
|
|
14
|
-
"@webstudio-is/image": "0.
|
|
15
|
-
"@webstudio-is/react-sdk": "0.
|
|
16
|
-
"@webstudio-is/sdk": "0.
|
|
17
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
18
|
-
"@webstudio-is/sdk-components-animation": "0.
|
|
19
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
20
|
-
"@webstudio-is/sdk-components-react-remix": "0.
|
|
14
|
+
"@webstudio-is/image": "0.252.2",
|
|
15
|
+
"@webstudio-is/react-sdk": "0.252.2",
|
|
16
|
+
"@webstudio-is/sdk": "0.252.2",
|
|
17
|
+
"@webstudio-is/sdk-components-react": "0.252.2",
|
|
18
|
+
"@webstudio-is/sdk-components-animation": "0.252.2",
|
|
19
|
+
"@webstudio-is/sdk-components-react-radix": "0.252.2",
|
|
20
|
+
"@webstudio-is/sdk-components-react-remix": "0.252.2",
|
|
21
21
|
"isbot": "^5.1.25",
|
|
22
22
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
23
23
|
"react-dom": "18.3.0-canary-14898b6a9-20240318"
|
|
@@ -30,6 +30,6 @@
|
|
|
30
30
|
"vite": "^6.3.4"
|
|
31
31
|
},
|
|
32
32
|
"engines": {
|
|
33
|
-
"node": ">=
|
|
33
|
+
"node": ">=22"
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
import * as constants from "__CONSTANTS__";
|
|
41
41
|
import css from "__CSS__?url";
|
|
42
42
|
import { sitemap } from "__SITEMAP__";
|
|
43
|
+
import { assets } from "__ASSETS__";
|
|
43
44
|
|
|
44
45
|
const customFetch: typeof fetch = (input, init) => {
|
|
45
46
|
if (typeof input !== "string") {
|
|
@@ -71,6 +72,12 @@ const customFetch: typeof fetch = (input, init) => {
|
|
|
71
72
|
return Promise.resolve(response);
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
if (isLocalResource(input, "assets")) {
|
|
76
|
+
const response = new Response(JSON.stringify(assets));
|
|
77
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
78
|
+
return Promise.resolve(response);
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
return cachedFetch(projectId, input, init);
|
|
75
82
|
};
|
|
76
83
|
|
|
@@ -237,16 +244,21 @@ export const action = async ({
|
|
|
237
244
|
throw new Error("Form bot field not found");
|
|
238
245
|
}
|
|
239
246
|
|
|
240
|
-
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
// Skip timestamp validation for Brave browser
|
|
248
|
+
// Brave Shields blocks matchMedia fingerprinting detection used in bot protection
|
|
249
|
+
// See: https://github.com/brave/brave-browser/issues/46541
|
|
250
|
+
if (formBotValue !== "brave") {
|
|
251
|
+
const submitTime = parseInt(formBotValue, 16);
|
|
252
|
+
// Assumes that the difference between the server time and the form submission time,
|
|
253
|
+
// including any client-server time drift, is within a 5-minute range.
|
|
254
|
+
// Note: submitTime might be NaN because formBotValue can be any string used for logging purposes.
|
|
255
|
+
// Example: `formBotValue: jsdom`, or `formBotValue: headless-env`
|
|
256
|
+
if (
|
|
257
|
+
Number.isNaN(submitTime) ||
|
|
258
|
+
Math.abs(Date.now() - submitTime) > 1000 * 60 * 5
|
|
259
|
+
) {
|
|
260
|
+
throw new Error(`Form bot value invalid ${formBotValue}`);
|
|
261
|
+
}
|
|
250
262
|
}
|
|
251
263
|
|
|
252
264
|
formData.delete(formIdFieldName);
|
|
@@ -9,6 +9,7 @@ import { Page, breakpoints } from "__CLIENT__";
|
|
|
9
9
|
import { getPageMeta, getRemixParams, getResources } from "__SERVER__";
|
|
10
10
|
import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
|
|
11
11
|
import { sitemap } from "__SITEMAP__";
|
|
12
|
+
import { assets } from "__ASSETS__";
|
|
12
13
|
|
|
13
14
|
const customFetch: typeof fetch = (input, init) => {
|
|
14
15
|
if (typeof input !== "string") {
|
|
@@ -40,6 +41,12 @@ const customFetch: typeof fetch = (input, init) => {
|
|
|
40
41
|
return Promise.resolve(response);
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
if (isLocalResource(input, "assets")) {
|
|
45
|
+
const response = new Response(JSON.stringify(assets));
|
|
46
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
47
|
+
return Promise.resolve(response);
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
return fetch(input, init);
|
|
44
51
|
};
|
|
45
52
|
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "react-router build",
|
|
7
7
|
"dev": "react-router dev",
|
|
8
|
-
"typecheck": "
|
|
8
|
+
"typecheck": "tsgo --noEmit"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@react-router/dev": "^7.5.3",
|
|
12
12
|
"@react-router/fs-routes": "^7.5.3",
|
|
13
|
-
"@webstudio-is/image": "0.
|
|
14
|
-
"@webstudio-is/react-sdk": "0.
|
|
15
|
-
"@webstudio-is/sdk": "0.
|
|
16
|
-
"@webstudio-is/sdk-components-animation": "0.
|
|
17
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
18
|
-
"@webstudio-is/sdk-components-react-router": "0.
|
|
19
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
13
|
+
"@webstudio-is/image": "0.252.2",
|
|
14
|
+
"@webstudio-is/react-sdk": "0.252.2",
|
|
15
|
+
"@webstudio-is/sdk": "0.252.2",
|
|
16
|
+
"@webstudio-is/sdk-components-animation": "0.252.2",
|
|
17
|
+
"@webstudio-is/sdk-components-react-radix": "0.252.2",
|
|
18
|
+
"@webstudio-is/sdk-components-react-router": "0.252.2",
|
|
19
|
+
"@webstudio-is/sdk-components-react": "0.252.2",
|
|
20
20
|
"isbot": "^5.1.25",
|
|
21
21
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
22
22
|
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
"typescript": "5.8.2"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
32
|
+
"node": ">=22"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -8,42 +8,14 @@ import {
|
|
|
8
8
|
} from "__CLIENT__";
|
|
9
9
|
import "__CSS__";
|
|
10
10
|
|
|
11
|
-
export const Head = ({
|
|
12
|
-
const { pageMeta } = data;
|
|
13
|
-
const { origin } = new URL(data.url);
|
|
11
|
+
export const Head = ({}: { data: PageContext["data"] }) => {
|
|
14
12
|
const ldJson = {
|
|
15
13
|
"@context": "https://schema.org",
|
|
16
14
|
"@type": "WebSite",
|
|
17
15
|
name: siteName,
|
|
18
|
-
url: origin,
|
|
19
16
|
};
|
|
20
|
-
let socialImageUrl = pageMeta.socialImageUrl;
|
|
21
|
-
if (pageMeta.socialImageAssetName) {
|
|
22
|
-
socialImageUrl = `${origin}${imageLoader({
|
|
23
|
-
src: `${assetBaseUrl}/${pageMeta.socialImageAssetName}`,
|
|
24
|
-
// Do not transform social image (not enough information do we need to do this)
|
|
25
|
-
format: "raw",
|
|
26
|
-
})}`;
|
|
27
|
-
}
|
|
28
|
-
const isTwitterCardSizeDefined = pageMeta.custom.some(
|
|
29
|
-
(meta) => meta.property === "twitter:card"
|
|
30
|
-
);
|
|
31
17
|
return (
|
|
32
18
|
<>
|
|
33
|
-
{data.url && <meta property="og:url" content={data.url} />}
|
|
34
|
-
<title>{pageMeta.title}</title>
|
|
35
|
-
<meta property="og:title" content={pageMeta.title} />
|
|
36
|
-
{pageMeta.description && (
|
|
37
|
-
<>
|
|
38
|
-
<meta name="description" content={pageMeta.description} />
|
|
39
|
-
<meta property="og:description" content={pageMeta.description} />
|
|
40
|
-
</>
|
|
41
|
-
)}
|
|
42
|
-
<meta property="og:type" content="website" />
|
|
43
|
-
{siteName && <meta property="og:site_name" content={siteName} />}
|
|
44
|
-
{socialImageUrl && (
|
|
45
|
-
<meta property="og:image" content={pageMeta.socialImageUrl} />
|
|
46
|
-
)}
|
|
47
19
|
{siteName && (
|
|
48
20
|
<script
|
|
49
21
|
type="application/ld+json"
|
|
@@ -52,18 +24,6 @@ export const Head = ({ data }: { data: PageContext["data"] }) => {
|
|
|
52
24
|
}}
|
|
53
25
|
></script>
|
|
54
26
|
)}
|
|
55
|
-
{pageMeta.excludePageFromSearch && (
|
|
56
|
-
<meta name="robots" content="noindex, nofollow" />
|
|
57
|
-
)}
|
|
58
|
-
{pageMeta.custom.map(({ property, content }) => (
|
|
59
|
-
<meta key={property} property={property} content={content} />
|
|
60
|
-
))}
|
|
61
|
-
{(pageMeta.socialImageAssetName !== undefined ||
|
|
62
|
-
pageMeta.socialImageUrl !== undefined) &&
|
|
63
|
-
isTwitterCardSizeDefined === false && (
|
|
64
|
-
<meta property="twitter:card" content="summary_large_image" />
|
|
65
|
-
)}
|
|
66
|
-
|
|
67
27
|
{favIconAsset && (
|
|
68
28
|
<link
|
|
69
29
|
rel="icon"
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { PageContext } from "vike/types";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
PageSettingsMeta,
|
|
4
|
+
PageSettingsTitle,
|
|
5
|
+
ReactSdkContext,
|
|
6
|
+
} from "@webstudio-is/react-sdk/runtime";
|
|
3
7
|
import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
|
|
4
|
-
import { Page, breakpoints } from "__CLIENT__";
|
|
8
|
+
import { Page, breakpoints, siteName } from "__CLIENT__";
|
|
5
9
|
|
|
6
10
|
const PageComponent = ({ data }: { data: PageContext["data"] }) => {
|
|
7
|
-
const { system, resources, url } = data;
|
|
11
|
+
const { system, resources, url, pageMeta } = data;
|
|
8
12
|
return (
|
|
9
13
|
<ReactSdkContext.Provider
|
|
10
14
|
value={{
|
|
@@ -17,6 +21,14 @@ const PageComponent = ({ data }: { data: PageContext["data"] }) => {
|
|
|
17
21
|
>
|
|
18
22
|
{/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */}
|
|
19
23
|
<Page key={url} system={system} />
|
|
24
|
+
<PageSettingsMeta
|
|
25
|
+
url={url}
|
|
26
|
+
pageMeta={pageMeta}
|
|
27
|
+
siteName={siteName}
|
|
28
|
+
imageLoader={imageLoader}
|
|
29
|
+
assetBaseUrl={assetBaseUrl}
|
|
30
|
+
/>
|
|
31
|
+
<PageSettingsTitle>{pageMeta.title}</PageSettingsTitle>
|
|
20
32
|
</ReactSdkContext.Provider>
|
|
21
33
|
);
|
|
22
34
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PageContextServer } from "vike/types";
|
|
2
|
-
import { redirect } from "vike/abort";
|
|
3
2
|
import { isLocalResource, loadResources } from "@webstudio-is/sdk/runtime";
|
|
4
3
|
import { getPageMeta, getResources } from "__SERVER__";
|
|
4
|
+
import { assets } from "__ASSETS__";
|
|
5
5
|
|
|
6
6
|
const customFetch: typeof fetch = (input, init) => {
|
|
7
7
|
if (typeof input !== "string") {
|
|
@@ -26,6 +26,12 @@ const customFetch: typeof fetch = (input, init) => {
|
|
|
26
26
|
return Promise.resolve(response);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
if (isLocalResource(input, "assets")) {
|
|
30
|
+
const response = new Response(JSON.stringify(assets));
|
|
31
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
32
|
+
return Promise.resolve(response);
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
return fetch(input, init);
|
|
30
36
|
};
|
|
31
37
|
|
|
@@ -50,14 +56,6 @@ export const data = async (pageContext: PageContextServer) => {
|
|
|
50
56
|
);
|
|
51
57
|
const pageMeta = getPageMeta({ system, resources });
|
|
52
58
|
|
|
53
|
-
if (pageMeta.redirect) {
|
|
54
|
-
const status =
|
|
55
|
-
pageMeta.status === 301 || pageMeta.status === 302
|
|
56
|
-
? pageMeta.status
|
|
57
|
-
: 302;
|
|
58
|
-
throw redirect(pageMeta.redirect, status);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
59
|
return {
|
|
62
60
|
url: url.href,
|
|
63
61
|
system,
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "vite build",
|
|
7
7
|
"dev": "vite dev",
|
|
8
|
-
"typecheck": "
|
|
8
|
+
"typecheck": "tsgo --noEmit"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@webstudio-is/image": "0.
|
|
12
|
-
"@webstudio-is/react-sdk": "0.
|
|
13
|
-
"@webstudio-is/sdk": "0.
|
|
14
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
15
|
-
"@webstudio-is/sdk-components-animation": "0.
|
|
16
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
11
|
+
"@webstudio-is/image": "0.252.2",
|
|
12
|
+
"@webstudio-is/react-sdk": "0.252.2",
|
|
13
|
+
"@webstudio-is/sdk": "0.252.2",
|
|
14
|
+
"@webstudio-is/sdk-components-react": "0.252.2",
|
|
15
|
+
"@webstudio-is/sdk-components-animation": "0.252.2",
|
|
16
|
+
"@webstudio-is/sdk-components-react-radix": "0.252.2",
|
|
17
17
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
18
18
|
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
|
19
19
|
"vike": "^0.4.229"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
"vite": "^6.3.4"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
|
-
"node": ">=
|
|
29
|
+
"node": ">=22"
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { type Root, createRoot } from "react-dom/client";
|
|
2
2
|
import type { OnRenderClientSync } from "vike/types";
|
|
3
|
-
// @todo think about how to make __generated__ typeable
|
|
4
|
-
/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
|
|
5
|
-
// @ts-ignore
|
|
6
|
-
import { CustomCode } from "../app/__generated__/_index";
|
|
7
3
|
|
|
8
4
|
let root: Root;
|
|
9
5
|
|
|
@@ -17,7 +13,7 @@ export const onRenderClient: OnRenderClientSync = (pageContext) => {
|
|
|
17
13
|
<meta charSet="UTF-8" />
|
|
18
14
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
19
15
|
<Head data={pageContext.data} />
|
|
20
|
-
|
|
16
|
+
{/* avoid hydrating custom code on client, it will duplicate all scripts */}
|
|
21
17
|
</head>
|
|
22
18
|
<Page data={pageContext.data} />
|
|
23
19
|
</>
|