@flexireact/core 3.0.3 ā 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -15
- package/dist/cli/index.js +31 -17
- package/dist/cli/index.js.map +1 -1
- package/dist/core/build/index.js +689 -0
- package/dist/core/build/index.js.map +1 -0
- package/dist/core/client/index.js +12 -15
- package/dist/core/client/index.js.map +1 -1
- package/dist/core/config.js +86 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.js +94 -51
- package/dist/core/index.js.map +1 -1
- package/dist/core/server/index.js +3 -0
- package/dist/core/server/index.js.map +1 -1
- package/dist/core/start-dev.js +3095 -0
- package/dist/core/start-dev.js.map +1 -0
- package/dist/core/start-prod.js +3095 -0
- package/dist/core/start-prod.js.map +1 -0
- package/package.json +11 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../core/build/index.ts","../../../core/utils.ts","../../../core/router/index.ts"],"sourcesContent":["/**\r\n * FlexiReact Build System\r\n * Uses esbuild for fast bundling of client and server code\r\n */\r\n\r\nimport * as esbuild from 'esbuild';\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport { findFiles, ensureDir, cleanDir, generateHash, isClientComponent, isIsland } from '../utils.js';\r\nimport { buildRouteTree } from '../router/index.js';\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\n/**\r\n * Build configuration\r\n */\r\nexport const BuildMode = {\r\n DEVELOPMENT: 'development',\r\n PRODUCTION: 'production'\r\n};\r\n\r\n/**\r\n * Main build function\r\n */\r\nexport async function build(options) {\r\n const {\r\n projectRoot,\r\n config,\r\n mode = BuildMode.PRODUCTION,\r\n analyze = false\r\n } = options;\r\n\r\n const startTime = Date.now();\r\n const outDir = config.outDir;\r\n const isDev = mode === BuildMode.DEVELOPMENT;\r\n\r\n console.log('\\nā” FlexiReact Build\\n');\r\n console.log(` Mode: ${mode}`);\r\n console.log(` Output: ${outDir}\\n`);\r\n\r\n // Clean output directory\r\n cleanDir(outDir);\r\n ensureDir(path.join(outDir, 'client'));\r\n ensureDir(path.join(outDir, 'server'));\r\n ensureDir(path.join(outDir, 'static'));\r\n\r\n // Build routes\r\n const routes = buildRouteTree(config.pagesDir, config.layoutsDir);\r\n \r\n // Find all client components and islands\r\n const clientEntries = findClientEntries(config.pagesDir, config.layoutsDir);\r\n \r\n // Build client bundle\r\n console.log('š¦ Building client bundle...');\r\n const clientResult = await buildClient({\r\n entries: clientEntries,\r\n outDir: path.join(outDir, 'client'),\r\n config,\r\n isDev\r\n });\r\n\r\n // Build server bundle\r\n console.log('š¦ Building server bundle...');\r\n const serverResult = await buildServer({\r\n pagesDir: config.pagesDir,\r\n layoutsDir: config.layoutsDir,\r\n outDir: path.join(outDir, 'server'),\r\n config,\r\n isDev\r\n });\r\n\r\n // Copy public assets\r\n console.log('š Copying public assets...');\r\n await copyPublicAssets(config.publicDir, path.join(outDir, 'static'));\r\n\r\n // Generate manifest\r\n const manifest = generateManifest({\r\n routes,\r\n clientResult,\r\n serverResult,\r\n config\r\n });\r\n \r\n fs.writeFileSync(\r\n path.join(outDir, 'manifest.json'),\r\n JSON.stringify(manifest, null, 2)\r\n );\r\n\r\n const duration = Date.now() - startTime;\r\n \r\n console.log('\\n⨠Build complete!\\n');\r\n console.log(` Duration: ${duration}ms`);\r\n console.log(` Client chunks: ${clientResult.outputs.length}`);\r\n console.log(` Server modules: ${serverResult.outputs.length}`);\r\n console.log('');\r\n\r\n // Generate bundle analysis if requested\r\n let analysis = null;\r\n if (analyze) {\r\n analysis = generateBundleAnalysis(clientResult, serverResult, outDir);\r\n }\r\n\r\n return {\r\n success: true,\r\n duration,\r\n manifest,\r\n clientResult,\r\n serverResult,\r\n analysis\r\n };\r\n}\r\n\r\n/**\r\n * Generates bundle analysis data\r\n */\r\nfunction generateBundleAnalysis(clientResult, serverResult, outDir) {\r\n const files: Record<string, { size: number; gzipSize?: number }> = {};\r\n let totalSize = 0;\r\n let totalGzipSize = 0;\r\n\r\n // Analyze client outputs\r\n for (const output of clientResult.outputs || []) {\r\n if (output.path && fs.existsSync(output.path)) {\r\n const stat = fs.statSync(output.path);\r\n const relativePath = path.relative(outDir, output.path);\r\n \r\n // Estimate gzip size (roughly 30% of original for JS)\r\n const gzipSize = Math.round(stat.size * 0.3);\r\n \r\n files[relativePath] = {\r\n size: stat.size,\r\n gzipSize\r\n };\r\n \r\n totalSize += stat.size;\r\n totalGzipSize += gzipSize;\r\n }\r\n }\r\n\r\n // Analyze server outputs\r\n for (const output of serverResult.outputs || []) {\r\n if (output.path && fs.existsSync(output.path)) {\r\n const stat = fs.statSync(output.path);\r\n const relativePath = path.relative(outDir, output.path);\r\n \r\n files[relativePath] = {\r\n size: stat.size\r\n };\r\n \r\n totalSize += stat.size;\r\n }\r\n }\r\n\r\n return {\r\n files,\r\n totalSize,\r\n totalGzipSize,\r\n clientSize: clientResult.outputs?.reduce((sum, o) => {\r\n if (o.path && fs.existsSync(o.path)) {\r\n return sum + fs.statSync(o.path).size;\r\n }\r\n return sum;\r\n }, 0) || 0,\r\n serverSize: serverResult.outputs?.reduce((sum, o) => {\r\n if (o.path && fs.existsSync(o.path)) {\r\n return sum + fs.statSync(o.path).size;\r\n }\r\n return sum;\r\n }, 0) || 0\r\n };\r\n}\r\n\r\n/**\r\n * Finds all client component entries\r\n */\r\nfunction findClientEntries(pagesDir, layoutsDir) {\r\n const entries = [];\r\n const dirs = [pagesDir, layoutsDir].filter(d => fs.existsSync(d));\r\n\r\n for (const dir of dirs) {\r\n const files = findFiles(dir, /\\.(jsx|tsx)$/);\r\n \r\n for (const file of files) {\r\n if (isClientComponent(file) || isIsland(file)) {\r\n entries.push(file);\r\n }\r\n }\r\n }\r\n\r\n return entries;\r\n}\r\n\r\n/**\r\n * Builds client-side JavaScript\r\n */\r\nasync function buildClient(options) {\r\n const { entries, outDir, config, isDev } = options;\r\n\r\n if (entries.length === 0) {\r\n return { outputs: [] };\r\n }\r\n\r\n // Create entry points map\r\n const entryPoints = {};\r\n for (const entry of entries) {\r\n const name = path.basename(entry, path.extname(entry));\r\n const hash = generateHash(entry);\r\n entryPoints[`${name}-${hash}`] = entry;\r\n }\r\n\r\n // Add runtime entry\r\n const runtimePath = path.join(__dirname, '..', 'client', 'runtime.js');\r\n if (fs.existsSync(runtimePath)) {\r\n entryPoints['runtime'] = runtimePath;\r\n }\r\n\r\n try {\r\n const result = await esbuild.build({\r\n entryPoints,\r\n bundle: true,\r\n splitting: true,\r\n format: 'esm',\r\n outdir: outDir,\r\n minify: !isDev && config.build.minify,\r\n sourcemap: config.build.sourcemap,\r\n target: config.build.target,\r\n jsx: 'automatic',\r\n jsxImportSource: 'react',\r\n metafile: true,\r\n external: [],\r\n define: {\r\n 'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production')\r\n },\r\n loader: {\r\n '.js': 'jsx',\r\n '.jsx': 'jsx',\r\n '.ts': 'tsx',\r\n '.tsx': 'tsx'\r\n }\r\n });\r\n\r\n const outputs = Object.keys(result.metafile.outputs).map(file => ({\r\n file: path.basename(file),\r\n size: result.metafile.outputs[file].bytes\r\n }));\r\n\r\n return { outputs, metafile: result.metafile };\r\n\r\n } catch (error) {\r\n console.error('Client build failed:', error);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Builds server-side modules\r\n */\r\nasync function buildServer(options) {\r\n const { pagesDir, layoutsDir, outDir, config, isDev } = options;\r\n\r\n const entries = [];\r\n \r\n // Find all page and layout files\r\n for (const dir of [pagesDir, layoutsDir]) {\r\n if (fs.existsSync(dir)) {\r\n entries.push(...findFiles(dir, /\\.(jsx|tsx|js|ts)$/));\r\n }\r\n }\r\n\r\n if (entries.length === 0) {\r\n return { outputs: [] };\r\n }\r\n\r\n // Create entry points\r\n const entryPoints = {};\r\n for (const entry of entries) {\r\n const relativePath = path.relative(pagesDir, entry);\r\n const name = relativePath.replace(/[\\/\\\\]/g, '_').replace(/\\.(jsx|tsx|js|ts)$/, '');\r\n entryPoints[name] = entry;\r\n }\r\n\r\n try {\r\n const result = await esbuild.build({\r\n entryPoints,\r\n bundle: true,\r\n format: 'esm',\r\n platform: 'node',\r\n outdir: outDir,\r\n minify: false, // Keep server code readable\r\n sourcemap: true,\r\n target: 'node18',\r\n jsx: 'automatic',\r\n jsxImportSource: 'react',\r\n metafile: true,\r\n packages: 'external', // Don't bundle node_modules\r\n loader: {\r\n '.js': 'jsx',\r\n '.jsx': 'jsx',\r\n '.ts': 'tsx',\r\n '.tsx': 'tsx'\r\n }\r\n });\r\n\r\n const outputs = Object.keys(result.metafile.outputs).map(file => ({\r\n file: path.basename(file),\r\n size: result.metafile.outputs[file].bytes\r\n }));\r\n\r\n return { outputs, metafile: result.metafile };\r\n\r\n } catch (error) {\r\n console.error('Server build failed:', error);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Copies public assets to output directory\r\n */\r\nasync function copyPublicAssets(publicDir, outDir) {\r\n if (!fs.existsSync(publicDir)) {\r\n return;\r\n }\r\n\r\n const copyRecursive = (src, dest) => {\r\n const entries = fs.readdirSync(src, { withFileTypes: true });\r\n \r\n ensureDir(dest);\r\n \r\n for (const entry of entries) {\r\n const srcPath = path.join(src, entry.name);\r\n const destPath = path.join(dest, entry.name);\r\n \r\n if (entry.isDirectory()) {\r\n copyRecursive(srcPath, destPath);\r\n } else {\r\n fs.copyFileSync(srcPath, destPath);\r\n }\r\n }\r\n };\r\n\r\n copyRecursive(publicDir, outDir);\r\n}\r\n\r\n/**\r\n * Generates build manifest\r\n */\r\nfunction generateManifest(options) {\r\n const { routes, clientResult, serverResult, config } = options;\r\n\r\n return {\r\n version: '2.0.0',\r\n generatedAt: new Date().toISOString(),\r\n routes: {\r\n pages: routes.pages.map(r => ({\r\n path: r.path,\r\n file: r.filePath,\r\n hasLayout: !!r.layout,\r\n hasLoading: !!r.loading,\r\n hasError: !!r.error\r\n })),\r\n api: routes.api.map(r => ({\r\n path: r.path,\r\n file: r.filePath\r\n }))\r\n },\r\n client: {\r\n chunks: clientResult.outputs || []\r\n },\r\n server: {\r\n modules: serverResult.outputs || []\r\n },\r\n config: {\r\n islands: config.islands.enabled,\r\n rsc: config.rsc.enabled\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Development build with watch mode\r\n */\r\nexport async function buildDev(options) {\r\n const { projectRoot, config, onChange } = options;\r\n\r\n const outDir = config.outDir;\r\n ensureDir(outDir);\r\n\r\n // Use esbuild's watch mode\r\n const ctx = await esbuild.context({\r\n entryPoints: findFiles(config.pagesDir, /\\.(jsx|tsx)$/),\r\n bundle: true,\r\n format: 'esm',\r\n outdir: path.join(outDir, 'dev'),\r\n sourcemap: true,\r\n jsx: 'automatic',\r\n jsxImportSource: 'react',\r\n loader: {\r\n '.js': 'jsx',\r\n '.jsx': 'jsx'\r\n },\r\n plugins: [{\r\n name: 'flexi-watch',\r\n setup(build) {\r\n build.onEnd(result => {\r\n if (result.errors.length === 0) {\r\n onChange?.();\r\n }\r\n });\r\n }\r\n }]\r\n });\r\n\r\n await ctx.watch();\r\n\r\n return ctx;\r\n}\r\n\r\nexport default {\r\n build,\r\n buildDev,\r\n BuildMode\r\n};\r\n","/**\r\n * FlexiReact Utility Functions\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport crypto from 'crypto';\r\n\r\n/**\r\n * Generates a unique hash for cache busting\r\n */\r\nexport function generateHash(content) {\r\n return crypto.createHash('md5').update(content).digest('hex').slice(0, 8);\r\n}\r\n\r\n/**\r\n * Escapes HTML special characters\r\n */\r\nexport function escapeHtml(str) {\r\n const htmlEntities = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": '''\r\n };\r\n return String(str).replace(/[&<>\"']/g, char => htmlEntities[char]);\r\n}\r\n\r\n/**\r\n * Recursively finds all files matching a pattern\r\n */\r\nexport function findFiles(dir, pattern, files = []) {\r\n if (!fs.existsSync(dir)) return files;\r\n \r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n \r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n \r\n if (entry.isDirectory()) {\r\n findFiles(fullPath, pattern, files);\r\n } else if (pattern.test(entry.name)) {\r\n files.push(fullPath);\r\n }\r\n }\r\n \r\n return files;\r\n}\r\n\r\n/**\r\n * Ensures a directory exists\r\n */\r\nexport function ensureDir(dir) {\r\n if (!fs.existsSync(dir)) {\r\n fs.mkdirSync(dir, { recursive: true });\r\n }\r\n}\r\n\r\n/**\r\n * Cleans a directory\r\n */\r\nexport function cleanDir(dir) {\r\n if (fs.existsSync(dir)) {\r\n fs.rmSync(dir, { recursive: true, force: true });\r\n }\r\n fs.mkdirSync(dir, { recursive: true });\r\n}\r\n\r\n/**\r\n * Copies a directory recursively\r\n */\r\nexport function copyDir(src, dest) {\r\n ensureDir(dest);\r\n \r\n const entries = fs.readdirSync(src, { withFileTypes: true });\r\n \r\n for (const entry of entries) {\r\n const srcPath = path.join(src, entry.name);\r\n const destPath = path.join(dest, entry.name);\r\n \r\n if (entry.isDirectory()) {\r\n copyDir(srcPath, destPath);\r\n } else {\r\n fs.copyFileSync(srcPath, destPath);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Debounce function for file watching\r\n */\r\nexport function debounce(fn, delay) {\r\n let timeout;\r\n return (...args) => {\r\n clearTimeout(timeout);\r\n timeout = setTimeout(() => fn(...args), delay);\r\n };\r\n}\r\n\r\n/**\r\n * Formats bytes to human readable string\r\n */\r\nexport function formatBytes(bytes) {\r\n if (bytes === 0) return '0 B';\r\n const k = 1024;\r\n const sizes = ['B', 'KB', 'MB', 'GB'];\r\n const i = Math.floor(Math.log(bytes) / Math.log(k));\r\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\r\n}\r\n\r\n/**\r\n * Formats milliseconds to human readable string\r\n */\r\nexport function formatTime(ms) {\r\n if (ms < 1000) return `${ms}ms`;\r\n return `${(ms / 1000).toFixed(2)}s`;\r\n}\r\n\r\n/**\r\n * Creates a deferred promise\r\n */\r\nexport function createDeferred() {\r\n let resolve, reject;\r\n const promise = new Promise((res, rej) => {\r\n resolve = res;\r\n reject = rej;\r\n });\r\n return { promise, resolve, reject };\r\n}\r\n\r\n/**\r\n * Sleep utility\r\n */\r\nexport function sleep(ms) {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n}\r\n\r\n/**\r\n * Check if a file is a server component (has 'use server' directive)\r\n */\r\nexport function isServerComponent(filePath) {\r\n try {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n const firstLine = content.split('\\n')[0].trim();\r\n return firstLine === \"'use server'\" || firstLine === '\"use server\"';\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Check if a file is a client component (has 'use client' directive)\r\n */\r\nexport function isClientComponent(filePath) {\r\n try {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n const firstLine = content.split('\\n')[0].trim();\r\n return firstLine === \"'use client'\" || firstLine === '\"use client\"';\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Check if a component is an island (has 'use island' directive)\r\n */\r\nexport function isIsland(filePath) {\r\n try {\r\n const content = fs.readFileSync(filePath, 'utf-8');\r\n const firstLine = content.split('\\n')[0].trim();\r\n return firstLine === \"'use island'\" || firstLine === '\"use island\"';\r\n } catch {\r\n return false;\r\n }\r\n}\r\n","/**\r\n * FlexiReact Router v2\r\n * Advanced file-based routing with nested routes, loading, and error boundaries\r\n * \r\n * Supports multiple routing conventions:\r\n * - pages/ : Traditional file-based routing (index.tsx, about.tsx)\r\n * - app/ : Next.js style App Router (page.tsx, layout.tsx)\r\n * - routes/ : FlexiReact v2 routes directory (home.tsx ā /, [slug].tsx ā /:slug)\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport { isServerComponent, isClientComponent, isIsland } from '../utils.js';\r\n\r\n/**\r\n * Route types\r\n */\r\nexport const RouteType = {\r\n PAGE: 'page',\r\n API: 'api',\r\n LAYOUT: 'layout',\r\n LOADING: 'loading',\r\n ERROR: 'error',\r\n NOT_FOUND: 'not-found'\r\n};\r\n\r\n/**\r\n * Builds the complete route tree from all routing directories\r\n */\r\nexport function buildRouteTree(pagesDir, layoutsDir, appDir = null, routesDir = null) {\r\n const projectRoot = path.dirname(pagesDir);\r\n \r\n const routes: {\r\n pages: any[];\r\n api: any[];\r\n layouts: Map<any, any>;\r\n tree: Record<string, any>;\r\n appRoutes: any[];\r\n flexiRoutes: any[];\r\n rootLayout?: string;\r\n } = {\r\n pages: [],\r\n api: [],\r\n layouts: new Map(),\r\n tree: {},\r\n appRoutes: [], // Next.js style app router routes\r\n flexiRoutes: [] // FlexiReact v2 routes/ directory\r\n };\r\n\r\n // 1. Scan routes/ directory (FlexiReact v2 - priority)\r\n const routesDirPath = routesDir || path.join(projectRoot, 'routes');\r\n if (fs.existsSync(routesDirPath)) {\r\n scanRoutesDirectory(routesDirPath, routesDirPath, routes);\r\n }\r\n\r\n // 2. Scan app/ directory (Next.js style App Router)\r\n const appDirPath = appDir || path.join(projectRoot, 'app');\r\n if (fs.existsSync(appDirPath)) {\r\n scanAppDirectory(appDirPath, appDirPath, routes);\r\n }\r\n\r\n // 3. Scan pages/ directory (traditional routing - fallback)\r\n if (fs.existsSync(pagesDir)) {\r\n scanDirectory(pagesDir, pagesDir, routes);\r\n }\r\n \r\n // 4. Scan layouts/ directory\r\n if (fs.existsSync(layoutsDir)) {\r\n scanLayouts(layoutsDir, routes.layouts);\r\n }\r\n\r\n // 5. Check for root layout in app/ directory\r\n const rootLayoutPath = path.join(appDirPath, 'layout.tsx');\r\n const rootLayoutPathJs = path.join(appDirPath, 'layout.jsx');\r\n if (fs.existsSync(rootLayoutPath)) {\r\n routes.rootLayout = rootLayoutPath;\r\n } else if (fs.existsSync(rootLayoutPathJs)) {\r\n routes.rootLayout = rootLayoutPathJs;\r\n }\r\n\r\n // Build route tree for nested routes\r\n routes.tree = buildTree([...routes.flexiRoutes, ...routes.appRoutes, ...routes.pages]);\r\n\r\n return routes;\r\n}\r\n\r\n/**\r\n * Scans routes/ directory for FlexiReact v2 style routing\r\n * \r\n * Convention:\r\n * - home.tsx ā /\r\n * - about.tsx ā /about\r\n * - blog/index.tsx ā /blog\r\n * - blog/[slug].tsx ā /blog/:slug\r\n * - (public)/home.tsx ā / (route group, not in URL)\r\n * - api/hello.ts ā /api/hello (API route)\r\n * - dashboard/layout.tsx ā layout for /dashboard/*\r\n */\r\nfunction scanRoutesDirectory(baseDir, currentDir, routes, parentSegments = [], parentLayout = null, parentMiddleware = null) {\r\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\r\n \r\n // Find special files in current directory\r\n let layoutFile = null;\r\n let loadingFile = null;\r\n let errorFile = null;\r\n let middlewareFile = null;\r\n \r\n for (const entry of entries) {\r\n if (entry.isFile()) {\r\n const name = entry.name.replace(/\\.(jsx|js|tsx|ts)$/, '');\r\n const fullPath = path.join(currentDir, entry.name);\r\n const ext = path.extname(entry.name);\r\n \r\n // Special files\r\n if (name === 'layout') layoutFile = fullPath;\r\n if (name === 'loading') loadingFile = fullPath;\r\n if (name === 'error') errorFile = fullPath;\r\n if (name === '_middleware' || name === 'middleware') middlewareFile = fullPath;\r\n \r\n // Skip special files and non-route files\r\n if (['layout', 'loading', 'error', 'not-found', '_middleware', 'middleware'].includes(name)) continue;\r\n if (!['.tsx', '.jsx', '.ts', '.js'].includes(ext)) continue;\r\n \r\n // API routes (in api/ folder or .ts/.js files in api/)\r\n const relativePath = path.relative(baseDir, currentDir);\r\n const isApiRoute = relativePath.startsWith('api') || relativePath.startsWith('api/');\r\n \r\n if (isApiRoute && ['.ts', '.js'].includes(ext)) {\r\n const apiPath = '/' + [...parentSegments, name === 'index' ? '' : name].filter(Boolean).join('/');\r\n routes.api.push({\r\n type: RouteType.API,\r\n path: apiPath.replace(/\\/+/g, '/') || '/',\r\n filePath: fullPath,\r\n pattern: createRoutePattern(apiPath),\r\n segments: [...parentSegments, name === 'index' ? '' : name].filter(Boolean)\r\n });\r\n continue;\r\n }\r\n \r\n // Page routes\r\n if (['.tsx', '.jsx'].includes(ext)) {\r\n let routePath;\r\n \r\n // home.tsx ā /\r\n if (name === 'home' && parentSegments.length === 0) {\r\n routePath = '/';\r\n }\r\n // index.tsx ā parent path\r\n else if (name === 'index') {\r\n routePath = '/' + parentSegments.join('/') || '/';\r\n }\r\n // [param].tsx ā /:param\r\n else if (name.startsWith('[') && name.endsWith(']')) {\r\n const paramName = name.slice(1, -1);\r\n // Handle catch-all [...slug]\r\n if (paramName.startsWith('...')) {\r\n routePath = '/' + [...parentSegments, '*' + paramName.slice(3)].join('/');\r\n } else {\r\n routePath = '/' + [...parentSegments, ':' + paramName].join('/');\r\n }\r\n }\r\n // regular.tsx ā /regular\r\n else {\r\n routePath = '/' + [...parentSegments, name].join('/');\r\n }\r\n \r\n routes.flexiRoutes.push({\r\n type: RouteType.PAGE,\r\n path: routePath.replace(/\\/+/g, '/'),\r\n filePath: fullPath,\r\n pattern: createRoutePattern(routePath),\r\n segments: routePath.split('/').filter(Boolean),\r\n layout: layoutFile || parentLayout,\r\n loading: loadingFile,\r\n error: errorFile,\r\n middleware: middlewareFile || parentMiddleware,\r\n isFlexiRouter: true,\r\n isServerComponent: isServerComponent(fullPath),\r\n isClientComponent: isClientComponent(fullPath),\r\n isIsland: isIsland(fullPath)\r\n });\r\n }\r\n }\r\n }\r\n \r\n // Recursively scan subdirectories\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const fullPath = path.join(currentDir, entry.name);\r\n const dirName = entry.name;\r\n \r\n // Skip special directories\r\n if (dirName.startsWith('_') || dirName.startsWith('.')) continue;\r\n \r\n // Handle route groups (parentheses) - don't add to URL\r\n const isGroup = dirName.startsWith('(') && dirName.endsWith(')');\r\n \r\n // Handle dynamic segments [param]\r\n let segmentName = dirName;\r\n if (dirName.startsWith('[') && dirName.endsWith(']')) {\r\n const paramName = dirName.slice(1, -1);\r\n if (paramName.startsWith('...')) {\r\n segmentName = '*' + paramName.slice(3);\r\n } else {\r\n segmentName = ':' + paramName;\r\n }\r\n }\r\n \r\n const newSegments = isGroup ? parentSegments : [...parentSegments, segmentName];\r\n const newLayout = layoutFile || parentLayout;\r\n const newMiddleware = middlewareFile || parentMiddleware;\r\n \r\n scanRoutesDirectory(baseDir, fullPath, routes, newSegments, newLayout, newMiddleware);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Scans app directory for Next.js style routing\r\n * Supports: page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx\r\n */\r\nfunction scanAppDirectory(baseDir, currentDir, routes, parentSegments = [], parentLayout = null, parentMiddleware = null) {\r\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\r\n \r\n // Find special files in current directory\r\n const specialFiles: Record<string, string | null> = {\r\n page: null,\r\n layout: null,\r\n loading: null,\r\n error: null,\r\n notFound: null,\r\n template: null,\r\n middleware: null\r\n };\r\n\r\n for (const entry of entries) {\r\n if (entry.isFile()) {\r\n const name = entry.name.replace(/\\.(jsx|js|tsx|ts)$/, '');\r\n const fullPath = path.join(currentDir, entry.name);\r\n \r\n if (name === 'page') specialFiles.page = fullPath;\r\n if (name === 'layout') specialFiles.layout = fullPath;\r\n if (name === 'loading') specialFiles.loading = fullPath;\r\n if (name === 'error') specialFiles.error = fullPath;\r\n if (name === 'not-found') specialFiles.notFound = fullPath;\r\n if (name === 'template') specialFiles.template = fullPath;\r\n if (name === 'middleware' || name === '_middleware') specialFiles.middleware = fullPath;\r\n }\r\n }\r\n\r\n // If there's a page.tsx, create a route\r\n if (specialFiles.page) {\r\n const routePath = '/' + parentSegments.join('/') || '/';\r\n \r\n routes.appRoutes.push({\r\n type: RouteType.PAGE,\r\n path: routePath.replace(/\\/+/g, '/'),\r\n filePath: specialFiles.page,\r\n pattern: createRoutePattern(routePath),\r\n segments: parentSegments,\r\n layout: specialFiles.layout || parentLayout,\r\n loading: specialFiles.loading,\r\n error: specialFiles.error,\r\n notFound: specialFiles.notFound,\r\n template: specialFiles.template,\r\n middleware: specialFiles.middleware || parentMiddleware,\r\n isAppRouter: true,\r\n isServerComponent: isServerComponent(specialFiles.page),\r\n isClientComponent: isClientComponent(specialFiles.page),\r\n isIsland: isIsland(specialFiles.page)\r\n });\r\n }\r\n\r\n // Recursively scan subdirectories\r\n for (const entry of entries) {\r\n if (entry.isDirectory()) {\r\n const fullPath = path.join(currentDir, entry.name);\r\n \r\n // Handle route groups (parentheses) - don't add to URL\r\n const isGroup = entry.name.startsWith('(') && entry.name.endsWith(')');\r\n \r\n // Handle dynamic segments [param]\r\n let segmentName = entry.name;\r\n if (entry.name.startsWith('[') && entry.name.endsWith(']')) {\r\n // Convert [param] to :param\r\n segmentName = ':' + entry.name.slice(1, -1);\r\n // Handle catch-all [...param]\r\n if (entry.name.startsWith('[...')) {\r\n segmentName = '*' + entry.name.slice(4, -1);\r\n }\r\n // Handle optional catch-all [[...param]]\r\n if (entry.name.startsWith('[[...')) {\r\n segmentName = '*' + entry.name.slice(5, -2);\r\n }\r\n }\r\n \r\n const newSegments = isGroup ? parentSegments : [...parentSegments, segmentName];\r\n const newLayout = specialFiles.layout || parentLayout;\r\n const newMiddleware = specialFiles.middleware || parentMiddleware;\r\n \r\n scanAppDirectory(baseDir, fullPath, routes, newSegments, newLayout, newMiddleware);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Scans directory recursively for route files\r\n */\r\nfunction scanDirectory(baseDir, currentDir, routes, parentSegments = []) {\r\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\r\n \r\n // First, find special files in current directory\r\n const specialFiles = {\r\n layout: null,\r\n loading: null,\r\n error: null,\r\n notFound: null\r\n };\r\n\r\n for (const entry of entries) {\r\n if (entry.isFile()) {\r\n const name = entry.name.replace(/\\.(jsx|js|tsx|ts)$/, '');\r\n const fullPath = path.join(currentDir, entry.name);\r\n \r\n if (name === 'layout') specialFiles.layout = fullPath;\r\n if (name === 'loading') specialFiles.loading = fullPath;\r\n if (name === 'error') specialFiles.error = fullPath;\r\n if (name === 'not-found' || name === '404') specialFiles.notFound = fullPath;\r\n }\r\n }\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(currentDir, entry.name);\r\n const relativePath = path.relative(baseDir, fullPath);\r\n\r\n if (entry.isDirectory()) {\r\n // Handle route groups (parentheses)\r\n const isGroup = entry.name.startsWith('(') && entry.name.endsWith(')');\r\n const newSegments = isGroup ? parentSegments : [...parentSegments, entry.name];\r\n \r\n scanDirectory(baseDir, fullPath, routes, newSegments);\r\n } else if (entry.isFile()) {\r\n const ext = path.extname(entry.name);\r\n const baseName = path.basename(entry.name, ext);\r\n \r\n // Skip special files (already processed)\r\n if (['layout', 'loading', 'error', 'not-found', '404'].includes(baseName)) {\r\n continue;\r\n }\r\n \r\n if (['.jsx', '.js', '.tsx', '.ts'].includes(ext)) {\r\n const isApi = relativePath.startsWith('api' + path.sep) || relativePath.startsWith('api/');\r\n \r\n if (isApi && ['.js', '.ts'].includes(ext)) {\r\n routes.api.push(createRoute(fullPath, baseDir, specialFiles, RouteType.API));\r\n } else if (!isApi && ['.jsx', '.tsx'].includes(ext)) {\r\n routes.pages.push(createRoute(fullPath, baseDir, specialFiles, RouteType.PAGE));\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Creates a route object from file path\r\n */\r\nfunction createRoute(filePath, baseDir, specialFiles, type) {\r\n const relativePath = path.relative(baseDir, filePath);\r\n const routePath = filePathToRoute(relativePath);\r\n \r\n return {\r\n type,\r\n path: routePath,\r\n filePath,\r\n pattern: createRoutePattern(routePath),\r\n segments: routePath.split('/').filter(Boolean),\r\n layout: specialFiles.layout,\r\n loading: specialFiles.loading,\r\n error: specialFiles.error,\r\n notFound: specialFiles.notFound,\r\n isServerComponent: isServerComponent(filePath),\r\n isClientComponent: isClientComponent(filePath),\r\n isIsland: isIsland(filePath)\r\n };\r\n}\r\n\r\n/**\r\n * Scans layouts directory\r\n */\r\nfunction scanLayouts(layoutsDir, layoutsMap) {\r\n const entries = fs.readdirSync(layoutsDir, { withFileTypes: true });\r\n \r\n for (const entry of entries) {\r\n if (entry.isFile() && /\\.(jsx|tsx)$/.test(entry.name)) {\r\n const name = entry.name.replace(/\\.(jsx|tsx)$/, '');\r\n layoutsMap.set(name, path.join(layoutsDir, entry.name));\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Converts file path to route path\r\n */\r\nfunction filePathToRoute(filePath) {\r\n let route = filePath.replace(/\\\\/g, '/');\r\n \r\n // Remove extension\r\n route = route.replace(/\\.(jsx|js|tsx|ts)$/, '');\r\n \r\n // Convert [param] to :param\r\n route = route.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, '*$1'); // Catch-all [...slug]\r\n route = route.replace(/\\[([^\\]]+)\\]/g, ':$1');\r\n \r\n // Handle index files\r\n if (route.endsWith('/index')) {\r\n route = route.slice(0, -6) || '/';\r\n } else if (route === 'index') {\r\n route = '/';\r\n }\r\n \r\n // Handle route groups - remove (groupName) from path\r\n route = route.replace(/\\/?\\([^)]+\\)\\/?/g, '/');\r\n \r\n // Ensure leading slash and clean up\r\n if (!route.startsWith('/')) {\r\n route = '/' + route;\r\n }\r\n route = route.replace(/\\/+/g, '/');\r\n \r\n return route;\r\n}\r\n\r\n/**\r\n * Creates regex pattern for route matching\r\n */\r\nfunction createRoutePattern(routePath) {\r\n let pattern = routePath\r\n .replace(/\\*[^/]*/g, '(.*)') // Catch-all\r\n .replace(/:[^/]+/g, '([^/]+)') // Dynamic segments\r\n .replace(/\\//g, '\\\\/');\r\n \r\n return new RegExp(`^${pattern}$`);\r\n}\r\n\r\n/**\r\n * Builds a tree structure for nested routes\r\n */\r\nfunction buildTree(routes) {\r\n const tree = { children: {}, routes: [] };\r\n \r\n for (const route of routes) {\r\n let current = tree;\r\n \r\n for (const segment of route.segments) {\r\n if (!current.children[segment]) {\r\n current.children[segment] = { children: {}, routes: [] };\r\n }\r\n current = current.children[segment];\r\n }\r\n \r\n current.routes.push(route);\r\n }\r\n \r\n return tree;\r\n}\r\n\r\n/**\r\n * Matches URL path against routes\r\n */\r\nexport function matchRoute(urlPath, routes) {\r\n const normalizedPath = urlPath === '' ? '/' : urlPath.split('?')[0];\r\n \r\n for (const route of routes) {\r\n const match = normalizedPath.match(route.pattern);\r\n \r\n if (match) {\r\n const params = extractParams(route.path, match);\r\n return { ...route, params };\r\n }\r\n }\r\n \r\n return null;\r\n}\r\n\r\n/**\r\n * Extracts parameters from route match\r\n */\r\nfunction extractParams(routePath, match) {\r\n const params = {};\r\n const paramNames = [];\r\n \r\n // Extract param names from route path\r\n const paramRegex = /:([^/]+)|\\*([^/]*)/g;\r\n let paramMatch;\r\n \r\n while ((paramMatch = paramRegex.exec(routePath)) !== null) {\r\n paramNames.push(paramMatch[1] || paramMatch[2] || 'splat');\r\n }\r\n \r\n paramNames.forEach((name, index) => {\r\n params[name] = match[index + 1];\r\n });\r\n \r\n return params;\r\n}\r\n\r\n/**\r\n * Finds all layouts that apply to a route\r\n */\r\nexport function findRouteLayouts(route, layoutsMap) {\r\n const layouts = [];\r\n \r\n // Check for segment-based layouts\r\n let currentPath = '';\r\n for (const segment of route.segments) {\r\n currentPath += '/' + segment;\r\n const layoutName = segment;\r\n \r\n if (layoutsMap.has(layoutName)) {\r\n layouts.push({\r\n name: layoutName,\r\n filePath: layoutsMap.get(layoutName)\r\n });\r\n }\r\n }\r\n \r\n // Check for route-specific layout\r\n if (route.layout) {\r\n layouts.push({\r\n name: 'route',\r\n filePath: route.layout\r\n });\r\n }\r\n \r\n // Check for root layout\r\n if (layoutsMap.has('root')) {\r\n layouts.unshift({\r\n name: 'root',\r\n filePath: layoutsMap.get('root')\r\n });\r\n }\r\n \r\n return layouts;\r\n}\r\n\r\nexport default {\r\n buildRouteTree,\r\n matchRoute,\r\n findRouteLayouts,\r\n RouteType\r\n};\r\n"],"mappings":"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","names":["fs","path","fs","path","path","fs","__filename","__dirname","path","build","fs"]}
|
|
@@ -10,22 +10,19 @@ function hydrateIsland(islandId, Component, props) {
|
|
|
10
10
|
if (element.hasAttribute("data-hydrated")) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}));
|
|
20
|
-
} catch (error) {
|
|
21
|
-
console.error(`Failed to hydrate island ${islandId}:`, error);
|
|
22
|
-
try {
|
|
23
|
-
createRoot(element).render(React.createElement(Component, props));
|
|
24
|
-
element.setAttribute("data-hydrated", "true");
|
|
25
|
-
} catch (fallbackError) {
|
|
26
|
-
console.error(`Fallback render also failed for ${islandId}:`, fallbackError);
|
|
13
|
+
hydrateRoot(element, React.createElement(Component, props), {
|
|
14
|
+
onRecoverableError: (error, errorInfo) => {
|
|
15
|
+
console.warn(`[FlexiReact] Hydration mismatch in ${islandId}:`, error);
|
|
16
|
+
if (process.env.NODE_ENV === "development") {
|
|
17
|
+
console.debug("Component stack:", errorInfo.componentStack);
|
|
18
|
+
}
|
|
27
19
|
}
|
|
28
|
-
}
|
|
20
|
+
});
|
|
21
|
+
element.setAttribute("data-hydrated", "true");
|
|
22
|
+
element.dispatchEvent(new CustomEvent("flexi:hydrated", {
|
|
23
|
+
bubbles: true,
|
|
24
|
+
detail: { islandId, props }
|
|
25
|
+
}));
|
|
29
26
|
}
|
|
30
27
|
function hydrateApp(App, props = {}) {
|
|
31
28
|
const root = document.getElementById("root");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../core/client/hydration.ts","../../../core/client/navigation.ts","../../../core/client/islands.ts","../../../core/client/Link.tsx"],"sourcesContent":["/**\r\n * FlexiReact Client Hydration\r\n * Handles selective hydration of islands and full app hydration\r\n */\r\n\r\nimport React from 'react';\r\nimport { hydrateRoot, createRoot } from 'react-dom/client';\r\n\r\n// Extend Window interface for __FLEXI_DATA__\r\ndeclare global {\r\n interface Window {\r\n __FLEXI_DATA__?: {\r\n islands?: any[];\r\n props?: Record<string, any>;\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates a specific island component\r\n */\r\nexport function hydrateIsland(islandId, Component, props) {\r\n const element = document.querySelector(`[data-island=\"${islandId}\"]`);\r\n \r\n if (!element) {\r\n console.warn(`Island element not found: ${islandId}`);\r\n return;\r\n }\r\n\r\n if (element.hasAttribute('data-hydrated')) {\r\n return; // Already hydrated\r\n }\r\n\r\n try {\r\n hydrateRoot(element, React.createElement(Component, props));\r\n element.setAttribute('data-hydrated', 'true');\r\n \r\n // Dispatch custom event\r\n element.dispatchEvent(new CustomEvent('flexi:hydrated', {\r\n bubbles: true,\r\n detail: { islandId, props }\r\n }));\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandId}:`, error);\r\n \r\n // Fallback: try full render instead of hydration\r\n try {\r\n createRoot(element).render(React.createElement(Component, props));\r\n element.setAttribute('data-hydrated', 'true');\r\n } catch (fallbackError) {\r\n console.error(`Fallback render also failed for ${islandId}:`, fallbackError);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates the entire application\r\n */\r\nexport function hydrateApp(App, props = {}) {\r\n const root = document.getElementById('root');\r\n \r\n if (!root) {\r\n console.error('Root element not found');\r\n return;\r\n }\r\n\r\n // Get server-rendered props\r\n const serverProps = window.__FLEXI_DATA__?.props || {};\r\n const mergedProps = { ...serverProps, ...props };\r\n\r\n try {\r\n hydrateRoot(root, React.createElement(App, mergedProps));\r\n } catch (error) {\r\n console.error('Hydration failed, falling back to full render:', error);\r\n createRoot(root).render(React.createElement(App, mergedProps));\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates all islands on the page\r\n */\r\nexport async function hydrateAllIslands(islandModules) {\r\n const islands = document.querySelectorAll('[data-island]');\r\n \r\n for (const element of islands) {\r\n if (element.hasAttribute('data-hydrated')) continue;\r\n \r\n const islandId = element.getAttribute('data-island');\r\n const islandName = element.getAttribute('data-island-name');\r\n const propsJson = element.getAttribute('data-island-props');\r\n \r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = islandModules[islandName];\r\n \r\n if (module) {\r\n hydrateIsland(islandId, module.default || module, props);\r\n }\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Progressive hydration based on visibility\r\n */\r\nexport function setupProgressiveHydration(islandModules) {\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach(async (entry) => {\r\n if (!entry.isIntersecting) return;\r\n \r\n const element = entry.target;\r\n if (element.hasAttribute('data-hydrated')) return;\r\n \r\n const islandName = element.getAttribute('data-island-name');\r\n const islandId = element.getAttribute('data-island');\r\n const propsJson = element.getAttribute('data-island-props');\r\n \r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = await islandModules[islandName]();\r\n \r\n hydrateIsland(islandId, module.default || module, props);\r\n observer.unobserve(element);\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n });\r\n },\r\n { rootMargin: '50px' }\r\n );\r\n\r\n document.querySelectorAll('[data-island]:not([data-hydrated])').forEach(el => {\r\n observer.observe(el);\r\n });\r\n\r\n return observer;\r\n}\r\n\r\nexport default {\r\n hydrateIsland,\r\n hydrateApp,\r\n hydrateAllIslands,\r\n setupProgressiveHydration\r\n};\r\n","/**\r\n * FlexiReact Client Navigation\r\n * Client-side navigation with prefetching\r\n */\r\n\r\nimport React from 'react';\r\n\r\n// Navigation state\r\nconst navigationState: {\r\n listeners: Set<(url: string) => void>;\r\n prefetched: Set<string>;\r\n} = {\r\n listeners: new Set(),\r\n prefetched: new Set()\r\n};\r\n\r\n/**\r\n * Navigates to a new URL\r\n */\r\ninterface NavigateOptions {\r\n replace?: boolean;\r\n scroll?: boolean;\r\n}\r\n\r\nexport function navigate(url: string, options: NavigateOptions = {}) {\r\n const { replace = false, scroll = true } = options;\r\n\r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n\r\n // Dispatch navigation event\r\n window.dispatchEvent(new CustomEvent('flexi:navigate', {\r\n detail: { url, replace, scroll }\r\n }));\r\n\r\n // Scroll to top if needed\r\n if (scroll) {\r\n window.scrollTo(0, 0);\r\n }\r\n\r\n // Notify listeners\r\n navigationState.listeners.forEach(listener => listener(url));\r\n\r\n // Fetch and render new page\r\n return fetchAndRender(url);\r\n}\r\n\r\n/**\r\n * Prefetches a URL for faster navigation\r\n */\r\nexport function prefetch(url) {\r\n if (navigationState.prefetched.has(url)) {\r\n return Promise.resolve();\r\n }\r\n\r\n navigationState.prefetched.add(url);\r\n\r\n // Create a link element for prefetching\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n document.head.appendChild(link);\r\n\r\n return Promise.resolve();\r\n}\r\n\r\n/**\r\n * Fetches and renders a new page\r\n */\r\nasync function fetchAndRender(url) {\r\n try {\r\n const response = await fetch(url, {\r\n headers: {\r\n 'X-Flexi-Navigation': 'true'\r\n }\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Navigation failed: ${response.status}`);\r\n }\r\n\r\n const html = await response.text();\r\n \r\n // Parse the HTML\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n\r\n // Update the page content\r\n const newRoot = doc.getElementById('root');\r\n const currentRoot = document.getElementById('root');\r\n\r\n if (newRoot && currentRoot) {\r\n currentRoot.innerHTML = newRoot.innerHTML;\r\n }\r\n\r\n // Update the title\r\n document.title = doc.title;\r\n\r\n // Update meta tags\r\n updateMetaTags(doc);\r\n\r\n // Re-hydrate islands\r\n window.dispatchEvent(new CustomEvent('flexi:pageload'));\r\n\r\n } catch (error) {\r\n console.error('Navigation error:', error);\r\n // Fallback to full page navigation\r\n window.location.href = url;\r\n }\r\n}\r\n\r\n/**\r\n * Updates meta tags from new document\r\n */\r\nfunction updateMetaTags(doc) {\r\n // Remove old meta tags\r\n document.querySelectorAll('meta[data-flexi]').forEach(el => el.remove());\r\n\r\n // Add new meta tags\r\n doc.querySelectorAll('meta').forEach(meta => {\r\n if (meta.name || meta.property) {\r\n const newMeta = meta.cloneNode(true);\r\n newMeta.setAttribute('data-flexi', 'true');\r\n document.head.appendChild(newMeta);\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Link component for client-side navigation\r\n */\r\nexport function Link({ href, children, prefetch: shouldPrefetch = true, replace = false, className, ...props }) {\r\n const handleClick = (e) => {\r\n // Allow normal navigation for external links or modified clicks\r\n if (\r\n e.ctrlKey ||\r\n e.metaKey ||\r\n e.shiftKey ||\r\n e.button !== 0 ||\r\n href.startsWith('http') ||\r\n href.startsWith('//')\r\n ) {\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n navigate(href, { replace });\r\n };\r\n\r\n const handleMouseEnter = () => {\r\n if (shouldPrefetch) {\r\n prefetch(href);\r\n }\r\n };\r\n\r\n return React.createElement('a', {\r\n href,\r\n onClick: handleClick,\r\n onMouseEnter: handleMouseEnter,\r\n className,\r\n ...props\r\n }, children);\r\n}\r\n\r\n/**\r\n * Hook to listen for navigation events\r\n */\r\nexport function useNavigation() {\r\n const [pathname, setPathname] = React.useState(\r\n typeof window !== 'undefined' ? window.location.pathname : '/'\r\n );\r\n\r\n React.useEffect(() => {\r\n const handleNavigation = (url) => {\r\n setPathname(new URL(url, window.location.origin).pathname);\r\n };\r\n\r\n navigationState.listeners.add(handleNavigation);\r\n\r\n const handlePopState = () => {\r\n setPathname(window.location.pathname);\r\n navigationState.listeners.forEach(listener => listener(window.location.pathname));\r\n };\r\n\r\n window.addEventListener('popstate', handlePopState);\r\n\r\n return () => {\r\n navigationState.listeners.delete(handleNavigation);\r\n window.removeEventListener('popstate', handlePopState);\r\n };\r\n }, []);\r\n\r\n return { pathname, navigate, prefetch };\r\n}\r\n\r\n// Setup popstate listener\r\nif (typeof window !== 'undefined') {\r\n window.addEventListener('popstate', () => {\r\n const url = window.location.pathname + window.location.search;\r\n navigationState.listeners.forEach(listener => listener(url));\r\n });\r\n}\r\n\r\nexport default {\r\n navigate,\r\n prefetch,\r\n Link,\r\n useNavigation\r\n};\r\n","/**\r\n * FlexiReact Client Islands\r\n * Client-side island utilities\r\n */\r\n\r\nimport React from 'react';\r\n\r\n/**\r\n * Hook to check if component is hydrated\r\n */\r\nexport function useIsland() {\r\n const [isHydrated, setIsHydrated] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n setIsHydrated(true);\r\n }, []);\r\n\r\n return { isHydrated };\r\n}\r\n\r\n/**\r\n * Island boundary component\r\n * Wraps interactive components for partial hydration\r\n */\r\nexport function IslandBoundary({ children, fallback = null, name = 'island' }) {\r\n const { isHydrated } = useIsland();\r\n\r\n // On server or before hydration, render children normally\r\n // The server will wrap this in the island marker\r\n if (!isHydrated && fallback) {\r\n return fallback;\r\n }\r\n\r\n return React.createElement('div', {\r\n 'data-island-boundary': name,\r\n children\r\n });\r\n}\r\n\r\n/**\r\n * Creates a lazy-loaded island\r\n */\r\ninterface LazyIslandOptions {\r\n fallback?: React.ReactNode;\r\n name?: string;\r\n}\r\n\r\nexport function createClientIsland(loader: () => Promise<any>, options: LazyIslandOptions = {}) {\r\n const { fallback = null, name = 'lazy-island' } = options;\r\n\r\n return function LazyIsland(props) {\r\n const [Component, setComponent] = React.useState(null);\r\n const [error, setError] = React.useState(null);\r\n\r\n React.useEffect(() => {\r\n loader()\r\n .then(mod => setComponent(() => mod.default || mod))\r\n .catch(err => setError(err));\r\n }, []);\r\n\r\n if (error) {\r\n return React.createElement('div', {\r\n className: 'island-error',\r\n children: `Failed to load ${name}`\r\n });\r\n }\r\n\r\n if (!Component) {\r\n return fallback || React.createElement('div', {\r\n className: 'island-loading',\r\n children: 'Loading...'\r\n });\r\n }\r\n\r\n return React.createElement(Component, props);\r\n };\r\n}\r\n\r\n/**\r\n * Island with interaction trigger\r\n * Only hydrates when user interacts\r\n */\r\nexport function InteractiveIsland({ children, trigger = 'click', fallback }) {\r\n const [shouldHydrate, setShouldHydrate] = React.useState(false);\r\n const ref = React.useRef(null);\r\n\r\n React.useEffect(() => {\r\n const element = ref.current;\r\n if (!element) return;\r\n\r\n const handleInteraction = () => {\r\n setShouldHydrate(true);\r\n };\r\n\r\n element.addEventListener(trigger, handleInteraction, { once: true });\r\n\r\n return () => {\r\n element.removeEventListener(trigger, handleInteraction);\r\n };\r\n }, [trigger]);\r\n\r\n if (!shouldHydrate) {\r\n return React.createElement('div', {\r\n ref,\r\n 'data-interactive-island': 'true',\r\n children: fallback || children\r\n });\r\n }\r\n\r\n return children;\r\n}\r\n\r\n/**\r\n * Media query island\r\n * Only hydrates when media query matches\r\n */\r\nexport function MediaIsland({ children, query, fallback }) {\r\n const [matches, setMatches] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n const mediaQuery = window.matchMedia(query);\r\n setMatches(mediaQuery.matches);\r\n\r\n const handler = (e) => setMatches(e.matches);\r\n mediaQuery.addEventListener('change', handler);\r\n\r\n return () => mediaQuery.removeEventListener('change', handler);\r\n }, [query]);\r\n\r\n if (!matches) {\r\n return fallback || null;\r\n }\r\n\r\n return children;\r\n}\r\n\r\nexport default {\r\n useIsland,\r\n IslandBoundary,\r\n createClientIsland,\r\n InteractiveIsland,\r\n MediaIsland\r\n};\r\n","'use client';\r\n\r\n/**\r\n * FlexiReact Link Component\r\n * Enhanced link with prefetching, client-side navigation, and loading states\r\n */\r\n\r\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\r\n\r\nexport interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\r\n /** The URL to navigate to */\r\n href: string;\r\n /** Prefetch the page on hover/visibility */\r\n prefetch?: boolean | 'hover' | 'viewport';\r\n /** Replace the current history entry instead of pushing */\r\n replace?: boolean;\r\n /** Scroll to top after navigation */\r\n scroll?: boolean;\r\n /** Show loading indicator while navigating */\r\n showLoading?: boolean;\r\n /** Custom loading component */\r\n loadingComponent?: React.ReactNode;\r\n /** Callback when navigation starts */\r\n onNavigationStart?: () => void;\r\n /** Callback when navigation ends */\r\n onNavigationEnd?: () => void;\r\n /** Children */\r\n children: React.ReactNode;\r\n}\r\n\r\n// Prefetch cache to avoid duplicate requests\r\nconst prefetchCache = new Set<string>();\r\n\r\n// Prefetch a URL\r\nasync function prefetchUrl(url: string): Promise<void> {\r\n if (prefetchCache.has(url)) return;\r\n \r\n try {\r\n // Mark as prefetched immediately to prevent duplicate requests\r\n prefetchCache.add(url);\r\n \r\n // Use link preload for better browser optimization\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n link.as = 'document';\r\n document.head.appendChild(link);\r\n \r\n // Also fetch the page to warm the cache\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), 5000);\r\n \r\n await fetch(url, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n signal: controller.signal,\r\n headers: {\r\n 'X-Flexi-Prefetch': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n \r\n clearTimeout(timeoutId);\r\n } catch (error) {\r\n // Remove from cache on error so it can be retried\r\n prefetchCache.delete(url);\r\n }\r\n}\r\n\r\n// Check if URL is internal\r\nfunction isInternalUrl(url: string): boolean {\r\n if (url.startsWith('/')) return true;\r\n if (url.startsWith('#')) return true;\r\n \r\n try {\r\n const parsed = new URL(url, window.location.origin);\r\n return parsed.origin === window.location.origin;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n// Navigate to a URL\r\nfunction navigate(url: string, options: { replace?: boolean; scroll?: boolean } = {}): void {\r\n const { replace = false, scroll = true } = options;\r\n \r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n \r\n // Dispatch popstate event to trigger any listeners\r\n window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));\r\n \r\n // Scroll to top if requested\r\n if (scroll) {\r\n window.scrollTo({ top: 0, behavior: 'smooth' });\r\n }\r\n}\r\n\r\n/**\r\n * Link component with prefetching and client-side navigation\r\n * \r\n * @example\r\n * ```tsx\r\n * import { Link } from '@flexireact/core/client';\r\n * \r\n * // Basic usage\r\n * <Link href=\"/about\">About</Link>\r\n * \r\n * // With prefetch on hover\r\n * <Link href=\"/products\" prefetch=\"hover\">Products</Link>\r\n * \r\n * // With prefetch on viewport visibility\r\n * <Link href=\"/contact\" prefetch=\"viewport\">Contact</Link>\r\n * \r\n * // Replace history instead of push\r\n * <Link href=\"/login\" replace>Login</Link>\r\n * \r\n * // Disable scroll to top\r\n * <Link href=\"/section#anchor\" scroll={false}>Go to section</Link>\r\n * ```\r\n */\r\nexport function Link({\r\n href,\r\n prefetch = true,\r\n replace = false,\r\n scroll = true,\r\n showLoading = false,\r\n loadingComponent,\r\n onNavigationStart,\r\n onNavigationEnd,\r\n children,\r\n className,\r\n onClick,\r\n onMouseEnter,\r\n onFocus,\r\n ...props\r\n}: LinkProps) {\r\n const [isNavigating, setIsNavigating] = useState(false);\r\n const linkRef = useRef<HTMLAnchorElement>(null);\r\n const hasPrefetched = useRef(false);\r\n\r\n // Prefetch on viewport visibility\r\n useEffect(() => {\r\n if (prefetch !== 'viewport' && prefetch !== true) return;\r\n if (!isInternalUrl(href)) return;\r\n if (hasPrefetched.current) return;\r\n\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach((entry) => {\r\n if (entry.isIntersecting) {\r\n prefetchUrl(href);\r\n hasPrefetched.current = true;\r\n observer.disconnect();\r\n }\r\n });\r\n },\r\n { rootMargin: '200px' }\r\n );\r\n\r\n if (linkRef.current) {\r\n observer.observe(linkRef.current);\r\n }\r\n\r\n return () => observer.disconnect();\r\n }, [href, prefetch]);\r\n\r\n // Handle hover prefetch\r\n const handleMouseEnter = useCallback(\r\n (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onMouseEnter?.(e);\r\n \r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onMouseEnter]\r\n );\r\n\r\n // Handle focus prefetch (for keyboard navigation)\r\n const handleFocus = useCallback(\r\n (e: React.FocusEvent<HTMLAnchorElement>) => {\r\n onFocus?.(e);\r\n \r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onFocus]\r\n );\r\n\r\n // Handle click for client-side navigation\r\n const handleClick = useCallback(\r\n async (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onClick?.(e);\r\n \r\n // Don't handle if default was prevented\r\n if (e.defaultPrevented) return;\r\n \r\n // Don't handle if modifier keys are pressed (open in new tab, etc.)\r\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\r\n \r\n // Don't handle external URLs\r\n if (!isInternalUrl(href)) return;\r\n \r\n // Don't handle if target is set\r\n if (props.target && props.target !== '_self') return;\r\n \r\n // Prevent default navigation\r\n e.preventDefault();\r\n \r\n // Start navigation\r\n setIsNavigating(true);\r\n onNavigationStart?.();\r\n \r\n try {\r\n // Fetch the new page\r\n const response = await fetch(href, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n headers: {\r\n 'X-Flexi-Navigation': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n \r\n if (response.ok) {\r\n const html = await response.text();\r\n \r\n // Parse and update the page\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n \r\n // Update title\r\n const newTitle = doc.querySelector('title')?.textContent;\r\n if (newTitle) {\r\n document.title = newTitle;\r\n }\r\n \r\n // Update body content (or specific container)\r\n const newContent = doc.querySelector('#root') || doc.body;\r\n const currentContent = document.querySelector('#root') || document.body;\r\n \r\n if (newContent && currentContent) {\r\n currentContent.innerHTML = newContent.innerHTML;\r\n }\r\n \r\n // Update URL\r\n navigate(href, { replace, scroll });\r\n } else {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n }\r\n } catch (error) {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n } finally {\r\n setIsNavigating(false);\r\n onNavigationEnd?.();\r\n }\r\n },\r\n [href, replace, scroll, onClick, onNavigationStart, onNavigationEnd, props.target]\r\n );\r\n\r\n return (\r\n <a\r\n ref={linkRef}\r\n href={href}\r\n className={className}\r\n onClick={handleClick}\r\n onMouseEnter={handleMouseEnter}\r\n onFocus={handleFocus}\r\n data-prefetch={prefetch}\r\n data-navigating={isNavigating || undefined}\r\n {...props}\r\n >\r\n {showLoading && isNavigating ? (\r\n loadingComponent || (\r\n <span className=\"flexi-link-loading\">\r\n <span className=\"flexi-link-spinner\" />\r\n {children}\r\n </span>\r\n )\r\n ) : (\r\n children\r\n )}\r\n </a>\r\n );\r\n}\r\n\r\n/**\r\n * Programmatic navigation function\r\n * \r\n * @example\r\n * ```tsx\r\n * import { useRouter } from '@flexireact/core/client';\r\n * \r\n * function MyComponent() {\r\n * const router = useRouter();\r\n * \r\n * const handleClick = () => {\r\n * router.push('/dashboard');\r\n * };\r\n * \r\n * return <button onClick={handleClick}>Go to Dashboard</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useRouter() {\r\n return {\r\n push(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: false, scroll: options?.scroll ?? true });\r\n // Trigger page reload for now (full SPA navigation requires more work)\r\n window.location.href = url;\r\n },\r\n \r\n replace(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: true, scroll: options?.scroll ?? true });\r\n window.location.href = url;\r\n },\r\n \r\n back() {\r\n window.history.back();\r\n },\r\n \r\n forward() {\r\n window.history.forward();\r\n },\r\n \r\n prefetch(url: string) {\r\n if (isInternalUrl(url)) {\r\n prefetchUrl(url);\r\n }\r\n },\r\n \r\n refresh() {\r\n window.location.reload();\r\n }\r\n };\r\n}\r\n\r\nexport default Link;\r\n"],"mappings":"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","names":["React","React","navigate","Link","prefetch"]}
|
|
1
|
+
{"version":3,"sources":["../../../core/client/hydration.ts","../../../core/client/navigation.ts","../../../core/client/islands.ts","../../../core/client/Link.tsx"],"sourcesContent":["/**\r\n * FlexiReact Client Hydration\r\n * Handles selective hydration of islands and full app hydration\r\n */\r\n\r\nimport React from 'react';\r\nimport { hydrateRoot, createRoot } from 'react-dom/client';\r\n\r\n// Extend Window interface for __FLEXI_DATA__\r\ndeclare global {\r\n interface Window {\r\n __FLEXI_DATA__?: {\r\n islands?: any[];\r\n props?: Record<string, any>;\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates a specific island component\r\n * React 19: Uses built-in hydration error recovery\r\n */\r\nexport function hydrateIsland(islandId, Component, props) {\r\n const element = document.querySelector(`[data-island=\"${islandId}\"]`);\r\n\r\n if (!element) {\r\n console.warn(`Island element not found: ${islandId}`);\r\n return;\r\n }\r\n\r\n if (element.hasAttribute('data-hydrated')) {\r\n return; // Already hydrated\r\n }\r\n\r\n // React 19: Built-in hydration error recovery\r\n hydrateRoot(element, React.createElement(Component, props), {\r\n onRecoverableError: (error, errorInfo) => {\r\n console.warn(`[FlexiReact] Hydration mismatch in ${islandId}:`, error);\r\n if (process.env.NODE_ENV === 'development') {\r\n console.debug('Component stack:', errorInfo.componentStack);\r\n }\r\n }\r\n });\r\n\r\n element.setAttribute('data-hydrated', 'true');\r\n\r\n // Dispatch custom event\r\n element.dispatchEvent(new CustomEvent('flexi:hydrated', {\r\n bubbles: true,\r\n detail: { islandId, props }\r\n }));\r\n}\r\n\r\n/**\r\n * Hydrates the entire application\r\n */\r\nexport function hydrateApp(App, props = {}) {\r\n const root = document.getElementById('root');\r\n\r\n if (!root) {\r\n console.error('Root element not found');\r\n return;\r\n }\r\n\r\n // Get server-rendered props\r\n const serverProps = window.__FLEXI_DATA__?.props || {};\r\n const mergedProps = { ...serverProps, ...props };\r\n\r\n try {\r\n hydrateRoot(root, React.createElement(App, mergedProps));\r\n } catch (error) {\r\n console.error('Hydration failed, falling back to full render:', error);\r\n createRoot(root).render(React.createElement(App, mergedProps));\r\n }\r\n}\r\n\r\n/**\r\n * Hydrates all islands on the page\r\n */\r\nexport async function hydrateAllIslands(islandModules) {\r\n const islands = document.querySelectorAll('[data-island]');\r\n\r\n for (const element of islands) {\r\n if (element.hasAttribute('data-hydrated')) continue;\r\n\r\n const islandId = element.getAttribute('data-island');\r\n const islandName = element.getAttribute('data-island-name');\r\n const propsJson = element.getAttribute('data-island-props');\r\n\r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = islandModules[islandName];\r\n\r\n if (module) {\r\n hydrateIsland(islandId, module.default || module, props);\r\n }\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Progressive hydration based on visibility\r\n */\r\nexport function setupProgressiveHydration(islandModules) {\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach(async (entry) => {\r\n if (!entry.isIntersecting) return;\r\n\r\n const element = entry.target;\r\n if (element.hasAttribute('data-hydrated')) return;\r\n\r\n const islandName = element.getAttribute('data-island-name');\r\n const islandId = element.getAttribute('data-island');\r\n const propsJson = element.getAttribute('data-island-props');\r\n\r\n try {\r\n const props = propsJson ? JSON.parse(propsJson) : {};\r\n const module = await islandModules[islandName]();\r\n\r\n hydrateIsland(islandId, module.default || module, props);\r\n observer.unobserve(element);\r\n } catch (error) {\r\n console.error(`Failed to hydrate island ${islandName}:`, error);\r\n }\r\n });\r\n },\r\n { rootMargin: '50px' }\r\n );\r\n\r\n document.querySelectorAll('[data-island]:not([data-hydrated])').forEach(el => {\r\n observer.observe(el);\r\n });\r\n\r\n return observer;\r\n}\r\n\r\nexport default {\r\n hydrateIsland,\r\n hydrateApp,\r\n hydrateAllIslands,\r\n setupProgressiveHydration\r\n};\r\n","/**\r\n * FlexiReact Client Navigation\r\n * Client-side navigation with prefetching\r\n */\r\n\r\nimport React from 'react';\r\n\r\n// Navigation state\r\nconst navigationState: {\r\n listeners: Set<(url: string) => void>;\r\n prefetched: Set<string>;\r\n} = {\r\n listeners: new Set(),\r\n prefetched: new Set()\r\n};\r\n\r\n/**\r\n * Navigates to a new URL\r\n */\r\ninterface NavigateOptions {\r\n replace?: boolean;\r\n scroll?: boolean;\r\n}\r\n\r\nexport function navigate(url: string, options: NavigateOptions = {}) {\r\n const { replace = false, scroll = true } = options;\r\n\r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n\r\n // Dispatch navigation event\r\n window.dispatchEvent(new CustomEvent('flexi:navigate', {\r\n detail: { url, replace, scroll }\r\n }));\r\n\r\n // Scroll to top if needed\r\n if (scroll) {\r\n window.scrollTo(0, 0);\r\n }\r\n\r\n // Notify listeners\r\n navigationState.listeners.forEach(listener => listener(url));\r\n\r\n // Fetch and render new page\r\n return fetchAndRender(url);\r\n}\r\n\r\n/**\r\n * Prefetches a URL for faster navigation\r\n */\r\nexport function prefetch(url) {\r\n if (navigationState.prefetched.has(url)) {\r\n return Promise.resolve();\r\n }\r\n\r\n navigationState.prefetched.add(url);\r\n\r\n // Create a link element for prefetching\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n document.head.appendChild(link);\r\n\r\n return Promise.resolve();\r\n}\r\n\r\n/**\r\n * Fetches and renders a new page\r\n */\r\nasync function fetchAndRender(url) {\r\n try {\r\n const response = await fetch(url, {\r\n headers: {\r\n 'X-Flexi-Navigation': 'true'\r\n }\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Navigation failed: ${response.status}`);\r\n }\r\n\r\n const html = await response.text();\r\n \r\n // Parse the HTML\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n\r\n // Update the page content\r\n const newRoot = doc.getElementById('root');\r\n const currentRoot = document.getElementById('root');\r\n\r\n if (newRoot && currentRoot) {\r\n currentRoot.innerHTML = newRoot.innerHTML;\r\n }\r\n\r\n // Update the title\r\n document.title = doc.title;\r\n\r\n // Update meta tags\r\n updateMetaTags(doc);\r\n\r\n // Re-hydrate islands\r\n window.dispatchEvent(new CustomEvent('flexi:pageload'));\r\n\r\n } catch (error) {\r\n console.error('Navigation error:', error);\r\n // Fallback to full page navigation\r\n window.location.href = url;\r\n }\r\n}\r\n\r\n/**\r\n * Updates meta tags from new document\r\n */\r\nfunction updateMetaTags(doc) {\r\n // Remove old meta tags\r\n document.querySelectorAll('meta[data-flexi]').forEach(el => el.remove());\r\n\r\n // Add new meta tags\r\n doc.querySelectorAll('meta').forEach(meta => {\r\n if (meta.name || meta.property) {\r\n const newMeta = meta.cloneNode(true);\r\n newMeta.setAttribute('data-flexi', 'true');\r\n document.head.appendChild(newMeta);\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Link component for client-side navigation\r\n */\r\nexport function Link({ href, children, prefetch: shouldPrefetch = true, replace = false, className, ...props }) {\r\n const handleClick = (e) => {\r\n // Allow normal navigation for external links or modified clicks\r\n if (\r\n e.ctrlKey ||\r\n e.metaKey ||\r\n e.shiftKey ||\r\n e.button !== 0 ||\r\n href.startsWith('http') ||\r\n href.startsWith('//')\r\n ) {\r\n return;\r\n }\r\n\r\n e.preventDefault();\r\n navigate(href, { replace });\r\n };\r\n\r\n const handleMouseEnter = () => {\r\n if (shouldPrefetch) {\r\n prefetch(href);\r\n }\r\n };\r\n\r\n return React.createElement('a', {\r\n href,\r\n onClick: handleClick,\r\n onMouseEnter: handleMouseEnter,\r\n className,\r\n ...props\r\n }, children);\r\n}\r\n\r\n/**\r\n * Hook to listen for navigation events\r\n */\r\nexport function useNavigation() {\r\n const [pathname, setPathname] = React.useState(\r\n typeof window !== 'undefined' ? window.location.pathname : '/'\r\n );\r\n\r\n React.useEffect(() => {\r\n const handleNavigation = (url) => {\r\n setPathname(new URL(url, window.location.origin).pathname);\r\n };\r\n\r\n navigationState.listeners.add(handleNavigation);\r\n\r\n const handlePopState = () => {\r\n setPathname(window.location.pathname);\r\n navigationState.listeners.forEach(listener => listener(window.location.pathname));\r\n };\r\n\r\n window.addEventListener('popstate', handlePopState);\r\n\r\n return () => {\r\n navigationState.listeners.delete(handleNavigation);\r\n window.removeEventListener('popstate', handlePopState);\r\n };\r\n }, []);\r\n\r\n return { pathname, navigate, prefetch };\r\n}\r\n\r\n// Setup popstate listener\r\nif (typeof window !== 'undefined') {\r\n window.addEventListener('popstate', () => {\r\n const url = window.location.pathname + window.location.search;\r\n navigationState.listeners.forEach(listener => listener(url));\r\n });\r\n}\r\n\r\nexport default {\r\n navigate,\r\n prefetch,\r\n Link,\r\n useNavigation\r\n};\r\n","/**\r\n * FlexiReact Client Islands\r\n * Client-side island utilities\r\n */\r\n\r\nimport React from 'react';\r\n\r\n/**\r\n * Hook to check if component is hydrated\r\n */\r\nexport function useIsland() {\r\n const [isHydrated, setIsHydrated] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n setIsHydrated(true);\r\n }, []);\r\n\r\n return { isHydrated };\r\n}\r\n\r\n/**\r\n * Island boundary component\r\n * Wraps interactive components for partial hydration\r\n */\r\nexport function IslandBoundary({ children, fallback = null, name = 'island' }) {\r\n const { isHydrated } = useIsland();\r\n\r\n // On server or before hydration, render children normally\r\n // The server will wrap this in the island marker\r\n if (!isHydrated && fallback) {\r\n return fallback;\r\n }\r\n\r\n return React.createElement('div', {\r\n 'data-island-boundary': name,\r\n children\r\n });\r\n}\r\n\r\n/**\r\n * Creates a lazy-loaded island\r\n */\r\ninterface LazyIslandOptions {\r\n fallback?: React.ReactNode;\r\n name?: string;\r\n}\r\n\r\nexport function createClientIsland(loader: () => Promise<any>, options: LazyIslandOptions = {}) {\r\n const { fallback = null, name = 'lazy-island' } = options;\r\n\r\n return function LazyIsland(props) {\r\n const [Component, setComponent] = React.useState(null);\r\n const [error, setError] = React.useState(null);\r\n\r\n React.useEffect(() => {\r\n loader()\r\n .then(mod => setComponent(() => mod.default || mod))\r\n .catch(err => setError(err));\r\n }, []);\r\n\r\n if (error) {\r\n return React.createElement('div', {\r\n className: 'island-error',\r\n children: `Failed to load ${name}`\r\n });\r\n }\r\n\r\n if (!Component) {\r\n return fallback || React.createElement('div', {\r\n className: 'island-loading',\r\n children: 'Loading...'\r\n });\r\n }\r\n\r\n return React.createElement(Component, props);\r\n };\r\n}\r\n\r\n/**\r\n * Island with interaction trigger\r\n * Only hydrates when user interacts\r\n */\r\nexport function InteractiveIsland({ children, trigger = 'click', fallback }) {\r\n const [shouldHydrate, setShouldHydrate] = React.useState(false);\r\n const ref = React.useRef(null);\r\n\r\n React.useEffect(() => {\r\n const element = ref.current;\r\n if (!element) return;\r\n\r\n const handleInteraction = () => {\r\n setShouldHydrate(true);\r\n };\r\n\r\n element.addEventListener(trigger, handleInteraction, { once: true });\r\n\r\n return () => {\r\n element.removeEventListener(trigger, handleInteraction);\r\n };\r\n }, [trigger]);\r\n\r\n if (!shouldHydrate) {\r\n return React.createElement('div', {\r\n ref,\r\n 'data-interactive-island': 'true',\r\n children: fallback || children\r\n });\r\n }\r\n\r\n return children;\r\n}\r\n\r\n/**\r\n * Media query island\r\n * Only hydrates when media query matches\r\n */\r\nexport function MediaIsland({ children, query, fallback }) {\r\n const [matches, setMatches] = React.useState(false);\r\n\r\n React.useEffect(() => {\r\n const mediaQuery = window.matchMedia(query);\r\n setMatches(mediaQuery.matches);\r\n\r\n const handler = (e) => setMatches(e.matches);\r\n mediaQuery.addEventListener('change', handler);\r\n\r\n return () => mediaQuery.removeEventListener('change', handler);\r\n }, [query]);\r\n\r\n if (!matches) {\r\n return fallback || null;\r\n }\r\n\r\n return children;\r\n}\r\n\r\nexport default {\r\n useIsland,\r\n IslandBoundary,\r\n createClientIsland,\r\n InteractiveIsland,\r\n MediaIsland\r\n};\r\n","'use client';\r\n\r\n/**\r\n * FlexiReact Link Component\r\n * Enhanced link with prefetching, client-side navigation, and loading states\r\n */\r\n\r\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\r\n\r\nexport interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\r\n /** The URL to navigate to */\r\n href: string;\r\n /** Prefetch the page on hover/visibility */\r\n prefetch?: boolean | 'hover' | 'viewport';\r\n /** Replace the current history entry instead of pushing */\r\n replace?: boolean;\r\n /** Scroll to top after navigation */\r\n scroll?: boolean;\r\n /** Show loading indicator while navigating */\r\n showLoading?: boolean;\r\n /** Custom loading component */\r\n loadingComponent?: React.ReactNode;\r\n /** Callback when navigation starts */\r\n onNavigationStart?: () => void;\r\n /** Callback when navigation ends */\r\n onNavigationEnd?: () => void;\r\n /** \r\n * Ref to the anchor element (React 19: ref as regular prop)\r\n * No forwardRef wrapper needed in React 19\r\n */\r\n ref?: React.Ref<HTMLAnchorElement>;\r\n /** Children */\r\n children: React.ReactNode;\r\n}\r\n\r\n// Prefetch cache to avoid duplicate requests\r\nconst prefetchCache = new Set<string>();\r\n\r\n// Prefetch a URL\r\nasync function prefetchUrl(url: string): Promise<void> {\r\n if (prefetchCache.has(url)) return;\r\n\r\n try {\r\n // Mark as prefetched immediately to prevent duplicate requests\r\n prefetchCache.add(url);\r\n\r\n // Use link preload for better browser optimization\r\n const link = document.createElement('link');\r\n link.rel = 'prefetch';\r\n link.href = url;\r\n link.as = 'document';\r\n document.head.appendChild(link);\r\n\r\n // Also fetch the page to warm the cache\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), 5000);\r\n\r\n await fetch(url, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n signal: controller.signal,\r\n headers: {\r\n 'X-Flexi-Prefetch': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n } catch (error) {\r\n // Remove from cache on error so it can be retried\r\n prefetchCache.delete(url);\r\n }\r\n}\r\n\r\n// Check if URL is internal\r\nfunction isInternalUrl(url: string): boolean {\r\n if (url.startsWith('/')) return true;\r\n if (url.startsWith('#')) return true;\r\n\r\n try {\r\n const parsed = new URL(url, window.location.origin);\r\n return parsed.origin === window.location.origin;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n// Navigate to a URL\r\nfunction navigate(url: string, options: { replace?: boolean; scroll?: boolean } = {}): void {\r\n const { replace = false, scroll = true } = options;\r\n\r\n if (replace) {\r\n window.history.replaceState({}, '', url);\r\n } else {\r\n window.history.pushState({}, '', url);\r\n }\r\n\r\n // Dispatch popstate event to trigger any listeners\r\n window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));\r\n\r\n // Scroll to top if requested\r\n if (scroll) {\r\n window.scrollTo({ top: 0, behavior: 'smooth' });\r\n }\r\n}\r\n\r\n/**\r\n * Link component with prefetching and client-side navigation\r\n * \r\n * @example\r\n * ```tsx\r\n * import { Link } from '@flexireact/core/client';\r\n * \r\n * // Basic usage\r\n * <Link href=\"/about\">About</Link>\r\n * \r\n * // With prefetch on hover\r\n * <Link href=\"/products\" prefetch=\"hover\">Products</Link>\r\n * \r\n * // With prefetch on viewport visibility\r\n * <Link href=\"/contact\" prefetch=\"viewport\">Contact</Link>\r\n * \r\n * // Replace history instead of push\r\n * <Link href=\"/login\" replace>Login</Link>\r\n * \r\n * // Disable scroll to top\r\n * <Link href=\"/section#anchor\" scroll={false}>Go to section</Link>\r\n * ```\r\n */\r\nexport function Link({\r\n href,\r\n prefetch = true,\r\n replace = false,\r\n scroll = true,\r\n showLoading = false,\r\n loadingComponent,\r\n onNavigationStart,\r\n onNavigationEnd,\r\n children,\r\n className,\r\n onClick,\r\n onMouseEnter,\r\n onFocus,\r\n ...props\r\n}: LinkProps) {\r\n const [isNavigating, setIsNavigating] = useState(false);\r\n const linkRef = useRef<HTMLAnchorElement>(null);\r\n const hasPrefetched = useRef(false);\r\n\r\n // Prefetch on viewport visibility\r\n useEffect(() => {\r\n if (prefetch !== 'viewport' && prefetch !== true) return;\r\n if (!isInternalUrl(href)) return;\r\n if (hasPrefetched.current) return;\r\n\r\n const observer = new IntersectionObserver(\r\n (entries) => {\r\n entries.forEach((entry) => {\r\n if (entry.isIntersecting) {\r\n prefetchUrl(href);\r\n hasPrefetched.current = true;\r\n observer.disconnect();\r\n }\r\n });\r\n },\r\n { rootMargin: '200px' }\r\n );\r\n\r\n if (linkRef.current) {\r\n observer.observe(linkRef.current);\r\n }\r\n\r\n return () => observer.disconnect();\r\n }, [href, prefetch]);\r\n\r\n // Handle hover prefetch\r\n const handleMouseEnter = useCallback(\r\n (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onMouseEnter?.(e);\r\n\r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onMouseEnter]\r\n );\r\n\r\n // Handle focus prefetch (for keyboard navigation)\r\n const handleFocus = useCallback(\r\n (e: React.FocusEvent<HTMLAnchorElement>) => {\r\n onFocus?.(e);\r\n\r\n if ((prefetch === 'hover' || prefetch === true) && isInternalUrl(href)) {\r\n prefetchUrl(href);\r\n }\r\n },\r\n [href, prefetch, onFocus]\r\n );\r\n\r\n // Handle click for client-side navigation\r\n const handleClick = useCallback(\r\n async (e: React.MouseEvent<HTMLAnchorElement>) => {\r\n onClick?.(e);\r\n\r\n // Don't handle if default was prevented\r\n if (e.defaultPrevented) return;\r\n\r\n // Don't handle if modifier keys are pressed (open in new tab, etc.)\r\n if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\r\n\r\n // Don't handle external URLs\r\n if (!isInternalUrl(href)) return;\r\n\r\n // Don't handle if target is set\r\n if (props.target && props.target !== '_self') return;\r\n\r\n // Prevent default navigation\r\n e.preventDefault();\r\n\r\n // Start navigation\r\n setIsNavigating(true);\r\n onNavigationStart?.();\r\n\r\n try {\r\n // Fetch the new page\r\n const response = await fetch(href, {\r\n method: 'GET',\r\n credentials: 'same-origin',\r\n headers: {\r\n 'X-Flexi-Navigation': '1',\r\n 'Accept': 'text/html'\r\n }\r\n });\r\n\r\n if (response.ok) {\r\n const html = await response.text();\r\n\r\n // Parse and update the page\r\n const parser = new DOMParser();\r\n const doc = parser.parseFromString(html, 'text/html');\r\n\r\n // Update title\r\n const newTitle = doc.querySelector('title')?.textContent;\r\n if (newTitle) {\r\n document.title = newTitle;\r\n }\r\n\r\n // Update body content (or specific container)\r\n const newContent = doc.querySelector('#root') || doc.body;\r\n const currentContent = document.querySelector('#root') || document.body;\r\n\r\n if (newContent && currentContent) {\r\n currentContent.innerHTML = newContent.innerHTML;\r\n }\r\n\r\n // Update URL\r\n navigate(href, { replace, scroll });\r\n } else {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n }\r\n } catch (error) {\r\n // Fallback to regular navigation on error\r\n window.location.href = href;\r\n } finally {\r\n setIsNavigating(false);\r\n onNavigationEnd?.();\r\n }\r\n },\r\n [href, replace, scroll, onClick, onNavigationStart, onNavigationEnd, props.target]\r\n );\r\n\r\n return (\r\n <a\r\n ref={linkRef}\r\n href={href}\r\n className={className}\r\n onClick={handleClick}\r\n onMouseEnter={handleMouseEnter}\r\n onFocus={handleFocus}\r\n data-prefetch={prefetch}\r\n data-navigating={isNavigating || undefined}\r\n {...props}\r\n >\r\n {showLoading && isNavigating ? (\r\n loadingComponent || (\r\n <span className=\"flexi-link-loading\">\r\n <span className=\"flexi-link-spinner\" />\r\n {children}\r\n </span>\r\n )\r\n ) : (\r\n children\r\n )}\r\n </a>\r\n );\r\n}\r\n\r\n/**\r\n * Programmatic navigation function\r\n * \r\n * @example\r\n * ```tsx\r\n * import { useRouter } from '@flexireact/core/client';\r\n * \r\n * function MyComponent() {\r\n * const router = useRouter();\r\n * \r\n * const handleClick = () => {\r\n * router.push('/dashboard');\r\n * };\r\n * \r\n * return <button onClick={handleClick}>Go to Dashboard</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useRouter() {\r\n return {\r\n push(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: false, scroll: options?.scroll ?? true });\r\n // Trigger page reload for now (full SPA navigation requires more work)\r\n window.location.href = url;\r\n },\r\n\r\n replace(url: string, options?: { scroll?: boolean }) {\r\n navigate(url, { replace: true, scroll: options?.scroll ?? true });\r\n window.location.href = url;\r\n },\r\n\r\n back() {\r\n window.history.back();\r\n },\r\n\r\n forward() {\r\n window.history.forward();\r\n },\r\n\r\n prefetch(url: string) {\r\n if (isInternalUrl(url)) {\r\n prefetchUrl(url);\r\n }\r\n },\r\n\r\n refresh() {\r\n window.location.reload();\r\n }\r\n };\r\n}\r\n\r\nexport default Link;\r\n"],"mappings":"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","names":["React","React","navigate","Link","prefetch"]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// core/config.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
5
|
+
var defaultConfig = {
|
|
6
|
+
// Directories
|
|
7
|
+
pagesDir: "pages",
|
|
8
|
+
layoutsDir: "layouts",
|
|
9
|
+
publicDir: "public",
|
|
10
|
+
outDir: ".flexi",
|
|
11
|
+
// Build options
|
|
12
|
+
build: {
|
|
13
|
+
target: "es2022",
|
|
14
|
+
minify: true,
|
|
15
|
+
sourcemap: true,
|
|
16
|
+
splitting: true
|
|
17
|
+
},
|
|
18
|
+
// Server options
|
|
19
|
+
server: {
|
|
20
|
+
port: 3e3,
|
|
21
|
+
host: "localhost"
|
|
22
|
+
},
|
|
23
|
+
// SSG options
|
|
24
|
+
ssg: {
|
|
25
|
+
enabled: false,
|
|
26
|
+
paths: []
|
|
27
|
+
},
|
|
28
|
+
// Islands (partial hydration)
|
|
29
|
+
islands: {
|
|
30
|
+
enabled: true
|
|
31
|
+
},
|
|
32
|
+
// RSC options
|
|
33
|
+
rsc: {
|
|
34
|
+
enabled: true
|
|
35
|
+
},
|
|
36
|
+
// Plugins
|
|
37
|
+
plugins: [],
|
|
38
|
+
// Styles (CSS files to include)
|
|
39
|
+
styles: [],
|
|
40
|
+
// Scripts (JS files to include)
|
|
41
|
+
scripts: [],
|
|
42
|
+
// Favicon path
|
|
43
|
+
favicon: null
|
|
44
|
+
};
|
|
45
|
+
async function loadConfig(projectRoot) {
|
|
46
|
+
const configPathTs = path.join(projectRoot, "flexireact.config.ts");
|
|
47
|
+
const configPathJs = path.join(projectRoot, "flexireact.config.js");
|
|
48
|
+
const configPath = fs.existsSync(configPathTs) ? configPathTs : configPathJs;
|
|
49
|
+
let userConfig = {};
|
|
50
|
+
if (fs.existsSync(configPath)) {
|
|
51
|
+
try {
|
|
52
|
+
const configUrl = pathToFileURL(configPath).href;
|
|
53
|
+
const module = await import(`${configUrl}?t=${Date.now()}`);
|
|
54
|
+
userConfig = module.default || module;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn("Warning: Failed to load flexireact config:", error.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return deepMerge(defaultConfig, userConfig);
|
|
60
|
+
}
|
|
61
|
+
function deepMerge(target, source) {
|
|
62
|
+
const result = { ...target };
|
|
63
|
+
for (const key in source) {
|
|
64
|
+
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
|
|
65
|
+
result[key] = deepMerge(target[key] || {}, source[key]);
|
|
66
|
+
} else {
|
|
67
|
+
result[key] = source[key];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
function resolvePaths(config, projectRoot) {
|
|
73
|
+
return {
|
|
74
|
+
...config,
|
|
75
|
+
pagesDir: path.resolve(projectRoot, config.pagesDir),
|
|
76
|
+
layoutsDir: path.resolve(projectRoot, config.layoutsDir),
|
|
77
|
+
publicDir: path.resolve(projectRoot, config.publicDir),
|
|
78
|
+
outDir: path.resolve(projectRoot, config.outDir)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export {
|
|
82
|
+
defaultConfig,
|
|
83
|
+
loadConfig,
|
|
84
|
+
resolvePaths
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../core/config.ts"],"sourcesContent":["/**\r\n * FlexiReact Configuration System\r\n * Handles loading and merging of configuration from flexireact.config.js\r\n */\r\n\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport { pathToFileURL } from 'url';\r\n\r\n// Default configuration\r\nexport const defaultConfig = {\r\n // Directories\r\n pagesDir: 'pages',\r\n layoutsDir: 'layouts',\r\n publicDir: 'public',\r\n outDir: '.flexi',\r\n \r\n // Build options\r\n build: {\r\n target: 'es2022',\r\n minify: true,\r\n sourcemap: true,\r\n splitting: true\r\n },\r\n \r\n // Server options\r\n server: {\r\n port: 3000,\r\n host: 'localhost'\r\n },\r\n \r\n // SSG options\r\n ssg: {\r\n enabled: false,\r\n paths: []\r\n },\r\n \r\n // Islands (partial hydration)\r\n islands: {\r\n enabled: true\r\n },\r\n \r\n // RSC options\r\n rsc: {\r\n enabled: true\r\n },\r\n \r\n // Plugins\r\n plugins: [],\r\n \r\n // Styles (CSS files to include)\r\n styles: [],\r\n \r\n // Scripts (JS files to include)\r\n scripts: [],\r\n \r\n // Favicon path\r\n favicon: null\r\n};\r\n\r\n/**\r\n * Loads configuration from the project root\r\n * @param {string} projectRoot - Path to project root\r\n * @returns {Object} Merged configuration\r\n */\r\nexport async function loadConfig(projectRoot: string) {\r\n // Try .ts first, then .js\r\n const configPathTs = path.join(projectRoot, 'flexireact.config.ts');\r\n const configPathJs = path.join(projectRoot, 'flexireact.config.js');\r\n const configPath = fs.existsSync(configPathTs) ? configPathTs : configPathJs;\r\n \r\n let userConfig = {};\r\n \r\n if (fs.existsSync(configPath)) {\r\n try {\r\n const configUrl = pathToFileURL(configPath).href;\r\n const module = await import(`${configUrl}?t=${Date.now()}`);\r\n userConfig = module.default || module;\r\n } catch (error: any) {\r\n console.warn('Warning: Failed to load flexireact config:', error.message);\r\n }\r\n }\r\n \r\n // Deep merge configs\r\n return deepMerge(defaultConfig, userConfig);\r\n}\r\n\r\n/**\r\n * Deep merge two objects\r\n */\r\nfunction deepMerge(target, source) {\r\n const result = { ...target };\r\n \r\n for (const key in source) {\r\n if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {\r\n result[key] = deepMerge(target[key] || {}, source[key]);\r\n } else {\r\n result[key] = source[key];\r\n }\r\n }\r\n \r\n return result;\r\n}\r\n\r\n/**\r\n * Resolves all paths in config relative to project root\r\n */\r\nexport function resolvePaths(config, projectRoot) {\r\n return {\r\n ...config,\r\n pagesDir: path.resolve(projectRoot, config.pagesDir),\r\n layoutsDir: path.resolve(projectRoot, config.layoutsDir),\r\n publicDir: path.resolve(projectRoot, config.publicDir),\r\n outDir: path.resolve(projectRoot, config.outDir)\r\n };\r\n}\r\n"],"mappings":";AAKA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAGvB,IAAM,gBAAgB;AAAA;AAAA,EAE3B,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EAGR,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAAA;AAAA,EAGA,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AAAA;AAAA,EAGA,KAAK;AAAA,IACH,SAAS;AAAA,IACT,OAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,SAAS;AAAA,IACP,SAAS;AAAA,EACX;AAAA;AAAA,EAGA,KAAK;AAAA,IACH,SAAS;AAAA,EACX;AAAA;AAAA,EAGA,SAAS,CAAC;AAAA;AAAA,EAGV,QAAQ,CAAC;AAAA;AAAA,EAGT,SAAS,CAAC;AAAA;AAAA,EAGV,SAAS;AACX;AAOA,eAAsB,WAAW,aAAqB;AAEpD,QAAM,eAAe,KAAK,KAAK,aAAa,sBAAsB;AAClE,QAAM,eAAe,KAAK,KAAK,aAAa,sBAAsB;AAClE,QAAM,aAAa,GAAG,WAAW,YAAY,IAAI,eAAe;AAEhE,MAAI,aAAa,CAAC;AAElB,MAAI,GAAG,WAAW,UAAU,GAAG;AAC7B,QAAI;AACF,YAAM,YAAY,cAAc,UAAU,EAAE;AAC5C,YAAM,SAAS,MAAM,OAAO,GAAG,SAAS,MAAM,KAAK,IAAI,CAAC;AACxD,mBAAa,OAAO,WAAW;AAAA,IACjC,SAAS,OAAY;AACnB,cAAQ,KAAK,8CAA8C,MAAM,OAAO;AAAA,IAC1E;AAAA,EACF;AAGA,SAAO,UAAU,eAAe,UAAU;AAC5C;AAKA,SAAS,UAAU,QAAQ,QAAQ;AACjC,QAAM,SAAS,EAAE,GAAG,OAAO;AAE3B,aAAW,OAAO,QAAQ;AACxB,QAAI,OAAO,GAAG,KAAK,OAAO,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,GAAG;AACjF,aAAO,GAAG,IAAI,UAAU,OAAO,GAAG,KAAK,CAAC,GAAG,OAAO,GAAG,CAAC;AAAA,IACxD,OAAO;AACL,aAAO,GAAG,IAAI,OAAO,GAAG;AAAA,IAC1B;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,aAAa,QAAQ,aAAa;AAChD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,KAAK,QAAQ,aAAa,OAAO,QAAQ;AAAA,IACnD,YAAY,KAAK,QAAQ,aAAa,OAAO,UAAU;AAAA,IACvD,WAAW,KAAK,QAAQ,aAAa,OAAO,SAAS;AAAA,IACrD,QAAQ,KAAK,QAAQ,aAAa,OAAO,MAAM;AAAA,EACjD;AACF;","names":[]}
|