@ereo/router-conventions 0.1.6

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 ADDED
@@ -0,0 +1,103 @@
1
+ # @ereo/router-conventions
2
+
3
+ File-based routing conventions for the EreoJS framework. Automatically configure routes based on filename patterns, enabling intuitive and powerful file-based routing.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @ereo/router-conventions
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { parseConvention, applyConventionConfig } from '@ereo/router-conventions';
15
+
16
+ // Parse a filename to extract convention info
17
+ const info = parseConvention('blog/[slug].ssg.tsx');
18
+ // => { basePath: 'blog/[slug]', renderMode: 'ssg', isApi: false, ... }
19
+
20
+ // Apply convention-based configuration to a route
21
+ const config = applyConventionConfig('blog/[slug].ssg.tsx');
22
+ // => { render: { mode: 'ssg', prerender: { enabled: true } }, ... }
23
+ ```
24
+
25
+ ## Convention Patterns
26
+
27
+ Use filename suffixes to configure route behavior:
28
+
29
+ | Pattern | Description |
30
+ |---------|-------------|
31
+ | `*.ssg.tsx` | Static Site Generation (pre-rendered at build time) |
32
+ | `*.server.tsx` | Server-side only (no client JavaScript) |
33
+ | `*.client.tsx` | Client-side rendering only |
34
+ | `*.api.tsx` | API endpoint (JSON response) |
35
+ | `*.rsc.tsx` | React Server Component |
36
+ | `_islands/*.tsx` | Auto-extracted island components |
37
+ | `_layout.tsx` | Nested layout wrapper |
38
+
39
+ ## Examples
40
+
41
+ ```
42
+ app/routes/
43
+ index.tsx # SSR (default)
44
+ about.server.tsx # Server-only, no hydration
45
+ blog/
46
+ index.tsx # SSR blog list
47
+ [slug].ssg.tsx # Static blog posts
48
+ api/
49
+ posts.api.tsx # JSON API endpoint
50
+ _islands/
51
+ Counter.tsx # Auto-hydrated island
52
+ ```
53
+
54
+ ## API
55
+
56
+ ### `parseConvention(filename)`
57
+
58
+ Parse a filename to extract convention information.
59
+
60
+ ```typescript
61
+ const info = parseConvention('posts/[id].ssg.tsx');
62
+ // {
63
+ // basePath: 'posts/[id]',
64
+ // renderMode: 'ssg',
65
+ // isApi: false,
66
+ // isIsland: false,
67
+ // isLayout: false,
68
+ // filename: 'posts/[id].ssg.tsx',
69
+ // extension: '.tsx'
70
+ // }
71
+ ```
72
+
73
+ ### `applyConventionConfig(routePath, explicitConfig?)`
74
+
75
+ Apply convention-based configuration, merging with explicit config.
76
+
77
+ ### `hasConvention(filename)`
78
+
79
+ Check if a filename uses any convention pattern.
80
+
81
+ ### `stripConvention(routePath)`
82
+
83
+ Remove convention suffix from a route path for URL generation.
84
+
85
+ ## Key Features
86
+
87
+ - Automatic render mode detection from filenames
88
+ - Support for SSG, SSR, CSR, API, and RSC modes
89
+ - Island component detection
90
+ - Layout file recognition
91
+ - Seamless integration with EreoJS router
92
+
93
+ ## Documentation
94
+
95
+ For full documentation, visit [https://ereo.dev/docs/router-conventions](https://ereo.dev/docs/router-conventions)
96
+
97
+ ## Part of EreoJS
98
+
99
+ This package is part of the [EreoJS monorepo](https://github.com/anthropics/ereo-js).
100
+
101
+ ## License
102
+
103
+ MIT
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @ereo/router-conventions - File naming convention parser
3
+ *
4
+ * Parses file names to determine route configuration:
5
+ * - [slug].ssg.tsx -> SSG mode
6
+ * - [slug].server.tsx -> Server-only, no hydration
7
+ * - [slug].client.tsx -> CSR only
8
+ * - [slug].api.tsx -> API endpoint
9
+ * - _islands/*.tsx -> Auto-extracted islands
10
+ */
11
+ import type { RenderMode, RouteConfig } from '@ereo/core';
12
+ /** Convention suffixes and their render modes */
13
+ export declare const CONVENTION_SUFFIXES: Record<string, RenderMode | 'api'>;
14
+ /** Parsed convention info from a filename */
15
+ export interface ConventionInfo {
16
+ /** Base route path (without convention suffix) */
17
+ basePath: string;
18
+ /** Detected render mode from convention */
19
+ renderMode?: RenderMode;
20
+ /** Whether this is an API route */
21
+ isApi: boolean;
22
+ /** Whether this is an island component */
23
+ isIsland: boolean;
24
+ /** Whether this is a layout file */
25
+ isLayout: boolean;
26
+ /** Original filename */
27
+ filename: string;
28
+ /** File extension */
29
+ extension: string;
30
+ }
31
+ /**
32
+ * Parse a filename to extract convention information.
33
+ *
34
+ * @example
35
+ * parseConvention('blog/[slug].ssg.tsx')
36
+ * // => { basePath: 'blog/[slug]', renderMode: 'ssg', isApi: false, ... }
37
+ *
38
+ * parseConvention('_islands/Counter.tsx')
39
+ * // => { basePath: '_islands/Counter', isIsland: true, ... }
40
+ */
41
+ export declare function parseConvention(filename: string): ConventionInfo;
42
+ /**
43
+ * Generate route configuration from convention info.
44
+ */
45
+ export declare function conventionToRouteConfig(info: ConventionInfo): Partial<RouteConfig>;
46
+ /**
47
+ * Check if a filename matches a convention pattern.
48
+ */
49
+ export declare function hasConvention(filename: string): boolean;
50
+ /**
51
+ * Get all supported convention patterns for documentation.
52
+ */
53
+ export declare function getConventionPatterns(): string[];
54
+ /**
55
+ * Strip convention suffix from route path for URL generation.
56
+ *
57
+ * @example
58
+ * stripConvention('blog/[slug].ssg') // => 'blog/[slug]'
59
+ */
60
+ export declare function stripConvention(routePath: string): string;
61
+ /**
62
+ * Apply convention-based configuration to a route.
63
+ * This merges convention config with explicit config exports.
64
+ */
65
+ export declare function applyConventionConfig(routePath: string, explicitConfig?: Partial<RouteConfig>): Partial<RouteConfig>;
66
+ //# sourceMappingURL=conventions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conventions.d.ts","sourceRoot":"","sources":["../src/conventions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D,iDAAiD;AACjD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK,CAMlE,CAAC;AAEF,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,mCAAmC;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAsChE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAsClF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAUhD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GACpC,OAAO,CAAC,WAAW,CAAC,CAatB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @ereo/router-conventions - Main exports
3
+ */
4
+ export { parseConvention, conventionToRouteConfig, hasConvention, stripConvention, applyConventionConfig, getConventionPatterns, CONVENTION_SUFFIXES, } from './conventions';
5
+ export type { ConventionInfo, } from './conventions';
6
+ export { integrateConventions } from './integration';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,cAAc,GACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,128 @@
1
+ // @bun
2
+ // src/conventions.ts
3
+ var CONVENTION_SUFFIXES = {
4
+ ".ssg": "ssg",
5
+ ".server": "ssr",
6
+ ".client": "csr",
7
+ ".api": "api",
8
+ ".rsc": "rsc"
9
+ };
10
+ function parseConvention(filename) {
11
+ const lastDot = filename.lastIndexOf(".");
12
+ const extension = lastDot >= 0 ? filename.slice(lastDot) : "";
13
+ const nameWithoutExt = lastDot >= 0 ? filename.slice(0, lastDot) : filename;
14
+ const isIsland = filename.includes("/_islands/") || filename.startsWith("_islands/");
15
+ const isLayout = nameWithoutExt.endsWith("/_layout") || nameWithoutExt === "_layout";
16
+ let basePath = nameWithoutExt;
17
+ let renderMode;
18
+ let isApi = false;
19
+ for (const [suffix, mode] of Object.entries(CONVENTION_SUFFIXES)) {
20
+ if (nameWithoutExt.endsWith(suffix)) {
21
+ basePath = nameWithoutExt.slice(0, -suffix.length);
22
+ if (mode === "api") {
23
+ isApi = true;
24
+ } else {
25
+ renderMode = mode;
26
+ }
27
+ break;
28
+ }
29
+ }
30
+ return {
31
+ basePath,
32
+ renderMode,
33
+ isApi,
34
+ isIsland,
35
+ isLayout,
36
+ filename,
37
+ extension
38
+ };
39
+ }
40
+ function conventionToRouteConfig(info) {
41
+ const config = {};
42
+ if (info.renderMode) {
43
+ config.render = {
44
+ mode: info.renderMode,
45
+ streaming: { enabled: info.renderMode === "ssr" }
46
+ };
47
+ if (info.renderMode === "ssg") {
48
+ config.render.prerender = {
49
+ enabled: true,
50
+ fallback: "blocking"
51
+ };
52
+ }
53
+ if (info.renderMode === "ssr" && info.filename.includes(".server.")) {
54
+ config.islands = { disabled: true, defaultStrategy: "none" };
55
+ }
56
+ }
57
+ if (info.isApi) {
58
+ config.render = { mode: "json" };
59
+ }
60
+ if (info.isIsland) {
61
+ config.islands = {
62
+ defaultStrategy: "load",
63
+ ...config.islands
64
+ };
65
+ }
66
+ return config;
67
+ }
68
+ function hasConvention(filename) {
69
+ const info = parseConvention(filename);
70
+ return !!info.renderMode || info.isApi || info.isIsland;
71
+ }
72
+ function getConventionPatterns() {
73
+ return [
74
+ "*.ssg.tsx - Static Site Generation (pre-rendered at build)",
75
+ "*.server.tsx - Server-side only (no client JavaScript)",
76
+ "*.client.tsx - Client-side rendering only",
77
+ "*.api.tsx - API endpoint (JSON response)",
78
+ "*.rsc.tsx - React Server Component",
79
+ "_islands/*.tsx - Auto-extracted island components",
80
+ "_layout.tsx - Nested layout wrapper"
81
+ ];
82
+ }
83
+ function stripConvention(routePath) {
84
+ for (const suffix of Object.keys(CONVENTION_SUFFIXES)) {
85
+ if (routePath.endsWith(suffix)) {
86
+ return routePath.slice(0, -suffix.length);
87
+ }
88
+ }
89
+ return routePath;
90
+ }
91
+ function applyConventionConfig(routePath, explicitConfig) {
92
+ const info = parseConvention(routePath);
93
+ const conventionConfig = conventionToRouteConfig(info);
94
+ return {
95
+ ...conventionConfig,
96
+ ...explicitConfig,
97
+ render: explicitConfig?.render ?? conventionConfig.render,
98
+ islands: explicitConfig?.islands ?? conventionConfig.islands,
99
+ cache: explicitConfig?.cache ?? conventionConfig.cache
100
+ };
101
+ }
102
+ // src/integration.ts
103
+ function integrateConventions(routes, options = {}) {
104
+ const { enabled = true } = options;
105
+ if (!enabled) {
106
+ return routes;
107
+ }
108
+ return routes.map((route) => {
109
+ const conventionConfig = applyConventionConfig(route.file, route.config);
110
+ return {
111
+ ...route,
112
+ config: {
113
+ ...route.config,
114
+ ...conventionConfig
115
+ }
116
+ };
117
+ });
118
+ }
119
+ export {
120
+ stripConvention,
121
+ parseConvention,
122
+ integrateConventions,
123
+ hasConvention,
124
+ getConventionPatterns,
125
+ conventionToRouteConfig,
126
+ applyConventionConfig,
127
+ CONVENTION_SUFFIXES
128
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @ereo/router-conventions - Integration with file router
3
+ *
4
+ * Integrates convention-based routing with the file router system.
5
+ */
6
+ import type { Route } from '@ereo/core';
7
+ /**
8
+ * Options for integrating conventions with the router.
9
+ */
10
+ export interface ConventionIntegrationOptions {
11
+ /** Enable convention parsing (default: true) */
12
+ enabled?: boolean;
13
+ /** Custom convention suffixes to add */
14
+ customSuffixes?: Record<string, string>;
15
+ /** Directories to scan for islands */
16
+ islandDirs?: string[];
17
+ }
18
+ /**
19
+ * Integrate conventions with discovered routes.
20
+ * This transforms route configs based on filename conventions.
21
+ */
22
+ export declare function integrateConventions(routes: Route[], options?: ConventionIntegrationOptions): Route[];
23
+ /**
24
+ * Generate route ID from convention info.
25
+ * Strips convention suffixes for cleaner route IDs.
26
+ */
27
+ export declare function generateRouteId(filePath: string): string;
28
+ /**
29
+ * Check if a route should be treated as an API route.
30
+ */
31
+ export declare function isApiRoute(filePath: string): boolean;
32
+ /**
33
+ * Check if a route should be treated as an island component.
34
+ */
35
+ export declare function isIslandComponent(filePath: string): boolean;
36
+ /**
37
+ * Get the effective render mode for a route file.
38
+ */
39
+ export declare function getEffectiveRenderMode(filePath: string, explicitMode?: string): string;
40
+ //# sourceMappingURL=integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wCAAwC;IACxC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,GAAE,4BAAiC,GACzC,KAAK,EAAE,CAkBT;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAaR"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@ereo/router-conventions",
3
+ "version": "0.1.6",
4
+ "license": "MIT",
5
+ "author": "Ereo Team",
6
+ "homepage": "https://ereo.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/ereojs/ereo.git",
10
+ "directory": "packages/router-conventions"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/ereojs/ereo/issues"
14
+ },
15
+ "description": "File-based routing conventions for EreoJS framework",
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "scripts": {
29
+ "build": "bun build ./src/index.ts --outdir ./dist --target bun --external @ereo/core --external @ereo/router && bun run build:types",
30
+ "build:types": "tsc --emitDeclarationOnly --outDir dist",
31
+ "dev": "bun build ./src/index.ts --outdir ./dist --target bun --watch",
32
+ "test": "bun test",
33
+ "typecheck": "tsc --noEmit"
34
+ },
35
+ "dependencies": {
36
+ "@ereo/core": "workspace:*",
37
+ "@ereo/router": "workspace:*"
38
+ },
39
+ "devDependencies": {
40
+ "@types/bun": "^1.1.0",
41
+ "typescript": "^5.4.0"
42
+ }
43
+ }