@blinkk/root 1.0.0-rc.9 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/render/render.tsx","../src/core/pages/ErrorPage.tsx","../src/core/pages/DevErrorPage.tsx","../src/core/pages/DevNotFoundPage.tsx","../src/render/accept-language.ts","../src/render/i18n-fallbacks.ts","../src/render/router.ts","../src/render/route-trie.ts"],"sourcesContent":["import {ComponentChildren, ComponentType} from 'preact';\nimport renderToString from 'preact-render-to-string';\n\nimport {HtmlContext, HTML_CONTEXT} from '../core/components/Html';\nimport {RootConfig} from '../core/config';\nimport {getTranslations, I18N_CONTEXT} from '../core/hooks/useI18nContext';\nimport {RequestContext, REQUEST_CONTEXT} from '../core/hooks/useRequestContext';\nimport {DevErrorPage} from '../core/pages/DevErrorPage';\nimport {DevNotFoundPage} from '../core/pages/DevNotFoundPage';\nimport {ErrorPage} from '../core/pages/ErrorPage';\nimport {\n Request,\n Response,\n NextFunction,\n HandlerContext,\n RouteParams,\n Route,\n HandlerRenderFn,\n HandlerRenderOptions,\n} from '../core/types';\nimport type {ElementGraph} from '../node/element-graph';\nimport {parseTagNames} from '../utils/elements';\n\nimport {AssetMap} from './asset-map/asset-map';\nimport {htmlMinify} from './html-minify';\nimport {htmlPretty} from './html-pretty';\nimport {getFallbackLocales} from './i18n-fallbacks';\nimport {RouteTrie} from './route-trie';\nimport {getRoutes, getAllPathsForRoute, replaceParams} from './router';\n\ninterface RenderHtmlOptions {\n /** Attrs passed to the <html> tag, e.g. `{lang: 'en'}`. */\n htmlAttrs?: preact.JSX.HTMLAttributes<HTMLHtmlElement>;\n /** Attrs passed to the <head> tag. */\n headAttrs?: preact.JSX.HTMLAttributes<HTMLHeadElement>;\n /** Child components for the <head> tag. */\n headComponents?: ComponentChildren[];\n /** Attrs passed to the <body> tag. */\n bodyAttrs?: preact.JSX.HTMLAttributes<HTMLBodyElement>;\n}\n\nexport class Renderer {\n private rootConfig: RootConfig;\n private routes: RouteTrie<Route>;\n private assetMap: AssetMap;\n private elementGraph: ElementGraph;\n\n constructor(\n rootConfig: RootConfig,\n options: {assetMap: AssetMap; elementGraph: ElementGraph}\n ) {\n this.rootConfig = rootConfig;\n this.routes = getRoutes(this.rootConfig);\n this.assetMap = options.assetMap;\n this.elementGraph = options.elementGraph;\n }\n\n async handle(req: Request, res: Response, next: NextFunction) {\n // TODO(stevenle): handle baseUrl config.\n const url = req.path.toLowerCase();\n const [route, routeParams] = this.routes.get(url);\n if (!route) {\n next();\n return;\n }\n if (route.locale) {\n routeParams.$locale = route.locale;\n }\n\n const fallbackLocales = route.isDefaultLocale\n ? getFallbackLocales(req)\n : [route.locale];\n const getPreferredLocale = (availableLocales: string[]) => {\n const lowerLocales = availableLocales.map((l) => l.toLowerCase());\n for (const fallbackLocale of fallbackLocales) {\n if (lowerLocales.includes(fallbackLocale.toLowerCase())) {\n return fallbackLocale;\n }\n }\n return req.rootConfig?.i18n?.defaultLocale || 'en';\n };\n\n const render404 = async () => {\n // Calling next() will allow the dev server or prod server handle the 404\n // page as appropriate for the env.\n next();\n };\n\n const render: HandlerRenderFn = async (\n props: any,\n options?: HandlerRenderOptions\n ) => {\n if (!route.module.default) {\n console.error(`no default component exported in route: ${route.src}`);\n render404();\n return;\n }\n const currentPath = req.path;\n const locale = options?.locale || route.locale;\n const translations = options?.translations;\n const output = await this.renderComponent(route.module.default, props, {\n currentPath,\n route,\n routeParams,\n locale,\n translations,\n });\n let html = output.html;\n if (this.rootConfig.prettyHtml) {\n html = await htmlPretty(html, this.rootConfig.prettyHtmlOptions);\n } else if (this.rootConfig.minifyHtml !== false) {\n html = await htmlMinify(html, this.rootConfig.minifyHtmlOptions);\n }\n if (req.viteServer) {\n html = await req.viteServer.transformIndexHtml(currentPath, html);\n }\n // Override the status code for 404 and 500 routes, which are defined at\n // routes/404.tsx and routes/500.tsx respectively.\n let statusCode = 200;\n if (route.src === 'routes/404.tsx') {\n statusCode = 404;\n } else if (route.src === 'routes/500.tsx') {\n statusCode = 500;\n }\n req.hooks.trigger('preRender');\n res.status(statusCode).set({'Content-Type': 'text/html'}).end(html);\n };\n\n if (route.module.handle) {\n const handlerContext: HandlerContext = {\n route: route,\n params: routeParams,\n i18nFallbackLocales: fallbackLocales,\n getPreferredLocale: getPreferredLocale,\n render: render,\n render404: render404,\n };\n req.handlerContext = handlerContext;\n return route.module.handle(req, res, next);\n }\n\n let props = {};\n if (route.module.getStaticProps) {\n const propsData = await route.module.getStaticProps({\n rootConfig: this.rootConfig,\n params: routeParams,\n });\n if (propsData.notFound) {\n return render404();\n }\n if (propsData.props) {\n props = propsData.props;\n }\n }\n await render(props);\n }\n\n private async renderComponent(\n Component: ComponentType,\n props: any,\n options: {\n currentPath: string;\n route: Route;\n routeParams: RouteParams;\n locale: string;\n translations?: Record<string, string>;\n }\n ) {\n const {currentPath, route, routeParams} = options;\n const locale = options.locale;\n const translations = {\n ...getTranslations(locale),\n ...(options.translations || {}),\n };\n const ctx: RequestContext = {\n currentPath,\n route,\n props,\n routeParams,\n locale,\n translations,\n };\n const htmlContext: HtmlContext = {\n htmlAttrs: {},\n headAttrs: {},\n headComponents: [],\n bodyAttrs: {},\n scriptDeps: [],\n };\n const vdom = (\n <REQUEST_CONTEXT.Provider value={ctx}>\n <I18N_CONTEXT.Provider value={{locale, translations}}>\n <HTML_CONTEXT.Provider value={htmlContext}>\n <Component {...props} />\n </HTML_CONTEXT.Provider>\n </I18N_CONTEXT.Provider>\n </REQUEST_CONTEXT.Provider>\n );\n const mainHtml = renderToString(vdom);\n\n const jsDeps = new Set<string>();\n const cssDeps = new Set<string>();\n\n // Walk the route's dependency tree for CSS dependencies that are added via\n // `import 'foo.scss'` or `import 'foo.module.scss'`.\n const routeAsset = await this.assetMap.get(route.src);\n if (routeAsset) {\n const routeCssDeps = await routeAsset.getCssDeps();\n routeCssDeps.forEach((dep) => {\n // Ignore ?inline css deps.\n if (dep.endsWith('?inline')) {\n return;\n }\n cssDeps.add(dep);\n });\n }\n\n // Parse the HTML for custom elements that are found within the project\n // and automatically inject the script deps for them.\n await this.collectElementDeps(mainHtml, jsDeps, cssDeps);\n\n // Add user defined scripts added via the `<Script>` component.\n await Promise.all(\n htmlContext.scriptDeps.map(async (scriptDep) => {\n if (!scriptDep.src) {\n return;\n }\n const assetId = String(scriptDep.src).slice(1);\n const scriptAsset = await this.assetMap.get(assetId);\n if (scriptAsset) {\n jsDeps.add(scriptAsset.assetUrl);\n const scriptJsDeps = await scriptAsset.getJsDeps();\n scriptJsDeps.forEach((dep) => jsDeps.add(dep));\n }\n })\n );\n\n const styleTags = Array.from(cssDeps).map((cssUrl) => {\n return <link rel=\"stylesheet\" href={cssUrl} />;\n });\n const scriptTags = Array.from(jsDeps).map((jsUrls) => {\n return <script type=\"module\" src={jsUrls} />;\n });\n\n const html = await this.renderHtml(mainHtml, {\n htmlAttrs: htmlContext.htmlAttrs,\n headAttrs: htmlContext.headAttrs,\n bodyAttrs: htmlContext.bodyAttrs,\n headComponents: [\n ...htmlContext.headComponents,\n ...styleTags,\n ...scriptTags,\n ],\n });\n return {html};\n }\n\n /** SSG renders a route. */\n async renderRoute(\n route: Route,\n options: {routeParams: Record<string, string>}\n ): Promise<{html?: string; notFound?: boolean}> {\n const routeParams = options.routeParams;\n if (route.locale) {\n routeParams.$locale = route.locale;\n }\n const Component = route.module.default;\n if (!Component) {\n throw new Error(\n 'unable to render route. the route should have a default export that renders a jsx component.'\n );\n }\n let props = {};\n let locale = route.locale;\n let translations = undefined;\n if (route.module.getStaticProps) {\n const propsData = await route.module.getStaticProps({\n rootConfig: this.rootConfig,\n params: routeParams,\n });\n if (propsData.notFound) {\n return {notFound: true};\n }\n if (propsData.props) {\n props = propsData.props;\n }\n if (propsData.locale) {\n locale = propsData.locale;\n }\n if (propsData.translations) {\n translations = propsData.translations;\n }\n }\n const routePath = route.isDefaultLocale\n ? route.routePath\n : route.localeRoutePath;\n const currentPath = replaceParams(routePath, {\n ...routeParams,\n locale: locale,\n });\n return this.renderComponent(Component, props, {\n currentPath,\n route,\n routeParams,\n locale,\n translations,\n });\n }\n\n async getSitemap(): Promise<\n Record<string, {route: Route; params: Record<string, string>}>\n > {\n const sitemap: Record<\n string,\n {route: Route; params: Record<string, string>}\n > = {};\n await this.routes.walk(async (urlPath: string, route: Route) => {\n const routePaths = await getAllPathsForRoute(urlPath, route);\n routePaths.forEach((routePath) => {\n sitemap[routePath.urlPath] = {\n route,\n params: routePath.params,\n };\n });\n });\n return sitemap;\n }\n\n private async renderHtml(html: string, options?: RenderHtmlOptions) {\n const htmlAttrs = options?.htmlAttrs || {};\n const headAttrs = options?.headAttrs || {};\n const bodyAttrs = options?.bodyAttrs || {};\n const page = (\n <html {...htmlAttrs}>\n <head {...headAttrs}>\n <meta charSet=\"utf-8\" />\n {options?.headComponents}\n </head>\n <body {...bodyAttrs} dangerouslySetInnerHTML={{__html: html}} />\n </html>\n );\n return `<!doctype html>\\n${renderToString(page)}\\n`;\n }\n\n async render404(options?: {currentPath?: string}) {\n const currentPath = options?.currentPath || '/404';\n const [route, routeParams] = this.routes.get('/404');\n if (route && route.src === 'routes/404.tsx' && route.module.default) {\n const Component = route.module.default;\n return this.renderComponent(\n Component,\n {},\n {currentPath, route, routeParams, locale: 'en'}\n );\n }\n\n const mainHtml = renderToString(\n <ErrorPage\n code={404}\n title=\"Not found\"\n message=\"Double-check the URL entered and try again.\"\n align=\"center\"\n />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [\n <title>404 Not Found</title>,\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1.0\"\n />,\n ],\n });\n return {html};\n }\n\n async renderError(err: any, options?: {currentPath?: string}) {\n const currentPath = options?.currentPath || '/500';\n const [route, routeParams] = this.routes.get('/500');\n if (route && route.src === 'routes/500.tsx' && route.module.default) {\n const Component = route.module.default;\n return this.renderComponent(\n Component,\n {error: err},\n {currentPath, route, routeParams, locale: 'en'}\n );\n }\n\n const mainHtml = renderToString(\n <ErrorPage\n code={500}\n title=\"Something went wrong\"\n message=\"An unknown error occurred.\"\n align=\"center\"\n />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [\n <title>500 Error</title>,\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1.0\"\n />,\n ],\n });\n return {html};\n }\n\n async renderDevServer404(req: Request) {\n const sitemap = await this.getSitemap();\n const mainHtml = renderToString(\n <DevNotFoundPage req={req} sitemap={sitemap} />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [<title>404 Not found | Root.js</title>],\n });\n return {html};\n }\n\n async renderDevServer500(req: Request, error: unknown) {\n const [route, routeParams] = this.routes.get(req.path);\n const mainHtml = renderToString(\n <DevErrorPage\n req={req}\n route={route}\n routeParams={routeParams}\n error={error}\n />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [<title>500 Error | Root.js</title>],\n });\n return {html};\n }\n\n /**\n * Parses rendered HTML for custom element tags used on the page and\n * automatically adds the JS/CSS deps to the page.\n */\n private async collectElementDeps(\n html: string,\n jsDeps: Set<string>,\n cssDeps: Set<string>\n ): Promise<{jsDeps: Set<string>; cssDeps: Set<string>}> {\n const elementsMap = this.elementGraph.sourceFiles;\n const assetMap = this.assetMap;\n\n const tagNames = new Set<string>();\n for (const tagName of parseTagNames(html)) {\n if (tagName && tagName in elementsMap) {\n tagNames.add(tagName);\n for (const depTagName of this.elementGraph.getDeps(tagName)) {\n tagNames.add(depTagName);\n }\n }\n }\n\n await Promise.all(\n Array.from(tagNames).map(async (tagName: string) => {\n const elementModule = elementsMap[tagName];\n const asset = await assetMap.get(elementModule.relPath);\n if (!asset) {\n return;\n }\n const assetJsDeps = await asset.getJsDeps();\n assetJsDeps.forEach((dep) => jsDeps.add(dep));\n const assetCssDeps = await asset.getCssDeps();\n assetCssDeps.forEach((dep) => {\n // Ignore ?inline css deps.\n if (dep.endsWith('?inline')) {\n return;\n }\n cssDeps.add(dep);\n });\n })\n );\n\n return {jsDeps, cssDeps};\n }\n}\n","import {ComponentChildren} from 'preact';\n\nconst STYLES = `\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');\n\n:root {\n --font-family-text: \"Inter\", sans-serif;\n}\n\nbody {\n font-family: var(--font-family-text);\n background: #F5F5F5;\n padding: 40px 16px;\n}\n\n.root {\n max-width: 1200px;\n margin: 0 auto;\n}\n\n.root.align-center {\n text-align: center;\n}\n\nh1.title {\n margin-top: 0;\n margin-bottom: 24px;\n}\n\np.message {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.box {\n font-size: 16px;\n line-height: 1.5;\n padding: 16px;\n border-radius: 12px;\n background: #ffffff;\n}\n\npre.box {\n white-space: pre-wrap;\n}\n\n@media (min-width: 500px) {\n body {\n padding: 40px;\n }\n\n .box {\n padding: 24px;\n }\n}\n\n@media (min-width: 1024px) {\n body {\n padding: 100px;\n }\n}\n`;\n\nexport interface ErrorPageProps {\n code: number;\n title?: string;\n message?: string;\n children?: ComponentChildren;\n align?: 'center';\n}\n\nexport function ErrorPage(props: ErrorPageProps) {\n const {code, message} = props;\n const title = props.title || code;\n return (\n <>\n <style dangerouslySetInnerHTML={{__html: STYLES}}></style>\n <div className={`root align-${props.align || 'left'}`}>\n <h1 className=\"title\">{title}</h1>\n {message && <p className=\"message\">{message}</p>}\n {props.children}\n </div>\n </>\n );\n}\n","import {Request, Route, RouteParams} from '../types';\n\nimport {ErrorPage} from './ErrorPage';\n\ninterface DevErrorPageProps {\n req: Request;\n route?: Route;\n routeParams?: RouteParams;\n error: any;\n}\n\nexport function DevErrorPage(props: DevErrorPageProps) {\n const req = props.req;\n const err = props.error;\n const route = props.route;\n const routeParams = props.routeParams;\n\n let errMsg = String(err);\n if (err && err.stack) {\n // Obfuscate some user info from the stack trace so that when people send\n // error reports and screenshots, less identifiable information is sent.\n errMsg = err.stack\n .replace(/\\(.*node_modules/g, '(node_modules')\n .replace(/at \\/.*node_modules/g, 'at node_modules');\n if (req.rootConfig?.rootDir) {\n errMsg = errMsg.replaceAll(req.rootConfig.rootDir, '<root>');\n }\n if (process.env.HOME) {\n errMsg = errMsg.replaceAll(process.env.HOME, '$HOME');\n }\n }\n return (\n <ErrorPage code={500} title=\"Something went wrong\">\n {errMsg && (\n <>\n <h2>Error</h2>\n <pre className=\"box\">\n <code>{errMsg}</code>\n </pre>\n </>\n )}\n <h2>Debug Info</h2>\n <pre className=\"box\">\n <code>{`url: ${req.originalUrl}\nroute: ${route?.src || 'null'}\nrouteParams: ${(routeParams && JSON.stringify(routeParams)) || 'null'}`}</code>\n </pre>\n </ErrorPage>\n );\n}\n","import {Request, Route} from '../types';\n\nimport {ErrorPage} from './ErrorPage';\n\ninterface DevNotFoundPageProps {\n req: Request;\n sitemap: Record<string, {route: Route; params: Record<string, string>}>;\n}\n\ninterface RoutesListMap {\n [src: string]: Array<RouteURLPath>;\n}\n\ninterface RouteURLPath {\n route: Route;\n urlPath: string;\n}\n\nexport function DevNotFoundPage(props: DevNotFoundPageProps) {\n const req = props.req;\n const routesListMap: RoutesListMap = {};\n let srcMaxLength = 0;\n Object.keys(props.sitemap).forEach((urlPath) => {\n const route = props.sitemap[urlPath].route;\n routesListMap[route.src] ??= [];\n routesListMap[route.src].push({route, urlPath});\n if (route.src.length > srcMaxLength) {\n srcMaxLength = route.src.length;\n }\n });\n const routeSrcs = Object.keys(routesListMap).sort(sortRouteFiles);\n const lines: string[] = [];\n routeSrcs.forEach((routeSrc) => {\n const routeUrls = routesListMap[routeSrc].sort(sortRouteURLs);\n routeUrls.forEach((routeUrl, i) => {\n const urlPath = routeUrl.urlPath;\n if (i === 0) {\n lines.push(`${routeSrc.padEnd(srcMaxLength, ' ')} => ${urlPath}`);\n } else {\n lines.push(`${''.padEnd(srcMaxLength, ' ')} => ${urlPath}`);\n }\n });\n });\n const routesListString = lines.join('\\n');\n // const routesListString = routesList\n // .map((route) => {\n // return `${route.urlPath.padEnd(srcMaxLength, ' ')} => ${route.src}`;\n // })\n // .join('\\n');\n return (\n <ErrorPage code={404} title=\"Not found\">\n <h2>Routes</h2>\n {Object.keys(routesListMap).length > 0 ? (\n <pre className=\"box\">\n <code>{routesListString}</code>\n </pre>\n ) : (\n <div className=\"box\">\n Add your first route at <code>/routes/index.tsx</code>\n </div>\n )}\n\n <h2>Debug Info</h2>\n <pre className=\"box\">\n <code>{`url: ${req.originalUrl}`}</code>\n </pre>\n </ErrorPage>\n );\n}\n\nfunction sortRouteFiles(a: string, b: string): number {\n if (a === 'routes/index.tsx') {\n return -1;\n }\n if (b === 'routes/index.tsx') {\n return 1;\n }\n return a.localeCompare(b);\n}\n\nfunction sortRouteURLs(a: RouteURLPath, b: RouteURLPath): number {\n if (a.route.isDefaultLocale && !b.route.isDefaultLocale) {\n return -1;\n }\n if (!a.route.isDefaultLocale && b.route.isDefaultLocale) {\n return 1;\n }\n return a.urlPath.localeCompare(b.urlPath);\n}\n","export const ACCEPT_LANG_RE =\n /((([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\\*)(;q=[0-1](\\.[0-9]+)?)?)*/g;\n\nexport interface AcceptLanguage {\n code: string;\n script?: string;\n region?: string;\n quality: number;\n}\n\nexport function parseAcceptLanguage(value: string): AcceptLanguage[] {\n const matches = String(value).match(ACCEPT_LANG_RE);\n if (!matches) {\n return [];\n }\n const results: AcceptLanguage[] = [];\n matches.forEach((m) => {\n if (!m) {\n return;\n }\n\n const parts = m.split(';');\n const ietf = parts[0].split('-');\n const hasScript = ietf.length === 3;\n\n results.push({\n code: ietf[0],\n script: hasScript ? ietf[1] : undefined,\n region: hasScript ? ietf[2] : ietf[1],\n quality: parts[1] ? parseFloat(parts[1].split('=')[1]) : 1.0,\n });\n });\n results.sort((a, b) => b.quality - a.quality);\n return results;\n}\n","/**\n * Utility functions for handling requests that mimic the Firebase Hosting i18n\n * fallback logic.\n * https://firebase.google.com/docs/hosting/i18n-rewrites\n */\n\nimport {Request} from '../core/types';\nimport {parseAcceptLanguage} from './accept-language';\n\nexport const UNKNOWN_COUNTRY = 'zz';\nexport const ES_419_COUNTRIES = [\n 'ar', // Argentina\n 'bo', // Bolivia\n 'cl', // Chile\n 'co', // Colombia\n 'cr', // Costa Rica\n 'cu', // Cuba\n 'do', // Dominican Republic\n 'ec', // Ecuador\n 'sv', // El Salvador\n 'gt', // Guatemala\n 'hn', // Honduras\n 'mx', // Mexico\n 'ni', // Nicaragua\n 'pa', // Panama\n 'py', // Paraguay\n 'pe', // Peru\n 'pr', // Puerto Rico\n 'uy', // Uruguay\n 've', // Venezuela\n];\n\nexport function getFallbackLocales(req: Request): string[] {\n const hl = getFirstQueryParam(req, 'hl');\n const countryCode = getCountry(req);\n\n // Web crawlers should only use the default locale.\n if (isWebCrawler(req)) {\n const defaultLocale = req.rootConfig?.i18n?.defaultLocale || 'en';\n if (hl && hl !== defaultLocale) {\n return [hl, defaultLocale];\n }\n return [defaultLocale];\n }\n\n const locales = new Set<string>();\n\n // Add locales from ?hl= query parameter.\n if (hl) {\n const langCode = hl;\n locales.add(`${langCode}_${countryCode}`);\n locales.add(`${langCode}_ALL`);\n locales.add(langCode);\n }\n\n const langs = getFallbackLanguages(req);\n\n // Add `{lang}_{country}` locales.\n langs.forEach((langCode) => {\n locales.add(`${langCode}_${countryCode}`);\n });\n\n // Add ALL_{country} locale.\n locales.add(`ALL_${countryCode}`);\n\n // Add `{lang}_ALL` and `{lang}` locales.\n // For Spanish-speaking LATAM countries, also add es-419.\n const isEs419Country = test419Country(countryCode);\n langs.forEach((langCode) => {\n locales.add(`${langCode}_ALL`);\n locales.add(langCode);\n if (langCode === 'es' && isEs419Country) {\n locales.add('es-419_ALL');\n locales.add('es-419');\n }\n });\n\n return Array.from(locales) as string[];\n}\n\nexport function getCountry(req: Request) {\n const normalize = (countryCode: string) => String(countryCode).toLowerCase();\n // Check the ?gl= query param.\n const gl = getFirstQueryParam(req, 'gl');\n if (gl) {\n return normalize(gl);\n }\n const gaeCountry =\n req.get('x-country-code') || req.get('x-appengine-country');\n if (gaeCountry) {\n return normalize(gaeCountry);\n }\n return UNKNOWN_COUNTRY;\n}\n\nfunction getFallbackLanguages(req: Request): string[] {\n const langs = new Set<string>();\n // Add languages from the Accept-Language header.\n const acceptLangHeader = req.get('accept-language') || '';\n if (acceptLangHeader) {\n parseAcceptLanguage(acceptLangHeader).forEach((lang) => {\n // For a lang like `en-US`, add both `en-US` and `en`.\n if (lang.region) {\n langs.add(`${lang.code}-${lang.region}`);\n // For Spanish-speaking LATAM countries, also add es-419.\n if (lang.code === 'es' && test419Country(lang.region)) {\n langs.add('es-419');\n }\n }\n langs.add(lang.code);\n });\n }\n // Fall back to \"en\" as a last resort.\n langs.add('en');\n return Array.from(langs);\n}\n\n/**\n * Returns the first query param value in a given request.\n *\n * For example, for a URL like `/?foo=bar&foo=baz`, calling\n * `getFirstQueryParam(req, 'foo')` would return `\"bar\"`.\n */\nfunction getFirstQueryParam(req: Request, key: string): string | null {\n const val = req.query[key];\n if (val === null || val === undefined) {\n return null;\n }\n if (Array.isArray(val)) {\n if (val.length === 0) {\n return null;\n }\n return String(val[0]);\n }\n return String(val);\n}\n\nfunction isWebCrawler(req: Request): boolean {\n const userAgentHeader = req.get('User-Agent');\n if (!userAgentHeader) {\n return false;\n }\n const userAgent = userAgentHeader.toLowerCase();\n return (\n userAgent.includes('googlebot') ||\n userAgent.includes('bingbot') ||\n userAgent.includes('twitterbot')\n );\n}\n\nexport function test419Country(countryCode: string) {\n return ES_419_COUNTRIES.includes(countryCode);\n}\n","import path from 'node:path';\n\nimport {RootConfig} from '../core/config';\nimport {Route, RouteModule} from '../core/types';\n\nimport {RouteTrie} from './route-trie';\n\nexport function getRoutes(rootConfig: RootConfig) {\n const locales = rootConfig.i18n?.locales || [];\n const basePath = rootConfig.base || '/';\n\n const defaultLocale = rootConfig.i18n?.defaultLocale || 'en';\n\n const routes = import.meta.glob(\n ['/routes/*.ts', '/routes/**/*.ts', '/routes/*.tsx', '/routes/**/*.tsx'],\n {\n eager: true,\n }\n );\n const trie = new RouteTrie<Route>();\n Object.keys(routes).forEach((modulePath) => {\n const src = modulePath.slice(1);\n let relativeRoutePath = modulePath.replace(/^\\/routes/, '');\n const parts = path.parse(relativeRoutePath);\n if (parts.name.startsWith('_')) {\n return;\n }\n if (parts.name === 'index') {\n relativeRoutePath = parts.dir;\n } else {\n relativeRoutePath = path.join(parts.dir, parts.name);\n }\n\n const urlFormat = '/[base]/[path]';\n const i18nUrlFormat = toSquareBrackets(\n rootConfig.i18n?.urlFormat || '/[locale]/[base]/[path]'\n );\n const placeholders = {\n base: removeSlashes(basePath),\n path: removeSlashes(relativeRoutePath),\n };\n\n const formatUrl = (format: string) => {\n const url = format\n .replaceAll('[base]', placeholders.base)\n .replaceAll('[path]', placeholders.path);\n return normalizeUrlPath(url, {\n trailingSlash: rootConfig.server?.trailingSlash,\n });\n };\n\n const routePath = formatUrl(urlFormat);\n const localeRoutePath = formatUrl(i18nUrlFormat);\n\n trie.add(routePath, {\n src,\n module: routes[modulePath] as RouteModule,\n locale: defaultLocale,\n isDefaultLocale: true,\n routePath: routePath,\n localeRoutePath: localeRoutePath,\n });\n\n // At the moment, all routes are assumed to use the site-wide i18n config.\n // TODO(stevenle): provide routes with a way to override the default\n // i18n serving behavior.\n if (i18nUrlFormat.includes('[locale]')) {\n locales.forEach((locale) => {\n const localePath = localeRoutePath.replace('[locale]', locale);\n if (localePath !== relativeRoutePath) {\n trie.add(localePath, {\n src,\n module: routes[modulePath] as RouteModule,\n locale: locale,\n isDefaultLocale: false,\n routePath,\n localeRoutePath,\n });\n }\n });\n }\n });\n return trie;\n}\n\nexport async function getAllPathsForRoute(\n urlPathFormat: string,\n route: Route\n): Promise<Array<{urlPath: string; params: Record<string, string>}>> {\n const routeModule = route.module;\n if (!routeModule.default) {\n return [];\n }\n\n const urlPaths: Array<{urlPath: string; params: Record<string, string>}> = [];\n if (routeModule.getStaticPaths) {\n const staticPaths = await routeModule.getStaticPaths();\n if (staticPaths.paths) {\n staticPaths.paths.forEach(\n (pathParams: {params: Record<string, string>}) => {\n const urlPath = replaceParams(urlPathFormat, pathParams.params || {});\n if (pathContainsPlaceholders(urlPath)) {\n console.warn(\n `path contains placeholders: ${urlPathFormat}, double check getStaticPaths() and ensure all params are returned. more info: https://rootjs.dev/guide/routes#getStaticPaths`\n );\n } else {\n urlPaths.push({\n urlPath: normalizeUrlPath(urlPath),\n params: pathParams.params || {},\n });\n }\n }\n );\n }\n } else if (\n routeModule.getStaticProps &&\n !pathContainsPlaceholders(urlPathFormat)\n ) {\n urlPaths.push({urlPath: normalizeUrlPath(urlPathFormat), params: {}});\n } else if (!routeModule.handle && !pathContainsPlaceholders(urlPathFormat)) {\n urlPaths.push({urlPath: normalizeUrlPath(urlPathFormat), params: {}});\n } else if (\n pathContainsPlaceholders(urlPathFormat) &&\n !routeModule.handle &&\n !routeModule.getStaticPaths\n ) {\n console.warn(\n [\n `warning: path contains placeholders: ${urlPathFormat}.`,\n `define either ssg getStaticPaths() or ssr handle() for route: ${route.src}.`,\n 'more info: https://rootjs.dev/guide/routes',\n ].join('\\n')\n );\n }\n\n return urlPaths;\n}\n\nexport function replaceParams(\n urlPathFormat: string,\n params: Record<string, string>\n) {\n const urlPath = urlPathFormat.replaceAll(\n /\\[\\[?(\\.\\.\\.)?([\\w\\-_]*)\\]?\\]/g,\n (match: string, _wildcard: string, key: string) => {\n const val = params[key];\n if (!val) {\n throw new Error(`unreplaced param ${match} in url: ${urlPathFormat}`);\n }\n return val;\n }\n );\n return urlPath;\n}\n\nexport function normalizeUrlPath(\n urlPath: string,\n options?: {trailingSlash?: boolean}\n) {\n // Collapse multiple slashes, e.g. `/foo//bar` => `/foo/bar`;\n urlPath = urlPath.replace(/\\/+/g, '/');\n // Remove trailing slash.\n if (\n options?.trailingSlash === false &&\n urlPath !== '/' &&\n urlPath.endsWith('/')\n ) {\n urlPath = urlPath.replace(/\\/*$/g, '');\n }\n // Add leading slash if needed.\n if (!urlPath.startsWith('/')) {\n urlPath = `/${urlPath}`;\n }\n return urlPath;\n}\n\nfunction pathContainsPlaceholders(urlPath: string) {\n const segments = urlPath.split('/');\n return segments.some((segment) => {\n return segment.startsWith('[') && segment.endsWith(']');\n });\n}\n\nfunction removeSlashes(str: string) {\n return str.replace(/^\\/*/g, '').replace(/\\/*$/g, '');\n}\n\n/**\n * Older path formats used `/{locale}/{path}` and should be converted to\n * `/[locale]/[base]/[path]`.\n */\nfunction toSquareBrackets(str: string) {\n if (str.includes('{') || str.includes('}')) {\n const val = str.replaceAll('{', '[').replaceAll('}', ']');\n console.warn(`\"${str}\" is a deprecated format, please switch to \"${val}\"`);\n return val;\n }\n return str;\n}\n","/**\n * A trie data structure that stores routes. Supports Next-style routing using\n * [param], [...catchall], and [[...optcatchall]] placeholders.\n */\nexport class RouteTrie<T> {\n private children: Record<string, RouteTrie<T>> = {};\n private paramNodes?: {[param: string]: ParamNode<T>};\n private catchAllNodes?: CatchAllNode<T>;\n private optCatchAllNodes?: CatchAllNode<T>;\n private route?: T;\n\n /**\n * Adds a route to the trie.\n */\n add(path: string, route: T) {\n path = this.normalizePath(path);\n\n // If the end was reached, save the value to the node.\n if (path === '') {\n this.route = route;\n return;\n }\n\n const [head, tail] = this.splitPath(path);\n\n if (head.startsWith('[[...') && head.endsWith(']]')) {\n const paramName = head.slice(5, -2);\n this.optCatchAllNodes = new CatchAllNode(paramName, route);\n return;\n }\n if (head.startsWith('[...') && head.endsWith(']')) {\n const paramName = head.slice(4, -1);\n this.catchAllNodes = new CatchAllNode(paramName, route);\n return;\n }\n\n let nextNode: RouteTrie<T>;\n if (head.startsWith('[') && head.endsWith(']')) {\n if (!this.paramNodes) {\n this.paramNodes = {};\n }\n const paramName = head.slice(1, -1);\n if (!this.paramNodes[paramName]) {\n this.paramNodes[paramName] = new ParamNode(paramName);\n }\n nextNode = this.paramNodes[paramName].trie;\n } else {\n nextNode = this.children[head];\n if (!nextNode) {\n nextNode = new RouteTrie();\n this.children[head] = nextNode;\n }\n }\n nextNode.add(tail, route);\n }\n\n /**\n * Returns a route mapped to the given path and any parameter values from the\n * URL.\n */\n get(path: string): [T | undefined, Record<string, string>] {\n const params = {};\n const route = this.getRoute(path, params);\n return [route, params];\n }\n\n /**\n * Walks the route trie and calls a callback function for each route.\n */\n walk(cb: (urlPath: string, route: T) => Promise<void> | void): Promise<void> {\n const promises: Array<Promise<void>> = [];\n const addPromise = (promise: Promise<void> | void) => {\n if (promise) {\n promises.push(promise);\n }\n };\n if (this.route) {\n addPromise(cb('/', this.route));\n }\n if (this.paramNodes) {\n Object.values(this.paramNodes).forEach((paramChild) => {\n const param = `[${paramChild.name}]`;\n paramChild.trie.walk((childPath: string, route: T) => {\n const paramUrlPath = `/${param}${childPath}`;\n addPromise(cb(paramUrlPath, route));\n });\n });\n }\n if (this.catchAllNodes) {\n const wildcardUrlPath = `/[...${this.catchAllNodes.name}]`;\n addPromise(cb(wildcardUrlPath, this.catchAllNodes.route));\n }\n if (this.optCatchAllNodes) {\n const wildcardUrlPath = `/[[...${this.optCatchAllNodes.name}]]`;\n addPromise(cb(wildcardUrlPath, this.optCatchAllNodes.route));\n }\n for (const subpath of Object.keys(this.children)) {\n const childTrie = this.children[subpath];\n childTrie.walk((childPath: string, childRoute: T) => {\n addPromise(cb(`/${subpath}${childPath}`, childRoute));\n });\n }\n return Promise.all(promises).then(() => {});\n }\n\n /**\n * Removes all routes from the trie.\n */\n clear() {\n this.children = {};\n this.paramNodes = undefined;\n this.catchAllNodes = undefined;\n this.optCatchAllNodes = undefined;\n this.route = undefined;\n }\n\n private getRoute(\n urlPath: string,\n params: Record<string, string>\n ): T | undefined {\n urlPath = this.normalizePath(urlPath);\n if (urlPath === '') {\n if (this.route) {\n return this.route;\n }\n if (this.optCatchAllNodes) {\n if (urlPath) {\n params[this.optCatchAllNodes.name] = urlPath;\n }\n return this.optCatchAllNodes.route;\n }\n return undefined;\n }\n\n const [head, tail] = this.splitPath(urlPath);\n\n const child = this.children[head];\n if (child) {\n const route = child.getRoute(tail, params);\n if (route) {\n return route;\n }\n }\n\n if (this.paramNodes) {\n for (const paramChild of Object.values(this.paramNodes)) {\n const route = paramChild.trie.getRoute(tail, params);\n if (route) {\n params[paramChild.name] = head;\n return route;\n }\n }\n }\n\n if (this.catchAllNodes) {\n params[this.catchAllNodes.name] = urlPath;\n return this.catchAllNodes.route;\n }\n\n if (this.optCatchAllNodes) {\n params[this.optCatchAllNodes.name] = urlPath;\n return this.optCatchAllNodes.route;\n }\n\n return undefined;\n }\n\n /**\n * Normalizes a path for inclusion into the route trie.\n */\n private normalizePath(path: string) {\n // Remove leading/trailing slashes.\n return path.replace(/^\\/+/g, '').replace(/\\/+$/g, '');\n }\n\n /**\n * Splits the parent directory from its children, e.g.:\n *\n * splitPath(\"foo/bar/baz\") -> [\"foo\", \"bar/baz\"]\n */\n private splitPath(path: string): [string, string] {\n const i = path.indexOf('/');\n if (i === -1) {\n return [path, ''];\n }\n return [path.slice(0, i), path.slice(i + 1)];\n }\n}\n\n/**\n * A node in the RouteTrie for a :param child.\n */\nclass ParamNode<T> {\n readonly name: string;\n readonly trie: RouteTrie<T> = new RouteTrie();\n\n constructor(name: string) {\n this.name = name;\n }\n}\n\n/**\n * A node in the RouteTrie for a *wildcard child.\n */\nclass CatchAllNode<T> {\n readonly name: string;\n readonly route: T;\n\n constructor(name: string, route: T) {\n this.name = name;\n this.route = route;\n }\n}\n"],"mappings":"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","names":["Fragment","jsx","jsxs","jsx","jsxs","path","_a","jsx","jsxs","props"]}
1
+ {"version":3,"sources":["../src/render/render.tsx","../src/core/pages/ErrorPage.tsx","../src/core/pages/DevErrorPage.tsx","../src/core/pages/DevNotFoundPage.tsx","../src/render/accept-language.ts","../src/render/i18n-fallbacks.ts","../src/render/router.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport {\n ComponentChildren,\n ComponentType,\n VNode,\n options as preactOptions,\n} from 'preact';\nimport renderToString from 'preact-render-to-string';\nimport {HtmlContext, HTML_CONTEXT} from '../core/components/Html';\nimport {RootConfig, RootSecurityConfig} from '../core/config';\nimport {getTranslations, I18N_CONTEXT} from '../core/hooks/useI18nContext';\nimport {RequestContext, REQUEST_CONTEXT} from '../core/hooks/useRequestContext';\nimport {DevErrorPage} from '../core/pages/DevErrorPage';\nimport {DevNotFoundPage} from '../core/pages/DevNotFoundPage';\nimport {ErrorPage} from '../core/pages/ErrorPage';\nimport {\n Request,\n Response,\n NextFunction,\n HandlerContext,\n RouteParams,\n Route,\n HandlerRenderFn,\n HandlerRenderOptions,\n} from '../core/types';\nimport type {ElementGraph} from '../node/element-graph';\nimport {parseTagNames} from '../utils/elements';\nimport {AssetMap} from './asset-map/asset-map';\nimport {htmlMinify} from './html-minify';\nimport {htmlPretty} from './html-pretty';\nimport {getFallbackLocales} from './i18n-fallbacks';\nimport {replaceParams, Router} from './router';\n\ninterface RenderHtmlOptions {\n /** Attrs passed to the <html> tag, e.g. `{lang: 'en'}`. */\n htmlAttrs?: preact.JSX.HTMLAttributes<HTMLHtmlElement>;\n /** Attrs passed to the <head> tag. */\n headAttrs?: preact.JSX.HTMLAttributes<HTMLHeadElement>;\n /** Child components for the <head> tag. */\n headComponents?: ComponentChildren[];\n /** Attrs passed to the <body> tag. */\n bodyAttrs?: preact.JSX.HTMLAttributes<HTMLBodyElement>;\n}\n\nexport class Renderer {\n private rootConfig: RootConfig;\n // private routes: RouteTrie<Route>;\n private assetMap: AssetMap;\n private elementGraph: ElementGraph;\n private router: Router;\n\n constructor(\n rootConfig: RootConfig,\n options: {assetMap: AssetMap; elementGraph: ElementGraph}\n ) {\n this.rootConfig = rootConfig;\n // this.routes = getRoutes(this.rootConfig);\n this.assetMap = options.assetMap;\n this.elementGraph = options.elementGraph;\n this.router = new Router(rootConfig);\n }\n\n async handle(req: Request, res: Response, next: NextFunction) {\n const url = req.path;\n const [route, routeParams] = this.router.get(url);\n if (!route) {\n next();\n return;\n }\n if (route.locale) {\n routeParams.$locale = route.locale;\n }\n\n const fallbackLocales = route.isDefaultLocale\n ? getFallbackLocales(req)\n : [route.locale];\n const getPreferredLocale = (availableLocales: string[]) => {\n const lowerLocales = availableLocales.map((l) => l.toLowerCase());\n for (const fallbackLocale of fallbackLocales) {\n if (lowerLocales.includes(fallbackLocale.toLowerCase())) {\n return fallbackLocale;\n }\n }\n return req.rootConfig?.i18n?.defaultLocale || 'en';\n };\n\n const render404 = async () => {\n // Calling next() will allow the dev server or prod server handle the 404\n // page as appropriate for the env.\n next();\n };\n\n const render: HandlerRenderFn = async (\n props: any,\n options?: HandlerRenderOptions\n ) => {\n if (!route.module.default) {\n console.error(`no default component exported in route: ${route.src}`);\n render404();\n return;\n }\n const securityConfig = this.getSecurityConfig();\n const cspEnabled = !!securityConfig.contentSecurityPolicy;\n const currentPath = req.path;\n const locale = options?.locale || route.locale;\n const translations = options?.translations;\n const nonce = cspEnabled ? this.generateNonce() : undefined;\n const output = await this.renderComponent(route.module.default, props, {\n currentPath,\n route,\n routeParams,\n locale,\n translations,\n nonce,\n });\n let html = output.html;\n if (this.rootConfig.prettyHtml) {\n html = await htmlPretty(html, this.rootConfig.prettyHtmlOptions);\n } else if (this.rootConfig.minifyHtml !== false) {\n html = await htmlMinify(html, this.rootConfig.minifyHtmlOptions);\n }\n if (req.viteServer) {\n html = await req.viteServer.transformIndexHtml(currentPath, html);\n if (nonce) {\n html = html.replace(\n '<script type=\"module\" src=\"/@vite/client\"></script>',\n `<script type=\"module\" src=\"/@vite/client\" nonce=\"${nonce}\"></script>`\n );\n }\n }\n // Override the status code for 404 and 500 routes, which are defined at\n // routes/404.tsx and routes/500.tsx respectively.\n let statusCode = 200;\n if (route.src === 'routes/404.tsx') {\n statusCode = 404;\n } else if (route.src === 'routes/500.tsx') {\n statusCode = 500;\n }\n req.hooks.trigger('preRender');\n res.status(statusCode);\n res.set({'Content-Type': 'text/html'});\n this.setSecurityHeaders(res, {\n securityConfig: securityConfig,\n nonce: nonce,\n });\n res.end(html);\n };\n\n if (route.module.handle) {\n const handlerContext: HandlerContext = {\n route: route,\n params: routeParams,\n i18nFallbackLocales: fallbackLocales,\n getPreferredLocale: getPreferredLocale,\n render: render,\n render404: render404,\n };\n req.handlerContext = handlerContext;\n return route.module.handle(req, res, next);\n }\n\n let props = {};\n if (route.module.getStaticProps) {\n const propsData = await route.module.getStaticProps({\n rootConfig: this.rootConfig,\n params: routeParams,\n });\n if (propsData.notFound) {\n return render404();\n }\n if (propsData.props) {\n props = propsData.props;\n }\n }\n await render(props);\n }\n\n private async renderComponent(\n Component: ComponentType,\n props: any,\n options: {\n currentPath: string;\n route: Route;\n routeParams: RouteParams;\n locale: string;\n translations?: Record<string, string>;\n nonce?: string;\n }\n ) {\n const {currentPath, route, routeParams, nonce} = options;\n const locale = options.locale;\n const translations = {\n ...getTranslations(locale),\n ...(options.translations || {}),\n };\n const ctx: RequestContext = {\n currentPath,\n route,\n props,\n routeParams,\n locale,\n translations,\n nonce,\n };\n const htmlContext: HtmlContext = {\n htmlAttrs: {},\n headAttrs: {},\n headComponents: [],\n bodyAttrs: {},\n scriptDeps: [],\n };\n const vdom = (\n <REQUEST_CONTEXT.Provider value={ctx}>\n <I18N_CONTEXT.Provider value={{locale, translations}}>\n <HTML_CONTEXT.Provider value={htmlContext}>\n <Component {...props} />\n </HTML_CONTEXT.Provider>\n </I18N_CONTEXT.Provider>\n </REQUEST_CONTEXT.Provider>\n );\n\n // Create a hook to auto-inject nonce values.\n // https://preactjs.com/guide/v10/options/\n const preactHook = preactOptions.vnode;\n let mainHtml: string;\n try {\n preactOptions.vnode = (vnode: VNode<any>) => {\n // Inject nonce to `<script>` tags.\n if (vnode && vnode.type === 'script') {\n vnode.props.nonce = nonce;\n }\n // Inject nonce to `<style>` tags.\n if (vnode && vnode.type === 'style') {\n vnode.props.nonce = nonce;\n }\n // Inject nonce to `<link rel=\"stylesheet\">` tags.\n if (\n vnode &&\n vnode.type === 'link' &&\n vnode.props.rel === 'stylesheet'\n ) {\n vnode.props.nonce = nonce;\n }\n // Call the normal preact hook.\n if (preactHook) {\n preactHook(vnode);\n }\n };\n mainHtml = renderToString(vdom);\n preactOptions.vnode = preactHook;\n } catch (err) {\n preactOptions.vnode = preactHook;\n throw err;\n }\n\n const jsDeps = new Set<string>();\n const cssDeps = new Set<string>();\n\n // Walk the route's dependency tree for CSS dependencies that are added via\n // `import 'foo.scss'` or `import 'foo.module.scss'`.\n const routeAsset = await this.assetMap.get(route.src);\n if (routeAsset) {\n const routeCssDeps = await routeAsset.getCssDeps();\n routeCssDeps.forEach((dep) => {\n // Ignore ?inline css deps.\n if (dep.endsWith('?inline')) {\n return;\n }\n cssDeps.add(dep);\n });\n }\n\n // Parse the HTML for custom elements that are found within the project\n // and automatically inject the script deps for them.\n await this.collectElementDeps(mainHtml, jsDeps, cssDeps);\n\n // Add user defined scripts added via the `<Script>` component.\n await Promise.all(\n htmlContext.scriptDeps.map(async (scriptDep) => {\n if (!scriptDep.src) {\n return;\n }\n const assetId = String(scriptDep.src).slice(1);\n const scriptAsset = await this.assetMap.get(assetId);\n if (scriptAsset) {\n jsDeps.add(scriptAsset.assetUrl);\n const scriptJsDeps = await scriptAsset.getJsDeps();\n scriptJsDeps.forEach((dep) => jsDeps.add(dep));\n }\n })\n );\n\n const styleTags = Array.from(cssDeps).map((cssUrl) => {\n return <link rel=\"stylesheet\" href={cssUrl} nonce={nonce} />;\n });\n const scriptTags = Array.from(jsDeps).map((jsUrls) => {\n return <script type=\"module\" src={jsUrls} nonce={nonce} />;\n });\n\n const html = await this.renderHtml(mainHtml, {\n htmlAttrs: htmlContext.htmlAttrs,\n headAttrs: htmlContext.headAttrs,\n bodyAttrs: htmlContext.bodyAttrs,\n headComponents: [\n ...htmlContext.headComponents,\n ...styleTags,\n ...scriptTags,\n ],\n });\n return {html};\n }\n\n /** SSG renders a route. */\n async renderRoute(\n route: Route,\n options: {routeParams: Record<string, string>}\n ): Promise<{html?: string; notFound?: boolean}> {\n const routeParams = options.routeParams;\n if (route.locale) {\n routeParams.$locale = route.locale;\n }\n const Component = route.module.default;\n if (!Component) {\n throw new Error(\n 'unable to render route. the route should have a default export that renders a jsx component.'\n );\n }\n let props = {};\n let locale = route.locale;\n let translations = undefined;\n if (route.module.getStaticProps) {\n const propsData = await route.module.getStaticProps({\n rootConfig: this.rootConfig,\n params: routeParams,\n });\n if (propsData.notFound) {\n return {notFound: true};\n }\n if (propsData.props) {\n props = propsData.props;\n }\n if (propsData.locale) {\n locale = propsData.locale;\n }\n if (propsData.translations) {\n translations = propsData.translations;\n }\n }\n const routePath = route.isDefaultLocale\n ? route.routePath\n : route.localeRoutePath;\n const currentPath = replaceParams(routePath, {\n ...routeParams,\n locale: locale,\n });\n return this.renderComponent(Component, props, {\n currentPath,\n route,\n routeParams,\n locale,\n translations,\n });\n }\n\n async getSitemap(): Promise<\n Record<string, {route: Route; params: Record<string, string>}>\n > {\n const sitemap: Record<\n string,\n {route: Route; params: Record<string, string>}\n > = {};\n await this.router.walk(async (urlPath: string, route: Route) => {\n const routePaths = await this.router.getAllPathsForRoute(urlPath, route);\n routePaths.forEach((routePath) => {\n sitemap[routePath.urlPath] = {\n route,\n params: routePath.params,\n };\n });\n });\n return sitemap;\n }\n\n private async renderHtml(html: string, options?: RenderHtmlOptions) {\n const htmlAttrs = options?.htmlAttrs || {};\n const headAttrs = options?.headAttrs || {};\n const bodyAttrs = options?.bodyAttrs || {};\n const page = (\n <html {...htmlAttrs}>\n <head {...headAttrs}>\n <meta charSet=\"utf-8\" />\n {options?.headComponents}\n </head>\n <body {...bodyAttrs} dangerouslySetInnerHTML={{__html: html}} />\n </html>\n );\n return `<!doctype html>\\n${renderToString(page)}\\n`;\n }\n\n async render404(options?: {currentPath?: string}) {\n const currentPath = options?.currentPath || '/404';\n const [route, routeParams] = this.router.get('/404');\n if (route && route.src === 'routes/404.tsx' && route.module.default) {\n const Component = route.module.default;\n return this.renderComponent(\n Component,\n {},\n {currentPath, route, routeParams, locale: 'en'}\n );\n }\n\n const mainHtml = renderToString(\n <ErrorPage\n code={404}\n title=\"Not found\"\n message=\"Double-check the URL entered and try again.\"\n align=\"center\"\n />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [\n <title>404 Not Found</title>,\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1.0\"\n />,\n ],\n });\n return {html};\n }\n\n async renderError(err: any, options?: {currentPath?: string}) {\n const currentPath = options?.currentPath || '/500';\n const [route, routeParams] = this.router.get('/500');\n if (route && route.src === 'routes/500.tsx' && route.module.default) {\n const Component = route.module.default;\n return this.renderComponent(\n Component,\n {error: err},\n {currentPath, route, routeParams, locale: 'en'}\n );\n }\n\n const mainHtml = renderToString(\n <ErrorPage\n code={500}\n title=\"Something went wrong\"\n message=\"An unknown error occurred.\"\n align=\"center\"\n />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [\n <title>500 Error</title>,\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1.0\"\n />,\n ],\n });\n return {html};\n }\n\n async renderDevServer404(req: Request) {\n const sitemap = await this.getSitemap();\n const mainHtml = renderToString(\n <DevNotFoundPage req={req} sitemap={sitemap} />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [<title>404 Not found | Root.js</title>],\n });\n return {html};\n }\n\n async renderDevServer500(req: Request, error: unknown) {\n const [route, routeParams] = this.router.get(req.path);\n const mainHtml = renderToString(\n <DevErrorPage\n req={req}\n route={route}\n routeParams={routeParams}\n error={error}\n />\n );\n const html = await this.renderHtml(mainHtml, {\n headComponents: [<title>500 Error | Root.js</title>],\n });\n return {html};\n }\n\n /**\n * Parses rendered HTML for custom element tags used on the page and\n * automatically adds the JS/CSS deps to the page.\n */\n private async collectElementDeps(\n html: string,\n jsDeps: Set<string>,\n cssDeps: Set<string>\n ): Promise<{jsDeps: Set<string>; cssDeps: Set<string>}> {\n const elementsMap = this.elementGraph.sourceFiles;\n const assetMap = this.assetMap;\n\n const tagNames = new Set<string>();\n for (const tagName of parseTagNames(html)) {\n if (tagName && tagName in elementsMap) {\n tagNames.add(tagName);\n for (const depTagName of this.elementGraph.getDeps(tagName)) {\n tagNames.add(depTagName);\n }\n }\n }\n\n await Promise.all(\n Array.from(tagNames).map(async (tagName: string) => {\n const elementModule = elementsMap[tagName];\n const asset = await assetMap.get(elementModule.relPath);\n if (!asset) {\n return;\n }\n const assetJsDeps = await asset.getJsDeps();\n assetJsDeps.forEach((dep) => jsDeps.add(dep));\n const assetCssDeps = await asset.getCssDeps();\n assetCssDeps.forEach((dep) => {\n // Ignore ?inline css deps.\n if (dep.endsWith('?inline')) {\n return;\n }\n cssDeps.add(dep);\n });\n })\n );\n\n return {jsDeps, cssDeps};\n }\n\n /**\n * Returns the `security` config value with default values inserted wherever\n * a user config value is blank or set to `true`.\n */\n private getSecurityConfig() {\n const userConfig: Partial<RootSecurityConfig> =\n this.rootConfig.server?.security || {};\n const securityConfig: Partial<RootSecurityConfig> = {};\n\n if (isTrueOrUndefined(userConfig.contentSecurityPolicy)) {\n // CSP default values from:\n // https://csp.withgoogle.com/docs/strict-csp.html\n securityConfig.contentSecurityPolicy = {\n directives: {\n 'base-uri': [\"'none'\"],\n 'object-src': [\"'none'\"],\n // NOTE: nonce is automatically added to this list.\n 'script-src': [\n \"'unsafe-inline'\",\n \"'unsafe-eval'\",\n \"'strict-dynamic' https: http:\",\n ],\n },\n reportOnly: true,\n };\n } else {\n securityConfig.contentSecurityPolicy = userConfig.contentSecurityPolicy;\n }\n\n if (isTrueOrUndefined(userConfig.xFrameOptions)) {\n securityConfig.xFrameOptions = 'SAMEORIGIN';\n } else {\n securityConfig.xFrameOptions = userConfig.xFrameOptions;\n }\n\n securityConfig.strictTransportSecurity =\n userConfig.strictTransportSecurity ?? true;\n securityConfig.xContentTypeOptions = userConfig.xContentTypeOptions ?? true;\n securityConfig.xXssProtection = userConfig.xXssProtection ?? true;\n\n return securityConfig as Required<RootSecurityConfig>;\n }\n\n /**\n * Generates a random string that can be used as the \"nonce\" value for CSP.\n */\n private generateNonce() {\n return crypto.randomBytes(16).toString('base64');\n }\n\n /**\n * Sets security-related HTTP headers.\n */\n private setSecurityHeaders(\n res: Response,\n options: {securityConfig: Required<RootSecurityConfig>; nonce?: string}\n ) {\n const securityConfig = options.securityConfig;\n\n // Content-Security-Policy.\n const contentSecurityPolicy = securityConfig.contentSecurityPolicy;\n if (typeof contentSecurityPolicy === 'object') {\n const directives = contentSecurityPolicy.directives || {};\n if (options.nonce) {\n if (!directives['script-src']) {\n directives['script-src'] = [\n \"'unsafe-inline'\",\n \"'unsafe-eval'\",\n \"'strict-dynamic' https: http:\",\n ];\n }\n directives['script-src'].push(`'nonce-${options.nonce}'`);\n }\n const headerSegments: string[] = [];\n Object.entries(directives).forEach(([key, values]) => {\n headerSegments.push([key, ...values].join(' '));\n });\n const csp = headerSegments.join('; ');\n if (contentSecurityPolicy.reportOnly === false) {\n res.setHeader('content-security-policy', csp);\n } else {\n res.setHeader('content-security-policy-report-only', csp);\n }\n }\n\n // X-Frame-Options.\n if (typeof securityConfig.xFrameOptions === 'string') {\n res.setHeader('x-frame-options', securityConfig.xFrameOptions);\n }\n\n // Strict-Transport-Security.\n if (securityConfig.strictTransportSecurity) {\n res.setHeader(\n 'strict-transport-security',\n 'max-age=63072000; includeSubdomains; preload'\n );\n }\n\n // X-Content-Type-Options.\n if (securityConfig.xContentTypeOptions) {\n res.setHeader('x-content-type-options', 'nosniff');\n }\n\n // X-XSS-Protection.\n if (securityConfig.xXssProtection) {\n res.setHeader('x-xss-protection', '1; mode=block');\n }\n }\n}\n\nfunction isTrueOrUndefined(value: any) {\n return value === true || value === undefined;\n}\n","import {ComponentChildren} from 'preact';\n\nconst STYLES = `\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');\n\n:root {\n --font-family-text: \"Inter\", sans-serif;\n}\n\nbody {\n font-family: var(--font-family-text);\n background: #F5F5F5;\n padding: 40px 16px;\n}\n\n.root {\n max-width: 1200px;\n margin: 0 auto;\n}\n\n.root.align-center {\n text-align: center;\n}\n\nh1.title {\n margin-top: 0;\n margin-bottom: 24px;\n}\n\np.message {\n margin-top: 0;\n margin-bottom: 0;\n}\n\n.box {\n font-size: 16px;\n line-height: 1.5;\n padding: 16px;\n border-radius: 12px;\n background: #ffffff;\n}\n\npre.box {\n white-space: pre-wrap;\n}\n\n@media (min-width: 500px) {\n body {\n padding: 40px;\n }\n\n .box {\n padding: 24px;\n }\n}\n\n@media (min-width: 1024px) {\n body {\n padding: 100px;\n }\n}\n`;\n\nexport interface ErrorPageProps {\n code: number;\n title?: string;\n message?: string;\n children?: ComponentChildren;\n align?: 'center';\n}\n\nexport function ErrorPage(props: ErrorPageProps) {\n const {code, message} = props;\n const title = props.title || code;\n return (\n <>\n <style dangerouslySetInnerHTML={{__html: STYLES}}></style>\n <div className={`root align-${props.align || 'left'}`}>\n <h1 className=\"title\">{title}</h1>\n {message && <p className=\"message\">{message}</p>}\n {props.children}\n </div>\n </>\n );\n}\n","import {Request, Route, RouteParams} from '../types';\n\nimport {ErrorPage} from './ErrorPage';\n\ninterface DevErrorPageProps {\n req: Request;\n route?: Route;\n routeParams?: RouteParams;\n error: any;\n}\n\nexport function DevErrorPage(props: DevErrorPageProps) {\n const req = props.req;\n const err = props.error;\n const route = props.route;\n const routeParams = props.routeParams;\n\n let errMsg = String(err);\n if (err && err.stack) {\n // Obfuscate some user info from the stack trace so that when people send\n // error reports and screenshots, less identifiable information is sent.\n errMsg = err.stack\n .replace(/\\(.*node_modules/g, '(node_modules')\n .replace(/at \\/.*node_modules/g, 'at node_modules');\n if (req.rootConfig?.rootDir) {\n errMsg = errMsg.replaceAll(req.rootConfig.rootDir, '<root>');\n }\n if (process.env.HOME) {\n errMsg = errMsg.replaceAll(process.env.HOME, '$HOME');\n }\n }\n return (\n <ErrorPage code={500} title=\"Something went wrong\">\n {errMsg && (\n <>\n <h2>Error</h2>\n <pre className=\"box\">\n <code>{errMsg}</code>\n </pre>\n </>\n )}\n <h2>Debug Info</h2>\n <pre className=\"box\">\n <code>{`url: ${req.originalUrl}\nroute: ${route?.src || 'null'}\nrouteParams: ${(routeParams && JSON.stringify(routeParams)) || 'null'}`}</code>\n </pre>\n </ErrorPage>\n );\n}\n","import {Request, Route} from '../types';\n\nimport {ErrorPage} from './ErrorPage';\n\ninterface DevNotFoundPageProps {\n req: Request;\n sitemap: Record<string, {route: Route; params: Record<string, string>}>;\n}\n\ninterface RoutesListMap {\n [src: string]: Array<RouteURLPath>;\n}\n\ninterface RouteURLPath {\n route: Route;\n urlPath: string;\n}\n\nexport function DevNotFoundPage(props: DevNotFoundPageProps) {\n const req = props.req;\n const routesListMap: RoutesListMap = {};\n let srcMaxLength = 0;\n Object.keys(props.sitemap).forEach((urlPath) => {\n const route = props.sitemap[urlPath].route;\n routesListMap[route.src] ??= [];\n routesListMap[route.src].push({route, urlPath});\n if (route.src.length > srcMaxLength) {\n srcMaxLength = route.src.length;\n }\n });\n const routeSrcs = Object.keys(routesListMap).sort(sortRouteFiles);\n const lines: string[] = [];\n routeSrcs.forEach((routeSrc) => {\n const routeUrls = routesListMap[routeSrc].sort(sortRouteURLs);\n routeUrls.forEach((routeUrl, i) => {\n const urlPath = routeUrl.urlPath;\n if (i === 0) {\n lines.push(`${routeSrc.padEnd(srcMaxLength, ' ')} => ${urlPath}`);\n } else {\n lines.push(`${''.padEnd(srcMaxLength, ' ')} => ${urlPath}`);\n }\n });\n });\n const routesListString = lines.join('\\n');\n // const routesListString = routesList\n // .map((route) => {\n // return `${route.urlPath.padEnd(srcMaxLength, ' ')} => ${route.src}`;\n // })\n // .join('\\n');\n return (\n <ErrorPage code={404} title=\"Not found\">\n <h2>Routes</h2>\n {Object.keys(routesListMap).length > 0 ? (\n <pre className=\"box\">\n <code>{routesListString}</code>\n </pre>\n ) : (\n <div className=\"box\">\n Add your first route at <code>/routes/index.tsx</code>\n </div>\n )}\n\n <h2>Debug Info</h2>\n <pre className=\"box\">\n <code>{`url: ${req.originalUrl}`}</code>\n </pre>\n </ErrorPage>\n );\n}\n\nfunction sortRouteFiles(a: string, b: string): number {\n if (a === 'routes/index.tsx') {\n return -1;\n }\n if (b === 'routes/index.tsx') {\n return 1;\n }\n return a.localeCompare(b);\n}\n\nfunction sortRouteURLs(a: RouteURLPath, b: RouteURLPath): number {\n if (a.route.isDefaultLocale && !b.route.isDefaultLocale) {\n return -1;\n }\n if (!a.route.isDefaultLocale && b.route.isDefaultLocale) {\n return 1;\n }\n return a.urlPath.localeCompare(b.urlPath);\n}\n","export const ACCEPT_LANG_RE =\n /((([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\\*)(;q=[0-1](\\.[0-9]+)?)?)*/g;\n\nexport interface AcceptLanguage {\n code: string;\n script?: string;\n region?: string;\n quality: number;\n}\n\nexport function parseAcceptLanguage(value: string): AcceptLanguage[] {\n const matches = String(value).match(ACCEPT_LANG_RE);\n if (!matches) {\n return [];\n }\n const results: AcceptLanguage[] = [];\n matches.forEach((m) => {\n if (!m) {\n return;\n }\n\n const parts = m.split(';');\n const ietf = parts[0].split('-');\n const hasScript = ietf.length === 3;\n\n results.push({\n code: ietf[0],\n script: hasScript ? ietf[1] : undefined,\n region: hasScript ? ietf[2] : ietf[1],\n quality: parts[1] ? parseFloat(parts[1].split('=')[1]) : 1.0,\n });\n });\n results.sort((a, b) => b.quality - a.quality);\n return results;\n}\n","/**\n * Utility functions for handling requests that mimic the Firebase Hosting i18n\n * fallback logic.\n * https://firebase.google.com/docs/hosting/i18n-rewrites\n */\n\nimport {Request} from '../core/types';\nimport {parseAcceptLanguage} from './accept-language';\n\nexport const UNKNOWN_COUNTRY = 'zz';\nexport const ES_419_COUNTRIES = [\n 'ar', // Argentina\n 'bo', // Bolivia\n 'cl', // Chile\n 'co', // Colombia\n 'cr', // Costa Rica\n 'cu', // Cuba\n 'do', // Dominican Republic\n 'ec', // Ecuador\n 'sv', // El Salvador\n 'gt', // Guatemala\n 'hn', // Honduras\n 'mx', // Mexico\n 'ni', // Nicaragua\n 'pa', // Panama\n 'py', // Paraguay\n 'pe', // Peru\n 'pr', // Puerto Rico\n 'uy', // Uruguay\n 've', // Venezuela\n];\n\nexport function getFallbackLocales(req: Request): string[] {\n const hl = getFirstQueryParam(req, 'hl');\n const countryCode = getCountry(req);\n\n // Web crawlers should only use the default locale.\n if (isWebCrawler(req)) {\n const defaultLocale = req.rootConfig?.i18n?.defaultLocale || 'en';\n if (hl && hl !== defaultLocale) {\n return [hl, defaultLocale];\n }\n return [defaultLocale];\n }\n\n const locales = new Set<string>();\n\n // Add locales from ?hl= query parameter.\n if (hl) {\n const langCode = hl;\n locales.add(`${langCode}_${countryCode}`);\n locales.add(`${langCode}_ALL`);\n locales.add(langCode);\n }\n\n const langs = getFallbackLanguages(req);\n\n // Add `{lang}_{country}` locales.\n langs.forEach((langCode) => {\n locales.add(`${langCode}_${countryCode}`);\n });\n\n // Add ALL_{country} locale.\n locales.add(`ALL_${countryCode}`);\n\n // Add `{lang}_ALL` and `{lang}` locales.\n const isEs419Country = test419Country(countryCode);\n langs.forEach((langCode) => {\n // For Spanish-speaking LATAM countries, also add es-419.\n if (langCode === 'es' && isEs419Country) {\n locales.add('es-419_ALL');\n locales.add('es-419');\n }\n locales.add(`${langCode}_ALL`);\n locales.add(langCode);\n });\n\n return Array.from(locales) as string[];\n}\n\nexport function getCountry(req: Request) {\n const normalize = (countryCode: string) => String(countryCode).toLowerCase();\n // Check the ?gl= query param.\n const gl = getFirstQueryParam(req, 'gl');\n if (gl) {\n return normalize(gl);\n }\n const gaeCountry =\n req.get('x-country-code') || req.get('x-appengine-country');\n if (gaeCountry) {\n return normalize(gaeCountry);\n }\n return UNKNOWN_COUNTRY;\n}\n\nfunction getFallbackLanguages(req: Request): string[] {\n const langs = new Set<string>();\n // Add languages from the Accept-Language header.\n const acceptLangHeader = req.get('accept-language') || '';\n if (acceptLangHeader) {\n parseAcceptLanguage(acceptLangHeader).forEach((lang) => {\n // For a lang like `en-US`, add both `en-US` and `en`.\n if (lang.region) {\n langs.add(`${lang.code}-${lang.region}`);\n // For Spanish-speaking LATAM countries, also add es-419.\n if (lang.code === 'es' && test419Country(lang.region)) {\n langs.add('es-419');\n }\n }\n langs.add(lang.code);\n });\n }\n // Fall back to \"en\" as a last resort.\n langs.add('en');\n return Array.from(langs);\n}\n\n/**\n * Returns the first query param value in a given request.\n *\n * For example, for a URL like `/?foo=bar&foo=baz`, calling\n * `getFirstQueryParam(req, 'foo')` would return `\"bar\"`.\n */\nfunction getFirstQueryParam(req: Request, key: string): string | null {\n const val = req.query[key];\n if (val === null || val === undefined) {\n return null;\n }\n if (Array.isArray(val)) {\n if (val.length === 0) {\n return null;\n }\n return String(val[0]);\n }\n return String(val);\n}\n\nfunction isWebCrawler(req: Request): boolean {\n const userAgentHeader = req.get('User-Agent');\n if (!userAgentHeader) {\n return false;\n }\n const userAgent = userAgentHeader.toLowerCase();\n return (\n userAgent.includes('googlebot') ||\n userAgent.includes('bingbot') ||\n userAgent.includes('twitterbot')\n );\n}\n\nexport function test419Country(countryCode: string) {\n return ES_419_COUNTRIES.includes(countryCode);\n}\n","import path from 'node:path';\nimport {RootConfig} from '../core/config';\nimport {Route, RouteModule} from '../core/types';\nimport {RouteTrie} from './route-trie';\n\nconst ROUTES_FILES = import.meta.glob<RouteModule>(\n ['/routes/*.ts', '/routes/**/*.ts', '/routes/*.tsx', '/routes/**/*.tsx'],\n {eager: true}\n);\n\nexport class Router {\n private rootConfig: RootConfig;\n private routeTrie: RouteTrie<Route>;\n\n constructor(rootConfig: RootConfig) {\n this.rootConfig = rootConfig;\n this.routeTrie = this.initRouteTrie();\n }\n\n get(url: string) {\n return this.routeTrie.get(url);\n }\n\n async walk(cb: (urlPath: string, route: Route) => void | Promise<void>) {\n await this.routeTrie.walk(cb);\n }\n\n private initRouteTrie() {\n const locales = this.rootConfig.i18n?.locales || [];\n const basePath = this.rootConfig.base || '/';\n const defaultLocale = this.rootConfig.i18n?.defaultLocale || 'en';\n\n const trie = new RouteTrie<Route>();\n Object.keys(ROUTES_FILES).forEach((modulePath) => {\n const src = modulePath.slice(1);\n let relativeRoutePath = modulePath.replace(/^\\/routes/, '');\n const parts = path.parse(relativeRoutePath);\n if (parts.name.startsWith('_')) {\n return;\n }\n if (parts.name === 'index') {\n relativeRoutePath = parts.dir;\n } else {\n relativeRoutePath = path.join(parts.dir, parts.name);\n }\n\n const urlFormat = '/[base]/[path]';\n const i18nUrlFormat = toSquareBrackets(\n this.rootConfig.i18n?.urlFormat || '/[locale]/[base]/[path]'\n );\n const placeholders = {\n base: removeSlashes(basePath),\n path: removeSlashes(relativeRoutePath),\n };\n\n const formatUrl = (format: string) => {\n const url = format\n .replaceAll('[base]', placeholders.base)\n .replaceAll('[path]', placeholders.path);\n return normalizeUrlPath(url, {\n trailingSlash: this.rootConfig.server?.trailingSlash,\n });\n };\n\n const routePath = formatUrl(urlFormat);\n const localeRoutePath = formatUrl(i18nUrlFormat);\n\n trie.add(routePath, {\n src,\n module: ROUTES_FILES[modulePath],\n locale: defaultLocale,\n isDefaultLocale: true,\n routePath: routePath,\n localeRoutePath: localeRoutePath,\n });\n\n // At the moment, all routes are assumed to use the site-wide i18n config.\n // TODO(stevenle): provide routes with a way to override the default\n // i18n serving behavior.\n if (i18nUrlFormat.includes('[locale]')) {\n locales.forEach((locale) => {\n const localePath = localeRoutePath.replace('[locale]', locale);\n if (localePath !== relativeRoutePath) {\n trie.add(localePath, {\n src,\n module: ROUTES_FILES[modulePath],\n locale: locale,\n isDefaultLocale: false,\n routePath,\n localeRoutePath,\n });\n }\n });\n }\n });\n return trie;\n }\n\n async getAllPathsForRoute(\n urlPathFormat: string,\n route: Route\n ): Promise<Array<{urlPath: string; params: Record<string, string>}>> {\n const routeModule = route.module;\n if (!routeModule.default) {\n return [];\n }\n\n const urlPaths: Array<{urlPath: string; params: Record<string, string>}> =\n [];\n if (routeModule.getStaticPaths) {\n const staticPaths = await routeModule.getStaticPaths({\n rootConfig: this.rootConfig,\n });\n if (staticPaths.paths) {\n staticPaths.paths.forEach(\n (pathParams: {params: Record<string, string>}) => {\n const urlPath = replaceParams(\n urlPathFormat,\n pathParams.params || {}\n );\n if (pathContainsPlaceholders(urlPath)) {\n console.warn(\n `path contains placeholders: ${urlPathFormat}, double check getStaticPaths() and ensure all params are returned. more info: https://rootjs.dev/guide/routes#getStaticPaths`\n );\n } else {\n urlPaths.push({\n urlPath: normalizeUrlPath(urlPath),\n params: pathParams.params || {},\n });\n }\n }\n );\n }\n } else if (\n routeModule.getStaticProps &&\n !pathContainsPlaceholders(urlPathFormat)\n ) {\n urlPaths.push({urlPath: normalizeUrlPath(urlPathFormat), params: {}});\n } else if (\n !routeModule.handle &&\n !pathContainsPlaceholders(urlPathFormat)\n ) {\n urlPaths.push({urlPath: normalizeUrlPath(urlPathFormat), params: {}});\n } else if (\n pathContainsPlaceholders(urlPathFormat) &&\n !routeModule.handle &&\n !routeModule.getStaticPaths\n ) {\n console.warn(\n [\n `warning: path contains placeholders: ${urlPathFormat}.`,\n `define either ssg getStaticPaths() or ssr handle() for route: ${route.src}.`,\n 'more info: https://rootjs.dev/guide/routes',\n ].join('\\n')\n );\n }\n\n return urlPaths;\n }\n}\n\nexport function replaceParams(\n urlPathFormat: string,\n params: Record<string, string>\n) {\n const urlPath = urlPathFormat.replaceAll(\n /\\[\\[?(\\.\\.\\.)?([\\w\\-_]*)\\]?\\]/g,\n (match: string, _wildcard: string, key: string) => {\n const val = params[key];\n if (!val) {\n throw new Error(`unreplaced param ${match} in url: ${urlPathFormat}`);\n }\n return val;\n }\n );\n return urlPath;\n}\n\nexport function normalizeUrlPath(\n urlPath: string,\n options?: {trailingSlash?: boolean}\n) {\n // Collapse multiple slashes, e.g. `/foo//bar` => `/foo/bar`;\n urlPath = urlPath.replace(/\\/+/g, '/');\n // Remove trailing slash.\n if (\n options?.trailingSlash === false &&\n urlPath !== '/' &&\n urlPath.endsWith('/')\n ) {\n urlPath = urlPath.replace(/\\/*$/g, '');\n }\n // Convert `/index` to `/`.\n if (urlPath.endsWith('/index')) {\n urlPath = urlPath.slice(0, -6);\n }\n // Add leading slash if needed.\n if (!urlPath.startsWith('/')) {\n urlPath = `/${urlPath}`;\n }\n return urlPath;\n}\n\nfunction pathContainsPlaceholders(urlPath: string) {\n const segments = urlPath.split('/');\n return segments.some((segment) => {\n return segment.startsWith('[') && segment.endsWith(']');\n });\n}\n\nfunction removeSlashes(str: string) {\n return str.replace(/^\\/*/g, '').replace(/\\/*$/g, '');\n}\n\n/**\n * Older path formats used `/{locale}/{path}` and should be converted to\n * `/[locale]/[base]/[path]`.\n */\nfunction toSquareBrackets(str: string) {\n if (str.includes('{') || str.includes('}')) {\n const val = str.replaceAll('{', '[').replaceAll('}', ']');\n console.warn(`\"${str}\" is a deprecated format, please switch to \"${val}\"`);\n return val;\n }\n return str;\n}\n"],"mappings":"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","names":["Fragment","jsx","jsxs","jsx","jsxs","jsx","jsxs","props"]}
@@ -167,6 +167,58 @@ interface RootI18nConfig {
167
167
  */
168
168
  groups?: Record<string, LocaleGroup>;
169
169
  }
170
+ interface RootRedirectConfig {
171
+ source: string;
172
+ destination: string;
173
+ type?: number;
174
+ }
175
+ interface RootHeaderConfig {
176
+ /** A glob pattern match (regex not supported yet). */
177
+ source: string;
178
+ headers: Array<{
179
+ key: string;
180
+ value: string;
181
+ }>;
182
+ }
183
+ interface ContentSecurityPolicyConfig {
184
+ directives?: Record<string, string[]>;
185
+ reportOnly?: boolean;
186
+ }
187
+ interface XFrameOptionsConfig {
188
+ action: 'DENY' | 'SAMEORIGIN';
189
+ }
190
+ interface RootSecurityConfig {
191
+ /**
192
+ * Content-Security-Policy config. If enabled, a nonce is auto-generated
193
+ * for every request and appended to script and stylesheet tags. You can
194
+ * validate your CSP headers using a tool like {@link https://csp-evaluator.withgoogle.com/}.
195
+ *
196
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP}
197
+ */
198
+ contentSecurityPolicy?: ContentSecurityPolicyConfig | boolean;
199
+ /**
200
+ * Strict-Transport-Security config. When enabled, the header value is set
201
+ * to `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload`.
202
+ */
203
+ strictTransportSecurity?: boolean;
204
+ /**
205
+ * X-Content-Type-Options config. When enabled, the header value is set to
206
+ * `X-Content-Type-Options: nosniff`.
207
+ */
208
+ xContentTypeOptions?: boolean;
209
+ /**
210
+ * X-Frame-Options config. Setting this value to `true` will default the
211
+ * header value to `X-Frame-Options: SAMEORIGIN`.
212
+ *
213
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options}
214
+ */
215
+ xFrameOptions?: 'DENY' | 'SAMEORIGIN' | boolean;
216
+ /**
217
+ * X-XSS-Protection config. When enabled, the header value is set to
218
+ * `X-XSS-Protection: 1; mode=block`.
219
+ */
220
+ xXssProtection?: boolean;
221
+ }
170
222
  interface RootServerConfig {
171
223
  /**
172
224
  * An array of middleware to add to the express server. These middleware are
@@ -187,6 +239,19 @@ interface RootServerConfig {
187
239
  * Cookie secret for the session middleware.
188
240
  */
189
241
  sessionCookieSecret?: string | string[];
242
+ /**
243
+ * List of redirects.
244
+ */
245
+ redirects?: RootRedirectConfig[];
246
+ /**
247
+ * HTTP headers to add to a response.
248
+ */
249
+ headers?: RootHeaderConfig[];
250
+ /**
251
+ * HTTP security settings. By default, all security settings are enabled with
252
+ * commonly used default values.
253
+ */
254
+ security?: RootSecurityConfig;
190
255
  }
191
256
  declare function defineConfig(config: RootUserConfig): RootUserConfig;
192
257
 
@@ -259,9 +324,9 @@ interface AssetMap {
259
324
 
260
325
  declare class Renderer {
261
326
  private rootConfig;
262
- private routes;
263
327
  private assetMap;
264
328
  private elementGraph;
329
+ private router;
265
330
  constructor(rootConfig: RootConfig, options: {
266
331
  assetMap: AssetMap;
267
332
  elementGraph: ElementGraph;
@@ -301,6 +366,19 @@ declare class Renderer {
301
366
  * automatically adds the JS/CSS deps to the page.
302
367
  */
303
368
  private collectElementDeps;
369
+ /**
370
+ * Returns the `security` config value with default values inserted wherever
371
+ * a user config value is blank or set to `true`.
372
+ */
373
+ private getSecurityConfig;
374
+ /**
375
+ * Generates a random string that can be used as the "nonce" value for CSP.
376
+ */
377
+ private generateNonce;
378
+ /**
379
+ * Sets security-related HTTP headers.
380
+ */
381
+ private setSecurityHeaders;
304
382
  }
305
383
 
306
384
  /**
@@ -332,10 +410,12 @@ type GetStaticProps<T = unknown> = (ctx: {
332
410
  }>;
333
411
  /**
334
412
  * The `getStaticPaths()` is used by the SSG build to determine all of the
335
- * paths that should exist for a given route. This should be used alongside a
413
+ * paths that exist for a given route. This should be used alongside a
336
414
  * parameterized route, e.g. `/routes/blog/[slug].tsx`.
337
415
  */
338
- type GetStaticPaths<T = RouteParams> = () => Promise<{
416
+ type GetStaticPaths<T = RouteParams> = (ctx: {
417
+ rootConfig: RootConfig;
418
+ }) => Promise<{
339
419
  paths: Array<{
340
420
  params: T;
341
421
  }>;
@@ -364,6 +444,10 @@ type Request = Request$1 & {
364
444
  viteServer?: ViteDevServer;
365
445
  /** The root.js renderer, to render routes within middleware. */
366
446
  renderer?: Renderer;
447
+ /** Logged in user for the request. */
448
+ user?: {
449
+ email: string;
450
+ };
367
451
  /**
368
452
  * Handler context, provided to route files that export a custom `handler()`
369
453
  * function.
@@ -390,7 +474,7 @@ type NextFunction = NextFunction$1;
390
474
  * A context variable passed to a route's `handle()` method within the req
391
475
  * object.
392
476
  */
393
- interface HandlerContext<T = any> {
477
+ interface HandlerContext<Props = any> {
394
478
  /**
395
479
  * The resolved route.
396
480
  */
@@ -412,7 +496,7 @@ interface HandlerContext<T = any> {
412
496
  */
413
497
  getPreferredLocale: (availableLocales: string[]) => string;
414
498
  /** Renders the default exported component from the route. */
415
- render: HandlerRenderFn;
499
+ render: HandlerRenderFn<Props>;
416
500
  /** Renders a 404 page. */
417
501
  render404: () => Promise<void>;
418
502
  }
@@ -469,6 +553,6 @@ interface HandlerRenderOptions {
469
553
  */
470
554
  translations?: Record<string, string>;
471
555
  }
472
- type HandlerRenderFn = (props: any, options?: HandlerRenderOptions) => Promise<void>;
556
+ type HandlerRenderFn<Props = any> = (props: Props, options?: HandlerRenderOptions) => Promise<void>;
473
557
 
474
- export { ConfigureServerHook as C, GetStaticProps as G, HandlerContext as H, LocaleGroup as L, MultipartFile as M, NextFunction as N, Plugin as P, Route as R, Server as S, RootUserConfig as a, RootConfig as b, RootI18nConfig as c, RootServerConfig as d, defineConfig as e, ConfigureServerOptions as f, configureServerPlugins as g, getVitePlugins as h, RouteParams as i, GetStaticPaths as j, RequestMiddleware as k, Request as l, Response as m, Handler as n, RouteModule as o, HandlerRenderOptions as p, HandlerRenderFn as q, SESSION_COOKIE as r, SessionMiddlewareOptions as s, SaveSessionOptions as t, sessionMiddleware as u, Session as v, Renderer as w };
558
+ export { Renderer as A, type ContentSecurityPolicyConfig as C, type GetStaticProps as G, type HandlerContext as H, type LocaleGroup as L, type MultipartFile as M, type NextFunction as N, type Plugin as P, type Route as R, type Server as S, type XFrameOptionsConfig as X, type RootUserConfig as a, type RootConfig as b, type RootI18nConfig as c, type RootRedirectConfig as d, type RootHeaderConfig as e, type RootSecurityConfig as f, type RootServerConfig as g, defineConfig as h, type ConfigureServerHook as i, type ConfigureServerOptions as j, configureServerPlugins as k, getVitePlugins as l, type RouteParams as m, type GetStaticPaths as n, type RequestMiddleware as o, type Request as p, type Response as q, type Handler as r, type RouteModule as s, type HandlerRenderOptions as t, type HandlerRenderFn as u, SESSION_COOKIE as v, type SessionMiddlewareOptions as w, type SaveSessionOptions as x, sessionMiddleware as y, Session as z };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@blinkk/root",
3
- "version": "1.0.0-rc.9",
3
+ "version": "1.0.1",
4
4
  "author": "s@blinkk.com",
5
5
  "license": "MIT",
6
6
  "engines": {
7
- "node": ">=16.0.0"
7
+ "node": ">=18.0.0"
8
8
  },
9
9
  "repository": {
10
10
  "type": "git",
@@ -54,21 +54,24 @@
54
54
  }
55
55
  },
56
56
  "dependencies": {
57
- "bundle-require": "^4.0.2",
58
- "busboy": "^1.6.0",
59
- "commander": "^11.0.0",
60
- "compression": "^1.7.4",
61
- "cookie-parser": "^1.4.6",
62
- "esbuild": "0.19.4",
63
- "express": "^4.18.2",
64
- "fs-extra": "^11.1.1",
65
- "html-minifier-terser": "^7.2.0",
66
- "js-beautify": "^1.14.9",
67
- "kleur": "^4.1.5",
68
- "sass": "^1.69.3",
69
- "sirv": "^2.0.3",
70
- "tiny-glob": "^0.2.9",
71
- "vite": "4.5.0"
57
+ "@types/micromatch": "4.0.6",
58
+ "bundle-require": "4.0.2",
59
+ "busboy": "1.6.0",
60
+ "commander": "11.0.0",
61
+ "compression": "1.7.4",
62
+ "cookie-parser": "1.4.6",
63
+ "dotenv": "16.4.5",
64
+ "esbuild": "0.19.9",
65
+ "express": "4.18.2",
66
+ "fs-extra": "11.1.1",
67
+ "html-minifier-terser": "7.2.0",
68
+ "js-beautify": "1.14.9",
69
+ "kleur": "4.1.5",
70
+ "micromatch": "4.0.5",
71
+ "sass": "1.69.3",
72
+ "sirv": "2.0.3",
73
+ "tiny-glob": "0.2.9",
74
+ "vite": "5.0.8"
72
75
  },
73
76
  "peerDependencies": {
74
77
  "firebase-admin": ">=11",
@@ -85,24 +88,24 @@
85
88
  }
86
89
  },
87
90
  "devDependencies": {
88
- "@types/busboy": "^1.5.0",
89
- "@types/compression": "^1.7.2",
90
- "@types/cookie-parser": "^1.4.3",
91
- "@types/express": "^4.17.13",
92
- "@types/fs-extra": "^11.0.2",
93
- "@types/html-minifier-terser": "^7.0.0",
94
- "@types/js-beautify": "^1.14.1",
95
- "@types/node": "^20.8.4",
96
- "@types/preact-custom-element": "^4.0.2",
97
- "firebase-admin": "^11.11.0",
98
- "firebase-functions": "^4.4.1",
99
- "nodemon": "^3.0.1",
100
- "preact": "^10.18.1",
101
- "preact-custom-element": "^4.3.0",
102
- "preact-render-to-string": "^6.2.2",
103
- "tsup": "^7.2.0",
104
- "typescript": "^5.2.2",
105
- "vitest": "^0.34.6"
91
+ "@types/busboy": "1.5.0",
92
+ "@types/compression": "1.7.2",
93
+ "@types/cookie-parser": "1.4.3",
94
+ "@types/express": "4.17.13",
95
+ "@types/fs-extra": "11.0.2",
96
+ "@types/html-minifier-terser": "7.0.0",
97
+ "@types/js-beautify": "1.14.1",
98
+ "@types/node": "20.8.4",
99
+ "@types/preact-custom-element": "4.0.2",
100
+ "firebase-admin": "11.11.0",
101
+ "firebase-functions": "4.8.0",
102
+ "nodemon": "3.0.1",
103
+ "preact": "10.19.3",
104
+ "preact-custom-element": "4.3.0",
105
+ "preact-render-to-string": "6.3.1",
106
+ "tsup": "8.0.1",
107
+ "typescript": "5.2.2",
108
+ "vitest": "0.34.6"
106
109
  },
107
110
  "scripts": {
108
111
  "build": "rm -rf dist && tsup-node",
@@ -1,61 +0,0 @@
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
-
17
- // src/render/html-minify.ts
18
- import { createRequire } from "module";
19
- var require2 = createRequire(import.meta.url);
20
- var { minify } = require2("html-minifier-terser");
21
- async function htmlMinify(html, options) {
22
- const minifyOptions = options || {
23
- collapseWhitespace: true,
24
- removeComments: true,
25
- preserveLineBreaks: true
26
- };
27
- try {
28
- const min = await minify(html, minifyOptions);
29
- return min.trimStart();
30
- } catch (e) {
31
- console.error("failed to minify html:", e);
32
- return html;
33
- }
34
- }
35
-
36
- // src/render/html-pretty.ts
37
- import { createRequire as createRequire2 } from "module";
38
- var require3 = createRequire2(import.meta.url);
39
- var beautify = require3("js-beautify");
40
- async function htmlPretty(html, options) {
41
- const prettyOptions = options || {
42
- indent_size: 0,
43
- end_with_newline: true,
44
- extra_liners: []
45
- };
46
- try {
47
- const output = beautify.html(html, prettyOptions);
48
- return output.trimStart();
49
- } catch (e) {
50
- console.error("failed to pretty html:", e);
51
- return html;
52
- }
53
- }
54
-
55
- export {
56
- isValidTagName,
57
- parseTagNames,
58
- htmlMinify,
59
- htmlPretty
60
- };
61
- //# sourceMappingURL=chunk-DFBTOMQF.js.map
@@ -1 +0,0 @@
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,CAAC;AACvB,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,GAAG;AACV,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,GAAG;AACV,YAAQ,MAAM,0BAA0B,CAAC;AACzC,WAAO;AAAA,EACT;AACF;","names":["require","createRequire","require"]}