@nerest/nerest 0.0.9 → 1.5.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 (89) hide show
  1. package/LICENSE.md +178 -0
  2. package/README.md +148 -39
  3. package/bin/index.ts +3 -0
  4. package/bin/typegen.ts +35 -0
  5. package/bin/watch.ts +3 -2
  6. package/build/configs/development.ts +63 -0
  7. package/build/configs/production.ts +59 -0
  8. package/build/configs/shared.ts +51 -0
  9. package/build/configs/vite-logger.development.ts +30 -0
  10. package/build/index.ts +53 -76
  11. package/client/index.ts +14 -2
  12. package/dist/bin/index.js +4 -0
  13. package/dist/bin/typegen.d.ts +1 -0
  14. package/dist/bin/typegen.js +31 -0
  15. package/dist/bin/watch.js +1 -2
  16. package/dist/build/configs/development.d.ts +4 -0
  17. package/dist/build/configs/development.js +53 -0
  18. package/dist/build/configs/production.d.ts +4 -0
  19. package/dist/build/configs/production.js +50 -0
  20. package/dist/build/configs/shared.d.ts +10 -0
  21. package/dist/build/configs/shared.js +24 -0
  22. package/dist/build/configs/vite-logger.development.d.ts +2 -0
  23. package/dist/build/configs/vite-logger.development.js +25 -0
  24. package/dist/build/index.js +39 -69
  25. package/dist/client/index.js +9 -2
  26. package/dist/server/development.d.ts +1 -1
  27. package/dist/server/development.js +52 -130
  28. package/dist/server/hooks/logger.d.ts +1 -0
  29. package/dist/server/hooks/logger.js +24 -0
  30. package/dist/server/hooks/props.d.ts +2 -1
  31. package/dist/server/hooks/props.js +6 -4
  32. package/dist/server/hooks/runtime.js +3 -3
  33. package/dist/server/{parts → loaders}/apps.d.ts +2 -1
  34. package/dist/server/loaders/apps.js +36 -0
  35. package/dist/server/loaders/assets.js +25 -2
  36. package/dist/server/loaders/build.d.ts +2 -0
  37. package/dist/server/loaders/build.js +11 -0
  38. package/dist/server/loaders/examples.js +7 -13
  39. package/dist/server/loaders/manifest.d.ts +8 -1
  40. package/dist/server/loaders/manifest.js +12 -4
  41. package/dist/server/loaders/preview.d.ts +4 -0
  42. package/dist/server/loaders/preview.js +11 -0
  43. package/dist/server/loaders/project.d.ts +16 -0
  44. package/dist/server/loaders/project.js +11 -0
  45. package/dist/server/loaders/schema.d.ts +2 -1
  46. package/dist/server/loaders/schema.js +12 -8
  47. package/dist/server/parts/k8s-probes.js +10 -6
  48. package/dist/server/parts/preview.d.ts +2 -1
  49. package/dist/server/parts/preview.js +5 -4
  50. package/dist/server/parts/render.d.ts +5 -3
  51. package/dist/server/parts/render.js +8 -8
  52. package/dist/server/parts/swagger.d.ts +2 -1
  53. package/dist/server/parts/swagger.js +8 -19
  54. package/dist/server/parts/validator.d.ts +3 -1
  55. package/dist/server/parts/validator.js +13 -0
  56. package/dist/server/production.js +17 -80
  57. package/dist/server/shared.d.ts +14 -0
  58. package/dist/server/shared.js +94 -0
  59. package/dist/server/utils.d.ts +1 -0
  60. package/dist/server/utils.js +5 -0
  61. package/package.json +46 -43
  62. package/schemas/nerest-build.schema.d.ts +19 -1
  63. package/schemas/nerest-build.schema.json +21 -1
  64. package/server/development.ts +67 -164
  65. package/server/hooks/logger.ts +31 -0
  66. package/server/hooks/props.ts +11 -6
  67. package/server/hooks/runtime.ts +3 -3
  68. package/server/loaders/apps.ts +58 -0
  69. package/server/loaders/assets.ts +30 -2
  70. package/server/loaders/build.ts +14 -0
  71. package/server/loaders/examples.ts +7 -15
  72. package/server/loaders/manifest.ts +23 -4
  73. package/server/loaders/preview.ts +17 -0
  74. package/server/loaders/project.ts +33 -0
  75. package/server/loaders/schema.ts +12 -10
  76. package/server/parts/k8s-probes.ts +26 -13
  77. package/server/parts/preview.ts +11 -4
  78. package/server/parts/render.ts +13 -9
  79. package/server/parts/swagger.ts +10 -29
  80. package/server/parts/validator.ts +14 -0
  81. package/server/production.ts +22 -106
  82. package/server/shared.ts +150 -0
  83. package/server/utils.ts +6 -0
  84. package/dist/server/parts/apps.js +0 -37
  85. package/dist/server/parts/props-hook.d.ts +0 -1
  86. package/dist/server/parts/props-hook.js +0 -8
  87. package/dist/server/parts/runtime-hook.d.ts +0 -2
  88. package/dist/server/parts/runtime-hook.js +0 -28
  89. package/server/parts/apps.ts +0 -59
@@ -1,10 +1,12 @@
1
- import React from 'react';
1
+ import type { ComponentType } from 'react';
2
+ import type { Project } from '../loaders/project.js';
2
3
  type RenderProps = {
3
4
  name: string;
4
5
  assets: string[];
5
- component: React.ComponentType;
6
+ component: ComponentType;
7
+ project: Project;
6
8
  };
7
- export declare function renderApp({ name, assets, component }: RenderProps, props?: Record<string, unknown>): {
9
+ export declare function renderApp({ name, assets, component, project }: RenderProps, props?: Record<string, unknown>): {
8
10
  html: string;
9
11
  assets: string[];
10
12
  };
@@ -1,18 +1,18 @@
1
- import React from 'react';
1
+ import { createElement } from 'react';
2
2
  import { renderToString } from 'react-dom/server';
3
- import { nanoid } from 'nanoid';
4
- export function renderApp({ name, assets, component }, props = {}) {
5
- const html = renderSsrComponent(name, component, props);
3
+ import { randomId } from '../utils.js';
4
+ export function renderApp({ name, assets, component, project }, props = {}) {
5
+ const html = renderSsrComponent(name, component, project, props);
6
6
  return { html, assets };
7
7
  }
8
- function renderSsrComponent(appName, appComponent, props) {
9
- const html = renderToString(React.createElement(appComponent, props));
8
+ function renderSsrComponent(appName, appComponent, project, props) {
9
+ const html = renderToString(createElement(appComponent, props));
10
10
  // There may be multiple instances of the same app on the page,
11
11
  // so we will use a randomized id to avoid collisions
12
- const appId = nanoid();
12
+ const appId = randomId();
13
13
  // data-app-name and data-app-id are used by client entrypoint to hydrate
14
14
  // apps using correct serialized props
15
- const container = `<div data-app-name="${appName}" data-app-id="${appId}">${html}</div>`;
15
+ const container = `<div data-project-name="${project.name}" data-app-name="${appName}" data-app-id="${appId}">${html}</div>`;
16
16
  const script = `<script type="application/json" data-app-id="${appId}">${JSON.stringify(props)}</script>`;
17
17
  return container + script;
18
18
  }
@@ -1,2 +1,3 @@
1
1
  import type { FastifyInstance } from 'fastify';
2
- export declare function setupSwagger(app: FastifyInstance): Promise<void>;
2
+ import type { Project } from '../loaders/project.js';
3
+ export declare function setupSwagger(app: FastifyInstance, project: Project): Promise<void>;
@@ -1,29 +1,18 @@
1
- import path from 'path';
2
- import fs from 'fs';
3
1
  import fastifySwagger from '@fastify/swagger';
4
2
  import fastifySwaggerUi from '@fastify/swagger-ui';
5
3
  // Setup automatic OpenAPI specification compilation and enable
6
4
  // Swagger UI at the `/api` route
7
- export async function setupSwagger(app) {
8
- let appInfo = {};
9
- try {
10
- const packageJson = fs.readFileSync(path.join(process.cwd(), 'package.json'), { encoding: 'utf-8' });
11
- appInfo = JSON.parse(packageJson);
12
- }
13
- catch (e) {
14
- // We only use package.json info to setup Swagger info and links,
15
- // if we are unable to load them -- that's fine
16
- }
17
- const homepage = appInfo.homepage ||
18
- (typeof appInfo.repository === 'string'
19
- ? appInfo.repository
20
- : appInfo.repository?.url);
5
+ export async function setupSwagger(app, project) {
6
+ const homepage = project.homepage ||
7
+ (typeof project.repository === 'string'
8
+ ? project.repository
9
+ : project.repository?.url);
21
10
  await app.register(fastifySwagger, {
22
11
  openapi: {
23
12
  info: {
24
- title: appInfo.name ?? 'Nerest micro frontend',
25
- description: appInfo.description,
26
- version: appInfo.version ?? '',
13
+ title: project.name || 'Nerest micro frontend',
14
+ description: project.description,
15
+ version: project.version ?? '',
27
16
  contact: homepage
28
17
  ? {
29
18
  name: 'Homepage',
@@ -1,2 +1,4 @@
1
1
  import Ajv from 'ajv';
2
- export declare const validator: Ajv.default;
2
+ import type { FastifyInstance } from 'fastify';
3
+ export declare const validator: Ajv.Ajv;
4
+ export declare function setupValidator(app: FastifyInstance): void;
@@ -18,3 +18,16 @@ export const validator = new Ajv.default({
18
18
  // `email`, `url`, etc. Used by default in fastify
19
19
  // https://www.npmjs.com/package/ajv-formats
20
20
  addFormats.default(validator);
21
+ // Setup schema validation. We have to use our own ajv instance that
22
+ // we can use both to validate request bodies and examples against
23
+ // app schemas
24
+ export function setupValidator(app) {
25
+ if (process.env.DISABLE_SCHEMA_VALIDATION) {
26
+ // If schema validation is disabled, return data as is without any checks
27
+ app.setValidatorCompiler(() => (data) => ({ value: data }));
28
+ }
29
+ else {
30
+ // If schema validation is enabled, validate and coerce data via ajv
31
+ app.setValidatorCompiler(({ schema }) => validator.compile(schema));
32
+ }
33
+ }
@@ -1,22 +1,11 @@
1
- // This is the nerest production server entrypoint
2
- import path from 'path';
3
- import fs from 'fs/promises';
4
- import fastify from 'fastify';
5
- import fastifyGracefulShutdown from 'fastify-graceful-shutdown';
6
- import { renderApp } from './parts/render.js';
7
- import { setupSwagger } from './parts/swagger.js';
8
- import { validator } from './parts/validator.js';
9
- import { renderPreviewPage } from './parts/preview.js';
10
- import { setupK8SProbes } from './parts/k8s-probes.js';
11
- import { runRuntimeHook } from './hooks/runtime.js';
12
- import { runPropsHook } from './hooks/props.js';
13
- // TODO: refactor to merge the similar parts between production and development server?
14
- async function runProductionServer() {
1
+ import { createServer } from './shared.js';
2
+ import { loadNerestManifest } from './loaders/manifest.js';
3
+ // Important: this file is the server entrypoint that will be built by vite
4
+ // in `build/index.ts`. All of the import.meta.glob's will be resolved at build time
5
+ async function runProductionServer(port) {
15
6
  const root = process.cwd();
16
- // TODO: error handling for file reading
17
- const apps = JSON.parse(await fs.readFile(path.join(root, 'build/nerest-manifest.json'), {
18
- encoding: 'utf-8',
19
- }));
7
+ // Load project information from the manifest generated during production build
8
+ const { project, apps } = await loadNerestManifest(root);
20
9
  const components = import.meta.glob('/apps/*/index.tsx', {
21
10
  import: 'default',
22
11
  eager: true,
@@ -24,70 +13,18 @@ async function runProductionServer() {
24
13
  const propsHooks = import.meta.glob('/apps/*/props.ts', {
25
14
  eager: true,
26
15
  });
27
- const app = fastify();
28
- // Setup schema validation. We have to use our own ajv instance that
29
- // we can use both to validate request bodies and examples against
30
- // app schemas
31
- app.setValidatorCompiler(({ schema }) => validator.compile(schema));
32
- await setupSwagger(app);
33
- for (const appEntry of Object.values(apps)) {
34
- const { name, examples, schema, assets } = appEntry;
35
- const component = components[`/apps/${name}/index.tsx`];
36
- const propsHook = async () => propsHooks[`/apps/${name}/props.ts`];
37
- const routeOptions = {};
38
- // TODO: report error if schema is missing, unless this app is client-only
39
- // TODO: disallow apps without schemas in production build
40
- if (schema) {
41
- routeOptions.schema = {
42
- // Use description as Swagger summary, since summary is visible
43
- // even when the route is collapsed in the UI
44
- summary: schema.description,
45
- // TODO: do we need to mix in examples like in the development server?
46
- body: schema,
47
- };
48
- }
49
- // POST /api/{name} -> render app with request.body as props
50
- app.post(`/api/${name}`, routeOptions, async (request) => {
51
- const props = await runPropsHook(request.body, propsHook);
52
- return renderApp({ name, assets, component }, props);
53
- });
54
- for (const [exampleName, example] of Object.entries(examples)) {
55
- // GET /api/{name}/examples/{example} -> render a preview page
56
- // with a predefined example body
57
- const exampleRoute = `/api/${name}/examples/${exampleName}`;
58
- app.get(exampleRoute, {
59
- schema: {
60
- // Add a clickable link to the example route in route's Swagger
61
- // description so it's easier to navigate to
62
- description: `Open sandbox: [${exampleRoute}](${exampleRoute})`,
63
- },
64
- }, async (_, reply) => {
65
- const props = await runPropsHook(example, propsHook);
66
- const { html, assets: outAssets } = renderApp({
67
- name,
68
- assets,
69
- component,
70
- }, props);
71
- reply.type('text/html');
72
- return renderPreviewPage(html, outAssets);
73
- });
74
- }
75
- }
76
- // Add graceful shutdown handler to prevent requests errors
77
- await app.register(fastifyGracefulShutdown);
78
- if (process.env.ENABLE_K8S_PROBES) {
79
- await setupK8SProbes(app);
80
- }
81
- // Execute runtime hook in nerest-runtime.ts if it exists
82
- await runRuntimeHook(app, async () => {
83
- const glob = import.meta.glob('/nerest-runtime.ts', { eager: true });
84
- return glob['/nerest-runtime.ts'];
16
+ const runtimeHook = import.meta.glob('/nerest/runtime.ts', { eager: true });
17
+ const app = await createServer({
18
+ root,
19
+ project,
20
+ apps,
21
+ loadComponent: async (entry) => components[`/apps/${entry}/index.tsx`],
22
+ loadPropsHook: async (entry) => propsHooks[`/apps/${entry}/props.ts`],
23
+ loadRuntimeHook: async () => runtimeHook['/nerest/runtime.ts'],
85
24
  });
86
- // TODO: remove hardcoded port
87
25
  await app.listen({
88
26
  host: '0.0.0.0',
89
- port: 3000,
27
+ port,
90
28
  });
91
- console.log('Nerest is listening on 0.0.0.0:3000');
92
29
  }
93
- runProductionServer();
30
+ runProductionServer(process.env.PORT ? Number(process.env.PORT) : 3000);
@@ -0,0 +1,14 @@
1
+ import fastify from 'fastify';
2
+ import type { ComponentType } from 'react';
3
+ import type { Project } from './loaders/project.js';
4
+ import type { AppEntry } from './loaders/apps.js';
5
+ type ServerOptions = {
6
+ root: string;
7
+ project: Project;
8
+ apps: Record<string, AppEntry>;
9
+ loadComponent: (entry: string) => Promise<ComponentType>;
10
+ loadPropsHook: (entry: string) => Promise<unknown>;
11
+ loadRuntimeHook: () => Promise<unknown>;
12
+ };
13
+ export declare function createServer(options: ServerOptions): Promise<fastify.FastifyInstance<fastify.RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>>;
14
+ export {};
@@ -0,0 +1,94 @@
1
+ import fastify from 'fastify';
2
+ import fastifyGracefulShutdown from 'fastify-graceful-shutdown';
3
+ import { renderApp } from './parts/render.js';
4
+ import { setupSwagger } from './parts/swagger.js';
5
+ import { setupValidator, validator } from './parts/validator.js';
6
+ import { renderPreviewPage } from './parts/preview.js';
7
+ import { setupK8SProbes } from './parts/k8s-probes.js';
8
+ import { runRuntimeHook } from './hooks/runtime.js';
9
+ import { runPropsHook } from './hooks/props.js';
10
+ import { runLoggerHook } from './hooks/logger.js';
11
+ import { loadPreviewParts } from './loaders/preview.js';
12
+ import { randomId } from './utils.js';
13
+ export async function createServer(options) {
14
+ const { project, root, loadRuntimeHook } = options;
15
+ const app = fastify({
16
+ logger: (await runLoggerHook(loadRuntimeHook)) ?? true,
17
+ ignoreTrailingSlash: true,
18
+ useSemicolonDelimiter: false,
19
+ // JSON parsing can take a long time and blocks the event loop,
20
+ // so we need to limit the size of the body. 10MB is a good compromise
21
+ // baseline that was chosen by experimenting with real world usage
22
+ bodyLimit: 10 * 1024 * 1024,
23
+ genReqId(req) {
24
+ return String(req.headers['x-request-id'] || randomId());
25
+ },
26
+ });
27
+ // Setup payload validation and Swagger based on apps' JSON Schema
28
+ setupValidator(app);
29
+ await setupSwagger(app, project);
30
+ // Load preview parts from `nerest/preview-{part}.html` files
31
+ const previewParts = await loadPreviewParts(root);
32
+ await setupRoutes(app, { ...options, previewParts });
33
+ // Add graceful shutdown handler to prevent requests errors
34
+ await app.register(fastifyGracefulShutdown);
35
+ if (process.env.ENABLE_K8S_PROBES) {
36
+ await setupK8SProbes(app);
37
+ }
38
+ // Execute runtime hook in nerest/runtime.ts if it exists
39
+ await runRuntimeHook(app, loadRuntimeHook);
40
+ return app;
41
+ }
42
+ async function setupRoutes(app, options) {
43
+ const { project, apps, previewParts, loadComponent, loadPropsHook } = options;
44
+ for (const appEntry of Object.values(apps)) {
45
+ const { name, examples, schema, assets } = appEntry;
46
+ const routeOptions = {};
47
+ // TODO: report error if schema is missing, making it mandatory
48
+ if (schema) {
49
+ routeOptions.schema = {
50
+ summary: schema.description,
51
+ // Tags are used to group routes in Swagger UI
52
+ tags: [name],
53
+ body: {
54
+ ...schema,
55
+ // Examples are also displayed in Swagger UI
56
+ examples: Object.values(examples),
57
+ },
58
+ };
59
+ }
60
+ // POST /api/{name} -> render app with request.body as props
61
+ app.post(`/api/${name}`, routeOptions, async (request) => {
62
+ const component = await loadComponent(name);
63
+ const props = await runPropsHook(app, () => loadPropsHook(name), request.body);
64
+ return renderApp({ name, assets, component, project }, props);
65
+ });
66
+ for (const [exampleName, example] of Object.entries(examples)) {
67
+ // Validate examples against schema
68
+ if (schema && !validator.validate(schema, example)) {
69
+ app.log.error(`Example "${exampleName}" of app "${name}" does not satisfy schema: ${validator.errorsText()}`);
70
+ }
71
+ // GET /api/{name}/examples/{example} -> render a preview page
72
+ const exampleRoute = `/api/${name}/examples/${exampleName}`;
73
+ app.get(exampleRoute, {
74
+ schema: {
75
+ // Add clickable link to go to the example in Swagger UI
76
+ description: `Open sandbox: [${exampleRoute}](${exampleRoute})`,
77
+ // Place examples under the same tag as the app
78
+ tags: [name],
79
+ },
80
+ }, async (request, reply) => {
81
+ const component = await loadComponent(name);
82
+ const props = await runPropsHook(app, () => loadPropsHook(name), example);
83
+ const { html, assets: outAssets } = renderApp({
84
+ name,
85
+ assets,
86
+ component,
87
+ project,
88
+ }, props);
89
+ reply.type('text/html');
90
+ return renderPreviewPage(html, outAssets, previewParts);
91
+ });
92
+ }
93
+ }
94
+ }
@@ -0,0 +1 @@
1
+ export declare function randomId(): string;
@@ -0,0 +1,5 @@
1
+ import crypto from 'crypto';
2
+ // Generate a random string ID 20 characters long
3
+ export function randomId() {
4
+ return crypto.randomBytes(10).toString('hex');
5
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@nerest/nerest",
3
- "version": "0.0.9",
3
+ "version": "1.5.0",
4
4
  "description": "React micro frontend framework",
5
- "homepage": "https://github.com/nerestjs/nerest#readme",
5
+ "homepage": "https://github.com/nerestjs/nerest",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/nerestjs/nerest.git"
9
9
  },
10
+ "license": "Apache-2.0",
10
11
  "type": "module",
11
12
  "bin": {
12
13
  "nerest": "dist/bin/index.js"
@@ -18,16 +19,23 @@
18
19
  "/client",
19
20
  "/schemas",
20
21
  "/server",
21
- "/README.md"
22
+ "/README.md",
23
+ "/LICENSE.md"
22
24
  ],
23
25
  "scripts": {
24
26
  "build": "tsc -p tsconfig.json -d",
25
- "lint": "eslint --ext .ts server bin",
27
+ "docs": "scripts/docs.sh",
28
+ "lint": "eslint --ext .ts .",
26
29
  "prepare": "simple-git-hooks",
30
+ "test": "npm run test:module && npm run test:integration:dev && npm run test:integration:prod",
31
+ "test:integration:dev": "node tests/integration/run.development.js",
32
+ "test:integration:prod": "node tests/integration/run.production.js",
33
+ "test:module": "vitest run tests/module/**",
27
34
  "typegen": "json2ts -i 'schemas/**/*.json' -o schemas --bannerComment ''"
28
35
  },
29
36
  "simple-git-hooks": {
30
- "pre-commit": "npx lint-staged"
37
+ "pre-commit": "npx lint-staged",
38
+ "commit-msg": "npx commitlint --edit ${1}"
31
39
  },
32
40
  "lint-staged": {
33
41
  "*.ts": [
@@ -41,51 +49,46 @@
41
49
  "sort-package-json"
42
50
  ]
43
51
  },
44
- "prettier": "@tinkoff/prettier-config",
45
- "eslintConfig": {
46
- "parserOptions": {
47
- "project": true
48
- },
49
- "extends": [
50
- "@tinkoff/eslint-config/lib",
51
- "@tinkoff/eslint-config/jest",
52
- "@tinkoff/eslint-config-react"
53
- ]
54
- },
55
52
  "dependencies": {
56
- "@fastify/static": "^6.12.0",
57
- "@fastify/swagger": "^8.12.0",
58
- "@fastify/swagger-ui": "^1.10.1",
59
- "ajv": "^8.12.0",
60
- "ajv-formats": "^2.1.1",
61
- "dotenv": "^16.3.1",
62
- "fast-uri": "^2.3.0",
63
- "fastify": "^4.24.3",
64
- "fastify-graceful-shutdown": "^3.5.1",
65
- "nanoid": "^5.0.2",
66
- "vite": "^4.5.0",
53
+ "@apidevtools/json-schema-ref-parser": "^11.9.3",
54
+ "@fastify/middie": "^9.0.3",
55
+ "@fastify/static": "^8.1.1",
56
+ "@fastify/swagger": "^9.4.2",
57
+ "@fastify/swagger-ui": "^5.2.2",
58
+ "ajv": "^8.17.1",
59
+ "ajv-formats": "^3.0.1",
60
+ "dotenv": "^16.4.7",
61
+ "fast-glob": "^3.3.3",
62
+ "fast-uri": "^3.0.6",
63
+ "fastify": "^5.2.1",
64
+ "fastify-graceful-shutdown": "^4.0.1",
65
+ "json-schema-to-typescript": "^15.0.4",
66
+ "vite": "^6.2.2",
67
67
  "vite-plugin-externals": "^0.6.2"
68
68
  },
69
69
  "devDependencies": {
70
- "@tinkoff/eslint-config": "^1.54.4",
71
- "@tinkoff/eslint-config-react": "^1.54.4",
72
- "@tinkoff/prettier-config": "^1.52.1",
73
- "@types/react": "^18.2.34",
74
- "@types/react-dom": "^18.2.14",
75
- "jest": "^29.7.0",
76
- "json-schema-to-typescript": "^13.1.1",
77
- "lint-staged": "^15.0.2",
78
- "react": "^18.2.0",
79
- "react-dom": "^18.2.0",
80
- "simple-git-hooks": "^2.9.0",
81
- "sort-package-json": "^2.6.0",
82
- "typescript": "^5.2.2"
70
+ "@commitlint/cli": "^19.8.0",
71
+ "@commitlint/config-conventional": "^19.8.0",
72
+ "@playwright/test": "^1.51.0",
73
+ "@tinkoff/eslint-config": "^5.0.1",
74
+ "@tinkoff/eslint-config-react": "^5.0.1",
75
+ "@tinkoff/prettier-config": "^5.0.0",
76
+ "@types/react": "^19.0.10",
77
+ "@types/react-dom": "^19.0.4",
78
+ "execa": "^9.5.2",
79
+ "lint-staged": "^15.5.0",
80
+ "react": "^19.0.0",
81
+ "react-dom": "^19.0.0",
82
+ "simple-git-hooks": "^2.11.1",
83
+ "sort-package-json": "^3.0.0",
84
+ "typescript": "^5.8.2",
85
+ "vitest": "^3.0.8"
83
86
  },
84
87
  "peerDependencies": {
85
- "react": "^18.0.0",
86
- "react-dom": "^18.0.0"
88
+ "react": "^18.0.0 || ^19.0.0",
89
+ "react-dom": "^18.0.0 || ^19.0.0"
87
90
  },
88
91
  "engines": {
89
- "node": ">=16.0.0"
92
+ "node": ">=v20.17.0"
90
93
  }
91
94
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Build configuration placed as nerest-build.schema.json in the root of the application.
2
+ * Build configuration placed as nerest/build.json in the root of the application.
3
3
  */
4
4
  export interface BuildConfiguration {
5
5
  /**
@@ -12,5 +12,23 @@ export interface BuildConfiguration {
12
12
  externals?: {
13
13
  [k: string]: string;
14
14
  };
15
+ /**
16
+ * Additional PostCSS configuration. Nerest uses the standard vite PostCSS configuration by default.
17
+ */
18
+ postcss?: {
19
+ /**
20
+ * List packages and options of PostCSS plugins to use. They will be added to the default plugin list.
21
+ */
22
+ plugins?: {
23
+ [k: string]: {
24
+ [k: string]: unknown;
25
+ };
26
+ };
27
+ [k: string]: unknown;
28
+ };
29
+ /**
30
+ * List of names of apps that have client side-effects, for example have their own self-initialization code that runs on import. Their entries will be loaded when hydration starts to run side-effect code.
31
+ */
32
+ clientSideEffects?: string[];
15
33
  [k: string]: unknown;
16
34
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft-07/schema",
3
3
  "title": "Build Configuration",
4
- "description": "Build configuration placed as nerest-build.schema.json in the root of the application.",
4
+ "description": "Build configuration placed as nerest/build.json in the root of the application.",
5
5
  "type": "object",
6
6
  "properties": {
7
7
  "excludes": {
@@ -17,6 +17,26 @@
17
17
  "additionalProperties": {
18
18
  "type": "string"
19
19
  }
20
+ },
21
+ "postcss": {
22
+ "description": "Additional PostCSS configuration. Nerest uses the standard vite PostCSS configuration by default.",
23
+ "type": "object",
24
+ "properties": {
25
+ "plugins": {
26
+ "description": "List packages and options of PostCSS plugins to use. They will be added to the default plugin list.",
27
+ "type": "object",
28
+ "additionalProperties": {
29
+ "type": "object"
30
+ }
31
+ }
32
+ }
33
+ },
34
+ "clientSideEffects": {
35
+ "description": "List of names of apps that have client side-effects, for example have their own self-initialization code that runs on import. Their entries will be loaded when hydration starts to run side-effect code.",
36
+ "type": "array",
37
+ "items": {
38
+ "type": "string"
39
+ }
20
40
  }
21
41
  }
22
42
  }