Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughExports a new build-time helper Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Build as Vite Build
participant Plugin as Vite Plugin
participant Glob as import.meta.glob
participant Core as `@geajs/core`
participant Runtime as App Runtime
Dev->>Build: run dev/build
Build->>Plugin: load & transform source (detect `router.setPath(...)`)
Plugin->>Plugin: transformFileRoutes(code) -> replaced code + injected import
Plugin->>Glob: emit page & layout glob patterns
Glob-->>Plugin: module loader maps (page: lazy, layout: eager)
Plugin->>Core: call __geaBuildFileRoutes(pageGlob, layoutGlob, basePath)
Core-->>Plugin: RouteMap (constructed mapping/layout groups)
Plugin->>Build: emit transformed module (uses setRoutes(RouteMap))
Runtime->>Runtime: route matching/rendering
Runtime->>Glob: load layout (eager) and page (lazy) modules
Glob-->>Runtime: module instances
Runtime->>Dev: render layout + page
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/gea/tests/router-file-routes.test.ts (1)
148-163: Test name and assertion may not match expected behavior.The test is named "catch-all is placed in root layout children" but the comments on lines 159-161 suggest
'*'is placed outside layouts. The assertion'*' in routes || '*' in (group?.children ?? {})passes either way, which could mask a regression if the behavior changes.Consider either:
- Renaming the test to reflect actual expected behavior
- Tightening the assertion to verify the specific location
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/gea/tests/router-file-routes.test.ts` around lines 148 - 163, The test name and assertion are inconsistent: the spec says "catch-all is placed in root layout children" but the comments say '*' belongs outside layouts and the assertion routes || group.children is too permissive; update the test to either rename it to reflect the actual expected behavior or tighten the assertion to check the exact location. Concretely, in the test using buildFileRoutes, update the it(...) title (the 'catch-all...' string) if you expect '*' to be outside the root layout, or change the assertion to assert.strictEqual('*' in (group?.children ?? {}), true) (or false) to explicitly verify whether routes['/'] (variable group) contains the '*' child; ensure you reference the same RootLayout, routes and group variables so the test fails if behavior changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.changeset/export-build-file-routes.md:
- Around line 1-7: The changeset currently only versions `@geajs/core` but the PR
also changes published output for `@geajs/vite-plugin`; add a corresponding entry
for `@geajs/vite-plugin` (either in this .changeset/export-build-file-routes.md or
a new changeset in the same PR) and ensure the metadata lists both packages with
the same release type (patch/minor as appropriate), and include a short
description like "Include buildFileRoutes export / router.setPath integration
for `@geajs/vite-plugin`" so both packages are coordinated in the release.
In `@examples/router-file-based/package.json`:
- Around line 2-4: The package manifest includes a hardcoded "version" property
which violates the repository policy of letting changesets manage package
versions; remove the "version": "1.0.0" entry from this package.json (or if
there's a justified exception, add a short comment in the repo docs explaining
why this package is excluded from changesets) so that version bumping is handled
exclusively by changesets and not manually.
In `@packages/gea/src/lib/router/file-routes.ts`:
- Around line 112-208: The layout nesting is being computed from route-pattern
strings (with :param/*), causing dynamic layouts to incorrectly claim sibling
static pages; fix by preserving the original filesystem directory paths (e.g.
raw layout dir strings) and compute ancestry from that file-tree representation
before converting segments to route patterns. Update buildNestedRouteMap to pass
both the pattern prefixes (layouts keys) and their corresponding raw filesystem
prefixes into buildLayoutTree, then change buildLayoutTree, belongsToNode, and
buildGroupChildren to use the filesystem-prefix ancestry checks (instead of
isAncestor/isUnderPrefix against route patterns) when deciding parent/child
relationships and page ownership; keep isAncestor/isUnderPrefix only for final
route-matching after the tree is built. Ensure any places referencing
node.prefix still distinguish and carry both node.fsPrefix and
node.patternPrefix (or similar) so layout.tree logic uses fsPrefix and route
matching uses patternPrefix.
In `@packages/vite-plugin-gea/src/transform-file-routes.ts`:
- Around line 16-50: The current transformFileRoutes implementation uses
SET_PATH_RE to patch text and wrongly mutates template literals, strings, and
comments; replace the regex approach by parsing the source into an AST (e.g.,
`@babel/parser` or acorn), walk for CallExpression nodes whose callee is a
MemberExpression with property name "setPath" and whose first argument is a
StringLiteral (this ensures you only transform real calls like
router.setPath('./dir')), then replace that CallExpression AST node with a
CallExpression that calls the IMPORT_MARKER identifier (i.e., buildFileRoutes
wrapper) passing import.meta.glob expressions built from the string argument;
also detect existing import for buildFileRoutes by checking ImportDeclaration
nodes instead of searching the text and insert IMPORT_STMT as an
ImportDeclaration only if missing; update transformFileRoutes to print/generate
code from the modified AST and return it (with map null) so comments, template
literals and other strings remain untouched.
---
Nitpick comments:
In `@packages/gea/tests/router-file-routes.test.ts`:
- Around line 148-163: The test name and assertion are inconsistent: the spec
says "catch-all is placed in root layout children" but the comments say '*'
belongs outside layouts and the assertion routes || group.children is too
permissive; update the test to either rename it to reflect the actual expected
behavior or tighten the assertion to check the exact location. Concretely, in
the test using buildFileRoutes, update the it(...) title (the 'catch-all...'
string) if you expect '*' to be outside the root layout, or change the assertion
to assert.strictEqual('*' in (group?.children ?? {}), true) (or false) to
explicitly verify whether routes['/'] (variable group) contains the '*' child;
ensure you reference the same RootLayout, routes and group variables so the test
fails if behavior changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3a6dd8ef-ba46-4edc-ba13-0c57f936e610
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (28)
.changeset/export-build-file-routes.mdexamples/router-file-based/README.mdexamples/router-file-based/index.htmlexamples/router-file-based/package.jsonexamples/router-file-based/src/App.tsxexamples/router-file-based/src/main.tsexamples/router-file-based/src/pages/[...all]/page.tsxexamples/router-file-based/src/pages/about/page.tsxexamples/router-file-based/src/pages/blog/[slug]/page.tsxexamples/router-file-based/src/pages/blog/page.tsxexamples/router-file-based/src/pages/layout.tsxexamples/router-file-based/src/pages/page.tsxexamples/router-file-based/src/pages/users/[id]/page.tsxexamples/router-file-based/src/pages/users/page.tsxexamples/router-file-based/src/styles.cssexamples/router-file-based/tsconfig.jsonexamples/router-file-based/vite.config.tspackage.jsonpackages/gea/src/index.tspackages/gea/src/lib/router/file-routes.tspackages/gea/src/lib/router/index.tspackages/gea/src/lib/router/router.tspackages/gea/tests/router-file-routes.test.tspackages/vite-plugin-gea/src/index.tspackages/vite-plugin-gea/src/transform-file-routes.tspackages/vite-plugin-gea/tests/transform-file-routes.test.tstests/e2e/playwright.config.tstests/e2e/router-file-based.spec.ts
| "name": "router-file-based-gea", | ||
| "version": "1.0.0", | ||
| "type": "module", |
There was a problem hiding this comment.
Remove manually managed version from this manifest (or document an exception).
Line 3 introduces a hardcoded package version, which conflicts with repository versioning policy.
Suggested adjustment
{
"name": "router-file-based-gea",
- "version": "1.0.0",
+ "private": true,
"type": "module",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "name": "router-file-based-gea", | |
| "version": "1.0.0", | |
| "type": "module", | |
| "name": "router-file-based-gea", | |
| "private": true, | |
| "type": "module", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/router-file-based/package.json` around lines 2 - 4, The package
manifest includes a hardcoded "version" property which violates the repository
policy of letting changesets manage package versions; remove the "version":
"1.0.0" entry from this package.json (or if there's a justified exception, add a
short comment in the repo docs explaining why this package is excluded from
changesets) so that version bumping is handled exclusively by changesets and not
manually.
| function buildLayoutTree(prefixes: string[], layouts: Map<string, any>): LayoutNode[] { | ||
| const nodes: LayoutNode[] = prefixes.map((prefix) => ({ | ||
| prefix, | ||
| layout: layouts.get(prefix)!, | ||
| children: [], | ||
| })) | ||
|
|
||
| const roots: LayoutNode[] = [] | ||
|
|
||
| for (let i = 0; i < nodes.length; i++) { | ||
| const node = nodes[i] | ||
| let parent: LayoutNode | null = null | ||
|
|
||
| for (let j = 0; j < i; j++) { | ||
| const candidate = nodes[j] | ||
| if (isAncestor(candidate.prefix, node.prefix)) { | ||
| // Pick the deepest ancestor | ||
| if (!parent || candidate.prefix.length > parent.prefix.length) { | ||
| parent = candidate | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (parent) { | ||
| parent.children.push(node) | ||
| } else { | ||
| roots.push(node) | ||
| } | ||
| } | ||
|
|
||
| return roots | ||
| } | ||
|
|
||
| // ── RouteMap builder ───────────────────────────────────────────── | ||
|
|
||
| /** True if `route` belongs directly to `node` (not to a child layout). */ | ||
| function belongsToNode(route: string, node: LayoutNode): boolean { | ||
| if (!isUnderPrefix(route, node.prefix)) return false | ||
| // Must not fall under any child layout | ||
| for (const child of node.children) { | ||
| if (isUnderPrefix(route, child.prefix)) return false | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| function buildGroupChildren( | ||
| node: LayoutNode, | ||
| pages: Array<{ route: string; loader: LazyComponent }>, | ||
| ): RouteMap { | ||
| const children: Record<string, any> = {} | ||
|
|
||
| // Pages owned directly by this node | ||
| for (const { route, loader } of pages) { | ||
| if (!belongsToNode(route, node)) continue | ||
| children[relativePath(route, node.prefix)] = loader | ||
| } | ||
|
|
||
| // Nested layout groups | ||
| for (const child of node.children) { | ||
| const key = relativePath(child.prefix, node.prefix) | ||
| const group: RouteGroupConfig = { | ||
| layout: child.layout, | ||
| children: buildGroupChildren(child, pages), | ||
| } | ||
| children[key] = group | ||
| } | ||
|
|
||
| return children as RouteMap | ||
| } | ||
|
|
||
| function buildNestedRouteMap( | ||
| pages: Array<{ route: string; loader: LazyComponent }>, | ||
| layouts: Map<string, any>, | ||
| ): RouteMap { | ||
| const sortedPrefixes = [...layouts.keys()].sort((a, b) => { | ||
| const da = a === '/' ? 0 : a.split('/').filter(Boolean).length | ||
| const db = b === '/' ? 0 : b.split('/').filter(Boolean).length | ||
| return da - db | ||
| }) | ||
|
|
||
| const roots = buildLayoutTree(sortedPrefixes, layouts) | ||
| const result: Record<string, any> = {} | ||
|
|
||
| // Pages not under any root layout node | ||
| for (const { route, loader } of pages) { | ||
| const underRoot = roots.some((r) => isUnderPrefix(route, r.prefix)) | ||
| if (!underRoot) result[route] = loader | ||
| } | ||
|
|
||
| // Layout groups | ||
| for (const root of roots) { | ||
| const group: RouteGroupConfig = { | ||
| layout: root.layout, | ||
| children: buildGroupChildren(root, pages), | ||
| } | ||
| result[root.prefix] = group | ||
| } |
There was a problem hiding this comment.
Build layout nesting from filesystem ancestry, not route-prefix matches.
isUnderPrefix() / isAncestor() are deciding ownership from converted route patterns, so dynamic layouts capture sibling static branches. With pages/[lang]/layout.tsx and sibling pages/about/page.tsx, isUnderPrefix('/about', '/:lang') is true, so Lines 196-198 pull /about under /:lang and Lines 164-166 re-emit it as child '/'. That makes /about render inside the [lang] layout even though it is not in that directory. Lines 121-138 have the same problem for sibling layouts like pages/[lang]/layout.tsx vs pages/en/layout.tsx. Please keep the original filesystem directories around and compute ancestry from the file tree before converting segments to :param / *.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/gea/src/lib/router/file-routes.ts` around lines 112 - 208, The
layout nesting is being computed from route-pattern strings (with :param/*),
causing dynamic layouts to incorrectly claim sibling static pages; fix by
preserving the original filesystem directory paths (e.g. raw layout dir strings)
and compute ancestry from that file-tree representation before converting
segments to route patterns. Update buildNestedRouteMap to pass both the pattern
prefixes (layouts keys) and their corresponding raw filesystem prefixes into
buildLayoutTree, then change buildLayoutTree, belongsToNode, and
buildGroupChildren to use the filesystem-prefix ancestry checks (instead of
isAncestor/isUnderPrefix against route patterns) when deciding parent/child
relationships and page ownership; keep isAncestor/isUnderPrefix only for final
route-matching after the tree is built. Ensure any places referencing
node.prefix still distinguish and carry both node.fsPrefix and
node.patternPrefix (or similar) so layout.tree logic uses fsPrefix and route
matching uses patternPrefix.
| const SET_PATH_RE = /\.setPath\(\s*(['"])((?:\.{1,2}\/)[^'"]*)\1\s*\)/g | ||
| const IMPORT_MARKER = '__geaBuildFileRoutes' | ||
| const IMPORT_STMT = `import { buildFileRoutes as ${IMPORT_MARKER} } from '@geajs/core';\n` | ||
|
|
||
| export function transformFileRoutes(code: string): { code: string; map: null } | null { | ||
| if (!code.includes('.setPath(')) return null | ||
|
|
||
| SET_PATH_RE.lastIndex = 0 | ||
| if (!SET_PATH_RE.test(code)) return null | ||
|
|
||
| SET_PATH_RE.lastIndex = 0 | ||
| const transformed = code.replace(SET_PATH_RE, (match, _quote, dirPath, offset) => { | ||
| // Skip if inside a block/JSDoc comment (line starts with optional whitespace then *) | ||
| const lineStart = code.lastIndexOf('\n', offset) + 1 | ||
| const linePrefix = code.slice(lineStart, offset) | ||
| if (/^\s*\*/.test(linePrefix)) return match | ||
|
|
||
| const pageGlob = JSON.stringify(`${dirPath}/**/page.{tsx,ts,jsx,js}`) | ||
| const layoutGlob = JSON.stringify(`${dirPath}/**/layout.{tsx,ts,jsx,js}`) | ||
| return ( | ||
| `.setRoutes(${IMPORT_MARKER}(` + | ||
| `import.meta.glob(${pageGlob}), ` + | ||
| `import.meta.glob(${layoutGlob}, { eager: true }), ` + | ||
| `${JSON.stringify(dirPath)}` + | ||
| `))` | ||
| ) | ||
| }) | ||
|
|
||
| if (transformed === code) return null | ||
|
|
||
| // Prepend the import only once (it may already be present from a previous | ||
| // HMR pass or if the user imports buildFileRoutes themselves). | ||
| const final = transformed.includes(IMPORT_MARKER) && code.includes(IMPORT_MARKER) | ||
| ? transformed | ||
| : IMPORT_STMT + transformed |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
python - <<'PY'
import pathlib
import re
pattern = re.compile(r"\.setPath\(\s*(['\"])((?:\.{1,2}\/)[^'\"]*)\1\s*\)")
def repl(m: re.Match[str]) -> str:
dir_path = m.group(2)
page_glob = f'"{dir_path}/**/page.{{tsx,ts,jsx,js}}"'
layout_glob = f'"{dir_path}/**/layout.{{tsx,ts,jsx,js}}"'
return (
'.setRoutes(__geaBuildFileRoutes('
f'import.meta.glob({page_glob}), '
f'import.meta.glob({layout_glob}, {{ eager: true }}), '
f'"{dir_path}"'
'))'
)
blog_path = pathlib.Path("examples/router-file-based/src/pages/blog/[slug]/page.tsx")
blog_code = blog_path.read_text()
print("--- regex matches inside the blog page source ---")
for m in pattern.finditer(blog_code):
line = blog_code.count("\n", 0, m.start()) + 1
print(f"blog match at Line {line}: {m.group(0)}")
print("--- transformed blog line containing the helper marker ---")
for i, line in enumerate(pattern.sub(repl, blog_code).splitlines(), 1):
if "__geaBuildFileRoutes" in line:
print(f"Line {i}: {line}")
sample = 'const s = "router.setPath(\'./pages\')"'
print("--- transformed plain-string sample ---")
print(pattern.sub(repl, sample))
PYRepository: dashersw/gea
Length of output: 682
Fix regex-based string matching to use AST-based call detection.
The regex matches .setPath() calls inside template literals and string literals, not just real method calls. The transformation at Line 28 in examples/router-file-based/src/pages/blog/[slug]/page.tsx injects the __geaBuildFileRoutes marker into the template literal content, corrupting displayed text. Additionally, transforming a plain string like const s = "router.setPath('./pages')" produces invalid syntax with unescaped " characters inside the quoted string. The comment guard (line 28-31) only skips JSDoc-style lines starting with *, leaving // and /* ... */ comments unprotected.
Switch to parsing the AST, transforming only actual CallExpression nodes for .setPath(), and detecting/deduplicating the helper import structurally instead of via text search.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/vite-plugin-gea/src/transform-file-routes.ts` around lines 16 - 50,
The current transformFileRoutes implementation uses SET_PATH_RE to patch text
and wrongly mutates template literals, strings, and comments; replace the regex
approach by parsing the source into an AST (e.g., `@babel/parser` or acorn), walk
for CallExpression nodes whose callee is a MemberExpression with property name
"setPath" and whose first argument is a StringLiteral (this ensures you only
transform real calls like router.setPath('./dir')), then replace that
CallExpression AST node with a CallExpression that calls the IMPORT_MARKER
identifier (i.e., buildFileRoutes wrapper) passing import.meta.glob expressions
built from the string argument; also detect existing import for buildFileRoutes
by checking ImportDeclaration nodes instead of searching the text and insert
IMPORT_STMT as an ImportDeclaration only if missing; update transformFileRoutes
to print/generate code from the modified AST and return it (with map null) so
comments, template literals and other strings remain untouched.
7209033 to
f25cc9f
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/gea-tools/package.json (1)
39-42:⚠️ Potential issue | 🟡 MinorMissing test script per coding guidelines.
This package does not define a test script. As per coding guidelines, each package must define its own test script in
package.jsonusingtsxwith the--testflag.Proposed fix to add test script
"scripts": { "compile": "tsc -p ./", - "watch": "tsc -w -p ./" + "watch": "tsc -w -p ./", + "test": "tsx --test ./tests/**/*.test.ts" },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/gea-tools/package.json` around lines 39 - 42, The package.json scripts block is missing a "test" entry; add a "test" script that runs the project's tests with tsx using the --test flag (e.g., add "test": "tsx --test" alongside "compile" and "watch") so the package follows the guideline requiring each package to define its own test script; update the scripts object in package.json accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/gea-tools/package.json`:
- Around line 45-46: The package.json has mismatched major versions between
"vscode-languageclient" and "vscode-languageserver"; pick a matching major
version for both (either upgrade "vscode-languageclient" to ^8.1.0 to match
"vscode-languageserver" or downgrade "vscode-languageserver" to a 6.x range) and
update package.json accordingly; if you choose to upgrade the client to 7/8.x
also update imports from "vscode-languageclient" to "vscode-languageclient/node"
and adjust LanguageClient usage (async start/stop and any API changes) in files
that reference LanguageClient to match the chosen major version.
- Line 44: The package.json in packages/gea-tools contains an unused dependency
"quill": "2.0.2"; remove this dependency entry from
packages/gea-tools/package.json (or if you prefer to relocate it, add the same
dependency to packages/gea/package.json) and then run the package manager to
update lockfiles; ensure tests/files that use quill (e.g.,
tests/quill-editor.test.ts and examples/jira_clone/) resolve from the package
that now declares it (packages/gea) so imports continue to work.
---
Outside diff comments:
In `@packages/gea-tools/package.json`:
- Around line 39-42: The package.json scripts block is missing a "test" entry;
add a "test" script that runs the project's tests with tsx using the --test flag
(e.g., add "test": "tsx --test" alongside "compile" and "watch") so the package
follows the guideline requiring each package to define its own test script;
update the scripts object in package.json accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 80fabf7f-fbcb-44fe-bdd8-42d55849bd95
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (20)
.changeset/export-build-file-routes.mdexamples/router-file-based/README.mdexamples/router-file-based/index.htmlexamples/router-file-based/package.jsonexamples/router-file-based/src/App.tsxexamples/router-file-based/src/main.tsexamples/router-file-based/src/pages/[...all]/page.tsxexamples/router-file-based/src/pages/about/page.tsxexamples/router-file-based/src/pages/blog/[slug]/page.tsxexamples/router-file-based/src/pages/blog/page.tsxexamples/router-file-based/src/pages/layout.tsxexamples/router-file-based/src/pages/page.tsxexamples/router-file-based/src/pages/users/[id]/page.tsxexamples/router-file-based/src/pages/users/page.tsxexamples/router-file-based/src/styles.cssexamples/router-file-based/tsconfig.jsonexamples/router-file-based/vite.config.tspackage.jsonpackages/gea-tools/package.jsonpackages/gea/src/index.ts
✅ Files skipped from review due to trivial changes (11)
- package.json
- examples/router-file-based/src/pages/[...all]/page.tsx
- examples/router-file-based/index.html
- examples/router-file-based/tsconfig.json
- packages/gea/src/index.ts
- examples/router-file-based/src/App.tsx
- examples/router-file-based/src/pages/layout.tsx
- examples/router-file-based/src/styles.css
- examples/router-file-based/src/pages/users/page.tsx
- examples/router-file-based/package.json
- examples/router-file-based/README.md
🚧 Files skipped from review as they are similar to previous changes (5)
- .changeset/export-build-file-routes.md
- examples/router-file-based/vite.config.ts
- examples/router-file-based/src/pages/blog/page.tsx
- examples/router-file-based/src/pages/page.tsx
- examples/router-file-based/src/main.ts
File-Based Router
Adds a file-based routing (like Next.js App Router) convention to Gea, allowing routes to be defined by the filesystem structure instead of manually calling
router.setRoutes(...).How it works
Calling
router.setPath('./pages')in your app is all that's needed. The@geajs/vite-plugintransforms this call at build time intoimport.meta.globcalls — pages are lazy-loaded, layouts are eagerly loaded.File conventions
pages/page.tsx/pages/about/page.tsx/aboutpages/blog/[slug]/page.tsx/blog/:slugpages/users/[id]/page.tsx/users/:idpages/[...all]/page.tsx*(catch-all)pages/layout.tsxLayouts (
layout.tsx) are matched to routes by directory prefix and wrap their subtree automatically via<Outlet />.Changes
@geajs/corebuildFileRoutes(pages, layouts, basePath)runtime helper that maps glob results to aRouteMap, resolving layout nesting by directory prefixbuildFileRoutesis now exported from the public package entry@geajs/vite-plugintransformFileRoutestransform that rewritesrouter.setPath(dir)into the expandedrouter.setRoutes(buildFileRoutes(...))form at compile timeexamples/router-file-basednpm run examples/router-file-basedTests
buildFileRoutes(runtime logic) andtransformFileRoutes(compiler transform)Summary by CodeRabbit
New Features
Documentation
Tests
Chores