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/lib/cli.js CHANGED
@@ -45,10 +45,11 @@ var jsonToGlobalConfig = (json) => {
45
45
  return zGlobalConfig.parse(json);
46
46
  };
47
47
  var PROJECT_TEMPALTES = [
48
- "vanilla",
49
- "vercel",
50
- "netlify-functions",
51
- "netlify-edge-functions"
48
+ { value: "vanilla", label: "Vanilla" },
49
+ { value: "vercel", label: "Vercel" },
50
+ { value: "netlify-functions", label: "Netlify Functions" },
51
+ { value: "netlify-edge-functions", label: "Netlify Edge Functions" },
52
+ { value: "ssg", label: "Static Site Generation (SSG)" }
52
53
  ];
53
54
 
54
55
  // src/fs-utils.ts
@@ -258,14 +259,14 @@ import { exit as exit3 } from "node:process";
258
259
  import { log as log3 } from "@clack/prompts";
259
260
 
260
261
  // src/prebuild.ts
261
- import { basename, dirname as dirname2, join as join4, normalize } from "node:path";
262
- import { createWriteStream } from "node:fs";
262
+ import { basename, dirname as dirname2, join as join6, normalize, relative } from "node:path";
263
+ import { createWriteStream, existsSync } from "node:fs";
263
264
  import {
264
- rm,
265
+ rm as rm3,
265
266
  access as access2,
266
267
  rename,
267
268
  cp,
268
- readFile as readFile4,
269
+ readFile as readFile6,
269
270
  writeFile as writeFile4,
270
271
  readdir
271
272
  } from "node:fs/promises";
@@ -279,9 +280,8 @@ import {
279
280
  generateCss,
280
281
  generateWebstudioComponent,
281
282
  getIndexesWithinAncestors,
282
- namespaceMeta,
283
283
  normalizeProps,
284
- generateRemixRoute,
284
+ generateRemixRoute as generateRemixRoute2,
285
285
  generateRemixParams,
286
286
  isCoreComponent
287
287
  } from "@webstudio-is/react-sdk";
@@ -290,15 +290,12 @@ import {
290
290
  findTreeInstanceIds,
291
291
  getPagePath,
292
292
  parseComponentName,
293
- generateFormsProperties,
294
- generateResourcesLoader,
293
+ generateResources,
295
294
  generatePageMeta,
296
- getStaticSiteMapXml
295
+ getStaticSiteMapXml,
296
+ replaceFormActionsWithResources
297
297
  } from "@webstudio-is/sdk";
298
298
  import { createImageLoader } from "@webstudio-is/image";
299
- import * as baseComponentMetas from "@webstudio-is/sdk-components-react/metas";
300
- import * as remixComponentMetas from "@webstudio-is/sdk-components-react-remix/metas";
301
- import * as radixComponentMetas from "@webstudio-is/sdk-components-react-radix/metas";
302
299
 
303
300
  // src/html-to-jsx.ts
304
301
  import {
@@ -367,6 +364,7 @@ var convertStyleString = (style) => {
367
364
  }
368
365
  return JSON.stringify(res);
369
366
  };
367
+ var escape = (value) => JSON.stringify(value);
370
368
  var toAttrString = (name, value) => {
371
369
  const attName = name.toLowerCase();
372
370
  const jsxName = attName === "class" ? "className" : attName;
@@ -376,7 +374,7 @@ var toAttrString = (name, value) => {
376
374
  if (attName === "style") {
377
375
  return `${jsxName}={${convertStyleString(value)}}`;
378
376
  }
379
- return `${jsxName}="${value}"`;
377
+ return `${jsxName}={${escape(value)}}`;
380
378
  };
381
379
  var attributesToString = (attributes) => attributes.map(([attName, value]) => ` ${toAttrString(attName, value)}`).join("");
382
380
  var convertTagName = (tagName) => {
@@ -389,15 +387,14 @@ var convertTagName = (tagName) => {
389
387
  }
390
388
  return tag;
391
389
  };
392
- var escapeValue = (value) => value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
393
390
  var htmlToJsx = (html) => {
394
391
  const parsedHtml = parseFragment(html, { scriptingEnabled: false });
395
392
  let result = "";
396
393
  for (const walkNode of walkChildNodes(parsedHtml)) {
397
394
  switch (walkNode.type) {
398
395
  case "text": {
399
- const escapedValue = escapeValue(walkNode.value);
400
- result += escapedValue ? "{`" + escapedValue + "`}" : "";
396
+ const escapedValue = escape(walkNode.value);
397
+ result += escapedValue ? "{" + escapedValue + "}" : "";
401
398
  break;
402
399
  }
403
400
  case "element-start": {
@@ -414,10 +411,161 @@ var htmlToJsx = (html) => {
414
411
  return result;
415
412
  };
416
413
 
414
+ // src/framework-remix.ts
415
+ import { join as join4 } from "node:path";
416
+ import { readFile as readFile4, rm } from "node:fs/promises";
417
+ import {
418
+ generateRemixRoute,
419
+ namespaceMeta
420
+ } from "@webstudio-is/react-sdk";
421
+ import * as baseComponentMetas from "@webstudio-is/sdk-components-react/metas";
422
+ import * as remixComponentMetas from "@webstudio-is/sdk-components-react-remix/metas";
423
+ import * as radixComponentMetas from "@webstudio-is/sdk-components-react-radix/metas";
424
+ var createFramework = async () => {
425
+ const routeTemplatesDir = join4("app", "route-templates");
426
+ const htmlTemplate = await readFile4(
427
+ join4(routeTemplatesDir, "html.tsx"),
428
+ "utf8"
429
+ );
430
+ const xmlTemplate = await readFile4(
431
+ join4(routeTemplatesDir, "xml.tsx"),
432
+ "utf8"
433
+ );
434
+ const defaultSitemapTemplate = await readFile4(
435
+ join4(routeTemplatesDir, "default-sitemap.tsx"),
436
+ "utf8"
437
+ );
438
+ const redirectTemplate = await readFile4(
439
+ join4(routeTemplatesDir, "redirect.tsx"),
440
+ "utf8"
441
+ );
442
+ await rm(routeTemplatesDir, { recursive: true, force: true });
443
+ const radixComponentNamespacedMetas = {};
444
+ for (const [name, meta] of Object.entries(radixComponentMetas)) {
445
+ const namespace = "@webstudio-is/sdk-components-react-radix";
446
+ radixComponentNamespacedMetas[`${namespace}:${name}`] = namespaceMeta(
447
+ meta,
448
+ namespace,
449
+ new Set(Object.keys(radixComponentMetas))
450
+ );
451
+ }
452
+ return {
453
+ components: [
454
+ {
455
+ source: "@webstudio-is/sdk-components-react",
456
+ metas: baseComponentMetas
457
+ },
458
+ {
459
+ source: "@webstudio-is/sdk-components-react-radix",
460
+ metas: radixComponentNamespacedMetas
461
+ },
462
+ {
463
+ source: "@webstudio-is/sdk-components-react-remix",
464
+ metas: remixComponentMetas
465
+ }
466
+ ],
467
+ html: ({ pagePath }) => [
468
+ {
469
+ file: join4("app", "routes", `${generateRemixRoute(pagePath)}.tsx`),
470
+ template: htmlTemplate
471
+ }
472
+ ],
473
+ xml: ({ pagePath }) => [
474
+ {
475
+ file: join4("app", "routes", `${generateRemixRoute(pagePath)}.tsx`),
476
+ template: xmlTemplate
477
+ }
478
+ ],
479
+ redirect: ({ pagePath }) => [
480
+ {
481
+ file: join4("app", "routes", `${generateRemixRoute(pagePath)}.ts`),
482
+ template: redirectTemplate
483
+ }
484
+ ],
485
+ defaultSitemap: () => [
486
+ {
487
+ file: join4(
488
+ "app",
489
+ "routes",
490
+ `${generateRemixRoute("/sitemap.xml")}.tsx`
491
+ ),
492
+ template: defaultSitemapTemplate
493
+ }
494
+ ]
495
+ };
496
+ };
497
+
498
+ // src/framework-vike-ssg.ts
499
+ import { join as join5 } from "node:path";
500
+ import { readFile as readFile5, rm as rm2 } from "node:fs/promises";
501
+ import { namespaceMeta as namespaceMeta2 } from "@webstudio-is/react-sdk";
502
+ import * as baseComponentMetas2 from "@webstudio-is/sdk-components-react/metas";
503
+ import * as radixComponentMetas2 from "@webstudio-is/sdk-components-react-radix/metas";
504
+ var generateVikeRoute = (pagePath) => {
505
+ if (pagePath === "/") {
506
+ return "index";
507
+ }
508
+ return pagePath;
509
+ };
510
+ var createFramework2 = async () => {
511
+ const routeTemplatesDir = join5("app", "route-templates");
512
+ const htmlPageTemplate = await readFile5(
513
+ join5(routeTemplatesDir, "html", "+Page.tsx"),
514
+ "utf8"
515
+ );
516
+ const htmlHeadTemplate = await readFile5(
517
+ join5(routeTemplatesDir, "html", "+Head.tsx"),
518
+ "utf8"
519
+ );
520
+ const htmlDataTemplate = await readFile5(
521
+ join5(routeTemplatesDir, "html", "+data.ts"),
522
+ "utf8"
523
+ );
524
+ await rm2(routeTemplatesDir, { recursive: true, force: true });
525
+ const radixComponentNamespacedMetas = {};
526
+ for (const [name, meta] of Object.entries(radixComponentMetas2)) {
527
+ const namespace = "@webstudio-is/sdk-components-react-radix";
528
+ radixComponentNamespacedMetas[`${namespace}:${name}`] = namespaceMeta2(
529
+ meta,
530
+ namespace,
531
+ new Set(Object.keys(radixComponentMetas2))
532
+ );
533
+ }
534
+ return {
535
+ components: [
536
+ {
537
+ source: "@webstudio-is/sdk-components-react",
538
+ metas: baseComponentMetas2
539
+ },
540
+ {
541
+ source: "@webstudio-is/sdk-components-react-radix",
542
+ metas: radixComponentNamespacedMetas
543
+ }
544
+ ],
545
+ html: ({ pagePath }) => [
546
+ {
547
+ file: join5("pages", generateVikeRoute(pagePath), "+Page.tsx"),
548
+ template: htmlPageTemplate
549
+ },
550
+ {
551
+ file: join5("pages", generateVikeRoute(pagePath), "+Head.tsx"),
552
+ template: htmlHeadTemplate
553
+ },
554
+ {
555
+ file: join5("pages", generateVikeRoute(pagePath), "+data.ts"),
556
+ template: htmlDataTemplate
557
+ }
558
+ ],
559
+ xml: () => [],
560
+ redirect: () => [],
561
+ defaultSitemap: () => []
562
+ };
563
+ };
564
+
417
565
  // src/prebuild.ts
418
566
  var limit = pLimit(10);
419
567
  var downloadAsset = async (url, name, assetBaseUrl) => {
420
- const assetPath = join4("public", assetBaseUrl, name);
568
+ const assetPath = join6("public", assetBaseUrl, name);
421
569
  const tempAssetPath = `${assetPath}.tmp`;
422
570
  try {
423
571
  await access2(assetPath);
@@ -441,8 +589,8 @@ var downloadAsset = async (url, name, assetBaseUrl) => {
441
589
  }
442
590
  };
443
591
  var mergeJsonInto = async (sourcePath, destinationPath) => {
444
- const sourceJson = await readFile4(sourcePath, "utf8");
445
- const destinationJson = await readFile4(destinationPath, "utf8").catch(
592
+ const sourceJson = await readFile6(sourcePath, "utf8");
593
+ const destinationJson = await readFile6(destinationPath, "utf8").catch(
446
594
  (error) => {
447
595
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
448
596
  return "{}";
@@ -451,7 +599,9 @@ var mergeJsonInto = async (sourcePath, destinationPath) => {
451
599
  }
452
600
  );
453
601
  const content = JSON.stringify(
454
- merge(JSON.parse(destinationJson), JSON.parse(sourceJson)),
602
+ merge(JSON.parse(destinationJson), JSON.parse(sourceJson), {
603
+ arrayMerge: (_target, source) => source
604
+ }),
455
605
  null,
456
606
  " "
457
607
  );
@@ -460,7 +610,7 @@ var mergeJsonInto = async (sourcePath, destinationPath) => {
460
610
  var isCliTemplate = async (template) => {
461
611
  const currentPath = fileURLToPath(new URL(import.meta.url));
462
612
  const templatesPath = normalize(
463
- join4(dirname2(currentPath), "..", "templates")
613
+ join6(dirname2(currentPath), "..", "templates")
464
614
  );
465
615
  const dirents = await readdir(templatesPath, { withFileTypes: true });
466
616
  for (const dirent of dirents) {
@@ -472,24 +622,34 @@ var isCliTemplate = async (template) => {
472
622
  };
473
623
  var getTemplatePath = async (template) => {
474
624
  const currentPath = fileURLToPath(new URL(import.meta.url));
475
- const templatePath = await isCliTemplate(template) ? normalize(join4(dirname2(currentPath), "..", "templates", template)) : template;
625
+ const templatePath = await isCliTemplate(template) ? normalize(join6(dirname2(currentPath), "..", "templates", template)) : template;
476
626
  return templatePath;
477
627
  };
478
- var copyTemplates = async (template = "defaults") => {
628
+ var copyTemplates = async (template) => {
479
629
  const templatePath = await getTemplatePath(template);
480
630
  await cp(templatePath, cwd3(), {
481
631
  recursive: true,
482
632
  filter: (source) => {
483
- return basename(source) !== "package.json";
633
+ const name = basename(source);
634
+ return name !== "package.json" && name !== "tsconfig.json";
484
635
  }
485
636
  });
486
- if (await isFileExists(join4(templatePath, "package.json")) === true) {
637
+ if (existsSync(join6(templatePath, "package.json"))) {
638
+ await mergeJsonInto(
639
+ join6(templatePath, "package.json"),
640
+ join6(cwd3(), "package.json")
641
+ );
642
+ }
643
+ if (existsSync(join6(templatePath, "tsconfig.json"))) {
487
644
  await mergeJsonInto(
488
- join4(templatePath, "package.json"),
489
- join4(cwd3(), "package.json")
645
+ join6(templatePath, "tsconfig.json"),
646
+ join6(cwd3(), "tsconfig.json")
490
647
  );
491
648
  }
492
649
  };
650
+ var importFrom = (importee, importer) => {
651
+ return relative(dirname2(importer), importee).replaceAll("\\", "/");
652
+ };
493
653
  var prebuild = async (options) => {
494
654
  if (options.template.length === 0) {
495
655
  log2.error(
@@ -499,7 +659,7 @@ Please check webstudio --help for more details`
499
659
  exit2(1);
500
660
  }
501
661
  for (const template of options.template) {
502
- if (template === "vanilla") {
662
+ if (template === "vanilla" || template === "ssg") {
503
663
  continue;
504
664
  }
505
665
  if (template.startsWith(".") || template.startsWith("/")) {
@@ -515,18 +675,25 @@ Please check webstudio --help for more details`
515
675
  }
516
676
  log2.step("Scaffolding the project files");
517
677
  const appRoot = "app";
518
- const generatedDir = join4(appRoot, "__generated__");
519
- await rm(generatedDir, { recursive: true, force: true });
520
- const routesDir = join4(appRoot, "routes");
521
- await rm(routesDir, { recursive: true, force: true });
522
- await copyTemplates();
678
+ const generatedDir = join6(appRoot, "__generated__");
679
+ await rm3(generatedDir, { recursive: true, force: true });
680
+ const routesDir = join6(appRoot, "routes");
681
+ await rm3(routesDir, { recursive: true, force: true });
682
+ await copyTemplates(options.template.includes("ssg") ? "ssg" : "defaults");
683
+ await writeFile4(join6(cwd3(), ".npmrc"), "force=true");
523
684
  for (const template of options.template) {
524
- if (template === "vanilla") {
685
+ if (template === "vanilla" || template === "ssg") {
525
686
  continue;
526
687
  }
527
688
  await copyTemplates(template);
528
689
  }
529
- const constants2 = await import(pathToFileURL(join4(cwd3(), "app/constants.mjs")).href);
690
+ let framework;
691
+ if (options.template.includes("ssg")) {
692
+ framework = await createFramework2();
693
+ } else {
694
+ framework = await createFramework();
695
+ }
696
+ const constants2 = await import(pathToFileURL(join6(cwd3(), "app/constants.mjs")).href);
530
697
  const { assetBaseUrl } = constants2;
531
698
  const siteData = await loadJSONFile(LOCAL_DATA_FILE);
532
699
  if (siteData === null) {
@@ -538,27 +705,14 @@ Please check webstudio --help for more details`
538
705
  if (domain === void 0) {
539
706
  throw new Error(`Project domain is missing from the project data`);
540
707
  }
541
- const radixComponentNamespacedMetas = Object.entries(
542
- radixComponentMetas
543
- ).reduce(
544
- (r, [name, meta]) => {
545
- const namespace = "@webstudio-is/sdk-components-react-radix";
546
- r[`${namespace}:${name}`] = namespaceMeta(
547
- meta,
548
- namespace,
549
- new Set(Object.keys(radixComponentMetas))
550
- );
551
- return r;
552
- },
553
- {}
554
- );
555
- const metas = new Map(
556
- Object.entries({
557
- ...baseComponentMetas,
558
- ...radixComponentNamespacedMetas,
559
- ...remixComponentMetas
560
- })
561
- );
708
+ const metas = /* @__PURE__ */ new Map();
709
+ const componentSources = /* @__PURE__ */ new Map();
710
+ for (const entry of framework.components) {
711
+ for (const [componentName, meta] of Object.entries(entry.metas)) {
712
+ metas.set(componentName, meta);
713
+ componentSources.set(componentName, entry.source);
714
+ }
715
+ }
562
716
  const projectMetas = /* @__PURE__ */ new Map();
563
717
  const componentsByPage = {};
564
718
  const siteDataByPage = {};
@@ -576,7 +730,9 @@ Please check webstudio --help for more details`
576
730
  props: siteData.build.props.map(([_id, prop]) => prop),
577
731
  assetBaseUrl,
578
732
  assets: new Map(siteData.assets.map((asset) => [asset.id, asset])),
579
- pages: siteData.build.pages
733
+ uploadingImageAssets: [],
734
+ pages: siteData.build.pages,
735
+ source: "prebuild"
580
736
  });
581
737
  const props = [];
582
738
  for (const prop of normalizedProps) {
@@ -676,7 +832,7 @@ Please check webstudio --help for more details`
676
832
  }
677
833
  }
678
834
  const assets = new Map(siteData.assets.map((asset) => [asset.id, asset]));
679
- const { cssText, classesMap } = generateCss({
835
+ const { cssText, classes } = generateCss({
680
836
  instances: new Map(siteData.build.instances),
681
837
  props: new Map(siteData.build.props),
682
838
  assets,
@@ -688,17 +844,7 @@ Please check webstudio --help for more details`
688
844
  assetBaseUrl,
689
845
  atomic: siteData.build.pages.compiler?.atomicStyles ?? true
690
846
  });
691
- await createFileIfNotExists(join4(generatedDir, "index.css"), cssText);
692
- const routeTemplatesDir = join4(cwd3(), "app/route-templates");
693
- const routeTemplatePath = normalize(join4(routeTemplatesDir, "html.tsx"));
694
- const routeXmlTemplatePath = normalize(join4(routeTemplatesDir, "xml.tsx"));
695
- const defaultSiteMapXmlPath = normalize(
696
- join4(routeTemplatesDir, "default-sitemap.tsx")
697
- );
698
- const routeFileTemplate = await readFile4(routeTemplatePath, "utf8");
699
- const routeXmlFileTemplate = await readFile4(routeXmlTemplatePath, "utf8");
700
- const defaultSiteMapTemplate = await readFile4(defaultSiteMapXmlPath, "utf8");
701
- await rm(routeTemplatesDir, { recursive: true, force: true });
847
+ await createFileIfNotExists(join6(generatedDir, "index.css"), cssText);
702
848
  for (const [pageId, pageComponents] of Object.entries(componentsByPage)) {
703
849
  const scope = createScope([
704
850
  // manually maintained list of occupied identifiers
@@ -709,18 +855,10 @@ Please check webstudio --help for more details`
709
855
  "_props"
710
856
  ]);
711
857
  const namespaces = /* @__PURE__ */ new Map();
712
- const BASE_NAMESPACE = "@webstudio-is/sdk-components-react";
713
- const REMIX_NAMESPACE = "@webstudio-is/sdk-components-react-remix";
714
858
  for (const component of pageComponents) {
715
- const parsed = parseComponentName(component);
716
- let [namespace] = parsed;
717
- const [_namespace, shortName] = parsed;
859
+ const namespace = componentSources.get(component);
718
860
  if (namespace === void 0) {
719
- if (shortName in remixComponentMetas) {
720
- namespace = REMIX_NAMESPACE;
721
- } else {
722
- namespace = BASE_NAMESPACE;
723
- }
861
+ continue;
724
862
  }
725
863
  if (namespaces.has(namespace) === false) {
726
864
  namespaces.set(
@@ -728,6 +866,7 @@ Please check webstudio --help for more details`
728
866
  /* @__PURE__ */ new Set()
729
867
  );
730
868
  }
869
+ const [_namespace, shortName] = parseComponentName(component);
731
870
  namespaces.get(namespace)?.add([shortName, component]);
732
871
  }
733
872
  let componentImports = "";
@@ -768,6 +907,11 @@ Please check webstudio --help for more details`
768
907
  const props = new Map(pageData.build.props);
769
908
  const dataSources = new Map(pageData.build.dataSources);
770
909
  const resources = new Map(pageData.build.resources);
910
+ replaceFormActionsWithResources({
911
+ instances,
912
+ resources,
913
+ props
914
+ });
771
915
  const pageComponent = generateWebstudioComponent({
772
916
  scope,
773
917
  name: "Page",
@@ -784,7 +928,7 @@ Please check webstudio --help for more details`
784
928
  instances,
785
929
  props,
786
930
  dataSources,
787
- classesMap,
931
+ classesMap: classes,
788
932
  indexesWithinAncestors: getIndexesWithinAncestors(
789
933
  projectMetas,
790
934
  instances,
@@ -796,12 +940,8 @@ Please check webstudio --help for more details`
796
940
  // fallback to user email when contact email is empty string
797
941
  projectMeta?.contactEmail || siteData.user?.email || void 0
798
942
  );
799
- const pageMeta = pageData.page.meta;
800
943
  const favIconAsset = assets.get(projectMeta?.faviconAssetId ?? "");
801
- const socialImageAsset = assets.get(pageMeta.socialImageAssetId ?? "");
802
944
  const pagePath = getPagePath(pageData.page.id, siteData.build.pages);
803
- const remixRoute = generateRemixRoute(pagePath);
804
- const fileName = `${remixRoute}.tsx`;
805
945
  const pageExports = `/* eslint-disable */
806
946
  /* This is a auto generated file for building the project */
807
947
 
@@ -816,9 +956,6 @@ Please check webstudio --help for more details`
816
956
  export const favIconAsset: ImageAsset | undefined =
817
957
  ${JSON.stringify(favIconAsset)};
818
958
 
819
- export const socialImageAsset: ImageAsset | undefined =
820
- ${JSON.stringify(socialImageAsset)};
821
-
822
959
  // Font assets on current page (can be preloaded)
823
960
  export const pageFontAssets: FontAsset[] =
824
961
  ${JSON.stringify(pageFontAssets)}
@@ -826,7 +963,7 @@ Please check webstudio --help for more details`
826
963
  export const pageBackgroundImageAssets: ImageAsset[] =
827
964
  ${JSON.stringify(pageBackgroundImageAssets)}
828
965
 
829
- ${remixRoute === "_index" ? `
966
+ ${pagePath === "/" ? `
830
967
  ${projectMeta?.code ? `
831
968
  const Script = ({children, ...props}: Record<string, string | boolean>) => {
832
969
  if (children == null) {
@@ -860,47 +997,59 @@ Please check webstudio --help for more details`
860
997
 
861
998
 
862
999
  import type { PageMeta } from "@webstudio-is/sdk";
863
- ${generateResourcesLoader({
1000
+ ${generateResources({
864
1001
  scope,
865
1002
  page: pageData.page,
866
1003
  dataSources,
1004
+ props,
867
1005
  resources
868
1006
  })}
869
1007
 
870
1008
  ${generatePageMeta({
871
1009
  globalScope: scope,
872
1010
  page: pageData.page,
873
- dataSources
1011
+ dataSources,
1012
+ assets
874
1013
  })}
875
1014
 
876
- ${generateFormsProperties(props)}
877
-
878
1015
  ${generateRemixParams(pageData.page.path)}
879
1016
 
880
1017
  export const projectId = "${siteData.build.projectId}";
881
1018
 
882
1019
  export const contactEmail = ${JSON.stringify(contactEmail)};
883
1020
  `;
884
- const routeFileContent = (documentType === "html" ? routeFileTemplate : routeXmlFileTemplate).replace(
885
- /".*\/__generated__\/_index"/,
886
- `"../__generated__/${remixRoute}"`
887
- ).replace(
888
- /".*\/__generated__\/_index.server"/,
889
- `"../__generated__/${remixRoute}.server"`
890
- );
891
- await createFileIfNotExists(join4(routesDir, fileName), routeFileContent);
892
- await createFileIfNotExists(join4(generatedDir, fileName), pageExports);
893
- await createFileIfNotExists(
894
- join4(generatedDir, `${remixRoute}.server.tsx`),
895
- serverExports
1021
+ const generatedBasename = generateRemixRoute2(pagePath);
1022
+ const clientFile = join6(generatedDir, `${generatedBasename}.tsx`);
1023
+ await createFileIfNotExists(clientFile, pageExports);
1024
+ const serverFile = join6(generatedDir, `${generatedBasename}.server.tsx`);
1025
+ await createFileIfNotExists(serverFile, serverExports);
1026
+ const getTemplates = documentType === "html" ? framework.html : framework.xml;
1027
+ for (const { file, template } of getTemplates({ pagePath })) {
1028
+ const content = template.replaceAll("__CONSTANTS__", importFrom("./app/constants.mjs", file)).replaceAll(
1029
+ "__SITEMAP__",
1030
+ importFrom(`./app/__generated__/$resources.sitemap.xml`, file)
1031
+ ).replaceAll(
1032
+ "__CLIENT__",
1033
+ importFrom(`./app/__generated__/${generatedBasename}`, file)
1034
+ ).replaceAll(
1035
+ "__SERVER__",
1036
+ importFrom(`./app/__generated__/${generatedBasename}.server`, file)
1037
+ ).replaceAll(
1038
+ "__CSS__",
1039
+ importFrom(`./app/__generated__/index.css`, file)
1040
+ );
1041
+ await createFileIfNotExists(file, content);
1042
+ }
1043
+ }
1044
+ for (const { file, template } of framework.defaultSitemap()) {
1045
+ const content = template.replaceAll(
1046
+ "__SITEMAP__",
1047
+ importFrom(`./app/__generated__/$resources.sitemap.xml`, file)
896
1048
  );
1049
+ await createFileIfNotExists(file, content);
897
1050
  }
898
1051
  await createFileIfNotExists(
899
- join4(routesDir, "[sitemap.xml]._index.tsx"),
900
- defaultSiteMapTemplate.replace(/".*\/__generated__\//, `"../__generated__/`)
901
- );
902
- await createFileIfNotExists(
903
- join4(generatedDir, "$resources.sitemap.xml.ts"),
1052
+ join6(generatedDir, "$resources.sitemap.xml.ts"),
904
1053
  `
905
1054
  export const sitemap = ${JSON.stringify(
906
1055
  getStaticSiteMapXml(siteData.build.pages, siteData.build.updatedAt),
@@ -912,15 +1061,23 @@ Please check webstudio --help for more details`
912
1061
  const redirects = siteData.build.pages?.redirects;
913
1062
  if (redirects !== void 0 && redirects.length > 0) {
914
1063
  for (const redirect of redirects) {
915
- const redirectPagePath = generateRemixRoute(redirect.old);
916
- const redirectFileName = `${redirectPagePath}.ts`;
917
- const content = `import { type LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
918
-
919
- export const loader = (arg: LoaderFunctionArgs) => {
920
- return redirect("${redirect.new}", ${redirect.status ?? 301});
921
- };
922
- `;
923
- await createFileIfNotExists(join4(routesDir, redirectFileName), content);
1064
+ const generatedBasename = generateRemixRoute2(redirect.old);
1065
+ await createFileIfNotExists(
1066
+ join6(generatedDir, `${generatedBasename}.ts`),
1067
+ `
1068
+ export const url = "${redirect.new}";
1069
+ export const status = ${redirect.status ?? 301};
1070
+ `
1071
+ );
1072
+ for (const { file, template } of framework.redirect({
1073
+ pagePath: redirect.old
1074
+ })) {
1075
+ const content = template.replaceAll(
1076
+ "__REDIRECT__",
1077
+ importFrom(`./app/__generated__/${generatedBasename}`, file)
1078
+ );
1079
+ await createFileIfNotExists(file, content);
1080
+ }
924
1081
  }
925
1082
  }
926
1083
  if (assetsToDownload.length > 0) {
@@ -945,9 +1102,9 @@ var buildOptions = (yargs) => yargs.option("assets", {
945
1102
  type: "array",
946
1103
  string: true,
947
1104
  default: [],
948
- describe: `Template to use for the build [choices: ${PROJECT_TEMPALTES.join(
949
- ", "
950
- )}]`
1105
+ describe: `Template to use for the build [choices: ${PROJECT_TEMPALTES.map(
1106
+ (item) => item.value
1107
+ ).join(", ")}]`
951
1108
  });
952
1109
  var build = async (options) => {
953
1110
  try {
@@ -966,7 +1123,7 @@ var build = async (options) => {
966
1123
 
967
1124
  // src/commands/init-flow.ts
968
1125
  import { chdir, cwd as cwd4 } from "node:process";
969
- import { join as join5 } from "node:path";
1126
+ import { join as join7 } from "node:path";
970
1127
  import pc2 from "picocolors";
971
1128
  import {
972
1129
  cancel as cancel2,
@@ -978,7 +1135,6 @@ import {
978
1135
  text as text2
979
1136
  } from "@clack/prompts";
980
1137
  import { $ } from "execa";
981
- import { titleCase } from "title-case";
982
1138
  var exitIfCancelled = (value) => {
983
1139
  if (isCancel2(value)) {
984
1140
  cancel2("Project initialization is cancelled");
@@ -1009,8 +1165,8 @@ var initFlow = async (options) => {
1009
1165
  }
1010
1166
  })
1011
1167
  );
1012
- await createFolderIfNotExists(join5(cwd4(), folderName));
1013
- chdir(join5(cwd4(), folderName));
1168
+ await createFolderIfNotExists(join7(cwd4(), folderName));
1169
+ chdir(join7(cwd4(), folderName));
1014
1170
  }
1015
1171
  const shareLink = exitIfCancelled(
1016
1172
  await text2({
@@ -1022,10 +1178,7 @@ var initFlow = async (options) => {
1022
1178
  projectTemplate = exitIfCancelled(
1023
1179
  await select({
1024
1180
  message: "Where would you like to deploy your project?",
1025
- options: PROJECT_TEMPALTES.map((template) => ({
1026
- value: template,
1027
- label: titleCase(template)
1028
- }))
1181
+ options: PROJECT_TEMPALTES
1029
1182
  })
1030
1183
  );
1031
1184
  shouldInstallDeps = exitIfCancelled(
@@ -1039,10 +1192,7 @@ var initFlow = async (options) => {
1039
1192
  projectTemplate = exitIfCancelled(
1040
1193
  await select({
1041
1194
  message: "Where would you like to deploy your project?",
1042
- options: PROJECT_TEMPALTES.map((template) => ({
1043
- value: template,
1044
- label: titleCase(template)
1045
- }))
1195
+ options: PROJECT_TEMPALTES
1046
1196
  })
1047
1197
  );
1048
1198
  }
@@ -1092,7 +1242,7 @@ import makeCLI from "yargs";
1092
1242
  // package.json
1093
1243
  var package_default = {
1094
1244
  name: "webstudio",
1095
- version: "0.167.0",
1245
+ version: "0.173.0",
1096
1246
  description: "Webstudio CLI",
1097
1247
  author: "Webstudio <github@webstudio.is>",
1098
1248
  homepage: "https://webstudio.is",
@@ -1101,6 +1251,12 @@ var package_default = {
1101
1251
  "webstudio-cli": "./bin.js",
1102
1252
  webstudio: "./bin.js"
1103
1253
  },
1254
+ imports: {
1255
+ "#cli": {
1256
+ webstudio: "./src/cli.ts",
1257
+ default: "./lib/cli.js"
1258
+ }
1259
+ },
1104
1260
  files: [
1105
1261
  "lib/*",
1106
1262
  "templates/*",
@@ -1109,13 +1265,13 @@ var package_default = {
1109
1265
  ],
1110
1266
  scripts: {
1111
1267
  typecheck: "tsc",
1112
- checks: "pnpm typecheck",
1113
1268
  build: "rm -rf lib && esbuild src/cli.ts --outdir=lib --bundle --format=esm --packages=external",
1114
- "local-run": "tsx --no-warnings ./src/bin.ts",
1115
- dev: "esbuild src/cli.ts --watch --bundle --format=esm --packages=external --outdir=./lib",
1116
1269
  test: "NODE_OPTIONS=--experimental-vm-modules jest"
1117
1270
  },
1118
1271
  license: "AGPL-3.0-or-later",
1272
+ engines: {
1273
+ node: ">=20.12"
1274
+ },
1119
1275
  dependencies: {
1120
1276
  "@clack/prompts": "^0.7.0",
1121
1277
  "@webstudio-is/http-client": "workspace:*",
@@ -1125,15 +1281,14 @@ var package_default = {
1125
1281
  "@webstudio-is/sdk-components-react": "workspace:*",
1126
1282
  "@webstudio-is/sdk-components-react-radix": "workspace:*",
1127
1283
  "@webstudio-is/sdk-components-react-remix": "workspace:*",
1128
- "change-case": "^5.0.2",
1284
+ "change-case": "^5.4.4",
1129
1285
  deepmerge: "^4.3.1",
1130
1286
  "env-paths": "^3.0.0",
1131
1287
  execa: "^7.2.0",
1132
- parse5: "7.1.2",
1133
1288
  "p-limit": "^4.0.0",
1289
+ parse5: "7.1.2",
1134
1290
  picocolors: "^1.0.1",
1135
1291
  "strip-indent": "^4.0.0",
1136
- "title-case": "^4.1.0",
1137
1292
  yargs: "^17.7.2",
1138
1293
  zod: "^3.22.4"
1139
1294
  },
@@ -1141,24 +1296,26 @@ var package_default = {
1141
1296
  "@jest/globals": "^29.7.0",
1142
1297
  "@netlify/remix-adapter": "^2.4.0",
1143
1298
  "@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",
1299
+ "@remix-run/cloudflare": "^2.10.3",
1300
+ "@remix-run/cloudflare-pages": "^2.10.3",
1301
+ "@remix-run/dev": "^2.10.3",
1302
+ "@remix-run/node": "^2.10.3",
1303
+ "@remix-run/react": "^2.10.3",
1304
+ "@remix-run/server-runtime": "^2.10.3",
1150
1305
  "@types/node": "^20.12.7",
1151
1306
  "@types/react": "^18.2.70",
1152
1307
  "@types/react-dom": "^18.2.25",
1153
1308
  "@types/yargs": "^17.0.32",
1309
+ "@vitejs/plugin-react": "^4.3.1",
1154
1310
  "@webstudio-is/form-handlers": "workspace:*",
1155
1311
  "@webstudio-is/jest-config": "workspace:*",
1156
1312
  "@webstudio-is/tsconfig": "workspace:*",
1157
1313
  react: "18.3.0-canary-14898b6a9-20240318",
1158
1314
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
1159
- typescript: "5.4.5",
1160
- vite: "^5.2.12",
1161
- wrangler: "^3.48.0"
1315
+ typescript: "5.5.2",
1316
+ vike: "^0.4.180",
1317
+ vite: "^5.3.4",
1318
+ wrangler: "^3.63.2"
1162
1319
  }
1163
1320
  };
1164
1321