@blinkk/root 3.0.1-alpha.0 → 3.0.1-beta.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/node/monorepo.ts","../src/utils/fsutils.ts","../src/node/load-config.ts","../src/node/pod-collector.ts","../src/node/vite.ts","../src/node/pods-vite-plugin.ts","../src/node/vite-plugin-root-jsx-virtual.ts"],"sourcesContent":["import path from 'node:path';\nimport {getWorkspaces, getWorkspaceRoot, PackageInfo} from 'workspace-tools';\nimport {fileExistsSync, loadJsonSync} from '../utils/fsutils.js';\n\ninterface WorkspacePackage {\n name: string;\n path: string;\n packageJson: PackageInfo;\n}\n\nexport function loadPackageJson(filepath: string): PackageInfo | null {\n if (!fileExistsSync(filepath)) {\n return null;\n }\n return loadJsonSync(filepath);\n}\n\n/**\n * Returns a map of all packages in the monorepo and the corresponding\n * package.json path.\n */\nfunction getMonorepoPackages(\n rootDir: string\n): Record<string, WorkspacePackage> {\n const monorepoRoot = getWorkspaceRoot(rootDir);\n if (!monorepoRoot) {\n return {};\n }\n\n const workspaces = getWorkspaces(monorepoRoot);\n const packages: Record<string, WorkspacePackage> = {};\n workspaces.forEach((workspaceInfo) => {\n packages[workspaceInfo.name] = workspaceInfo;\n });\n return packages;\n}\n\n/**\n * Returns the top-level monorepo package's deps, if any.\n */\nexport function getMonorepoPackageDeps(\n rootDir: string\n): Record<string, string> {\n const monorepoRoot = getWorkspaceRoot(rootDir);\n if (!monorepoRoot) {\n return {};\n }\n\n const packageJsonPath = path.join(monorepoRoot, 'package.json');\n const packageJson = loadPackageJson(packageJsonPath);\n return packageJson?.dependencies || {};\n}\n\n/**\n * Flattens package.json deps from the root project dir, taking into account any\n * deps from the monorepo root as well as any `workspace:` deps from within the\n * monorepo.\n */\nexport function flattenPackageDepsFromMonorepo(\n rootDir: string,\n options?: {ignore?: Set<string>}\n): Record<string, string> {\n const packageJsonPath = path.resolve(rootDir, 'package.json');\n const packageJson = loadPackageJson(packageJsonPath);\n const monorepoDeps = getMonorepoPackageDeps(rootDir);\n\n // Flatten `peerDependencies` and `dependencies`.\n const projectDeps = {\n ...packageJson?.peerDependencies,\n ...packageJson?.dependencies,\n };\n\n const allDeps: Record<string, string> = {};\n const workspacePackages = getMonorepoPackages(rootDir);\n const ignore = options?.ignore || new Set();\n Object.entries(projectDeps).forEach(([depName, depVersion]) => {\n if (\n depName.startsWith('@blinkk/root') &&\n depVersion.startsWith('workspace:')\n ) {\n const packageInfo = workspacePackages[depName];\n if (packageInfo) {\n allDeps[depName] = packageInfo.packageJson.version;\n }\n } else if (depVersion.startsWith('workspace:')) {\n // For internal packages within the workspace, recursively collect the\n // deps from those packages.\n if (ignore.has(depName)) {\n return;\n }\n ignore.add(depName);\n const packageInfo = workspacePackages[depName];\n if (packageInfo) {\n const workspacePackageDir = packageInfo.path;\n const deps = flattenPackageDepsFromMonorepo(workspacePackageDir, {\n ignore: ignore,\n });\n for (const key in deps) {\n const currentValue = allDeps[key];\n if (\n deps[key] &&\n deps[key] !== '*' &&\n (!currentValue || currentValue === '*')\n ) {\n allDeps[key] = deps[key];\n }\n }\n }\n } else if (depVersion === '*' && monorepoDeps[depName]) {\n // For any dependencies using a wildcard version `*`, if the top-level\n // package.json has the depdenency defined, overwrite the version.\n allDeps[depName] = monorepoDeps[depName];\n } else {\n allDeps[depName] = depVersion;\n }\n });\n return sortDeps(allDeps);\n}\n\nfunction sortDeps(deps: Record<string, string>): Record<string, string> {\n const keys = Object.keys(deps).sort();\n const sortedDeps: Record<string, string> = {};\n for (const key of keys) {\n sortedDeps[key] = deps[key];\n }\n return sortedDeps;\n}\n","import 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.promises.writeFile(filepath, content);\n}\n\nexport async function makeDir(dirpath: string) {\n try {\n await fs.promises.access(dirpath);\n } catch (e) {\n await fs.promises.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, {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.promises.rm(dirpath, {recursive: true, force: true});\n}\n\nexport async function loadJson<T = unknown>(filepath: string): Promise<T> {\n const content = await fs.promises.readFile(filepath, 'utf-8');\n return JSON.parse(content);\n}\n\nexport function loadJsonSync<T = unknown>(filepath: string): T {\n const content = fs.readFileSync(filepath, 'utf-8');\n return JSON.parse(content);\n}\n\nexport async function writeJson(filepath: string, data: any) {\n const content = JSON.stringify(data, null, 2);\n await writeFile(filepath, content);\n}\n\nexport async function isDirectory(dirpath: string) {\n return fs.promises\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.promises\n .access(filepath)\n .then(() => true)\n .catch(() => false);\n}\n\nexport function fileExistsSync(filepath: string): boolean {\n try {\n fs.accessSync(filepath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function dirExists(dirpath: string): Promise<boolean> {\n try {\n const stat = await fs.promises.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.promises.realpath(dirpath);\n const inner = await fs.promises.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.promises.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';\nimport {bundleRequire} from 'bundle-require';\nimport {build, Plugin as EsbuildPlugin} from 'esbuild';\nimport {RootConfig} from '../core/config.js';\nimport {fileExists} from '../utils/fsutils.js';\nimport {flattenPackageDepsFromMonorepo} from './monorepo.js';\n\nexport interface ConfigOptions {\n command: string;\n}\n\nexport async function loadRootConfig(\n rootDir: string,\n options: ConfigOptions\n): Promise<RootConfig> {\n const {rootConfig} = await loadRootConfigWithDeps(rootDir, options);\n return rootConfig;\n}\n\nexport async function loadRootConfigWithDeps(\n rootDir: string,\n options: ConfigOptions\n): Promise<{rootConfig: RootConfig; dependencies: string[]}> {\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 esbuildOptions: {plugins: [esbuildExternalsPlugin({rootDir})]},\n });\n let config = configBundle.mod.default || {};\n if (typeof config === 'function') {\n config = (await config(options)) || {};\n }\n const rootConfig = Object.assign({}, config, {rootDir});\n validateRootconfig(rootConfig);\n return {rootConfig, dependencies: configBundle.dependencies};\n}\n\nfunction validateRootconfig(rootConfig: RootConfig) {\n // Update vite legacy config options.\n const scss: any = rootConfig.vite?.css?.preprocessorOptions?.scss;\n if (scss?.includePaths) {\n console.warn(\n '[deprecation warning] root.config.ts: vite.css.preprocessorOptions.scss.includePaths is deprecated. rename \"includePaths\" -> \"loadPaths\"'\n );\n scss.loadPaths = scss.includePaths;\n }\n}\n\n/**\n * Compiles a root.config.ts file to root.config.js.\n */\nexport async function bundleRootConfig(rootDir: string, outPath: string) {\n const configPath = path.resolve(rootDir, 'root.config.ts');\n const configExists = await fileExists(configPath);\n if (!configExists) {\n throw new Error(`${configPath} does not exist`);\n }\n await build({\n entryPoints: [configPath],\n bundle: true,\n minify: true,\n platform: 'node',\n outfile: outPath,\n sourcemap: 'inline',\n metafile: true,\n format: 'esm',\n plugins: [esbuildExternalsPlugin({rootDir})],\n });\n}\n\n/**\n * Loads a pre-bundled config file from dist/root.config.js.\n */\nexport async function loadBundledConfig(\n rootDir: string,\n options: ConfigOptions\n): Promise<RootConfig> {\n const configPath = path.resolve(rootDir, 'dist/root.config.js');\n const exists = await fileExists(configPath);\n if (!exists) {\n throw new Error(`${configPath} does not exist`);\n }\n const module = await import(configPath);\n let config = module.default || {};\n if (typeof config === 'function') {\n config = (await config(options)) || {};\n }\n return Object.assign({}, config, {rootDir});\n}\n\n/**\n * Externalizes node_modules deps from the package and any dependent packages\n * from the monorepo.\n */\nfunction esbuildExternalsPlugin(options: {rootDir: string}): EsbuildPlugin {\n const rootDir = options.rootDir;\n const allDeps = flattenPackageDepsFromMonorepo(rootDir);\n\n function getPackageName(id: string): string {\n const segments = id.split('/');\n if (segments.length > 1) {\n // Check if package is an org path like `@blinkk/root`.\n if (segments[0].startsWith('@') && segments[0].length > 1) {\n return `${segments[0]}/${segments[1]}`;\n }\n // For imports like `my-package/subpackage`, return `my-package`.\n return segments[0];\n }\n return id;\n }\n\n return {\n name: 'root-externals-plugin',\n setup(build) {\n build.onResolve({filter: /.*/}, (args) => {\n const id = args.path;\n if (id[0] !== '.' && !id.startsWith('@/')) {\n const packageName = getPackageName(id);\n if (packageName in allDeps) {\n return {\n external: true,\n };\n }\n }\n return null;\n });\n },\n };\n}\n","import path from 'node:path';\nimport glob from 'tiny-glob';\nimport {RootConfig} from '../core/config.js';\nimport {Pod, PodConfig} from '../core/pod.js';\nimport {Plugin} from '../core/plugin.js';\nimport {isDirectory, isJsFile} from '../utils/fsutils.js';\n\nexport interface ResolvedPodRoute {\n filePath: string;\n relPath: string;\n routePath: string;\n}\n\nexport interface ResolvedPodCollection {\n filePath: string;\n relPath: string;\n id: string;\n}\n\nexport interface ResolvedPod {\n name: string;\n enabled: boolean;\n mount: string;\n priority: number;\n routesDir?: string;\n elementsDirs: string[];\n bundlesDir?: string;\n collectionsDir?: string;\n translationsDir?: string;\n routeFiles: ResolvedPodRoute[];\n bundleFiles: string[];\n collectionFiles: ResolvedPodCollection[];\n translationFiles: Array<{locale: string; filePath: string}>;\n config: PodConfig;\n}\n\nlet cachedPods: ResolvedPod[] | null = null;\n\nexport function invalidatePodCache() {\n cachedPods = null;\n}\n\nexport async function collectPods(rootConfig: RootConfig): Promise<ResolvedPod[]> {\n if (cachedPods) {\n return cachedPods;\n }\n\n const plugins = rootConfig.plugins || [];\n const userPodConfigs = rootConfig.pods || {};\n\n const rawPods = await resolvePluginPods(plugins, rootConfig);\n const resolved: ResolvedPod[] = [];\n\n const seenNames = new Set<string>();\n for (const pod of rawPods) {\n if (seenNames.has(pod.name)) {\n throw new Error(\n `Duplicate pod name: \"${pod.name}\". Each pod must have a unique name.`\n );\n }\n seenNames.add(pod.name);\n\n const userConfig = userPodConfigs[pod.name] || {};\n if (userConfig.enabled === false) {\n continue;\n }\n\n const resolvedPod = await resolvePod(pod, userConfig, rootConfig);\n resolved.push(resolvedPod);\n }\n\n validateCollectionIds(resolved, rootConfig);\n\n cachedPods = resolved;\n return resolved;\n}\n\nasync function resolvePluginPods(\n plugins: Plugin[],\n rootConfig: RootConfig\n): Promise<Pod[]> {\n const pods: Pod[] = [];\n for (const plugin of plugins) {\n if (!plugin.pod) {\n continue;\n }\n if (typeof plugin.pod === 'function') {\n const result = await plugin.pod({rootConfig});\n pods.push(result);\n } else if (Array.isArray(plugin.pod)) {\n pods.push(...plugin.pod);\n } else {\n pods.push(plugin.pod);\n }\n }\n return pods;\n}\n\nasync function resolvePod(\n pod: Pod,\n userConfig: PodConfig,\n rootConfig: RootConfig\n): Promise<ResolvedPod> {\n const mount = normalizeMountPath(userConfig.mount ?? pod.mount ?? '/');\n const priority = userConfig.priority ?? pod.priority ?? 0;\n\n const routeFiles = pod.routesDir\n ? await scanRouteFiles(pod.routesDir, mount, userConfig, rootConfig)\n : [];\n\n const bundleFiles = pod.bundlesDir\n ? await scanBundleFiles(pod.bundlesDir)\n : [];\n\n const collectionFiles = pod.collectionsDir\n ? await scanCollectionFiles(pod.collectionsDir, userConfig)\n : [];\n\n const translationFiles = pod.translationsDir\n ? await scanTranslationFiles(pod.translationsDir)\n : [];\n\n return {\n name: pod.name,\n enabled: true,\n mount,\n priority,\n routesDir: pod.routesDir,\n elementsDirs: pod.elementsDirs || [],\n bundlesDir: pod.bundlesDir,\n collectionsDir: pod.collectionsDir,\n translationsDir: pod.translationsDir,\n routeFiles,\n bundleFiles,\n collectionFiles,\n translationFiles,\n config: userConfig,\n };\n}\n\nasync function scanRouteFiles(\n routesDir: string,\n mount: string,\n userConfig: PodConfig,\n rootConfig: RootConfig\n): Promise<ResolvedPodRoute[]> {\n if (!(await isDirectory(routesDir))) {\n return [];\n }\n\n const files = await glob('**/*', {cwd: routesDir});\n const routes: ResolvedPodRoute[] = [];\n const excludePatterns = userConfig.routes?.exclude || [];\n\n for (const file of files) {\n const parts = path.parse(file);\n if (parts.name.startsWith('_') || !isJsFile(parts.base)) {\n continue;\n }\n\n if (shouldExclude(file, excludePatterns)) {\n continue;\n }\n\n const filePath = path.join(routesDir, file);\n let relativeRoutePath = '/' + file.replace(/\\\\/g, '/');\n const routeParts = path.parse(relativeRoutePath);\n if (routeParts.name === 'index') {\n relativeRoutePath = routeParts.dir;\n } else {\n relativeRoutePath = path.join(routeParts.dir, routeParts.name);\n }\n relativeRoutePath = relativeRoutePath.replace(/\\\\/g, '/');\n\n const routePath = normalizeRoutePath(mount, relativeRoutePath, rootConfig);\n\n routes.push({filePath, relPath: file, routePath});\n }\n\n return routes;\n}\n\nasync function scanBundleFiles(bundlesDir: string): Promise<string[]> {\n if (!(await isDirectory(bundlesDir))) {\n return [];\n }\n const files = await glob('*', {cwd: bundlesDir});\n return files\n .filter((file) => isJsFile(path.parse(file).base))\n .map((file) => path.join(bundlesDir, file));\n}\n\nasync function scanCollectionFiles(\n collectionsDir: string,\n userConfig: PodConfig\n): Promise<ResolvedPodCollection[]> {\n if (!(await isDirectory(collectionsDir))) {\n return [];\n }\n const files = await glob('**/*.schema.ts', {cwd: collectionsDir});\n const excludeIds = new Set(userConfig.collections?.exclude || []);\n const renameMap = userConfig.collections?.rename || {};\n const collections: ResolvedPodCollection[] = [];\n\n for (const file of files) {\n const parts = path.parse(file);\n const rawId = parts.name.replace('.schema', '');\n if (excludeIds.has(rawId)) {\n continue;\n }\n const id = renameMap[rawId] || rawId;\n collections.push({\n filePath: path.join(collectionsDir, file),\n relPath: file,\n id,\n });\n }\n\n return collections;\n}\n\nasync function scanTranslationFiles(\n translationsDir: string\n): Promise<Array<{locale: string; filePath: string}>> {\n if (!(await isDirectory(translationsDir))) {\n return [];\n }\n const files = await glob('*.json', {cwd: translationsDir});\n return files.map((file) => ({\n locale: path.parse(file).name,\n filePath: path.join(translationsDir, file),\n }));\n}\n\nfunction validateCollectionIds(\n pods: ResolvedPod[],\n rootConfig: RootConfig\n) {\n const seenIds = new Map<string, string>();\n\n for (const pod of pods) {\n for (const col of pod.collectionFiles) {\n const existing = seenIds.get(col.id);\n if (existing) {\n throw new Error(\n `Collection \"${col.id}\" is defined by both \"${existing}\" and ` +\n `\"${pod.name}\". Use rootConfig.pods['${pod.name}'].collections.rename ` +\n `to disambiguate.`\n );\n }\n seenIds.set(col.id, pod.name);\n }\n }\n}\n\nfunction normalizeMountPath(mount: string): string {\n if (!mount.startsWith('/')) {\n mount = '/' + mount;\n }\n if (mount.endsWith('/') && mount.length > 1) {\n mount = mount.slice(0, -1);\n }\n return mount;\n}\n\nfunction normalizeRoutePath(\n mount: string,\n relativeRoutePath: string,\n rootConfig: RootConfig\n): string {\n const basePath = rootConfig.base || '/';\n let fullPath: string;\n if (mount === '/') {\n fullPath = relativeRoutePath || '/';\n } else {\n fullPath = mount + (relativeRoutePath || '');\n }\n\n if (basePath !== '/') {\n const base = basePath.replace(/^\\/|\\/$/g, '');\n fullPath = `/${base}${fullPath}`;\n }\n\n // Collapse multiple slashes.\n fullPath = fullPath.replace(/\\/+/g, '/');\n if (!fullPath.startsWith('/')) {\n fullPath = '/' + fullPath;\n }\n return fullPath || '/';\n}\n\nfunction shouldExclude(\n filePath: string,\n patterns: (string | RegExp)[]\n): boolean {\n for (const pattern of patterns) {\n if (typeof pattern === 'string') {\n if (filePath.includes(pattern)) {\n return true;\n }\n } else {\n if (pattern.test(filePath)) {\n return true;\n }\n }\n }\n return false;\n}\n","import {createServer, ViteDevServer} from 'vite';\nimport type {Plugin, EnvironmentModuleNode} from 'vite';\nimport {RootConfig} from '../core/config.js';\nimport {getVitePlugins} from '../core/plugin.js';\nimport {rootPodsVitePlugin} from './pods-vite-plugin.js';\nimport {preactToRootJsxPlugin} from './vite-plugin-root-jsx-virtual.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', '@blinkk/root-cms/richtext', /^virtual:/],\n },\n plugins: [\n rootPodsVitePlugin(rootConfig),\n hmrSSRReload(),\n preactToRootJsxPlugin({useRootJsx: !!rootConfig.jsxRenderer?.mode}),\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<T = Record<string, any>>(\n rootConfig: RootConfig,\n file: string\n): Promise<T> {\n const viteServer = await createViteServer(rootConfig, {hmr: false});\n const module = await viteServer.ssrLoadModule(file);\n await viteServer.close();\n return module as T;\n}\n\n/**\n * Vite plugin to reload the page when SSR modules change.\n * https://github.com/vitejs/vite/issues/19114\n */\nfunction hmrSSRReload(): Plugin {\n return {\n name: 'hmr-ssr-reload',\n enforce: 'post',\n hotUpdate: {\n order: 'post',\n handler({modules, server, timestamp}) {\n if (this.environment.name !== 'ssr') {\n return;\n }\n\n let hasSsrOnlyModules = false;\n const invalidatedModules = new Set<EnvironmentModuleNode>();\n for (const mod of modules) {\n if (mod.id === null) {\n continue;\n }\n const clientModule =\n server.environments.client.moduleGraph.getModuleById(mod.id);\n if (clientModule) {\n continue;\n }\n\n hasSsrOnlyModules = true;\n this.environment.moduleGraph.invalidateModule(\n mod,\n invalidatedModules,\n timestamp,\n true\n );\n }\n\n if (hasSsrOnlyModules) {\n server.ws.send({type: 'full-reload'});\n return [];\n }\n\n return;\n },\n },\n };\n}\n","import path from 'node:path';\nimport type {Plugin as VitePlugin} from 'vite';\nimport glob from 'tiny-glob';\nimport {RootConfig} from '../core/config.js';\nimport {isDirectory, isJsFile} from '../utils/fsutils.js';\nimport {collectPods, invalidatePodCache, ResolvedPod} from './pod-collector.js';\n\nexport const VIRTUAL_ROUTES_ID = 'virtual:root/routes';\nexport const VIRTUAL_SCHEMAS_ID = 'virtual:root/schemas';\nexport const VIRTUAL_TRANSLATIONS_ID = 'virtual:root/translations';\n\nconst RESOLVED_VIRTUAL_ROUTES = '\\0' + VIRTUAL_ROUTES_ID;\nconst RESOLVED_VIRTUAL_SCHEMAS = '\\0' + VIRTUAL_SCHEMAS_ID;\nconst RESOLVED_VIRTUAL_TRANSLATIONS = '\\0' + VIRTUAL_TRANSLATIONS_ID;\n\nexport function rootPodsVitePlugin(rootConfig: RootConfig): VitePlugin {\n let podsPromise: Promise<ResolvedPod[]> | null = null;\n\n const getPods = () => {\n if (!podsPromise) {\n podsPromise = collectPods(rootConfig);\n }\n return podsPromise;\n };\n\n return {\n name: 'root:pods',\n enforce: 'pre',\n\n resolveId(id) {\n if (id === VIRTUAL_ROUTES_ID) return RESOLVED_VIRTUAL_ROUTES;\n if (id === VIRTUAL_SCHEMAS_ID) return RESOLVED_VIRTUAL_SCHEMAS;\n if (id === VIRTUAL_TRANSLATIONS_ID) return RESOLVED_VIRTUAL_TRANSLATIONS;\n return null;\n },\n\n async load(id) {\n if (id === RESOLVED_VIRTUAL_ROUTES) {\n return buildRoutesModule(rootConfig, await getPods());\n }\n if (id === RESOLVED_VIRTUAL_SCHEMAS) {\n return buildSchemasModule(rootConfig, await getPods());\n }\n if (id === RESOLVED_VIRTUAL_TRANSLATIONS) {\n return buildTranslationsModule(rootConfig, await getPods());\n }\n return null;\n },\n\n configureServer(server) {\n const watchDirs: string[] = [];\n getPods().then((pods) => {\n for (const pod of pods) {\n if (pod.routesDir) watchDirs.push(pod.routesDir);\n if (pod.collectionsDir) watchDirs.push(pod.collectionsDir);\n if (pod.translationsDir) watchDirs.push(pod.translationsDir);\n if (pod.bundlesDir) watchDirs.push(pod.bundlesDir);\n for (const dir of pod.elementsDirs) {\n watchDirs.push(dir);\n }\n }\n for (const dir of watchDirs) {\n server.watcher.add(dir);\n }\n });\n\n const invalidateVirtualModules = () => {\n invalidatePodCache();\n podsPromise = null;\n const mods = [\n RESOLVED_VIRTUAL_ROUTES,\n RESOLVED_VIRTUAL_SCHEMAS,\n RESOLVED_VIRTUAL_TRANSLATIONS,\n ];\n for (const modId of mods) {\n const mod = server.moduleGraph.getModuleById(modId);\n if (mod) {\n server.moduleGraph.invalidateModule(mod);\n }\n }\n server.ws.send({type: 'full-reload'});\n };\n\n server.watcher.on('add', (filePath) => {\n if (isInPodDir(filePath, watchDirs)) {\n invalidateVirtualModules();\n }\n });\n server.watcher.on('unlink', (filePath) => {\n if (isInPodDir(filePath, watchDirs)) {\n invalidateVirtualModules();\n }\n });\n },\n };\n}\n\nfunction isInPodDir(filePath: string, watchDirs: string[]): boolean {\n for (const dir of watchDirs) {\n if (filePath.startsWith(dir)) {\n return true;\n }\n }\n return false;\n}\n\nasync function buildRoutesModule(\n rootConfig: RootConfig,\n pods: ResolvedPod[]\n): Promise<string> {\n const rootDir = rootConfig.rootDir;\n const imports: string[] = [];\n const userEntries: string[] = [];\n const podEntries: string[] = [];\n let idx = 0;\n\n // User project routes.\n const routesDir = path.join(rootDir, 'routes');\n if (await isDirectory(routesDir)) {\n const files = await glob('**/*', {cwd: routesDir});\n for (const file of files) {\n const parts = path.parse(file);\n if (parts.name.startsWith('_') || !isJsFile(parts.base)) {\n continue;\n }\n const modulePath = `/routes/${file.replace(/\\\\/g, '/')}`;\n const varName = `_r${idx++}`;\n imports.push(`import * as ${varName} from '${modulePath}';`);\n userEntries.push(` '${modulePath}': ${varName},`);\n }\n }\n\n // Pod routes.\n for (const pod of pods) {\n for (const route of pod.routeFiles) {\n const varName = `_r${idx++}`;\n imports.push(`import * as ${varName} from '${route.filePath}';`);\n podEntries.push(\n ` '${route.filePath}': {module: ${varName}, podName: ${JSON.stringify(pod.name)}, routePath: ${JSON.stringify(route.routePath)}, src: ${JSON.stringify(`pod/${pod.name}/${route.relPath}`)}},`\n );\n }\n }\n\n return [\n ...imports,\n '',\n 'export const ROUTE_MODULES = {',\n ...userEntries,\n '};',\n '',\n 'export const POD_ROUTE_MODULES = {',\n ...podEntries,\n '};',\n ].join('\\n');\n}\n\nasync function buildSchemasModule(\n rootConfig: RootConfig,\n pods: ResolvedPod[]\n): Promise<string> {\n const rootDir = rootConfig.rootDir;\n const imports: string[] = [];\n const entries: string[] = [];\n let idx = 0;\n\n // User project collections — these win over pod collections.\n const userCollectionIds = new Set<string>();\n const collectionsDir = path.join(rootDir, 'collections');\n if (await isDirectory(collectionsDir)) {\n const files = await glob('**/*.schema.ts', {cwd: collectionsDir});\n for (const file of files) {\n const parts = path.parse(file);\n const id = parts.name.replace('.schema', '');\n userCollectionIds.add(id);\n const modulePath = `/collections/${file.replace(/\\\\/g, '/')}`;\n const varName = `_s${idx++}`;\n imports.push(`import * as ${varName} from '${modulePath}';`);\n entries.push(` '${modulePath}': ${varName},`);\n }\n }\n\n // Also scan root-level schema files that match the existing patterns\n // (e.g. /templates/**/*.schema.ts), excluding known non-collection dirs.\n const rootSchemaFiles = await scanRootSchemaFiles(rootDir);\n for (const file of rootSchemaFiles) {\n const modulePath = `/${file.replace(/\\\\/g, '/')}`;\n // Skip if already in collections.\n if (modulePath.startsWith('/collections/')) continue;\n const varName = `_s${idx++}`;\n imports.push(`import * as ${varName} from '${modulePath}';`);\n entries.push(` '${modulePath}': ${varName},`);\n }\n\n // Pod collections — user project wins on id collision.\n for (const pod of pods) {\n for (const col of pod.collectionFiles) {\n if (userCollectionIds.has(col.id)) {\n continue;\n }\n const varName = `_s${idx++}`;\n imports.push(`import * as ${varName} from '${col.filePath}';`);\n const key = `/collections/${col.id}.schema.ts`;\n entries.push(` '${key}': ${varName},`);\n }\n }\n\n return [\n ...imports,\n '',\n 'export const SCHEMA_MODULES = {',\n ...entries,\n '};',\n ].join('\\n');\n}\n\nasync function scanRootSchemaFiles(rootDir: string): Promise<string[]> {\n const excludeDirs = ['appengine', 'functions', 'gae', 'node_modules', 'dist'];\n let files: string[];\n try {\n files = await glob('**/*.schema.ts', {cwd: rootDir});\n } catch {\n return [];\n }\n return files.filter((file) => {\n const normalized = file.replace(/\\\\/g, '/');\n return !excludeDirs.some((dir) => normalized.startsWith(dir + '/'));\n });\n}\n\nasync function buildTranslationsModule(\n rootConfig: RootConfig,\n pods: ResolvedPod[]\n): Promise<string> {\n const rootDir = rootConfig.rootDir;\n const imports: string[] = [];\n const entries: string[] = [];\n let idx = 0;\n\n // User translations take precedence.\n const userLocales = new Set<string>();\n const translationsDir = path.join(rootDir, 'translations');\n if (await isDirectory(translationsDir)) {\n const files = await glob('*.json', {cwd: translationsDir});\n for (const file of files) {\n const locale = path.parse(file).name;\n userLocales.add(locale);\n const modulePath = `/translations/${file}`;\n const varName = `_t${idx++}`;\n imports.push(`import ${varName} from '${modulePath}';`);\n entries.push(` '${modulePath}': {default: ${varName}},`);\n }\n }\n\n // Pod translations.\n for (const pod of pods) {\n for (const t of pod.translationFiles) {\n const varName = `_t${idx++}`;\n imports.push(`import ${varName} from '${t.filePath}';`);\n // Use a synthetic key that includes the pod name for merging.\n const key = `/translations/pod:${pod.name}:${t.locale}.json`;\n entries.push(` '${key}': {default: ${varName}},`);\n }\n }\n\n return [\n ...imports,\n '',\n 'export const TRANSLATION_MODULES = {',\n ...entries,\n '};',\n ].join('\\n');\n}\n","import type {Plugin} from 'vite';\n\nexport interface PreactToRootJsxPluginOptions {\n /** Whether Root's JSX renderer mode is enabled. */\n useRootJsx?: boolean;\n}\n\n/**\n * Vite plugin that aliases `preact` imports to `@blinkk/root/jsx` in the SSR\n * environment only.\n *\n * When `jsxRenderer.mode` is configured, the project uses Root's built-in JSX\n * runtime instead of Preact for server-side rendering. This plugin redirects\n * `preact` imports (`preact`, `preact/hooks`, `preact/jsx-runtime`, etc.) to\n * Root's JSX package, but only in the SSR environment. Client-side code (e.g.\n * islands that depend on real Preact for hydration) is left untouched.\n */\nexport function preactToRootJsxPlugin(\n options?: PreactToRootJsxPluginOptions\n): Plugin {\n const useRootJsx = options?.useRootJsx ?? false;\n\n /**\n * Preact import specifiers to redirect in SSR when `jsxRenderer.mode` is\n * enabled.\n */\n const SSR_REDIRECTS: Record<string, string> = {\n preact: '@blinkk/root/jsx',\n 'preact/hooks': '@blinkk/root/jsx',\n 'preact/jsx-runtime': '@blinkk/root/jsx/jsx-runtime',\n 'preact/jsx-dev-runtime': '@blinkk/root/jsx/jsx-dev-runtime',\n };\n\n return {\n name: 'root:preact-to-jsx',\n async resolveId(id, importer, resolveOptions) {\n if (!useRootJsx) {\n return null;\n }\n\n const ssrTarget = SSR_REDIRECTS[id];\n if (!ssrTarget) {\n return null;\n }\n\n // Only rewrite in the SSR environment. Client-side code (islands)\n // continues to use the real Preact package.\n const isSSR = this.environment?.name === 'ssr';\n if (!isSSR) {\n return null;\n }\n\n const resolved = await this.resolve(ssrTarget, importer, {\n ...resolveOptions,\n skipSelf: true,\n });\n return resolved;\n },\n };\n}\n"],"mappings":"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","names":["path","path","path","path","build","path","glob","glob","path","path","glob","path","glob"]}
@@ -32,4 +32,4 @@ export {
32
32
  configureServerPlugins,
33
33
  getVitePlugins
34
34
  };
35
- //# sourceMappingURL=chunk-6FDF3DXT.js.map
35
+ //# sourceMappingURL=chunk-XSNCF7WU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/plugin.ts"],"sourcesContent":["import {ViteDevServer, PluginOption as VitePlugin} from 'vite';\nimport {RootConfig} from './config.js';\nimport {Pod, PodFactory} from './pod.js';\nimport {NextFunction, Request, Response, Server} from './types.js';\n\ntype MaybePromise<T> = T | Promise<T>;\n\ntype PreBuildHook = (rootConfig: RootConfig) => MaybePromise<void>;\n\nexport interface PostBuildOptions {\n /** Whether the build was SSR-only (no SSG pre-rendering). */\n ssrOnly?: boolean;\n}\n\ntype PostBuildHook = (\n rootConfig: RootConfig,\n options?: PostBuildOptions\n) => MaybePromise<void>;\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 PluginHooks {\n /**\n * Hook that runs before the build starts.\n */\n preBuild?: PreBuildHook;\n\n /**\n * Hook that runs after the build completes.\n */\n postBuild?: PostBuildHook;\n\n /**\n * Post-render hook that's called before the HTML is rendered to the response\n * object. If a string is returned from this hook, it will replace the\n * rendered HTML.\n */\n preRender?: (html: string) => void | MaybePromise<string>;\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 * Hook for file changes.\n */\n onFileChange?: (\n eventName: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir',\n path: string\n ) => void;\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 /** Plugin lifecycle callback hooks. */\n hooks?: PluginHooks;\n /** Custom 404 handler. */\n handle404?: (\n req: Request,\n res: Response,\n next: NextFunction\n ) => void | Promise<void>;\n\n /**\n * Registers a pod (a mini root.js site) that gets merged into the parent\n * site at dev/build time. A plugin may return a single pod, an array of\n * pods, or a factory that receives the rootConfig.\n */\n pod?: Pod | Pod[] | PodFactory;\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 const viteServer = server.get('viteServer') as ViteDevServer;\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 if (viteServer && plugin.onFileChange) {\n viteServer.watcher.on('all', plugin.onFileChange);\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":";AAoGA,eAAsB,uBACpB,QACA,UACA,SACA,SACA;AACA,QAAM,YAA+B,CAAC;AACtC,QAAM,aAAa,OAAO,IAAI,YAAY;AAG1C,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;AAEA,QAAI,cAAc,OAAO,cAAc;AACrC,iBAAW,QAAQ,GAAG,OAAO,OAAO,YAAY;AAAA,IAClD;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":[]}
package/dist/cli.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { f as Server } from './types-BstdV3Rj.js';
1
+ import { f as Server } from './types-Cksf99CK.js';
2
2
  import 'express';
3
3
  import 'preact';
4
4
  import 'vite';
5
- import 'html-minifier-terser';
6
- import 'js-beautify';
7
5
  import './jsx-C8BaDh-4.js';
8
6
  import './jsx-dev-runtime-DUJrvx5P.js';
7
+ import 'html-minifier-terser';
8
+ import 'js-beautify';
9
9
 
10
10
  interface BuildOptions {
11
11
  ssrOnly?: boolean;
package/dist/cli.js CHANGED
@@ -8,11 +8,11 @@ import {
8
8
  dev,
9
9
  preview,
10
10
  start
11
- } from "./chunk-G7OKXRD3.js";
11
+ } from "./chunk-GSR74KEI.js";
12
12
  import "./chunk-6P3B7ZXL.js";
13
13
  import "./chunk-JCRXBB26.js";
14
- import "./chunk-EBE4O463.js";
15
- import "./chunk-6FDF3DXT.js";
14
+ import "./chunk-PVBPP5LN.js";
15
+ import "./chunk-XSNCF7WU.js";
16
16
  export {
17
17
  CliRunner,
18
18
  build,
package/dist/core.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { g as Route } from './types-BstdV3Rj.js';
2
- export { p as ConfigureServerHook, q as ConfigureServerOptions, C as ContentSecurityPolicyConfig, B as GetStaticContent, x as GetStaticPaths, G as GetStaticProps, z as Handler, H as HandlerContext, F as HandlerRenderFn, E as HandlerRenderOptions, L as LocaleGroup, M as MultipartFile, N as NextFunction, t as Plugin, r as PluginHooks, P as PostBuildOptions, a as Request, y as RequestMiddleware, b as Response, j as RootBuildConfig, R as RootConfig, l as RootHeaderConfig, i as RootI18nConfig, k as RootRedirectConfig, m as RootSecurityConfig, n as RootServerConfig, h as RootUserConfig, D as RouteModule, w as RouteParams, f as Server, I as Sitemap, J as SitemapItem, A as StaticContentResult, X as XFrameOptionsConfig, u as configureServerPlugins, o as defineConfig, v as getVitePlugins } from './types-BstdV3Rj.js';
1
+ import { g as Route } from './types-Cksf99CK.js';
2
+ export { u as ConfigureServerHook, v as ConfigureServerOptions, C as ContentSecurityPolicyConfig, I as GetStaticContent, B as GetStaticPaths, G as GetStaticProps, E as Handler, H as HandlerContext, O as HandlerRenderFn, K as HandlerRenderOptions, L as LocaleGroup, M as MultipartFile, N as NextFunction, x as Plugin, w as PluginHooks, p as Pod, P as PodConfig, q as PodFactory, t as PostBuildOptions, a as Request, D as RequestMiddleware, b as Response, j as RootBuildConfig, R as RootConfig, l as RootHeaderConfig, i as RootI18nConfig, k as RootRedirectConfig, m as RootSecurityConfig, n as RootServerConfig, h as RootUserConfig, J as RouteModule, A as RouteParams, f as Server, Q as Sitemap, T as SitemapItem, F as StaticContentResult, X as XFrameOptionsConfig, y as configureServerPlugins, o as defineConfig, r as definePod, z as getVitePlugins } from './types-Cksf99CK.js';
3
3
  import * as preact$1 from 'preact';
4
4
  import { FunctionalComponent, ComponentChildren } from 'preact';
5
5
  import 'express';
6
6
  import 'vite';
7
- import 'html-minifier-terser';
8
- import 'js-beautify';
9
7
  import './jsx-C8BaDh-4.js';
10
8
  import './jsx-dev-runtime-DUJrvx5P.js';
9
+ import 'html-minifier-terser';
10
+ import 'js-beautify';
11
11
 
12
12
  type BodyProps = preact.JSX.HTMLAttributes<HTMLBodyElement> & {
13
13
  children?: ComponentChildren;
package/dist/core.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  import {
11
11
  configureServerPlugins,
12
12
  getVitePlugins
13
- } from "./chunk-6FDF3DXT.js";
13
+ } from "./chunk-XSNCF7WU.js";
14
14
 
15
15
  // src/core/config.ts
16
16
  function defineConfig(config) {
@@ -172,6 +172,11 @@ function replaceStringParams(str, params) {
172
172
  return match;
173
173
  });
174
174
  }
175
+
176
+ // src/core/pod.ts
177
+ function definePod(pod) {
178
+ return pod;
179
+ }
175
180
  export {
176
181
  Body,
177
182
  HTML_CONTEXT,
@@ -182,6 +187,7 @@ export {
182
187
  TranslationMiddlewareProvider,
183
188
  configureServerPlugins,
184
189
  defineConfig,
190
+ definePod,
185
191
  getTranslations,
186
192
  getVitePlugins,
187
193
  replaceParams,
package/dist/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/config.ts","../src/core/components/Body.tsx","../src/core/components/Head.ts","../src/core/components/Script.ts","../src/core/hooks/useStringParams.tsx","../src/core/hooks/useTranslationsMiddleware.tsx","../src/core/hooks/useTranslations.ts"],"sourcesContent":["import {UserConfig as ViteUserConfig} from 'vite';\nimport {HtmlMinifyOptions} from '../render/html-minify.js';\nimport {HtmlPrettyOptions} from '../render/html-pretty.js';\nimport {JsxRenderOptions} from '../jsx/jsx-render.js';\nimport {Plugin} from './plugin.js';\nimport {RequestMiddleware} from './types.js';\n\nexport interface RootUserConfig {\n /**\n * Canonical domain the website will serve on. Useful for things like the\n * sitemap, SEO tags, etc.\n */\n domain?: string;\n\n /**\n * The base URL path that the site will serve on. Defaults to `/`;\n */\n base?: string;\n\n /**\n * Config for auto-injecting custom element dependencies.\n */\n elements?: {\n /**\n * A list of directories to use to look for custom elements. The dir path\n * should be relative to the project dir, e.g. \"path/to/elements\".\n */\n include?: string[];\n\n /**\n * A list of RegEx patterns to exclude. The string passed to the RegEx is\n * the file URL relative to the project root, e.g. \"/elements/foo/foo.ts\".\n */\n exclude?: RegExp[];\n };\n\n /**\n * Config for manually injecting stylesheet entries as\n * `<link rel=\"stylesheet\">` tags.\n */\n styles?: {\n /**\n * Project-root-relative stylesheet files to include on every rendered\n * page, e.g. `styles/index.css`.\n *\n * Entries are normalized as URL paths, so both `styles/index.css` and\n * `/styles/index.css` resolve to `/styles/index.css`.\n *\n * Manual entries are injected first, then auto-collected CSS\n * dependencies are merged after. Duplicate URLs are de-duped so each\n * stylesheet is only injected once.\n */\n entries?: string[];\n };\n\n /**\n * Config options for localization and internationalization.\n */\n i18n?: RootI18nConfig;\n\n /**\n * Config options for the Root.js express server.\n */\n server?: RootServerConfig;\n\n /**\n * Build options for the `root build` command.\n */\n build?: RootBuildConfig;\n\n /**\n * Vite config.\n * @see {@link https://vitejs.dev/config/} for more information.\n */\n vite?: ViteUserConfig;\n\n /**\n * Config for the built-in JSX-to-HTML renderer.\n *\n * - `mode: 'pretty'` (default) — block-level elements render on their own\n * line with no indentation.\n * - `mode: 'minimal'` — compact output with no extra whitespace.\n *\n * Use `blockElements` to specify additional custom element tag names that\n * should be treated as block-level in pretty mode.\n *\n * @example\n * ```ts\n * export default defineConfig({\n * jsxRenderer: {\n * mode: 'pretty',\n * blockElements: ['my-card', 'my-section'],\n * },\n * });\n * ```\n */\n jsxRenderer?: JsxRenderOptions;\n\n /**\n * Whether to automatically minify HTML output. This is enabled by default,\n * in order to disable, pass `minifyHtml: false` to root.config.ts.\n */\n minifyHtml?: boolean;\n\n /**\n * Options to pass to html-minifier-terser.\n */\n minifyHtmlOptions?: HtmlMinifyOptions;\n\n /**\n * Whether to pretty print HTML output.\n */\n prettyHtml?: boolean;\n\n /**\n * Options to pass to js-beautify.\n */\n prettyHtmlOptions?: HtmlPrettyOptions;\n\n /**\n * Whether to include a sitemap.xml file to the build output.\n */\n sitemap?: boolean;\n\n /**\n * Plugins.\n */\n plugins?: Plugin[];\n\n /**\n * Experimental config options. Note: these are subject to change at any time.\n */\n experiments?: {\n /** Whether to render `<script>` tags with `async`. */\n enableScriptAsync?: boolean;\n };\n}\n\nexport type RootConfig = RootUserConfig & {\n rootDir: string;\n};\n\nexport interface LocaleGroup {\n label?: string;\n locales: string[];\n}\n\nexport interface RootI18nConfig {\n /**\n * Locales enabled for the site.\n */\n locales?: string[];\n\n /**\n * The default locale to use. Defaults is `en`.\n */\n defaultLocale?: string;\n\n /**\n * URL format for localized content. Default is `/[locale]/[base]/[path]`.\n */\n urlFormat?: string;\n\n /**\n * Localization groups, to help UIs (like Root.js CMS) logically group\n * locales.\n */\n groups?: Record<string, LocaleGroup>;\n}\n\nexport interface RootBuildConfig {\n /**\n * Excludes the `/intl/{defaultLocale}/...` path from the SSG build.\n */\n excludeDefaultLocaleFromIntlPaths?: boolean;\n}\n\nexport interface RootRedirectConfig {\n /**\n * The source path to redirect. Accepts placeholders in the format\n * `[key]` or `[...key]`. Use `[key]` for single segments and\n * `[...key]` for multi-segment wildcards.\n * @example \"/old-path/[id]\" or \"/old-path/[...wildcard]\"\n */\n source: string;\n\n /**\n * The destination to redirect to. Placeholders from the source can\n * optionally be inserted into the destination using the same\n * placeholder format.\n * @example \"/new-path/[id]\" or \"/new-path/[...wildcard]\"\n */\n destination: string;\n\n /**\n * The redirect type (`301` = permanent, `302` = temporary). If unspecified,\n * defaults to `302` (temporary).\n */\n type?: 301 | 302;\n}\n\nexport interface RootHeaderConfig {\n /** A glob pattern match (regex not supported yet). */\n source: string;\n headers: Array<{\n key: string;\n value: string;\n }>;\n}\n\nexport interface ContentSecurityPolicyConfig {\n directives?: Record<string, string[]>;\n reportOnly?: boolean;\n}\n\nexport interface XFrameOptionsConfig {\n action: 'DENY' | 'SAMEORIGIN';\n}\n\nexport interface RootSecurityConfig {\n /**\n * Content-Security-Policy config. If enabled, a nonce is auto-generated\n * for every request and appended to script and stylesheet tags. You can\n * validate your CSP headers using a tool like {@link https://csp-evaluator.withgoogle.com/}.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP}\n */\n contentSecurityPolicy?: ContentSecurityPolicyConfig | boolean;\n\n /**\n * Strict-Transport-Security config. When enabled, the header value is set\n * to `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload`.\n */\n strictTransportSecurity?: boolean;\n\n /**\n * X-Content-Type-Options config. When enabled, the header value is set to\n * `X-Content-Type-Options: nosniff`.\n */\n xContentTypeOptions?: boolean;\n\n /**\n * X-Frame-Options config. Setting this value to `true` will default the\n * header value to `X-Frame-Options: SAMEORIGIN`.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options}\n */\n xFrameOptions?: 'DENY' | 'SAMEORIGIN' | boolean;\n\n /**\n * X-XSS-Protection config. When enabled, the header value is set to\n * `X-XSS-Protection: 1; mode=block`.\n */\n xXssProtection?: boolean;\n}\n\nexport interface RootServerConfig {\n /**\n * An array of middleware to add to the express server. These middleware are\n * added to the beginning of the express app.\n */\n middlewares?: RequestMiddleware[];\n\n /**\n * The `trailingSlash` config allows you to control how the server handles\n * trailing slashes. This config only affects URLs that do not have a file\n * extension (i.e. HTML paths).\n *\n * - When `true`, the server redirects URLs to add a trailing slash\n * - When `false`, the server redirects URLs to remove a trailing slash\n * - When unspecified, the server allows URLs with and without trailing slash\n */\n trailingSlash?: boolean;\n\n /**\n * Cookie secret for the session middleware.\n *\n * Generate a secure secret with:\n * ```\n * node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n * ```\n */\n sessionCookieSecret?: string | string[];\n\n /**\n * List of redirects. Supports optional wildcards.\n *\n * @example\n * ```ts\n * redirects: [\n * {\n * source: '/old-path/[id]',\n * destination: '/new-path/[id]',\n * type: 301,\n * },\n * {\n * source: '/old-path/[...wildcard]',\n * destination: '/new-path/[...wildcard]',\n * },\n * ]\n * ```\n */\n redirects?: RootRedirectConfig[];\n\n /**\n * HTTP headers to add to a response.\n */\n headers?: RootHeaderConfig[];\n\n /**\n * HTTP security settings. By default, all security settings are enabled with\n * commonly used default values.\n */\n security?: RootSecurityConfig;\n\n /**\n * Home page URL path, which is printed when the dev server starts.\n */\n homePagePath?: string;\n}\n\nexport function defineConfig(config: RootUserConfig): RootUserConfig {\n return config;\n}\n","import {ComponentChildren, FunctionalComponent} from 'preact';\nimport {useContext} from 'preact/hooks';\nimport {HTML_CONTEXT} from './Html.js';\n\nexport type BodyProps = preact.JSX.HTMLAttributes<HTMLBodyElement> & {\n children?: ComponentChildren;\n};\n\n/**\n * The `<Body>` component can be used to update attrs in the `<body>` tag.\n *\n * Usage:\n *\n * ```tsx\n * <Body className=\"body\">\n * <h1>Hello world</h1>\n * </Body>\n * ```\n *\n * Output:\n *\n * ```html\n * <body class=\"body\">\n * <h1>Hello world</h1>\n * </body>\n */\nexport const Body: FunctionalComponent<BodyProps> = ({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 <Body> component'\n );\n }\n context.bodyAttrs = attrs;\n return <>{children}</>;\n};\n","import {ComponentChildren, FunctionalComponent} from 'preact';\nimport {useContext} from 'preact/hooks';\nimport {HTML_CONTEXT} from './Html.js';\n\nexport type HeadProps = preact.JSX.HTMLAttributes<HTMLHeadElement> & {\n children?: ComponentChildren;\n};\n\n/**\n * The <Head> component can be used for injecting elements into the HTML head\n * tag from any part of a page. The <Head> can be added via any component or\n * sub-component and will automatically be hoisted to the `<head>` element.\n *\n * Usage:\n *\n * ```tsx\n * <Head>\n * <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n * </Head>\n * ```\n */\nexport const Head: FunctionalComponent<HeadProps> = ({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 <Head> component'\n );\n }\n context.headComponents.push(children);\n context.headAttrs = attrs;\n return null;\n};\n","import {FunctionalComponent} from 'preact';\nimport {useContext} from 'preact/hooks';\nimport {HTML_CONTEXT} from './Html.js';\n\nexport type ScriptProps = preact.JSX.HTMLAttributes<HTMLScriptElement> & {\n // Cast the \"src\" attr to string. See:\n // https://github.com/blinkk/rootjs/issues/519\n src?: string;\n};\n\n/**\n * The <Script> component is used for rendering any custom script modules. At\n * the moment, the system only pre-renders and bundles files that are in the\n * `/bundles` folder at the root of the project.\n *\n * Usage:\n *\n * ```tsx\n * <Script src=\"/bundles/main.ts\" />\n * ```\n */\nexport const Script: FunctionalComponent<ScriptProps> = (props) => {\n const context = useContext(HTML_CONTEXT);\n if (!context) {\n throw new Error(\n 'HTML_CONTEXT not found, double-check usage of the <Script> component'\n );\n }\n context.scriptDeps.push(props);\n return null;\n};\n","import {ComponentChildren, FunctionalComponent, createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport type StringParamsContext = Record<string, string>;\n\nexport const STRING_PARAMS_CONTEXT = createContext<StringParamsContext | null>(\n null\n);\n\nexport interface StringParamsProviderProps {\n value?: StringParamsContext;\n children?: ComponentChildren;\n}\n\nexport const StringParamsProvider: FunctionalComponent<\n StringParamsProviderProps\n> = (props) => {\n // Allow for nested param values from parent content providers.\n const parent = useContext(STRING_PARAMS_CONTEXT) || {};\n const merged = {...parent, ...props.value};\n return (\n <STRING_PARAMS_CONTEXT.Provider value={merged}>\n {props.children}\n </STRING_PARAMS_CONTEXT.Provider>\n );\n};\n\n/**\n * A hook that returns a map of string params, configured via the\n * `StringParamsProvider` context provider. These params are automatically\n * applied to the `useTranslations()` hook.\n *\n *\n * Usage:\n *\n * ```\n * export default function Page() {\n * return (\n * <StringParamsProvider value={{name: 'Alice'}}>\n * <SayHello />\n * </StringParamsProvider>\n * );\n * }\n *\n * function SayHello() {\n * const t = useTranslations();\n * return <h1>{t('Hello, {name}!')}</h1>;\n * }\n * ```\n *\n * This should render `<h1>Hello, Alice!</h1>`.\n */\nexport function useStringParams() {\n return useContext(STRING_PARAMS_CONTEXT) || {};\n}\n","import {ComponentChildren, FunctionalComponent, createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\ntype TransformFn = (str: string) => string;\n\nexport interface TranslationMiddleware {\n /** Transform the string before translation lookup. */\n beforeTranslate?: TransformFn;\n /** Transform the string after translation lookup. */\n afterTranslate?: TransformFn;\n /** Transform the string before `{param}` values are replaced. */\n beforeReplaceParams?: TransformFn;\n /** Transform the string after `{param}` values are replaced. */\n afterReplaceParams?: TransformFn;\n}\n\nexport interface TranslationMiddlewareContext {\n beforeTranslateFns: TransformFn[];\n afterTranslateFns: TransformFn[];\n beforeReplaceParamsFns: TransformFn[];\n afterReplaceParamsFns: TransformFn[];\n}\n\nconst TRANSLATION_MIDDLEWARE_CONTEXT =\n createContext<TranslationMiddlewareContext | null>(null);\n\nexport interface TranslationMiddlewareProviderProps {\n value?: TranslationMiddleware;\n children?: ComponentChildren;\n}\n\nexport const TranslationMiddlewareProvider: FunctionalComponent<\n TranslationMiddlewareProviderProps\n> = (props) => {\n const parent = useContext(TRANSLATION_MIDDLEWARE_CONTEXT) || {\n beforeTranslateFns: [],\n afterTranslateFns: [],\n beforeReplaceParamsFns: [],\n afterReplaceParamsFns: [],\n };\n const merged: TranslationMiddlewareContext = {\n beforeTranslateFns: [...parent.beforeTranslateFns],\n afterTranslateFns: [...parent.afterTranslateFns],\n beforeReplaceParamsFns: [...parent.beforeReplaceParamsFns],\n afterReplaceParamsFns: [...parent.afterReplaceParamsFns],\n };\n if (props.value?.beforeTranslate) {\n merged.beforeTranslateFns.push(props.value.beforeTranslate);\n }\n if (props.value?.afterTranslate) {\n merged.afterTranslateFns.push(props.value.afterTranslate);\n }\n if (props.value?.beforeReplaceParams) {\n merged.beforeReplaceParamsFns.push(props.value.beforeReplaceParams);\n }\n if (props.value?.afterReplaceParams) {\n merged.afterReplaceParamsFns.push(props.value.afterReplaceParams);\n }\n return (\n <TRANSLATION_MIDDLEWARE_CONTEXT.Provider value={merged}>\n {props.children}\n </TRANSLATION_MIDDLEWARE_CONTEXT.Provider>\n );\n};\n\nexport function useTranslationMiddleware(): TranslationMiddlewareContext {\n return (\n useContext(TRANSLATION_MIDDLEWARE_CONTEXT) || {\n beforeTranslateFns: [],\n afterTranslateFns: [],\n beforeReplaceParamsFns: [],\n afterReplaceParamsFns: [],\n }\n );\n}\n","import {I18nContext, useI18nContext} from './useI18nContext.js';\nimport {useStringParams} from './useStringParams.js';\nimport {useTranslationMiddleware} from './useTranslationsMiddleware.js';\n\n/**\n * A hook that returns a function that can be used to translate a string, and\n * optionally replace any parameterized values that are surrounded in curly\n * braces.\n *\n * Usage:\n *\n * ```ts\n * const t = useTranslations();\n * t('Hello {name}', {name: 'Bob'});\n * // => 'Bounjour Bob'\n * ```\n */\nexport function useTranslations() {\n // Ignore I18nContext not found error when used with client-side rehydration.\n let i18nContext: I18nContext | null = null;\n try {\n i18nContext = useI18nContext();\n } catch (err) {\n console.warn('I18nContext not found');\n }\n const translations = i18nContext?.translations || {};\n const stringParams = useStringParams();\n const middleware = useTranslationMiddleware();\n\n function t(str: string, params?: Record<string, string | number>): string;\n function t(\n str: string | null | undefined,\n params?: Record<string, string | number>\n ): string | undefined {\n // Suppress verbatim `'undefined'` and `'null'` output, which can occur when\n // using `useTranslations` with undefined values.\n if (typeof str === 'undefined' || str === null) {\n // Return `undefined` to suppress empty props, e.g.\n // <a title={t(undefined)}>Learn more</a>\n return undefined;\n }\n let input = normalizeString(str);\n middleware.beforeTranslateFns.forEach((fn) => {\n input = fn(input);\n });\n let translation = translations[input] ?? input ?? '';\n middleware.afterTranslateFns.forEach((fn) => {\n translation = fn(translation);\n });\n\n // Replace string params, e.g. \"Hello, {name}\".\n middleware.beforeReplaceParamsFns.forEach((fn) => {\n translation = fn(translation);\n });\n if (testHasStringParams(translation)) {\n translation = replaceStringParams(translation, {\n ...stringParams,\n ...params,\n });\n }\n middleware.afterReplaceParamsFns.forEach((fn) => {\n translation = fn(translation);\n });\n\n return translation;\n }\n return t;\n}\n\n/**\n * Cleans a string that's used for translations. Performs the following:\n * - Removes any leading/trailing whitespace\n * - Removes spaces at the end of any line\n */\nexport function normalizeString(str: string) {\n const lines = String(str)\n .trim()\n .split('\\n')\n .map((line) => removeTrailingWhitespace(line));\n return lines.join('\\n');\n}\n\nfunction removeTrailingWhitespace(str: string) {\n return String(str)\n .trimEnd()\n .replace(/&nbsp;$/, '');\n}\n\nfunction testHasStringParams(str: string) {\n return str.includes('{') && str.includes('}');\n}\n\n/**\n * Replaces string placeholder params, e.g.\n *\n * ```\n * replaceStringParams('Hello, {name}!', {name: 'Joe'})\n * // => 'Hello, Joe!'\n * ```\n */\nfunction replaceStringParams(\n str: string,\n params: Record<string, string | number>\n): string {\n return str.replace(/{([^}]+)}/g, (match, key) => {\n if (key in params) {\n return String(params[key]);\n }\n return match;\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;AAiUO,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;;;AClUA,SAAQ,kBAAiB;AAiChB;AARF,IAAM,OAAuC,CAAC,EAAC,UAAU,GAAG,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,gCAAG,UAAS;AACrB;;;AClCA,SAAQ,cAAAA,mBAAiB;AAoBlB,IAAM,OAAuC,CAAC,EAAC,UAAU,GAAG,MAAK,MAAM;AAC5E,QAAM,UAAUC,YAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,UAAQ,eAAe,KAAK,QAAQ;AACpC,UAAQ,YAAY;AACpB,SAAO;AACT;;;AC9BA,SAAQ,cAAAC,mBAAiB;AAoBlB,IAAM,SAA2C,CAAC,UAAU;AACjE,QAAM,UAAUC,YAAW,YAAY;AACvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,UAAQ,WAAW,KAAK,KAAK;AAC7B,SAAO;AACT;;;AC9BA,SAAgD,qBAAoB;AACpE,SAAQ,cAAAC,mBAAiB;AAoBrB,gBAAAC,YAAA;AAhBG,IAAM,wBAAwB;AAAA,EACnC;AACF;AAOO,IAAM,uBAET,CAAC,UAAU;AAEb,QAAM,SAASD,YAAW,qBAAqB,KAAK,CAAC;AACrD,QAAM,SAAS,EAAC,GAAG,QAAQ,GAAG,MAAM,MAAK;AACzC,SACE,gBAAAC,KAAC,sBAAsB,UAAtB,EAA+B,OAAO,QACpC,gBAAM,UACT;AAEJ;AA2BO,SAAS,kBAAkB;AAChC,SAAOD,YAAW,qBAAqB,KAAK,CAAC;AAC/C;;;ACtDA,SAAgD,iBAAAE,sBAAoB;AACpE,SAAQ,cAAAC,mBAAiB;AA0DrB,gBAAAC,YAAA;AApCJ,IAAM,iCACJF,eAAmD,IAAI;AAOlD,IAAM,gCAET,CAAC,UAAU;AACb,QAAM,SAASC,YAAW,8BAA8B,KAAK;AAAA,IAC3D,oBAAoB,CAAC;AAAA,IACrB,mBAAmB,CAAC;AAAA,IACpB,wBAAwB,CAAC;AAAA,IACzB,uBAAuB,CAAC;AAAA,EAC1B;AACA,QAAM,SAAuC;AAAA,IAC3C,oBAAoB,CAAC,GAAG,OAAO,kBAAkB;AAAA,IACjD,mBAAmB,CAAC,GAAG,OAAO,iBAAiB;AAAA,IAC/C,wBAAwB,CAAC,GAAG,OAAO,sBAAsB;AAAA,IACzD,uBAAuB,CAAC,GAAG,OAAO,qBAAqB;AAAA,EACzD;AACA,MAAI,MAAM,OAAO,iBAAiB;AAChC,WAAO,mBAAmB,KAAK,MAAM,MAAM,eAAe;AAAA,EAC5D;AACA,MAAI,MAAM,OAAO,gBAAgB;AAC/B,WAAO,kBAAkB,KAAK,MAAM,MAAM,cAAc;AAAA,EAC1D;AACA,MAAI,MAAM,OAAO,qBAAqB;AACpC,WAAO,uBAAuB,KAAK,MAAM,MAAM,mBAAmB;AAAA,EACpE;AACA,MAAI,MAAM,OAAO,oBAAoB;AACnC,WAAO,sBAAsB,KAAK,MAAM,MAAM,kBAAkB;AAAA,EAClE;AACA,SACE,gBAAAC,KAAC,+BAA+B,UAA/B,EAAwC,OAAO,QAC7C,gBAAM,UACT;AAEJ;AAEO,SAAS,2BAAyD;AACvE,SACED,YAAW,8BAA8B,KAAK;AAAA,IAC5C,oBAAoB,CAAC;AAAA,IACrB,mBAAmB,CAAC;AAAA,IACpB,wBAAwB,CAAC;AAAA,IACzB,uBAAuB,CAAC;AAAA,EAC1B;AAEJ;;;ACzDO,SAAS,kBAAkB;AAEhC,MAAI,cAAkC;AACtC,MAAI;AACF,kBAAc,eAAe;AAAA,EAC/B,SAAS,KAAK;AACZ,YAAQ,KAAK,uBAAuB;AAAA,EACtC;AACA,QAAM,eAAe,aAAa,gBAAgB,CAAC;AACnD,QAAM,eAAe,gBAAgB;AACrC,QAAM,aAAa,yBAAyB;AAG5C,WAAS,EACP,KACA,QACoB;AAGpB,QAAI,OAAO,QAAQ,eAAe,QAAQ,MAAM;AAG9C,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,gBAAgB,GAAG;AAC/B,eAAW,mBAAmB,QAAQ,CAAC,OAAO;AAC5C,cAAQ,GAAG,KAAK;AAAA,IAClB,CAAC;AACD,QAAI,cAAc,aAAa,KAAK,KAAK,SAAS;AAClD,eAAW,kBAAkB,QAAQ,CAAC,OAAO;AAC3C,oBAAc,GAAG,WAAW;AAAA,IAC9B,CAAC;AAGD,eAAW,uBAAuB,QAAQ,CAAC,OAAO;AAChD,oBAAc,GAAG,WAAW;AAAA,IAC9B,CAAC;AACD,QAAI,oBAAoB,WAAW,GAAG;AACpC,oBAAc,oBAAoB,aAAa;AAAA,QAC7C,GAAG;AAAA,QACH,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AACA,eAAW,sBAAsB,QAAQ,CAAC,OAAO;AAC/C,oBAAc,GAAG,WAAW;AAAA,IAC9B,CAAC;AAED,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOO,SAAS,gBAAgB,KAAa;AAC3C,QAAM,QAAQ,OAAO,GAAG,EACrB,KAAK,EACL,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,yBAAyB,IAAI,CAAC;AAC/C,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,yBAAyB,KAAa;AAC7C,SAAO,OAAO,GAAG,EACd,QAAQ,EACR,QAAQ,WAAW,EAAE;AAC1B;AAEA,SAAS,oBAAoB,KAAa;AACxC,SAAO,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,GAAG;AAC9C;AAUA,SAAS,oBACP,KACA,QACQ;AACR,SAAO,IAAI,QAAQ,cAAc,CAAC,OAAO,QAAQ;AAC/C,QAAI,OAAO,QAAQ;AACjB,aAAO,OAAO,OAAO,GAAG,CAAC;AAAA,IAC3B;AACA,WAAO;AAAA,EACT,CAAC;AACH;","names":["useContext","useContext","useContext","useContext","useContext","jsx","createContext","useContext","jsx"]}
1
+ {"version":3,"sources":["../src/core/config.ts","../src/core/components/Body.tsx","../src/core/components/Head.ts","../src/core/components/Script.ts","../src/core/hooks/useStringParams.tsx","../src/core/hooks/useTranslationsMiddleware.tsx","../src/core/hooks/useTranslations.ts","../src/core/pod.ts"],"sourcesContent":["import {UserConfig as ViteUserConfig} from 'vite';\nimport {JsxRenderOptions} from '../jsx/jsx-render.js';\nimport {HtmlMinifyOptions} from '../render/html-minify.js';\nimport {HtmlPrettyOptions} from '../render/html-pretty.js';\nimport {Plugin} from './plugin.js';\nimport {PodConfig} from './pod.js';\nimport {RequestMiddleware} from './types.js';\n\nexport interface RootUserConfig {\n /**\n * Canonical domain the website will serve on. Useful for things like the\n * sitemap, SEO tags, etc.\n */\n domain?: string;\n\n /**\n * The base URL path that the site will serve on. Defaults to `/`;\n */\n base?: string;\n\n /**\n * Config for auto-injecting custom element dependencies.\n */\n elements?: {\n /**\n * A list of directories to use to look for custom elements. The dir path\n * should be relative to the project dir, e.g. \"path/to/elements\".\n */\n include?: string[];\n\n /**\n * A list of RegEx patterns to exclude. The string passed to the RegEx is\n * the file URL relative to the project root, e.g. \"/elements/foo/foo.ts\".\n */\n exclude?: RegExp[];\n };\n\n /**\n * Config for manually injecting stylesheet entries as\n * `<link rel=\"stylesheet\">` tags.\n */\n styles?: {\n /**\n * Project-root-relative stylesheet files to include on every rendered\n * page, e.g. `styles/index.css`.\n *\n * Entries are normalized as URL paths, so both `styles/index.css` and\n * `/styles/index.css` resolve to `/styles/index.css`.\n *\n * Manual entries are injected first, then auto-collected CSS\n * dependencies are merged after. Duplicate URLs are de-duped so each\n * stylesheet is only injected once.\n */\n entries?: string[];\n };\n\n /**\n * Config options for localization and internationalization.\n */\n i18n?: RootI18nConfig;\n\n /**\n * Config options for the Root.js express server.\n */\n server?: RootServerConfig;\n\n /**\n * Build options for the `root build` command.\n */\n build?: RootBuildConfig;\n\n /**\n * Vite config.\n * @see {@link https://vitejs.dev/config/} for more information.\n */\n vite?: ViteUserConfig;\n\n /**\n * Config for the built-in JSX-to-HTML renderer.\n *\n * - `mode: 'pretty'` (default) — block-level elements render on their own\n * line with no indentation.\n * - `mode: 'minimal'` — compact output with no extra whitespace.\n *\n * Use `blockElements` to specify additional custom element tag names that\n * should be treated as block-level in pretty mode.\n *\n * @example\n * ```ts\n * export default defineConfig({\n * jsxRenderer: {\n * mode: 'pretty',\n * blockElements: ['my-card', 'my-section'],\n * },\n * });\n * ```\n */\n jsxRenderer?: JsxRenderOptions;\n\n /**\n * Whether to automatically minify HTML output. This is enabled by default,\n * in order to disable, pass `minifyHtml: false` to root.config.ts.\n */\n minifyHtml?: boolean;\n\n /**\n * Options to pass to html-minifier-terser.\n */\n minifyHtmlOptions?: HtmlMinifyOptions;\n\n /**\n * Whether to pretty print HTML output.\n */\n prettyHtml?: boolean;\n\n /**\n * Options to pass to js-beautify.\n */\n prettyHtmlOptions?: HtmlPrettyOptions;\n\n /**\n * Whether to include a sitemap.xml file to the build output.\n */\n sitemap?: boolean;\n\n /**\n * Plugins.\n */\n plugins?: Plugin[];\n\n /**\n * Per-pod user-level overrides. The key is the pod name as declared in\n * Pod.name.\n */\n pods?: Record<string, PodConfig>;\n\n /**\n * Experimental config options. Note: these are subject to change at any time.\n */\n experiments?: {\n /** Whether to render `<script>` tags with `async`. */\n enableScriptAsync?: boolean;\n };\n}\n\nexport type RootConfig = RootUserConfig & {\n rootDir: string;\n};\n\nexport interface LocaleGroup {\n label?: string;\n locales: string[];\n}\n\nexport interface RootI18nConfig {\n /**\n * Locales enabled for the site.\n */\n locales?: string[];\n\n /**\n * The default locale to use. Defaults is `en`.\n */\n defaultLocale?: string;\n\n /**\n * URL format for localized content. Default is `/[locale]/[base]/[path]`.\n */\n urlFormat?: string;\n\n /**\n * Localization groups, to help UIs (like Root.js CMS) logically group\n * locales.\n */\n groups?: Record<string, LocaleGroup>;\n}\n\nexport interface RootBuildConfig {\n /**\n * Excludes the `/intl/{defaultLocale}/...` path from the SSG build.\n */\n excludeDefaultLocaleFromIntlPaths?: boolean;\n}\n\nexport interface RootRedirectConfig {\n /**\n * The source path to redirect. Accepts placeholders in the format\n * `[key]` or `[...key]`. Use `[key]` for single segments and\n * `[...key]` for multi-segment wildcards.\n * @example \"/old-path/[id]\" or \"/old-path/[...wildcard]\"\n */\n source: string;\n\n /**\n * The destination to redirect to. Placeholders from the source can\n * optionally be inserted into the destination using the same\n * placeholder format.\n * @example \"/new-path/[id]\" or \"/new-path/[...wildcard]\"\n */\n destination: string;\n\n /**\n * The redirect type (`301` = permanent, `302` = temporary). If unspecified,\n * defaults to `302` (temporary).\n */\n type?: 301 | 302;\n}\n\nexport interface RootHeaderConfig {\n /** A glob pattern match (regex not supported yet). */\n source: string;\n headers: Array<{\n key: string;\n value: string;\n }>;\n}\n\nexport interface ContentSecurityPolicyConfig {\n directives?: Record<string, string[]>;\n reportOnly?: boolean;\n}\n\nexport interface XFrameOptionsConfig {\n action: 'DENY' | 'SAMEORIGIN';\n}\n\nexport interface RootSecurityConfig {\n /**\n * Content-Security-Policy config. If enabled, a nonce is auto-generated\n * for every request and appended to script and stylesheet tags. You can\n * validate your CSP headers using a tool like {@link https://csp-evaluator.withgoogle.com/}.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP}\n */\n contentSecurityPolicy?: ContentSecurityPolicyConfig | boolean;\n\n /**\n * Strict-Transport-Security config. When enabled, the header value is set\n * to `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload`.\n */\n strictTransportSecurity?: boolean;\n\n /**\n * X-Content-Type-Options config. When enabled, the header value is set to\n * `X-Content-Type-Options: nosniff`.\n */\n xContentTypeOptions?: boolean;\n\n /**\n * X-Frame-Options config. Setting this value to `true` will default the\n * header value to `X-Frame-Options: SAMEORIGIN`.\n *\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options}\n */\n xFrameOptions?: 'DENY' | 'SAMEORIGIN' | boolean;\n\n /**\n * X-XSS-Protection config. When enabled, the header value is set to\n * `X-XSS-Protection: 1; mode=block`.\n */\n xXssProtection?: boolean;\n}\n\nexport interface RootServerConfig {\n /**\n * An array of middleware to add to the express server. These middleware are\n * added to the beginning of the express app.\n */\n middlewares?: RequestMiddleware[];\n\n /**\n * The `trailingSlash` config allows you to control how the server handles\n * trailing slashes. This config only affects URLs that do not have a file\n * extension (i.e. HTML paths).\n *\n * - When `true`, the server redirects URLs to add a trailing slash\n * - When `false`, the server redirects URLs to remove a trailing slash\n * - When unspecified, the server allows URLs with and without trailing slash\n */\n trailingSlash?: boolean;\n\n /**\n * Cookie secret for the session middleware.\n *\n * Generate a secure secret with:\n * ```\n * node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"\n * ```\n */\n sessionCookieSecret?: string | string[];\n\n /**\n * List of redirects. Supports optional wildcards.\n *\n * @example\n * ```ts\n * redirects: [\n * {\n * source: '/old-path/[id]',\n * destination: '/new-path/[id]',\n * type: 301,\n * },\n * {\n * source: '/old-path/[...wildcard]',\n * destination: '/new-path/[...wildcard]',\n * },\n * ]\n * ```\n */\n redirects?: RootRedirectConfig[];\n\n /**\n * HTTP headers to add to a response.\n */\n headers?: RootHeaderConfig[];\n\n /**\n * HTTP security settings. By default, all security settings are enabled with\n * commonly used default values.\n */\n security?: RootSecurityConfig;\n\n /**\n * Home page URL path, which is printed when the dev server starts.\n */\n homePagePath?: string;\n}\n\nexport function defineConfig(config: RootUserConfig): RootUserConfig {\n return config;\n}\n","import {ComponentChildren, FunctionalComponent} from 'preact';\nimport {useContext} from 'preact/hooks';\nimport {HTML_CONTEXT} from './Html.js';\n\nexport type BodyProps = preact.JSX.HTMLAttributes<HTMLBodyElement> & {\n children?: ComponentChildren;\n};\n\n/**\n * The `<Body>` component can be used to update attrs in the `<body>` tag.\n *\n * Usage:\n *\n * ```tsx\n * <Body className=\"body\">\n * <h1>Hello world</h1>\n * </Body>\n * ```\n *\n * Output:\n *\n * ```html\n * <body class=\"body\">\n * <h1>Hello world</h1>\n * </body>\n */\nexport const Body: FunctionalComponent<BodyProps> = ({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 <Body> component'\n );\n }\n context.bodyAttrs = attrs;\n return <>{children}</>;\n};\n","import {ComponentChildren, FunctionalComponent} from 'preact';\nimport {useContext} from 'preact/hooks';\nimport {HTML_CONTEXT} from './Html.js';\n\nexport type HeadProps = preact.JSX.HTMLAttributes<HTMLHeadElement> & {\n children?: ComponentChildren;\n};\n\n/**\n * The <Head> component can be used for injecting elements into the HTML head\n * tag from any part of a page. The <Head> can be added via any component or\n * sub-component and will automatically be hoisted to the `<head>` element.\n *\n * Usage:\n *\n * ```tsx\n * <Head>\n * <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n * </Head>\n * ```\n */\nexport const Head: FunctionalComponent<HeadProps> = ({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 <Head> component'\n );\n }\n context.headComponents.push(children);\n context.headAttrs = attrs;\n return null;\n};\n","import {FunctionalComponent} from 'preact';\nimport {useContext} from 'preact/hooks';\nimport {HTML_CONTEXT} from './Html.js';\n\nexport type ScriptProps = preact.JSX.HTMLAttributes<HTMLScriptElement> & {\n // Cast the \"src\" attr to string. See:\n // https://github.com/blinkk/rootjs/issues/519\n src?: string;\n};\n\n/**\n * The <Script> component is used for rendering any custom script modules. At\n * the moment, the system only pre-renders and bundles files that are in the\n * `/bundles` folder at the root of the project.\n *\n * Usage:\n *\n * ```tsx\n * <Script src=\"/bundles/main.ts\" />\n * ```\n */\nexport const Script: FunctionalComponent<ScriptProps> = (props) => {\n const context = useContext(HTML_CONTEXT);\n if (!context) {\n throw new Error(\n 'HTML_CONTEXT not found, double-check usage of the <Script> component'\n );\n }\n context.scriptDeps.push(props);\n return null;\n};\n","import {ComponentChildren, FunctionalComponent, createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\nexport type StringParamsContext = Record<string, string>;\n\nexport const STRING_PARAMS_CONTEXT = createContext<StringParamsContext | null>(\n null\n);\n\nexport interface StringParamsProviderProps {\n value?: StringParamsContext;\n children?: ComponentChildren;\n}\n\nexport const StringParamsProvider: FunctionalComponent<\n StringParamsProviderProps\n> = (props) => {\n // Allow for nested param values from parent content providers.\n const parent = useContext(STRING_PARAMS_CONTEXT) || {};\n const merged = {...parent, ...props.value};\n return (\n <STRING_PARAMS_CONTEXT.Provider value={merged}>\n {props.children}\n </STRING_PARAMS_CONTEXT.Provider>\n );\n};\n\n/**\n * A hook that returns a map of string params, configured via the\n * `StringParamsProvider` context provider. These params are automatically\n * applied to the `useTranslations()` hook.\n *\n *\n * Usage:\n *\n * ```\n * export default function Page() {\n * return (\n * <StringParamsProvider value={{name: 'Alice'}}>\n * <SayHello />\n * </StringParamsProvider>\n * );\n * }\n *\n * function SayHello() {\n * const t = useTranslations();\n * return <h1>{t('Hello, {name}!')}</h1>;\n * }\n * ```\n *\n * This should render `<h1>Hello, Alice!</h1>`.\n */\nexport function useStringParams() {\n return useContext(STRING_PARAMS_CONTEXT) || {};\n}\n","import {ComponentChildren, FunctionalComponent, createContext} from 'preact';\nimport {useContext} from 'preact/hooks';\n\ntype TransformFn = (str: string) => string;\n\nexport interface TranslationMiddleware {\n /** Transform the string before translation lookup. */\n beforeTranslate?: TransformFn;\n /** Transform the string after translation lookup. */\n afterTranslate?: TransformFn;\n /** Transform the string before `{param}` values are replaced. */\n beforeReplaceParams?: TransformFn;\n /** Transform the string after `{param}` values are replaced. */\n afterReplaceParams?: TransformFn;\n}\n\nexport interface TranslationMiddlewareContext {\n beforeTranslateFns: TransformFn[];\n afterTranslateFns: TransformFn[];\n beforeReplaceParamsFns: TransformFn[];\n afterReplaceParamsFns: TransformFn[];\n}\n\nconst TRANSLATION_MIDDLEWARE_CONTEXT =\n createContext<TranslationMiddlewareContext | null>(null);\n\nexport interface TranslationMiddlewareProviderProps {\n value?: TranslationMiddleware;\n children?: ComponentChildren;\n}\n\nexport const TranslationMiddlewareProvider: FunctionalComponent<\n TranslationMiddlewareProviderProps\n> = (props) => {\n const parent = useContext(TRANSLATION_MIDDLEWARE_CONTEXT) || {\n beforeTranslateFns: [],\n afterTranslateFns: [],\n beforeReplaceParamsFns: [],\n afterReplaceParamsFns: [],\n };\n const merged: TranslationMiddlewareContext = {\n beforeTranslateFns: [...parent.beforeTranslateFns],\n afterTranslateFns: [...parent.afterTranslateFns],\n beforeReplaceParamsFns: [...parent.beforeReplaceParamsFns],\n afterReplaceParamsFns: [...parent.afterReplaceParamsFns],\n };\n if (props.value?.beforeTranslate) {\n merged.beforeTranslateFns.push(props.value.beforeTranslate);\n }\n if (props.value?.afterTranslate) {\n merged.afterTranslateFns.push(props.value.afterTranslate);\n }\n if (props.value?.beforeReplaceParams) {\n merged.beforeReplaceParamsFns.push(props.value.beforeReplaceParams);\n }\n if (props.value?.afterReplaceParams) {\n merged.afterReplaceParamsFns.push(props.value.afterReplaceParams);\n }\n return (\n <TRANSLATION_MIDDLEWARE_CONTEXT.Provider value={merged}>\n {props.children}\n </TRANSLATION_MIDDLEWARE_CONTEXT.Provider>\n );\n};\n\nexport function useTranslationMiddleware(): TranslationMiddlewareContext {\n return (\n useContext(TRANSLATION_MIDDLEWARE_CONTEXT) || {\n beforeTranslateFns: [],\n afterTranslateFns: [],\n beforeReplaceParamsFns: [],\n afterReplaceParamsFns: [],\n }\n );\n}\n","import {I18nContext, useI18nContext} from './useI18nContext.js';\nimport {useStringParams} from './useStringParams.js';\nimport {useTranslationMiddleware} from './useTranslationsMiddleware.js';\n\n/**\n * A hook that returns a function that can be used to translate a string, and\n * optionally replace any parameterized values that are surrounded in curly\n * braces.\n *\n * Usage:\n *\n * ```ts\n * const t = useTranslations();\n * t('Hello {name}', {name: 'Bob'});\n * // => 'Bounjour Bob'\n * ```\n */\nexport function useTranslations() {\n // Ignore I18nContext not found error when used with client-side rehydration.\n let i18nContext: I18nContext | null = null;\n try {\n i18nContext = useI18nContext();\n } catch (err) {\n console.warn('I18nContext not found');\n }\n const translations = i18nContext?.translations || {};\n const stringParams = useStringParams();\n const middleware = useTranslationMiddleware();\n\n function t(str: string, params?: Record<string, string | number>): string;\n function t(\n str: string | null | undefined,\n params?: Record<string, string | number>\n ): string | undefined {\n // Suppress verbatim `'undefined'` and `'null'` output, which can occur when\n // using `useTranslations` with undefined values.\n if (typeof str === 'undefined' || str === null) {\n // Return `undefined` to suppress empty props, e.g.\n // <a title={t(undefined)}>Learn more</a>\n return undefined;\n }\n let input = normalizeString(str);\n middleware.beforeTranslateFns.forEach((fn) => {\n input = fn(input);\n });\n let translation = translations[input] ?? input ?? '';\n middleware.afterTranslateFns.forEach((fn) => {\n translation = fn(translation);\n });\n\n // Replace string params, e.g. \"Hello, {name}\".\n middleware.beforeReplaceParamsFns.forEach((fn) => {\n translation = fn(translation);\n });\n if (testHasStringParams(translation)) {\n translation = replaceStringParams(translation, {\n ...stringParams,\n ...params,\n });\n }\n middleware.afterReplaceParamsFns.forEach((fn) => {\n translation = fn(translation);\n });\n\n return translation;\n }\n return t;\n}\n\n/**\n * Cleans a string that's used for translations. Performs the following:\n * - Removes any leading/trailing whitespace\n * - Removes spaces at the end of any line\n */\nexport function normalizeString(str: string) {\n const lines = String(str)\n .trim()\n .split('\\n')\n .map((line) => removeTrailingWhitespace(line));\n return lines.join('\\n');\n}\n\nfunction removeTrailingWhitespace(str: string) {\n return String(str)\n .trimEnd()\n .replace(/&nbsp;$/, '');\n}\n\nfunction testHasStringParams(str: string) {\n return str.includes('{') && str.includes('}');\n}\n\n/**\n * Replaces string placeholder params, e.g.\n *\n * ```\n * replaceStringParams('Hello, {name}!', {name: 'Joe'})\n * // => 'Hello, Joe!'\n * ```\n */\nfunction replaceStringParams(\n str: string,\n params: Record<string, string | number>\n): string {\n return str.replace(/{([^}]+)}/g, (match, key) => {\n if (key in params) {\n return String(params[key]);\n }\n return match;\n });\n}\n","import {PluginOption as VitePlugin} from 'vite';\nimport {RootConfig} from './config.js';\n\nexport interface Pod {\n /** Unique pod name, e.g. '@blinkk/root-docs-pod'. */\n name: string;\n\n /**\n * URL prefix for pod routes. Routes within the pod are served under this\n * mount path. Defaults to '/'.\n */\n mount?: string;\n\n /**\n * Priority for route conflict resolution. Higher values win when multiple\n * pods register the same URL path. User-site routes always take precedence\n * regardless of priority. Defaults to 0.\n */\n priority?: number;\n\n /** Absolute path to the pod's routes/ directory. */\n routesDir?: string;\n\n /** Absolute path(s) to the pod's elements/ directory(s). */\n elementsDirs?: string[];\n\n /** Absolute path to the pod's bundles/ directory. */\n bundlesDir?: string;\n\n /** Absolute path to the pod's collections/ directory (root-cms only). */\n collectionsDir?: string;\n\n /** Absolute path to the pod's translations/ directory. */\n translationsDir?: string;\n\n /** Extra Vite plugins contributed by the pod. */\n vitePlugins?: VitePlugin[];\n}\n\nexport type PodFactory = (ctx: {rootConfig: RootConfig}) => Pod | Promise<Pod>;\n\nexport interface PodConfig {\n /** Whether the pod is enabled. Defaults to true. */\n enabled?: boolean;\n\n /** Override the pod's mount path. */\n mount?: string;\n\n /** Override the pod's priority. */\n priority?: number;\n\n /** Filter pod routes. */\n routes?: {\n exclude?: (string | RegExp)[];\n };\n\n /** Configure pod collections. */\n collections?: {\n exclude?: string[];\n /** Rename collection ids, e.g. {Posts: 'DocsPosts'}. */\n rename?: Record<string, string>;\n };\n}\n\n/**\n * Helper to define a pod with type-checking.\n */\nexport function definePod(pod: Pod): Pod {\n return pod;\n}\n"],"mappings":"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","names":["useContext","useContext","useContext","useContext","useContext","jsx","createContext","useContext","jsx"]}
package/dist/functions.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createPreviewServer,
3
3
  createProdServer
4
- } from "./chunk-G7OKXRD3.js";
4
+ } from "./chunk-GSR74KEI.js";
5
5
  import "./chunk-6P3B7ZXL.js";
6
6
  import "./chunk-JCRXBB26.js";
7
- import "./chunk-EBE4O463.js";
8
- import "./chunk-6FDF3DXT.js";
7
+ import "./chunk-PVBPP5LN.js";
8
+ import "./chunk-XSNCF7WU.js";
9
9
 
10
10
  // src/functions/server.ts
11
11
  import path from "path";
@@ -1,12 +1,12 @@
1
- import { R as RootConfig, a as Request, b as Response, N as NextFunction } from './types-BstdV3Rj.js';
2
- export { S as SESSION_COOKIE, d as SaveSessionOptions, e as Session, c as SessionMiddlewareOptions, s as sessionMiddleware } from './types-BstdV3Rj.js';
1
+ import { R as RootConfig, a as Request, b as Response, N as NextFunction } from './types-Cksf99CK.js';
2
+ export { S as SESSION_COOKIE, d as SaveSessionOptions, e as Session, c as SessionMiddlewareOptions, s as sessionMiddleware } from './types-Cksf99CK.js';
3
3
  import { RequestHandler } from 'express';
4
4
  import 'preact';
5
5
  import 'vite';
6
- import 'html-minifier-terser';
7
- import 'js-beautify';
8
6
  import './jsx-C8BaDh-4.js';
9
7
  import './jsx-dev-runtime-DUJrvx5P.js';
8
+ import 'html-minifier-terser';
9
+ import 'js-beautify';
10
10
 
11
11
  /**
12
12
  * Middleware that injects the root.js project config into the request context.
package/dist/node.d.ts CHANGED
@@ -1,12 +1,44 @@
1
- import { R as RootConfig } from './types-BstdV3Rj.js';
1
+ import { P as PodConfig, R as RootConfig } from './types-Cksf99CK.js';
2
2
  import { PackageInfo } from 'workspace-tools';
3
3
  import { ViteDevServer } from 'vite';
4
4
  import 'express';
5
5
  import 'preact';
6
- import 'html-minifier-terser';
7
- import 'js-beautify';
8
6
  import './jsx-C8BaDh-4.js';
9
7
  import './jsx-dev-runtime-DUJrvx5P.js';
8
+ import 'html-minifier-terser';
9
+ import 'js-beautify';
10
+
11
+ interface ResolvedPodRoute {
12
+ filePath: string;
13
+ relPath: string;
14
+ routePath: string;
15
+ }
16
+ interface ResolvedPodCollection {
17
+ filePath: string;
18
+ relPath: string;
19
+ id: string;
20
+ }
21
+ interface ResolvedPod {
22
+ name: string;
23
+ enabled: boolean;
24
+ mount: string;
25
+ priority: number;
26
+ routesDir?: string;
27
+ elementsDirs: string[];
28
+ bundlesDir?: string;
29
+ collectionsDir?: string;
30
+ translationsDir?: string;
31
+ routeFiles: ResolvedPodRoute[];
32
+ bundleFiles: string[];
33
+ collectionFiles: ResolvedPodCollection[];
34
+ translationFiles: Array<{
35
+ locale: string;
36
+ filePath: string;
37
+ }>;
38
+ config: PodConfig;
39
+ }
40
+ declare function invalidatePodCache(): void;
41
+ declare function collectPods(rootConfig: RootConfig): Promise<ResolvedPod[]>;
10
42
 
11
43
  interface ConfigOptions {
12
44
  command: string;
@@ -56,4 +88,4 @@ declare function createViteServer(rootConfig: RootConfig, options?: CreateViteSe
56
88
  */
57
89
  declare function viteSsrLoadModule<T = Record<string, any>>(rootConfig: RootConfig, file: string): Promise<T>;
58
90
 
59
- export { type ConfigOptions, type CreateViteServerOptions, bundleRootConfig, createViteServer, flattenPackageDepsFromMonorepo, getMonorepoPackageDeps, loadBundledConfig, loadPackageJson, loadRootConfig, loadRootConfigWithDeps, viteSsrLoadModule };
91
+ export { type ConfigOptions, type CreateViteServerOptions, type ResolvedPod, type ResolvedPodCollection, type ResolvedPodRoute, bundleRootConfig, collectPods, createViteServer, flattenPackageDepsFromMonorepo, getMonorepoPackageDeps, invalidatePodCache, loadBundledConfig, loadPackageJson, loadRootConfig, loadRootConfigWithDeps, viteSsrLoadModule };
package/dist/node.js CHANGED
@@ -1,20 +1,24 @@
1
1
  import {
2
2
  bundleRootConfig,
3
+ collectPods,
3
4
  createViteServer,
4
5
  flattenPackageDepsFromMonorepo,
5
6
  getMonorepoPackageDeps,
7
+ invalidatePodCache,
6
8
  loadBundledConfig,
7
9
  loadPackageJson,
8
10
  loadRootConfig,
9
11
  loadRootConfigWithDeps,
10
12
  viteSsrLoadModule
11
- } from "./chunk-EBE4O463.js";
12
- import "./chunk-6FDF3DXT.js";
13
+ } from "./chunk-PVBPP5LN.js";
14
+ import "./chunk-XSNCF7WU.js";
13
15
  export {
14
16
  bundleRootConfig,
17
+ collectPods,
15
18
  createViteServer,
16
19
  flattenPackageDepsFromMonorepo,
17
20
  getMonorepoPackageDeps,
21
+ invalidatePodCache,
18
22
  loadBundledConfig,
19
23
  loadPackageJson,
20
24
  loadRootConfig,
package/dist/render.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { K as Renderer } from './types-BstdV3Rj.js';
1
+ export { U as Renderer } from './types-Cksf99CK.js';
2
2
  import 'express';
3
3
  import 'preact';
4
4
  import 'vite';
5
- import 'html-minifier-terser';
6
- import 'js-beautify';
7
5
  import './jsx-C8BaDh-4.js';
8
6
  import './jsx-dev-runtime-DUJrvx5P.js';
7
+ import 'html-minifier-terser';
8
+ import 'js-beautify';
package/dist/render.js CHANGED
@@ -12877,10 +12877,9 @@ function test419Country(countryCode) {
12877
12877
 
12878
12878
  // src/render/router.ts
12879
12879
  import path from "path";
12880
- var ROUTES_FILES = import.meta.glob(
12881
- ["/routes/*.ts", "/routes/**/*.ts", "/routes/*.tsx", "/routes/**/*.tsx"],
12882
- { eager: true }
12883
- );
12880
+ import { ROUTE_MODULES, POD_ROUTE_MODULES } from "virtual:root/routes";
12881
+ var ROUTES_FILES = ROUTE_MODULES;
12882
+ var POD_ROUTES = POD_ROUTE_MODULES || {};
12884
12883
  var Router = class {
12885
12884
  rootConfig;
12886
12885
  routeTrie;
@@ -12901,6 +12900,9 @@ var Router = class {
12901
12900
  const locales = this.rootConfig.i18n?.locales || [];
12902
12901
  const basePath = this.rootConfig.base || "/";
12903
12902
  const defaultLocale = this.rootConfig.i18n?.defaultLocale || "en";
12903
+ const i18nUrlFormat = toSquareBrackets(
12904
+ this.rootConfig.i18n?.urlFormat || "/[locale]/[base]/[path]"
12905
+ );
12904
12906
  const trie = new RouteTrie();
12905
12907
  Object.keys(ROUTES_FILES).forEach((modulePath) => {
12906
12908
  const src = modulePath.slice(1);
@@ -12915,9 +12917,6 @@ var Router = class {
12915
12917
  relativeRoutePath = path.join(parts.dir, parts.name);
12916
12918
  }
12917
12919
  const urlFormat = "/[base]/[path]";
12918
- const i18nUrlFormat = toSquareBrackets(
12919
- this.rootConfig.i18n?.urlFormat || "/[locale]/[base]/[path]"
12920
- );
12921
12920
  const placeholders = {
12922
12921
  base: removeSlashes(basePath),
12923
12922
  path: removeSlashes(relativeRoutePath)
@@ -12954,6 +12953,42 @@ var Router = class {
12954
12953
  });
12955
12954
  }
12956
12955
  });
12956
+ Object.keys(POD_ROUTES).forEach((key) => {
12957
+ const entry = POD_ROUTES[key];
12958
+ const routePath = entry.routePath;
12959
+ const localeRoutePath = i18nUrlFormat.includes("[locale]") ? i18nUrlFormat.replaceAll("[base]", removeSlashes(basePath)).replaceAll("[path]", removeSlashes(routePath)) : routePath;
12960
+ const normalizedLocaleRoutePath = normalizeUrlPath(localeRoutePath, {
12961
+ trailingSlash: this.rootConfig.server?.trailingSlash
12962
+ });
12963
+ trie.add(routePath, {
12964
+ src: entry.src,
12965
+ module: entry.module,
12966
+ locale: defaultLocale,
12967
+ isDefaultLocale: true,
12968
+ routePath,
12969
+ localeRoutePath: normalizedLocaleRoutePath,
12970
+ podName: entry.podName
12971
+ });
12972
+ if (i18nUrlFormat.includes("[locale]")) {
12973
+ locales.forEach((locale) => {
12974
+ const localePath = normalizedLocaleRoutePath.replace(
12975
+ "[locale]",
12976
+ locale
12977
+ );
12978
+ if (localePath !== routePath) {
12979
+ trie.add(localePath, {
12980
+ src: entry.src,
12981
+ module: entry.module,
12982
+ locale,
12983
+ isDefaultLocale: false,
12984
+ routePath,
12985
+ localeRoutePath: normalizedLocaleRoutePath,
12986
+ podName: entry.podName
12987
+ });
12988
+ }
12989
+ });
12990
+ }
12991
+ });
12957
12992
  return trie;
12958
12993
  }
12959
12994
  async getAllPathsForRoute(urlPathFormat, route) {