@blinkk/root 1.0.0-beta.6 → 1.0.0-beta.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/root.js CHANGED
@@ -21,18 +21,22 @@ async function main() {
21
21
  .description('generates a static build')
22
22
  .option('--ssr-only', 'produce a ssr-only build')
23
23
  .option('--mode <mode>', 'see: https://vitejs.dev/guide/env-and-mode.html#modes', 'production')
24
+ .option('-c, --concurrency <num>', 'number of files to build concurrently', 10)
24
25
  .action(build);
25
26
  program
26
27
  .command('dev [path]')
27
28
  .description('starts the server in development mode')
29
+ .option('--host <host>', 'network address the server should listen on, e.g. 127.0.0.1')
28
30
  .action(dev);
29
31
  program
30
32
  .command('preview [path]')
31
33
  .description('starts the server in preview mode')
34
+ .option('--host <host>', 'network address the server should listen on, e.g. 127.0.0.1')
32
35
  .action(preview);
33
36
  program
34
37
  .command('start [path]')
35
38
  .description('starts the server in production mode')
39
+ .option('--host <host>', 'network address the server should listen on, e.g. 127.0.0.1')
36
40
  .action(start);
37
41
  await program.parseAsync(process.argv);
38
42
  }
@@ -0,0 +1,182 @@
1
+ // src/middleware/common.ts
2
+ import path from "node:path";
3
+ function rootProjectMiddleware(options) {
4
+ return (req, _, next) => {
5
+ req.rootConfig = options.rootConfig;
6
+ next();
7
+ };
8
+ }
9
+ function trailingSlashMiddleware(options) {
10
+ var _a;
11
+ const trailingSlash = (_a = options.rootConfig.server) == null ? void 0 : _a.trailingSlash;
12
+ return (req, res, next) => {
13
+ if (trailingSlash === true && !path.extname(req.path) && !req.path.endsWith("/")) {
14
+ const redirectPath = `${req.path}/`;
15
+ redirectWithQuery(req, res, 301, redirectPath);
16
+ return;
17
+ }
18
+ if (trailingSlash === false && !path.extname(req.path) && req.path !== "/" && req.path.endsWith("/")) {
19
+ const redirectPath = removeTrailingSlashes(req.path);
20
+ redirectWithQuery(req, res, 301, redirectPath);
21
+ return;
22
+ }
23
+ next();
24
+ };
25
+ }
26
+ function redirectWithQuery(req, res, redirectCode, redirectPath) {
27
+ const queryStr = getQueryStr(req);
28
+ const redirectUrl = queryStr ? `${redirectPath}?${queryStr}` : redirectPath;
29
+ res.redirect(redirectCode, redirectUrl);
30
+ }
31
+ function getQueryStr(req) {
32
+ const qIndex = req.originalUrl.indexOf("?");
33
+ if (qIndex === -1) {
34
+ return "";
35
+ }
36
+ return req.originalUrl.slice(qIndex + 1);
37
+ }
38
+ function removeTrailingSlashes(urlPath) {
39
+ while (urlPath.endsWith("/") && urlPath !== "/") {
40
+ urlPath = urlPath.slice(0, -1);
41
+ }
42
+ return urlPath;
43
+ }
44
+
45
+ // src/middleware/multipart.ts
46
+ import busboy from "busboy";
47
+ var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
48
+ function multipartMiddleware(options) {
49
+ const maxFileSize = (options == null ? void 0 : options.maxFileSize) || DEFAULT_MAX_FILE_SIZE;
50
+ return (req, res, next) => {
51
+ const contentType = String(req.headers["content-type"] || "");
52
+ if (req.method === "POST" && contentType.startsWith("multipart/form-data")) {
53
+ const parser = busboy({ headers: req.headers });
54
+ const fields = {};
55
+ const files = {};
56
+ parser.on("field", (fieldname, val) => {
57
+ fields[fieldname] = val;
58
+ });
59
+ parser.on(
60
+ "file",
61
+ (fieldname, file, meta) => {
62
+ const { filename, encoding, mimeType: mimetype } = meta;
63
+ const fileChunks = [];
64
+ let totalBytesRead = 0;
65
+ file.on("data", (chunk) => {
66
+ totalBytesRead += chunk.length;
67
+ if (totalBytesRead > maxFileSize) {
68
+ console.error(`File size exceeds the limit: ${fieldname}.`);
69
+ file.removeAllListeners("data");
70
+ file.resume();
71
+ } else {
72
+ fileChunks.push(chunk);
73
+ }
74
+ });
75
+ file.on("end", () => {
76
+ if (totalBytesRead <= maxFileSize) {
77
+ const buffer = Buffer.concat(fileChunks);
78
+ files[fieldname] = {
79
+ fieldname,
80
+ buffer,
81
+ originalName: filename,
82
+ encoding,
83
+ mimetype
84
+ };
85
+ }
86
+ });
87
+ }
88
+ );
89
+ parser.on("finish", () => {
90
+ req.body = fields;
91
+ req.files = files;
92
+ next();
93
+ });
94
+ if (req.rawBody) {
95
+ parser.end(req.rawBody);
96
+ } else {
97
+ req.pipe(parser);
98
+ }
99
+ } else {
100
+ next();
101
+ }
102
+ };
103
+ }
104
+
105
+ // src/middleware/session.ts
106
+ var SESSION_COOKIE = "__session";
107
+ var DEFAULT_MAX_AGE = 60 * 60 * 24 * 5 * 1e3;
108
+ function sessionMiddleware(options) {
109
+ const maxAge = (options == null ? void 0 : options.maxAge) || DEFAULT_MAX_AGE;
110
+ return (req, res, next) => {
111
+ const cookieValue = String(req.signedCookies[SESSION_COOKIE] || "");
112
+ const session = Session.fromCookieValue(cookieValue);
113
+ req.session = session;
114
+ res.session = session;
115
+ res.saveSession = () => {
116
+ const secureCookie = Boolean(process.env.NODE_ENV !== "development");
117
+ const cookieValue2 = session.toString();
118
+ res.cookie(SESSION_COOKIE, cookieValue2, {
119
+ maxAge,
120
+ httpOnly: true,
121
+ secure: secureCookie,
122
+ signed: true,
123
+ sameSite: "strict"
124
+ });
125
+ };
126
+ req.hooks.add("beforeRender", () => {
127
+ res.saveSession();
128
+ });
129
+ next();
130
+ };
131
+ }
132
+ var Session = class {
133
+ constructor(data) {
134
+ this.data = {};
135
+ this.data = data || {};
136
+ }
137
+ static fromCookieValue(cookieValue) {
138
+ const data = {};
139
+ if (cookieValue.startsWith("b64:")) {
140
+ try {
141
+ const encodedStr = cookieValue.slice(4);
142
+ const params = new URLSearchParams(base64Decode(encodedStr));
143
+ for (const [key, value] of params.entries()) {
144
+ data[key] = value;
145
+ }
146
+ } catch (err) {
147
+ console.warn("failed to parse session cookie:", err);
148
+ return new Session();
149
+ }
150
+ }
151
+ return new Session(data);
152
+ }
153
+ getItem(key) {
154
+ return this.data[key] ?? null;
155
+ }
156
+ setItem(key, value) {
157
+ this.data[key] = value;
158
+ }
159
+ removeItem(key) {
160
+ delete this.data[key];
161
+ }
162
+ toString() {
163
+ const params = new URLSearchParams(this.data);
164
+ return `b64:${base64Encode(params.toString())}`;
165
+ }
166
+ };
167
+ function base64Encode(str) {
168
+ return Buffer.from(str, "utf-8").toString("base64");
169
+ }
170
+ function base64Decode(str) {
171
+ return Buffer.from(str, "base64").toString("utf-8");
172
+ }
173
+
174
+ export {
175
+ rootProjectMiddleware,
176
+ trailingSlashMiddleware,
177
+ multipartMiddleware,
178
+ SESSION_COOKIE,
179
+ sessionMiddleware,
180
+ Session
181
+ };
182
+ //# sourceMappingURL=chunk-AJF5RXXV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware/common.ts","../src/middleware/multipart.ts","../src/middleware/session.ts"],"sourcesContent":["import path from 'node:path';\nimport {RootConfig} from '../core/config';\nimport {Request, Response, NextFunction} from '../core/types';\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 * 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 {Request, Response, NextFunction, MultipartFile} from '../core/types';\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?: {maxFileSize?: number}) {\n const maxFileSize = options?.maxFileSize || DEFAULT_MAX_FILE_SIZE;\n\n return (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}\n","import {NextFunction, Request, Response} from '../core/types';\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\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 = () => {\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 res.cookie(SESSION_COOKIE, cookieValue, {\n maxAge: maxAge,\n httpOnly: true,\n secure: secureCookie,\n signed: true,\n sameSite: 'strict',\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,3 +1,19 @@
1
+ // src/utils/elements.ts
2
+ var ELEMENT_RE = /^[a-z][\w-]*-[\w-]*$/;
3
+ var HTML_ELEMENTS_REGEX = /<([a-z][\w-]*-[\w-]*)/g;
4
+ function isValidTagName(tagName) {
5
+ return ELEMENT_RE.test(tagName);
6
+ }
7
+ function parseTagNames(src) {
8
+ const tagNames = /* @__PURE__ */ new Set();
9
+ const matches = Array.from(src.matchAll(HTML_ELEMENTS_REGEX));
10
+ for (const match of matches) {
11
+ const tagName = match[1];
12
+ tagNames.add(tagName);
13
+ }
14
+ return Array.from(tagNames);
15
+ }
16
+
1
17
  // src/render/html-minify.ts
2
18
  import { createRequire } from "module";
3
19
  var require2 = createRequire(import.meta.url);
@@ -17,22 +33,6 @@ async function htmlMinify(html, options) {
17
33
  }
18
34
  }
19
35
 
20
- // src/utils/elements.ts
21
- var ELEMENT_RE = /^[a-z][\w-]*-[\w-]*$/;
22
- var HTML_ELEMENTS_REGEX = /<([a-z][\w-]*-[\w-]*)/g;
23
- function isValidTagName(tagName) {
24
- return ELEMENT_RE.test(tagName);
25
- }
26
- function parseTagNames(src) {
27
- const tagNames = /* @__PURE__ */ new Set();
28
- const matches = Array.from(src.matchAll(HTML_ELEMENTS_REGEX));
29
- for (const match of matches) {
30
- const tagName = match[1];
31
- tagNames.add(tagName);
32
- }
33
- return Array.from(tagNames);
34
- }
35
-
36
36
  // src/render/html-pretty.ts
37
37
  import { createRequire as createRequire2 } from "module";
38
38
  var require3 = createRequire2(import.meta.url);
@@ -53,9 +53,9 @@ async function htmlPretty(html, options) {
53
53
  }
54
54
 
55
55
  export {
56
- htmlMinify,
57
56
  isValidTagName,
58
57
  parseTagNames,
58
+ htmlMinify,
59
59
  htmlPretty
60
60
  };
61
- //# sourceMappingURL=chunk-WVRC46JG.js.map
61
+ //# sourceMappingURL=chunk-DXD5LKU3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/elements.ts","../src/render/html-minify.ts","../src/render/html-pretty.ts"],"sourcesContent":["export const ELEMENT_RE = /^[a-z][\\w-]*-[\\w-]*$/;\nexport const HTML_ELEMENTS_REGEX = /<([a-z][\\w-]*-[\\w-]*)/g;\n\n/**\n * Returns true if the tagName is a valid custom element tag.\n */\nexport function isValidTagName(tagName: string) {\n return ELEMENT_RE.test(tagName);\n}\n\n/**\n * Returns a list of custom elements used in a src string (e.g. HTML, jsx, lit).\n * NOTE: the impl uses a simple regex, so tagNames used in comments and attr\n * values may be included.\n */\nexport function parseTagNames(src: string): string[] {\n const tagNames = new Set<string>();\n const matches = Array.from(src.matchAll(HTML_ELEMENTS_REGEX));\n for (const match of matches) {\n const tagName = match[1];\n tagNames.add(tagName);\n }\n return Array.from(tagNames);\n}\n","import {createRequire} from 'module';\n\nconst require = createRequire(import.meta.url);\nimport type {Options} from 'html-minifier-terser';\n\nconst {minify} = require('html-minifier-terser');\n\nexport type HtmlMinifyOptions = Options;\n\nexport async function htmlMinify(\n html: string,\n options?: HtmlMinifyOptions\n): Promise<string> {\n const minifyOptions = options || {\n collapseWhitespace: true,\n removeComments: true,\n preserveLineBreaks: true,\n };\n try {\n const min = await minify(html, minifyOptions);\n return min.trimStart();\n } catch (e) {\n console.error('failed to minify html:', e);\n return html;\n }\n}\n","import {createRequire} from 'module';\n\nconst require = createRequire(import.meta.url);\nimport type {HTMLBeautifyOptions} from 'js-beautify';\n\nconst beautify = require('js-beautify');\n\nexport type HtmlPrettyOptions = HTMLBeautifyOptions;\n\nexport async function htmlPretty(\n html: string,\n options?: HtmlPrettyOptions\n): Promise<string> {\n const prettyOptions = options || {\n indent_size: 0,\n end_with_newline: true,\n extra_liners: [],\n };\n try {\n const output = beautify.html(html, prettyOptions);\n return output.trimStart();\n } catch (e) {\n console.error('failed to pretty html:', e);\n return html;\n }\n}\n"],"mappings":";AAAO,IAAM,aAAa;AACnB,IAAM,sBAAsB;AAK5B,SAAS,eAAe,SAAiB;AAC9C,SAAO,WAAW,KAAK,OAAO;AAChC;AAOO,SAAS,cAAc,KAAuB;AACnD,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,UAAU,MAAM,KAAK,IAAI,SAAS,mBAAmB,CAAC;AAC5D,aAAW,SAAS,SAAS;AAC3B,UAAM,UAAU,MAAM;AACtB,aAAS,IAAI,OAAO;AAAA,EACtB;AACA,SAAO,MAAM,KAAK,QAAQ;AAC5B;;;ACvBA,SAAQ,qBAAoB;AAE5B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAG7C,IAAM,EAAC,OAAM,IAAIA,SAAQ,sBAAsB;AAI/C,eAAsB,WACpB,MACA,SACiB;AACjB,QAAM,gBAAgB,WAAW;AAAA,IAC/B,oBAAoB;AAAA,IACpB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,EACtB;AACA,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,MAAM,aAAa;AAC5C,WAAO,IAAI,UAAU;AAAA,EACvB,SAAS,GAAP;AACA,YAAQ,MAAM,0BAA0B,CAAC;AACzC,WAAO;AAAA,EACT;AACF;;;ACzBA,SAAQ,iBAAAC,sBAAoB;AAE5B,IAAMC,WAAUD,eAAc,YAAY,GAAG;AAG7C,IAAM,WAAWC,SAAQ,aAAa;AAItC,eAAsB,WACpB,MACA,SACiB;AACjB,QAAM,gBAAgB,WAAW;AAAA,IAC/B,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,cAAc,CAAC;AAAA,EACjB;AACA,MAAI;AACF,UAAM,SAAS,SAAS,KAAK,MAAM,aAAa;AAChD,WAAO,OAAO,UAAU;AAAA,EAC1B,SAAS,GAAP;AACA,YAAQ,MAAM,0BAA0B,CAAC;AACzC,WAAO;AAAA,EACT;AACF;","names":["require","createRequire","require"]}
@@ -65,4 +65,4 @@ export {
65
65
  REQUEST_CONTEXT,
66
66
  useRequestContext
67
67
  };
68
- //# sourceMappingURL=chunk-WTSNW7BB.js.map
68
+ //# sourceMappingURL=chunk-HQXIHYDN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/components/Html.tsx","../src/core/hooks/useI18nContext.ts","../src/core/hooks/useRequestContext.ts"],"sourcesContent":["import {ComponentChildren, FunctionalComponent, createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport interface HtmlContext {\n htmlAttrs: preact.JSX.HTMLAttributes<HTMLHtmlElement>;\n headAttrs: preact.JSX.HTMLAttributes<HTMLHeadElement>;\n headComponents: ComponentChildren[];\n bodyAttrs: preact.JSX.HTMLAttributes<HTMLBodyElement>;\n scriptDeps: Array<preact.JSX.HTMLAttributes<HTMLScriptElement>>;\n}\n\nexport const HTML_CONTEXT = createContext<HtmlContext | null>(null);\n\nexport type HtmlProps = preact.JSX.HTMLAttributes<HTMLHtmlElement> & {\n children?: ComponentChildren;\n};\n\n/**\n * The `<Html>` component can be used to update attrs in the `<html>` tag.\n *\n * Usage:\n *\n * ```tsx\n * <Html lang=\"en-US\">\n * <h1>Hello world</h1>\n * </Html>\n * ```\n */\nexport const Html: FunctionalComponent<HtmlProps> = ({children, ...attrs}) => {\n const context = useContext(HTML_CONTEXT);\n if (!context) {\n throw new Error(\n 'HTML_CONTEXT not found, double-check usage of the <Html> component'\n );\n }\n context.htmlAttrs = attrs;\n return <>{children}</>;\n};\n","import path from 'node:path';\n\nimport {createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport const I18N_CONTEXT = createContext<I18nContext | null>(null);\n\nexport interface I18nContext {\n locale: string;\n translations: Record<string, string>;\n}\n\n/**\n * A hook that returns information about the current i18n context, including the\n * locale for the given route and a map of translations for that locale.\n */\nexport function useI18nContext() {\n const context = useContext(I18N_CONTEXT);\n if (!context) {\n throw new Error('I18N_CONTEXT not found');\n }\n return context;\n}\n\nexport function getTranslations(locale: string): Record<string, string> {\n const translations: Record<string, Record<string, string>> = {};\n const translationsFiles = import.meta.glob(['/translations/*.json'], {\n eager: true,\n }) as Record<string, {default?: Record<string, string>}>;\n Object.keys(translationsFiles).forEach((translationPath) => {\n const parts = path.parse(translationPath);\n const locale = parts.name;\n const module = translationsFiles[translationPath];\n if (module && module.default) {\n translations[locale] = module.default;\n }\n });\n return translations[locale] || {};\n}\n","import {createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nimport {Route} from '../types';\n\nexport interface RequestContext {\n /**\n * The current request path, e.g. `/foo/bar` (default route path) or\n * `/{locale}/foo/bar` (localized route path).\n */\n currentPath: string;\n /** The route file. */\n route: Route;\n /**\n * Route param values. E.g. for a route like `routes/blog/[slug].tsx`,\n * visiting `/blog/foo` will pass {slug: 'foo'} here.\n */\n routeParams: Record<string, string>;\n /** Props passed to the route's server component. */\n props: any;\n /** The current locale. */\n locale: string;\n /** Translations map for the current locale. */\n translations: Record<string, string>;\n}\n\nexport const REQUEST_CONTEXT = createContext<RequestContext | null>(null);\n\n/**\n * A hook that returns information about the current route.\n *\n * Usage:\n *\n * ```ts\n * const ctx = useRequestContext();\n * ctx.route.src;\n * // => 'routes/index.tsx'\n * ```\n */\nexport function useRequestContext() {\n const context = useContext(REQUEST_CONTEXT);\n if (!context) {\n throw new Error('REQUEST_CONTEXT not found');\n }\n return context;\n}\n"],"mappings":";AAAA,SAAgD,qBAAoB;AACpE,SAAQ,kBAAiB;AAmChB;AAzBF,IAAM,eAAe,cAAkC,IAAI;AAiB3D,IAAM,OAAuC,CAAC,EAAC,aAAa,MAAK,MAAM;AAC5E,QAAM,UAAU,WAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,UAAQ,YAAY;AACpB,SAAO;AAAA,IAAG;AAAA,GAAS;AACrB;;;ACrCA,OAAO,UAAU;AAEjB,SAAQ,iBAAAA,sBAAoB;AAC5B,SAAQ,cAAAC,mBAAiB;AAElB,IAAM,eAAeD,eAAkC,IAAI;AAW3D,SAAS,iBAAiB;AAC/B,QAAM,UAAUC,YAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,QAAwC;AACtE,QAAM,eAAuD,CAAC;AAC9D,QAAM,oBAAoB,YAAY,KAAK,CAAC,sBAAsB,GAAG;AAAA,IACnE,OAAO;AAAA,EACT,CAAC;AACD,SAAO,KAAK,iBAAiB,EAAE,QAAQ,CAAC,oBAAoB;AAC1D,UAAM,QAAQ,KAAK,MAAM,eAAe;AACxC,UAAMC,UAAS,MAAM;AACrB,UAAM,SAAS,kBAAkB;AACjC,QAAI,UAAU,OAAO,SAAS;AAC5B,mBAAaA,WAAU,OAAO;AAAA,IAChC;AAAA,EACF,CAAC;AACD,SAAO,aAAa,WAAW,CAAC;AAClC;;;ACtCA,SAAQ,iBAAAC,sBAAoB;AAC5B,SAAQ,cAAAC,mBAAiB;AAyBlB,IAAM,kBAAkBD,eAAqC,IAAI;AAajE,SAAS,oBAAoB;AAClC,QAAM,UAAUC,YAAW,eAAe;AAC1C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AACA,SAAO;AACT;","names":["createContext","useContext","locale","createContext","useContext"]}
@@ -28,4 +28,4 @@ export {
28
28
  configureServerPlugins,
29
29
  getVitePlugins
30
30
  };
31
- //# sourceMappingURL=chunk-DTEQ2AIW.js.map
31
+ //# sourceMappingURL=chunk-QKBMWK5B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/plugin.ts"],"sourcesContent":["import {PluginOption as VitePlugin} from 'vite';\n\nimport {RootConfig} from './config';\nimport {Server} from './types';\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport type ConfigureServerHook = (\n server: Server,\n options: ConfigureServerOptions\n) => MaybePromise<void> | MaybePromise<() => void>;\n\nexport interface ConfigureServerOptions {\n type: 'dev' | 'preview' | 'prod';\n rootConfig: RootConfig;\n}\n\nexport interface Plugin {\n [key: string]: any;\n /** The name of the plugin. */\n name?: string;\n /**\n * Configures the root.js express server. Any middleware defined by the plugin\n * will be added to the server first. If a callback fn is returned, it will\n * be called after the root.js middlewares are added.\n */\n configureServer?: ConfigureServerHook;\n /**\n * Returns a list of deps to bundle for ssr. The files will be bundled and\n * output to `dist/server/`. The return value should be a map of\n * `{output filename => input filepath}`.\n *\n * E.g. a value of `{foo: 'path/to/bar.js'}` will output `dist/server/foo.js`.\n *\n * @experimental This config is subject to change to be incorporated into a\n * broader config option called \"ssr\" or \"ssrOptions\".\n */\n ssrInput?: () => {[entryAlias: string]: string};\n /** Adds vite plugins. */\n vitePlugins?: VitePlugin[];\n}\n\n/**\n * Runs the pre-hook configureServer method of every plugin, calls a callback\n * function, and then runs the configureServer's post-hook if provided. Plugins\n * provide a post-hook by returning a callback function from configureServer.\n */\nexport async function configureServerPlugins(\n server: Server,\n callback: () => Promise<void>,\n plugins: Plugin[],\n options: ConfigureServerOptions\n) {\n const postHooks: Array<() => void> = [];\n\n // Call the `configureServer()` method for each plugin.\n for (const plugin of plugins) {\n if (plugin.configureServer) {\n const postHook = await plugin.configureServer(server, options);\n if (postHook) {\n postHooks.push(postHook);\n }\n }\n }\n\n // Register any built-in middleware.\n callback();\n\n // Run any post hooks returned by `plugin.configureServer()`.\n for (const postHook of postHooks) {\n await postHook();\n }\n}\n\nexport function getVitePlugins(plugins: Plugin[]): VitePlugin[] {\n const vitePlugins: VitePlugin[] = [];\n for (const plugin of plugins) {\n if (plugin.vitePlugins) {\n vitePlugins.push(...plugin.vitePlugins);\n }\n }\n return vitePlugins;\n}\n"],"mappings":";AA+CA,eAAsB,uBACpB,QACA,UACA,SACA,SACA;AACA,QAAM,YAA+B,CAAC;AAGtC,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,iBAAiB;AAC1B,YAAM,WAAW,MAAM,OAAO,gBAAgB,QAAQ,OAAO;AAC7D,UAAI,UAAU;AACZ,kBAAU,KAAK,QAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAGA,WAAS;AAGT,aAAW,YAAY,WAAW;AAChC,UAAM,SAAS;AAAA,EACjB;AACF;AAEO,SAAS,eAAe,SAAiC;AAC9D,QAAM,cAA4B,CAAC;AACnC,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,aAAa;AACtB,kBAAY,KAAK,GAAG,OAAO,WAAW;AAAA,IACxC;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getVitePlugins
3
- } from "./chunk-DTEQ2AIW.js";
3
+ } from "./chunk-QKBMWK5B.js";
4
4
 
5
5
  // src/node/load-config.ts
6
6
  import path2 from "node:path";
@@ -55,6 +55,17 @@ async function isDirectory(dirpath) {
55
55
  function fileExists(filepath) {
56
56
  return fs.access(filepath).then(() => true).catch(() => false);
57
57
  }
58
+ async function dirExists(dirpath) {
59
+ try {
60
+ const stat = await fs.stat(dirpath);
61
+ return stat.isDirectory();
62
+ } catch (error) {
63
+ if (error.code === "ENOENT") {
64
+ return false;
65
+ }
66
+ throw error;
67
+ }
68
+ }
58
69
  async function directoryContains(dirpath, subpath) {
59
70
  const outer = await fs.realpath(dirpath);
60
71
  const inner = await fs.realpath(subpath);
@@ -80,10 +91,9 @@ async function loadRootConfig(rootDir, options) {
80
91
  }
81
92
 
82
93
  // src/node/vite.ts
83
- import path3 from "node:path";
84
94
  import { createServer } from "vite";
85
95
  async function createViteServer(rootConfig, options) {
86
- var _a, _b;
96
+ var _a, _b, _c;
87
97
  const rootDir = rootConfig.rootDir;
88
98
  const viteConfig = rootConfig.vite || {};
89
99
  let hmrOptions = (_a = viteConfig.server) == null ? void 0 : _a.hmr;
@@ -96,7 +106,7 @@ async function createViteServer(rootConfig, options) {
96
106
  ...viteConfig,
97
107
  mode: "development",
98
108
  root: rootDir,
99
- publicDir: path3.join(rootDir, "public"),
109
+ publicDir: false,
100
110
  server: {
101
111
  ...viteConfig.server || {},
102
112
  middlewareMode: true,
@@ -108,7 +118,8 @@ async function createViteServer(rootConfig, options) {
108
118
  include: [
109
119
  ...(options == null ? void 0 : options.optimizeDeps) || [],
110
120
  ...((_b = viteConfig.optimizeDeps) == null ? void 0 : _b.include) || []
111
- ]
121
+ ],
122
+ extensions: [...((_c = viteConfig.optimizeDeps) == null ? void 0 : _c.extensions) || [], ".tsx"]
112
123
  },
113
124
  ssr: {
114
125
  ...viteConfig.ssr || {},
@@ -142,9 +153,10 @@ export {
142
153
  loadJson,
143
154
  isDirectory,
144
155
  fileExists,
156
+ dirExists,
145
157
  directoryContains,
146
158
  loadRootConfig,
147
159
  createViteServer,
148
160
  viteSsrLoadModule
149
161
  };
150
- //# sourceMappingURL=chunk-LTSJAEBG.js.map
162
+ //# sourceMappingURL=chunk-SDT4J6VY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/node/load-config.ts","../src/utils/fsutils.ts","../src/node/vite.ts"],"sourcesContent":["import path from 'node:path';\nimport {bundleRequire} from 'bundle-require';\nimport {RootConfig} from '../core/config';\nimport {fileExists} from '../utils/fsutils';\n\nexport interface ConfigOptions {\n command: string;\n}\n\nexport async function loadRootConfig(\n rootDir: string,\n options: ConfigOptions\n): Promise<RootConfig> {\n const configPath = path.resolve(rootDir, 'root.config.ts');\n const exists = await fileExists(configPath);\n if (!exists) {\n throw new Error(`${configPath} does not exist`);\n }\n const configBundle = await bundleRequire({\n filepath: configPath,\n });\n let config = configBundle.mod.default || {};\n if (typeof config === 'function') {\n config = (await config(options)) || {};\n }\n return Object.assign({}, config, {rootDir});\n}\n","import {promises as fs} from 'node:fs';\nimport path from 'node:path';\n\nimport fsExtra from 'fs-extra';\nimport glob from 'tiny-glob';\n\nexport function isJsFile(filename: string) {\n return !!filename.match(/\\.(j|t)sx?$/);\n}\n\nexport async function writeFile(filepath: string, content: string) {\n const dirPath = path.dirname(filepath);\n await makeDir(dirPath);\n await fs.writeFile(filepath, content);\n}\n\nexport async function makeDir(dirpath: string) {\n try {\n await fs.access(dirpath);\n } catch (e) {\n await fs.mkdir(dirpath, {recursive: true});\n }\n}\n\nexport async function copyDir(srcdir: string, dstdir: string) {\n if (!fsExtra.existsSync(srcdir)) {\n return;\n }\n fsExtra.copySync(srcdir, dstdir, {recursive: true, overwrite: true});\n}\n\n/**\n * Copies a glob of files from one directory to another.\n *\n * Example:\n *\n * ```\n * await copyGlob('*.css', 'src/styles', 'dist/html/styles');\n * ```\n */\nexport async function copyGlob(\n pattern: string,\n srcdir: string,\n dstdir: string\n) {\n const files = await glob(pattern, {cwd: srcdir});\n if (files.length > 0) {\n await makeDir(dstdir);\n }\n files.forEach((file) => {\n fsExtra.copySync(path.resolve(srcdir, file), path.resolve(dstdir, file));\n });\n}\n\nexport async function rmDir(dirpath: string) {\n await fs.rm(dirpath, {recursive: true, force: true});\n}\n\nexport async function loadJson<T = unknown>(filepath: string): Promise<T> {\n const content = await fs.readFile(filepath, 'utf-8');\n return JSON.parse(content);\n}\n\nexport async function isDirectory(dirpath: string) {\n return fs\n .stat(dirpath)\n .then((fsStat) => {\n return fsStat.isDirectory();\n })\n .catch((err) => {\n if (err.code === 'ENOENT') {\n return false;\n }\n throw err;\n });\n}\n\nexport function fileExists(filepath: string): Promise<boolean> {\n return fs\n .access(filepath)\n .then(() => true)\n .catch(() => false);\n}\n\nexport async function dirExists(dirpath: string): Promise<boolean> {\n try {\n const stat = await fs.stat(dirpath);\n return stat.isDirectory();\n } catch (error) {\n if (error.code === 'ENOENT') {\n return false;\n }\n throw error;\n }\n}\n\nexport async function directoryContains(\n dirpath: string,\n subpath: string\n): Promise<boolean> {\n const outer = await fs.realpath(dirpath);\n const inner = await fs.realpath(subpath);\n const rel = path.relative(outer, inner);\n return !rel.startsWith('..');\n}\n\nexport async function listFilesRecursive(dirPath: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await fs.readdir(dirPath, {withFileTypes: true});\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n if (entry.isDirectory()) {\n const subFiles = await listFilesRecursive(fullPath);\n files.push(...subFiles);\n } else {\n files.push(fullPath);\n }\n }\n return files;\n}\n","import path from 'node:path';\n\nimport {createServer, ViteDevServer} from 'vite';\n\nimport {RootConfig} from '../core/config.js';\nimport {getVitePlugins} from '../core/plugin.js';\n\nexport interface CreateViteServerOptions {\n /** Override HMR settings. */\n hmr?: boolean;\n /** The port the server will run on. */\n port?: number;\n /** List of files to include in the optimizeDeps.include config. */\n optimizeDeps?: string[];\n}\n\n/**\n * Returns a vite dev server.\n */\nexport async function createViteServer(\n rootConfig: RootConfig,\n options?: CreateViteServerOptions\n): Promise<ViteDevServer> {\n const rootDir = rootConfig.rootDir;\n const viteConfig = rootConfig.vite || {};\n\n let hmrOptions = viteConfig.server?.hmr;\n if (options?.hmr === false) {\n hmrOptions = false;\n } else if (typeof hmrOptions === 'undefined' && options?.port) {\n // Automatically set the HMR port to `port + 10`. This allows multiple\n // root.js dev servers to run without conflicts.\n hmrOptions = {port: options.port + 10};\n }\n\n const viteServer = await createServer({\n ...viteConfig,\n mode: 'development',\n root: rootDir,\n // publicDir is disabled from the vite dev server since it's handled by the\n // root dev server directly, which allows user middlewares to override\n // files in the public dir.\n publicDir: false,\n server: {\n ...(viteConfig.server || {}),\n middlewareMode: true,\n hmr: hmrOptions,\n },\n appType: 'custom',\n optimizeDeps: {\n ...(viteConfig.optimizeDeps || {}),\n include: [\n ...(options?.optimizeDeps || []),\n ...(viteConfig.optimizeDeps?.include || []),\n ],\n extensions: [...(viteConfig.optimizeDeps?.extensions || []), '.tsx'],\n },\n ssr: {\n ...(viteConfig.ssr || {}),\n noExternal: ['@blinkk/root'],\n },\n esbuild: {\n ...(viteConfig.esbuild || {}),\n jsx: 'automatic',\n jsxImportSource: 'preact',\n },\n plugins: [\n ...(viteConfig.plugins || []),\n ...getVitePlugins(rootConfig.plugins || []),\n ],\n });\n return viteServer;\n}\n\n/**\n * Shortcut `viteServer.ssrLoadModule()` without starting an actual dev server.\n */\nexport async function viteSsrLoadModule(\n rootConfig: RootConfig,\n file: string\n): Promise<Record<string, any>> {\n const viteServer = await createViteServer(rootConfig, {hmr: false});\n const module = await viteServer.ssrLoadModule(file);\n await viteServer.close();\n return module;\n}\n"],"mappings":"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","names":["path","path"]}