@moostjs/vite 0.6.3 → 0.6.4

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 CHANGED
@@ -101,6 +101,54 @@ moostVite({
101
101
  })
102
102
  ```
103
103
 
104
+ ## SSR Mode
105
+
106
+ Add `ssrEntry` to enable server-side rendering. The plugin handles everything automatically — SSR rendering in dev, multi-environment builds for production:
107
+
108
+ ```ts
109
+ moostVite({
110
+ entry: '/src/main.ts',
111
+ middleware: true,
112
+ prefix: '/api',
113
+ ssrEntry: '/src/entry-server.ts',
114
+ })
115
+ ```
116
+
117
+ In dev, the plugin adds an SSR fallback middleware that renders pages on the server using your `entry-server.ts`. In production, `vite build` produces three bundles in a single pass:
118
+
119
+ - **client** — optimized browser assets (`dist/client/`)
120
+ - **ssr** — server-side render function (`dist/server/ssr/`)
121
+ - **server** — production Node.js server (`dist/server/server.js`)
122
+
123
+ ### SPA Mode
124
+
125
+ Omit `ssrEntry` and Vite serves the app as a standard SPA. The production build still generates a server that serves static files and API routes — it just skips server-side rendering.
126
+
127
+ ### Custom Server Entry
128
+
129
+ By default, `vite build` auto-generates a minimal production server. If you need custom middleware (compression, auth, logging), provide your own server file:
130
+
131
+ ```ts
132
+ moostVite({
133
+ entry: '/src/main.ts',
134
+ middleware: true,
135
+ prefix: '/api',
136
+ ssrEntry: '/src/entry-server.ts',
137
+ serverEntry: './server.ts',
138
+ })
139
+ ```
140
+
141
+ ```ts
142
+ // server.ts
143
+ import { createSSRServer } from '@moostjs/vite/server'
144
+
145
+ const app = await createSSRServer()
146
+ // app.use(compression())
147
+ await app.listen()
148
+ ```
149
+
150
+ `createSSRServer` handles dev/prod branching — in dev it creates a Vite dev server, in prod it serves built assets with `sirv`.
151
+
104
152
  ## SSR Local Fetch
105
153
 
106
154
  When `ssrFetch` is enabled (default: `true`), the plugin patches `globalThis.fetch` so that local paths are routed in-process through Moost instead of making a real HTTP request. This is useful for SSR where server-side code fetches from its own API:
@@ -141,6 +189,10 @@ The plugin injects a `__VITE_ID` decorator on `@Injectable` and `@Controller` cl
141
189
  | `ssrFetch` | `boolean` | `true` | Enable local fetch interception for SSR |
142
190
  | `middleware` | `boolean` | `false` | Run Moost as Connect middleware (Vite serves frontend) |
143
191
  | `prefix` | `string` | — | URL prefix filter for middleware mode (e.g. `'/api'`) |
192
+ | `ssrEntry` | `string` | — | Vue/React SSR entry module (e.g. `'/src/entry-server.ts'`) |
193
+ | `ssrOutlet` | `string` | `'<!--ssr-outlet-->'` | HTML placeholder for SSR-rendered content |
194
+ | `ssrState` | `string` | `'<!--ssr-state-->'` | HTML placeholder for SSR state transfer script |
195
+ | `serverEntry` | `string` | — | Custom production server entry file (e.g. `'./server.ts'`) |
144
196
 
145
197
  Options marked "backend mode only" are ignored when `middleware: true` — the user's `vite.config.ts` controls build/server configuration in middleware mode.
146
198
 
package/dist/index.cjs CHANGED
@@ -21,9 +21,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
+ const node_fs = __toESM(require("node:fs"));
25
+ const node_path = __toESM(require("node:path"));
24
26
  const vite = __toESM(require("vite"));
25
27
  const magic_string = __toESM(require("magic-string"));
26
- const node_fs = __toESM(require("node:fs"));
27
28
  const node_module = __toESM(require("node:module"));
28
29
  const moost = __toESM(require("moost"));
29
30
 
@@ -321,7 +322,18 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
321
322
  *
322
323
  * @param {TMoostViteDevOptions} options - Configuration options for the Moost Vite plugin.
323
324
  * @returns {PluginOption} The configured Vite plugin.
324
- */ function moostVite(options) {
325
+ */ const DEFAULT_SERVER_ENTRY_CODE = `import { createSSRServer } from '@moostjs/vite/server'
326
+ const app = await createSSRServer()
327
+ await app.listen()
328
+ `;
329
+ function generatedServerEntry(root) {
330
+ const dir = (0, node_path.resolve)(root || process.cwd(), "node_modules", ".moost-vite");
331
+ (0, node_fs.mkdirSync)(dir, { recursive: true });
332
+ const entryPath = (0, node_path.resolve)(dir, "server-entry.mjs");
333
+ (0, node_fs.writeFileSync)(entryPath, DEFAULT_SERVER_ENTRY_CODE);
334
+ return entryPath;
335
+ }
336
+ function moostVite(options) {
325
337
  const isTest = process.env.NODE_ENV === "test";
326
338
  const isProd = process.env.NODE_ENV === "production";
327
339
  const externals = options.externals ?? true;
@@ -374,43 +386,49 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
374
386
  __MOOST_ENTRY__: JSON.stringify(options.entry),
375
387
  __MOOST_PREFIX__: JSON.stringify(prefix || "/api")
376
388
  };
377
- const environments = {
378
- client: { build: { outDir: cfg.build?.outDir || "dist/client" } },
379
- server: {
380
- build: {
381
- outDir: "dist",
382
- emptyOutDir: false,
383
- ssr: "server.ts",
384
- minify: false,
385
- sourcemap: !!(options.sourcemap ?? true),
386
- rollupOptions: {
387
- external: (id) => {
388
- if (id === "@moostjs/vite/server") return false;
389
- return defaultExternals.some((ext) => ext.test(id));
390
- },
391
- output: { format: "esm" }
392
- }
393
- },
394
- define: serverDefines
395
- }
396
- };
389
+ const serverEntry = options.serverEntry || generatedServerEntry(cfg.root);
390
+ const ssrInput = { server: serverEntry };
397
391
  if (options.ssrEntry) {
398
- const ssrEntryBasename = entryBasename(options.ssrEntry);
399
- environments.client.build.ssrManifest = true;
400
- environments.ssr = { build: {
401
- outDir: "dist/ssr",
402
- ssr: options.ssrEntry
403
- } };
404
- serverDefines.__MOOST_SSR_ENTRY__ = JSON.stringify(`./ssr/${ssrEntryBasename}`);
392
+ const ssrBasename = entryBasename(options.ssrEntry);
393
+ ssrInput[`ssr/${ssrBasename.replace(/\.js$/, "")}`] = options.ssrEntry;
394
+ serverDefines.__MOOST_SSR_ENTRY__ = JSON.stringify(`./ssr/${ssrBasename}`);
405
395
  serverDefines.__MOOST_SSR_OUTLET__ = JSON.stringify(options.ssrOutlet || DEFAULT_SSR_OUTLET);
406
396
  serverDefines.__MOOST_SSR_STATE__ = JSON.stringify(options.ssrState || DEFAULT_SSR_STATE);
407
397
  }
408
- const envNames = Object.keys(environments);
398
+ const outDir = cfg.build?.outDir || "dist";
409
399
  return {
400
+ ...options.ssrEntry && { appType: "custom" },
410
401
  builder: { async buildApp(builder) {
411
- for (const name of envNames) if (builder.environments[name]) await builder.build(builder.environments[name]);
402
+ (0, node_fs.rmSync)((0, node_path.resolve)(cfg.root || process.cwd(), outDir), {
403
+ recursive: true,
404
+ force: true
405
+ });
406
+ for (const env of Object.values(builder.environments)) await builder.build(env);
412
407
  } },
413
- environments
408
+ environments: {
409
+ client: { build: {
410
+ outDir: `${outDir}/client`,
411
+ emptyOutDir: false,
412
+ ...options.ssrEntry && { ssrManifest: true }
413
+ } },
414
+ ssr: {
415
+ build: {
416
+ outDir: `${outDir}/server`,
417
+ emptyOutDir: false,
418
+ minify: false,
419
+ sourcemap: !!(options.sourcemap ?? true),
420
+ rollupOptions: {
421
+ input: ssrInput,
422
+ external: (id) => {
423
+ if (id === "@moostjs/vite/server") return false;
424
+ return defaultExternals.some((ext) => ext.test(id));
425
+ },
426
+ output: { format: "esm" }
427
+ }
428
+ },
429
+ define: serverDefines
430
+ }
431
+ }
414
432
  };
415
433
  }
416
434
  const entry = cfg.build?.rollupOptions?.input || options.entry;
@@ -446,6 +464,7 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
446
464
  config.__moostViteOptions = {
447
465
  entry: options.entry,
448
466
  ssrEntry: options.ssrEntry,
467
+ serverEntry: options.serverEntry,
449
468
  prefix,
450
469
  port: options.port,
451
470
  ssrOutlet: options.ssrOutlet,
@@ -494,7 +513,7 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
494
513
  logger$1.debug("🚀 Reloading Moost App...");
495
514
  console.log();
496
515
  await ssrImport(options.entry);
497
- await new Promise((resolve) => setTimeout(resolve, 1));
516
+ await new Promise((resolve$1) => setTimeout(resolve$1, 1));
498
517
  }
499
518
  if (options.middleware && prefix) {
500
519
  const url = req.url || "";
@@ -541,7 +560,35 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
541
560
  delete pluginConfig.transform;
542
561
  delete pluginConfig.hotUpdate;
543
562
  }
544
- return pluginConfig;
563
+ const ssrFallbackPlugin = !isProd && !isTest && options.middleware && options.ssrEntry ? {
564
+ name: `${PLUGIN_NAME}:ssr-fallback`,
565
+ async configureServer(server) {
566
+ const ssrOutlet = options.ssrOutlet || DEFAULT_SSR_OUTLET;
567
+ const ssrState = options.ssrState || DEFAULT_SSR_STATE;
568
+ const fs = await import("node:fs/promises");
569
+ return () => {
570
+ server.middlewares.use(async (req, res, next) => {
571
+ if (req.method !== "GET") return next();
572
+ const url = req.originalUrl || req.url || "/";
573
+ try {
574
+ let template = await fs.readFile((0, node_path.resolve)(process.cwd(), "index.html"), "utf-8");
575
+ template = await server.transformIndexHtml(url, template);
576
+ const { render } = await server.ssrLoadModule(options.ssrEntry);
577
+ const { html: appHtml, state } = await render(url);
578
+ res.statusCode = 200;
579
+ res.setHeader("Content-Type", "text/html");
580
+ res.end(template.replace(ssrOutlet, appHtml).replace(ssrState, state ? `<script>window.__SSR_STATE__=${state}</script>` : ""));
581
+ } catch (e) {
582
+ server.ssrFixStacktrace(e);
583
+ console.error(e);
584
+ res.statusCode = 500;
585
+ res.end(e.message);
586
+ }
587
+ });
588
+ };
589
+ }
590
+ } : null;
591
+ return [pluginConfig, ssrFallbackPlugin];
545
592
  }
546
593
 
547
594
  //#endregion
package/dist/index.d.ts CHANGED
@@ -113,26 +113,14 @@ interface TMoostViteDevOptions {
113
113
  * Default: `'<!--ssr-state-->'`
114
114
  */
115
115
  ssrState?: string;
116
+ /**
117
+ * Path to a custom server entry file (e.g., `'./server.ts'`).
118
+ * When provided, this file is used as the production server build entry.
119
+ * When omitted in middleware mode, the plugin auto-generates a minimal
120
+ * production server that serves static files + Moost API (+ SSR if configured).
121
+ */
122
+ serverEntry?: string;
116
123
  }
117
- /**
118
- * The main Vite plugin for integrating Moost applications.
119
- *
120
- * Features:
121
- * - **Adapter Detection**: Detects Moost adapter usage (`http`, `cli`, `wf`) and applies relevant configurations.
122
- * - **Dev Mode Middleware**:
123
- * - Patches `MoostHttp.prototype.listen` to register a custom middleware for serving the app via Vite's dev server instead of binding to a port.
124
- * - Handles Moost state cleanup and hot module replacement (HMR) during development.
125
- * - **Class Tracking**: Injects a `__VITE_ID()` decorator into exported classes to enable tracking and cleanup during hot reloads.
126
- * - **Externals Support**:
127
- * - Allows marking Node.js built-in modules and dependencies from `package.json` (optionally excluding workspace dependencies) as external during builds.
128
- * - Configured via the `externals` option, which helps reduce bundle size and ensures compatibility with runtime environments.
129
- * - **Build and Test Friendly**:
130
- * - Avoids interfering with Vitest runs by skipping dev-specific behaviors during tests.
131
- * - Ensures proper SSR entry setup and Rollup configurations for production builds.
132
- *
133
- * @param {TMoostViteDevOptions} options - Configuration options for the Moost Vite plugin.
134
- * @returns {PluginOption} The configured Vite plugin.
135
- */
136
124
  declare function moostVite(options: TMoostViteDevOptions): PluginOption;
137
125
 
138
126
  export { moostVite };
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
1
3
  import { createServerModuleRunner } from "vite";
2
4
  import MagicString from "magic-string";
3
- import { readFileSync } from "node:fs";
4
5
  import { builtinModules } from "node:module";
5
6
  import { Moost, clearGlobalWooks, createLogger, getMoostInfact, getMoostMate } from "moost";
6
7
 
@@ -298,7 +299,18 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
298
299
  *
299
300
  * @param {TMoostViteDevOptions} options - Configuration options for the Moost Vite plugin.
300
301
  * @returns {PluginOption} The configured Vite plugin.
301
- */ function moostVite(options) {
302
+ */ const DEFAULT_SERVER_ENTRY_CODE = `import { createSSRServer } from '@moostjs/vite/server'
303
+ const app = await createSSRServer()
304
+ await app.listen()
305
+ `;
306
+ function generatedServerEntry(root) {
307
+ const dir = resolve(root || process.cwd(), "node_modules", ".moost-vite");
308
+ mkdirSync(dir, { recursive: true });
309
+ const entryPath = resolve(dir, "server-entry.mjs");
310
+ writeFileSync(entryPath, DEFAULT_SERVER_ENTRY_CODE);
311
+ return entryPath;
312
+ }
313
+ function moostVite(options) {
302
314
  const isTest = false;
303
315
  const isProd = false;
304
316
  const externals = options.externals ?? true;
@@ -351,43 +363,49 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
351
363
  __MOOST_ENTRY__: JSON.stringify(options.entry),
352
364
  __MOOST_PREFIX__: JSON.stringify(prefix || "/api")
353
365
  };
354
- const environments = {
355
- client: { build: { outDir: cfg.build?.outDir || "dist/client" } },
356
- server: {
357
- build: {
358
- outDir: "dist",
359
- emptyOutDir: false,
360
- ssr: "server.ts",
361
- minify: false,
362
- sourcemap: !!(options.sourcemap ?? true),
363
- rollupOptions: {
364
- external: (id) => {
365
- if (id === "@moostjs/vite/server") return false;
366
- return defaultExternals.some((ext) => ext.test(id));
367
- },
368
- output: { format: "esm" }
369
- }
370
- },
371
- define: serverDefines
372
- }
373
- };
366
+ const serverEntry = options.serverEntry || generatedServerEntry(cfg.root);
367
+ const ssrInput = { server: serverEntry };
374
368
  if (options.ssrEntry) {
375
- const ssrEntryBasename = entryBasename(options.ssrEntry);
376
- environments.client.build.ssrManifest = true;
377
- environments.ssr = { build: {
378
- outDir: "dist/ssr",
379
- ssr: options.ssrEntry
380
- } };
381
- serverDefines.__MOOST_SSR_ENTRY__ = JSON.stringify(`./ssr/${ssrEntryBasename}`);
369
+ const ssrBasename = entryBasename(options.ssrEntry);
370
+ ssrInput[`ssr/${ssrBasename.replace(/\.js$/, "")}`] = options.ssrEntry;
371
+ serverDefines.__MOOST_SSR_ENTRY__ = JSON.stringify(`./ssr/${ssrBasename}`);
382
372
  serverDefines.__MOOST_SSR_OUTLET__ = JSON.stringify(options.ssrOutlet || DEFAULT_SSR_OUTLET);
383
373
  serverDefines.__MOOST_SSR_STATE__ = JSON.stringify(options.ssrState || DEFAULT_SSR_STATE);
384
374
  }
385
- const envNames = Object.keys(environments);
375
+ const outDir = cfg.build?.outDir || "dist";
386
376
  return {
377
+ ...options.ssrEntry && { appType: "custom" },
387
378
  builder: { async buildApp(builder) {
388
- for (const name of envNames) if (builder.environments[name]) await builder.build(builder.environments[name]);
379
+ rmSync(resolve(cfg.root || process.cwd(), outDir), {
380
+ recursive: true,
381
+ force: true
382
+ });
383
+ for (const env of Object.values(builder.environments)) await builder.build(env);
389
384
  } },
390
- environments
385
+ environments: {
386
+ client: { build: {
387
+ outDir: `${outDir}/client`,
388
+ emptyOutDir: false,
389
+ ...options.ssrEntry && { ssrManifest: true }
390
+ } },
391
+ ssr: {
392
+ build: {
393
+ outDir: `${outDir}/server`,
394
+ emptyOutDir: false,
395
+ minify: false,
396
+ sourcemap: !!(options.sourcemap ?? true),
397
+ rollupOptions: {
398
+ input: ssrInput,
399
+ external: (id) => {
400
+ if (id === "@moostjs/vite/server") return false;
401
+ return defaultExternals.some((ext) => ext.test(id));
402
+ },
403
+ output: { format: "esm" }
404
+ }
405
+ },
406
+ define: serverDefines
407
+ }
408
+ }
391
409
  };
392
410
  }
393
411
  const entry = cfg.build?.rollupOptions?.input || options.entry;
@@ -423,6 +441,7 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
423
441
  config.__moostViteOptions = {
424
442
  entry: options.entry,
425
443
  ssrEntry: options.ssrEntry,
444
+ serverEntry: options.serverEntry,
426
445
  prefix,
427
446
  port: options.port,
428
447
  ssrOutlet: options.ssrOutlet,
@@ -471,7 +490,7 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
471
490
  logger$1.debug("🚀 Reloading Moost App...");
472
491
  console.log();
473
492
  await ssrImport(options.entry);
474
- await new Promise((resolve) => setTimeout(resolve, 1));
493
+ await new Promise((resolve$1) => setTimeout(resolve$1, 1));
475
494
  }
476
495
  if (options.middleware && prefix) {
477
496
  const url = req.url || "";
@@ -518,7 +537,35 @@ const REG_REPLACE_EXPORT_CLASS = /(^\s*@(Injectable|Controller)\()/gm;
518
537
  delete pluginConfig.transform;
519
538
  delete pluginConfig.hotUpdate;
520
539
  }
521
- return pluginConfig;
540
+ const ssrFallbackPlugin = !isProd && !isTest && options.middleware && options.ssrEntry ? {
541
+ name: `${PLUGIN_NAME}:ssr-fallback`,
542
+ async configureServer(server) {
543
+ const ssrOutlet = options.ssrOutlet || DEFAULT_SSR_OUTLET;
544
+ const ssrState = options.ssrState || DEFAULT_SSR_STATE;
545
+ const fs = await import("node:fs/promises");
546
+ return () => {
547
+ server.middlewares.use(async (req, res, next) => {
548
+ if (req.method !== "GET") return next();
549
+ const url = req.originalUrl || req.url || "/";
550
+ try {
551
+ let template = await fs.readFile(resolve(process.cwd(), "index.html"), "utf-8");
552
+ template = await server.transformIndexHtml(url, template);
553
+ const { render } = await server.ssrLoadModule(options.ssrEntry);
554
+ const { html: appHtml, state } = await render(url);
555
+ res.statusCode = 200;
556
+ res.setHeader("Content-Type", "text/html");
557
+ res.end(template.replace(ssrOutlet, appHtml).replace(ssrState, state ? `<script>window.__SSR_STATE__=${state}</script>` : ""));
558
+ } catch (e) {
559
+ server.ssrFixStacktrace(e);
560
+ console.error(e);
561
+ res.statusCode = 500;
562
+ res.end(e.message);
563
+ }
564
+ });
565
+ };
566
+ }
567
+ } : null;
568
+ return [pluginConfig, ssrFallbackPlugin];
522
569
  }
523
570
 
524
571
  //#endregion
@@ -51,34 +51,36 @@ async function createSSRServer(options) {
51
51
  const ssrEntry = config.ssrEntry;
52
52
  const ssrOutlet$1 = config.ssrOutlet || DEFAULT_SSR_OUTLET;
53
53
  const ssrState$1 = config.ssrState || DEFAULT_SSR_STATE;
54
- if (!ssrEntry) throw new Error("createSSRServer: ssrEntry is required. Set it in moostVite() options or pass it directly.");
55
- const fs$1 = await import("node:fs/promises");
56
- const path$1 = await import("node:path");
57
- const ssrFallback = async (req, res, next) => {
58
- if (req.method !== "GET") return next();
59
- const url = req.originalUrl || req.url || "/";
60
- try {
61
- let template$1 = await fs$1.readFile(path$1.resolve(process.cwd(), "index.html"), "utf-8");
62
- template$1 = await vite.transformIndexHtml(url, template$1);
63
- const { render: render$1 } = await vite.ssrLoadModule(ssrEntry);
64
- const { html: appHtml, state } = await render$1(url);
65
- res.statusCode = 200;
66
- res.setHeader("Content-Type", "text/html");
67
- res.end(template$1.replace(ssrOutlet$1, appHtml).replace(ssrState$1, state ? `<script>window.__SSR_STATE__=${state}</script>` : ""));
68
- } catch (e) {
69
- vite.ssrFixStacktrace(e);
70
- console.error(e);
71
- res.statusCode = 500;
72
- res.end(e.message);
73
- }
74
- };
54
+ let ssrFallback = null;
55
+ if (ssrEntry) {
56
+ const fs$1 = await import("node:fs/promises");
57
+ const path$1 = await import("node:path");
58
+ ssrFallback = async (req, res, next) => {
59
+ if (req.method !== "GET") return next();
60
+ const url = req.originalUrl || req.url || "/";
61
+ try {
62
+ let template$1 = await fs$1.readFile(path$1.resolve(process.cwd(), "index.html"), "utf-8");
63
+ template$1 = await vite.transformIndexHtml(url, template$1);
64
+ const { render: render$1 } = await vite.ssrLoadModule(ssrEntry);
65
+ const { html: appHtml, state } = await render$1(url);
66
+ res.statusCode = 200;
67
+ res.setHeader("Content-Type", "text/html");
68
+ res.end(template$1.replace(ssrOutlet$1, appHtml).replace(ssrState$1, state ? `<script>window.__SSR_STATE__=${state}</script>` : ""));
69
+ } catch (e) {
70
+ vite.ssrFixStacktrace(e);
71
+ console.error(e);
72
+ res.statusCode = 500;
73
+ res.end(e.message);
74
+ }
75
+ };
76
+ }
75
77
  return {
76
78
  use(mw) {
77
79
  userMiddlewares.push(mw);
78
80
  },
79
81
  async listen(_port) {
80
82
  for (const mw of userMiddlewares) vite.middlewares.use(mw);
81
- vite.middlewares.use(ssrFallback);
83
+ if (ssrFallback) vite.middlewares.use(ssrFallback);
82
84
  await vite.listen();
83
85
  vite.printUrls();
84
86
  }
@@ -87,7 +89,7 @@ async function createSSRServer(options) {
87
89
  const fs = await import("node:fs/promises");
88
90
  const path = await import("node:path");
89
91
  const { createServer: createHttpServer } = await import("node:http");
90
- const clientDir = opts.clientDir || "dist/client";
92
+ const clientDir = opts.clientDir || path.resolve(__dirname, "../client");
91
93
  const ssrOutlet = opts.ssrOutlet || (typeof __MOOST_SSR_OUTLET__ !== "undefined" ? __MOOST_SSR_OUTLET__ : "<!--ssr-outlet-->");
92
94
  const ssrState = opts.ssrState || (typeof __MOOST_SSR_STATE__ !== "undefined" ? __MOOST_SSR_STATE__ : "<!--ssr-state-->");
93
95
  let prefix = opts.prefix || __MOOST_PREFIX__;
@@ -28,34 +28,36 @@ async function createSSRServer(options) {
28
28
  const ssrEntry = config.ssrEntry;
29
29
  const ssrOutlet$1 = config.ssrOutlet || DEFAULT_SSR_OUTLET;
30
30
  const ssrState$1 = config.ssrState || DEFAULT_SSR_STATE;
31
- if (!ssrEntry) throw new Error("createSSRServer: ssrEntry is required. Set it in moostVite() options or pass it directly.");
32
- const fs$1 = await import("node:fs/promises");
33
- const path$1 = await import("node:path");
34
- const ssrFallback = async (req, res, next) => {
35
- if (req.method !== "GET") return next();
36
- const url = req.originalUrl || req.url || "/";
37
- try {
38
- let template$1 = await fs$1.readFile(path$1.resolve(process.cwd(), "index.html"), "utf-8");
39
- template$1 = await vite.transformIndexHtml(url, template$1);
40
- const { render: render$1 } = await vite.ssrLoadModule(ssrEntry);
41
- const { html: appHtml, state } = await render$1(url);
42
- res.statusCode = 200;
43
- res.setHeader("Content-Type", "text/html");
44
- res.end(template$1.replace(ssrOutlet$1, appHtml).replace(ssrState$1, state ? `<script>window.__SSR_STATE__=${state}</script>` : ""));
45
- } catch (e) {
46
- vite.ssrFixStacktrace(e);
47
- console.error(e);
48
- res.statusCode = 500;
49
- res.end(e.message);
50
- }
51
- };
31
+ let ssrFallback = null;
32
+ if (ssrEntry) {
33
+ const fs$1 = await import("node:fs/promises");
34
+ const path$1 = await import("node:path");
35
+ ssrFallback = async (req, res, next) => {
36
+ if (req.method !== "GET") return next();
37
+ const url = req.originalUrl || req.url || "/";
38
+ try {
39
+ let template$1 = await fs$1.readFile(path$1.resolve(process.cwd(), "index.html"), "utf-8");
40
+ template$1 = await vite.transformIndexHtml(url, template$1);
41
+ const { render: render$1 } = await vite.ssrLoadModule(ssrEntry);
42
+ const { html: appHtml, state } = await render$1(url);
43
+ res.statusCode = 200;
44
+ res.setHeader("Content-Type", "text/html");
45
+ res.end(template$1.replace(ssrOutlet$1, appHtml).replace(ssrState$1, state ? `<script>window.__SSR_STATE__=${state}</script>` : ""));
46
+ } catch (e) {
47
+ vite.ssrFixStacktrace(e);
48
+ console.error(e);
49
+ res.statusCode = 500;
50
+ res.end(e.message);
51
+ }
52
+ };
53
+ }
52
54
  return {
53
55
  use(mw) {
54
56
  userMiddlewares.push(mw);
55
57
  },
56
58
  async listen(_port) {
57
59
  for (const mw of userMiddlewares) vite.middlewares.use(mw);
58
- vite.middlewares.use(ssrFallback);
60
+ if (ssrFallback) vite.middlewares.use(ssrFallback);
59
61
  await vite.listen();
60
62
  vite.printUrls();
61
63
  }
@@ -64,7 +66,7 @@ async function createSSRServer(options) {
64
66
  const fs = await import("node:fs/promises");
65
67
  const path = await import("node:path");
66
68
  const { createServer: createHttpServer } = await import("node:http");
67
- const clientDir = opts.clientDir || "dist/client";
69
+ const clientDir = opts.clientDir || path.resolve(import.meta.dirname, "../client");
68
70
  const ssrOutlet = opts.ssrOutlet || (typeof __MOOST_SSR_OUTLET__ !== "undefined" ? __MOOST_SSR_OUTLET__ : "<!--ssr-outlet-->");
69
71
  const ssrState = opts.ssrState || (typeof __MOOST_SSR_STATE__ !== "undefined" ? __MOOST_SSR_STATE__ : "<!--ssr-state-->");
70
72
  let prefix = opts.prefix || __MOOST_PREFIX__;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moostjs/vite",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Vite Dev plugin for moostjs",
5
5
  "keywords": [
6
6
  "composables",
@@ -58,8 +58,8 @@
58
58
  "peerDependencies": {
59
59
  "sirv": "^3.0.0",
60
60
  "vite": "^8.0.0",
61
- "@moostjs/event-http": "^0.6.3",
62
- "moost": "^0.6.3"
61
+ "@moostjs/event-http": "^0.6.4",
62
+ "moost": "^0.6.4"
63
63
  },
64
64
  "peerDependenciesMeta": {
65
65
  "sirv": {