webstudio 0.163.0 → 0.167.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 +337 -192
- package/package.json +27 -25
- package/templates/cloudflare/package.json +3 -3
- package/templates/cloudflare/tsconfig.json +1 -1
- package/templates/defaults/app/root.tsx +20 -2
- package/templates/defaults/app/route-templates/html.tsx +0 -2
- package/templates/defaults/package.json +13 -13
- package/templates/defaults/tsconfig.json +1 -1
- package/templates/internal/tsconfig.json +1 -1
- package/templates/netlify-edge-functions/package.json +2 -2
- package/templates/netlify-functions/package.json +2 -2
- package/templates/saas-helpers/tsconfig.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/cli.ts
|
|
2
|
-
import { exit, argv } from "node:process";
|
|
2
|
+
import { exit as exit4, argv } from "node:process";
|
|
3
3
|
import { hideBin } from "yargs/helpers";
|
|
4
4
|
|
|
5
5
|
// src/config.ts
|
|
@@ -94,10 +94,41 @@ var loadJSONFile = async (filePath) => {
|
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
// src/commands/link.ts
|
|
97
|
-
import {
|
|
97
|
+
import { cwd, exit } from "node:process";
|
|
98
98
|
import { join as join2 } from "node:path";
|
|
99
|
-
import * as readline from "node:readline/promises";
|
|
100
99
|
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
100
|
+
import { cancel, isCancel, log, text } from "@clack/prompts";
|
|
101
|
+
var parseShareLink = (value) => {
|
|
102
|
+
const url = new URL(value);
|
|
103
|
+
const origin = url.origin;
|
|
104
|
+
const token = url.searchParams.get("authToken");
|
|
105
|
+
const segments = url.pathname.split("/").slice(1);
|
|
106
|
+
if (segments.length !== 2 || segments[0] !== "builder") {
|
|
107
|
+
throw Error("Segments not matching");
|
|
108
|
+
}
|
|
109
|
+
const [_builder, projectId] = segments;
|
|
110
|
+
if (token == null) {
|
|
111
|
+
throw Error("Token is missing");
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
origin,
|
|
115
|
+
projectId,
|
|
116
|
+
token
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
var validateShareLink = (value) => {
|
|
120
|
+
if (value.length === 0) {
|
|
121
|
+
return "Share link is required";
|
|
122
|
+
}
|
|
123
|
+
if (URL.canParse(value) === false) {
|
|
124
|
+
return "Share link is invalid";
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
parseShareLink(value);
|
|
128
|
+
} catch {
|
|
129
|
+
return "Share link is invalid";
|
|
130
|
+
}
|
|
131
|
+
};
|
|
101
132
|
var linkOptions = (yargs) => yargs.option("link", {
|
|
102
133
|
alias: "l",
|
|
103
134
|
type: "string",
|
|
@@ -108,62 +139,48 @@ var link = async (options) => {
|
|
|
108
139
|
if (options.link) {
|
|
109
140
|
shareLink = options.link;
|
|
110
141
|
} else {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const origin = shareLinkUrl.origin;
|
|
119
|
-
const token = shareLinkUrl.searchParams.get("authToken");
|
|
120
|
-
const paths = shareLinkUrl.pathname.split("/").slice(1);
|
|
121
|
-
if (paths[0] !== "builder" || paths.length !== 2) {
|
|
122
|
-
throw new Error("Invalid share link.");
|
|
123
|
-
}
|
|
124
|
-
const projectId = paths[1];
|
|
125
|
-
if (token == null) {
|
|
126
|
-
throw new Error("Invalid share link.");
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
const currentConfig = await readFile2(GLOBAL_CONFIG_FILE, "utf-8");
|
|
130
|
-
const currentConfigJson = jsonToGlobalConfig(JSON.parse(currentConfig));
|
|
131
|
-
const newConfig = {
|
|
132
|
-
...currentConfigJson,
|
|
133
|
-
[projectId]: {
|
|
134
|
-
origin,
|
|
135
|
-
token
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
await writeFile2(GLOBAL_CONFIG_FILE, JSON.stringify(newConfig, null, 2));
|
|
139
|
-
console.info(`Saved credentials for project ${projectId}.
|
|
140
|
-
You can find your config at ${GLOBAL_CONFIG_FILE}
|
|
141
|
-
`);
|
|
142
|
-
const localConfig = {
|
|
143
|
-
projectId
|
|
144
|
-
};
|
|
145
|
-
await createFileIfNotExists(
|
|
146
|
-
join2(cwd(), LOCAL_CONFIG_FILE),
|
|
147
|
-
JSON.stringify(localConfig, null, 2)
|
|
148
|
-
);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
if (error instanceof Error && "code" in error && error.code === "ENONET") {
|
|
151
|
-
throw new Error(`Global config file is not found`);
|
|
142
|
+
shareLink = await text({
|
|
143
|
+
message: "Please paste a link from the Share Dialog in the builder",
|
|
144
|
+
validate: validateShareLink
|
|
145
|
+
});
|
|
146
|
+
if (isCancel(shareLink)) {
|
|
147
|
+
cancel("Project linking is cancelled");
|
|
148
|
+
exit(1);
|
|
152
149
|
}
|
|
153
|
-
throw error;
|
|
154
150
|
}
|
|
151
|
+
const { origin, projectId, token } = parseShareLink(shareLink);
|
|
152
|
+
const currentConfig = await readFile2(GLOBAL_CONFIG_FILE, "utf-8");
|
|
153
|
+
const currentConfigJson = jsonToGlobalConfig(JSON.parse(currentConfig));
|
|
154
|
+
const newConfig = {
|
|
155
|
+
...currentConfigJson,
|
|
156
|
+
[projectId]: {
|
|
157
|
+
origin,
|
|
158
|
+
token
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
await writeFile2(GLOBAL_CONFIG_FILE, JSON.stringify(newConfig, null, 2));
|
|
162
|
+
log.info(`Saved credentials for project ${projectId}.
|
|
163
|
+
You can find your config at ${GLOBAL_CONFIG_FILE}`);
|
|
164
|
+
const localConfig = {
|
|
165
|
+
projectId
|
|
166
|
+
};
|
|
167
|
+
await createFileIfNotExists(
|
|
168
|
+
join2(cwd(), LOCAL_CONFIG_FILE),
|
|
169
|
+
JSON.stringify(localConfig, null, 2)
|
|
170
|
+
);
|
|
171
|
+
log.step("The project is linked successfully");
|
|
155
172
|
};
|
|
156
173
|
|
|
157
174
|
// src/commands/sync.ts
|
|
158
175
|
import { readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
159
176
|
import { cwd as cwd2 } from "node:process";
|
|
160
177
|
import { join as join3 } from "node:path";
|
|
161
|
-
import
|
|
178
|
+
import pc from "picocolors";
|
|
179
|
+
import { spinner } from "@clack/prompts";
|
|
162
180
|
import {
|
|
163
181
|
loadProjectDataByBuildId,
|
|
164
182
|
loadProjectDataById
|
|
165
183
|
} from "@webstudio-is/http-client";
|
|
166
|
-
import pc from "picocolors";
|
|
167
184
|
var syncOptions = (yargs) => yargs.option("buildId", {
|
|
168
185
|
type: "string",
|
|
169
186
|
describe: "[Experimental] Project build id to sync"
|
|
@@ -175,15 +192,15 @@ var syncOptions = (yargs) => yargs.option("buildId", {
|
|
|
175
192
|
describe: "[Experimental] Service token"
|
|
176
193
|
});
|
|
177
194
|
var sync = async (options) => {
|
|
178
|
-
const
|
|
179
|
-
|
|
195
|
+
const syncing = spinner();
|
|
196
|
+
syncing.start("Synchronizing project data");
|
|
180
197
|
const definedOptionValues = [
|
|
181
198
|
options.buildId,
|
|
182
199
|
options.origin,
|
|
183
200
|
options.authToken
|
|
184
201
|
].filter(Boolean);
|
|
185
202
|
if (definedOptionValues.length > 0 && definedOptionValues.length < 3) {
|
|
186
|
-
|
|
203
|
+
syncing.stop(`Please provide buildId, origin and authToken`, 2);
|
|
187
204
|
return;
|
|
188
205
|
}
|
|
189
206
|
let project;
|
|
@@ -194,17 +211,12 @@ var sync = async (options) => {
|
|
|
194
211
|
origin: options.origin
|
|
195
212
|
});
|
|
196
213
|
} else {
|
|
197
|
-
if (await isFileExists(GLOBAL_CONFIG_FILE) === false) {
|
|
198
|
-
spinner.fail(
|
|
199
|
-
`Global config file at path ${GLOBAL_CONFIG_FILE} is not found. Please link your project using webstudio link command`
|
|
200
|
-
);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
214
|
const globalConfigText = await readFile3(GLOBAL_CONFIG_FILE, "utf-8");
|
|
204
215
|
const globalConfig = jsonToGlobalConfig(JSON.parse(globalConfigText));
|
|
205
216
|
if (await isFileExists(LOCAL_CONFIG_FILE) === false) {
|
|
206
|
-
|
|
207
|
-
`Local config file is not found. Please make sure current directory is a webstudio project
|
|
217
|
+
syncing.stop(
|
|
218
|
+
`Local config file is not found. Please make sure current directory is a webstudio project`,
|
|
219
|
+
2
|
|
208
220
|
);
|
|
209
221
|
return;
|
|
210
222
|
}
|
|
@@ -215,29 +227,35 @@ var sync = async (options) => {
|
|
|
215
227
|
const localConfig = jsonToLocalConfig(JSON.parse(localConfigText));
|
|
216
228
|
const projectConfig = globalConfig[localConfig.projectId];
|
|
217
229
|
if (projectConfig === void 0) {
|
|
218
|
-
|
|
219
|
-
`Project config is not found, please run ${pc.dim("webstudio link")}
|
|
230
|
+
syncing.stop(
|
|
231
|
+
`Project config is not found, please run ${pc.dim("webstudio link")}`,
|
|
232
|
+
2
|
|
220
233
|
);
|
|
221
234
|
return;
|
|
222
235
|
}
|
|
223
236
|
const { origin, token } = projectConfig;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
237
|
+
try {
|
|
238
|
+
project = await loadProjectDataById({
|
|
239
|
+
projectId: localConfig.projectId,
|
|
240
|
+
authToken: token,
|
|
241
|
+
origin
|
|
242
|
+
});
|
|
243
|
+
} catch (error) {
|
|
244
|
+
syncing.stop(error.message, 2);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
230
247
|
}
|
|
231
248
|
project;
|
|
232
|
-
spinner.text = "Saving project data to config file";
|
|
233
249
|
const localBuildFilePath = join3(cwd2(), LOCAL_DATA_FILE);
|
|
234
250
|
await createFileIfNotExists(localBuildFilePath);
|
|
235
251
|
await writeFile3(localBuildFilePath, JSON.stringify(project, null, 2), "utf8");
|
|
236
|
-
|
|
252
|
+
syncing.stop("Project data synchronized successfully");
|
|
237
253
|
};
|
|
238
254
|
|
|
239
255
|
// src/commands/build.ts
|
|
240
256
|
import { access as access3 } from "node:fs/promises";
|
|
257
|
+
import { exit as exit3 } from "node:process";
|
|
258
|
+
import { log as log3 } from "@clack/prompts";
|
|
241
259
|
|
|
242
260
|
// src/prebuild.ts
|
|
243
261
|
import { basename, dirname as dirname2, join as join4, normalize } from "node:path";
|
|
@@ -252,10 +270,10 @@ import {
|
|
|
252
270
|
readdir
|
|
253
271
|
} from "node:fs/promises";
|
|
254
272
|
import { pipeline } from "node:stream/promises";
|
|
255
|
-
import { cwd as cwd3 } from "node:process";
|
|
273
|
+
import { cwd as cwd3, exit as exit2 } from "node:process";
|
|
256
274
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
257
275
|
import pLimit from "p-limit";
|
|
258
|
-
import
|
|
276
|
+
import { log as log2, spinner as spinner2 } from "@clack/prompts";
|
|
259
277
|
import merge from "deepmerge";
|
|
260
278
|
import {
|
|
261
279
|
generateCss,
|
|
@@ -281,6 +299,122 @@ import { createImageLoader } from "@webstudio-is/image";
|
|
|
281
299
|
import * as baseComponentMetas from "@webstudio-is/sdk-components-react/metas";
|
|
282
300
|
import * as remixComponentMetas from "@webstudio-is/sdk-components-react-remix/metas";
|
|
283
301
|
import * as radixComponentMetas from "@webstudio-is/sdk-components-react-radix/metas";
|
|
302
|
+
|
|
303
|
+
// src/html-to-jsx.ts
|
|
304
|
+
import {
|
|
305
|
+
parseFragment,
|
|
306
|
+
defaultTreeAdapter
|
|
307
|
+
} from "parse5";
|
|
308
|
+
import { camelCase } from "change-case";
|
|
309
|
+
var BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
310
|
+
"async",
|
|
311
|
+
"autofocus",
|
|
312
|
+
"autoplay",
|
|
313
|
+
"checked",
|
|
314
|
+
"contenteditable",
|
|
315
|
+
"controls",
|
|
316
|
+
"default",
|
|
317
|
+
"defer",
|
|
318
|
+
"disabled",
|
|
319
|
+
"formnovalidate",
|
|
320
|
+
"hidden",
|
|
321
|
+
"ismap",
|
|
322
|
+
"itemscope",
|
|
323
|
+
"loop",
|
|
324
|
+
"multiple",
|
|
325
|
+
"muted",
|
|
326
|
+
"nomodule",
|
|
327
|
+
"novalidate",
|
|
328
|
+
"open",
|
|
329
|
+
"playsinline",
|
|
330
|
+
"readonly",
|
|
331
|
+
"required",
|
|
332
|
+
"reversed",
|
|
333
|
+
"scoped",
|
|
334
|
+
"selected",
|
|
335
|
+
"truespeed"
|
|
336
|
+
]);
|
|
337
|
+
var isBooleanAttr = (name) => BOOLEAN_ATTRIBUTES.has(name.toLowerCase());
|
|
338
|
+
function* walkChildNodes(node) {
|
|
339
|
+
if (defaultTreeAdapter.isCommentNode(node) || defaultTreeAdapter.isTextNode(node) || defaultTreeAdapter.isDocumentTypeNode(node)) {
|
|
340
|
+
throw new Error("Unsupported node type");
|
|
341
|
+
}
|
|
342
|
+
for (const childNode of node.childNodes) {
|
|
343
|
+
if (defaultTreeAdapter.isCommentNode(childNode)) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (defaultTreeAdapter.isTextNode(childNode)) {
|
|
347
|
+
yield { type: "text", value: childNode.value };
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (false === defaultTreeAdapter.isElementNode(childNode)) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const attributes = childNode.attrs.map((attr) => [
|
|
354
|
+
attr.name,
|
|
355
|
+
attr.value
|
|
356
|
+
]);
|
|
357
|
+
yield { type: "element-start", tagName: childNode.tagName, attributes };
|
|
358
|
+
yield* walkChildNodes(childNode);
|
|
359
|
+
yield { type: "element-end", tagName: childNode.tagName };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
var convertStyleString = (style) => {
|
|
363
|
+
const styles = style.split(";").map((style2) => style2.trim()).map((style2) => style2.split(":").map((part) => part.trim()));
|
|
364
|
+
const res = {};
|
|
365
|
+
for (const [name, value] of styles) {
|
|
366
|
+
res[camelCase(name)] = value;
|
|
367
|
+
}
|
|
368
|
+
return JSON.stringify(res);
|
|
369
|
+
};
|
|
370
|
+
var toAttrString = (name, value) => {
|
|
371
|
+
const attName = name.toLowerCase();
|
|
372
|
+
const jsxName = attName === "class" ? "className" : attName;
|
|
373
|
+
if (value === "" && isBooleanAttr(attName)) {
|
|
374
|
+
return `${jsxName}`;
|
|
375
|
+
}
|
|
376
|
+
if (attName === "style") {
|
|
377
|
+
return `${jsxName}={${convertStyleString(value)}}`;
|
|
378
|
+
}
|
|
379
|
+
return `${jsxName}="${value}"`;
|
|
380
|
+
};
|
|
381
|
+
var attributesToString = (attributes) => attributes.map(([attName, value]) => ` ${toAttrString(attName, value)}`).join("");
|
|
382
|
+
var convertTagName = (tagName) => {
|
|
383
|
+
const tag = tagName.toLowerCase();
|
|
384
|
+
if (tag === "script") {
|
|
385
|
+
return "Script";
|
|
386
|
+
}
|
|
387
|
+
if (tag === "style") {
|
|
388
|
+
return "Style";
|
|
389
|
+
}
|
|
390
|
+
return tag;
|
|
391
|
+
};
|
|
392
|
+
var escapeValue = (value) => value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
393
|
+
var htmlToJsx = (html) => {
|
|
394
|
+
const parsedHtml = parseFragment(html, { scriptingEnabled: false });
|
|
395
|
+
let result = "";
|
|
396
|
+
for (const walkNode of walkChildNodes(parsedHtml)) {
|
|
397
|
+
switch (walkNode.type) {
|
|
398
|
+
case "text": {
|
|
399
|
+
const escapedValue = escapeValue(walkNode.value);
|
|
400
|
+
result += escapedValue ? "{`" + escapedValue + "`}" : "";
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
case "element-start": {
|
|
404
|
+
const tag = convertTagName(walkNode.tagName);
|
|
405
|
+
result += `<${tag}${attributesToString(walkNode.attributes)}>`;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case "element-end": {
|
|
409
|
+
result += `</${convertTagName(walkNode.tagName)}>`;
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// src/prebuild.ts
|
|
284
418
|
var limit = pLimit(10);
|
|
285
419
|
var downloadAsset = async (url, name, assetBaseUrl) => {
|
|
286
420
|
const assetPath = join4("public", assetBaseUrl, name);
|
|
@@ -357,12 +491,12 @@ var copyTemplates = async (template = "defaults") => {
|
|
|
357
491
|
}
|
|
358
492
|
};
|
|
359
493
|
var prebuild = async (options) => {
|
|
360
|
-
if (options.template ===
|
|
361
|
-
|
|
362
|
-
`
|
|
363
|
-
|
|
364
|
-
Please check webstudio --help for more details`
|
|
494
|
+
if (options.template.length === 0) {
|
|
495
|
+
log2.error(
|
|
496
|
+
`Template is not provided
|
|
497
|
+
Please check webstudio --help for more details`
|
|
365
498
|
);
|
|
499
|
+
exit2(1);
|
|
366
500
|
}
|
|
367
501
|
for (const template of options.template) {
|
|
368
502
|
if (template === "vanilla") {
|
|
@@ -372,16 +506,14 @@ var prebuild = async (options) => {
|
|
|
372
506
|
continue;
|
|
373
507
|
}
|
|
374
508
|
if (await isCliTemplate(template) === false) {
|
|
375
|
-
|
|
376
|
-
`
|
|
377
|
-
|
|
378
|
-
Please check webstudio --help for more details`
|
|
509
|
+
log2.error(
|
|
510
|
+
`Template ${options.template} is not available
|
|
511
|
+
Please check webstudio --help for more details`
|
|
379
512
|
);
|
|
513
|
+
exit2(1);
|
|
380
514
|
}
|
|
381
515
|
}
|
|
382
|
-
|
|
383
|
-
spinner.start();
|
|
384
|
-
spinner.text = "Generating files";
|
|
516
|
+
log2.step("Scaffolding the project files");
|
|
385
517
|
const appRoot = "app";
|
|
386
518
|
const generatedDir = join4(appRoot, "__generated__");
|
|
387
519
|
await rm(generatedDir, { recursive: true, force: true });
|
|
@@ -544,7 +676,6 @@ var prebuild = async (options) => {
|
|
|
544
676
|
}
|
|
545
677
|
}
|
|
546
678
|
const assets = new Map(siteData.assets.map((asset) => [asset.id, asset]));
|
|
547
|
-
spinner.text = "Generating css file";
|
|
548
679
|
const { cssText, classesMap } = generateCss({
|
|
549
680
|
instances: new Map(siteData.build.instances),
|
|
550
681
|
props: new Map(siteData.build.props),
|
|
@@ -558,7 +689,6 @@ var prebuild = async (options) => {
|
|
|
558
689
|
atomic: siteData.build.pages.compiler?.atomicStyles ?? true
|
|
559
690
|
});
|
|
560
691
|
await createFileIfNotExists(join4(generatedDir, "index.css"), cssText);
|
|
561
|
-
spinner.text = "Generating routes and pages";
|
|
562
692
|
const routeTemplatesDir = join4(cwd3(), "app/route-templates");
|
|
563
693
|
const routeTemplatePath = normalize(join4(routeTemplatesDir, "html.tsx"));
|
|
564
694
|
const routeXmlTemplatePath = normalize(join4(routeTemplatesDir, "xml.tsx"));
|
|
@@ -669,6 +799,9 @@ var prebuild = async (options) => {
|
|
|
669
799
|
const pageMeta = pageData.page.meta;
|
|
670
800
|
const favIconAsset = assets.get(projectMeta?.faviconAssetId ?? "");
|
|
671
801
|
const socialImageAsset = assets.get(pageMeta.socialImageAssetId ?? "");
|
|
802
|
+
const pagePath = getPagePath(pageData.page.id, siteData.build.pages);
|
|
803
|
+
const remixRoute = generateRemixRoute(pagePath);
|
|
804
|
+
const fileName = `${remixRoute}.tsx`;
|
|
672
805
|
const pageExports = `/* eslint-disable */
|
|
673
806
|
/* This is a auto generated file for building the project */
|
|
674
807
|
|
|
@@ -693,6 +826,29 @@ var prebuild = async (options) => {
|
|
|
693
826
|
export const pageBackgroundImageAssets: ImageAsset[] =
|
|
694
827
|
${JSON.stringify(pageBackgroundImageAssets)}
|
|
695
828
|
|
|
829
|
+
${remixRoute === "_index" ? `
|
|
830
|
+
${projectMeta?.code ? `
|
|
831
|
+
const Script = ({children, ...props}: Record<string, string | boolean>) => {
|
|
832
|
+
if (children == null) {
|
|
833
|
+
return <script {...props} />;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
return <script {...props} dangerouslySetInnerHTML={{__html: children}} />;
|
|
837
|
+
};
|
|
838
|
+
const Style = ({children, ...props}: Record<string, string | boolean>) => {
|
|
839
|
+
if (children == null) {
|
|
840
|
+
return <style {...props} />;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
return <style {...props} dangerouslySetInnerHTML={{__html: children}} />;
|
|
844
|
+
};
|
|
845
|
+
` : ""}
|
|
846
|
+
|
|
847
|
+
export const CustomCode = () => {
|
|
848
|
+
return (<>${projectMeta?.code ? htmlToJsx(projectMeta.code) : ""}</>);
|
|
849
|
+
}
|
|
850
|
+
` : ""}
|
|
851
|
+
|
|
696
852
|
${xmlPresentationComponents}
|
|
697
853
|
|
|
698
854
|
${pageComponent}
|
|
@@ -724,14 +880,7 @@ var prebuild = async (options) => {
|
|
|
724
880
|
export const projectId = "${siteData.build.projectId}";
|
|
725
881
|
|
|
726
882
|
export const contactEmail = ${JSON.stringify(contactEmail)};
|
|
727
|
-
|
|
728
|
-
export const customCode = ${JSON.stringify(
|
|
729
|
-
projectMeta?.code?.trim() ?? ""
|
|
730
|
-
)};
|
|
731
883
|
`;
|
|
732
|
-
const pagePath = getPagePath(pageData.page.id, siteData.build.pages);
|
|
733
|
-
const remixRoute = generateRemixRoute(pagePath);
|
|
734
|
-
const fileName = `${remixRoute}.tsx`;
|
|
735
884
|
const routeFileContent = (documentType === "html" ? routeFileTemplate : routeXmlFileTemplate).replace(
|
|
736
885
|
/".*\/__generated__\/_index"/,
|
|
737
886
|
`"../__generated__/${remixRoute}"`
|
|
@@ -762,7 +911,6 @@ var prebuild = async (options) => {
|
|
|
762
911
|
);
|
|
763
912
|
const redirects = siteData.build.pages?.redirects;
|
|
764
913
|
if (redirects !== void 0 && redirects.length > 0) {
|
|
765
|
-
spinner.text = "Generating redirects";
|
|
766
914
|
for (const redirect of redirects) {
|
|
767
915
|
const redirectPagePath = generateRemixRoute(redirect.old);
|
|
768
916
|
const redirectFileName = `${redirectPagePath}.ts`;
|
|
@@ -775,9 +923,13 @@ var prebuild = async (options) => {
|
|
|
775
923
|
await createFileIfNotExists(join4(routesDir, redirectFileName), content);
|
|
776
924
|
}
|
|
777
925
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
926
|
+
if (assetsToDownload.length > 0) {
|
|
927
|
+
const downloading = spinner2();
|
|
928
|
+
downloading.start("Downloading fonts and images");
|
|
929
|
+
await Promise.all(assetsToDownload);
|
|
930
|
+
downloading.stop("Downloaded fonts and images");
|
|
931
|
+
}
|
|
932
|
+
log2.step("Build finished");
|
|
781
933
|
};
|
|
782
934
|
|
|
783
935
|
// src/commands/build.ts
|
|
@@ -792,6 +944,7 @@ var buildOptions = (yargs) => yargs.option("assets", {
|
|
|
792
944
|
}).option("template", {
|
|
793
945
|
type: "array",
|
|
794
946
|
string: true,
|
|
947
|
+
default: [],
|
|
795
948
|
describe: `Template to use for the build [choices: ${PROJECT_TEMPALTES.join(
|
|
796
949
|
", "
|
|
797
950
|
)}]`
|
|
@@ -801,9 +954,10 @@ var build = async (options) => {
|
|
|
801
954
|
await access3(LOCAL_DATA_FILE);
|
|
802
955
|
} catch (error) {
|
|
803
956
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
804
|
-
|
|
957
|
+
log3.error(
|
|
805
958
|
`You need to link a webstudio project before building it. Run \`webstudio link\` to link a project.`
|
|
806
959
|
);
|
|
960
|
+
exit3(1);
|
|
807
961
|
}
|
|
808
962
|
throw error;
|
|
809
963
|
}
|
|
@@ -813,110 +967,99 @@ var build = async (options) => {
|
|
|
813
967
|
// src/commands/init-flow.ts
|
|
814
968
|
import { chdir, cwd as cwd4 } from "node:process";
|
|
815
969
|
import { join as join5 } from "node:path";
|
|
816
|
-
import ora3 from "ora";
|
|
817
|
-
|
|
818
|
-
// src/prompts.ts
|
|
819
|
-
import prompts from "prompts";
|
|
820
|
-
var prompt = (prompt2) => {
|
|
821
|
-
return prompts([
|
|
822
|
-
{
|
|
823
|
-
...prompt2,
|
|
824
|
-
onState: (state) => {
|
|
825
|
-
if (state.aborted) {
|
|
826
|
-
process.nextTick(() => {
|
|
827
|
-
process.exit(0);
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
]);
|
|
833
|
-
};
|
|
834
|
-
|
|
835
|
-
// src/commands/init-flow.ts
|
|
836
970
|
import pc2 from "picocolors";
|
|
971
|
+
import {
|
|
972
|
+
cancel as cancel2,
|
|
973
|
+
confirm,
|
|
974
|
+
isCancel as isCancel2,
|
|
975
|
+
log as log4,
|
|
976
|
+
select,
|
|
977
|
+
spinner as spinner3,
|
|
978
|
+
text as text2
|
|
979
|
+
} from "@clack/prompts";
|
|
837
980
|
import { $ } from "execa";
|
|
838
981
|
import { titleCase } from "title-case";
|
|
982
|
+
var exitIfCancelled = (value) => {
|
|
983
|
+
if (isCancel2(value)) {
|
|
984
|
+
cancel2("Project initialization is cancelled");
|
|
985
|
+
process.exit(0);
|
|
986
|
+
}
|
|
987
|
+
return value;
|
|
988
|
+
};
|
|
839
989
|
var initFlow = async (options) => {
|
|
840
990
|
const isProjectConfigured = await isFileExists(".webstudio/config.json");
|
|
841
991
|
let shouldInstallDeps = false;
|
|
842
992
|
let folderName;
|
|
843
993
|
let projectTemplate = void 0;
|
|
844
994
|
if (isProjectConfigured === false) {
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
995
|
+
const shouldCreateFolder = exitIfCancelled(
|
|
996
|
+
await confirm({
|
|
997
|
+
message: "Would you like to create a project folder? (no to use current folder)",
|
|
998
|
+
initialValue: true
|
|
999
|
+
})
|
|
1000
|
+
);
|
|
851
1001
|
if (shouldCreateFolder === true) {
|
|
852
|
-
folderName = (
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1002
|
+
folderName = exitIfCancelled(
|
|
1003
|
+
await text2({
|
|
1004
|
+
message: "Please enter a project name",
|
|
1005
|
+
validate(value) {
|
|
1006
|
+
if (value.length === 0) {
|
|
1007
|
+
return "Folder name is required";
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
})
|
|
1011
|
+
);
|
|
860
1012
|
await createFolderIfNotExists(join5(cwd4(), folderName));
|
|
861
1013
|
chdir(join5(cwd4(), folderName));
|
|
862
1014
|
}
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
});
|
|
868
|
-
if (projectLink === void 0) {
|
|
869
|
-
throw new Error(`Project Link is required`);
|
|
870
|
-
}
|
|
871
|
-
await link({ link: projectLink });
|
|
872
|
-
const { deployTarget } = await prompt({
|
|
873
|
-
type: "select",
|
|
874
|
-
name: "deployTarget",
|
|
875
|
-
message: "Where would you like to deploy your project?",
|
|
876
|
-
choices: PROJECT_TEMPALTES.map((template) => {
|
|
877
|
-
return {
|
|
878
|
-
title: titleCase(template),
|
|
879
|
-
value: template
|
|
880
|
-
};
|
|
1015
|
+
const shareLink = exitIfCancelled(
|
|
1016
|
+
await text2({
|
|
1017
|
+
message: "Please paste a link from the Share Dialog in the builder",
|
|
1018
|
+
validate: validateShareLink
|
|
881
1019
|
})
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1020
|
+
);
|
|
1021
|
+
await link({ link: shareLink });
|
|
1022
|
+
projectTemplate = exitIfCancelled(
|
|
1023
|
+
await select({
|
|
1024
|
+
message: "Where would you like to deploy your project?",
|
|
1025
|
+
options: PROJECT_TEMPALTES.map((template) => ({
|
|
1026
|
+
value: template,
|
|
1027
|
+
label: titleCase(template)
|
|
1028
|
+
}))
|
|
1029
|
+
})
|
|
1030
|
+
);
|
|
1031
|
+
shouldInstallDeps = exitIfCancelled(
|
|
1032
|
+
await confirm({
|
|
1033
|
+
message: "Would you like to install dependencies? (recommended)",
|
|
1034
|
+
initialValue: true
|
|
1035
|
+
})
|
|
1036
|
+
);
|
|
891
1037
|
}
|
|
892
|
-
await sync({ buildId: void 0, origin: void 0, authToken: void 0 });
|
|
893
1038
|
if (projectTemplate === void 0) {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
value: template
|
|
902
|
-
};
|
|
1039
|
+
projectTemplate = exitIfCancelled(
|
|
1040
|
+
await select({
|
|
1041
|
+
message: "Where would you like to deploy your project?",
|
|
1042
|
+
options: PROJECT_TEMPALTES.map((template) => ({
|
|
1043
|
+
value: template,
|
|
1044
|
+
label: titleCase(template)
|
|
1045
|
+
}))
|
|
903
1046
|
})
|
|
904
|
-
|
|
905
|
-
projectTemplate = deployTarget;
|
|
1047
|
+
);
|
|
906
1048
|
}
|
|
1049
|
+
await sync({ buildId: void 0, origin: void 0, authToken: void 0 });
|
|
907
1050
|
await build({
|
|
908
1051
|
...options,
|
|
909
1052
|
...projectTemplate && { template: [projectTemplate] }
|
|
910
1053
|
});
|
|
911
1054
|
if (shouldInstallDeps === true) {
|
|
912
|
-
const
|
|
913
|
-
|
|
1055
|
+
const install = spinner3();
|
|
1056
|
+
install.start("Installing dependencies");
|
|
914
1057
|
await $`npm install`;
|
|
915
|
-
|
|
1058
|
+
install.stop("Installed dependencies");
|
|
916
1059
|
}
|
|
917
|
-
|
|
918
|
-
Your project was successfully synced \u{1F389}`)));
|
|
919
|
-
|
|
1060
|
+
log4.message();
|
|
1061
|
+
log4.message(pc2.green(pc2.bold(`Your project was successfully synced \u{1F389}`)));
|
|
1062
|
+
log4.message(
|
|
920
1063
|
[
|
|
921
1064
|
"Now you can:",
|
|
922
1065
|
folderName && `Go to your project: ${pc2.dim(`cd ${folderName}`)}`,
|
|
@@ -949,7 +1092,7 @@ import makeCLI from "yargs";
|
|
|
949
1092
|
// package.json
|
|
950
1093
|
var package_default = {
|
|
951
1094
|
name: "webstudio",
|
|
952
|
-
version: "0.
|
|
1095
|
+
version: "0.167.0",
|
|
953
1096
|
description: "Webstudio CLI",
|
|
954
1097
|
author: "Webstudio <github@webstudio.is>",
|
|
955
1098
|
homepage: "https://webstudio.is",
|
|
@@ -969,10 +1112,12 @@ var package_default = {
|
|
|
969
1112
|
checks: "pnpm typecheck",
|
|
970
1113
|
build: "rm -rf lib && esbuild src/cli.ts --outdir=lib --bundle --format=esm --packages=external",
|
|
971
1114
|
"local-run": "tsx --no-warnings ./src/bin.ts",
|
|
972
|
-
dev: "esbuild src/cli.ts --watch --bundle --format=esm --packages=external --outdir=./lib"
|
|
1115
|
+
dev: "esbuild src/cli.ts --watch --bundle --format=esm --packages=external --outdir=./lib",
|
|
1116
|
+
test: "NODE_OPTIONS=--experimental-vm-modules jest"
|
|
973
1117
|
},
|
|
974
1118
|
license: "AGPL-3.0-or-later",
|
|
975
1119
|
dependencies: {
|
|
1120
|
+
"@clack/prompts": "^0.7.0",
|
|
976
1121
|
"@webstudio-is/http-client": "workspace:*",
|
|
977
1122
|
"@webstudio-is/image": "workspace:*",
|
|
978
1123
|
"@webstudio-is/react-sdk": "workspace:*",
|
|
@@ -980,39 +1125,39 @@ var package_default = {
|
|
|
980
1125
|
"@webstudio-is/sdk-components-react": "workspace:*",
|
|
981
1126
|
"@webstudio-is/sdk-components-react-radix": "workspace:*",
|
|
982
1127
|
"@webstudio-is/sdk-components-react-remix": "workspace:*",
|
|
1128
|
+
"change-case": "^5.0.2",
|
|
983
1129
|
deepmerge: "^4.3.1",
|
|
984
1130
|
"env-paths": "^3.0.0",
|
|
985
1131
|
execa: "^7.2.0",
|
|
986
|
-
|
|
1132
|
+
parse5: "7.1.2",
|
|
987
1133
|
"p-limit": "^4.0.0",
|
|
988
|
-
picocolors: "^1.0.
|
|
989
|
-
prompts: "^2.4.2",
|
|
1134
|
+
picocolors: "^1.0.1",
|
|
990
1135
|
"strip-indent": "^4.0.0",
|
|
991
1136
|
"title-case": "^4.1.0",
|
|
992
1137
|
yargs: "^17.7.2",
|
|
993
1138
|
zod: "^3.22.4"
|
|
994
1139
|
},
|
|
995
1140
|
devDependencies: {
|
|
996
|
-
"@
|
|
997
|
-
"@netlify/remix-
|
|
998
|
-
"@remix-
|
|
999
|
-
"@remix-run/cloudflare
|
|
1000
|
-
"@remix-run/
|
|
1001
|
-
"@remix-run/
|
|
1002
|
-
"@remix-run/
|
|
1003
|
-
"@remix-run/
|
|
1141
|
+
"@jest/globals": "^29.7.0",
|
|
1142
|
+
"@netlify/remix-adapter": "^2.4.0",
|
|
1143
|
+
"@netlify/remix-edge-adapter": "3.3.0",
|
|
1144
|
+
"@remix-run/cloudflare": "^2.9.2",
|
|
1145
|
+
"@remix-run/cloudflare-pages": "^2.9.2",
|
|
1146
|
+
"@remix-run/dev": "^2.9.2",
|
|
1147
|
+
"@remix-run/node": "^2.9.2",
|
|
1148
|
+
"@remix-run/react": "^2.9.2",
|
|
1149
|
+
"@remix-run/server-runtime": "^2.9.2",
|
|
1004
1150
|
"@types/node": "^20.12.7",
|
|
1005
|
-
"@types/prompts": "^2.4.5",
|
|
1006
1151
|
"@types/react": "^18.2.70",
|
|
1007
1152
|
"@types/react-dom": "^18.2.25",
|
|
1008
1153
|
"@types/yargs": "^17.0.32",
|
|
1009
1154
|
"@webstudio-is/form-handlers": "workspace:*",
|
|
1155
|
+
"@webstudio-is/jest-config": "workspace:*",
|
|
1010
1156
|
"@webstudio-is/tsconfig": "workspace:*",
|
|
1011
1157
|
react: "18.3.0-canary-14898b6a9-20240318",
|
|
1012
1158
|
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
|
1013
|
-
tsx: "^4.7.2",
|
|
1014
1159
|
typescript: "5.4.5",
|
|
1015
|
-
vite: "^5.2.
|
|
1160
|
+
vite: "^5.2.12",
|
|
1016
1161
|
wrangler: "^3.48.0"
|
|
1017
1162
|
}
|
|
1018
1163
|
};
|
|
@@ -1056,7 +1201,7 @@ var main = async () => {
|
|
|
1056
1201
|
await cmd.parse();
|
|
1057
1202
|
} catch (error) {
|
|
1058
1203
|
console.error(error);
|
|
1059
|
-
|
|
1204
|
+
exit4(1);
|
|
1060
1205
|
}
|
|
1061
1206
|
};
|
|
1062
1207
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webstudio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.167.0",
|
|
4
4
|
"description": "Webstudio CLI",
|
|
5
5
|
"author": "Webstudio <github@webstudio.is>",
|
|
6
6
|
"homepage": "https://webstudio.is",
|
|
@@ -17,53 +17,55 @@
|
|
|
17
17
|
],
|
|
18
18
|
"license": "AGPL-3.0-or-later",
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@clack/prompts": "^0.7.0",
|
|
21
|
+
"change-case": "^5.0.2",
|
|
20
22
|
"deepmerge": "^4.3.1",
|
|
21
23
|
"env-paths": "^3.0.0",
|
|
22
24
|
"execa": "^7.2.0",
|
|
23
|
-
"
|
|
25
|
+
"parse5": "7.1.2",
|
|
24
26
|
"p-limit": "^4.0.0",
|
|
25
|
-
"picocolors": "^1.0.
|
|
26
|
-
"prompts": "^2.4.2",
|
|
27
|
+
"picocolors": "^1.0.1",
|
|
27
28
|
"strip-indent": "^4.0.0",
|
|
28
29
|
"title-case": "^4.1.0",
|
|
29
30
|
"yargs": "^17.7.2",
|
|
30
31
|
"zod": "^3.22.4",
|
|
31
|
-
"@webstudio-is/http-client": "0.
|
|
32
|
-
"@webstudio-is/
|
|
33
|
-
"@webstudio-is/
|
|
34
|
-
"@webstudio-is/sdk": "0.
|
|
35
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
36
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
37
|
-
"@webstudio-is/sdk-components-react-remix": "0.
|
|
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"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
|
-
"@
|
|
41
|
-
"@netlify/remix-
|
|
42
|
-
"@remix-
|
|
43
|
-
"@remix-run/cloudflare
|
|
44
|
-
"@remix-run/
|
|
45
|
-
"@remix-run/
|
|
46
|
-
"@remix-run/
|
|
47
|
-
"@remix-run/
|
|
41
|
+
"@jest/globals": "^29.7.0",
|
|
42
|
+
"@netlify/remix-adapter": "^2.4.0",
|
|
43
|
+
"@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",
|
|
48
50
|
"@types/node": "^20.12.7",
|
|
49
|
-
"@types/prompts": "^2.4.5",
|
|
50
51
|
"@types/react": "^18.2.70",
|
|
51
52
|
"@types/react-dom": "^18.2.25",
|
|
52
53
|
"@types/yargs": "^17.0.32",
|
|
53
54
|
"react": "18.3.0-canary-14898b6a9-20240318",
|
|
54
55
|
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
|
55
|
-
"tsx": "^4.7.2",
|
|
56
56
|
"typescript": "5.4.5",
|
|
57
|
-
"vite": "^5.2.
|
|
57
|
+
"vite": "^5.2.12",
|
|
58
58
|
"wrangler": "^3.48.0",
|
|
59
|
-
"@webstudio-is/form-handlers": "0.
|
|
60
|
-
"@webstudio-is/tsconfig": "1.0.7"
|
|
59
|
+
"@webstudio-is/form-handlers": "0.167.0",
|
|
60
|
+
"@webstudio-is/tsconfig": "1.0.7",
|
|
61
|
+
"@webstudio-is/jest-config": "1.0.7"
|
|
61
62
|
},
|
|
62
63
|
"scripts": {
|
|
63
64
|
"typecheck": "tsc",
|
|
64
65
|
"checks": "pnpm typecheck",
|
|
65
66
|
"build": "rm -rf lib && esbuild src/cli.ts --outdir=lib --bundle --format=esm --packages=external",
|
|
66
67
|
"local-run": "tsx --no-warnings ./src/bin.ts",
|
|
67
|
-
"dev": "esbuild src/cli.ts --watch --bundle --format=esm --packages=external --outdir=./lib"
|
|
68
|
+
"dev": "esbuild src/cli.ts --watch --bundle --format=esm --packages=external --outdir=./lib",
|
|
69
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
|
|
68
70
|
}
|
|
69
71
|
}
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
"build-cf-types": "wrangler types"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@remix-run/cloudflare": "2.9.
|
|
16
|
-
"@remix-run/cloudflare-pages": "2.9.
|
|
17
|
-
"isbot": "^
|
|
15
|
+
"@remix-run/cloudflare": "2.9.2",
|
|
16
|
+
"@remix-run/cloudflare-pages": "2.9.2",
|
|
17
|
+
"isbot": "^5.1.8"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@cloudflare/workers-types": "^4.20240405.0",
|
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
|
|
3
|
+
import { Links, Meta, Outlet, useMatches } from "@remix-run/react";
|
|
4
|
+
// @todo think about how to make __generated__ typeable
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import { CustomCode } from "./__generated__/_index";
|
|
2
7
|
|
|
3
8
|
const Root = () => {
|
|
9
|
+
// Get language from matches
|
|
10
|
+
const matches = useMatches();
|
|
11
|
+
|
|
12
|
+
const lastMatchWithLanguage = matches.findLast((match) => {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
const language = match?.data?.pageMeta?.language;
|
|
15
|
+
return language != null;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
const lang = lastMatchWithLanguage?.data?.pageMeta?.language ?? "en";
|
|
20
|
+
|
|
4
21
|
return (
|
|
5
|
-
<html lang=
|
|
22
|
+
<html lang={lang}>
|
|
6
23
|
<head>
|
|
7
24
|
<meta charSet="utf-8" />
|
|
8
25
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
9
26
|
<Meta />
|
|
10
27
|
<Links />
|
|
28
|
+
<CustomCode />
|
|
11
29
|
</head>
|
|
12
30
|
<Outlet />
|
|
13
31
|
</html>
|
|
@@ -81,7 +81,6 @@ export const loader = async (arg: LoaderFunctionArgs) => {
|
|
|
81
81
|
status: pageMeta.status,
|
|
82
82
|
headers: {
|
|
83
83
|
"Cache-Control": "public, max-age=600",
|
|
84
|
-
"x-ws-language": pageMeta.language ?? "en",
|
|
85
84
|
},
|
|
86
85
|
}
|
|
87
86
|
);
|
|
@@ -90,7 +89,6 @@ export const loader = async (arg: LoaderFunctionArgs) => {
|
|
|
90
89
|
export const headers: HeadersFunction = ({ loaderHeaders }) => {
|
|
91
90
|
return {
|
|
92
91
|
"Cache-Control": "public, max-age=0, must-revalidate",
|
|
93
|
-
"x-ws-language": loaderHeaders.get("x-ws-language") ?? "",
|
|
94
92
|
};
|
|
95
93
|
};
|
|
96
94
|
|
|
@@ -8,26 +8,26 @@
|
|
|
8
8
|
"typecheck": "tsc"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@remix-run/node": "2.9.
|
|
12
|
-
"@remix-run/react": "2.9.
|
|
13
|
-
"@remix-run/server-runtime": "2.9.
|
|
14
|
-
"@webstudio-is/react-sdk": "0.
|
|
15
|
-
"@webstudio-is/sdk-components-react-radix": "0.
|
|
16
|
-
"@webstudio-is/sdk-components-react-remix": "0.
|
|
17
|
-
"@webstudio-is/sdk-components-react": "0.
|
|
18
|
-
"@webstudio-is/form-handlers": "0.
|
|
19
|
-
"@webstudio-is/image": "0.
|
|
20
|
-
"@webstudio-is/sdk": "0.
|
|
21
|
-
"isbot": "^
|
|
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",
|
|
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.
|
|
26
|
+
"@remix-run/dev": "2.9.2",
|
|
27
27
|
"@types/react": "^18.2.70",
|
|
28
28
|
"@types/react-dom": "^18.2.25",
|
|
29
29
|
"typescript": "5.4.5",
|
|
30
|
-
"vite": "^5.2.
|
|
30
|
+
"vite": "^5.2.12"
|
|
31
31
|
},
|
|
32
32
|
"engines": {
|
|
33
33
|
"node": ">=20.0.0"
|