@nerest/nerest 0.0.3 → 0.0.5

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 (60) hide show
  1. package/README.md +38 -3
  2. package/bin/build.ts +8 -0
  3. package/bin/index.ts +7 -1
  4. package/bin/watch.ts +2 -11
  5. package/build/index.ts +82 -0
  6. package/dist/bin/build.d.ts +1 -0
  7. package/dist/bin/build.js +11 -0
  8. package/dist/bin/index.d.ts +1 -1
  9. package/dist/bin/index.js +7 -1
  10. package/dist/bin/watch.js +2 -8
  11. package/dist/build/index.d.ts +1 -0
  12. package/dist/build/index.js +72 -0
  13. package/dist/server/development.d.ts +1 -0
  14. package/dist/server/{index.js → development.js} +46 -21
  15. package/dist/server/loaders/assets.d.ts +2 -0
  16. package/dist/server/loaders/assets.js +14 -0
  17. package/dist/server/loaders/manifest.d.ts +1 -0
  18. package/dist/server/loaders/manifest.js +17 -0
  19. package/dist/server/{apps.d.ts → parts/apps.d.ts} +1 -1
  20. package/dist/server/{apps.js → parts/apps.js} +9 -16
  21. package/dist/server/parts/k8s-probes.d.ts +2 -0
  22. package/dist/server/parts/k8s-probes.js +31 -0
  23. package/dist/server/parts/render.d.ts +11 -0
  24. package/dist/server/{render.js → parts/render.js} +2 -3
  25. package/package.json +18 -15
  26. package/server/{index.ts → development.ts} +47 -21
  27. package/server/loaders/assets.ts +19 -0
  28. package/server/loaders/manifest.ts +11 -0
  29. package/server/{apps.ts → parts/apps.ts} +10 -17
  30. package/server/parts/k8s-probes.ts +30 -0
  31. package/server/{render.ts → parts/render.ts} +7 -5
  32. package/server/production.ts +115 -0
  33. package/dist/client/entry.d.ts +0 -1
  34. package/dist/client/entry.js +0 -3
  35. package/dist/server/app-parts/assets.d.ts +0 -2
  36. package/dist/server/app-parts/assets.js +0 -15
  37. package/dist/server/app-parts/preview.js +0 -27
  38. package/dist/server/assets.d.ts +0 -1
  39. package/dist/server/assets.js +0 -28
  40. package/dist/server/entry.d.ts +0 -2
  41. package/dist/server/entry.js +0 -17
  42. package/dist/server/index.d.ts +0 -5
  43. package/dist/server/preview.d.ts +0 -1
  44. package/dist/server/render.d.ts +0 -6
  45. package/server/app-parts/assets.ts +0 -17
  46. /package/dist/server/{app-parts → loaders}/examples.d.ts +0 -0
  47. /package/dist/server/{app-parts → loaders}/examples.js +0 -0
  48. /package/dist/server/{app-parts → loaders}/schema.d.ts +0 -0
  49. /package/dist/server/{app-parts → loaders}/schema.js +0 -0
  50. /package/dist/server/{app-parts → parts}/preview.d.ts +0 -0
  51. /package/dist/server/{preview.js → parts/preview.js} +0 -0
  52. /package/dist/server/{swagger.d.ts → parts/swagger.d.ts} +0 -0
  53. /package/dist/server/{swagger.js → parts/swagger.js} +0 -0
  54. /package/dist/server/{validator.d.ts → parts/validator.d.ts} +0 -0
  55. /package/dist/server/{validator.js → parts/validator.js} +0 -0
  56. /package/server/{app-parts → loaders}/examples.ts +0 -0
  57. /package/server/{app-parts → loaders}/schema.ts +0 -0
  58. /package/server/{preview.ts → parts/preview.ts} +0 -0
  59. /package/server/{swagger.ts → parts/swagger.ts} +0 -0
  60. /package/server/{validator.ts → parts/validator.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerest/nerest",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "React micro frontend framework",
5
5
  "homepage": "https://github.com/nerestjs/nerest#readme",
6
6
  "repository": {
@@ -13,6 +13,7 @@
13
13
  "files": [
14
14
  "/dist",
15
15
  "/bin",
16
+ "/build",
16
17
  "/client",
17
18
  "/server",
18
19
  "/README.md"
@@ -46,29 +47,31 @@
46
47
  ]
47
48
  },
48
49
  "dependencies": {
49
- "@fastify/static": "^6.9.0",
50
- "@fastify/swagger": "^8.3.1",
51
- "@fastify/swagger-ui": "^1.4.0",
50
+ "@fastify/static": "^6.10.2",
51
+ "@fastify/swagger": "^8.5.1",
52
+ "@fastify/swagger-ui": "^1.8.1",
52
53
  "ajv": "^8.12.0",
53
54
  "ajv-formats": "^2.1.1",
55
+ "dotenv": "^16.1.4",
54
56
  "fast-uri": "^2.2.0",
55
- "fastify": "^4.13.0",
56
- "nanoid": "^3.3.4",
57
- "vite": "^4.1.4"
57
+ "fastify": "^4.17.0",
58
+ "fastify-graceful-shutdown": "^3.5.1",
59
+ "nanoid": "^3.3.6",
60
+ "vite": "^4.3.9"
58
61
  },
59
62
  "devDependencies": {
60
- "@tinkoff/eslint-config": "^1.50.1",
61
- "@tinkoff/eslint-config-react": "^1.50.2",
62
- "@tinkoff/prettier-config": "^1.47.1",
63
- "@types/react": "^18.0.28",
64
- "@types/react-dom": "^18.0.11",
65
- "jest": "^29.4.3",
66
- "lint-staged": "^13.1.2",
63
+ "@tinkoff/eslint-config": "^1.52.1",
64
+ "@tinkoff/eslint-config-react": "^1.52.1",
65
+ "@tinkoff/prettier-config": "^1.52.1",
66
+ "@types/react": "^18.2.7",
67
+ "@types/react-dom": "^18.2.4",
68
+ "jest": "^29.5.0",
69
+ "lint-staged": "^13.2.2",
67
70
  "react": "^18.2.0",
68
71
  "react-dom": "^18.2.0",
69
72
  "simple-git-hooks": "^2.8.1",
70
73
  "sort-package-json": "^2.4.1",
71
- "typescript": "^4.9.5"
74
+ "typescript": "^5.0.4"
72
75
  },
73
76
  "peerDependencies": {
74
77
  "react": "^18.0.0",
@@ -1,4 +1,4 @@
1
- // This is the nerest server entrypoint
1
+ // This is the nerest development server entrypoint
2
2
  import path from 'path';
3
3
  import type { ServerResponse } from 'http';
4
4
 
@@ -9,16 +9,16 @@ import type { RollupWatcher, RollupWatcherEvent } from 'rollup';
9
9
  import type { RouteShorthandOptions } from 'fastify';
10
10
  import fastify from 'fastify';
11
11
  import fastifyStatic from '@fastify/static';
12
+ import fastifyGracefulShutdown from 'fastify-graceful-shutdown';
12
13
 
13
- import { loadApps } from './apps';
14
- import { renderApp } from './render';
15
- import { renderPreviewPage } from './preview';
16
- import { validator } from './validator';
17
- import { setupSwagger } from './swagger';
14
+ import { loadApps } from './parts/apps';
15
+ import { renderApp } from './parts/render';
16
+ import { renderPreviewPage } from './parts/preview';
17
+ import { validator } from './parts/validator';
18
+ import { setupSwagger } from './parts/swagger';
19
+ import { setupK8SProbes } from './parts/k8s-probes';
18
20
 
19
- // TODO: this turned out to be a dev server, production server
20
- // will most likely be implemented separately
21
- export async function createServer() {
21
+ export async function runDevelopmentServer() {
22
22
  const root = process.cwd();
23
23
 
24
24
  // TODO: move build config into a separate file
@@ -27,6 +27,7 @@ export async function createServer() {
27
27
  const config: InlineConfig = {
28
28
  root,
29
29
  appType: 'custom',
30
+ envPrefix: 'NEREST_',
30
31
  server: { middlewareMode: true },
31
32
  build: {
32
33
  // Manifest is needed to report used assets in SSR handles
@@ -37,12 +38,18 @@ export async function createServer() {
37
38
  rollupOptions: {
38
39
  input: '/node_modules/@nerest/nerest/client/index.ts',
39
40
  output: {
40
- entryFileNames: `assets/[name].js`,
41
- chunkFileNames: `assets/[name].js`,
42
- assetFileNames: `assets/[name].[ext]`,
41
+ dir: 'build',
42
+ entryFileNames: `client/assets/[name].js`,
43
+ chunkFileNames: `client/assets/[name].js`,
44
+ assetFileNames: `client/assets/[name].[ext]`,
43
45
  },
44
46
  },
45
47
  },
48
+ // TODO: this doesn't seem to work without the index.html file entry and
49
+ // produces warnings in dev mode. look into this maybe
50
+ optimizeDeps: {
51
+ disabled: true,
52
+ },
46
53
  };
47
54
 
48
55
  // Build the clientside assets and watch for changes
@@ -50,7 +57,8 @@ export async function createServer() {
50
57
  await startClientBuildWatcher(config);
51
58
 
52
59
  // Load app entries following the `apps/{name}/index.tsx` convention
53
- const apps = await loadApps(root);
60
+ // TODO: remove hardcoded port
61
+ const apps = await loadApps(root, 'http://0.0.0.0:3000/');
54
62
 
55
63
  // Start vite server that will be rendering SSR components
56
64
  const viteSsr = await vite.createServer(config);
@@ -68,8 +76,7 @@ export async function createServer() {
68
76
 
69
77
  const routeOptions: RouteShorthandOptions = {};
70
78
 
71
- // TODO: report error if schema is missing, unless this app is client-only.
72
- // TODO: disallow apps without schemas in production build
79
+ // TODO: report error if schema is missing, unless this app is client-only
73
80
  if (schema) {
74
81
  routeOptions.schema = {
75
82
  // Use description as Swagger summary, since summary is visible
@@ -92,8 +99,11 @@ export async function createServer() {
92
99
  fixStacktrace: true,
93
100
  });
94
101
  return renderApp(
95
- appEntry,
96
- ssrComponent.default,
102
+ {
103
+ name,
104
+ assets: appEntry.assets,
105
+ component: ssrComponent.default,
106
+ },
97
107
  request.body as Record<string, unknown>
98
108
  );
99
109
  });
@@ -124,8 +134,11 @@ export async function createServer() {
124
134
  fixStacktrace: true,
125
135
  });
126
136
  const { html, assets } = renderApp(
127
- appEntry,
128
- ssrComponent.default,
137
+ {
138
+ name,
139
+ assets: appEntry.assets,
140
+ component: ssrComponent.default,
141
+ },
129
142
  example as Record<string, unknown>
130
143
  );
131
144
 
@@ -137,9 +150,16 @@ export async function createServer() {
137
150
  }
138
151
  }
139
152
 
153
+ // Add graceful shutdown handler to prevent requests errors
154
+ await app.register(fastifyGracefulShutdown);
155
+
156
+ if (process.env.ENABLE_K8S_PROBES) {
157
+ await setupK8SProbes(app);
158
+ }
159
+
140
160
  // TODO: only do this locally, load from CDN in production
141
161
  await app.register(fastifyStatic, {
142
- root: path.join(root, 'dist'),
162
+ root: path.join(root, 'build'),
143
163
  // TODO: maybe use @fastify/cors instead
144
164
  setHeaders(res: ServerResponse) {
145
165
  res.setHeader('Access-Control-Allow-Origin', '*');
@@ -151,7 +171,13 @@ export async function createServer() {
151
171
  },
152
172
  });
153
173
 
154
- return { app };
174
+ // TODO: remove hardcoded port
175
+ await app.listen({
176
+ host: '0.0.0.0',
177
+ port: 3000,
178
+ });
179
+
180
+ console.log('Nerest is listening on 0.0.0.0:3000');
155
181
  }
156
182
 
157
183
  // TODO: this should probably be moved from here
@@ -0,0 +1,19 @@
1
+ import type { Manifest } from 'vite';
2
+
3
+ // Extracts the list of assets for a given app from the manifest file
4
+ export function loadAppAssets(
5
+ appName: string,
6
+ manifest: Manifest,
7
+ staticPath: string
8
+ ) {
9
+ // TODO: handling errors and potentially missing entries
10
+ // All apps share the same JS entry that dynamically imports the chunks of the apps
11
+ // that are used on the page based on their name
12
+ const clientEntryJs =
13
+ manifest['node_modules/@nerest/nerest/client/index.ts'].file;
14
+
15
+ // Each app has its own CSS bundles, if it imports any CSS
16
+ const appCss = manifest[`apps/${appName}/index.tsx`].css ?? [];
17
+
18
+ return [clientEntryJs, ...appCss].map((x) => staticPath + x);
19
+ }
@@ -0,0 +1,11 @@
1
+ import path from 'path';
2
+ import fs from 'fs/promises';
3
+
4
+ // Manifest is used to provide assets list for every app
5
+ // for use with SSR
6
+ export async function loadAppManifest(root: string) {
7
+ // TODO: error handling
8
+ const manifestPath = path.join(root, 'build', 'manifest.json');
9
+ const manifestData = await fs.readFile(manifestPath, { encoding: 'utf8' });
10
+ return JSON.parse(manifestData);
11
+ }
@@ -2,9 +2,10 @@ import path from 'path';
2
2
  import fs from 'fs/promises';
3
3
  import type { Manifest } from 'vite';
4
4
 
5
- import { loadAppAssets } from './app-parts/assets';
6
- import { loadAppExamples } from './app-parts/examples';
7
- import { loadAppSchema } from './app-parts/schema';
5
+ import { loadAppAssets } from '../loaders/assets';
6
+ import { loadAppExamples } from '../loaders/examples';
7
+ import { loadAppSchema } from '../loaders/schema';
8
+ import { loadAppManifest } from '../loaders/manifest';
8
9
 
9
10
  export type AppEntry = {
10
11
  name: string;
@@ -18,9 +19,9 @@ export type AppEntry = {
18
19
  // Build the record of the available apps by convention
19
20
  // apps -> /apps/{name}/index.tsx
20
21
  // examples -> /apps/{name}/examples/{example}.json
21
- export async function loadApps(root: string) {
22
+ export async function loadApps(root: string, deployedStaticPath: string) {
22
23
  const appsRoot = path.join(root, 'apps');
23
- const manifest = await loadManifest(root);
24
+ const manifest = await loadAppManifest(root);
24
25
 
25
26
  const appsDirs = (await fs.readdir(appsRoot, { withFileTypes: true }))
26
27
  .filter((d) => d.isDirectory())
@@ -28,7 +29,7 @@ export async function loadApps(root: string) {
28
29
 
29
30
  const apps: Array<[name: string, entry: AppEntry]> = [];
30
31
  for (const appDir of appsDirs) {
31
- apps.push(await loadApp(appsRoot, appDir, manifest));
32
+ apps.push(await loadApp(appsRoot, appDir, manifest, deployedStaticPath));
32
33
  }
33
34
 
34
35
  return Object.fromEntries(apps);
@@ -37,7 +38,8 @@ export async function loadApps(root: string) {
37
38
  async function loadApp(
38
39
  appsRoot: string,
39
40
  name: string,
40
- manifest: Manifest
41
+ manifest: Manifest,
42
+ deployedStaticPath: string
41
43
  ): Promise<[name: string, entry: AppEntry]> {
42
44
  // TODO: report problems with loading entries, assets and/or examples
43
45
  const appRoot = path.join(appsRoot, name);
@@ -47,18 +49,9 @@ async function loadApp(
47
49
  name,
48
50
  root: appRoot,
49
51
  entry: path.join(appRoot, 'index.tsx'),
50
- assets: loadAppAssets(manifest, name),
52
+ assets: loadAppAssets(name, manifest, deployedStaticPath),
51
53
  examples: await loadAppExamples(appRoot),
52
54
  schema: await loadAppSchema(appRoot),
53
55
  },
54
56
  ];
55
57
  }
56
-
57
- // Manifest is used to provide assets list for every app
58
- // for use with SSR
59
- async function loadManifest(root: string) {
60
- // TODO: error handling
61
- const manifestPath = path.join(root, 'dist', 'manifest.json');
62
- const manifestData = await fs.readFile(manifestPath, { encoding: 'utf8' });
63
- return JSON.parse(manifestData);
64
- }
@@ -0,0 +1,30 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+
3
+ // Setup routes for k8s probes to check if application is live
4
+ export async function setupK8SProbes(app: FastifyInstance) {
5
+ // Handler for graceful shutdowns
6
+ // K8s can initiate shutdown at any moment: on pods restart or on deploy.
7
+ // So, if we receive shutdown request, we:
8
+ // - starts to send 503 response code on readiness probes to stop receiving requests
9
+ // - finishes all current requests
10
+ // - shuts down the server
11
+ let isShutdownInProgress = false;
12
+ app.gracefulShutdown((code, next) => {
13
+ // TODO: replace with nerest logger, when it'll be ready
14
+ console.log('Graceful shutdown in process...');
15
+ isShutdownInProgress = true;
16
+ next();
17
+ });
18
+
19
+ app.get('/livenessProbe', { logLevel: 'silent' }, (req, res) => {
20
+ res.status(200).send();
21
+ });
22
+
23
+ app.get('/readinessProbe', { logLevel: 'silent' }, (req, res) => {
24
+ if (isShutdownInProgress) {
25
+ res.status(503).send({ status: 'Shutdown in progress' });
26
+ } else {
27
+ res.status(200).send();
28
+ }
29
+ });
30
+ }
@@ -2,15 +2,17 @@ import React from 'react';
2
2
  import { renderToString } from 'react-dom/server';
3
3
  import { nanoid } from 'nanoid';
4
4
 
5
- import type { AppEntry } from './apps';
5
+ type RenderProps = {
6
+ name: string;
7
+ assets: string[];
8
+ component: React.ComponentType;
9
+ };
6
10
 
7
11
  export function renderApp(
8
- appEntry: AppEntry,
9
- appComponent: React.ComponentType,
12
+ { name, assets, component }: RenderProps,
10
13
  props: Record<string, unknown> = {}
11
14
  ) {
12
- const { name, assets } = appEntry;
13
- const html = renderSsrComponent(name, appComponent, props);
15
+ const html = renderSsrComponent(name, component, props);
14
16
  return { html, assets };
15
17
  }
16
18
 
@@ -0,0 +1,115 @@
1
+ // This is the nerest production server entrypoint
2
+ import path from 'path';
3
+ import fs from 'fs/promises';
4
+
5
+ import fastify from 'fastify';
6
+ import fastifyGracefulShutdown from 'fastify-graceful-shutdown';
7
+ import type { RouteShorthandOptions } from 'fastify';
8
+
9
+ import type { AppEntry } from './parts/apps';
10
+ import { renderApp } from './parts/render';
11
+ import { setupSwagger } from './parts/swagger';
12
+ import { validator } from './parts/validator';
13
+ import { renderPreviewPage } from './parts/preview';
14
+ import { setupK8SProbes } from './parts/k8s-probes';
15
+
16
+ // TODO: refactor to merge the similar parts between production and development server?
17
+ async function runProductionServer() {
18
+ const root = process.cwd();
19
+
20
+ // TODO: error handling for file reading
21
+ const apps = JSON.parse(
22
+ await fs.readFile(path.join(root, 'build/nerest-manifest.json'), {
23
+ encoding: 'utf-8',
24
+ })
25
+ ) as Record<string, AppEntry>;
26
+
27
+ // TODO: fix client-side vite types
28
+ const components = import.meta.glob('/apps/*/index.tsx', {
29
+ import: 'default',
30
+ eager: true,
31
+ }) as Record<string, React.ComponentType>;
32
+
33
+ const app = fastify();
34
+
35
+ // Setup schema validation. We have to use our own ajv instance that
36
+ // we can use both to validate request bodies and examples against
37
+ // app schemas
38
+ app.setValidatorCompiler(({ schema }) => validator.compile(schema));
39
+
40
+ await setupSwagger(app);
41
+
42
+ for (const appEntry of Object.values(apps)) {
43
+ const { name, examples, schema, assets } = appEntry;
44
+ const component = components[`/apps/${name}/index.tsx`];
45
+
46
+ const routeOptions: RouteShorthandOptions = {};
47
+
48
+ // TODO: report error if schema is missing, unless this app is client-only
49
+ // TODO: disallow apps without schemas in production build
50
+ if (schema) {
51
+ routeOptions.schema = {
52
+ // Use description as Swagger summary, since summary is visible
53
+ // even when the route is collapsed in the UI
54
+ summary: schema.description as string,
55
+ // TODO: do we need to mix in examples like in the development server?
56
+ body: schema,
57
+ };
58
+ }
59
+
60
+ // POST /api/{name} -> render app with request.body as props
61
+ app.post(`/api/${name}`, routeOptions, (request) =>
62
+ renderApp(
63
+ { name, assets, component },
64
+ request.body as Record<string, unknown>
65
+ )
66
+ );
67
+
68
+ for (const [exampleName, example] of Object.entries(examples)) {
69
+ // GET /api/{name}/examples/{example} -> render a preview page
70
+ // with a predefined example body
71
+ const exampleRoute = `/api/${name}/examples/${exampleName}`;
72
+ app.get(
73
+ exampleRoute,
74
+ {
75
+ schema: {
76
+ // Add a clickable link to the example route in route's Swagger
77
+ // description so it's easier to navigate to
78
+ description: `Open sandbox: [${exampleRoute}](${exampleRoute})`,
79
+ },
80
+ },
81
+ async (_, reply) => {
82
+ const { html, assets: outAssets } = renderApp(
83
+ {
84
+ name,
85
+ assets,
86
+ component,
87
+ },
88
+ example as Record<string, unknown>
89
+ );
90
+
91
+ reply.type('text/html');
92
+
93
+ return renderPreviewPage(html, outAssets);
94
+ }
95
+ );
96
+ }
97
+ }
98
+
99
+ // Add graceful shutdown handler to prevent requests errors
100
+ await app.register(fastifyGracefulShutdown);
101
+
102
+ if (process.env.ENABLE_K8S_PROBES) {
103
+ await setupK8SProbes(app);
104
+ }
105
+
106
+ // TODO: remove hardcoded port
107
+ await app.listen({
108
+ host: '0.0.0.0',
109
+ port: 3000,
110
+ });
111
+
112
+ console.log('Nerest is listening on 0.0.0.0:3000');
113
+ }
114
+
115
+ runProductionServer();
@@ -1 +0,0 @@
1
- export {};
@@ -1,3 +0,0 @@
1
- const apps = import.meta.glob('/apps/*/index.tsx', { import: 'default' });
2
- console.log(apps);
3
- export {};
@@ -1,2 +0,0 @@
1
- import type { Manifest } from 'vite';
2
- export declare function loadAppAssets(manifest: Manifest, appName: string): string[];
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadAppAssets = void 0;
4
- // TODO: this is not as simple in the real world
5
- const publicPath = process.env.PUBLIC_PATH ?? 'http://0.0.0.0:3000/';
6
- // Extracts the list of assets for a given app from the manifest file
7
- function loadAppAssets(manifest, appName) {
8
- const entries = Object.entries(manifest);
9
- // TODO: handling errors and potentially missing entries
10
- const clientEntryJs = entries.find(([_, entry]) => entry.isEntry)?.[1].file;
11
- const appCss = entries.find(([name, _]) => name.includes(`/${appName}/index.tsx`))?.[1]
12
- .css ?? [];
13
- return [clientEntryJs, ...appCss].map((x) => publicPath + x);
14
- }
15
- exports.loadAppAssets = loadAppAssets;
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.renderPreviewPage = void 0;
4
- function renderPreviewPage(html, assets) {
5
- const { scripts, styles } = mapAssets(assets);
6
- return `
7
- <html>
8
- <head>
9
- ${styles.join('\n')}
10
- </head>
11
- <body>
12
- ${html}
13
- ${scripts.join('\n')}
14
- </body>
15
- </html>
16
- `;
17
- }
18
- exports.renderPreviewPage = renderPreviewPage;
19
- function mapAssets(assets) {
20
- const scripts = assets
21
- .filter((src) => src.endsWith('.js'))
22
- .map((src) => `<script type="module" src="${src}"></script>`);
23
- const styles = assets
24
- .filter((src) => src.endsWith('.css'))
25
- .map((src) => `<link rel="stylesheet" href="${src}">`);
26
- return { scripts, styles };
27
- }
@@ -1 +0,0 @@
1
- export declare function getAppAssets(appName: string, root: string): Promise<string[]>;
@@ -1,28 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getAppAssets = void 0;
7
- const path_1 = __importDefault(require("path"));
8
- const promises_1 = __importDefault(require("fs/promises"));
9
- // TODO: way more complicated in the real world
10
- const publicPath = process.env.PUBLIC_PATH ?? 'http://127.0.0.1:3000/';
11
- async function getAppAssets(appName, root) {
12
- const manifest = await loadManifest(root);
13
- const assets = extractAssetsFromManifest(manifest, appName);
14
- return assets.map((x) => publicPath + x);
15
- }
16
- exports.getAppAssets = getAppAssets;
17
- async function loadManifest(root) {
18
- const manifestPath = path_1.default.join(root, 'dist', 'manifest.json');
19
- const manifestData = await promises_1.default.readFile(manifestPath, { encoding: 'utf8' });
20
- return JSON.parse(manifestData);
21
- }
22
- function extractAssetsFromManifest(manifest, appName) {
23
- const entries = Object.entries(manifest);
24
- const clientEntryJs = entries.find(([_, entry]) => entry.isEntry)?.[1].file;
25
- const appCss = entries.find(([name, _]) => name.includes(`/${appName}/index.tsx`))?.[1]
26
- .css ?? [];
27
- return [clientEntryJs, ...appCss];
28
- }
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare function renderSsrComponent(appName: string, AppComponent: React.ComponentType, props?: Record<string, unknown>): string;
@@ -1,17 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.renderSsrComponent = void 0;
7
- const react_1 = __importDefault(require("react"));
8
- const server_1 = require("react-dom/server");
9
- const nanoid_1 = require("nanoid");
10
- function renderSsrComponent(appName, AppComponent, props = {}) {
11
- const html = (0, server_1.renderToString)(react_1.default.createElement(AppComponent, { ...props }));
12
- const appId = (0, nanoid_1.nanoid)();
13
- const container = `<div data-app-name="${appName}" data-app-id="${appId}">${html}</div>`;
14
- const script = `<script type="application/json" data-app-id="${appId}">${JSON.stringify(props)}</script>`;
15
- return container + script;
16
- }
17
- exports.renderSsrComponent = renderSsrComponent;
@@ -1,5 +0,0 @@
1
- /// <reference types="node" />
2
- import type { ServerResponse } from 'http';
3
- export declare function createServer(): Promise<{
4
- app: import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof ServerResponse>, import("http").IncomingMessage, ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault> & PromiseLike<import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof ServerResponse>, import("http").IncomingMessage, ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>>;
5
- }>;
@@ -1 +0,0 @@
1
- export declare function renderPreviewPage(html: string, assets: string[]): string;
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- import type { AppEntry } from './apps';
3
- export declare function renderApp(appEntry: AppEntry, appComponent: React.ComponentType, props?: Record<string, unknown>): {
4
- html: string;
5
- assets: string[];
6
- };
@@ -1,17 +0,0 @@
1
- import type { Manifest } from 'vite';
2
-
3
- // TODO: this is not as simple in the real world
4
- const publicPath = process.env.PUBLIC_PATH ?? 'http://0.0.0.0:3000/';
5
-
6
- // Extracts the list of assets for a given app from the manifest file
7
- export function loadAppAssets(manifest: Manifest, appName: string) {
8
- const entries = Object.entries(manifest);
9
-
10
- // TODO: handling errors and potentially missing entries
11
- const clientEntryJs = entries.find(([_, entry]) => entry.isEntry)?.[1].file;
12
- const appCss =
13
- entries.find(([name, _]) => name.includes(`/${appName}/index.tsx`))?.[1]
14
- .css ?? [];
15
-
16
- return [clientEntryJs, ...appCss].map((x) => publicPath + x);
17
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes