@blinkk/root 2.0.0-rc.0 → 2.0.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.
@@ -12,6 +12,7 @@ import {
12
12
  dirExists,
13
13
  directoryContains,
14
14
  fileExists,
15
+ flattenPackageDepsFromMonorepo,
15
16
  isDirectory,
16
17
  isJsFile,
17
18
  loadBundledConfig,
@@ -21,21 +22,21 @@ import {
21
22
  rmDir,
22
23
  writeFile,
23
24
  writeJson
24
- } from "./chunk-UJ6F5RQ5.js";
25
+ } from "./chunk-HTBDTUFE.js";
25
26
  import {
26
27
  configureServerPlugins,
27
28
  getVitePlugins
28
- } from "./chunk-ZUUGSC6Z.js";
29
+ } from "./chunk-MA6MSEP4.js";
29
30
  import {
30
31
  RouteTrie,
31
32
  htmlMinify,
32
33
  htmlPretty,
33
34
  isValidTagName,
34
35
  parseTagNames
35
- } from "./chunk-5YDJSEDK.js";
36
+ } from "./chunk-3KGEENDW.js";
36
37
 
37
38
  // src/cli/cli.ts
38
- import { Command } from "commander";
39
+ import { Command, InvalidArgumentError } from "commander";
39
40
  import { bgGreen, black } from "kleur/colors";
40
41
 
41
42
  // src/cli/build.ts
@@ -369,11 +370,6 @@ async function build(rootProjectDir, options) {
369
370
  });
370
371
  }
371
372
  const rootPlugins = rootConfig.plugins || [];
372
- for (const plugin of rootPlugins) {
373
- if (typeof plugin.hooks?.startup === "function") {
374
- await plugin.hooks.startup({ command: "build", rootConfig });
375
- }
376
- }
377
373
  const viteConfig = rootConfig.vite || {};
378
374
  const vitePlugins = [
379
375
  ...viteConfig.plugins || [],
@@ -605,7 +601,17 @@ async function build(rootProjectDir, options) {
605
601
  if (!ssrOnly) {
606
602
  const render = await import(path3.join(distDir, "server/render.js"));
607
603
  const renderer = new render.Renderer(rootConfig, { assetMap, elementGraph });
608
- const sitemap = await renderer.getSitemap();
604
+ let sitemap = await renderer.getSitemap();
605
+ if (options?.filter) {
606
+ const filterRegex = new RegExp(`^${options.filter}`);
607
+ const filteredSitemap = {};
608
+ Object.entries(sitemap).forEach(([urlPath, sitemapItem]) => {
609
+ if (urlPath.match(filterRegex)) {
610
+ filteredSitemap[urlPath] = sitemapItem;
611
+ }
612
+ });
613
+ sitemap = filteredSitemap;
614
+ }
609
615
  const sitemapXmlItems = [];
610
616
  if (rootConfig.sitemap && !rootConfig.domain) {
611
617
  throw new Error(
@@ -615,56 +621,94 @@ async function build(rootProjectDir, options) {
615
621
  const domain = rootConfig.domain;
616
622
  console.log("\nhtml output:");
617
623
  const batchSize = Number(options?.concurrency || 10);
618
- await batchAsyncCalls(Object.keys(sitemap), batchSize, async (urlPath) => {
619
- const sitemapItem = sitemap[urlPath];
620
- try {
621
- const data = await renderer.renderRoute(sitemapItem.route, {
622
- routeParams: sitemapItem.params
623
- });
624
- if (data.notFound) {
625
- return;
626
- }
627
- let outFilePath = path3.join(urlPath.slice(1), "index.html");
628
- if (outFilePath.endsWith("404/index.html")) {
629
- outFilePath = outFilePath.replace("404/index.html", "404.html");
624
+ await batchAsyncCalls(
625
+ Object.entries(sitemap),
626
+ batchSize,
627
+ async ([urlPath, sitemapItem]) => {
628
+ if (rootConfig.build?.excludeDefaultLocaleFromIntlPaths) {
629
+ const defaultLocale = rootConfig.i18n?.defaultLocale || "en";
630
+ if (sitemapItem.locale === defaultLocale) {
631
+ return;
632
+ }
630
633
  }
631
- const outPath = path3.join(buildDir, outFilePath);
632
- if (rootConfig.sitemap && outFilePath.endsWith("index.html")) {
633
- const sitemapXmlItem = {
634
- url: `${domain}${urlPath}`,
635
- locale: sitemapItem.locale,
636
- alts: []
637
- };
638
- sitemapXmlItems.push(sitemapXmlItem);
639
- if (sitemapItem.alts) {
640
- Object.entries(sitemapItem.alts).forEach(([altLocale, item]) => {
641
- sitemapXmlItem.alts.push({
642
- url: `${domain}${item.urlPath}`,
643
- locale: altLocale,
644
- hreflang: item.hrefLang
634
+ try {
635
+ const routeModule = sitemapItem.route.module;
636
+ if (routeModule.getStaticContent) {
637
+ let props;
638
+ if (routeModule.getStaticProps) {
639
+ props = await routeModule.getStaticProps({
640
+ rootConfig,
641
+ params: sitemapItem.params
645
642
  });
646
- });
643
+ if (props?.notFound) {
644
+ return;
645
+ }
646
+ } else {
647
+ props = { rootConfig, params: sitemapItem.params };
648
+ }
649
+ const result = await routeModule.getStaticContent(props);
650
+ let body;
651
+ if (typeof result === "string") {
652
+ body = result;
653
+ } else if (result && typeof result === "object") {
654
+ body = result.body;
655
+ } else {
656
+ body = "";
657
+ }
658
+ const outFilePath2 = urlPath.slice(1);
659
+ const outPath2 = path3.join(buildDir, outFilePath2);
660
+ await makeDir(path3.dirname(outPath2));
661
+ await writeFile(outPath2, body);
662
+ printFileOutput(fileSize(outPath2), "dist/html/", outFilePath2);
663
+ return;
647
664
  }
665
+ const data = await renderer.renderRoute(sitemapItem.route, {
666
+ routeParams: sitemapItem.params
667
+ });
668
+ if (data.notFound) {
669
+ return;
670
+ }
671
+ let outFilePath = path3.join(urlPath.slice(1), "index.html");
672
+ if (outFilePath.endsWith("404/index.html")) {
673
+ outFilePath = outFilePath.replace("404/index.html", "404.html");
674
+ }
675
+ const outPath = path3.join(buildDir, outFilePath);
676
+ if (rootConfig.sitemap && outFilePath.endsWith("index.html")) {
677
+ const sitemapXmlItem = {
678
+ url: `${domain}${urlPath}`,
679
+ locale: sitemapItem.locale,
680
+ alts: []
681
+ };
682
+ sitemapXmlItems.push(sitemapXmlItem);
683
+ if (sitemapItem.alts) {
684
+ Object.entries(sitemapItem.alts).forEach(([altLocale, item]) => {
685
+ sitemapXmlItem.alts.push({
686
+ url: `${domain}${item.urlPath}`,
687
+ locale: altLocale,
688
+ hreflang: item.hrefLang
689
+ });
690
+ });
691
+ }
692
+ }
693
+ let html = data.html || "";
694
+ if (rootConfig.prettyHtml) {
695
+ html = await htmlPretty(html, rootConfig.prettyHtmlOptions);
696
+ } else if (rootConfig.minifyHtml) {
697
+ html = await htmlMinify(html, rootConfig.minifyHtmlOptions);
698
+ }
699
+ await writeFile(outPath, html);
700
+ printFileOutput(fileSize(outPath), "dist/html/", outFilePath);
701
+ } catch (e) {
702
+ logBuildError(
703
+ { route: sitemapItem.route, params: sitemapItem.params, urlPath },
704
+ e
705
+ );
706
+ throw new Error(
707
+ `BuildError: ${urlPath} (${sitemapItem.route.src}) failed to build.`
708
+ );
648
709
  }
649
- let html = data.html || "";
650
- if (rootConfig.prettyHtml !== false) {
651
- html = await htmlPretty(html, rootConfig.prettyHtmlOptions);
652
- }
653
- if (rootConfig.minifyHtml !== false) {
654
- html = await htmlMinify(html, rootConfig.minifyHtmlOptions);
655
- }
656
- await writeFile(outPath, html);
657
- printFileOutput(fileSize(outPath), "dist/html/", outFilePath);
658
- } catch (e) {
659
- logBuildError(
660
- { route: sitemapItem.route, params: sitemapItem.params, urlPath },
661
- e
662
- );
663
- throw new Error(
664
- `BuildError: ${urlPath} (${sitemapItem.route.src}) failed to build.`
665
- );
666
710
  }
667
- });
711
+ );
668
712
  if (rootConfig.sitemap) {
669
713
  const sitemapXmlBuilder = [];
670
714
  sitemapXmlItems.sort((a, b) => a.url.localeCompare(b.url));
@@ -860,7 +904,8 @@ async function createPackage(rootProjectDir, options) {
860
904
  }
861
905
  }
862
906
  if (target === "appengine") {
863
- await onAppEngine({ rootDir, packageJson, outDir });
907
+ const appYamlPath = options?.appYaml || path5.join(rootDir, "app.yaml");
908
+ await onAppEngine({ packageJson, outDir, appYamlPath });
864
909
  } else if (target === "firebase") {
865
910
  await onFirebase({ rootDir, packageJson, outDir });
866
911
  }
@@ -883,77 +928,30 @@ async function generatePackageJson(rootDir) {
883
928
  const packageJson = await loadJson(
884
929
  path5.resolve(rootDir, "package.json")
885
930
  );
886
- if (packageJson.peerDependencies) {
887
- await updatePeerDepsFromWorkspace(rootDir, packageJson);
888
- }
889
- return packageJson;
890
- }
891
- async function updatePeerDepsFromWorkspace(rootDir, packageJson) {
892
- const requiredPeerDeps = getRequiredPeerDeps(packageJson);
893
- if (requiredPeerDeps.length === 0) {
894
- return;
895
- }
896
- const workspaceRoot = await findWorkspaceRoot(rootDir);
897
- if (!workspaceRoot) {
898
- return;
899
- }
900
- const workspacePackageJsonPath = path5.resolve(workspaceRoot, "package.json");
901
- const workspacePackageJson = await loadJson(
902
- workspacePackageJsonPath
903
- );
904
- const workspaceDeps = workspacePackageJson.dependencies || {};
905
- function setDep(name, value) {
906
- if (!packageJson.dependencies) {
907
- packageJson.dependencies = {};
931
+ const allDeps = flattenPackageDepsFromMonorepo(rootDir);
932
+ packageJson.dependencies ??= {};
933
+ for (const depName in allDeps) {
934
+ if (!packageJson.dependencies[depName]) {
935
+ packageJson.dependencies[depName] = allDeps[depName];
908
936
  }
909
- packageJson.dependencies[name] = value;
910
- }
911
- for (const peerDep of requiredPeerDeps) {
912
- const workspaceDepVersion = workspaceDeps[peerDep];
913
- if (workspaceDepVersion) {
914
- setDep(peerDep, workspaceDepVersion);
915
- } else {
916
- console.warn(
917
- `could not find peer dep "${peerDep}" in workspace "${workspacePackageJsonPath}"`
918
- );
919
- }
920
- }
921
- }
922
- async function findWorkspaceRoot(rootDir) {
923
- const parentDir = path5.dirname(rootDir);
924
- if (parentDir === "/") {
925
- return null;
926
937
  }
927
- const pnpmWorkspaceFile = path5.resolve(parentDir, "pnpm-workspace.yaml");
928
- if (await fileExists(pnpmWorkspaceFile)) {
929
- return parentDir;
930
- }
931
- const packageJsonPath = path5.resolve(parentDir, "package.json");
932
- if (await fileExists(packageJsonPath)) {
933
- const parentPackageJson = await loadJson(packageJsonPath);
934
- if (parentPackageJson && parentPackageJson.workspaces) {
935
- return parentDir;
938
+ Object.entries(packageJson.dependencies).forEach(([depName, depVersion]) => {
939
+ if (depVersion.startsWith("workspace:")) {
940
+ delete packageJson.dependencies[depName];
936
941
  }
942
+ });
943
+ if (packageJson.peerDependencies) {
944
+ delete packageJson.peerDependencies;
937
945
  }
938
- return findWorkspaceRoot(parentDir);
939
- }
940
- function getRequiredPeerDeps(packageJson) {
941
- const requiredPeerDeps = [];
942
- const peerDeps = packageJson.peerDependencies || {};
943
- const peerDepsMeta = packageJson.peerDependenciesMeta || {};
944
- for (const key in peerDeps) {
945
- const meta = peerDepsMeta[key];
946
- if (!meta?.optional) {
947
- requiredPeerDeps.push(key);
948
- }
946
+ if (packageJson.devDependencies) {
947
+ delete packageJson.devDependencies;
949
948
  }
950
- return requiredPeerDeps;
949
+ return packageJson;
951
950
  }
952
951
  async function onAppEngine(options) {
953
- const { rootDir, outDir, packageJson } = options;
954
- const configPath = path5.resolve(rootDir, "app.yaml");
955
- if (await fileExists(configPath)) {
956
- await fs4.copyFile(configPath, path5.resolve(outDir, "app.yaml"));
952
+ const { outDir, packageJson, appYamlPath } = options;
953
+ if (await fileExists(appYamlPath)) {
954
+ await fs4.copyFile(appYamlPath, path5.resolve(outDir, "app.yaml"));
957
955
  }
958
956
  if (packageJson.scripts?.start) {
959
957
  packageJson.scripts = { start: packageJson.scripts.start };
@@ -1061,9 +1059,15 @@ function verifyRedirectConfig(redirect) {
1061
1059
  if (!redirect.source) {
1062
1060
  return false;
1063
1061
  }
1062
+ if (!redirect.source.startsWith("/")) {
1063
+ return false;
1064
+ }
1064
1065
  if (!redirect.destination) {
1065
1066
  return false;
1066
1067
  }
1068
+ if (!testIsRedirectValid(redirect.destination)) {
1069
+ return false;
1070
+ }
1067
1071
  return true;
1068
1072
  }
1069
1073
  function replaceParams(urlPathFormat, params) {
@@ -1079,6 +1083,15 @@ function replaceParams(urlPathFormat, params) {
1079
1083
  );
1080
1084
  return urlPath;
1081
1085
  }
1086
+ function testIsRedirectValid(url) {
1087
+ if (url.startsWith("/")) {
1088
+ return true;
1089
+ }
1090
+ if (url.match(/^https?:\/\//)) {
1091
+ return true;
1092
+ }
1093
+ return false;
1094
+ }
1082
1095
 
1083
1096
  // src/render/asset-map/dev-asset-map.ts
1084
1097
  import path6 from "node:path";
@@ -1269,9 +1282,10 @@ async function dev(rootProjectDir, options) {
1269
1282
  const server = await createDevServer({ rootDir, port });
1270
1283
  const rootConfig = server.get("rootConfig");
1271
1284
  const basePath = rootConfig.base || "";
1285
+ const homePagePath = rootConfig.server?.homePagePath || basePath;
1272
1286
  console.log();
1273
1287
  console.log(`${dim2("\u2503")} project: ${rootDir}`);
1274
- console.log(`${dim2("\u2503")} server: http://${host}:${port}${basePath}`);
1288
+ console.log(`${dim2("\u2503")} server: http://${host}:${port}${homePagePath}`);
1275
1289
  if (testCmsEnabled(rootConfig)) {
1276
1290
  console.log(`${dim2("\u2503")} cms: http://${host}:${port}/cms/`);
1277
1291
  }
@@ -1330,11 +1344,6 @@ async function createDevServer(options) {
1330
1344
  plugins,
1331
1345
  { type: "dev", rootConfig }
1332
1346
  );
1333
- for (const plugin of plugins) {
1334
- if (typeof plugin.hooks?.startup === "function") {
1335
- await plugin.hooks.startup({ command: "dev", rootConfig });
1336
- }
1337
- }
1338
1347
  return server;
1339
1348
  }
1340
1349
  async function createViteMiddleware(options) {
@@ -1475,6 +1484,175 @@ function debounce(fn, timeout) {
1475
1484
  };
1476
1485
  }
1477
1486
 
1487
+ // src/cli/gae-deploy.ts
1488
+ import { execSync } from "node:child_process";
1489
+ import fs5 from "node:fs";
1490
+ import yaml from "js-yaml";
1491
+ async function gaeDeploy(appDir, options) {
1492
+ if (!appDir) {
1493
+ throw new Error(
1494
+ "[gae-deplopy] Missing app dir, e.g. `root gae-deploy <app dir>`"
1495
+ );
1496
+ }
1497
+ const project = options?.project;
1498
+ if (!project) {
1499
+ throw new Error("[gae-deplopy] Missing: --project");
1500
+ }
1501
+ process.chdir(appDir);
1502
+ const appYamlPath = "app.yaml";
1503
+ if (!fs5.existsSync(appYamlPath)) {
1504
+ throw new Error(`[gae-deplopy] Missing: ${appYamlPath}`);
1505
+ }
1506
+ const appYaml = yaml.load(fs5.readFileSync(appYamlPath, "utf8"));
1507
+ const service = appYaml.service;
1508
+ if (!service) {
1509
+ throw new Error(
1510
+ '[gae-deploy] Missing service definition. Ensure "service" is defined in app.yaml.'
1511
+ );
1512
+ }
1513
+ let prefix = options?.prefix;
1514
+ if (!prefix) {
1515
+ prefix = options?.promote ? "prod" : "staging";
1516
+ }
1517
+ const version = `${prefix}-${getTimestamp()}`;
1518
+ if (testVersionTooLong(version, service, project)) {
1519
+ throw new Error(`version "${version}" should not exceed 63 chars`);
1520
+ }
1521
+ console.log("[gae-deploy] \u{1F680} starting deployment...");
1522
+ const appYamlHasPlaceholders = testHasEnvPlaceholders(appYaml);
1523
+ let backupAppYaml = "";
1524
+ if (appYamlHasPlaceholders) {
1525
+ backupAppYaml = updateAppYamlEnv(appYamlPath, appYaml);
1526
+ }
1527
+ execSync(
1528
+ `gcloud app deploy -q --project=${project} --version=${version} --no-promote app.yaml`,
1529
+ { stdio: "inherit" }
1530
+ );
1531
+ if (backupAppYaml) {
1532
+ fs5.copyFileSync(backupAppYaml, "app.yaml");
1533
+ }
1534
+ if (options?.healthcheckUrl) {
1535
+ const healthcheckPassed = await testHealth(
1536
+ project,
1537
+ service,
1538
+ version,
1539
+ options.healthcheckUrl
1540
+ );
1541
+ if (!healthcheckPassed) {
1542
+ throw new Error(
1543
+ `[gae-deploy] \u274C health check failed. check logs:
1544
+ ${getLogsURL(
1545
+ project,
1546
+ service,
1547
+ version
1548
+ )}`
1549
+ );
1550
+ }
1551
+ console.log("[gae-deploy] \u2705 health check succeeded!");
1552
+ }
1553
+ if (options?.promote) {
1554
+ console.log(`[gae-deploy] promoting version ${version}...`);
1555
+ execSync(
1556
+ `gcloud app -q --project=${project} services set-traffic ${service} --splits ${version}=1`,
1557
+ { stdio: "inherit" }
1558
+ );
1559
+ }
1560
+ if (options?.maxVersions) {
1561
+ const resp = execSync(
1562
+ `gcloud app versions list --project ${project} --service ${service} --format json`
1563
+ ).toString();
1564
+ const versions = JSON.parse(resp.toString());
1565
+ const managedVersions = versions.filter(
1566
+ (appInfo) => testManagedVersion(service, appInfo, prefix)
1567
+ ).sort(
1568
+ (a, b) => new Date(b.last_deployed_time.datetime).getTime() - new Date(a.last_deployed_time.datetime).getTime()
1569
+ );
1570
+ console.log(
1571
+ `[gae-deploy] \u{1F4E6} all managed versions (${managedVersions.length}):`
1572
+ );
1573
+ for (const eachVersion of managedVersions) {
1574
+ console.log(` ${eachVersion.id}`);
1575
+ }
1576
+ for (const eachVersion of managedVersions.slice(options.maxVersions - 1)) {
1577
+ console.log(`[gae-deploy] \u2702\uFE0F deleting old version ${eachVersion.id}`);
1578
+ execSync(
1579
+ `gcloud app versions delete -q --project=${project} --service=${service} ${eachVersion.id}`,
1580
+ { stdio: "inherit" }
1581
+ );
1582
+ }
1583
+ }
1584
+ console.log(
1585
+ `[gae-deploy] \u2728 deployment complete
1586
+ versions: ${getVersionsURL(
1587
+ project,
1588
+ service
1589
+ )}`
1590
+ );
1591
+ }
1592
+ function testVersionTooLong(version, service, project) {
1593
+ const subdomain = `${version}-dot-${service}-dot-${project}`;
1594
+ return subdomain.length > 63;
1595
+ }
1596
+ function getLogsURL(project, service, version) {
1597
+ return `https://console.cloud.google.com/logs/query;query=resource.type%3D"gae_app"%0Aresource.labels.module_id%3D"${service}"%0Aresource.labels.version_id%3D"${version}"?project=${project}`;
1598
+ }
1599
+ function getVersionsURL(project, service) {
1600
+ return `https://console.cloud.google.com/appengine/versions?project=${project}&serviceId=${service}`;
1601
+ }
1602
+ function testHasEnvPlaceholders(appYaml) {
1603
+ if (typeof appYaml?.env_variables !== "object") {
1604
+ return false;
1605
+ }
1606
+ const envVars = appYaml.env_variables;
1607
+ return Object.values(envVars).some((envValue) => {
1608
+ return testIsEnvPlaceholder(envValue);
1609
+ });
1610
+ }
1611
+ function testIsEnvPlaceholder(envValue) {
1612
+ if (typeof envValue === "string") {
1613
+ return envValue.startsWith("{") && envValue.endsWith("}");
1614
+ }
1615
+ return false;
1616
+ }
1617
+ function updateAppYamlEnv(appYamlPath, appYaml) {
1618
+ const backupAppYaml = `${appYamlPath}.bak`;
1619
+ fs5.copyFileSync(appYamlPath, backupAppYaml);
1620
+ let content = fs5.readFileSync(appYamlPath, "utf8");
1621
+ const envVars = appYaml.env_variables;
1622
+ Object.entries(envVars).forEach(([envVar, envValue]) => {
1623
+ if (testIsEnvPlaceholder(envValue)) {
1624
+ const value = process.env[envVar];
1625
+ if (!value && content.includes(`{${envVar}}`)) {
1626
+ console.warn(`[gae-deploy] Missing environment variable: ${envVar}`);
1627
+ content = content.replaceAll(`{${envVar}}`, "");
1628
+ } else if (value && content.includes(`{${envVar}}`)) {
1629
+ content = content.replaceAll(`{${envVar}}`, value);
1630
+ }
1631
+ }
1632
+ });
1633
+ fs5.writeFileSync(appYamlPath, content, "utf8");
1634
+ return backupAppYaml;
1635
+ }
1636
+ function getTimestamp() {
1637
+ const pretty = (num) => (num + "").padStart(2, "0");
1638
+ const date = /* @__PURE__ */ new Date();
1639
+ return `${date.getFullYear()}${pretty(date.getMonth() + 1)}${pretty(
1640
+ date.getDate()
1641
+ )}t${pretty(date.getHours())}${pretty(date.getMinutes())}`;
1642
+ }
1643
+ async function testHealth(project, service, version, healthcheckUrl) {
1644
+ if (!healthcheckUrl.startsWith("/")) {
1645
+ healthcheckUrl = `/${healthcheckUrl}`;
1646
+ }
1647
+ const url = `https://${version}-dot-${service}-dot-${project}.appspot.com${healthcheckUrl}`;
1648
+ const response = await fetch(url);
1649
+ return response.status === 200;
1650
+ }
1651
+ function testManagedVersion(service, appInfo, prefix) {
1652
+ const re = new RegExp(`${prefix}-\\d{8}t\\d{4}`);
1653
+ return appInfo.service === service && re.exec(appInfo.id) && appInfo.traffic_split === 0;
1654
+ }
1655
+
1478
1656
  // src/cli/preview.ts
1479
1657
  import path8 from "node:path";
1480
1658
  import compression from "compression";
@@ -1509,7 +1687,7 @@ async function createPreviewServer(options) {
1509
1687
  server.use(cookieParser2(sessionCookieSecret));
1510
1688
  server.use(sessionMiddleware());
1511
1689
  const plugins = rootConfig.plugins || [];
1512
- configureServerPlugins(
1690
+ await configureServerPlugins(
1513
1691
  server,
1514
1692
  async () => {
1515
1693
  const userMiddlewares = rootConfig.server?.middlewares || [];
@@ -1539,11 +1717,6 @@ async function createPreviewServer(options) {
1539
1717
  plugins,
1540
1718
  { type: "preview", rootConfig }
1541
1719
  );
1542
- for (const plugin of plugins) {
1543
- if (typeof plugin.hooks?.startup === "function") {
1544
- await plugin.hooks.startup({ command: "preview", rootConfig });
1545
- }
1546
- }
1547
1720
  return server;
1548
1721
  }
1549
1722
  async function rootPreviewRendererMiddleware(options) {
@@ -1660,7 +1833,7 @@ async function createProdServer(options) {
1660
1833
  server.use(cookieParser3(sessionCookieSecret));
1661
1834
  server.use(sessionMiddleware());
1662
1835
  const plugins = rootConfig.plugins || [];
1663
- configureServerPlugins(
1836
+ await configureServerPlugins(
1664
1837
  server,
1665
1838
  async () => {
1666
1839
  const userMiddlewares = rootConfig.server?.middlewares || [];
@@ -1690,11 +1863,6 @@ async function createProdServer(options) {
1690
1863
  plugins,
1691
1864
  { type: "prod", rootConfig }
1692
1865
  );
1693
- for (const plugin of plugins) {
1694
- if (typeof plugin.hooks?.startup === "function") {
1695
- await plugin.hooks.startup({ command: "start", rootConfig });
1696
- }
1697
- }
1698
1866
  return server;
1699
1867
  }
1700
1868
  async function rootProdRendererMiddleware(options) {
@@ -1805,11 +1973,18 @@ var CliRunner = class {
1805
1973
  "-c, --concurrency <num>",
1806
1974
  "number of files to build concurrently",
1807
1975
  "10"
1976
+ ).option(
1977
+ "--filter <urlPathRegex>",
1978
+ 'builds the url paths that match the given regex, e.g. "/products/.*"',
1979
+ ""
1808
1980
  ).action(build);
1809
1981
  program.command("codegen [type] [name]").description("generates boilerplate code").option("--out <outdir>", "output dir").action(codegen);
1810
1982
  program.command("create-package [path]").alias("package").description(
1811
1983
  "creates a standalone npm package for deployment to various hosting services"
1812
- ).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((rootPackageDir, options) => {
1984
+ ).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").option(
1985
+ "--app-yaml <path>",
1986
+ 'for appengine targets, path to app.yaml (defaults to "app.yaml")'
1987
+ ).action((rootPackageDir, options) => {
1813
1988
  options.version = this.version;
1814
1989
  createPackage(rootPackageDir, options);
1815
1990
  });
@@ -1817,6 +1992,19 @@ var CliRunner = class {
1817
1992
  "--host <host>",
1818
1993
  "network address the server should listen on, e.g. 127.0.0.1"
1819
1994
  ).action(dev);
1995
+ program.command("gae-deploy <appdir>").description(
1996
+ "appengine deploy utility that can optionally run healthchecks before diverting traffic to the new version and clean up old versions"
1997
+ ).option("--project <project>", "GCP project id").option("--prefix <prefix>", "prefix to append the version").option(
1998
+ "--promote",
1999
+ "whether to promote the version (if healthchecks pass)"
2000
+ ).option(
2001
+ "--healthcheck-url <url>",
2002
+ 'healthcheck url path (e.g. "/healthcheck") which should return a 200 status with the text "OK"'
2003
+ ).option(
2004
+ "--max-versions <num>",
2005
+ "the max number of versions to keep",
2006
+ numberFlag
2007
+ ).action(gaeDeploy);
1820
2008
  program.command("preview [path]").description("starts the server in preview mode").option(
1821
2009
  "--host <host>",
1822
2010
  "network address the server should listen on, e.g. 127.0.0.1"
@@ -1828,6 +2016,13 @@ var CliRunner = class {
1828
2016
  await program.parseAsync(argv);
1829
2017
  }
1830
2018
  };
2019
+ function numberFlag(value) {
2020
+ const num = parseInt(value);
2021
+ if (isNaN(num)) {
2022
+ throw new InvalidArgumentError(`not a number: ${value}`);
2023
+ }
2024
+ return num;
2025
+ }
1831
2026
 
1832
2027
  export {
1833
2028
  build,
@@ -1840,4 +2035,4 @@ export {
1840
2035
  createProdServer,
1841
2036
  CliRunner
1842
2037
  };
1843
- //# sourceMappingURL=chunk-7D26JHIF.js.map
2038
+ //# sourceMappingURL=chunk-A4DNVP34.js.map