@application-kit/nestjs-vite 0.1.0 → 0.3.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +58 -7
  3. package/dist/cjs/filters/index.d.ts +2 -0
  4. package/dist/cjs/filters/index.js +18 -0
  5. package/dist/cjs/filters/not-found.filter.d.ts +11 -0
  6. package/dist/cjs/filters/not-found.filter.js +48 -0
  7. package/dist/cjs/filters/unauthorized-redirect.filter.d.ts +7 -0
  8. package/dist/cjs/filters/unauthorized-redirect.filter.js +42 -0
  9. package/dist/cjs/index.d.ts +4 -0
  10. package/dist/cjs/index.js +4 -0
  11. package/dist/cjs/renderer/html-template.d.ts +5 -0
  12. package/dist/cjs/renderer/html-template.js +18 -0
  13. package/dist/cjs/renderer/index.d.ts +3 -0
  14. package/dist/cjs/renderer/index.js +19 -0
  15. package/dist/cjs/renderer/render-html-page.d.ts +3 -0
  16. package/dist/cjs/renderer/render-html-page.js +9 -0
  17. package/dist/cjs/renderer/vite-application-renderer.d.ts +12 -0
  18. package/dist/cjs/renderer/vite-application-renderer.js +44 -0
  19. package/dist/cjs/ssr-entry/ssr-entry-with-vite.d.ts +4 -4
  20. package/dist/cjs/ssr-entry/ssr-entry.interface.d.ts +9 -7
  21. package/dist/cjs/testing/fake-application-renderer.d.ts +5 -0
  22. package/dist/cjs/testing/fake-application-renderer.js +10 -0
  23. package/dist/cjs/testing/fake-application-renderer.module.d.ts +2 -0
  24. package/dist/cjs/testing/fake-application-renderer.module.js +22 -0
  25. package/dist/cjs/testing/index.d.ts +2 -0
  26. package/dist/cjs/testing/index.js +18 -0
  27. package/dist/cjs/vite-application-renderer.interface.d.ts +1 -1
  28. package/dist/cjs/vite-ssr-options.interface.d.ts +10 -0
  29. package/dist/cjs/vite-ssr-options.interface.js +2 -0
  30. package/dist/cjs/vite-ssr.module.d.ts +5 -0
  31. package/dist/cjs/vite-ssr.module.js +55 -0
  32. package/dist/cjs/vite.constants.d.ts +1 -0
  33. package/dist/cjs/vite.constants.js +2 -1
  34. package/dist/esm/filters/index.d.ts +2 -0
  35. package/dist/esm/filters/index.js +2 -0
  36. package/dist/esm/filters/not-found.filter.d.ts +11 -0
  37. package/dist/esm/filters/not-found.filter.js +45 -0
  38. package/dist/esm/filters/unauthorized-redirect.filter.d.ts +7 -0
  39. package/dist/esm/filters/unauthorized-redirect.filter.js +39 -0
  40. package/dist/esm/index.d.ts +4 -0
  41. package/dist/esm/index.js +4 -0
  42. package/dist/esm/renderer/html-template.d.ts +5 -0
  43. package/dist/esm/renderer/html-template.js +14 -0
  44. package/dist/esm/renderer/index.d.ts +3 -0
  45. package/dist/esm/renderer/index.js +3 -0
  46. package/dist/esm/renderer/render-html-page.d.ts +3 -0
  47. package/dist/esm/renderer/render-html-page.js +5 -0
  48. package/dist/esm/renderer/vite-application-renderer.d.ts +12 -0
  49. package/dist/esm/renderer/vite-application-renderer.js +41 -0
  50. package/dist/esm/ssr-entry/ssr-entry-with-vite.d.ts +4 -4
  51. package/dist/esm/ssr-entry/ssr-entry.interface.d.ts +9 -7
  52. package/dist/esm/testing/fake-application-renderer.d.ts +5 -0
  53. package/dist/esm/testing/fake-application-renderer.js +7 -0
  54. package/dist/esm/testing/fake-application-renderer.module.d.ts +2 -0
  55. package/dist/esm/testing/fake-application-renderer.module.js +19 -0
  56. package/dist/esm/testing/index.d.ts +2 -0
  57. package/dist/esm/testing/index.js +2 -0
  58. package/dist/esm/vite-application-renderer.interface.d.ts +1 -1
  59. package/dist/esm/vite-ssr-options.interface.d.ts +10 -0
  60. package/dist/esm/vite-ssr-options.interface.js +1 -0
  61. package/dist/esm/vite-ssr.module.d.ts +5 -0
  62. package/dist/esm/vite-ssr.module.js +52 -0
  63. package/dist/esm/vite.constants.d.ts +1 -0
  64. package/dist/esm/vite.constants.js +1 -0
  65. package/package.json +14 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @application-kit/nestjs-vite
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 73ac7bd: SSR-страница целиком в пакете: `ViteSsrModule.forRoot({ htmlPath, ssrEntry, isProduction, notFoundPath,
8
+ buildInitialState })` — глобальный модуль с загрузчиком `index.html` и обёрткой энтри под production или Vite dev
9
+ server, рендерером `ViteApplicationRenderer` под `VITE_APPLICATION_RENDERER` и фильтром 404 `NotFoundFilter`.
10
+ `renderHtmlPage(renderer, request, reply)` — один путь HTML-ответа для контроллера и фильтра.
11
+ `UnauthorizedRedirectFilter(signInPath)` — 401: JSON-клиенту тело 401, навигации браузера — переход на вход с
12
+ `?continue=`, GraphQL-вызову — повторный бросок драйверу. Плейсхолдеры шаблона — `STATE_PLACEHOLDER`,
13
+ `TEMPLATE_PLACEHOLDER`, `LANG_ATTRIBUTE_PATTERN`; значения состояния уходят в `<script>` JS-литералами с
14
+ экранированным `<` при любом сериализаторе потребителя. Новая peer-зависимость — `@nestjs/core`.
15
+
16
+ **Ломающее:** SSR-энтри отдаёт страницу `SsrPageInterface { lang, state, statusCode, template }` вместо
17
+ `{ context, store, template }` — ключи `window.*`, язык `<html lang>` и статус выбирает энтри; дженерик контекста
18
+ у `SsrEntryInterface` и тип `SsrEntryInterfaceRender` удалены, `initialState` обязателен. У
19
+ `ViteApplicationRendererInterface.renderApplication` нет второго параметра: данные запроса собирает
20
+ `buildInitialState`.
21
+
22
+ ## 0.2.0
23
+
24
+ ### Minor Changes
25
+
26
+ - 6ddb211: Вход `/testing`: `FakeApplicationRendererModule` — глобальный модуль с фейком рендерера под токеном
27
+ `VITE_APPLICATION_RENDERER` для спек, предмет которых не SSR (ответ — `<div id="app">{url}</div>`, статус 200), и сам
28
+ фейк `fakeApplicationRenderer`. Vite вход не тянет.
29
+
3
30
  ## 0.1.0
4
31
 
5
32
  ### Minor Changes
package/README.md CHANGED
@@ -26,15 +26,66 @@ export default defineConfig({
26
26
  статическими расширениями и внутренние пути Vite уходят в Vite, остальные — в Nest; при
27
27
  горячем обновлении приложение пересоздаётся.
28
28
 
29
- ## Рендер
29
+ ## SSR-страница
30
30
 
31
- - `ViteModule` — глобальный модуль с Vite dev server под токеном `VITE_DEV_SERVER` (только dev).
32
- - `HTML_LOADER_INTERFACE` — `HtmlLoaderWithVite` (dev, `transformIndexHtml`) или `HtmlLoader`
33
- (production, читает `index.html` один раз и кеширует).
34
- - `SSR_ENTRY_INTERFACE` — SSR-entry приложения; в dev его оборачивает `SsrEntryWithVite`:
31
+ ```ts
32
+ @Module({
33
+ imports: [
34
+ ViteSsrModule.forRoot({
35
+ buildInitialState: (request) => ({ locale: resolveLocale(request) }),
36
+ htmlPath: isProduction ? path.resolve(dirname, 'client') : dirname,
37
+ isProduction,
38
+ notFoundPath: '/404',
39
+ ssrEntry: EntrySsr,
40
+ }),
41
+ ],
42
+ })
43
+ export class ClientModule {}
44
+ ```
45
+
46
+ `ViteSsrModule.forRoot` — глобальный модуль: рендерер под `VITE_APPLICATION_RENDERER` виден контроллерам страниц в
47
+ любом модуле. Опции:
48
+
49
+ - `ssrEntry` — класс SSR-энтри (`SsrEntryInterface`). Модуль провайдит его сам, поэтому зависимости энтри должны быть
50
+ видны из глобальных модулей.
51
+ - `buildInitialState(request)` — данные запроса для энтри (локаль, идентификатор для поддержки…): запрос виден только
52
+ рендереру, а что из него нужно странице, знает приложение.
53
+ - `htmlPath` — каталог с `index.html`; `notFoundPath` — путь страницы «не найдено», его рендерит фильтр 404.
54
+ - `isProduction` — `HtmlLoader` (читает `index.html` один раз и кеширует) и энтри как есть; иначе `ViteModule`
55
+ с Vite dev server под `VITE_DEV_SERVER`, `HtmlLoaderWithVite` (`transformIndexHtml`) и обёртка `SsrEntryWithVite`:
35
56
  ошибка рендера получает стектрейс по исходникам (`ssrFixStacktrace`) и уходит как `PluginRenderError`.
36
- - `VITE_APPLICATION_RENDERER` — токен рендерера приложения (`ViteApplicationRendererInterface`),
37
- реализация остаётся за приложением.
57
+
58
+ Энтри отдаёт страницу `SsrPageInterface`: `template` — разметка приложения, `state` — ключ `window.*` → строка,
59
+ `lang` — значение `<html lang>`, `statusCode` — статус ответа. Рендерер вставляет её в `index.html` приложения по
60
+ плейсхолдерам:
61
+
62
+ | Константа | В шаблоне | Чем заменяется |
63
+ |---|---|---|
64
+ | `STATE_PLACEHOLDER` | `<script id="state"></script>` | `<script>` с присваиваниями `window[ключ] = значение` |
65
+ | `TEMPLATE_PLACEHOLDER` | `<div id="app"></div>` | тот же `div` с разметкой `template` внутри |
66
+ | `LANG_ATTRIBUTE_PATTERN` | `<html lang="…">` с любым значением | `lang` страницы |
67
+
68
+ Значения состояния уходят JS-литералами строки с экранированным `<`: `</script>` в данных не закроет тег, какой бы
69
+ сериализатор ни выбрало приложение. Клиентский энтри читает их до гидрации.
70
+
71
+ `renderHtmlPage(renderer, request, reply)` — один путь HTML-ответа: ставит статус рендера и `Content-Type: text/html`
72
+ и возвращает HTML. Контроллер с `@Res({ passthrough: true })` возвращает его, фильтр — отправляет. Декоратор
73
+ `@Header('Content-Type')` на маршруте страницы не нужен.
74
+
75
+ ### Фильтры
76
+
77
+ - `NotFoundFilter` — регистрирует `ViteSsrModule`: `NotFoundException` отвечает отрендеренной страницей
78
+ `notFoundPath` со статусом рендера.
79
+ - `UnauthorizedRedirectFilter(signInPath)` — регистрирует модуль, которому принадлежит страница входа:
80
+ `{ provide: APP_FILTER, useValue: new UnauthorizedRedirectFilter('/sign-in') }`. JSON-клиент (`Content-Type:
81
+ application/json`) получает 401 с JSON-телом, навигация браузера — 302 на `<signInPath>?continue=<url>`,
82
+ GraphQL-вызов — повторный бросок исключения драйверу GraphQL.
83
+
84
+ ## Тесты потребителя
85
+
86
+ Вход `@application-kit/nestjs-vite/testing` — `FakeApplicationRendererModule`: глобальный модуль с фейком под
87
+ `VITE_APPLICATION_RENDERER` для спек, предмет которых не SSR (контроллеры зависят от рендерера, а настоящий поднимает
88
+ Vite). Фейк отвечает `<div id="app">{url}</div>` со статусом 200; сам он — `fakeApplicationRenderer`. Vite вход не тянет.
38
89
 
39
90
  ## Формат
40
91
 
@@ -0,0 +1,2 @@
1
+ export * from './not-found.filter.js';
2
+ export * from './unauthorized-redirect.filter.js';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./not-found.filter.js"), exports);
18
+ __exportStar(require("./unauthorized-redirect.filter.js"), exports);
@@ -0,0 +1,11 @@
1
+ import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
2
+ import { NotFoundException } from '@nestjs/common';
3
+ import { ModuleRef } from '@nestjs/core';
4
+ import type { ViteSsrOptionsInterface } from '../vite-ssr-options.interface.js';
5
+ export declare class NotFoundFilter implements ExceptionFilter<NotFoundException> {
6
+ private readonly moduleReference;
7
+ private readonly options;
8
+ constructor(moduleReference: ModuleRef, options: Pick<ViteSsrOptionsInterface, 'notFoundPath'>);
9
+ catch(_exception: NotFoundException, host: ArgumentsHost): Promise<void>;
10
+ private resolveRenderer;
11
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.NotFoundFilter = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const core_1 = require("@nestjs/core");
18
+ const render_html_page_js_1 = require("../renderer/render-html-page.js");
19
+ const vite_application_renderer_interface_js_1 = require("../vite-application-renderer.interface.js");
20
+ const vite_constants_js_1 = require("../vite.constants.js");
21
+ let NotFoundFilter = class NotFoundFilter {
22
+ moduleReference;
23
+ options;
24
+ constructor(moduleReference, options) {
25
+ this.moduleReference = moduleReference;
26
+ this.options = options;
27
+ }
28
+ async catch(_exception, host) {
29
+ const context = host.switchToHttp();
30
+ const reply = context.getResponse();
31
+ const request = context.getRequest();
32
+ const renderer = await this.resolveRenderer(request);
33
+ request.raw.url = this.options.notFoundPath;
34
+ await reply.send(await (0, render_html_page_js_1.renderHtmlPage)(renderer, request, reply));
35
+ }
36
+ async resolveRenderer(request) {
37
+ const contextId = core_1.ContextIdFactory.getByRequest(request);
38
+ this.moduleReference.registerRequestByContextId(request, contextId);
39
+ return this.moduleReference.resolve(vite_application_renderer_interface_js_1.VITE_APPLICATION_RENDERER, contextId);
40
+ }
41
+ };
42
+ exports.NotFoundFilter = NotFoundFilter;
43
+ exports.NotFoundFilter = NotFoundFilter = __decorate([
44
+ (0, common_1.Catch)(common_1.NotFoundException),
45
+ __param(0, (0, common_1.Inject)(core_1.ModuleRef)),
46
+ __param(1, (0, common_1.Inject)(vite_constants_js_1.VITE_SSR_OPTIONS)),
47
+ __metadata("design:paramtypes", [core_1.ModuleRef, Object])
48
+ ], NotFoundFilter);
@@ -0,0 +1,7 @@
1
+ import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
2
+ import { UnauthorizedException } from '@nestjs/common';
3
+ export declare class UnauthorizedRedirectFilter implements ExceptionFilter<UnauthorizedException> {
4
+ private readonly signInPath;
5
+ constructor(signInPath: string);
6
+ catch(exception: UnauthorizedException, host: ArgumentsHost): Promise<void>;
7
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.UnauthorizedRedirectFilter = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ let UnauthorizedRedirectFilter = class UnauthorizedRedirectFilter {
15
+ signInPath;
16
+ constructor(signInPath) {
17
+ this.signInPath = signInPath;
18
+ }
19
+ async catch(exception, host) {
20
+ if (host.getType() === 'graphql') {
21
+ throw exception;
22
+ }
23
+ const context = host.switchToHttp();
24
+ const reply = context.getResponse();
25
+ const request = context.getRequest();
26
+ const contentType = request.headers['content-type'];
27
+ if (typeof contentType === 'string' && contentType.includes('application/json')) {
28
+ await reply.status(common_1.HttpStatus.UNAUTHORIZED).send({
29
+ error: exception.name,
30
+ message: exception.message,
31
+ statusCode: common_1.HttpStatus.UNAUTHORIZED,
32
+ });
33
+ return;
34
+ }
35
+ await reply.status(common_1.HttpStatus.FOUND).redirect(`${this.signInPath}?continue=${request.url}`);
36
+ }
37
+ };
38
+ exports.UnauthorizedRedirectFilter = UnauthorizedRedirectFilter;
39
+ exports.UnauthorizedRedirectFilter = UnauthorizedRedirectFilter = __decorate([
40
+ (0, common_1.Catch)(common_1.UnauthorizedException),
41
+ __metadata("design:paramtypes", [String])
42
+ ], UnauthorizedRedirectFilter);
@@ -1,6 +1,10 @@
1
+ export * from './filters/index.js';
1
2
  export * from './html-loader/index.js';
3
+ export * from './renderer/index.js';
2
4
  export * from './ssr-entry/index.js';
3
5
  export * from './vite-plugin/index.js';
4
6
  export * from './vite-application-renderer.interface.js';
7
+ export * from './vite-ssr-options.interface.js';
8
+ export * from './vite-ssr.module.js';
5
9
  export * from './vite.constants.js';
6
10
  export * from './vite.module.js';
package/dist/cjs/index.js CHANGED
@@ -14,9 +14,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./filters/index.js"), exports);
17
18
  __exportStar(require("./html-loader/index.js"), exports);
19
+ __exportStar(require("./renderer/index.js"), exports);
18
20
  __exportStar(require("./ssr-entry/index.js"), exports);
19
21
  __exportStar(require("./vite-plugin/index.js"), exports);
20
22
  __exportStar(require("./vite-application-renderer.interface.js"), exports);
23
+ __exportStar(require("./vite-ssr-options.interface.js"), exports);
24
+ __exportStar(require("./vite-ssr.module.js"), exports);
21
25
  __exportStar(require("./vite.constants.js"), exports);
22
26
  __exportStar(require("./vite.module.js"), exports);
@@ -0,0 +1,5 @@
1
+ import type { SsrPageInterface } from '../ssr-entry/ssr-entry.interface.js';
2
+ export declare const STATE_PLACEHOLDER = "<script id=\"state\"></script>";
3
+ export declare const TEMPLATE_PLACEHOLDER = "<div id=\"app\"></div>";
4
+ export declare const LANG_ATTRIBUTE_PATTERN: RegExp;
5
+ export declare const fillHtmlTemplate: (html: string, page: SsrPageInterface) => string;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fillHtmlTemplate = exports.LANG_ATTRIBUTE_PATTERN = exports.TEMPLATE_PLACEHOLDER = exports.STATE_PLACEHOLDER = void 0;
4
+ exports.STATE_PLACEHOLDER = '<script id="state"></script>';
5
+ exports.TEMPLATE_PLACEHOLDER = '<div id="app"></div>';
6
+ exports.LANG_ATTRIBUTE_PATTERN = /<html\s+lang="[^"]*"/u;
7
+ const toScriptLiteral = (value) => JSON.stringify(value).replaceAll('<', String.raw `\u003C`);
8
+ const toAttributeValue = (value) => value.replaceAll('&', '&amp;').replaceAll('"', '&quot;');
9
+ const toStateScript = (state) => {
10
+ const assignments = Object.entries(state)
11
+ .map(([key, value]) => `window[${toScriptLiteral(key)}] = ${toScriptLiteral(value)};`);
12
+ return `<script>${assignments.join('')}</script>`;
13
+ };
14
+ const fillHtmlTemplate = (html, page) => html
15
+ .replace(exports.STATE_PLACEHOLDER, () => toStateScript(page.state))
16
+ .replace(exports.TEMPLATE_PLACEHOLDER, () => `<div id="app">${page.template}</div>`)
17
+ .replace(exports.LANG_ATTRIBUTE_PATTERN, () => `<html lang="${toAttributeValue(page.lang)}"`);
18
+ exports.fillHtmlTemplate = fillHtmlTemplate;
@@ -0,0 +1,3 @@
1
+ export * from './html-template.js';
2
+ export * from './render-html-page.js';
3
+ export * from './vite-application-renderer.js';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./html-template.js"), exports);
18
+ __exportStar(require("./render-html-page.js"), exports);
19
+ __exportStar(require("./vite-application-renderer.js"), exports);
@@ -0,0 +1,3 @@
1
+ import type { FastifyReply, FastifyRequest } from 'fastify';
2
+ import type { ViteApplicationRendererInterface } from '../vite-application-renderer.interface.js';
3
+ export declare const renderHtmlPage: (renderer: ViteApplicationRendererInterface, request: FastifyRequest, reply: FastifyReply) => Promise<string>;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderHtmlPage = void 0;
4
+ const renderHtmlPage = async (renderer, request, reply) => {
5
+ const { html, statusCode } = await renderer.renderApplication(request);
6
+ void reply.code(statusCode).header('Content-Type', 'text/html');
7
+ return html;
8
+ };
9
+ exports.renderHtmlPage = renderHtmlPage;
@@ -0,0 +1,12 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { HtmlLoaderInterface } from '../html-loader/html-loader.interface.js';
3
+ import type { SsrEntryInterface } from '../ssr-entry/ssr-entry.interface.js';
4
+ import type { ApplicationInterface, ViteApplicationRendererInterface } from '../vite-application-renderer.interface.js';
5
+ import type { ViteSsrOptionsInterface } from '../vite-ssr-options.interface.js';
6
+ export declare class ViteApplicationRenderer implements ViteApplicationRendererInterface {
7
+ private readonly ssrEntry;
8
+ private readonly htmlLoader;
9
+ private readonly options;
10
+ constructor(ssrEntry: SsrEntryInterface, htmlLoader: HtmlLoaderInterface, options: Pick<ViteSsrOptionsInterface, 'buildInitialState' | 'htmlPath'>);
11
+ renderApplication(request: FastifyRequest): Promise<ApplicationInterface>;
12
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ViteApplicationRenderer = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const html_loader_interface_js_1 = require("../html-loader/html-loader.interface.js");
18
+ const ssr_entry_interface_js_1 = require("../ssr-entry/ssr-entry.interface.js");
19
+ const vite_constants_js_1 = require("../vite.constants.js");
20
+ const html_template_js_1 = require("./html-template.js");
21
+ let ViteApplicationRenderer = class ViteApplicationRenderer {
22
+ ssrEntry;
23
+ htmlLoader;
24
+ options;
25
+ constructor(ssrEntry, htmlLoader, options) {
26
+ this.ssrEntry = ssrEntry;
27
+ this.htmlLoader = htmlLoader;
28
+ this.options = options;
29
+ }
30
+ async renderApplication(request) {
31
+ const { url } = request;
32
+ const html = await this.htmlLoader.loadHtml(this.options.htmlPath, url);
33
+ const page = await this.ssrEntry.render(url, this.options.buildInitialState(request));
34
+ return { html: (0, html_template_js_1.fillHtmlTemplate)(html, page), statusCode: page.statusCode };
35
+ }
36
+ };
37
+ exports.ViteApplicationRenderer = ViteApplicationRenderer;
38
+ exports.ViteApplicationRenderer = ViteApplicationRenderer = __decorate([
39
+ (0, common_1.Injectable)(),
40
+ __param(0, (0, common_1.Inject)(ssr_entry_interface_js_1.SSR_ENTRY_INTERFACE)),
41
+ __param(1, (0, common_1.Inject)(html_loader_interface_js_1.HTML_LOADER_INTERFACE)),
42
+ __param(2, (0, common_1.Inject)(vite_constants_js_1.VITE_SSR_OPTIONS)),
43
+ __metadata("design:paramtypes", [Object, Object, Object])
44
+ ], ViteApplicationRenderer);
@@ -1,8 +1,8 @@
1
1
  import type { ViteDevServer } from 'vite';
2
- import type { SsrEntryInterface, SsrEntryInterfaceRender } from './ssr-entry.interface.js';
3
- export declare class SsrEntryWithVite implements SsrEntryInterface<unknown> {
2
+ import type { SsrEntryInterface, SsrPageInterface } from './ssr-entry.interface.js';
3
+ export declare class SsrEntryWithVite implements SsrEntryInterface {
4
4
  private readonly originSsrEntry;
5
5
  private readonly viteDevelopmentServer;
6
- constructor(originSsrEntry: SsrEntryInterface<unknown>, viteDevelopmentServer: ViteDevServer);
7
- render(url: string, initialState?: Record<string, unknown>): SsrEntryInterfaceRender<unknown>;
6
+ constructor(originSsrEntry: SsrEntryInterface, viteDevelopmentServer: ViteDevServer);
7
+ render(url: string, initialState: Record<string, unknown>): Promise<SsrPageInterface>;
8
8
  }
@@ -1,9 +1,11 @@
1
+ import type { HttpStatus } from '@nestjs/common';
1
2
  export declare const SSR_ENTRY_INTERFACE: unique symbol;
2
- export interface SsrEntryInterface<SsrContext> {
3
- render(url: string, initialState?: Record<string, unknown>): SsrEntryInterfaceRender<SsrContext>;
3
+ export interface SsrEntryInterface {
4
+ render(url: string, initialState: Record<string, unknown>): Promise<SsrPageInterface>;
5
+ }
6
+ export interface SsrPageInterface {
7
+ readonly lang: string;
8
+ readonly state: Readonly<Record<string, string>>;
9
+ readonly statusCode: HttpStatus;
10
+ readonly template: string;
4
11
  }
5
- export type SsrEntryInterfaceRender<T> = Promise<{
6
- context: T;
7
- store: string;
8
- template: string;
9
- }>;
@@ -0,0 +1,5 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { ApplicationInterface } from '../vite-application-renderer.interface.js';
3
+ export declare const fakeApplicationRenderer: {
4
+ renderApplication: (request: Pick<FastifyRequest, "url">) => Promise<ApplicationInterface>;
5
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fakeApplicationRenderer = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ exports.fakeApplicationRenderer = {
6
+ renderApplication: async (request) => {
7
+ await Promise.resolve();
8
+ return { html: `<div id="app">${request.url}</div>`, statusCode: common_1.HttpStatus.OK };
9
+ },
10
+ };
@@ -0,0 +1,2 @@
1
+ export declare class FakeApplicationRendererModule {
2
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.FakeApplicationRendererModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const vite_application_renderer_interface_js_1 = require("../vite-application-renderer.interface.js");
12
+ const fake_application_renderer_js_1 = require("./fake-application-renderer.js");
13
+ let FakeApplicationRendererModule = class FakeApplicationRendererModule {
14
+ };
15
+ exports.FakeApplicationRendererModule = FakeApplicationRendererModule;
16
+ exports.FakeApplicationRendererModule = FakeApplicationRendererModule = __decorate([
17
+ (0, common_1.Global)(),
18
+ (0, common_1.Module)({
19
+ exports: [vite_application_renderer_interface_js_1.VITE_APPLICATION_RENDERER],
20
+ providers: [{ provide: vite_application_renderer_interface_js_1.VITE_APPLICATION_RENDERER, useValue: fake_application_renderer_js_1.fakeApplicationRenderer }],
21
+ })
22
+ ], FakeApplicationRendererModule);
@@ -0,0 +1,2 @@
1
+ export * from './fake-application-renderer.js';
2
+ export * from './fake-application-renderer.module.js';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./fake-application-renderer.js"), exports);
18
+ __exportStar(require("./fake-application-renderer.module.js"), exports);
@@ -5,7 +5,7 @@ interface AppInterface {
5
5
  statusCode: HttpStatus;
6
6
  }
7
7
  interface ViteAppRendererInterface {
8
- renderApplication(request: FastifyRequest, initialState?: Record<string, unknown>): Promise<AppInterface>;
8
+ renderApplication(request: FastifyRequest): Promise<AppInterface>;
9
9
  }
10
10
  export type { AppInterface as ApplicationInterface, ViteAppRendererInterface as ViteApplicationRendererInterface };
11
11
  export declare const VITE_APPLICATION_RENDERER: unique symbol;
@@ -0,0 +1,10 @@
1
+ import type { Type } from '@nestjs/common';
2
+ import type { FastifyRequest } from 'fastify';
3
+ import type { SsrEntryInterface } from './ssr-entry/ssr-entry.interface.js';
4
+ export interface ViteSsrOptionsInterface {
5
+ buildInitialState(request: FastifyRequest): Record<string, unknown>;
6
+ readonly htmlPath: string;
7
+ readonly isProduction: boolean;
8
+ readonly notFoundPath: string;
9
+ readonly ssrEntry: Type<SsrEntryInterface>;
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ import type { DynamicModule } from '@nestjs/common';
2
+ import type { ViteSsrOptionsInterface } from './vite-ssr-options.interface.js';
3
+ export declare class ViteSsrModule {
4
+ static forRoot(options: ViteSsrOptionsInterface): DynamicModule;
5
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ViteSsrModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const core_1 = require("@nestjs/core");
12
+ const not_found_filter_js_1 = require("./filters/not-found.filter.js");
13
+ const html_loader_with_vite_js_1 = require("./html-loader/html-loader-with-vite.js");
14
+ const html_loader_interface_js_1 = require("./html-loader/html-loader.interface.js");
15
+ const html_loader_js_1 = require("./html-loader/html-loader.js");
16
+ const vite_application_renderer_js_1 = require("./renderer/vite-application-renderer.js");
17
+ const ssr_entry_with_vite_js_1 = require("./ssr-entry/ssr-entry-with-vite.js");
18
+ const ssr_entry_interface_js_1 = require("./ssr-entry/ssr-entry.interface.js");
19
+ const vite_application_renderer_interface_js_1 = require("./vite-application-renderer.interface.js");
20
+ const vite_constants_js_1 = require("./vite.constants.js");
21
+ const vite_module_js_1 = require("./vite.module.js");
22
+ const productionProviders = (ssrEntry) => [
23
+ { provide: html_loader_interface_js_1.HTML_LOADER_INTERFACE, useClass: html_loader_js_1.HtmlLoader },
24
+ { provide: ssr_entry_interface_js_1.SSR_ENTRY_INTERFACE, useExisting: ssrEntry },
25
+ ];
26
+ const developmentProviders = (ssrEntry) => [
27
+ { provide: html_loader_interface_js_1.HTML_LOADER_INTERFACE, useClass: html_loader_with_vite_js_1.HtmlLoaderWithVite },
28
+ {
29
+ inject: [ssrEntry, vite_constants_js_1.VITE_DEV_SERVER],
30
+ provide: ssr_entry_interface_js_1.SSR_ENTRY_INTERFACE,
31
+ useFactory: (entry, server) => new ssr_entry_with_vite_js_1.SsrEntryWithVite(entry, server),
32
+ },
33
+ ];
34
+ let ViteSsrModule = class ViteSsrModule {
35
+ static forRoot(options) {
36
+ const { isProduction, ssrEntry } = options;
37
+ return {
38
+ exports: [vite_application_renderer_interface_js_1.VITE_APPLICATION_RENDERER],
39
+ global: true,
40
+ imports: isProduction ? [] : [vite_module_js_1.ViteModule],
41
+ module: this,
42
+ providers: [
43
+ { provide: vite_constants_js_1.VITE_SSR_OPTIONS, useValue: options },
44
+ ssrEntry,
45
+ ...(isProduction ? productionProviders(ssrEntry) : developmentProviders(ssrEntry)),
46
+ { provide: vite_application_renderer_interface_js_1.VITE_APPLICATION_RENDERER, useClass: vite_application_renderer_js_1.ViteApplicationRenderer },
47
+ { provide: core_1.APP_FILTER, useClass: not_found_filter_js_1.NotFoundFilter },
48
+ ],
49
+ };
50
+ }
51
+ };
52
+ exports.ViteSsrModule = ViteSsrModule;
53
+ exports.ViteSsrModule = ViteSsrModule = __decorate([
54
+ (0, common_1.Module)({})
55
+ ], ViteSsrModule);
@@ -1 +1,2 @@
1
1
  export declare const VITE_DEV_SERVER: unique symbol;
2
+ export declare const VITE_SSR_OPTIONS: unique symbol;
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VITE_DEV_SERVER = void 0;
3
+ exports.VITE_SSR_OPTIONS = exports.VITE_DEV_SERVER = void 0;
4
4
  exports.VITE_DEV_SERVER = Symbol('VITE_DEV_SERVER');
5
+ exports.VITE_SSR_OPTIONS = Symbol('VITE_SSR_OPTIONS');
@@ -0,0 +1,2 @@
1
+ export * from './not-found.filter.js';
2
+ export * from './unauthorized-redirect.filter.js';
@@ -0,0 +1,2 @@
1
+ export * from './not-found.filter.js';
2
+ export * from './unauthorized-redirect.filter.js';
@@ -0,0 +1,11 @@
1
+ import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
2
+ import { NotFoundException } from '@nestjs/common';
3
+ import { ModuleRef } from '@nestjs/core';
4
+ import type { ViteSsrOptionsInterface } from '../vite-ssr-options.interface.js';
5
+ export declare class NotFoundFilter implements ExceptionFilter<NotFoundException> {
6
+ private readonly moduleReference;
7
+ private readonly options;
8
+ constructor(moduleReference: ModuleRef, options: Pick<ViteSsrOptionsInterface, 'notFoundPath'>);
9
+ catch(_exception: NotFoundException, host: ArgumentsHost): Promise<void>;
10
+ private resolveRenderer;
11
+ }
@@ -0,0 +1,45 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Catch, Inject, NotFoundException } from '@nestjs/common';
14
+ import { ContextIdFactory, ModuleRef } from '@nestjs/core';
15
+ import { renderHtmlPage } from '../renderer/render-html-page.js';
16
+ import { VITE_APPLICATION_RENDERER } from '../vite-application-renderer.interface.js';
17
+ import { VITE_SSR_OPTIONS } from '../vite.constants.js';
18
+ let NotFoundFilter = class NotFoundFilter {
19
+ moduleReference;
20
+ options;
21
+ constructor(moduleReference, options) {
22
+ this.moduleReference = moduleReference;
23
+ this.options = options;
24
+ }
25
+ async catch(_exception, host) {
26
+ const context = host.switchToHttp();
27
+ const reply = context.getResponse();
28
+ const request = context.getRequest();
29
+ const renderer = await this.resolveRenderer(request);
30
+ request.raw.url = this.options.notFoundPath;
31
+ await reply.send(await renderHtmlPage(renderer, request, reply));
32
+ }
33
+ async resolveRenderer(request) {
34
+ const contextId = ContextIdFactory.getByRequest(request);
35
+ this.moduleReference.registerRequestByContextId(request, contextId);
36
+ return this.moduleReference.resolve(VITE_APPLICATION_RENDERER, contextId);
37
+ }
38
+ };
39
+ NotFoundFilter = __decorate([
40
+ Catch(NotFoundException),
41
+ __param(0, Inject(ModuleRef)),
42
+ __param(1, Inject(VITE_SSR_OPTIONS)),
43
+ __metadata("design:paramtypes", [ModuleRef, Object])
44
+ ], NotFoundFilter);
45
+ export { NotFoundFilter };
@@ -0,0 +1,7 @@
1
+ import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
2
+ import { UnauthorizedException } from '@nestjs/common';
3
+ export declare class UnauthorizedRedirectFilter implements ExceptionFilter<UnauthorizedException> {
4
+ private readonly signInPath;
5
+ constructor(signInPath: string);
6
+ catch(exception: UnauthorizedException, host: ArgumentsHost): Promise<void>;
7
+ }
@@ -0,0 +1,39 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Catch, HttpStatus, UnauthorizedException } from '@nestjs/common';
11
+ let UnauthorizedRedirectFilter = class UnauthorizedRedirectFilter {
12
+ signInPath;
13
+ constructor(signInPath) {
14
+ this.signInPath = signInPath;
15
+ }
16
+ async catch(exception, host) {
17
+ if (host.getType() === 'graphql') {
18
+ throw exception;
19
+ }
20
+ const context = host.switchToHttp();
21
+ const reply = context.getResponse();
22
+ const request = context.getRequest();
23
+ const contentType = request.headers['content-type'];
24
+ if (typeof contentType === 'string' && contentType.includes('application/json')) {
25
+ await reply.status(HttpStatus.UNAUTHORIZED).send({
26
+ error: exception.name,
27
+ message: exception.message,
28
+ statusCode: HttpStatus.UNAUTHORIZED,
29
+ });
30
+ return;
31
+ }
32
+ await reply.status(HttpStatus.FOUND).redirect(`${this.signInPath}?continue=${request.url}`);
33
+ }
34
+ };
35
+ UnauthorizedRedirectFilter = __decorate([
36
+ Catch(UnauthorizedException),
37
+ __metadata("design:paramtypes", [String])
38
+ ], UnauthorizedRedirectFilter);
39
+ export { UnauthorizedRedirectFilter };
@@ -1,6 +1,10 @@
1
+ export * from './filters/index.js';
1
2
  export * from './html-loader/index.js';
3
+ export * from './renderer/index.js';
2
4
  export * from './ssr-entry/index.js';
3
5
  export * from './vite-plugin/index.js';
4
6
  export * from './vite-application-renderer.interface.js';
7
+ export * from './vite-ssr-options.interface.js';
8
+ export * from './vite-ssr.module.js';
5
9
  export * from './vite.constants.js';
6
10
  export * from './vite.module.js';
package/dist/esm/index.js CHANGED
@@ -1,6 +1,10 @@
1
+ export * from './filters/index.js';
1
2
  export * from './html-loader/index.js';
3
+ export * from './renderer/index.js';
2
4
  export * from './ssr-entry/index.js';
3
5
  export * from './vite-plugin/index.js';
4
6
  export * from './vite-application-renderer.interface.js';
7
+ export * from './vite-ssr-options.interface.js';
8
+ export * from './vite-ssr.module.js';
5
9
  export * from './vite.constants.js';
6
10
  export * from './vite.module.js';
@@ -0,0 +1,5 @@
1
+ import type { SsrPageInterface } from '../ssr-entry/ssr-entry.interface.js';
2
+ export declare const STATE_PLACEHOLDER = "<script id=\"state\"></script>";
3
+ export declare const TEMPLATE_PLACEHOLDER = "<div id=\"app\"></div>";
4
+ export declare const LANG_ATTRIBUTE_PATTERN: RegExp;
5
+ export declare const fillHtmlTemplate: (html: string, page: SsrPageInterface) => string;
@@ -0,0 +1,14 @@
1
+ export const STATE_PLACEHOLDER = '<script id="state"></script>';
2
+ export const TEMPLATE_PLACEHOLDER = '<div id="app"></div>';
3
+ export const LANG_ATTRIBUTE_PATTERN = /<html\s+lang="[^"]*"/u;
4
+ const toScriptLiteral = (value) => JSON.stringify(value).replaceAll('<', String.raw `\u003C`);
5
+ const toAttributeValue = (value) => value.replaceAll('&', '&amp;').replaceAll('"', '&quot;');
6
+ const toStateScript = (state) => {
7
+ const assignments = Object.entries(state)
8
+ .map(([key, value]) => `window[${toScriptLiteral(key)}] = ${toScriptLiteral(value)};`);
9
+ return `<script>${assignments.join('')}</script>`;
10
+ };
11
+ export const fillHtmlTemplate = (html, page) => html
12
+ .replace(STATE_PLACEHOLDER, () => toStateScript(page.state))
13
+ .replace(TEMPLATE_PLACEHOLDER, () => `<div id="app">${page.template}</div>`)
14
+ .replace(LANG_ATTRIBUTE_PATTERN, () => `<html lang="${toAttributeValue(page.lang)}"`);
@@ -0,0 +1,3 @@
1
+ export * from './html-template.js';
2
+ export * from './render-html-page.js';
3
+ export * from './vite-application-renderer.js';
@@ -0,0 +1,3 @@
1
+ export * from './html-template.js';
2
+ export * from './render-html-page.js';
3
+ export * from './vite-application-renderer.js';
@@ -0,0 +1,3 @@
1
+ import type { FastifyReply, FastifyRequest } from 'fastify';
2
+ import type { ViteApplicationRendererInterface } from '../vite-application-renderer.interface.js';
3
+ export declare const renderHtmlPage: (renderer: ViteApplicationRendererInterface, request: FastifyRequest, reply: FastifyReply) => Promise<string>;
@@ -0,0 +1,5 @@
1
+ export const renderHtmlPage = async (renderer, request, reply) => {
2
+ const { html, statusCode } = await renderer.renderApplication(request);
3
+ void reply.code(statusCode).header('Content-Type', 'text/html');
4
+ return html;
5
+ };
@@ -0,0 +1,12 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { HtmlLoaderInterface } from '../html-loader/html-loader.interface.js';
3
+ import type { SsrEntryInterface } from '../ssr-entry/ssr-entry.interface.js';
4
+ import type { ApplicationInterface, ViteApplicationRendererInterface } from '../vite-application-renderer.interface.js';
5
+ import type { ViteSsrOptionsInterface } from '../vite-ssr-options.interface.js';
6
+ export declare class ViteApplicationRenderer implements ViteApplicationRendererInterface {
7
+ private readonly ssrEntry;
8
+ private readonly htmlLoader;
9
+ private readonly options;
10
+ constructor(ssrEntry: SsrEntryInterface, htmlLoader: HtmlLoaderInterface, options: Pick<ViteSsrOptionsInterface, 'buildInitialState' | 'htmlPath'>);
11
+ renderApplication(request: FastifyRequest): Promise<ApplicationInterface>;
12
+ }
@@ -0,0 +1,41 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Inject, Injectable } from '@nestjs/common';
14
+ import { HTML_LOADER_INTERFACE } from '../html-loader/html-loader.interface.js';
15
+ import { SSR_ENTRY_INTERFACE } from '../ssr-entry/ssr-entry.interface.js';
16
+ import { VITE_SSR_OPTIONS } from '../vite.constants.js';
17
+ import { fillHtmlTemplate } from './html-template.js';
18
+ let ViteApplicationRenderer = class ViteApplicationRenderer {
19
+ ssrEntry;
20
+ htmlLoader;
21
+ options;
22
+ constructor(ssrEntry, htmlLoader, options) {
23
+ this.ssrEntry = ssrEntry;
24
+ this.htmlLoader = htmlLoader;
25
+ this.options = options;
26
+ }
27
+ async renderApplication(request) {
28
+ const { url } = request;
29
+ const html = await this.htmlLoader.loadHtml(this.options.htmlPath, url);
30
+ const page = await this.ssrEntry.render(url, this.options.buildInitialState(request));
31
+ return { html: fillHtmlTemplate(html, page), statusCode: page.statusCode };
32
+ }
33
+ };
34
+ ViteApplicationRenderer = __decorate([
35
+ Injectable(),
36
+ __param(0, Inject(SSR_ENTRY_INTERFACE)),
37
+ __param(1, Inject(HTML_LOADER_INTERFACE)),
38
+ __param(2, Inject(VITE_SSR_OPTIONS)),
39
+ __metadata("design:paramtypes", [Object, Object, Object])
40
+ ], ViteApplicationRenderer);
41
+ export { ViteApplicationRenderer };
@@ -1,8 +1,8 @@
1
1
  import type { ViteDevServer } from 'vite';
2
- import type { SsrEntryInterface, SsrEntryInterfaceRender } from './ssr-entry.interface.js';
3
- export declare class SsrEntryWithVite implements SsrEntryInterface<unknown> {
2
+ import type { SsrEntryInterface, SsrPageInterface } from './ssr-entry.interface.js';
3
+ export declare class SsrEntryWithVite implements SsrEntryInterface {
4
4
  private readonly originSsrEntry;
5
5
  private readonly viteDevelopmentServer;
6
- constructor(originSsrEntry: SsrEntryInterface<unknown>, viteDevelopmentServer: ViteDevServer);
7
- render(url: string, initialState?: Record<string, unknown>): SsrEntryInterfaceRender<unknown>;
6
+ constructor(originSsrEntry: SsrEntryInterface, viteDevelopmentServer: ViteDevServer);
7
+ render(url: string, initialState: Record<string, unknown>): Promise<SsrPageInterface>;
8
8
  }
@@ -1,9 +1,11 @@
1
+ import type { HttpStatus } from '@nestjs/common';
1
2
  export declare const SSR_ENTRY_INTERFACE: unique symbol;
2
- export interface SsrEntryInterface<SsrContext> {
3
- render(url: string, initialState?: Record<string, unknown>): SsrEntryInterfaceRender<SsrContext>;
3
+ export interface SsrEntryInterface {
4
+ render(url: string, initialState: Record<string, unknown>): Promise<SsrPageInterface>;
5
+ }
6
+ export interface SsrPageInterface {
7
+ readonly lang: string;
8
+ readonly state: Readonly<Record<string, string>>;
9
+ readonly statusCode: HttpStatus;
10
+ readonly template: string;
4
11
  }
5
- export type SsrEntryInterfaceRender<T> = Promise<{
6
- context: T;
7
- store: string;
8
- template: string;
9
- }>;
@@ -0,0 +1,5 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { ApplicationInterface } from '../vite-application-renderer.interface.js';
3
+ export declare const fakeApplicationRenderer: {
4
+ renderApplication: (request: Pick<FastifyRequest, "url">) => Promise<ApplicationInterface>;
5
+ };
@@ -0,0 +1,7 @@
1
+ import { HttpStatus } from '@nestjs/common';
2
+ export const fakeApplicationRenderer = {
3
+ renderApplication: async (request) => {
4
+ await Promise.resolve();
5
+ return { html: `<div id="app">${request.url}</div>`, statusCode: HttpStatus.OK };
6
+ },
7
+ };
@@ -0,0 +1,2 @@
1
+ export declare class FakeApplicationRendererModule {
2
+ }
@@ -0,0 +1,19 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Global, Module } from '@nestjs/common';
8
+ import { VITE_APPLICATION_RENDERER } from '../vite-application-renderer.interface.js';
9
+ import { fakeApplicationRenderer } from './fake-application-renderer.js';
10
+ let FakeApplicationRendererModule = class FakeApplicationRendererModule {
11
+ };
12
+ FakeApplicationRendererModule = __decorate([
13
+ Global(),
14
+ Module({
15
+ exports: [VITE_APPLICATION_RENDERER],
16
+ providers: [{ provide: VITE_APPLICATION_RENDERER, useValue: fakeApplicationRenderer }],
17
+ })
18
+ ], FakeApplicationRendererModule);
19
+ export { FakeApplicationRendererModule };
@@ -0,0 +1,2 @@
1
+ export * from './fake-application-renderer.js';
2
+ export * from './fake-application-renderer.module.js';
@@ -0,0 +1,2 @@
1
+ export * from './fake-application-renderer.js';
2
+ export * from './fake-application-renderer.module.js';
@@ -5,7 +5,7 @@ interface AppInterface {
5
5
  statusCode: HttpStatus;
6
6
  }
7
7
  interface ViteAppRendererInterface {
8
- renderApplication(request: FastifyRequest, initialState?: Record<string, unknown>): Promise<AppInterface>;
8
+ renderApplication(request: FastifyRequest): Promise<AppInterface>;
9
9
  }
10
10
  export type { AppInterface as ApplicationInterface, ViteAppRendererInterface as ViteApplicationRendererInterface };
11
11
  export declare const VITE_APPLICATION_RENDERER: unique symbol;
@@ -0,0 +1,10 @@
1
+ import type { Type } from '@nestjs/common';
2
+ import type { FastifyRequest } from 'fastify';
3
+ import type { SsrEntryInterface } from './ssr-entry/ssr-entry.interface.js';
4
+ export interface ViteSsrOptionsInterface {
5
+ buildInitialState(request: FastifyRequest): Record<string, unknown>;
6
+ readonly htmlPath: string;
7
+ readonly isProduction: boolean;
8
+ readonly notFoundPath: string;
9
+ readonly ssrEntry: Type<SsrEntryInterface>;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { DynamicModule } from '@nestjs/common';
2
+ import type { ViteSsrOptionsInterface } from './vite-ssr-options.interface.js';
3
+ export declare class ViteSsrModule {
4
+ static forRoot(options: ViteSsrOptionsInterface): DynamicModule;
5
+ }
@@ -0,0 +1,52 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Module } from '@nestjs/common';
8
+ import { APP_FILTER } from '@nestjs/core';
9
+ import { NotFoundFilter } from './filters/not-found.filter.js';
10
+ import { HtmlLoaderWithVite } from './html-loader/html-loader-with-vite.js';
11
+ import { HTML_LOADER_INTERFACE } from './html-loader/html-loader.interface.js';
12
+ import { HtmlLoader } from './html-loader/html-loader.js';
13
+ import { ViteApplicationRenderer } from './renderer/vite-application-renderer.js';
14
+ import { SsrEntryWithVite } from './ssr-entry/ssr-entry-with-vite.js';
15
+ import { SSR_ENTRY_INTERFACE } from './ssr-entry/ssr-entry.interface.js';
16
+ import { VITE_APPLICATION_RENDERER } from './vite-application-renderer.interface.js';
17
+ import { VITE_DEV_SERVER, VITE_SSR_OPTIONS } from './vite.constants.js';
18
+ import { ViteModule } from './vite.module.js';
19
+ const productionProviders = (ssrEntry) => [
20
+ { provide: HTML_LOADER_INTERFACE, useClass: HtmlLoader },
21
+ { provide: SSR_ENTRY_INTERFACE, useExisting: ssrEntry },
22
+ ];
23
+ const developmentProviders = (ssrEntry) => [
24
+ { provide: HTML_LOADER_INTERFACE, useClass: HtmlLoaderWithVite },
25
+ {
26
+ inject: [ssrEntry, VITE_DEV_SERVER],
27
+ provide: SSR_ENTRY_INTERFACE,
28
+ useFactory: (entry, server) => new SsrEntryWithVite(entry, server),
29
+ },
30
+ ];
31
+ let ViteSsrModule = class ViteSsrModule {
32
+ static forRoot(options) {
33
+ const { isProduction, ssrEntry } = options;
34
+ return {
35
+ exports: [VITE_APPLICATION_RENDERER],
36
+ global: true,
37
+ imports: isProduction ? [] : [ViteModule],
38
+ module: this,
39
+ providers: [
40
+ { provide: VITE_SSR_OPTIONS, useValue: options },
41
+ ssrEntry,
42
+ ...(isProduction ? productionProviders(ssrEntry) : developmentProviders(ssrEntry)),
43
+ { provide: VITE_APPLICATION_RENDERER, useClass: ViteApplicationRenderer },
44
+ { provide: APP_FILTER, useClass: NotFoundFilter },
45
+ ],
46
+ };
47
+ }
48
+ };
49
+ ViteSsrModule = __decorate([
50
+ Module({})
51
+ ], ViteSsrModule);
52
+ export { ViteSsrModule };
@@ -1 +1,2 @@
1
1
  export declare const VITE_DEV_SERVER: unique symbol;
2
+ export declare const VITE_SSR_OPTIONS: unique symbol;
@@ -1 +1,2 @@
1
1
  export const VITE_DEV_SERVER = Symbol('VITE_DEV_SERVER');
2
+ export const VITE_SSR_OPTIONS = Symbol('VITE_SSR_OPTIONS');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@application-kit/nestjs-vite",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Мост Vite ↔ NestJS для SSR: dev-обвязка, загрузка HTML и ssr-entry",
5
5
  "keywords": [
6
6
  "nestjs",
@@ -32,6 +32,16 @@
32
32
  }
33
33
  },
34
34
  "./package.json": "./package.json",
35
+ "./testing": {
36
+ "import": {
37
+ "types": "./dist/esm/testing/index.d.ts",
38
+ "default": "./dist/esm/testing/index.js"
39
+ },
40
+ "require": {
41
+ "types": "./dist/cjs/testing/index.d.ts",
42
+ "default": "./dist/cjs/testing/index.js"
43
+ }
44
+ },
35
45
  "./vite-plugin": {
36
46
  "import": {
37
47
  "types": "./dist/esm/vite-plugin/index.d.ts",
@@ -50,7 +60,7 @@
50
60
  "dist"
51
61
  ],
52
62
  "scripts": {
53
- "build": "rm -rf ./dist && node ../../scripts/barrels.mjs create ./src && tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json && node ../../scripts/barrels.mjs clean ./src && node ../../scripts/mark-cjs.mjs ./dist/cjs",
63
+ "build": "rm -rf ./dist && node ../../scripts/barrels.mjs create ./src --entry-only=testing && tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json && node ../../scripts/barrels.mjs clean ./src && node ../../scripts/mark-cjs.mjs ./dist/cjs",
54
64
  "lint:tsc": "tsc --noEmit --pretty --project .",
55
65
  "test:mutation": "stryker run",
56
66
  "test:unit": "vitest run"
@@ -59,6 +69,7 @@
59
69
  "@nestjs/common": "^11.2.1",
60
70
  "@nestjs/core": "^11.2.1",
61
71
  "@nestjs/platform-fastify": "^11.2.1",
72
+ "@nestjs/testing": "^11.2.7",
62
73
  "@stryker-mutator/core": "^10.0.0",
63
74
  "@stryker-mutator/vitest-runner": "^10.0.0",
64
75
  "@types/node": "^26.2.0",
@@ -71,6 +82,7 @@
71
82
  },
72
83
  "peerDependencies": {
73
84
  "@nestjs/common": "^11.0.0",
85
+ "@nestjs/core": "^11.0.0",
74
86
  "@nestjs/platform-fastify": "^11.0.0",
75
87
  "fastify": "^5.0.0",
76
88
  "vite": "^8.0.0"