@blinkk/root 1.0.0-rc.4 → 1.0.0-rc.40

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.
@@ -1,9 +1,12 @@
1
1
  import {
2
+ headersMiddleware,
2
3
  rootProjectMiddleware,
3
4
  sessionMiddleware,
4
5
  trailingSlashMiddleware
5
- } from "./chunk-WNXIRMFF.js";
6
+ } from "./chunk-TZAHHHA4.js";
6
7
  import {
8
+ bundleRootConfig,
9
+ copyDir,
7
10
  copyGlob,
8
11
  createViteServer,
9
12
  dirExists,
@@ -11,24 +14,31 @@ import {
11
14
  fileExists,
12
15
  isDirectory,
13
16
  isJsFile,
17
+ loadBundledConfig,
14
18
  loadJson,
15
19
  loadRootConfig,
16
20
  makeDir,
17
21
  rmDir,
18
- writeFile
19
- } from "./chunk-J2ANSYAE.js";
22
+ writeFile,
23
+ writeJson
24
+ } from "./chunk-ODXHLJOY.js";
20
25
  import {
21
26
  configureServerPlugins,
22
27
  getVitePlugins
23
28
  } from "./chunk-QKBMWK5B.js";
24
29
  import {
30
+ RouteTrie,
25
31
  htmlMinify,
26
32
  htmlPretty,
27
33
  isValidTagName,
28
34
  parseTagNames
29
- } from "./chunk-DFBTOMQF.js";
35
+ } from "./chunk-IUQLRDFW.js";
30
36
 
31
- // src/cli/commands/build.ts
37
+ // src/cli/cli.ts
38
+ import { Command } from "commander";
39
+ import { bgGreen, black } from "kleur/colors";
40
+
41
+ // src/cli/build.ts
32
42
  import path3 from "node:path";
33
43
  import { fileURLToPath } from "node:url";
34
44
  import fsExtra from "fs-extra";
@@ -102,10 +112,9 @@ var ElementGraph = class _ElementGraph {
102
112
  }
103
113
  };
104
114
  async function getElements(rootConfig) {
105
- var _a;
106
115
  const rootDir = rootConfig.rootDir;
107
116
  const elementsDirs = getElementsDirs(rootConfig);
108
- const excludePatterns = ((_a = rootConfig.elements) == null ? void 0 : _a.exclude) || [];
117
+ const excludePatterns = rootConfig.elements?.exclude || [];
109
118
  const excludeElement = (moduleId) => {
110
119
  return excludePatterns.some((pattern) => Boolean(moduleId.match(pattern)));
111
120
  };
@@ -130,11 +139,10 @@ async function getElements(rootConfig) {
130
139
  return graph;
131
140
  }
132
141
  function getElementsDirs(rootConfig) {
133
- var _a;
134
142
  const rootDir = rootConfig.rootDir;
135
143
  const workspaceRoot = searchForWorkspaceRoot(rootDir);
136
144
  const elementsDirs = [path.join(rootDir, "elements")];
137
- const elementsInclude = ((_a = rootConfig.elements) == null ? void 0 : _a.include) || [];
145
+ const elementsInclude = rootConfig.elements?.include || [];
138
146
  for (const dirPath of elementsInclude) {
139
147
  const elementsDir = path.resolve(rootDir, dirPath);
140
148
  if (!directoryContains(rootDir, elementsDir)) {
@@ -312,16 +320,15 @@ async function batchAsyncCalls(data, batchSize, asyncFunction) {
312
320
  return result;
313
321
  }
314
322
 
315
- // src/cli/commands/build.ts
323
+ // src/cli/build.ts
316
324
  var __dirname = path3.dirname(fileURLToPath(import.meta.url));
317
325
  async function build(rootProjectDir, options) {
318
- var _a, _b, _c, _d, _e, _f, _g, _h;
319
- const mode = (options == null ? void 0 : options.mode) || "production";
326
+ const mode = options?.mode || "production";
320
327
  process.env.NODE_ENV = mode;
321
328
  const rootDir = path3.resolve(rootProjectDir || process.cwd());
322
329
  const rootConfig = await loadRootConfig(rootDir, { command: "build" });
323
330
  const distDir = path3.join(rootDir, "dist");
324
- const ssrOnly = (options == null ? void 0 : options.ssrOnly) || false;
331
+ const ssrOnly = options?.ssrOnly || false;
325
332
  console.log();
326
333
  console.log(`${dim("\u2503")} project: ${rootDir}`);
327
334
  console.log(`${dim("\u2503")} output: ${distDir}/html`);
@@ -379,7 +386,7 @@ async function build(rootProjectDir, options) {
379
386
  Object.assign(ssrInput, plugin.ssrInput());
380
387
  }
381
388
  });
382
- const noExternalConfig = (_a = viteConfig.ssr) == null ? void 0 : _a.noExternal;
389
+ const noExternalConfig = viteConfig.ssr?.noExternal;
383
390
  const noExternal = [];
384
391
  if (noExternalConfig) {
385
392
  if (Array.isArray(noExternalConfig)) {
@@ -392,9 +399,9 @@ async function build(rootProjectDir, options) {
392
399
  ...baseConfig,
393
400
  publicDir: false,
394
401
  build: {
395
- ...viteConfig == null ? void 0 : viteConfig.build,
402
+ ...viteConfig?.build,
396
403
  rollupOptions: {
397
- ...(_b = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _b.rollupOptions,
404
+ ...viteConfig?.build?.rollupOptions,
398
405
  input: ssrInput,
399
406
  output: {
400
407
  format: "esm",
@@ -415,16 +422,16 @@ async function build(rootProjectDir, options) {
415
422
  ssr: {
416
423
  ...viteConfig.ssr,
417
424
  target: "node",
418
- noExternal: ["@blinkk/root", ...noExternal]
425
+ noExternal: ["@blinkk/root", "@blinkk/root-cms/richtext", ...noExternal]
419
426
  }
420
427
  });
421
428
  await viteBuild({
422
429
  ...baseConfig,
423
430
  publicDir: false,
424
431
  build: {
425
- ...viteConfig == null ? void 0 : viteConfig.build,
432
+ ...viteConfig?.build,
426
433
  rollupOptions: {
427
- ...(_c = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _c.rollupOptions,
434
+ ...viteConfig?.build?.rollupOptions,
428
435
  input: [...routeFiles],
429
436
  output: {
430
437
  format: "esm",
@@ -432,12 +439,12 @@ async function build(rootProjectDir, options) {
432
439
  assetFileNames: "assets/[name].[hash][extname]",
433
440
  chunkFileNames: "chunks/[hash].min.js",
434
441
  sanitizeFileName,
435
- ...(_e = (_d = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _d.rollupOptions) == null ? void 0 : _e.output
442
+ ...viteConfig?.build?.rollupOptions?.output
436
443
  }
437
444
  },
438
- outDir: path3.join(distDir, "routes"),
445
+ outDir: path3.join(distDir, ".build/routes"),
439
446
  ssr: true,
440
- ssrManifest: true,
447
+ ssrManifest: false,
441
448
  ssrEmitAssets: true,
442
449
  manifest: true,
443
450
  cssCodeSplit: true,
@@ -453,9 +460,9 @@ async function build(rootProjectDir, options) {
453
460
  ...baseConfig,
454
461
  publicDir: false,
455
462
  build: {
456
- ...viteConfig == null ? void 0 : viteConfig.build,
463
+ ...viteConfig?.build,
457
464
  rollupOptions: {
458
- ...(_f = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _f.rollupOptions,
465
+ ...viteConfig?.build?.rollupOptions,
459
466
  input: [...elements, ...bundleScripts],
460
467
  output: {
461
468
  format: "esm",
@@ -463,10 +470,10 @@ async function build(rootProjectDir, options) {
463
470
  assetFileNames: "assets/[name].[hash][extname]",
464
471
  chunkFileNames: "chunks/[hash].min.js",
465
472
  sanitizeFileName,
466
- ...(_h = (_g = viteConfig == null ? void 0 : viteConfig.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output
473
+ ...viteConfig?.build?.rollupOptions?.output
467
474
  }
468
475
  },
469
- outDir: path3.join(distDir, "client"),
476
+ outDir: path3.join(distDir, ".build/client"),
470
477
  ssr: false,
471
478
  ssrManifest: false,
472
479
  manifest: true,
@@ -478,18 +485,22 @@ async function build(rootProjectDir, options) {
478
485
  }
479
486
  });
480
487
  } else {
481
- await writeFile(path3.join(distDir, "client/manifest.json"), "{}");
488
+ await writeFile(
489
+ path3.join(distDir, ".build/client/.vite/manifest.json"),
490
+ "{}"
491
+ );
482
492
  }
493
+ await bundleRootConfig(rootDir, path3.join(distDir, "root.config.js"));
483
494
  await copyGlob(
484
495
  "**/*.css",
485
- path3.join(distDir, "routes"),
486
- path3.join(distDir, "client")
496
+ path3.join(distDir, ".build/routes"),
497
+ path3.join(distDir, ".build/client")
487
498
  );
488
499
  const routesManifest = await loadJson(
489
- path3.join(distDir, "routes/manifest.json")
500
+ path3.join(distDir, ".build/routes/.vite/manifest.json")
490
501
  );
491
502
  const clientManifest = await loadJson(
492
- path3.join(distDir, "client/manifest.json")
503
+ path3.join(distDir, ".build/client/.vite/manifest.json")
493
504
  );
494
505
  function collectRouteCss(asset, cssDeps, visited) {
495
506
  if (!asset || !asset.file || visited.has(asset.file)) {
@@ -525,12 +536,12 @@ async function build(rootProjectDir, options) {
525
536
  );
526
537
  const rootManifest = assetMap.toJson();
527
538
  await writeFile(
528
- path3.join(distDir, "client/root-manifest.json"),
539
+ path3.join(distDir, ".root/manifest.json"),
529
540
  JSON.stringify(rootManifest, null, 2)
530
541
  );
531
542
  const elementGraphJson = elementGraph.toJson();
532
543
  await writeFile(
533
- path3.join(distDir, "client/root-element-graph.json"),
544
+ path3.join(distDir, ".root/elements.json"),
534
545
  JSON.stringify(elementGraphJson, null, 2)
535
546
  );
536
547
  const buildDir = path3.join(distDir, "html");
@@ -540,25 +551,37 @@ async function build(rootProjectDir, options) {
540
551
  } else {
541
552
  await makeDir(buildDir);
542
553
  }
554
+ const seenAssets = /* @__PURE__ */ new Set();
555
+ async function copyAssetToDistHtml(assetUrl) {
556
+ if (seenAssets.has(assetUrl)) {
557
+ return;
558
+ }
559
+ seenAssets.add(assetUrl);
560
+ const assetRelPath = assetUrl.slice(1);
561
+ const assetFrom = path3.join(distDir, ".build/client", assetRelPath);
562
+ const assetTo = path3.join(buildDir, assetRelPath);
563
+ if (!await fileExists(assetFrom)) {
564
+ console.log(`${assetFrom} does not exist`);
565
+ return;
566
+ }
567
+ await fsExtra.copy(assetFrom, assetTo);
568
+ printFileOutput(fileSize(assetTo), "dist/html/", assetRelPath);
569
+ }
543
570
  console.log("\njs/css output:");
544
571
  await Promise.all(
545
572
  Object.keys(rootManifest).map(async (src) => {
573
+ const assetData = rootManifest[src];
546
574
  if (isRouteFile(src)) {
575
+ const importedCss = assetData.importedCss || [];
576
+ for (const cssAssetUrl of importedCss) {
577
+ await copyAssetToDistHtml(cssAssetUrl);
578
+ }
547
579
  return;
548
580
  }
549
- const assetData = rootManifest[src];
550
581
  if (!assetData.assetUrl) {
551
582
  return;
552
583
  }
553
- const assetRelPath = assetData.assetUrl.slice(1);
554
- const assetFrom = path3.join(distDir, "client", assetRelPath);
555
- const assetTo = path3.join(buildDir, assetRelPath);
556
- if (!await fileExists(assetFrom)) {
557
- console.log(`${assetFrom} does not exist`);
558
- return;
559
- }
560
- await fsExtra.copy(assetFrom, assetTo);
561
- printFileOutput(fileSize(assetTo), "dist/html/", assetRelPath);
584
+ await copyAssetToDistHtml(assetData.assetUrl);
562
585
  })
563
586
  );
564
587
  if (!ssrOnly) {
@@ -566,7 +589,7 @@ async function build(rootProjectDir, options) {
566
589
  const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
567
590
  const sitemap = await renderer.getSitemap();
568
591
  console.log("\nhtml output:");
569
- const batchSize = Number((options == null ? void 0 : options.concurrency) || 10);
592
+ const batchSize = Number(options?.concurrency || 10);
570
593
  await batchAsyncCalls(Object.keys(sitemap), batchSize, async (urlPath) => {
571
594
  const { route, params } = sitemap[urlPath];
572
595
  try {
@@ -651,8 +674,164 @@ function formatParams(params) {
651
674
  }).join("\n");
652
675
  }
653
676
 
654
- // src/cli/commands/dev.ts
655
- import path5 from "node:path";
677
+ // src/cli/create-package.ts
678
+ import { promises as fs3 } from "node:fs";
679
+ import path4 from "node:path";
680
+ import { build as esbuild } from "esbuild";
681
+ async function createPackage(rootProjectDir, options) {
682
+ const mode = options?.mode || "production";
683
+ process.env.NODE_ENV = mode;
684
+ const rootDir = path4.resolve(rootProjectDir || process.cwd());
685
+ const distDir = path4.join(rootDir, "dist");
686
+ const target = options?.target || await getDefaultTarget(rootDir);
687
+ const outDir = path4.resolve(options?.out || target || "out");
688
+ await build(rootProjectDir, { ssrOnly: true, mode });
689
+ await rmDir(outDir);
690
+ await makeDir(outDir);
691
+ await copyDir(distDir, path4.resolve(outDir, "dist"));
692
+ const collectionsDir = path4.resolve(rootDir, "collections");
693
+ if (await dirExists(collectionsDir)) {
694
+ await copyDir(collectionsDir, path4.join(outDir, "collections"));
695
+ }
696
+ const packageJson = await generatePackageJson(rootDir);
697
+ if (target === "appengine") {
698
+ await onAppEngine({ rootDir, packageJson, outDir });
699
+ } else if (target === "firebase") {
700
+ await onFirebase({ rootDir, packageJson, outDir });
701
+ }
702
+ await writeJson(path4.resolve(outDir, "package.json"), packageJson);
703
+ console.log("done!");
704
+ console.log(`saved package to ${outDir}`);
705
+ }
706
+ async function getDefaultTarget(rootDir) {
707
+ const firebaseConfigPath = path4.resolve(rootDir, "firebase.json");
708
+ if (await fileExists(firebaseConfigPath)) {
709
+ return "firebase";
710
+ }
711
+ const appEngineConfigPath = path4.resolve(rootDir, "app.yaml");
712
+ if (await fileExists(appEngineConfigPath)) {
713
+ return "appengine";
714
+ }
715
+ return null;
716
+ }
717
+ async function generatePackageJson(rootDir) {
718
+ const packageJson = await loadJson(
719
+ path4.resolve(rootDir, "package.json")
720
+ );
721
+ if (packageJson.peerDependencies) {
722
+ await updatePeerDepsFromWorkspace(rootDir, packageJson);
723
+ }
724
+ return packageJson;
725
+ }
726
+ async function updatePeerDepsFromWorkspace(rootDir, packageJson) {
727
+ const requiredPeerDeps = getRequiredPeerDeps(packageJson);
728
+ if (requiredPeerDeps.length === 0) {
729
+ return;
730
+ }
731
+ const workspaceRoot = await findWorkspaceRoot(rootDir);
732
+ if (!workspaceRoot) {
733
+ return;
734
+ }
735
+ const workspacePackageJsonPath = path4.resolve(workspaceRoot, "package.json");
736
+ const workspacePackageJson = await loadJson(
737
+ workspacePackageJsonPath
738
+ );
739
+ const workspaceDeps = workspacePackageJson.dependencies || {};
740
+ function setDep(name, value) {
741
+ if (!packageJson.dependencies) {
742
+ packageJson.dependencies = {};
743
+ }
744
+ packageJson.dependencies[name] = value;
745
+ }
746
+ for (const peerDep of requiredPeerDeps) {
747
+ const workspaceDepVersion = workspaceDeps[peerDep];
748
+ if (workspaceDepVersion) {
749
+ setDep(peerDep, workspaceDepVersion);
750
+ } else {
751
+ console.warn(
752
+ `could not find peer dep "${peerDep}" in workspace "${workspacePackageJsonPath}"`
753
+ );
754
+ }
755
+ }
756
+ }
757
+ async function findWorkspaceRoot(rootDir) {
758
+ const parentDir = path4.dirname(rootDir);
759
+ if (parentDir === "/") {
760
+ return null;
761
+ }
762
+ const pnpmWorkspaceFile = path4.resolve(parentDir, "pnpm-workspace.yaml");
763
+ if (await fileExists(pnpmWorkspaceFile)) {
764
+ return parentDir;
765
+ }
766
+ const packageJsonPath = path4.resolve(parentDir, "package.json");
767
+ if (await fileExists(packageJsonPath)) {
768
+ const parentPackageJson = await loadJson(packageJsonPath);
769
+ if (parentPackageJson && parentPackageJson.workspaces) {
770
+ return parentDir;
771
+ }
772
+ }
773
+ return findWorkspaceRoot(parentDir);
774
+ }
775
+ function getRequiredPeerDeps(packageJson) {
776
+ const requiredPeerDeps = [];
777
+ const peerDeps = packageJson.peerDependencies || {};
778
+ const peerDepsMeta = packageJson.peerDependenciesMeta || {};
779
+ for (const key in peerDeps) {
780
+ const meta = peerDepsMeta[key];
781
+ if (!meta?.optional) {
782
+ requiredPeerDeps.push(key);
783
+ }
784
+ }
785
+ return requiredPeerDeps;
786
+ }
787
+ async function onAppEngine(options) {
788
+ const { rootDir, outDir } = options;
789
+ const configPath = path4.resolve(rootDir, "app.yaml");
790
+ if (await fileExists(configPath)) {
791
+ await fs3.copyFile(configPath, path4.resolve(outDir, "app.yaml"));
792
+ }
793
+ }
794
+ async function onFirebase(options) {
795
+ const { rootDir, outDir } = options;
796
+ const outBasename = path4.basename(outDir);
797
+ if (outBasename === "functions") {
798
+ const indexTsFile = path4.resolve(rootDir, "index.ts");
799
+ if (await fileExists(indexTsFile)) {
800
+ await bundleTsFile(indexTsFile, path4.resolve(outDir, "index.js"));
801
+ }
802
+ }
803
+ }
804
+ async function bundleTsFile(srcPath, outPath) {
805
+ await esbuild({
806
+ entryPoints: [srcPath],
807
+ bundle: true,
808
+ minify: true,
809
+ platform: "node",
810
+ outfile: outPath,
811
+ sourcemap: "inline",
812
+ metafile: true,
813
+ format: "esm",
814
+ plugins: [
815
+ {
816
+ name: "externalize-deps",
817
+ setup(build2) {
818
+ build2.onResolve({ filter: /.*/ }, (args) => {
819
+ const id = args.path;
820
+ if (id[0] !== "." && !path4.isAbsolute(id)) {
821
+ return {
822
+ external: true
823
+ };
824
+ }
825
+ return null;
826
+ });
827
+ }
828
+ }
829
+ ]
830
+ });
831
+ }
832
+
833
+ // src/cli/dev.ts
834
+ import path6 from "node:path";
656
835
  import { fileURLToPath as fileURLToPath2 } from "node:url";
657
836
  import cookieParser from "cookie-parser";
658
837
  import { default as express } from "express";
@@ -683,8 +862,53 @@ var Hooks = class {
683
862
  }
684
863
  };
685
864
 
865
+ // src/middleware/redirects.ts
866
+ function redirectsMiddleware(options) {
867
+ const routeTrie = new RouteTrie();
868
+ const redirects = options.redirects || [];
869
+ redirects.forEach((redirect) => {
870
+ if (!verifyRedirectConfig(redirect)) {
871
+ console.warn("ignoring invalid redirect config:", redirect);
872
+ return;
873
+ }
874
+ routeTrie.add(redirect.source, redirect);
875
+ });
876
+ return (req, res, next) => {
877
+ const [redirect, params] = routeTrie.get(req.path);
878
+ if (redirect) {
879
+ const destination = replaceParams(redirect.destination, params);
880
+ const code = redirect.type || 302;
881
+ res.redirect(code, destination);
882
+ return;
883
+ }
884
+ next();
885
+ };
886
+ }
887
+ function verifyRedirectConfig(redirect) {
888
+ if (!redirect.source) {
889
+ return false;
890
+ }
891
+ if (!redirect.destination) {
892
+ return false;
893
+ }
894
+ return true;
895
+ }
896
+ function replaceParams(urlPathFormat, params) {
897
+ const urlPath = urlPathFormat.replaceAll(
898
+ /\[\[?(\.\.\.)?([\w\-_]*)\]?\]/g,
899
+ (match, _wildcard, key) => {
900
+ const val = params[key];
901
+ if (!val) {
902
+ throw new Error(`unreplaced param ${match} in url: ${urlPathFormat}`);
903
+ }
904
+ return val;
905
+ }
906
+ );
907
+ return urlPath;
908
+ }
909
+
686
910
  // src/render/asset-map/dev-asset-map.ts
687
- import path4 from "node:path";
911
+ import path5 from "node:path";
688
912
  import { searchForWorkspaceRoot as searchForWorkspaceRoot2 } from "vite";
689
913
  var DevServerAssetMap = class {
690
914
  constructor(rootConfig, moduleGraph) {
@@ -692,7 +916,7 @@ var DevServerAssetMap = class {
692
916
  this.moduleGraph = moduleGraph;
693
917
  }
694
918
  async get(src) {
695
- const file = path4.resolve(this.rootConfig.rootDir, src);
919
+ const file = path5.resolve(this.rootConfig.rootDir, src);
696
920
  const viteModules = this.moduleGraph.getModulesByFile(file);
697
921
  if (viteModules && viteModules.size > 0) {
698
922
  const [viteModule] = viteModules;
@@ -724,7 +948,7 @@ var DevServerAssetMap = class {
724
948
  return null;
725
949
  }
726
950
  filePathToSrc(file) {
727
- return path4.relative(this.rootConfig.rootDir, file);
951
+ return path5.relative(this.rootConfig.rootDir, file);
728
952
  }
729
953
  };
730
954
  var DevServerAsset = class _DevServerAsset {
@@ -761,7 +985,7 @@ var DevServerAsset = class _DevServerAsset {
761
985
  return;
762
986
  }
763
987
  visited.add(asset.moduleId);
764
- const parts = path4.parse(asset.assetUrl);
988
+ const parts = path5.parse(asset.assetUrl);
765
989
  if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
766
990
  urls.add(asset.assetUrl);
767
991
  }
@@ -788,7 +1012,7 @@ var DevServerAsset = class _DevServerAsset {
788
1012
  }
789
1013
  visited.add(asset.assetUrl);
790
1014
  if (asset.src.endsWith(".scss")) {
791
- const parts = path4.parse(asset.src);
1015
+ const parts = path5.parse(asset.src);
792
1016
  if (!parts.name.startsWith("_")) {
793
1017
  urls.add(asset.assetUrl);
794
1018
  }
@@ -854,47 +1078,63 @@ function randString(len) {
854
1078
  return result.join("");
855
1079
  }
856
1080
 
857
- // src/cli/commands/dev.ts
858
- var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
1081
+ // src/cli/dev.ts
1082
+ var __dirname2 = path6.dirname(fileURLToPath2(import.meta.url));
859
1083
  async function dev(rootProjectDir, options) {
860
1084
  process.env.NODE_ENV = "development";
861
- const rootDir = path5.resolve(rootProjectDir || process.cwd());
1085
+ const rootDir = path6.resolve(rootProjectDir || process.cwd());
862
1086
  const defaultPort = parseInt(process.env.PORT || "4007");
863
- const host = (options == null ? void 0 : options.host) || "localhost";
1087
+ const host = options?.host || "localhost";
864
1088
  const port = await findOpenPort(defaultPort, defaultPort + 10);
1089
+ const server = await createDevServer({ rootDir, port });
1090
+ const rootConfig = server.get("rootConfig");
1091
+ const basePath = rootConfig.base || "";
865
1092
  console.log();
866
1093
  console.log(`${dim2("\u2503")} project: ${rootDir}`);
867
- console.log(`${dim2("\u2503")} server: http://${host}:${port}`);
1094
+ console.log(`${dim2("\u2503")} server: http://${host}:${port}${basePath}`);
1095
+ if (testCmsEnabled(rootConfig)) {
1096
+ console.log(`${dim2("\u2503")} cms: http://${host}:${port}/cms/`);
1097
+ }
868
1098
  console.log(`${dim2("\u2503")} mode: development`);
869
1099
  console.log();
870
- const server = await createDevServer({ rootDir, port });
871
1100
  server.listen(port, host);
872
1101
  }
873
1102
  async function createDevServer(options) {
874
- var _a;
875
- const rootDir = path5.resolve((options == null ? void 0 : options.rootDir) || process.cwd());
1103
+ const rootDir = path6.resolve(options?.rootDir || process.cwd());
876
1104
  const rootConfig = await loadRootConfig(rootDir, { command: "dev" });
877
- const port = options == null ? void 0 : options.port;
1105
+ const port = options?.port;
878
1106
  const server = express();
1107
+ server.set("rootConfig", rootConfig);
879
1108
  server.disable("x-powered-by");
1109
+ const { viteServer, viteMiddleware } = await createViteMiddleware({
1110
+ rootConfig,
1111
+ port
1112
+ });
880
1113
  server.use(rootProjectMiddleware({ rootConfig }));
881
- server.use(await viteServerMiddleware({ rootConfig, port }));
1114
+ server.use(viteMiddleware);
882
1115
  server.use(hooksMiddleware());
883
- const sessionCookieSecret = ((_a = rootConfig.server) == null ? void 0 : _a.sessionCookieSecret) || randString(36);
1116
+ const sessionCookieSecret = rootConfig.server?.sessionCookieSecret || randString(36);
884
1117
  server.use(cookieParser(sessionCookieSecret));
885
1118
  server.use(sessionMiddleware());
886
1119
  const plugins = rootConfig.plugins || [];
887
1120
  await configureServerPlugins(
888
1121
  server,
889
1122
  async () => {
890
- var _a2;
891
- const userMiddlewares = ((_a2 = rootConfig.server) == null ? void 0 : _a2.middlewares) || [];
1123
+ const userMiddlewares = rootConfig.server?.middlewares || [];
892
1124
  for (const middleware of userMiddlewares) {
893
1125
  server.use(middleware);
894
1126
  }
895
- const publicDir = path5.join(rootDir, "public");
1127
+ if (rootConfig.server?.redirects) {
1128
+ server.use(
1129
+ redirectsMiddleware({ redirects: rootConfig.server.redirects })
1130
+ );
1131
+ }
1132
+ if (rootConfig.server?.headers) {
1133
+ server.use(headersMiddleware({ rootConfig }));
1134
+ }
1135
+ const publicDir = path6.join(rootDir, "public");
896
1136
  if (await dirExists(publicDir)) {
897
- server.use(sirv(publicDir, { dev: false }));
1137
+ server.use(rootPublicDirMiddleware({ publicDir, viteServer }));
898
1138
  }
899
1139
  server.use(trailingSlashMiddleware({ rootConfig }));
900
1140
  server.use(rootDevServerMiddleware());
@@ -906,7 +1146,7 @@ async function createDevServer(options) {
906
1146
  );
907
1147
  return server;
908
1148
  }
909
- async function viteServerMiddleware(options) {
1149
+ async function createViteMiddleware(options) {
910
1150
  const rootConfig = options.rootConfig;
911
1151
  const rootDir = rootConfig.rootDir;
912
1152
  let elementGraph = await getElements(rootConfig);
@@ -914,10 +1154,10 @@ async function viteServerMiddleware(options) {
914
1154
  return sourceFile.relPath;
915
1155
  });
916
1156
  const bundleScripts = [];
917
- if (await isDirectory(path5.join(rootDir, "bundles"))) {
1157
+ if (await isDirectory(path6.join(rootDir, "bundles"))) {
918
1158
  const bundleFiles = await glob3("bundles/*", { cwd: rootDir });
919
1159
  bundleFiles.forEach((file) => {
920
- const parts = path5.parse(file);
1160
+ const parts = path6.parse(file);
921
1161
  if (isJsFile(parts.base)) {
922
1162
  bundleScripts.push(file);
923
1163
  }
@@ -929,7 +1169,7 @@ async function viteServerMiddleware(options) {
929
1169
  optimizeDeps
930
1170
  });
931
1171
  function isInElementsDir(changedFilePath) {
932
- const filePath = path5.resolve(changedFilePath);
1172
+ const filePath = path6.resolve(changedFilePath);
933
1173
  const elementsDirs = getElementsDirs(rootConfig);
934
1174
  return elementsDirs.some((dirPath) => filePath.startsWith(dirPath));
935
1175
  }
@@ -945,10 +1185,10 @@ async function viteServerMiddleware(options) {
945
1185
  console.log(`element deleted: ${filePath}`);
946
1186
  }
947
1187
  });
948
- return async (req, res, next) => {
1188
+ const viteMiddleware = async (req, res, next) => {
949
1189
  try {
950
1190
  req.viteServer = viteServer;
951
- const renderModulePath = path5.resolve(__dirname2, "./render.js");
1191
+ const renderModulePath = path6.resolve(__dirname2, "./render.js");
952
1192
  const render = await viteServer.ssrLoadModule(
953
1193
  renderModulePath
954
1194
  );
@@ -962,6 +1202,28 @@ async function viteServerMiddleware(options) {
962
1202
  next(e);
963
1203
  }
964
1204
  };
1205
+ return { viteServer, viteMiddleware };
1206
+ }
1207
+ function rootPublicDirMiddleware(options) {
1208
+ const publicDir = options.publicDir;
1209
+ const sirvOptions = { dev: false };
1210
+ let handler = sirv(publicDir, sirvOptions);
1211
+ const reloadPublicDirCache = debounce(() => {
1212
+ handler = sirv(publicDir, sirvOptions);
1213
+ }, 1e3);
1214
+ function isInPublicDir(changedFilePath) {
1215
+ const filePath = path6.resolve(changedFilePath);
1216
+ return filePath.startsWith(publicDir);
1217
+ }
1218
+ const watcher = options.viteServer.watcher;
1219
+ watcher.on("all", (event, filepath) => {
1220
+ if (isInPublicDir(filepath)) {
1221
+ reloadPublicDirCache();
1222
+ }
1223
+ });
1224
+ return (req, res, next) => {
1225
+ handler(req, res, next);
1226
+ };
965
1227
  }
966
1228
  function rootDevServerMiddleware() {
967
1229
  return async (req, res, next) => {
@@ -980,7 +1242,7 @@ function rootDevServer404Middleware() {
980
1242
  console.error(`\u2753 404 ${req.originalUrl}`);
981
1243
  if (req.renderer) {
982
1244
  const url = req.path;
983
- const ext = path5.extname(url);
1245
+ const ext = path6.extname(url);
984
1246
  if (!ext) {
985
1247
  const renderer = req.renderer;
986
1248
  const data = await renderer.renderDevServer404(req);
@@ -998,7 +1260,7 @@ function rootDevServer500Middleware() {
998
1260
  console.error(String(err.stack || err));
999
1261
  if (req.renderer) {
1000
1262
  const url = req.path;
1001
- const ext = path5.extname(url);
1263
+ const ext = path6.extname(url);
1002
1264
  if (!ext) {
1003
1265
  const renderer = req.renderer;
1004
1266
  const data = await renderer.renderDevServer500(req, err);
@@ -1010,9 +1272,20 @@ function rootDevServer500Middleware() {
1010
1272
  next(err);
1011
1273
  };
1012
1274
  }
1275
+ function testCmsEnabled(rootConfig) {
1276
+ const plugins = rootConfig.plugins || [];
1277
+ return Boolean(plugins.find((plugin) => plugin.name === "root-cms"));
1278
+ }
1279
+ function debounce(fn, timeout) {
1280
+ let timer;
1281
+ return (...args) => {
1282
+ clearTimeout(timer);
1283
+ timer = setTimeout(() => fn(...args), timeout);
1284
+ };
1285
+ }
1013
1286
 
1014
- // src/cli/commands/preview.ts
1015
- import path6 from "node:path";
1287
+ // src/cli/preview.ts
1288
+ import path7 from "node:path";
1016
1289
  import compression from "compression";
1017
1290
  import cookieParser2 from "cookie-parser";
1018
1291
  import { default as express2 } from "express";
@@ -1020,10 +1293,10 @@ import { dim as dim3 } from "kleur/colors";
1020
1293
  import sirv2 from "sirv";
1021
1294
  async function preview(rootProjectDir, options) {
1022
1295
  process.env.NODE_ENV = "development";
1023
- const rootDir = path6.resolve(rootProjectDir || process.cwd());
1296
+ const rootDir = path7.resolve(rootProjectDir || process.cwd());
1024
1297
  const server = await createPreviewServer({ rootDir });
1025
1298
  const port = parseInt(process.env.PORT || "4007");
1026
- const host = (options == null ? void 0 : options.host) || "localhost";
1299
+ const host = options?.host || "localhost";
1027
1300
  console.log();
1028
1301
  console.log(`${dim3("\u2503")} project: ${rootDir}`);
1029
1302
  console.log(`${dim3("\u2503")} server: http://${host}:${port}`);
@@ -1032,29 +1305,35 @@ async function preview(rootProjectDir, options) {
1032
1305
  server.listen(port, host);
1033
1306
  }
1034
1307
  async function createPreviewServer(options) {
1035
- var _a;
1036
1308
  const rootDir = options.rootDir;
1037
- const rootConfig = await loadRootConfig(rootDir, { command: "preview" });
1038
- const distDir = path6.join(rootDir, "dist");
1309
+ const rootConfig = await loadBundledConfig(rootDir, { command: "preview" });
1310
+ const distDir = path7.join(rootDir, "dist");
1039
1311
  const server = express2();
1040
1312
  server.disable("x-powered-by");
1041
1313
  server.use(compression());
1042
1314
  server.use(rootProjectMiddleware({ rootConfig }));
1043
1315
  server.use(await rootPreviewRendererMiddleware({ rootConfig, distDir }));
1044
1316
  server.use(hooksMiddleware());
1045
- const sessionCookieSecret = ((_a = rootConfig.server) == null ? void 0 : _a.sessionCookieSecret) || randString(36);
1317
+ const sessionCookieSecret = rootConfig.server?.sessionCookieSecret || randString(36);
1046
1318
  server.use(cookieParser2(sessionCookieSecret));
1047
1319
  server.use(sessionMiddleware());
1048
1320
  const plugins = rootConfig.plugins || [];
1049
1321
  configureServerPlugins(
1050
1322
  server,
1051
1323
  async () => {
1052
- var _a2;
1053
- const userMiddlewares = ((_a2 = rootConfig.server) == null ? void 0 : _a2.middlewares) || [];
1324
+ const userMiddlewares = rootConfig.server?.middlewares || [];
1054
1325
  userMiddlewares.forEach((middleware) => {
1055
1326
  server.use(middleware);
1056
1327
  });
1057
- const publicDir = path6.join(distDir, "html");
1328
+ if (rootConfig.server?.redirects) {
1329
+ server.use(
1330
+ redirectsMiddleware({ redirects: rootConfig.server.redirects })
1331
+ );
1332
+ }
1333
+ if (rootConfig.server?.headers) {
1334
+ server.use(headersMiddleware({ rootConfig }));
1335
+ }
1336
+ const publicDir = path7.join(distDir, "html");
1058
1337
  server.use(sirv2(publicDir, { dev: false }));
1059
1338
  server.use(trailingSlashMiddleware({ rootConfig }));
1060
1339
  server.use(rootPreviewServerMiddleware());
@@ -1068,20 +1347,17 @@ async function createPreviewServer(options) {
1068
1347
  }
1069
1348
  async function rootPreviewRendererMiddleware(options) {
1070
1349
  const { distDir, rootConfig } = options;
1071
- const render = await import(path6.join(distDir, "server/render.js"));
1072
- const manifestPath = path6.join(distDir, "client/root-manifest.json");
1350
+ const render = await import(path7.join(distDir, "server/render.js"));
1351
+ const manifestPath = path7.join(distDir, ".root/manifest.json");
1073
1352
  if (!await fileExists(manifestPath)) {
1074
1353
  throw new Error(
1075
1354
  `could not find ${manifestPath}. run \`root build\` before \`root preview\`.`
1076
1355
  );
1077
1356
  }
1078
- const elementGraphJsonPath = path6.join(
1079
- distDir,
1080
- "client/root-element-graph.json"
1081
- );
1357
+ const elementGraphJsonPath = path7.join(distDir, ".root/elements.json");
1082
1358
  if (!await fileExists(elementGraphJsonPath)) {
1083
1359
  throw new Error(
1084
- `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
1360
+ `could not find ${elementGraphJsonPath}. run \`root build\` before \`root preview\`.`
1085
1361
  );
1086
1362
  }
1087
1363
  const rootManifest = await loadJson(manifestPath);
@@ -1103,7 +1379,7 @@ function rootPreviewServerMiddleware() {
1103
1379
  try {
1104
1380
  console.error(`error rendering ${req.originalUrl}`);
1105
1381
  console.error(e.stack || e);
1106
- const { html } = await renderer.renderError(e);
1382
+ const { html } = await renderer.renderDevServer500(req, e);
1107
1383
  res.status(500).set({ "Content-Type": "text/html" }).end(html);
1108
1384
  } catch (e2) {
1109
1385
  console.error("failed to render custom error");
@@ -1118,7 +1394,7 @@ function rootPreviewServer404Middleware() {
1118
1394
  console.error(`\u2753 404 ${req.originalUrl}`);
1119
1395
  if (req.renderer) {
1120
1396
  const url = req.path;
1121
- const ext = path6.extname(url);
1397
+ const ext = path7.extname(url);
1122
1398
  if (!ext) {
1123
1399
  const renderer = req.renderer;
1124
1400
  const data = await renderer.render404({ currentPath: url });
@@ -1136,7 +1412,7 @@ function rootPreviewServer500Middleware() {
1136
1412
  console.error(String(err.stack || err));
1137
1413
  if (req.renderer) {
1138
1414
  const url = req.path;
1139
- const ext = path6.extname(url);
1415
+ const ext = path7.extname(url);
1140
1416
  if (!ext) {
1141
1417
  const renderer = req.renderer;
1142
1418
  const data = await renderer.renderDevServer500(req, err);
@@ -1149,8 +1425,8 @@ function rootPreviewServer500Middleware() {
1149
1425
  };
1150
1426
  }
1151
1427
 
1152
- // src/cli/commands/start.ts
1153
- import path7 from "node:path";
1428
+ // src/cli/start.ts
1429
+ import path8 from "node:path";
1154
1430
  import compression2 from "compression";
1155
1431
  import cookieParser3 from "cookie-parser";
1156
1432
  import { default as express3 } from "express";
@@ -1158,10 +1434,10 @@ import { dim as dim4 } from "kleur/colors";
1158
1434
  import sirv3 from "sirv";
1159
1435
  async function start(rootProjectDir, options) {
1160
1436
  process.env.NODE_ENV = "production";
1161
- const rootDir = path7.resolve(rootProjectDir || process.cwd());
1437
+ const rootDir = path8.resolve(rootProjectDir || process.cwd());
1162
1438
  const server = await createProdServer({ rootDir });
1163
1439
  const port = parseInt(process.env.PORT || "4007");
1164
- const host = (options == null ? void 0 : options.host) || "localhost";
1440
+ const host = options?.host || "localhost";
1165
1441
  console.log();
1166
1442
  console.log(`${dim4("\u2503")} project: ${rootDir}`);
1167
1443
  console.log(`${dim4("\u2503")} server: http://${host}:${port}`);
@@ -1170,29 +1446,35 @@ async function start(rootProjectDir, options) {
1170
1446
  server.listen(port, host);
1171
1447
  }
1172
1448
  async function createProdServer(options) {
1173
- var _a;
1174
1449
  const rootDir = options.rootDir;
1175
- const rootConfig = await loadRootConfig(rootDir, { command: "start" });
1176
- const distDir = path7.join(rootDir, "dist");
1450
+ const rootConfig = await loadBundledConfig(rootDir, { command: "start" });
1451
+ const distDir = path8.join(rootDir, "dist");
1177
1452
  const server = express3();
1178
1453
  server.disable("x-powered-by");
1179
1454
  server.use(compression2());
1180
1455
  server.use(rootProjectMiddleware({ rootConfig }));
1181
1456
  server.use(await rootProdRendererMiddleware({ rootConfig, distDir }));
1182
1457
  server.use(hooksMiddleware());
1183
- const sessionCookieSecret = ((_a = rootConfig.server) == null ? void 0 : _a.sessionCookieSecret) || randString(36);
1458
+ const sessionCookieSecret = rootConfig.server?.sessionCookieSecret || randString(36);
1184
1459
  server.use(cookieParser3(sessionCookieSecret));
1185
1460
  server.use(sessionMiddleware());
1186
1461
  const plugins = rootConfig.plugins || [];
1187
1462
  configureServerPlugins(
1188
1463
  server,
1189
1464
  async () => {
1190
- var _a2;
1191
- const userMiddlewares = ((_a2 = rootConfig.server) == null ? void 0 : _a2.middlewares) || [];
1465
+ const userMiddlewares = rootConfig.server?.middlewares || [];
1192
1466
  userMiddlewares.forEach((middleware) => {
1193
1467
  server.use(middleware);
1194
1468
  });
1195
- const publicDir = path7.join(distDir, "html");
1469
+ if (rootConfig.server?.redirects) {
1470
+ server.use(
1471
+ redirectsMiddleware({ redirects: rootConfig.server.redirects })
1472
+ );
1473
+ }
1474
+ if (rootConfig.server?.headers) {
1475
+ server.use(headersMiddleware({ rootConfig }));
1476
+ }
1477
+ const publicDir = path8.join(distDir, "html");
1196
1478
  server.use(sirv3(publicDir, { dev: false }));
1197
1479
  server.use(trailingSlashMiddleware({ rootConfig }));
1198
1480
  server.use(rootProdServerMiddleware());
@@ -1206,17 +1488,14 @@ async function createProdServer(options) {
1206
1488
  }
1207
1489
  async function rootProdRendererMiddleware(options) {
1208
1490
  const { distDir, rootConfig } = options;
1209
- const render = await import(path7.join(distDir, "server/render.js"));
1210
- const manifestPath = path7.join(distDir, "client/root-manifest.json");
1491
+ const render = await import(path8.join(distDir, "server/render.js"));
1492
+ const manifestPath = path8.join(distDir, ".root/manifest.json");
1211
1493
  if (!await fileExists(manifestPath)) {
1212
1494
  throw new Error(
1213
1495
  `could not find ${manifestPath}. run \`root build\` before \`root start\`.`
1214
1496
  );
1215
1497
  }
1216
- const elementGraphJsonPath = path7.join(
1217
- distDir,
1218
- "client/root-element-graph.json"
1219
- );
1498
+ const elementGraphJsonPath = path8.join(distDir, ".root/elements.json");
1220
1499
  if (!await fileExists(elementGraphJsonPath)) {
1221
1500
  throw new Error(
1222
1501
  `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
@@ -1256,7 +1535,7 @@ function rootProdServer404Middleware() {
1256
1535
  console.error(`\u2753 404 ${req.originalUrl}`);
1257
1536
  if (req.renderer) {
1258
1537
  const url = req.path;
1259
- const ext = path7.extname(url);
1538
+ const ext = path8.extname(url);
1260
1539
  if (!ext) {
1261
1540
  const renderer = req.renderer;
1262
1541
  const data = await renderer.render404({ currentPath: url });
@@ -1274,7 +1553,7 @@ function rootProdServer500Middleware() {
1274
1553
  console.error(String(err.stack || err));
1275
1554
  if (req.renderer) {
1276
1555
  const url = req.path;
1277
- const ext = path7.extname(url);
1556
+ const ext = path8.extname(url);
1278
1557
  if (!ext) {
1279
1558
  const renderer = req.renderer;
1280
1559
  const data = await renderer.renderError(err);
@@ -1287,13 +1566,61 @@ function rootProdServer500Middleware() {
1287
1566
  };
1288
1567
  }
1289
1568
 
1569
+ // src/cli/cli.ts
1570
+ var CliRunner = class {
1571
+ constructor(name, version) {
1572
+ this.name = name;
1573
+ this.version = version;
1574
+ }
1575
+ async run(argv) {
1576
+ const program = new Command("root");
1577
+ program.version(this.version);
1578
+ program.option("-q, --quiet", "quiet");
1579
+ program.hook("preAction", (cmd) => {
1580
+ if (!cmd.opts().quiet) {
1581
+ console.log(
1582
+ `\u{1F955} ${bgGreen(black(` ${this.name} `))} v${this.version}
1583
+ `
1584
+ );
1585
+ }
1586
+ });
1587
+ program.command("build [path]").description("generates a static build").option("--ssr-only", "produce a ssr-only build").option(
1588
+ "--mode <mode>",
1589
+ "see: https://vitejs.dev/guide/env-and-mode.html#modes",
1590
+ "production"
1591
+ ).option(
1592
+ "-c, --concurrency <num>",
1593
+ "number of files to build concurrently",
1594
+ "10"
1595
+ ).action(build);
1596
+ program.command("create-package [path]").alias("package").description(
1597
+ "creates a standalone npm package for deployment to various hosting services"
1598
+ ).option("--target <target>", "hosting target, i.e. appengine or firebase").option("--out <outdir>", "output dir").option("--mode <mode>", "deployment mode, i.e. production or preview").action(createPackage);
1599
+ program.command("dev [path]").description("starts the server in development mode").option(
1600
+ "--host <host>",
1601
+ "network address the server should listen on, e.g. 127.0.0.1"
1602
+ ).action(dev);
1603
+ program.command("preview [path]").description("starts the server in preview mode").option(
1604
+ "--host <host>",
1605
+ "network address the server should listen on, e.g. 127.0.0.1"
1606
+ ).action(preview);
1607
+ program.command("start [path]").description("starts the server in production mode").option(
1608
+ "--host <host>",
1609
+ "network address the server should listen on, e.g. 127.0.0.1"
1610
+ ).action(start);
1611
+ await program.parseAsync(argv);
1612
+ }
1613
+ };
1614
+
1290
1615
  export {
1291
1616
  build,
1617
+ createPackage,
1292
1618
  dev,
1293
1619
  createDevServer,
1294
1620
  preview,
1295
1621
  createPreviewServer,
1296
1622
  start,
1297
- createProdServer
1623
+ createProdServer,
1624
+ CliRunner
1298
1625
  };
1299
- //# sourceMappingURL=chunk-CY3DVKXO.js.map
1626
+ //# sourceMappingURL=chunk-HRGMPZCK.js.map