@blinkk/root 1.0.6 → 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) {
@@ -810,9 +890,9 @@ function getRequiredPeerDeps(packageJson) {
810
890
  }
811
891
  async function onAppEngine(options) {
812
892
  const { rootDir, outDir, packageJson } = options;
813
- const configPath = path4.resolve(rootDir, "app.yaml");
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"));
816
896
  }
817
897
  if (packageJson.scripts?.start) {
818
898
  packageJson.scripts = { start: packageJson.scripts.start };
@@ -822,11 +902,11 @@ async function onAppEngine(options) {
822
902
  }
823
903
  async function onFirebase(options) {
824
904
  const { rootDir, outDir, packageJson } = options;
825
- const outBasename = path4.basename(outDir);
905
+ const outBasename = path5.basename(outDir);
826
906
  if (outBasename === "functions") {
827
- const indexTsFile = path4.resolve(rootDir, "index.ts");
907
+ const indexTsFile = path5.resolve(rootDir, "index.ts");
828
908
  if (await fileExists(indexTsFile)) {
829
- await bundleTsFile(indexTsFile, path4.resolve(outDir, "index.js"));
909
+ await bundleTsFile(indexTsFile, path5.resolve(outDir, "index.js"));
830
910
  }
831
911
  }
832
912
  if (packageJson.scripts?.start) {
@@ -851,7 +931,7 @@ async function bundleTsFile(srcPath, outPath) {
851
931
  setup(build2) {
852
932
  build2.onResolve({ filter: /.*/ }, (args) => {
853
933
  const id = args.path;
854
- if (id[0] !== "." && !path4.isAbsolute(id)) {
934
+ if (id[0] !== "." && !path5.isAbsolute(id)) {
855
935
  return {
856
936
  external: true
857
937
  };
@@ -865,13 +945,13 @@ async function bundleTsFile(srcPath, outPath) {
865
945
  }
866
946
 
867
947
  // src/cli/dev.ts
868
- import path6 from "node:path";
869
- import { fileURLToPath as fileURLToPath2 } from "node:url";
948
+ import path7 from "node:path";
949
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
870
950
  import cookieParser from "cookie-parser";
871
951
  import { default as express } from "express";
872
952
  import { dim as dim2 } from "kleur/colors";
873
953
  import sirv from "sirv";
874
- import glob3 from "tiny-glob";
954
+ import glob4 from "tiny-glob";
875
955
 
876
956
  // src/middleware/hooks.ts
877
957
  function hooksMiddleware() {
@@ -940,7 +1020,7 @@ function replaceParams(urlPathFormat, params) {
940
1020
  }
941
1021
 
942
1022
  // src/render/asset-map/dev-asset-map.ts
943
- import path5 from "node:path";
1023
+ import path6 from "node:path";
944
1024
  import { searchForWorkspaceRoot as searchForWorkspaceRoot2 } from "vite";
945
1025
  var DevServerAssetMap = class {
946
1026
  rootConfig;
@@ -950,7 +1030,7 @@ var DevServerAssetMap = class {
950
1030
  this.moduleGraph = moduleGraph;
951
1031
  }
952
1032
  async get(src) {
953
- const file = path5.resolve(this.rootConfig.rootDir, src);
1033
+ const file = path6.resolve(this.rootConfig.rootDir, src);
954
1034
  const viteModules = this.moduleGraph.getModulesByFile(file);
955
1035
  if (viteModules && viteModules.size > 0) {
956
1036
  const [viteModule] = viteModules;
@@ -982,7 +1062,7 @@ var DevServerAssetMap = class {
982
1062
  return null;
983
1063
  }
984
1064
  filePathToSrc(file) {
985
- return path5.relative(this.rootConfig.rootDir, file);
1065
+ return path6.relative(this.rootConfig.rootDir, file);
986
1066
  }
987
1067
  };
988
1068
  var DevServerAsset = class _DevServerAsset {
@@ -1024,7 +1104,7 @@ var DevServerAsset = class _DevServerAsset {
1024
1104
  return;
1025
1105
  }
1026
1106
  visited.add(asset.moduleId);
1027
- const parts = path5.parse(asset.assetUrl);
1107
+ const parts = path6.parse(asset.assetUrl);
1028
1108
  if ([".js", ".jsx", ".ts", ".tsx"].includes(parts.ext) && asset.moduleId.includes("/elements/")) {
1029
1109
  urls.add(asset.assetUrl);
1030
1110
  }
@@ -1051,7 +1131,7 @@ var DevServerAsset = class _DevServerAsset {
1051
1131
  }
1052
1132
  visited.add(asset.assetUrl);
1053
1133
  if (asset.src.endsWith(".scss")) {
1054
- const parts = path5.parse(asset.src);
1134
+ const parts = path6.parse(asset.src);
1055
1135
  if (!parts.name.startsWith("_")) {
1056
1136
  urls.add(asset.assetUrl);
1057
1137
  }
@@ -1118,10 +1198,10 @@ function randString(len) {
1118
1198
  }
1119
1199
 
1120
1200
  // src/cli/dev.ts
1121
- var __dirname2 = path6.dirname(fileURLToPath2(import.meta.url));
1201
+ var __dirname3 = path7.dirname(fileURLToPath3(import.meta.url));
1122
1202
  async function dev(rootProjectDir, options) {
1123
1203
  process.env.NODE_ENV = "development";
1124
- const rootDir = path6.resolve(rootProjectDir || process.cwd());
1204
+ const rootDir = path7.resolve(rootProjectDir || process.cwd());
1125
1205
  const defaultPort = parseInt(process.env.PORT || "4007");
1126
1206
  const host = options?.host || "localhost";
1127
1207
  const port = await findOpenPort(defaultPort, defaultPort + 10);
@@ -1139,7 +1219,7 @@ async function dev(rootProjectDir, options) {
1139
1219
  server.listen(port, host);
1140
1220
  }
1141
1221
  async function createDevServer(options) {
1142
- const rootDir = path6.resolve(options?.rootDir || process.cwd());
1222
+ const rootDir = path7.resolve(options?.rootDir || process.cwd());
1143
1223
  const rootConfig = await loadRootConfig(rootDir, { command: "dev" });
1144
1224
  const port = options?.port;
1145
1225
  const server = express();
@@ -1172,7 +1252,7 @@ async function createDevServer(options) {
1172
1252
  if (rootConfig.server?.headers) {
1173
1253
  server.use(headersMiddleware({ rootConfig }));
1174
1254
  }
1175
- const publicDir = path6.join(rootDir, "public");
1255
+ const publicDir = path7.join(rootDir, "public");
1176
1256
  if (await dirExists(publicDir)) {
1177
1257
  server.use(rootPublicDirMiddleware({ publicDir, viteServer }));
1178
1258
  }
@@ -1194,10 +1274,10 @@ async function createViteMiddleware(options) {
1194
1274
  return sourceFile.relPath;
1195
1275
  });
1196
1276
  const bundleScripts = [];
1197
- if (await isDirectory(path6.join(rootDir, "bundles"))) {
1198
- const bundleFiles = await glob3("bundles/*", { cwd: rootDir });
1277
+ if (await isDirectory(path7.join(rootDir, "bundles"))) {
1278
+ const bundleFiles = await glob4("bundles/*", { cwd: rootDir });
1199
1279
  bundleFiles.forEach((file) => {
1200
- const parts = path6.parse(file);
1280
+ const parts = path7.parse(file);
1201
1281
  if (isJsFile(parts.base)) {
1202
1282
  bundleScripts.push(file);
1203
1283
  }
@@ -1209,7 +1289,7 @@ async function createViteMiddleware(options) {
1209
1289
  optimizeDeps
1210
1290
  });
1211
1291
  function isInElementsDir(changedFilePath) {
1212
- const filePath = path6.resolve(changedFilePath);
1292
+ const filePath = path7.resolve(changedFilePath);
1213
1293
  const elementsDirs = getElementsDirs(rootConfig);
1214
1294
  return elementsDirs.some((dirPath) => filePath.startsWith(dirPath));
1215
1295
  }
@@ -1228,7 +1308,7 @@ async function createViteMiddleware(options) {
1228
1308
  const viteMiddleware = async (req, res, next) => {
1229
1309
  try {
1230
1310
  req.viteServer = viteServer;
1231
- const renderModulePath = path6.resolve(__dirname2, "./render.js");
1311
+ const renderModulePath = path7.resolve(__dirname3, "./render.js");
1232
1312
  const render = await viteServer.ssrLoadModule(
1233
1313
  renderModulePath
1234
1314
  );
@@ -1252,7 +1332,7 @@ function rootPublicDirMiddleware(options) {
1252
1332
  handler = sirv(publicDir, sirvOptions);
1253
1333
  }, 1e3);
1254
1334
  function isInPublicDir(changedFilePath) {
1255
- const filePath = path6.resolve(changedFilePath);
1335
+ const filePath = path7.resolve(changedFilePath);
1256
1336
  return filePath.startsWith(publicDir);
1257
1337
  }
1258
1338
  const watcher = options.viteServer.watcher;
@@ -1282,7 +1362,7 @@ function rootDevServer404Middleware() {
1282
1362
  console.error(`\u2753 404 ${req.originalUrl}`);
1283
1363
  if (req.renderer) {
1284
1364
  const url = req.path;
1285
- const ext = path6.extname(url);
1365
+ const ext = path7.extname(url);
1286
1366
  if (!ext) {
1287
1367
  const renderer = req.renderer;
1288
1368
  const data = await renderer.renderDevServer404(req);
@@ -1300,7 +1380,7 @@ function rootDevServer500Middleware() {
1300
1380
  console.error(String(err.stack || err));
1301
1381
  if (req.renderer) {
1302
1382
  const url = req.path;
1303
- const ext = path6.extname(url);
1383
+ const ext = path7.extname(url);
1304
1384
  if (!ext) {
1305
1385
  const renderer = req.renderer;
1306
1386
  const data = await renderer.renderDevServer500(req, err);
@@ -1325,7 +1405,7 @@ function debounce(fn, timeout) {
1325
1405
  }
1326
1406
 
1327
1407
  // src/cli/preview.ts
1328
- import path7 from "node:path";
1408
+ import path8 from "node:path";
1329
1409
  import compression from "compression";
1330
1410
  import cookieParser2 from "cookie-parser";
1331
1411
  import { default as express2 } from "express";
@@ -1333,7 +1413,7 @@ import { dim as dim3 } from "kleur/colors";
1333
1413
  import sirv2 from "sirv";
1334
1414
  async function preview(rootProjectDir, options) {
1335
1415
  process.env.NODE_ENV = "development";
1336
- const rootDir = path7.resolve(rootProjectDir || process.cwd());
1416
+ const rootDir = path8.resolve(rootProjectDir || process.cwd());
1337
1417
  const server = await createPreviewServer({ rootDir });
1338
1418
  const port = parseInt(process.env.PORT || "4007");
1339
1419
  const host = options?.host || "localhost";
@@ -1347,7 +1427,7 @@ async function preview(rootProjectDir, options) {
1347
1427
  async function createPreviewServer(options) {
1348
1428
  const rootDir = options.rootDir;
1349
1429
  const rootConfig = await loadBundledConfig(rootDir, { command: "preview" });
1350
- const distDir = path7.join(rootDir, "dist");
1430
+ const distDir = path8.join(rootDir, "dist");
1351
1431
  const server = express2();
1352
1432
  server.disable("x-powered-by");
1353
1433
  server.use(compression());
@@ -1373,7 +1453,7 @@ async function createPreviewServer(options) {
1373
1453
  if (rootConfig.server?.headers) {
1374
1454
  server.use(headersMiddleware({ rootConfig }));
1375
1455
  }
1376
- const publicDir = path7.join(distDir, "html");
1456
+ const publicDir = path8.join(distDir, "html");
1377
1457
  server.use(sirv2(publicDir, { dev: false }));
1378
1458
  server.use(trailingSlashMiddleware({ rootConfig }));
1379
1459
  server.use(rootPreviewServerMiddleware());
@@ -1387,14 +1467,14 @@ async function createPreviewServer(options) {
1387
1467
  }
1388
1468
  async function rootPreviewRendererMiddleware(options) {
1389
1469
  const { distDir, rootConfig } = options;
1390
- const render = await import(path7.join(distDir, "server/render.js"));
1391
- 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");
1392
1472
  if (!await fileExists(manifestPath)) {
1393
1473
  throw new Error(
1394
1474
  `could not find ${manifestPath}. run \`root build\` before \`root preview\`.`
1395
1475
  );
1396
1476
  }
1397
- const elementGraphJsonPath = path7.join(distDir, ".root/elements.json");
1477
+ const elementGraphJsonPath = path8.join(distDir, ".root/elements.json");
1398
1478
  if (!await fileExists(elementGraphJsonPath)) {
1399
1479
  throw new Error(
1400
1480
  `could not find ${elementGraphJsonPath}. run \`root build\` before \`root preview\`.`
@@ -1434,7 +1514,7 @@ function rootPreviewServer404Middleware() {
1434
1514
  console.error(`\u2753 404 ${req.originalUrl}`);
1435
1515
  if (req.renderer) {
1436
1516
  const url = req.path;
1437
- const ext = path7.extname(url);
1517
+ const ext = path8.extname(url);
1438
1518
  if (!ext) {
1439
1519
  const renderer = req.renderer;
1440
1520
  const data = await renderer.render404({ currentPath: url });
@@ -1452,7 +1532,7 @@ function rootPreviewServer500Middleware() {
1452
1532
  console.error(String(err.stack || err));
1453
1533
  if (req.renderer) {
1454
1534
  const url = req.path;
1455
- const ext = path7.extname(url);
1535
+ const ext = path8.extname(url);
1456
1536
  if (!ext) {
1457
1537
  const renderer = req.renderer;
1458
1538
  const data = await renderer.renderDevServer500(req, err);
@@ -1466,7 +1546,7 @@ function rootPreviewServer500Middleware() {
1466
1546
  }
1467
1547
 
1468
1548
  // src/cli/start.ts
1469
- import path8 from "node:path";
1549
+ import path9 from "node:path";
1470
1550
  import compression2 from "compression";
1471
1551
  import cookieParser3 from "cookie-parser";
1472
1552
  import { default as express3 } from "express";
@@ -1474,7 +1554,7 @@ import { dim as dim4 } from "kleur/colors";
1474
1554
  import sirv3 from "sirv";
1475
1555
  async function start(rootProjectDir, options) {
1476
1556
  process.env.NODE_ENV = "production";
1477
- const rootDir = path8.resolve(rootProjectDir || process.cwd());
1557
+ const rootDir = path9.resolve(rootProjectDir || process.cwd());
1478
1558
  const server = await createProdServer({ rootDir });
1479
1559
  const port = parseInt(process.env.PORT || "4007");
1480
1560
  const host = options?.host || "localhost";
@@ -1488,7 +1568,7 @@ async function start(rootProjectDir, options) {
1488
1568
  async function createProdServer(options) {
1489
1569
  const rootDir = options.rootDir;
1490
1570
  const rootConfig = await loadBundledConfig(rootDir, { command: "start" });
1491
- const distDir = path8.join(rootDir, "dist");
1571
+ const distDir = path9.join(rootDir, "dist");
1492
1572
  const server = express3();
1493
1573
  server.disable("x-powered-by");
1494
1574
  server.use(compression2());
@@ -1514,7 +1594,7 @@ async function createProdServer(options) {
1514
1594
  if (rootConfig.server?.headers) {
1515
1595
  server.use(headersMiddleware({ rootConfig }));
1516
1596
  }
1517
- const publicDir = path8.join(distDir, "html");
1597
+ const publicDir = path9.join(distDir, "html");
1518
1598
  server.use(sirv3(publicDir, { dev: false }));
1519
1599
  server.use(trailingSlashMiddleware({ rootConfig }));
1520
1600
  server.use(rootProdServerMiddleware());
@@ -1528,14 +1608,14 @@ async function createProdServer(options) {
1528
1608
  }
1529
1609
  async function rootProdRendererMiddleware(options) {
1530
1610
  const { distDir, rootConfig } = options;
1531
- const render = await import(path8.join(distDir, "server/render.js"));
1532
- 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");
1533
1613
  if (!await fileExists(manifestPath)) {
1534
1614
  throw new Error(
1535
1615
  `could not find ${manifestPath}. run \`root build\` before \`root start\`.`
1536
1616
  );
1537
1617
  }
1538
- const elementGraphJsonPath = path8.join(distDir, ".root/elements.json");
1618
+ const elementGraphJsonPath = path9.join(distDir, ".root/elements.json");
1539
1619
  if (!await fileExists(elementGraphJsonPath)) {
1540
1620
  throw new Error(
1541
1621
  `could not find ${elementGraphJsonPath}. run \`root build\` before \`root start\`.`
@@ -1575,7 +1655,7 @@ function rootProdServer404Middleware() {
1575
1655
  console.error(`\u2753 404 ${req.originalUrl}`);
1576
1656
  if (req.renderer) {
1577
1657
  const url = req.path;
1578
- const ext = path8.extname(url);
1658
+ const ext = path9.extname(url);
1579
1659
  if (!ext) {
1580
1660
  const renderer = req.renderer;
1581
1661
  const data = await renderer.render404({ currentPath: url });
@@ -1593,7 +1673,7 @@ function rootProdServer500Middleware() {
1593
1673
  console.error(String(err.stack || err));
1594
1674
  if (req.renderer) {
1595
1675
  const url = req.path;
1596
- const ext = path8.extname(url);
1676
+ const ext = path9.extname(url);
1597
1677
  if (!ext) {
1598
1678
  const renderer = req.renderer;
1599
1679
  const data = await renderer.renderError(err);
@@ -1635,6 +1715,7 @@ var CliRunner = class {
1635
1715
  "number of files to build concurrently",
1636
1716
  "10"
1637
1717
  ).action(build);
1718
+ program.command("codegen [type] [name]").description("generates boilerplate code").option("--out <outdir>", "output dir").action(codegen);
1638
1719
  program.command("create-package [path]").alias("package").description(
1639
1720
  "creates a standalone npm package for deployment to various hosting services"
1640
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) => {
@@ -1668,4 +1749,4 @@ export {
1668
1749
  createProdServer,
1669
1750
  CliRunner
1670
1751
  };
1671
- //# sourceMappingURL=chunk-7Z2GR6V5.js.map
1752
+ //# sourceMappingURL=chunk-GNRN2XDJ.js.map