@moriajs/core 0.1.1 → 0.2.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/.turbo/turbo-build.log +1 -1
- package/dist/app.d.ts +4 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +23 -3
- package/dist/app.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts +80 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +208 -0
- package/dist/router.js.map +1 -0
- package/dist/vite.d.ts +24 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +63 -0
- package/dist/vite.js.map +1 -0
- package/package.json +8 -2
- package/src/app.ts +28 -3
- package/src/config.ts +14 -0
- package/src/index.ts +4 -1
- package/src/router.ts +278 -0
- package/src/vite.ts +85 -0
package/.turbo/turbo-build.log
CHANGED
package/dist/app.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type FastifyInstance, type FastifyServerOptions } from 'fastify';
|
|
2
2
|
import { type MoriaConfig } from './config.js';
|
|
3
3
|
import { type MoriaPlugin } from './plugins.js';
|
|
4
|
+
import type { ViteDevServer } from 'vite';
|
|
4
5
|
/**
|
|
5
6
|
* Options for creating a MoriaJS application.
|
|
6
7
|
*/
|
|
@@ -17,6 +18,8 @@ export interface MoriaAppOptions {
|
|
|
17
18
|
export interface MoriaApp {
|
|
18
19
|
/** The underlying Fastify instance */
|
|
19
20
|
server: FastifyInstance;
|
|
21
|
+
/** The Vite dev server (only in development mode) */
|
|
22
|
+
vite?: ViteDevServer;
|
|
20
23
|
/** Register a MoriaJS plugin */
|
|
21
24
|
use: (plugin: MoriaPlugin) => Promise<void>;
|
|
22
25
|
/** Start the server */
|
|
@@ -34,7 +37,7 @@ export interface MoriaApp {
|
|
|
34
37
|
* ```ts
|
|
35
38
|
* import { createApp } from '@moriajs/core';
|
|
36
39
|
*
|
|
37
|
-
* const app = await createApp();
|
|
40
|
+
* const app = await createApp({ config: { mode: 'development' } });
|
|
38
41
|
* await app.listen({ port: 3000 });
|
|
39
42
|
* ```
|
|
40
43
|
*/
|
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAOnF,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9B,wCAAwC;IACxC,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACzC;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACrB,sCAAsC;IACtC,MAAM,EAAE,eAAe,CAAC;IACxB,qDAAqD;IACrD,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,gCAAgC;IAChC,GAAG,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,uBAAuB;IACvB,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxE,sBAAsB;IACtB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4EhF"}
|
package/dist/app.js
CHANGED
|
@@ -3,6 +3,10 @@ import cors from '@fastify/cors';
|
|
|
3
3
|
import cookie from '@fastify/cookie';
|
|
4
4
|
import compress from '@fastify/compress';
|
|
5
5
|
import helmet from '@fastify/helmet';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import { createViteDevMiddleware, serveProductionAssets } from './vite.js';
|
|
9
|
+
import { registerRoutes } from './router.js';
|
|
6
10
|
/**
|
|
7
11
|
* Create a new MoriaJS application.
|
|
8
12
|
*
|
|
@@ -10,19 +14,21 @@ import helmet from '@fastify/helmet';
|
|
|
10
14
|
* ```ts
|
|
11
15
|
* import { createApp } from '@moriajs/core';
|
|
12
16
|
*
|
|
13
|
-
* const app = await createApp();
|
|
17
|
+
* const app = await createApp({ config: { mode: 'development' } });
|
|
14
18
|
* await app.listen({ port: 3000 });
|
|
15
19
|
* ```
|
|
16
20
|
*/
|
|
17
21
|
export async function createApp(options = {}) {
|
|
18
22
|
const config = options.config ?? {};
|
|
23
|
+
const mode = config.mode ?? (process.env.NODE_ENV === 'production' ? 'production' : 'development');
|
|
24
|
+
const rootDir = config.rootDir ?? process.cwd();
|
|
19
25
|
const port = config.server?.port ?? 3000;
|
|
20
26
|
const host = config.server?.host ?? '0.0.0.0';
|
|
21
27
|
// Create Fastify instance with sensible defaults
|
|
22
28
|
const server = Fastify({
|
|
23
29
|
logger: {
|
|
24
30
|
level: config.server?.logLevel ?? 'info',
|
|
25
|
-
transport:
|
|
31
|
+
transport: mode !== 'production'
|
|
26
32
|
? { target: 'pino-pretty', options: { colorize: true } }
|
|
27
33
|
: undefined,
|
|
28
34
|
},
|
|
@@ -37,14 +43,28 @@ export async function createApp(options = {}) {
|
|
|
37
43
|
await server.register(compress);
|
|
38
44
|
await server.register(helmet, {
|
|
39
45
|
// Relax CSP in development for Vite HMR
|
|
40
|
-
contentSecurityPolicy:
|
|
46
|
+
contentSecurityPolicy: mode === 'production',
|
|
41
47
|
});
|
|
42
48
|
// Health check route
|
|
43
49
|
server.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
|
|
50
|
+
// ─── Vite Integration ────────────────────────────────────
|
|
51
|
+
let vite;
|
|
52
|
+
if (mode === 'development') {
|
|
53
|
+
vite = await createViteDevMiddleware(server, { ...config, rootDir });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
await serveProductionAssets(server, { ...config, rootDir });
|
|
57
|
+
}
|
|
58
|
+
// ─── File-Based Routing ──────────────────────────────────
|
|
59
|
+
const routesDir = path.resolve(rootDir, config.routes?.dir ?? 'src/routes');
|
|
60
|
+
if (fs.existsSync(routesDir)) {
|
|
61
|
+
await registerRoutes(server, routesDir, { mode, config });
|
|
62
|
+
}
|
|
44
63
|
// Plugin registry
|
|
45
64
|
const plugins = [];
|
|
46
65
|
const app = {
|
|
47
66
|
server,
|
|
67
|
+
vite,
|
|
48
68
|
async use(plugin) {
|
|
49
69
|
plugins.push(plugin);
|
|
50
70
|
await plugin.register({ server, config });
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,OAA4D,MAAM,SAAS,CAAC;AACnF,OAAO,IAAI,MAAM,eAAe,CAAC;AACjC,OAAO,MAAM,MAAM,iBAAiB,CAAC;AACrC,OAAO,QAAQ,MAAM,mBAAmB,CAAC;AACzC,OAAO,MAAM,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* Used in `moria.config.ts` files in user projects.
|
|
4
4
|
*/
|
|
5
5
|
export interface MoriaConfig {
|
|
6
|
+
/** Application mode */
|
|
7
|
+
mode?: 'development' | 'production';
|
|
8
|
+
/** Project root directory (auto-detected if not set) */
|
|
9
|
+
rootDir?: string;
|
|
6
10
|
/** Server configuration */
|
|
7
11
|
server?: {
|
|
8
12
|
/** Port to listen on (default: 3000) */
|
|
@@ -41,6 +45,13 @@ export interface MoriaConfig {
|
|
|
41
45
|
vite?: {
|
|
42
46
|
/** Path to Vite config file */
|
|
43
47
|
configFile?: string;
|
|
48
|
+
/** Client entry point (default: '/src/entry-client.ts') */
|
|
49
|
+
clientEntry?: string;
|
|
50
|
+
};
|
|
51
|
+
/** File-based routing configuration */
|
|
52
|
+
routes?: {
|
|
53
|
+
/** Routes directory relative to rootDir (default: 'src/routes') */
|
|
54
|
+
dir?: string;
|
|
44
55
|
};
|
|
45
56
|
}
|
|
46
57
|
/**
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB,2BAA2B;IAC3B,MAAM,CAAC,EAAE;QACL,wCAAwC;QACxC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,2CAA2C;QAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,kCAAkC;QAClC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;QAC9E,yBAAyB;QACzB,IAAI,CAAC,EAAE;YACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;YACrC,WAAW,CAAC,EAAE,OAAO,CAAC;SACzB,CAAC;KACL,CAAC;IAEF,6BAA6B;IAC7B,QAAQ,CAAC,EAAE;QACP,kDAAkD;QAClD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,qBAAqB;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,+CAA+C;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IAEF,mCAAmC;IACnC,IAAI,CAAC,EAAE;QACH,qBAAqB;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,2CAA2C;QAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gCAAgC;QAChC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,yCAAyC;QACzC,aAAa,CAAC,EAAE,OAAO,CAAC;KAC3B,CAAC;IAEF,iCAAiC;IACjC,IAAI,CAAC,EAAE;QACH,+BAA+B;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB,uBAAuB;IACvB,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IAEpC,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,2BAA2B;IAC3B,MAAM,CAAC,EAAE;QACL,wCAAwC;QACxC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,2CAA2C;QAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,kCAAkC;QAClC,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;QAC9E,yBAAyB;QACzB,IAAI,CAAC,EAAE;YACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;YACrC,WAAW,CAAC,EAAE,OAAO,CAAC;SACzB,CAAC;KACL,CAAC;IAEF,6BAA6B;IAC7B,QAAQ,CAAC,EAAE;QACP,kDAAkD;QAClD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,qBAAqB;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,+CAA+C;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IAEF,mCAAmC;IACnC,IAAI,CAAC,EAAE;QACH,qBAAqB;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,2CAA2C;QAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gCAAgC;QAChC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,yCAAyC;QACzC,aAAa,CAAC,EAAE,OAAO,CAAC;KAC3B,CAAC;IAEF,iCAAiC;IACjC,IAAI,CAAC,EAAE;QACH,+BAA+B;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,2DAA2D;QAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF,uCAAuC;IACvC,MAAM,CAAC,EAAE;QACL,mEAAmE;QACnE,GAAG,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACL;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAE7D"}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA+DA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB;IAC5C,OAAO,MAAM,CAAC;AAClB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
* @moriajs/core
|
|
3
3
|
*
|
|
4
4
|
* Framework core: Fastify server factory with sensible defaults,
|
|
5
|
-
* plugin registration, and
|
|
5
|
+
* plugin registration, Vite integration, and file-based routing.
|
|
6
6
|
*/
|
|
7
7
|
export { createApp } from './app.js';
|
|
8
8
|
export { defineConfig } from './config.js';
|
|
9
9
|
export { defineMoriaPlugin } from './plugins.js';
|
|
10
|
+
export { createViteDevMiddleware, serveProductionAssets, getHtmlScripts } from './vite.js';
|
|
11
|
+
export { scanRoutes, registerRoutes, filePathToUrlPath } from './router.js';
|
|
10
12
|
export type { MoriaApp, MoriaAppOptions } from './app.js';
|
|
11
13
|
export type { MoriaConfig } from './config.js';
|
|
12
14
|
export type { MoriaPlugin, MoriaPluginContext } from './plugins.js';
|
|
15
|
+
export type { RouteEntry, RouteHandler } from './router.js';
|
|
13
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE5E,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* @moriajs/core
|
|
3
3
|
*
|
|
4
4
|
* Framework core: Fastify server factory with sensible defaults,
|
|
5
|
-
* plugin registration, and
|
|
5
|
+
* plugin registration, Vite integration, and file-based routing.
|
|
6
6
|
*/
|
|
7
7
|
export { createApp } from './app.js';
|
|
8
8
|
export { defineConfig } from './config.js';
|
|
9
9
|
export { defineMoriaPlugin } from './plugins.js';
|
|
10
|
+
export { createViteDevMiddleware, serveProductionAssets, getHtmlScripts } from './vite.js';
|
|
11
|
+
export { scanRoutes, registerRoutes, filePathToUrlPath } from './router.js';
|
|
10
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based routing for MoriaJS.
|
|
3
|
+
*
|
|
4
|
+
* Scans `src/routes/` for route files and auto-registers them with Fastify.
|
|
5
|
+
*
|
|
6
|
+
* Convention:
|
|
7
|
+
* src/routes/api/hello.ts → GET /api/hello (API handler)
|
|
8
|
+
* src/routes/api/users/[id].ts → GET /api/users/:id (API handler)
|
|
9
|
+
* src/routes/pages/index.ts → GET / (SSR page)
|
|
10
|
+
* src/routes/pages/about.ts → GET /about (SSR page)
|
|
11
|
+
*
|
|
12
|
+
* API routes export named HTTP method functions:
|
|
13
|
+
* export function GET(request, reply) { ... }
|
|
14
|
+
*
|
|
15
|
+
* Page routes export a Mithril component + optional data loader:
|
|
16
|
+
* export default { view() { return m('h1', 'Hello') } }
|
|
17
|
+
* export async function getServerData(request) { return { user: ... } }
|
|
18
|
+
*/
|
|
19
|
+
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
20
|
+
import type { MoriaConfig } from './config.js';
|
|
21
|
+
/** Supported HTTP methods in route files. */
|
|
22
|
+
declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
|
|
23
|
+
type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
24
|
+
/** Handler function exported from a route file. */
|
|
25
|
+
export type RouteHandler = (request: FastifyRequest, reply: FastifyReply) => unknown | Promise<unknown>;
|
|
26
|
+
/** Data loader for page routes — runs on server before render. */
|
|
27
|
+
export type GetServerData = (request: FastifyRequest) => unknown | Promise<unknown>;
|
|
28
|
+
/** Discovered route entry. */
|
|
29
|
+
export interface RouteEntry {
|
|
30
|
+
/** File path relative to routes dir */
|
|
31
|
+
filePath: string;
|
|
32
|
+
/** URL path pattern (e.g., /api/users/:id) */
|
|
33
|
+
urlPath: string;
|
|
34
|
+
/** Route type: 'api' or 'page' */
|
|
35
|
+
type: 'api' | 'page';
|
|
36
|
+
/** HTTP method → handler map (API routes) */
|
|
37
|
+
methods: Partial<Record<Lowercase<HttpMethod>, RouteHandler>>;
|
|
38
|
+
/** Mithril component (page routes only) */
|
|
39
|
+
component?: unknown;
|
|
40
|
+
/** Server data loader (page routes only) */
|
|
41
|
+
getServerData?: GetServerData;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options for route registration.
|
|
45
|
+
*/
|
|
46
|
+
export interface RegisterRoutesOptions {
|
|
47
|
+
/** Application mode */
|
|
48
|
+
mode?: 'development' | 'production';
|
|
49
|
+
/** MoriaJS config for renderer options */
|
|
50
|
+
config?: Partial<MoriaConfig>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Convert a file path to a URL path.
|
|
54
|
+
*
|
|
55
|
+
* - Strips file extension
|
|
56
|
+
* - Converts `[param]` → `:param`
|
|
57
|
+
* - Converts `[...slug]` → `*`
|
|
58
|
+
* - Converts `index` → `/`
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* filePathToUrlPath('api/users/[id].ts') → '/api/users/:id'
|
|
62
|
+
* filePathToUrlPath('pages/index.ts') → '/'
|
|
63
|
+
* filePathToUrlPath('pages/about.ts') → '/about'
|
|
64
|
+
*/
|
|
65
|
+
export declare function filePathToUrlPath(filePath: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Scan a directory for route files and return discovered routes.
|
|
68
|
+
*
|
|
69
|
+
* @param routesDir - Absolute path to the routes directory (e.g., `<project>/src/routes`)
|
|
70
|
+
*/
|
|
71
|
+
export declare function scanRoutes(routesDir: string): Promise<RouteEntry[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Register discovered routes with a Fastify server.
|
|
74
|
+
*
|
|
75
|
+
* Page routes with Mithril components are auto-wrapped with SSR rendering.
|
|
76
|
+
* API routes are registered directly as Fastify handlers.
|
|
77
|
+
*/
|
|
78
|
+
export declare function registerRoutes(server: FastifyInstance, routesDir: string, options?: RegisterRoutesOptions): Promise<RouteEntry[]>;
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,6CAA6C;AAC7C,QAAA,MAAM,YAAY,uEAAwE,CAAC;AAC3F,KAAK,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhD,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAExG,kEAAkE;AAClE,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpF,8BAA8B;AAC9B,MAAM,WAAW,UAAU;IACvB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC;IAC9D,2CAA2C;IAC3C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,aAAa,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,uBAAuB;IACvB,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACpC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAkC1D;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAgGzE;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAChC,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qBAA0B,GACpC,OAAO,CAAC,UAAU,EAAE,CAAC,CAsDvB"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based routing for MoriaJS.
|
|
3
|
+
*
|
|
4
|
+
* Scans `src/routes/` for route files and auto-registers them with Fastify.
|
|
5
|
+
*
|
|
6
|
+
* Convention:
|
|
7
|
+
* src/routes/api/hello.ts → GET /api/hello (API handler)
|
|
8
|
+
* src/routes/api/users/[id].ts → GET /api/users/:id (API handler)
|
|
9
|
+
* src/routes/pages/index.ts → GET / (SSR page)
|
|
10
|
+
* src/routes/pages/about.ts → GET /about (SSR page)
|
|
11
|
+
*
|
|
12
|
+
* API routes export named HTTP method functions:
|
|
13
|
+
* export function GET(request, reply) { ... }
|
|
14
|
+
*
|
|
15
|
+
* Page routes export a Mithril component + optional data loader:
|
|
16
|
+
* export default { view() { return m('h1', 'Hello') } }
|
|
17
|
+
* export async function getServerData(request) { return { user: ... } }
|
|
18
|
+
*/
|
|
19
|
+
import { glob } from 'glob';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { pathToFileURL } from 'node:url';
|
|
22
|
+
/** Supported HTTP methods in route files. */
|
|
23
|
+
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
|
|
24
|
+
/**
|
|
25
|
+
* Convert a file path to a URL path.
|
|
26
|
+
*
|
|
27
|
+
* - Strips file extension
|
|
28
|
+
* - Converts `[param]` → `:param`
|
|
29
|
+
* - Converts `[...slug]` → `*`
|
|
30
|
+
* - Converts `index` → `/`
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* filePathToUrlPath('api/users/[id].ts') → '/api/users/:id'
|
|
34
|
+
* filePathToUrlPath('pages/index.ts') → '/'
|
|
35
|
+
* filePathToUrlPath('pages/about.ts') → '/about'
|
|
36
|
+
*/
|
|
37
|
+
export function filePathToUrlPath(filePath) {
|
|
38
|
+
// Remove extension
|
|
39
|
+
let route = filePath.replace(/\.(ts|js|mts|mjs)$/, '');
|
|
40
|
+
// Normalize separators
|
|
41
|
+
route = route.replace(/\\/g, '/');
|
|
42
|
+
// Convert [param] → :param
|
|
43
|
+
route = route.replace(/\[([^\].]+)\]/g, ':$1');
|
|
44
|
+
// Convert [...slug] → *
|
|
45
|
+
route = route.replace(/\[\.\.\.([^\]]+)\]/g, '*');
|
|
46
|
+
// Handle pages prefix — strip "pages" and make root-relative
|
|
47
|
+
if (route.startsWith('pages/')) {
|
|
48
|
+
route = route.slice(5); // remove "pages"
|
|
49
|
+
}
|
|
50
|
+
// Handle api prefix — keep "api"
|
|
51
|
+
// (no transformation needed)
|
|
52
|
+
// Handle index files → parent path
|
|
53
|
+
route = route.replace(/\/index$/, '');
|
|
54
|
+
// Ensure leading slash
|
|
55
|
+
if (!route.startsWith('/')) {
|
|
56
|
+
route = '/' + route;
|
|
57
|
+
}
|
|
58
|
+
// Root case
|
|
59
|
+
if (route === '') {
|
|
60
|
+
route = '/';
|
|
61
|
+
}
|
|
62
|
+
return route;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Scan a directory for route files and return discovered routes.
|
|
66
|
+
*
|
|
67
|
+
* @param routesDir - Absolute path to the routes directory (e.g., `<project>/src/routes`)
|
|
68
|
+
*/
|
|
69
|
+
export async function scanRoutes(routesDir) {
|
|
70
|
+
const pattern = '**/*.{ts,js,mts,mjs}';
|
|
71
|
+
const files = await glob(pattern, {
|
|
72
|
+
cwd: routesDir,
|
|
73
|
+
posix: true,
|
|
74
|
+
ignore: ['**/_*', '**/*.d.ts', '**/*.test.*', '**/*.spec.*'],
|
|
75
|
+
});
|
|
76
|
+
const routes = [];
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
const urlPath = filePathToUrlPath(file);
|
|
79
|
+
const type = file.startsWith('api/') ? 'api' : 'page';
|
|
80
|
+
const absolutePath = path.resolve(routesDir, file);
|
|
81
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
82
|
+
// Dynamically import the route module
|
|
83
|
+
let mod;
|
|
84
|
+
try {
|
|
85
|
+
mod = await import(fileUrl);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.warn(`[moria] Failed to load route: ${file}`, err);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (type === 'page') {
|
|
92
|
+
// ─── Page route: expects default Mithril component ───────
|
|
93
|
+
const component = mod.default;
|
|
94
|
+
const getServerData = typeof mod.getServerData === 'function'
|
|
95
|
+
? mod.getServerData
|
|
96
|
+
: undefined;
|
|
97
|
+
// Page routes can also export raw HTTP handlers (backward compat)
|
|
98
|
+
if (component && typeof component === 'object' && 'view' in component) {
|
|
99
|
+
routes.push({
|
|
100
|
+
filePath: file,
|
|
101
|
+
urlPath,
|
|
102
|
+
type: 'page',
|
|
103
|
+
methods: {},
|
|
104
|
+
component,
|
|
105
|
+
getServerData,
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
// Fallback: if default export is a function, treat as API-style handler
|
|
110
|
+
if (typeof component === 'function') {
|
|
111
|
+
routes.push({
|
|
112
|
+
filePath: file,
|
|
113
|
+
urlPath,
|
|
114
|
+
type: 'page',
|
|
115
|
+
methods: { get: component },
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
// Also check for named HTTP method exports
|
|
120
|
+
const methods = {};
|
|
121
|
+
for (const method of HTTP_METHODS) {
|
|
122
|
+
const handler = mod[method] ?? mod[method.toLowerCase()];
|
|
123
|
+
if (typeof handler === 'function') {
|
|
124
|
+
methods[method.toLowerCase()] = handler;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (Object.keys(methods).length > 0) {
|
|
128
|
+
routes.push({ filePath: file, urlPath, type: 'page', methods });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
console.warn(`[moria] Page route has no component or handlers: ${file}`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// ─── API route: expects named HTTP method exports ────────
|
|
135
|
+
const methods = {};
|
|
136
|
+
for (const method of HTTP_METHODS) {
|
|
137
|
+
const handler = mod[method] ?? mod[method.toLowerCase()];
|
|
138
|
+
if (typeof handler === 'function') {
|
|
139
|
+
methods[method.toLowerCase()] = handler;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Also support default export as GET handler
|
|
143
|
+
if (typeof mod.default === 'function' && !methods.get) {
|
|
144
|
+
methods.get = mod.default;
|
|
145
|
+
}
|
|
146
|
+
if (Object.keys(methods).length === 0) {
|
|
147
|
+
console.warn(`[moria] Route file has no handlers: ${file}`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
routes.push({ filePath: file, urlPath, type: 'api', methods });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return routes;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Register discovered routes with a Fastify server.
|
|
157
|
+
*
|
|
158
|
+
* Page routes with Mithril components are auto-wrapped with SSR rendering.
|
|
159
|
+
* API routes are registered directly as Fastify handlers.
|
|
160
|
+
*/
|
|
161
|
+
export async function registerRoutes(server, routesDir, options = {}) {
|
|
162
|
+
const routes = await scanRoutes(routesDir);
|
|
163
|
+
const mode = options.mode ?? 'development';
|
|
164
|
+
const config = options.config ?? {};
|
|
165
|
+
for (const route of routes) {
|
|
166
|
+
// ─── Page route with Mithril component → SSR handler ─────
|
|
167
|
+
if (route.component) {
|
|
168
|
+
const component = route.component;
|
|
169
|
+
const getServerData = route.getServerData;
|
|
170
|
+
const clientEntry = config.vite?.clientEntry ?? '/src/entry-client.ts';
|
|
171
|
+
server.route({
|
|
172
|
+
method: 'GET',
|
|
173
|
+
url: route.urlPath,
|
|
174
|
+
handler: async (request, reply) => {
|
|
175
|
+
// Load server data if available
|
|
176
|
+
let initialData;
|
|
177
|
+
if (getServerData) {
|
|
178
|
+
initialData = (await getServerData(request));
|
|
179
|
+
}
|
|
180
|
+
// Dynamic import of renderer (avoids circular deps)
|
|
181
|
+
const { renderToString } = await import('@moriajs/renderer');
|
|
182
|
+
const html = await renderToString(component, {
|
|
183
|
+
title: component.title ?? 'MoriaJS App',
|
|
184
|
+
initialData,
|
|
185
|
+
mode,
|
|
186
|
+
clientEntry,
|
|
187
|
+
});
|
|
188
|
+
reply.type('text/html');
|
|
189
|
+
return html;
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
server.log.info(`Page: GET ${route.urlPath} → ${route.filePath} (SSR)`);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
// ─── API / raw handler routes ────────────────────────────
|
|
196
|
+
for (const [method, handler] of Object.entries(route.methods)) {
|
|
197
|
+
server.route({
|
|
198
|
+
method: method.toUpperCase(),
|
|
199
|
+
url: route.urlPath,
|
|
200
|
+
handler: handler,
|
|
201
|
+
});
|
|
202
|
+
server.log.info(`Route: ${method.toUpperCase()} ${route.urlPath} → ${route.filePath}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
server.log.info(`Registered ${routes.length} file-based route(s)`);
|
|
206
|
+
return routes;
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite integration for MoriaJS.
|
|
3
|
+
*
|
|
4
|
+
* Provides Vite dev server in middleware mode (development)
|
|
5
|
+
* and static file serving for production builds.
|
|
6
|
+
*/
|
|
7
|
+
import type { FastifyInstance } from 'fastify';
|
|
8
|
+
import type { ViteDevServer } from 'vite';
|
|
9
|
+
import { type MoriaConfig } from './config.js';
|
|
10
|
+
/**
|
|
11
|
+
* Attach Vite dev server to Fastify in middleware mode.
|
|
12
|
+
* This enables HMR and on-the-fly module transformation.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createViteDevMiddleware(server: FastifyInstance, config?: Partial<MoriaConfig>): Promise<ViteDevServer>;
|
|
15
|
+
/**
|
|
16
|
+
* Serve production-built client assets via @fastify/static.
|
|
17
|
+
*/
|
|
18
|
+
export declare function serveProductionAssets(server: FastifyInstance, config?: Partial<MoriaConfig>): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Generate the HTML shell for a page, with appropriate script tags
|
|
21
|
+
* depending on dev vs production mode.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getHtmlScripts(mode: 'development' | 'production', config?: Partial<MoriaConfig>): string;
|
|
24
|
+
//# sourceMappingURL=vite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;GAGG;AACH,wBAAsB,uBAAuB,CACzC,MAAM,EAAE,eAAe,EACvB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAClC,OAAO,CAAC,aAAa,CAAC,CA4BxB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACvC,MAAM,EAAE,eAAe,EACvB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAClC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,EAAE,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,MAAM,CAW5G"}
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite integration for MoriaJS.
|
|
3
|
+
*
|
|
4
|
+
* Provides Vite dev server in middleware mode (development)
|
|
5
|
+
* and static file serving for production builds.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Attach Vite dev server to Fastify in middleware mode.
|
|
9
|
+
* This enables HMR and on-the-fly module transformation.
|
|
10
|
+
*/
|
|
11
|
+
export async function createViteDevMiddleware(server, config = {}) {
|
|
12
|
+
const { createServer: createViteServer } = await import('vite');
|
|
13
|
+
const middie = (await import('@fastify/middie')).default;
|
|
14
|
+
// Register Express-style middleware support
|
|
15
|
+
await server.register(middie);
|
|
16
|
+
const vite = await createViteServer({
|
|
17
|
+
root: config.rootDir ?? process.cwd(),
|
|
18
|
+
configFile: config.vite?.configFile,
|
|
19
|
+
server: {
|
|
20
|
+
middlewareMode: true,
|
|
21
|
+
hmr: true,
|
|
22
|
+
},
|
|
23
|
+
appType: 'custom',
|
|
24
|
+
});
|
|
25
|
+
// Use Vite's connect middleware stack
|
|
26
|
+
server.use(vite.middlewares);
|
|
27
|
+
server.log.info('Vite dev server attached (HMR enabled)');
|
|
28
|
+
// Ensure Vite is closed when Fastify shuts down
|
|
29
|
+
server.addHook('onClose', async () => {
|
|
30
|
+
await vite.close();
|
|
31
|
+
});
|
|
32
|
+
return vite;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Serve production-built client assets via @fastify/static.
|
|
36
|
+
*/
|
|
37
|
+
export async function serveProductionAssets(server, config = {}) {
|
|
38
|
+
const path = await import('node:path');
|
|
39
|
+
const fastifyStatic = (await import('@fastify/static')).default;
|
|
40
|
+
const distDir = path.resolve(config.rootDir ?? process.cwd(), 'dist', 'client');
|
|
41
|
+
await server.register(fastifyStatic, {
|
|
42
|
+
root: distDir,
|
|
43
|
+
prefix: '/assets/',
|
|
44
|
+
decorateReply: false,
|
|
45
|
+
});
|
|
46
|
+
server.log.info(`Serving static assets from ${distDir}`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Generate the HTML shell for a page, with appropriate script tags
|
|
50
|
+
* depending on dev vs production mode.
|
|
51
|
+
*/
|
|
52
|
+
export function getHtmlScripts(mode, config = {}) {
|
|
53
|
+
if (mode === 'development') {
|
|
54
|
+
const clientEntry = config.vite?.clientEntry ?? '/src/entry-client.ts';
|
|
55
|
+
return [
|
|
56
|
+
`<script type="module" src="/@vite/client"></script>`,
|
|
57
|
+
`<script type="module" src="${clientEntry}"></script>`,
|
|
58
|
+
].join('\n ');
|
|
59
|
+
}
|
|
60
|
+
// Production: reference the built bundle
|
|
61
|
+
return `<script type="module" src="/assets/entry-client.js"></script>`;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=vite.js.map
|
package/dist/vite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CACzC,MAAuB,EACvB,SAA+B,EAAE;IAEjC,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC;IAEzD,4CAA4C;IAC5C,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE9B,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC;QAChC,IAAI,EAAE,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE;QACrC,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU;QACnC,MAAM,EAAE;YACJ,cAAc,EAAE,IAAI;YACpB,GAAG,EAAE,IAAI;SACZ;QACD,OAAO,EAAE,QAAQ;KACpB,CAAC,CAAC;IAEH,sCAAsC;IACtC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IAE1D,gDAAgD;IAChD,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QACjC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACvC,MAAuB,EACvB,SAA+B,EAAE;IAEjC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,CAAC,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC;IAEhE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEhF,MAAM,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE;QACjC,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,UAAU;QAClB,aAAa,EAAE,KAAK;KACvB,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAkC,EAAE,SAA+B,EAAE;IAChG,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,EAAE,WAAW,IAAI,sBAAsB,CAAC;QACvE,OAAO;YACH,qDAAqD;YACrD,8BAA8B,WAAW,aAAa;SACzD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC;IAED,yCAAyC;IACzC,OAAO,+DAA+D,CAAC;AAC3E,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moriajs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MoriaJS framework core — Fastify server, Vite integration, and routing",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,13 +18,19 @@
|
|
|
18
18
|
"@fastify/compress": "^8.0.0",
|
|
19
19
|
"@fastify/static": "^9.0.0",
|
|
20
20
|
"@fastify/helmet": "^13.0.0",
|
|
21
|
-
"
|
|
21
|
+
"@fastify/middie": "^9.0.0",
|
|
22
|
+
"pino-pretty": "^13.0.0",
|
|
23
|
+
"vite": "^6.0.0",
|
|
24
|
+
"glob": "^11.0.0"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
24
27
|
"typescript": "^5.7.0",
|
|
25
28
|
"rimraf": "^6.0.0",
|
|
26
29
|
"@types/node": "^22.0.0"
|
|
27
30
|
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@moriajs/renderer": "0.2.0"
|
|
33
|
+
},
|
|
28
34
|
"license": "MIT",
|
|
29
35
|
"author": "Guntur-D <guntur.d.npm@gmail.com>",
|
|
30
36
|
"repository": {
|
package/src/app.ts
CHANGED
|
@@ -3,8 +3,13 @@ import cors from '@fastify/cors';
|
|
|
3
3
|
import cookie from '@fastify/cookie';
|
|
4
4
|
import compress from '@fastify/compress';
|
|
5
5
|
import helmet from '@fastify/helmet';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import fs from 'node:fs';
|
|
6
8
|
import { type MoriaConfig } from './config.js';
|
|
7
9
|
import { type MoriaPlugin } from './plugins.js';
|
|
10
|
+
import { createViteDevMiddleware, serveProductionAssets } from './vite.js';
|
|
11
|
+
import { registerRoutes } from './router.js';
|
|
12
|
+
import type { ViteDevServer } from 'vite';
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
15
|
* Options for creating a MoriaJS application.
|
|
@@ -23,6 +28,8 @@ export interface MoriaAppOptions {
|
|
|
23
28
|
export interface MoriaApp {
|
|
24
29
|
/** The underlying Fastify instance */
|
|
25
30
|
server: FastifyInstance;
|
|
31
|
+
/** The Vite dev server (only in development mode) */
|
|
32
|
+
vite?: ViteDevServer;
|
|
26
33
|
/** Register a MoriaJS plugin */
|
|
27
34
|
use: (plugin: MoriaPlugin) => Promise<void>;
|
|
28
35
|
/** Start the server */
|
|
@@ -38,12 +45,14 @@ export interface MoriaApp {
|
|
|
38
45
|
* ```ts
|
|
39
46
|
* import { createApp } from '@moriajs/core';
|
|
40
47
|
*
|
|
41
|
-
* const app = await createApp();
|
|
48
|
+
* const app = await createApp({ config: { mode: 'development' } });
|
|
42
49
|
* await app.listen({ port: 3000 });
|
|
43
50
|
* ```
|
|
44
51
|
*/
|
|
45
52
|
export async function createApp(options: MoriaAppOptions = {}): Promise<MoriaApp> {
|
|
46
53
|
const config = options.config ?? {};
|
|
54
|
+
const mode = config.mode ?? (process.env.NODE_ENV === 'production' ? 'production' : 'development');
|
|
55
|
+
const rootDir = config.rootDir ?? process.cwd();
|
|
47
56
|
const port = config.server?.port ?? 3000;
|
|
48
57
|
const host = config.server?.host ?? '0.0.0.0';
|
|
49
58
|
|
|
@@ -52,7 +61,7 @@ export async function createApp(options: MoriaAppOptions = {}): Promise<MoriaApp
|
|
|
52
61
|
logger: {
|
|
53
62
|
level: config.server?.logLevel ?? 'info',
|
|
54
63
|
transport:
|
|
55
|
-
|
|
64
|
+
mode !== 'production'
|
|
56
65
|
? { target: 'pino-pretty', options: { colorize: true } }
|
|
57
66
|
: undefined,
|
|
58
67
|
},
|
|
@@ -69,17 +78,33 @@ export async function createApp(options: MoriaAppOptions = {}): Promise<MoriaApp
|
|
|
69
78
|
await server.register(compress);
|
|
70
79
|
await server.register(helmet, {
|
|
71
80
|
// Relax CSP in development for Vite HMR
|
|
72
|
-
contentSecurityPolicy:
|
|
81
|
+
contentSecurityPolicy: mode === 'production',
|
|
73
82
|
});
|
|
74
83
|
|
|
75
84
|
// Health check route
|
|
76
85
|
server.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
|
|
77
86
|
|
|
87
|
+
// ─── Vite Integration ────────────────────────────────────
|
|
88
|
+
let vite: ViteDevServer | undefined;
|
|
89
|
+
|
|
90
|
+
if (mode === 'development') {
|
|
91
|
+
vite = await createViteDevMiddleware(server, { ...config, rootDir });
|
|
92
|
+
} else {
|
|
93
|
+
await serveProductionAssets(server, { ...config, rootDir });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── File-Based Routing ──────────────────────────────────
|
|
97
|
+
const routesDir = path.resolve(rootDir, config.routes?.dir ?? 'src/routes');
|
|
98
|
+
if (fs.existsSync(routesDir)) {
|
|
99
|
+
await registerRoutes(server, routesDir, { mode, config });
|
|
100
|
+
}
|
|
101
|
+
|
|
78
102
|
// Plugin registry
|
|
79
103
|
const plugins: MoriaPlugin[] = [];
|
|
80
104
|
|
|
81
105
|
const app: MoriaApp = {
|
|
82
106
|
server,
|
|
107
|
+
vite,
|
|
83
108
|
|
|
84
109
|
async use(plugin: MoriaPlugin) {
|
|
85
110
|
plugins.push(plugin);
|
package/src/config.ts
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
* Used in `moria.config.ts` files in user projects.
|
|
4
4
|
*/
|
|
5
5
|
export interface MoriaConfig {
|
|
6
|
+
/** Application mode */
|
|
7
|
+
mode?: 'development' | 'production';
|
|
8
|
+
|
|
9
|
+
/** Project root directory (auto-detected if not set) */
|
|
10
|
+
rootDir?: string;
|
|
11
|
+
|
|
6
12
|
/** Server configuration */
|
|
7
13
|
server?: {
|
|
8
14
|
/** Port to listen on (default: 3000) */
|
|
@@ -44,6 +50,14 @@ export interface MoriaConfig {
|
|
|
44
50
|
vite?: {
|
|
45
51
|
/** Path to Vite config file */
|
|
46
52
|
configFile?: string;
|
|
53
|
+
/** Client entry point (default: '/src/entry-client.ts') */
|
|
54
|
+
clientEntry?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** File-based routing configuration */
|
|
58
|
+
routes?: {
|
|
59
|
+
/** Routes directory relative to rootDir (default: 'src/routes') */
|
|
60
|
+
dir?: string;
|
|
47
61
|
};
|
|
48
62
|
}
|
|
49
63
|
|
package/src/index.ts
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
* @moriajs/core
|
|
3
3
|
*
|
|
4
4
|
* Framework core: Fastify server factory with sensible defaults,
|
|
5
|
-
* plugin registration, and
|
|
5
|
+
* plugin registration, Vite integration, and file-based routing.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
export { createApp } from './app.js';
|
|
9
9
|
export { defineConfig } from './config.js';
|
|
10
10
|
export { defineMoriaPlugin } from './plugins.js';
|
|
11
|
+
export { createViteDevMiddleware, serveProductionAssets, getHtmlScripts } from './vite.js';
|
|
12
|
+
export { scanRoutes, registerRoutes, filePathToUrlPath } from './router.js';
|
|
11
13
|
|
|
12
14
|
export type { MoriaApp, MoriaAppOptions } from './app.js';
|
|
13
15
|
export type { MoriaConfig } from './config.js';
|
|
14
16
|
export type { MoriaPlugin, MoriaPluginContext } from './plugins.js';
|
|
17
|
+
export type { RouteEntry, RouteHandler } from './router.js';
|
package/src/router.ts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based routing for MoriaJS.
|
|
3
|
+
*
|
|
4
|
+
* Scans `src/routes/` for route files and auto-registers them with Fastify.
|
|
5
|
+
*
|
|
6
|
+
* Convention:
|
|
7
|
+
* src/routes/api/hello.ts → GET /api/hello (API handler)
|
|
8
|
+
* src/routes/api/users/[id].ts → GET /api/users/:id (API handler)
|
|
9
|
+
* src/routes/pages/index.ts → GET / (SSR page)
|
|
10
|
+
* src/routes/pages/about.ts → GET /about (SSR page)
|
|
11
|
+
*
|
|
12
|
+
* API routes export named HTTP method functions:
|
|
13
|
+
* export function GET(request, reply) { ... }
|
|
14
|
+
*
|
|
15
|
+
* Page routes export a Mithril component + optional data loader:
|
|
16
|
+
* export default { view() { return m('h1', 'Hello') } }
|
|
17
|
+
* export async function getServerData(request) { return { user: ... } }
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
21
|
+
import { glob } from 'glob';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { pathToFileURL } from 'node:url';
|
|
24
|
+
import type { MoriaConfig } from './config.js';
|
|
25
|
+
|
|
26
|
+
/** Supported HTTP methods in route files. */
|
|
27
|
+
const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] as const;
|
|
28
|
+
type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
29
|
+
|
|
30
|
+
/** Handler function exported from a route file. */
|
|
31
|
+
export type RouteHandler = (request: FastifyRequest, reply: FastifyReply) => unknown | Promise<unknown>;
|
|
32
|
+
|
|
33
|
+
/** Data loader for page routes — runs on server before render. */
|
|
34
|
+
export type GetServerData = (request: FastifyRequest) => unknown | Promise<unknown>;
|
|
35
|
+
|
|
36
|
+
/** Discovered route entry. */
|
|
37
|
+
export interface RouteEntry {
|
|
38
|
+
/** File path relative to routes dir */
|
|
39
|
+
filePath: string;
|
|
40
|
+
/** URL path pattern (e.g., /api/users/:id) */
|
|
41
|
+
urlPath: string;
|
|
42
|
+
/** Route type: 'api' or 'page' */
|
|
43
|
+
type: 'api' | 'page';
|
|
44
|
+
/** HTTP method → handler map (API routes) */
|
|
45
|
+
methods: Partial<Record<Lowercase<HttpMethod>, RouteHandler>>;
|
|
46
|
+
/** Mithril component (page routes only) */
|
|
47
|
+
component?: unknown;
|
|
48
|
+
/** Server data loader (page routes only) */
|
|
49
|
+
getServerData?: GetServerData;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Options for route registration.
|
|
54
|
+
*/
|
|
55
|
+
export interface RegisterRoutesOptions {
|
|
56
|
+
/** Application mode */
|
|
57
|
+
mode?: 'development' | 'production';
|
|
58
|
+
/** MoriaJS config for renderer options */
|
|
59
|
+
config?: Partial<MoriaConfig>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Convert a file path to a URL path.
|
|
64
|
+
*
|
|
65
|
+
* - Strips file extension
|
|
66
|
+
* - Converts `[param]` → `:param`
|
|
67
|
+
* - Converts `[...slug]` → `*`
|
|
68
|
+
* - Converts `index` → `/`
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* filePathToUrlPath('api/users/[id].ts') → '/api/users/:id'
|
|
72
|
+
* filePathToUrlPath('pages/index.ts') → '/'
|
|
73
|
+
* filePathToUrlPath('pages/about.ts') → '/about'
|
|
74
|
+
*/
|
|
75
|
+
export function filePathToUrlPath(filePath: string): string {
|
|
76
|
+
// Remove extension
|
|
77
|
+
let route = filePath.replace(/\.(ts|js|mts|mjs)$/, '');
|
|
78
|
+
|
|
79
|
+
// Normalize separators
|
|
80
|
+
route = route.replace(/\\/g, '/');
|
|
81
|
+
|
|
82
|
+
// Convert [param] → :param
|
|
83
|
+
route = route.replace(/\[([^\].]+)\]/g, ':$1');
|
|
84
|
+
|
|
85
|
+
// Convert [...slug] → *
|
|
86
|
+
route = route.replace(/\[\.\.\.([^\]]+)\]/g, '*');
|
|
87
|
+
|
|
88
|
+
// Handle pages prefix — strip "pages" and make root-relative
|
|
89
|
+
if (route.startsWith('pages/')) {
|
|
90
|
+
route = route.slice(5); // remove "pages"
|
|
91
|
+
}
|
|
92
|
+
// Handle api prefix — keep "api"
|
|
93
|
+
// (no transformation needed)
|
|
94
|
+
|
|
95
|
+
// Handle index files → parent path
|
|
96
|
+
route = route.replace(/\/index$/, '');
|
|
97
|
+
|
|
98
|
+
// Ensure leading slash
|
|
99
|
+
if (!route.startsWith('/')) {
|
|
100
|
+
route = '/' + route;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Root case
|
|
104
|
+
if (route === '') {
|
|
105
|
+
route = '/';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return route;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Scan a directory for route files and return discovered routes.
|
|
113
|
+
*
|
|
114
|
+
* @param routesDir - Absolute path to the routes directory (e.g., `<project>/src/routes`)
|
|
115
|
+
*/
|
|
116
|
+
export async function scanRoutes(routesDir: string): Promise<RouteEntry[]> {
|
|
117
|
+
const pattern = '**/*.{ts,js,mts,mjs}';
|
|
118
|
+
const files = await glob(pattern, {
|
|
119
|
+
cwd: routesDir,
|
|
120
|
+
posix: true,
|
|
121
|
+
ignore: ['**/_*', '**/*.d.ts', '**/*.test.*', '**/*.spec.*'],
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const routes: RouteEntry[] = [];
|
|
125
|
+
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
const urlPath = filePathToUrlPath(file);
|
|
128
|
+
const type: 'api' | 'page' = file.startsWith('api/') ? 'api' : 'page';
|
|
129
|
+
|
|
130
|
+
const absolutePath = path.resolve(routesDir, file);
|
|
131
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
132
|
+
|
|
133
|
+
// Dynamically import the route module
|
|
134
|
+
let mod: Record<string, unknown>;
|
|
135
|
+
try {
|
|
136
|
+
mod = await import(fileUrl);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.warn(`[moria] Failed to load route: ${file}`, err);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (type === 'page') {
|
|
143
|
+
// ─── Page route: expects default Mithril component ───────
|
|
144
|
+
const component = mod.default;
|
|
145
|
+
const getServerData = typeof mod.getServerData === 'function'
|
|
146
|
+
? mod.getServerData as GetServerData
|
|
147
|
+
: undefined;
|
|
148
|
+
|
|
149
|
+
// Page routes can also export raw HTTP handlers (backward compat)
|
|
150
|
+
if (component && typeof component === 'object' && 'view' in component) {
|
|
151
|
+
routes.push({
|
|
152
|
+
filePath: file,
|
|
153
|
+
urlPath,
|
|
154
|
+
type: 'page',
|
|
155
|
+
methods: {},
|
|
156
|
+
component,
|
|
157
|
+
getServerData,
|
|
158
|
+
});
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Fallback: if default export is a function, treat as API-style handler
|
|
163
|
+
if (typeof component === 'function') {
|
|
164
|
+
routes.push({
|
|
165
|
+
filePath: file,
|
|
166
|
+
urlPath,
|
|
167
|
+
type: 'page',
|
|
168
|
+
methods: { get: component as RouteHandler },
|
|
169
|
+
});
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Also check for named HTTP method exports
|
|
174
|
+
const methods: Partial<Record<Lowercase<HttpMethod>, RouteHandler>> = {};
|
|
175
|
+
for (const method of HTTP_METHODS) {
|
|
176
|
+
const handler = mod[method] ?? mod[method.toLowerCase()];
|
|
177
|
+
if (typeof handler === 'function') {
|
|
178
|
+
methods[method.toLowerCase() as Lowercase<HttpMethod>] = handler as RouteHandler;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (Object.keys(methods).length > 0) {
|
|
182
|
+
routes.push({ filePath: file, urlPath, type: 'page', methods });
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.warn(`[moria] Page route has no component or handlers: ${file}`);
|
|
187
|
+
} else {
|
|
188
|
+
// ─── API route: expects named HTTP method exports ────────
|
|
189
|
+
const methods: Partial<Record<Lowercase<HttpMethod>, RouteHandler>> = {};
|
|
190
|
+
for (const method of HTTP_METHODS) {
|
|
191
|
+
const handler = mod[method] ?? mod[method.toLowerCase()];
|
|
192
|
+
if (typeof handler === 'function') {
|
|
193
|
+
methods[method.toLowerCase() as Lowercase<HttpMethod>] = handler as RouteHandler;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Also support default export as GET handler
|
|
198
|
+
if (typeof mod.default === 'function' && !methods.get) {
|
|
199
|
+
methods.get = mod.default as RouteHandler;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (Object.keys(methods).length === 0) {
|
|
203
|
+
console.warn(`[moria] Route file has no handlers: ${file}`);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
routes.push({ filePath: file, urlPath, type: 'api', methods });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return routes;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Register discovered routes with a Fastify server.
|
|
216
|
+
*
|
|
217
|
+
* Page routes with Mithril components are auto-wrapped with SSR rendering.
|
|
218
|
+
* API routes are registered directly as Fastify handlers.
|
|
219
|
+
*/
|
|
220
|
+
export async function registerRoutes(
|
|
221
|
+
server: FastifyInstance,
|
|
222
|
+
routesDir: string,
|
|
223
|
+
options: RegisterRoutesOptions = {}
|
|
224
|
+
): Promise<RouteEntry[]> {
|
|
225
|
+
const routes = await scanRoutes(routesDir);
|
|
226
|
+
const mode = options.mode ?? 'development';
|
|
227
|
+
const config = options.config ?? {};
|
|
228
|
+
|
|
229
|
+
for (const route of routes) {
|
|
230
|
+
// ─── Page route with Mithril component → SSR handler ─────
|
|
231
|
+
if (route.component) {
|
|
232
|
+
const component = route.component;
|
|
233
|
+
const getServerData = route.getServerData;
|
|
234
|
+
const clientEntry = config.vite?.clientEntry ?? '/src/entry-client.ts';
|
|
235
|
+
|
|
236
|
+
server.route({
|
|
237
|
+
method: 'GET',
|
|
238
|
+
url: route.urlPath,
|
|
239
|
+
handler: async (request: FastifyRequest, reply: FastifyReply) => {
|
|
240
|
+
// Load server data if available
|
|
241
|
+
let initialData: Record<string, unknown> | undefined;
|
|
242
|
+
if (getServerData) {
|
|
243
|
+
initialData = (await getServerData(request)) as Record<string, unknown>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Dynamic import of renderer (avoids circular deps)
|
|
247
|
+
const { renderToString } = await import('@moriajs/renderer');
|
|
248
|
+
|
|
249
|
+
const html = await renderToString(component, {
|
|
250
|
+
title: (component as { title?: string }).title ?? 'MoriaJS App',
|
|
251
|
+
initialData,
|
|
252
|
+
mode,
|
|
253
|
+
clientEntry,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
reply.type('text/html');
|
|
257
|
+
return html;
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
server.log.info(`Page: GET ${route.urlPath} → ${route.filePath} (SSR)`);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ─── API / raw handler routes ────────────────────────────
|
|
266
|
+
for (const [method, handler] of Object.entries(route.methods)) {
|
|
267
|
+
server.route({
|
|
268
|
+
method: method.toUpperCase() as Uppercase<string>,
|
|
269
|
+
url: route.urlPath,
|
|
270
|
+
handler: handler as RouteHandler,
|
|
271
|
+
});
|
|
272
|
+
server.log.info(`Route: ${method.toUpperCase()} ${route.urlPath} → ${route.filePath}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
server.log.info(`Registered ${routes.length} file-based route(s)`);
|
|
277
|
+
return routes;
|
|
278
|
+
}
|
package/src/vite.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite integration for MoriaJS.
|
|
3
|
+
*
|
|
4
|
+
* Provides Vite dev server in middleware mode (development)
|
|
5
|
+
* and static file serving for production builds.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { FastifyInstance } from 'fastify';
|
|
9
|
+
import type { ViteDevServer } from 'vite';
|
|
10
|
+
import { type MoriaConfig } from './config.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Attach Vite dev server to Fastify in middleware mode.
|
|
14
|
+
* This enables HMR and on-the-fly module transformation.
|
|
15
|
+
*/
|
|
16
|
+
export async function createViteDevMiddleware(
|
|
17
|
+
server: FastifyInstance,
|
|
18
|
+
config: Partial<MoriaConfig> = {}
|
|
19
|
+
): Promise<ViteDevServer> {
|
|
20
|
+
const { createServer: createViteServer } = await import('vite');
|
|
21
|
+
const middie = (await import('@fastify/middie')).default;
|
|
22
|
+
|
|
23
|
+
// Register Express-style middleware support
|
|
24
|
+
await server.register(middie);
|
|
25
|
+
|
|
26
|
+
const vite = await createViteServer({
|
|
27
|
+
root: config.rootDir ?? process.cwd(),
|
|
28
|
+
configFile: config.vite?.configFile,
|
|
29
|
+
server: {
|
|
30
|
+
middlewareMode: true,
|
|
31
|
+
hmr: true,
|
|
32
|
+
},
|
|
33
|
+
appType: 'custom',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Use Vite's connect middleware stack
|
|
37
|
+
server.use(vite.middlewares);
|
|
38
|
+
|
|
39
|
+
server.log.info('Vite dev server attached (HMR enabled)');
|
|
40
|
+
|
|
41
|
+
// Ensure Vite is closed when Fastify shuts down
|
|
42
|
+
server.addHook('onClose', async () => {
|
|
43
|
+
await vite.close();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return vite;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Serve production-built client assets via @fastify/static.
|
|
51
|
+
*/
|
|
52
|
+
export async function serveProductionAssets(
|
|
53
|
+
server: FastifyInstance,
|
|
54
|
+
config: Partial<MoriaConfig> = {}
|
|
55
|
+
): Promise<void> {
|
|
56
|
+
const path = await import('node:path');
|
|
57
|
+
const fastifyStatic = (await import('@fastify/static')).default;
|
|
58
|
+
|
|
59
|
+
const distDir = path.resolve(config.rootDir ?? process.cwd(), 'dist', 'client');
|
|
60
|
+
|
|
61
|
+
await server.register(fastifyStatic, {
|
|
62
|
+
root: distDir,
|
|
63
|
+
prefix: '/assets/',
|
|
64
|
+
decorateReply: false,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
server.log.info(`Serving static assets from ${distDir}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Generate the HTML shell for a page, with appropriate script tags
|
|
72
|
+
* depending on dev vs production mode.
|
|
73
|
+
*/
|
|
74
|
+
export function getHtmlScripts(mode: 'development' | 'production', config: Partial<MoriaConfig> = {}): string {
|
|
75
|
+
if (mode === 'development') {
|
|
76
|
+
const clientEntry = config.vite?.clientEntry ?? '/src/entry-client.ts';
|
|
77
|
+
return [
|
|
78
|
+
`<script type="module" src="/@vite/client"></script>`,
|
|
79
|
+
`<script type="module" src="${clientEntry}"></script>`,
|
|
80
|
+
].join('\n ');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Production: reference the built bundle
|
|
84
|
+
return `<script type="module" src="/assets/entry-client.js"></script>`;
|
|
85
|
+
}
|