@blinkk/root 1.0.5 → 1.0.7

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.
@@ -143,7 +143,7 @@ function sessionMiddleware(options) {
143
143
  sameSite
144
144
  });
145
145
  };
146
- req.hooks.add("beforeRender", () => {
146
+ req.hooks.add("preRender", () => {
147
147
  res.saveSession();
148
148
  });
149
149
  next();
@@ -200,4 +200,4 @@ export {
200
200
  sessionMiddleware,
201
201
  Session
202
202
  };
203
- //# sourceMappingURL=chunk-MLPPFGKW.js.map
203
+ //# sourceMappingURL=chunk-BMKSXG4B.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware/common.ts","../src/middleware/multipart.ts","../src/middleware/session.ts"],"sourcesContent":["import path from 'node:path';\nimport micromatch from 'micromatch';\nimport {RootConfig} from '../core/config.js';\nimport {Request, Response, NextFunction} from '../core/types.js';\n\n/**\n * Middleware that injects the root.js project config into the request context.\n */\nexport function rootProjectMiddleware(options: {rootConfig: RootConfig}) {\n return (req: Request, _: Response, next: NextFunction) => {\n req.rootConfig = options.rootConfig;\n next();\n };\n}\n\n/**\n * Middleware that injects HTTP headers from the `server.headers` config in\n * root.config.ts.\n */\nexport function headersMiddleware(options: {rootConfig: RootConfig}) {\n const headersUserConfig = options.rootConfig.server?.headers || [];\n // Filter header config values that are invalid.\n const headersConfig = headersUserConfig.filter((headerConfig) => {\n return (\n headerConfig.source &&\n headerConfig.headers &&\n headerConfig.headers.length > 0\n );\n });\n return (req: Request, res: Response, next: NextFunction) => {\n headersConfig.forEach((headerConfig) => {\n if (micromatch.isMatch(req.path, headerConfig.source)) {\n headerConfig.headers.forEach((header) => {\n if (header.key) {\n res.setHeader(String(header.key), String(header.value));\n }\n });\n }\n });\n next();\n };\n}\n\n/**\n * Trailing slash middleware. Handles trailing slash redirects (preserving any\n * query params) using the `server.trailingSlash` config in root.config.ts.\n */\nexport function trailingSlashMiddleware(options: {rootConfig: RootConfig}) {\n const trailingSlash = options.rootConfig.server?.trailingSlash;\n\n return (req: Request, res: Response, next: NextFunction) => {\n // If `trailingSlash: false`, force a trailing slash in the URL.\n if (\n trailingSlash === true &&\n !path.extname(req.path) &&\n !req.path.endsWith('/')\n ) {\n const redirectPath = `${req.path}/`;\n redirectWithQuery(req, res, 301, redirectPath);\n return;\n }\n\n // If `trailingSlash: false`, remove any trailing slash from the URL.\n if (\n trailingSlash === false &&\n !path.extname(req.path) &&\n req.path !== '/' &&\n req.path.endsWith('/')\n ) {\n const redirectPath = removeTrailingSlashes(req.path);\n redirectWithQuery(req, res, 301, redirectPath);\n return;\n }\n\n next();\n };\n}\n\n/**\n * Issues an HTTP redirect, preserving any query params from the original req.\n */\nfunction redirectWithQuery(\n req: Request,\n res: Response,\n redirectCode: number,\n redirectPath: string\n) {\n const queryStr = getQueryStr(req);\n const redirectUrl = queryStr ? `${redirectPath}?${queryStr}` : redirectPath;\n res.redirect(redirectCode, redirectUrl);\n}\n\n/**\n * Returns the query string for a request, or empty string if no query.\n */\nfunction getQueryStr(req: Request): string {\n const qIndex = req.originalUrl.indexOf('?');\n if (qIndex === -1) {\n return '';\n }\n return req.originalUrl.slice(qIndex + 1);\n}\n\n/**\n * Removes trailing slashes from a URL path.\n * Note: A path with only slashes (e.g. `///`) returns `/`.\n */\nfunction removeTrailingSlashes(urlPath: string) {\n while (urlPath.endsWith('/') && urlPath !== '/') {\n urlPath = urlPath.slice(0, -1);\n }\n return urlPath;\n}\n","import busboy from 'busboy';\nimport {RequestHandler} from 'express';\nimport {Request, Response, NextFunction, MultipartFile} from '../core/types.js';\n\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\n\n/**\n * Middleware for parsing multipart file uploads that's compatible with the dev\n * server and Firebase Functions.\n *\n * Context:\n * https://stackoverflow.com/questions/47242340/how-to-perform-an-http-file-upload-using-express-on-cloud-functions-for-firebase\n */\nexport function multipartMiddleware(options?: {\n maxFileSize?: number;\n}): RequestHandler {\n const maxFileSize = options?.maxFileSize || DEFAULT_MAX_FILE_SIZE;\n const handler: any = (req: Request, res: Response, next: NextFunction) => {\n const contentType = String(req.headers['content-type'] || '');\n if (\n req.method === 'POST' &&\n contentType.startsWith('multipart/form-data')\n ) {\n const parser = busboy({headers: req.headers});\n\n // Data storage for fields and files\n const fields: {[fieldname: string]: string} = {};\n const files: {[name: string]: MultipartFile} = {};\n\n // Handle field data.\n parser.on('field', (fieldname: string, val: any) => {\n fields[fieldname] = val;\n });\n\n // Handle file data. Files are saved to an in-memory buffer.\n parser.on(\n 'file',\n (\n fieldname: string,\n file: any,\n meta: {\n filename: string;\n encoding: string;\n mimeType: string;\n }\n ) => {\n const {filename, encoding, mimeType: mimetype} = meta;\n const fileChunks: Uint8Array[] = [];\n let totalBytesRead = 0;\n\n file.on('data', (chunk: Uint8Array) => {\n totalBytesRead += chunk.length;\n\n if (totalBytesRead > maxFileSize) {\n // File size exceeds the limit, stop reading.\n console.error(`File size exceeds the limit: ${fieldname}.`);\n file.removeAllListeners('data');\n // Consume and discard remaining data.\n file.resume();\n } else {\n fileChunks.push(chunk);\n }\n });\n\n file.on('end', () => {\n if (totalBytesRead <= maxFileSize) {\n const buffer = Buffer.concat(fileChunks);\n\n files[fieldname] = {\n fieldname,\n buffer,\n originalName: filename,\n encoding,\n mimetype,\n };\n }\n });\n }\n );\n\n // Update `req.body` and `req.files`.\n parser.on('finish', () => {\n req.body = fields;\n req.files = files;\n next();\n });\n\n // Pipe the request to Busboy. On Firebase Functions, `rawBody` contains\n // the multipart request. Otherwise the express request can be piped to\n // the Busboy parser.\n if (req.rawBody) {\n parser.end(req.rawBody);\n } else {\n req.pipe(parser);\n }\n } else {\n next();\n }\n };\n return handler;\n}\n","import {NextFunction, Request, Response} from '../core/types.js';\n\nexport const SESSION_COOKIE = '__session';\n\n// 5 days.\nconst DEFAULT_MAX_AGE = 60 * 60 * 24 * 5 * 1000;\n\nexport interface SessionMiddlewareOptions {\n maxAge?: number;\n}\n\nexport interface SaveSessionOptions {\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\n/**\n * Middleware for storing session data stored in an http cookie called\n * `__session`. This cookie is compatible with Firebase Hosting:\n * https://firebase.google.com/docs/hosting/manage-cache#using_cookies\n */\nexport function sessionMiddleware(options?: SessionMiddlewareOptions) {\n const maxAge = options?.maxAge || DEFAULT_MAX_AGE;\n return (req: Request, res: Response, next: NextFunction) => {\n const cookieValue = String(req.signedCookies[SESSION_COOKIE] || '');\n const session = Session.fromCookieValue(cookieValue);\n req.session = session;\n res.session = session;\n res.saveSession = (saveSessionOptions?: SaveSessionOptions) => {\n // \"secure\" cookies require https, so disable \"secure\" when in development.\n const secureCookie = Boolean(process.env.NODE_ENV !== 'development');\n const cookieValue = session.toString();\n const sameSite = saveSessionOptions?.sameSite || 'strict';\n res.cookie(SESSION_COOKIE, cookieValue, {\n maxAge: maxAge,\n httpOnly: true,\n secure: secureCookie,\n signed: true,\n sameSite: sameSite,\n });\n };\n req.hooks.add('beforeRender', () => {\n res.saveSession();\n });\n next();\n };\n}\n\nexport class Session {\n private data: Record<string, string> = {};\n\n constructor(data?: Record<string, string>) {\n this.data = data || {};\n }\n\n static fromCookieValue(cookieValue: string) {\n const data: Record<string, string> = {};\n if (cookieValue.startsWith('b64:')) {\n try {\n const encodedStr = cookieValue.slice(4);\n const params = new URLSearchParams(base64Decode(encodedStr));\n for (const [key, value] of params.entries()) {\n data[key] = value;\n }\n } catch (err) {\n console.warn('failed to parse session cookie:', err);\n return new Session();\n }\n }\n return new Session(data);\n }\n\n getItem(key: string): string | null {\n return this.data[key] ?? null;\n }\n\n setItem(key: string, value: string) {\n this.data[key] = value;\n }\n\n removeItem(key: string) {\n delete this.data[key];\n }\n\n toString(): string {\n const params = new URLSearchParams(this.data);\n return `b64:${base64Encode(params.toString())}`;\n }\n}\n\nfunction base64Encode(str: string): string {\n return Buffer.from(str, 'utf-8').toString('base64');\n}\n\nfunction base64Decode(str: string): string {\n return Buffer.from(str, 'base64').toString('utf-8');\n}\n"],"mappings":"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","names":["cookieValue"]}
1
+ {"version":3,"sources":["../src/middleware/common.ts","../src/middleware/multipart.ts","../src/middleware/session.ts"],"sourcesContent":["import path from 'node:path';\nimport micromatch from 'micromatch';\nimport {RootConfig} from '../core/config.js';\nimport {Request, Response, NextFunction} from '../core/types.js';\n\n/**\n * Middleware that injects the root.js project config into the request context.\n */\nexport function rootProjectMiddleware(options: {rootConfig: RootConfig}) {\n return (req: Request, _: Response, next: NextFunction) => {\n req.rootConfig = options.rootConfig;\n next();\n };\n}\n\n/**\n * Middleware that injects HTTP headers from the `server.headers` config in\n * root.config.ts.\n */\nexport function headersMiddleware(options: {rootConfig: RootConfig}) {\n const headersUserConfig = options.rootConfig.server?.headers || [];\n // Filter header config values that are invalid.\n const headersConfig = headersUserConfig.filter((headerConfig) => {\n return (\n headerConfig.source &&\n headerConfig.headers &&\n headerConfig.headers.length > 0\n );\n });\n return (req: Request, res: Response, next: NextFunction) => {\n headersConfig.forEach((headerConfig) => {\n if (micromatch.isMatch(req.path, headerConfig.source)) {\n headerConfig.headers.forEach((header) => {\n if (header.key) {\n res.setHeader(String(header.key), String(header.value));\n }\n });\n }\n });\n next();\n };\n}\n\n/**\n * Trailing slash middleware. Handles trailing slash redirects (preserving any\n * query params) using the `server.trailingSlash` config in root.config.ts.\n */\nexport function trailingSlashMiddleware(options: {rootConfig: RootConfig}) {\n const trailingSlash = options.rootConfig.server?.trailingSlash;\n\n return (req: Request, res: Response, next: NextFunction) => {\n // If `trailingSlash: false`, force a trailing slash in the URL.\n if (\n trailingSlash === true &&\n !path.extname(req.path) &&\n !req.path.endsWith('/')\n ) {\n const redirectPath = `${req.path}/`;\n redirectWithQuery(req, res, 301, redirectPath);\n return;\n }\n\n // If `trailingSlash: false`, remove any trailing slash from the URL.\n if (\n trailingSlash === false &&\n !path.extname(req.path) &&\n req.path !== '/' &&\n req.path.endsWith('/')\n ) {\n const redirectPath = removeTrailingSlashes(req.path);\n redirectWithQuery(req, res, 301, redirectPath);\n return;\n }\n\n next();\n };\n}\n\n/**\n * Issues an HTTP redirect, preserving any query params from the original req.\n */\nfunction redirectWithQuery(\n req: Request,\n res: Response,\n redirectCode: number,\n redirectPath: string\n) {\n const queryStr = getQueryStr(req);\n const redirectUrl = queryStr ? `${redirectPath}?${queryStr}` : redirectPath;\n res.redirect(redirectCode, redirectUrl);\n}\n\n/**\n * Returns the query string for a request, or empty string if no query.\n */\nfunction getQueryStr(req: Request): string {\n const qIndex = req.originalUrl.indexOf('?');\n if (qIndex === -1) {\n return '';\n }\n return req.originalUrl.slice(qIndex + 1);\n}\n\n/**\n * Removes trailing slashes from a URL path.\n * Note: A path with only slashes (e.g. `///`) returns `/`.\n */\nfunction removeTrailingSlashes(urlPath: string) {\n while (urlPath.endsWith('/') && urlPath !== '/') {\n urlPath = urlPath.slice(0, -1);\n }\n return urlPath;\n}\n","import busboy from 'busboy';\nimport {RequestHandler} from 'express';\nimport {Request, Response, NextFunction, MultipartFile} from '../core/types.js';\n\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\n\n/**\n * Middleware for parsing multipart file uploads that's compatible with the dev\n * server and Firebase Functions.\n *\n * Context:\n * https://stackoverflow.com/questions/47242340/how-to-perform-an-http-file-upload-using-express-on-cloud-functions-for-firebase\n */\nexport function multipartMiddleware(options?: {\n maxFileSize?: number;\n}): RequestHandler {\n const maxFileSize = options?.maxFileSize || DEFAULT_MAX_FILE_SIZE;\n const handler: any = (req: Request, res: Response, next: NextFunction) => {\n const contentType = String(req.headers['content-type'] || '');\n if (\n req.method === 'POST' &&\n contentType.startsWith('multipart/form-data')\n ) {\n const parser = busboy({headers: req.headers});\n\n // Data storage for fields and files\n const fields: {[fieldname: string]: string} = {};\n const files: {[name: string]: MultipartFile} = {};\n\n // Handle field data.\n parser.on('field', (fieldname: string, val: any) => {\n fields[fieldname] = val;\n });\n\n // Handle file data. Files are saved to an in-memory buffer.\n parser.on(\n 'file',\n (\n fieldname: string,\n file: any,\n meta: {\n filename: string;\n encoding: string;\n mimeType: string;\n }\n ) => {\n const {filename, encoding, mimeType: mimetype} = meta;\n const fileChunks: Uint8Array[] = [];\n let totalBytesRead = 0;\n\n file.on('data', (chunk: Uint8Array) => {\n totalBytesRead += chunk.length;\n\n if (totalBytesRead > maxFileSize) {\n // File size exceeds the limit, stop reading.\n console.error(`File size exceeds the limit: ${fieldname}.`);\n file.removeAllListeners('data');\n // Consume and discard remaining data.\n file.resume();\n } else {\n fileChunks.push(chunk);\n }\n });\n\n file.on('end', () => {\n if (totalBytesRead <= maxFileSize) {\n const buffer = Buffer.concat(fileChunks);\n\n files[fieldname] = {\n fieldname,\n buffer,\n originalName: filename,\n encoding,\n mimetype,\n };\n }\n });\n }\n );\n\n // Update `req.body` and `req.files`.\n parser.on('finish', () => {\n req.body = fields;\n req.files = files;\n next();\n });\n\n // Pipe the request to Busboy. On Firebase Functions, `rawBody` contains\n // the multipart request. Otherwise the express request can be piped to\n // the Busboy parser.\n if (req.rawBody) {\n parser.end(req.rawBody);\n } else {\n req.pipe(parser);\n }\n } else {\n next();\n }\n };\n return handler;\n}\n","import {NextFunction, Request, Response} from '../core/types.js';\n\nexport const SESSION_COOKIE = '__session';\n\n// 5 days.\nconst DEFAULT_MAX_AGE = 60 * 60 * 24 * 5 * 1000;\n\nexport interface SessionMiddlewareOptions {\n maxAge?: number;\n}\n\nexport interface SaveSessionOptions {\n sameSite?: 'strict' | 'lax' | 'none';\n}\n\n/**\n * Middleware for storing session data stored in an http cookie called\n * `__session`. This cookie is compatible with Firebase Hosting:\n * https://firebase.google.com/docs/hosting/manage-cache#using_cookies\n */\nexport function sessionMiddleware(options?: SessionMiddlewareOptions) {\n const maxAge = options?.maxAge || DEFAULT_MAX_AGE;\n return (req: Request, res: Response, next: NextFunction) => {\n const cookieValue = String(req.signedCookies[SESSION_COOKIE] || '');\n const session = Session.fromCookieValue(cookieValue);\n req.session = session;\n res.session = session;\n res.saveSession = (saveSessionOptions?: SaveSessionOptions) => {\n // \"secure\" cookies require https, so disable \"secure\" when in development.\n const secureCookie = Boolean(process.env.NODE_ENV !== 'development');\n const cookieValue = session.toString();\n const sameSite = saveSessionOptions?.sameSite || 'strict';\n res.cookie(SESSION_COOKIE, cookieValue, {\n maxAge: maxAge,\n httpOnly: true,\n secure: secureCookie,\n signed: true,\n sameSite: sameSite,\n });\n };\n req.hooks.add('preRender', () => {\n res.saveSession();\n });\n next();\n };\n}\n\nexport class Session {\n private data: Record<string, string> = {};\n\n constructor(data?: Record<string, string>) {\n this.data = data || {};\n }\n\n static fromCookieValue(cookieValue: string) {\n const data: Record<string, string> = {};\n if (cookieValue.startsWith('b64:')) {\n try {\n const encodedStr = cookieValue.slice(4);\n const params = new URLSearchParams(base64Decode(encodedStr));\n for (const [key, value] of params.entries()) {\n data[key] = value;\n }\n } catch (err) {\n console.warn('failed to parse session cookie:', err);\n return new Session();\n }\n }\n return new Session(data);\n }\n\n getItem(key: string): string | null {\n return this.data[key] ?? null;\n }\n\n setItem(key: string, value: string) {\n this.data[key] = value;\n }\n\n removeItem(key: string) {\n delete this.data[key];\n }\n\n toString(): string {\n const params = new URLSearchParams(this.data);\n return `b64:${base64Encode(params.toString())}`;\n }\n}\n\nfunction base64Encode(str: string): string {\n return Buffer.from(str, 'utf-8').toString('base64');\n}\n\nfunction base64Decode(str: string): string {\n return Buffer.from(str, 'base64').toString('utf-8');\n}\n"],"mappings":"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","names":["cookieValue"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getVitePlugins
3
- } from "./chunk-6JZGMQBR.js";
3
+ } from "./chunk-WICFYADD.js";
4
4
 
5
5
  // src/node/load-config.ts
6
6
  import path2 from "node:path";
@@ -264,4 +264,4 @@ export {
264
264
  createViteServer,
265
265
  viteSsrLoadModule
266
266
  };
267
- //# sourceMappingURL=chunk-YKQ4SVXX.js.map
267
+ //# sourceMappingURL=chunk-G5ALYSEQ.js.map
@@ -3,7 +3,7 @@ import {
3
3
  rootProjectMiddleware,
4
4
  sessionMiddleware,
5
5
  trailingSlashMiddleware
6
- } from "./chunk-MLPPFGKW.js";
6
+ } from "./chunk-BMKSXG4B.js";
7
7
  import {
8
8
  bundleRootConfig,
9
9
  copyDir,
@@ -21,11 +21,11 @@ import {
21
21
  rmDir,
22
22
  writeFile,
23
23
  writeJson
24
- } from "./chunk-YKQ4SVXX.js";
24
+ } from "./chunk-G5ALYSEQ.js";
25
25
  import {
26
26
  configureServerPlugins,
27
27
  getVitePlugins
28
- } from "./chunk-6JZGMQBR.js";
28
+ } from "./chunk-WICFYADD.js";
29
29
  import {
30
30
  RouteTrie,
31
31
  htmlMinify,
@@ -687,24 +687,104 @@ function formatParams(params) {
687
687
  }).join("\n");
688
688
  }
689
689
 
690
- // src/cli/create-package.ts
690
+ // src/cli/codegen.ts
691
691
  import { promises as fs3 } from "node:fs";
692
692
  import path4 from "node:path";
693
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
694
+ import glob3 from "tiny-glob";
695
+ var __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
696
+ async function codegen(type, name, options) {
697
+ const rootDir = path4.resolve(process.cwd());
698
+ const project = new Project(rootDir);
699
+ await project.generateCode(type, name, options);
700
+ }
701
+ var Project = class {
702
+ rootDir;
703
+ tplDirs;
704
+ constructor(rootDir) {
705
+ this.rootDir = rootDir;
706
+ this.tplDirs = [
707
+ path4.join(rootDir, "codegen"),
708
+ path4.join(__dirname2, "../codegen")
709
+ ];
710
+ }
711
+ async generateCode(type, name, options) {
712
+ const files = await this.loadFiles(type);
713
+ if (Object.keys(files).length === 0) {
714
+ console.log(`no files in codegen/${type}/*.tpl`);
715
+ return;
716
+ }
717
+ const outname = options?.out || `${type}s`;
718
+ const outdir = path4.join(this.rootDir, outname, name);
719
+ if (await dirExists(outdir)) {
720
+ await rmDir(outdir);
721
+ }
722
+ await makeDir(outdir);
723
+ for (const tplName in files) {
724
+ const tpl = await files[tplName].read();
725
+ const content = tpl.replaceAll("[[name]]", name).replaceAll("[[name:camel_upper]]", toCamelCaseUpper(name));
726
+ const filename = tplName.replace("[name]", name);
727
+ const filepath = path4.join(outdir, filename);
728
+ await writeFile(filepath, content);
729
+ console.log(`saved ${filepath}`);
730
+ }
731
+ }
732
+ /**
733
+ * Searches the project's "codegen" dir (or fallsback to root's "codegen" dir)
734
+ * and returns a `TemplateMap` for all files in `codgen/<type>/*.tpl`.
735
+ */
736
+ async loadFiles(type) {
737
+ for (const tplDir of this.tplDirs) {
738
+ const typeDir = path4.join(tplDir, type);
739
+ if (await dirExists(typeDir)) {
740
+ return this.loadTplFiles(typeDir);
741
+ }
742
+ }
743
+ return {};
744
+ }
745
+ /**
746
+ * Reads files matching `*.tpl` in a given directory.
747
+ */
748
+ async loadTplFiles(dirpath) {
749
+ const tplFiles = {};
750
+ const files = await glob3("*.tpl", { cwd: dirpath });
751
+ for (const filename of files) {
752
+ const filepath = path4.join(dirpath, filename);
753
+ const name = filename.slice(0, -4);
754
+ tplFiles[name] = {
755
+ read: () => fs3.readFile(filepath, "utf-8")
756
+ };
757
+ }
758
+ return tplFiles;
759
+ }
760
+ };
761
+ function toCamelCaseUpper(str) {
762
+ const segments = str.split("-");
763
+ return segments.map((part) => toTitleCase(part)).join("");
764
+ }
765
+ function toTitleCase(str) {
766
+ const ch = String(str).charAt(0).toUpperCase();
767
+ return `${ch}${str.slice(1)}`;
768
+ }
769
+
770
+ // src/cli/create-package.ts
771
+ import { promises as fs4 } from "node:fs";
772
+ import path5 from "node:path";
693
773
  import { build as esbuild } from "esbuild";
694
774
  async function createPackage(rootProjectDir, options) {
695
775
  const mode = options?.mode || "production";
696
776
  process.env.NODE_ENV = mode;
697
- const rootDir = path4.resolve(rootProjectDir || process.cwd());
698
- const distDir = path4.join(rootDir, "dist");
777
+ const rootDir = path5.resolve(rootProjectDir || process.cwd());
778
+ const distDir = path5.join(rootDir, "dist");
699
779
  const target = options?.target || await getDefaultTarget(rootDir);
700
- const outDir = path4.resolve(options?.out || target || "out");
780
+ const outDir = path5.resolve(options?.out || target || "out");
701
781
  await build(rootProjectDir, { ssrOnly: true, mode });
702
782
  await rmDir(outDir);
703
783
  await makeDir(outDir);
704
- await copyDir(distDir, path4.resolve(outDir, "dist"));
705
- const collectionsDir = path4.resolve(rootDir, "collections");
784
+ await copyDir(distDir, path5.resolve(outDir, "dist"));
785
+ const collectionsDir = path5.resolve(rootDir, "collections");
706
786
  if (await dirExists(collectionsDir)) {
707
- await copyDir(collectionsDir, path4.join(outDir, "collections"));
787
+ await copyDir(collectionsDir, path5.join(outDir, "collections"));
708
788
  }
709
789
  const packageJson = await generatePackageJson(rootDir);
710
790
  if (options?.version && packageJson.dependencies) {
@@ -723,16 +803,16 @@ async function createPackage(rootProjectDir, options) {
723
803
  } else if (target === "firebase") {
724
804
  await onFirebase({ rootDir, packageJson, outDir });
725
805
  }
726
- await writeJson(path4.resolve(outDir, "package.json"), packageJson);
806
+ await writeJson(path5.resolve(outDir, "package.json"), packageJson);
727
807
  console.log("done!");
728
808
  console.log(`saved package to ${outDir}`);
729
809
  }
730
810
  async function getDefaultTarget(rootDir) {
731
- const firebaseConfigPath = path4.resolve(rootDir, "firebase.json");
811
+ const firebaseConfigPath = path5.resolve(rootDir, "firebase.json");
732
812
  if (await fileExists(firebaseConfigPath)) {
733
813
  return "firebase";
734
814
  }
735
- const appEngineConfigPath = path4.resolve(rootDir, "app.yaml");
815
+ const appEngineConfigPath = path5.resolve(rootDir, "app.yaml");
736
816
  if (await fileExists(appEngineConfigPath)) {
737
817
  return "appengine";
738
818
  }
@@ -740,7 +820,7 @@ async function getDefaultTarget(rootDir) {
740
820
  }
741
821
  async function generatePackageJson(rootDir) {
742
822
  const packageJson = await loadJson(
743
- path4.resolve(rootDir, "package.json")
823
+ path5.resolve(rootDir, "package.json")
744
824
  );
745
825
  if (packageJson.peerDependencies) {
746
826
  await updatePeerDepsFromWorkspace(rootDir, packageJson);
@@ -756,7 +836,7 @@ async function updatePeerDepsFromWorkspace(rootDir, packageJson) {
756
836
  if (!workspaceRoot) {
757
837
  return;
758
838
  }
759
- const workspacePackageJsonPath = path4.resolve(workspaceRoot, "package.json");
839
+ const workspacePackageJsonPath = path5.resolve(workspaceRoot, "package.json");
760
840
  const workspacePackageJson = await loadJson(
761
841
  workspacePackageJsonPath
762
842
  );
@@ -779,15 +859,15 @@ async function updatePeerDepsFromWorkspace(rootDir, packageJson) {
779
859
  }
780
860
  }
781
861
  async function findWorkspaceRoot(rootDir) {
782
- const parentDir = path4.dirname(rootDir);
862
+ const parentDir = path5.dirname(rootDir);
783
863
  if (parentDir === "/") {
784
864
  return null;
785
865
  }
786
- const pnpmWorkspaceFile = path4.resolve(parentDir, "pnpm-workspace.yaml");
866
+ const pnpmWorkspaceFile = path5.resolve(parentDir, "pnpm-workspace.yaml");
787
867
  if (await fileExists(pnpmWorkspaceFile)) {
788
868
  return parentDir;
789
869
  }
790
- const packageJsonPath = path4.resolve(parentDir, "package.json");
870
+ const packageJsonPath = path5.resolve(parentDir, "package.json");
791
871
  if (await fileExists(packageJsonPath)) {
792
872
  const parentPackageJson = await loadJson(packageJsonPath);
793
873
  if (parentPackageJson && parentPackageJson.workspaces) {
@@ -809,21 +889,31 @@ function getRequiredPeerDeps(packageJson) {
809
889
  return requiredPeerDeps;
810
890
  }
811
891
  async function onAppEngine(options) {
812
- const { rootDir, outDir } = options;
813
- const configPath = path4.resolve(rootDir, "app.yaml");
892
+ const { rootDir, outDir, packageJson } = options;
893
+ const configPath = path5.resolve(rootDir, "app.yaml");
814
894
  if (await fileExists(configPath)) {
815
- await fs3.copyFile(configPath, path4.resolve(outDir, "app.yaml"));
895
+ await fs4.copyFile(configPath, path5.resolve(outDir, "app.yaml"));
896
+ }
897
+ if (packageJson.scripts?.start) {
898
+ packageJson.scripts = { start: packageJson.scripts.start };
899
+ } else {
900
+ packageJson.scripts = { start: "root start --host=0.0.0.0" };
816
901
  }
817
902
  }
818
903
  async function onFirebase(options) {
819
- const { rootDir, outDir } = options;
820
- const outBasename = path4.basename(outDir);
904
+ const { rootDir, outDir, packageJson } = options;
905
+ const outBasename = path5.basename(outDir);
821
906
  if (outBasename === "functions") {
822
- const indexTsFile = path4.resolve(rootDir, "index.ts");
907
+ const indexTsFile = path5.resolve(rootDir, "index.ts");
823
908
  if (await fileExists(indexTsFile)) {
824
- await bundleTsFile(indexTsFile, path4.resolve(outDir, "index.js"));
909
+ await bundleTsFile(indexTsFile, path5.resolve(outDir, "index.js"));
825
910
  }
826
911
  }
912
+ if (packageJson.scripts?.start) {
913
+ packageJson.scripts = { start: packageJson.scripts.start };
914
+ } else {
915
+ packageJson.scripts = { start: "root start --host=0.0.0.0" };
916
+ }
827
917
  }
828
918
  async function bundleTsFile(srcPath, outPath) {
829
919
  await esbuild({
@@ -841,7 +931,7 @@ async function bundleTsFile(srcPath, outPath) {
841
931
  setup(build2) {
842
932
  build2.onResolve({ filter: /.*/ }, (args) => {
843
933
  const id = args.path;
844
- if (id[0] !== "." && !path4.isAbsolute(id)) {
934
+ if (id[0] !== "." && !path5.isAbsolute(id)) {
845
935
  return {
846
936
  external: true
847
937
  };
@@ -855,13 +945,13 @@ async function bundleTsFile(srcPath, outPath) {
855
945
  }
856
946
 
857
947
  // src/cli/dev.ts
858
- import path6 from "node:path";
859
- import { fileURLToPath as fileURLToPath2 } from "node:url";
948
+ import path7 from "node:path";
949
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
860
950
  import cookieParser from "cookie-parser";
861
951
  import { default as express } from "express";
862
952
  import { dim as dim2 } from "kleur/colors";
863
953
  import sirv from "sirv";
864
- import glob3 from "tiny-glob";
954
+ import glob4 from "tiny-glob";
865
955
 
866
956
  // src/middleware/hooks.ts
867
957
  function hooksMiddleware() {
@@ -930,7 +1020,7 @@ function replaceParams(urlPathFormat, params) {
930
1020
  }
931
1021
 
932
1022
  // src/render/asset-map/dev-asset-map.ts
933
- import path5 from "node:path";
1023
+ import path6 from "node:path";
934
1024
  import { searchForWorkspaceRoot as searchForWorkspaceRoot2 } from "vite";
935
1025
  var DevServerAssetMap = class {
936
1026
  rootConfig;
@@ -940,7 +1030,7 @@ var DevServerAssetMap = class {
940
1030
  this.moduleGraph = moduleGraph;
941
1031
  }
942
1032
  async get(src) {
943
- const file = path5.resolve(this.rootConfig.rootDir, src);
1033
+ const file = path6.resolve(this.rootConfig.rootDir, src);
944
1034
  const viteModules = this.moduleGraph.getModulesByFile(file);
945
1035
  if (viteModules && viteModules.size > 0) {
946
1036
  const [viteModule] = viteModules;
@@ -972,7 +1062,7 @@ var DevServerAssetMap = class {
972
1062
  return null;
973
1063
  }
974
1064
  filePathToSrc(file) {
975
- return path5.relative(this.rootConfig.rootDir, file);
1065
+ return path6.relative(this.rootConfig.rootDir, file);
976
1066
  }
977
1067
  };
978
1068
  var DevServerAsset = class _DevServerAsset {
@@ -1014,7 +1104,7 @@ var DevServerAsset = class _DevServerAsset {
1014
1104
  return;
1015
1105
  }
1016
1106
  visited.add(asset.moduleId);
1017
- const parts = path5.parse(asset.assetUrl);
1107
+ const parts = path6.parse(asset.assetUrl);
1018
1108
  if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
1019
1109
  urls.add(asset.assetUrl);
1020
1110
  }
@@ -1041,7 +1131,7 @@ var DevServerAsset = class _DevServerAsset {
1041
1131
  }
1042
1132
  visited.add(asset.assetUrl);
1043
1133
  if (asset.src.endsWith(".scss")) {
1044
- const parts = path5.parse(asset.src);
1134
+ const parts = path6.parse(asset.src);
1045
1135
  if (!parts.name.startsWith("_")) {
1046
1136
  urls.add(asset.assetUrl);
1047
1137
  }
@@ -1108,10 +1198,10 @@ function randString(len) {
1108
1198
  }
1109
1199
 
1110
1200
  // src/cli/dev.ts
1111
- var __dirname2 = path6.dirname(fileURLToPath2(import.meta.url));
1201
+ var __dirname3 = path7.dirname(fileURLToPath3(import.meta.url));
1112
1202
  async function dev(rootProjectDir, options) {
1113
1203
  process.env.NODE_ENV = "development";
1114
- const rootDir = path6.resolve(rootProjectDir || process.cwd());
1204
+ const rootDir = path7.resolve(rootProjectDir || process.cwd());
1115
1205
  const defaultPort = parseInt(process.env.PORT || "4007");
1116
1206
  const host = options?.host || "localhost";
1117
1207
  const port = await findOpenPort(defaultPort, defaultPort + 10);
@@ -1129,7 +1219,7 @@ async function dev(rootProjectDir, options) {
1129
1219
  server.listen(port, host);
1130
1220
  }
1131
1221
  async function createDevServer(options) {
1132
- const rootDir = path6.resolve(options?.rootDir || process.cwd());
1222
+ const rootDir = path7.resolve(options?.rootDir || process.cwd());
1133
1223
  const rootConfig = await loadRootConfig(rootDir, { command: "dev" });
1134
1224
  const port = options?.port;
1135
1225
  const server = express();
@@ -1162,7 +1252,7 @@ async function createDevServer(options) {
1162
1252
  if (rootConfig.server?.headers) {
1163
1253
  server.use(headersMiddleware({ rootConfig }));
1164
1254
  }
1165
- const publicDir = path6.join(rootDir, "public");
1255
+ const publicDir = path7.join(rootDir, "public");
1166
1256
  if (await dirExists(publicDir)) {
1167
1257
  server.use(rootPublicDirMiddleware({ publicDir, viteServer }));
1168
1258
  }
@@ -1184,10 +1274,10 @@ async function createViteMiddleware(options) {
1184
1274
  return sourceFile.relPath;
1185
1275
  });
1186
1276
  const bundleScripts = [];
1187
- if (await isDirectory(path6.join(rootDir, "bundles"))) {
1188
- const bundleFiles = await glob3("bundles/*", { cwd: rootDir });
1277
+ if (await isDirectory(path7.join(rootDir, "bundles"))) {
1278
+ const bundleFiles = await glob4("bundles/*", { cwd: rootDir });
1189
1279
  bundleFiles.forEach((file) => {
1190
- const parts = path6.parse(file);
1280
+ const parts = path7.parse(file);
1191
1281
  if (isJsFile(parts.base)) {
1192
1282
  bundleScripts.push(file);
1193
1283
  }
@@ -1199,7 +1289,7 @@ async function createViteMiddleware(options) {
1199
1289
  optimizeDeps
1200
1290
  });
1201
1291
  function isInElementsDir(changedFilePath) {
1202
- const filePath = path6.resolve(changedFilePath);
1292
+ const filePath = path7.resolve(changedFilePath);
1203
1293
  const elementsDirs = getElementsDirs(rootConfig);
1204
1294
  return elementsDirs.some((dirPath) => filePath.startsWith(dirPath));
1205
1295
  }
@@ -1218,7 +1308,7 @@ async function createViteMiddleware(options) {
1218
1308
  const viteMiddleware = async (req, res, next) => {
1219
1309
  try {
1220
1310
  req.viteServer = viteServer;
1221
- const renderModulePath = path6.resolve(__dirname2, "./render.js");
1311
+ const renderModulePath = path7.resolve(__dirname3, "./render.js");
1222
1312
  const render = await viteServer.ssrLoadModule(
1223
1313
  renderModulePath
1224
1314
  );
@@ -1242,7 +1332,7 @@ function rootPublicDirMiddleware(options) {
1242
1332
  handler = sirv(publicDir, sirvOptions);
1243
1333
  }, 1e3);
1244
1334
  function isInPublicDir(changedFilePath) {
1245
- const filePath = path6.resolve(changedFilePath);
1335
+ const filePath = path7.resolve(changedFilePath);
1246
1336
  return filePath.startsWith(publicDir);
1247
1337
  }
1248
1338
  const watcher = options.viteServer.watcher;
@@ -1272,7 +1362,7 @@ function rootDevServer404Middleware() {
1272
1362
  console.error(`\u2753 404 ${req.originalUrl}`);
1273
1363
  if (req.renderer) {
1274
1364
  const url = req.path;
1275
- const ext = path6.extname(url);
1365
+ const ext = path7.extname(url);
1276
1366
  if (!ext) {
1277
1367
  const renderer = req.renderer;
1278
1368
  const data = await renderer.renderDevServer404(req);
@@ -1290,7 +1380,7 @@ function rootDevServer500Middleware() {
1290
1380
  console.error(String(err.stack || err));
1291
1381
  if (req.renderer) {
1292
1382
  const url = req.path;
1293
- const ext = path6.extname(url);
1383
+ const ext = path7.extname(url);
1294
1384
  if (!ext) {
1295
1385
  const renderer = req.renderer;
1296
1386
  const data = await renderer.renderDevServer500(req, err);
@@ -1315,7 +1405,7 @@ function debounce(fn, timeout) {
1315
1405
  }
1316
1406
 
1317
1407
  // src/cli/preview.ts
1318
- import path7 from "node:path";
1408
+ import path8 from "node:path";
1319
1409
  import compression from "compression";
1320
1410
  import cookieParser2 from "cookie-parser";
1321
1411
  import { default as express2 } from "express";
@@ -1323,7 +1413,7 @@ import { dim as dim3 } from "kleur/colors";
1323
1413
  import sirv2 from "sirv";
1324
1414
  async function preview(rootProjectDir, options) {
1325
1415
  process.env.NODE_ENV = "development";
1326
- const rootDir = path7.resolve(rootProjectDir || process.cwd());
1416
+ const rootDir = path8.resolve(rootProjectDir || process.cwd());
1327
1417
  const server = await createPreviewServer({ rootDir });
1328
1418
  const port = parseInt(process.env.PORT || "4007");
1329
1419
  const host = options?.host || "localhost";
@@ -1337,7 +1427,7 @@ async function preview(rootProjectDir, options) {
1337
1427
  async function createPreviewServer(options) {
1338
1428
  const rootDir = options.rootDir;
1339
1429
  const rootConfig = await loadBundledConfig(rootDir, { command: "preview" });
1340
- const distDir = path7.join(rootDir, "dist");
1430
+ const distDir = path8.join(rootDir, "dist");
1341
1431
  const server = express2();
1342
1432
  server.disable("x-powered-by");
1343
1433
  server.use(compression());
@@ -1363,7 +1453,7 @@ async function createPreviewServer(options) {
1363
1453
  if (rootConfig.server?.headers) {
1364
1454
  server.use(headersMiddleware({ rootConfig }));
1365
1455
  }
1366
- const publicDir = path7.join(distDir, "html");
1456
+ const publicDir = path8.join(distDir, "html");
1367
1457
  server.use(sirv2(publicDir, { dev: false }));
1368
1458
  server.use(trailingSlashMiddleware({ rootConfig }));
1369
1459
  server.use(rootPreviewServerMiddleware());
@@ -1377,14 +1467,14 @@ async function createPreviewServer(options) {
1377
1467
  }
1378
1468
  async function rootPreviewRendererMiddleware(options) {
1379
1469
  const { distDir, rootConfig } = options;
1380
- const render = await import(path7.join(distDir, "server/render.js"));
1381
- const manifestPath = path7.join(distDir, ".root/manifest.json");
1470
+ const render = await import(path8.join(distDir, "server/render.js"));
1471
+ const manifestPath = path8.join(distDir, ".root/manifest.json");
1382
1472
  if (!await fileExists(manifestPath)) {
1383
1473
  throw new Error(
1384
1474
  `could not find ${manifestPath}. run \`root build\` before \`root preview\`.`
1385
1475
  );
1386
1476
  }
1387
- const elementGraphJsonPath = path7.join(distDir, ".root/elements.json");
1477
+ const elementGraphJsonPath = path8.join(distDir, ".root/elements.json");
1388
1478
  if (!await fileExists(elementGraphJsonPath)) {
1389
1479
  throw new Error(
1390
1480
  `could not find ${elementGraphJsonPath}. run \`root build\` before \`root preview\`.`
@@ -1424,7 +1514,7 @@ function rootPreviewServer404Middleware() {
1424
1514
  console.error(`\u2753 404 ${req.originalUrl}`);
1425
1515
  if (req.renderer) {
1426
1516
  const url = req.path;
1427
- const ext = path7.extname(url);
1517
+ const ext = path8.extname(url);
1428
1518
  if (!ext) {
1429
1519
  const renderer = req.renderer;
1430
1520
  const data = await renderer.render404({ currentPath: url });
@@ -1442,7 +1532,7 @@ function rootPreviewServer500Middleware() {
1442
1532
  console.error(String(err.stack || err));
1443
1533
  if (req.renderer) {
1444
1534
  const url = req.path;
1445
- const ext = path7.extname(url);
1535
+ const ext = path8.extname(url);
1446
1536
  if (!ext) {
1447
1537
  const renderer = req.renderer;
1448
1538
  const data = await renderer.renderDevServer500(req, err);
@@ -1456,7 +1546,7 @@ function rootPreviewServer500Middleware() {
1456
1546
  }
1457
1547
 
1458
1548
  // src/cli/start.ts
1459
- import path8 from "node:path";
1549
+ import path9 from "node:path";
1460
1550
  import compression2 from "compression";
1461
1551
  import cookieParser3 from "cookie-parser";
1462
1552
  import { default as express3 } from "express";
@@ -1464,7 +1554,7 @@ import { dim as dim4 } from "kleur/colors";
1464
1554
  import sirv3 from "sirv";
1465
1555
  async function start(rootProjectDir, options) {
1466
1556
  process.env.NODE_ENV = "production";
1467
- const rootDir = path8.resolve(rootProjectDir || process.cwd());
1557
+ const rootDir = path9.resolve(rootProjectDir || process.cwd());
1468
1558
  const server = await createProdServer({ rootDir });
1469
1559
  const port = parseInt(process.env.PORT || "4007");
1470
1560
  const host = options?.host || "localhost";
@@ -1478,7 +1568,7 @@ async function start(rootProjectDir, options) {
1478
1568
  async function createProdServer(options) {
1479
1569
  const rootDir = options.rootDir;
1480
1570
  const rootConfig = await loadBundledConfig(rootDir, { command: "start" });
1481
- const distDir = path8.join(rootDir, "dist");
1571
+ const distDir = path9.join(rootDir, "dist");
1482
1572
  const server = express3();
1483
1573
  server.disable("x-powered-by");
1484
1574
  server.use(compression2());
@@ -1504,7 +1594,7 @@ async function createProdServer(options) {
1504
1594
  if (rootConfig.server?.headers) {
1505
1595
  server.use(headersMiddleware({ rootConfig }));
1506
1596
  }
1507
- const publicDir = path8.join(distDir, "html");
1597
+ const publicDir = path9.join(distDir, "html");
1508
1598
  server.use(sirv3(publicDir, { dev: false }));
1509
1599
  server.use(trailingSlashMiddleware({ rootConfig }));
1510
1600
  server.use(rootProdServerMiddleware());
@@ -1518,14 +1608,14 @@ async function createProdServer(options) {
1518
1608
  }
1519
1609
  async function rootProdRendererMiddleware(options) {
1520
1610
  const { distDir, rootConfig } = options;
1521
- const render = await import(path8.join(distDir, "server/render.js"));
1522
- const manifestPath = path8.join(distDir, ".root/manifest.json");
1611
+ const render = await import(path9.join(distDir, "server/render.js"));
1612
+ const manifestPath = path9.join(distDir, ".root/manifest.json");
1523
1613
  if (!await fileExists(manifestPath)) {
1524
1614
  throw new Error(
1525
1615
  `could not find ${manifestPath}. run \`root build\` before \`root start\`.`
1526
1616
  );
1527
1617
  }
1528
- const elementGraphJsonPath = path8.join(distDir, ".root/elements.json");
1618
+ const elementGraphJsonPath = path9.join(distDir, ".root/elements.json");
1529
1619
  if (!await fileExists(elementGraphJsonPath)) {
1530
1620
  throw new Error(
1531
1621
  `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
@@ -1565,7 +1655,7 @@ function rootProdServer404Middleware() {
1565
1655
  console.error(`\u2753 404 ${req.originalUrl}`);
1566
1656
  if (req.renderer) {
1567
1657
  const url = req.path;
1568
- const ext = path8.extname(url);
1658
+ const ext = path9.extname(url);
1569
1659
  if (!ext) {
1570
1660
  const renderer = req.renderer;
1571
1661
  const data = await renderer.render404({ currentPath: url });
@@ -1583,7 +1673,7 @@ function rootProdServer500Middleware() {
1583
1673
  console.error(String(err.stack || err));
1584
1674
  if (req.renderer) {
1585
1675
  const url = req.path;
1586
- const ext = path8.extname(url);
1676
+ const ext = path9.extname(url);
1587
1677
  if (!ext) {
1588
1678
  const renderer = req.renderer;
1589
1679
  const data = await renderer.renderError(err);
@@ -1625,6 +1715,7 @@ var CliRunner = class {
1625
1715
  "number of files to build concurrently",
1626
1716
  "10"
1627
1717
  ).action(build);
1718
+ program.command("codegen [type] [name]").description("generates boilerplate code").option("--out <outdir>", "output dir").action(codegen);
1628
1719
  program.command("create-package [path]").alias("package").description(
1629
1720
  "creates a standalone npm package for deployment to various hosting services"
1630
1721
  ).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) => {
@@ -1658,4 +1749,4 @@ export {
1658
1749
  createProdServer,
1659
1750
  CliRunner
1660
1751
  };
1661
- //# sourceMappingURL=chunk-U2COJCIX.js.map
1752
+ //# sourceMappingURL=chunk-GNRN2XDJ.js.map