@happy-dom/server-renderer 20.0.11 → 20.1.1

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 (72) hide show
  1. package/lib/ServerRenderer.d.ts +3 -3
  2. package/lib/ServerRenderer.d.ts.map +1 -1
  3. package/lib/ServerRenderer.js +27 -13
  4. package/lib/ServerRenderer.js.map +1 -1
  5. package/lib/ServerRendererBrowser.d.ts.map +1 -1
  6. package/lib/ServerRendererBrowser.js +7 -133
  7. package/lib/ServerRendererBrowser.js.map +1 -1
  8. package/lib/{ServerRendererWorker.d.ts → ServerRendererBrowserWorker.d.ts} +2 -2
  9. package/lib/ServerRendererBrowserWorker.d.ts.map +1 -0
  10. package/lib/{ServerRendererWorker.js → ServerRendererBrowserWorker.js} +3 -3
  11. package/lib/ServerRendererBrowserWorker.js.map +1 -0
  12. package/lib/ServerRendererPage.d.ts +24 -0
  13. package/lib/ServerRendererPage.d.ts.map +1 -0
  14. package/lib/ServerRendererPage.js +234 -0
  15. package/lib/ServerRendererPage.js.map +1 -0
  16. package/lib/ServerRendererPageWorker.d.ts +15 -0
  17. package/lib/ServerRendererPageWorker.d.ts.map +1 -0
  18. package/lib/ServerRendererPageWorker.js +38 -0
  19. package/lib/ServerRendererPageWorker.js.map +1 -0
  20. package/lib/ServerRendererServer.d.ts.map +1 -1
  21. package/lib/ServerRendererServer.js +0 -4
  22. package/lib/ServerRendererServer.js.map +1 -1
  23. package/lib/config/DefaultServerRendererConfiguration.d.ts.map +1 -1
  24. package/lib/config/DefaultServerRendererConfiguration.js +6 -6
  25. package/lib/config/DefaultServerRendererConfiguration.js.map +1 -1
  26. package/lib/enums/ServerRendererModeEnum.d.ts +6 -0
  27. package/lib/enums/ServerRendererModeEnum.d.ts.map +1 -0
  28. package/lib/enums/ServerRendererModeEnum.js +9 -0
  29. package/lib/enums/ServerRendererModeEnum.js.map +1 -0
  30. package/lib/index.d.ts +2 -1
  31. package/lib/index.d.ts.map +1 -1
  32. package/lib/index.js +2 -1
  33. package/lib/index.js.map +1 -1
  34. package/lib/types/IOptionalServerRendererConfiguration.d.ts +11 -2
  35. package/lib/types/IOptionalServerRendererConfiguration.d.ts.map +1 -1
  36. package/lib/types/IServerRendererConfiguration.d.ts +11 -2
  37. package/lib/types/IServerRendererConfiguration.d.ts.map +1 -1
  38. package/lib/types/IServerRendererItem.d.ts +2 -1
  39. package/lib/types/IServerRendererItem.d.ts.map +1 -1
  40. package/lib/types/IServerRendererResult.d.ts +4 -4
  41. package/lib/types/IServerRendererResult.d.ts.map +1 -1
  42. package/lib/utilities/BrowserWindowPolyfill.d.ts.map +1 -1
  43. package/lib/utilities/BrowserWindowPolyfill.js +2 -2
  44. package/lib/utilities/BrowserWindowPolyfill.js.map +1 -1
  45. package/lib/utilities/HelpPrinterRows.d.ts.map +1 -1
  46. package/lib/utilities/HelpPrinterRows.js +25 -4
  47. package/lib/utilities/HelpPrinterRows.js.map +1 -1
  48. package/lib/utilities/ProcessArgumentsParser.d.ts.map +1 -1
  49. package/lib/utilities/ProcessArgumentsParser.js +86 -22
  50. package/lib/utilities/ProcessArgumentsParser.js.map +1 -1
  51. package/lib/utilities/ServerRendererConfigurationFactory.js +1 -1
  52. package/lib/utilities/ServerRendererConfigurationFactory.js.map +1 -1
  53. package/package.json +3 -2
  54. package/src/ServerRenderer.ts +36 -13
  55. package/src/ServerRendererBrowser.ts +7 -151
  56. package/src/{ServerRendererWorker.ts → ServerRendererBrowserWorker.ts} +2 -2
  57. package/src/ServerRendererPage.ts +279 -0
  58. package/src/ServerRendererPageWorker.ts +55 -0
  59. package/src/ServerRendererServer.ts +3 -8
  60. package/src/config/DefaultServerRendererConfiguration.ts +6 -6
  61. package/src/enums/ServerRendererModeEnum.ts +8 -0
  62. package/src/index.ts +3 -1
  63. package/src/types/IOptionalServerRendererConfiguration.ts +21 -2
  64. package/src/types/IServerRendererConfiguration.ts +20 -2
  65. package/src/types/IServerRendererItem.ts +2 -1
  66. package/src/types/IServerRendererResult.ts +4 -4
  67. package/src/utilities/BrowserWindowPolyfill.ts +3 -2
  68. package/src/utilities/HelpPrinterRows.ts +25 -4
  69. package/src/utilities/ProcessArgumentsParser.ts +93 -24
  70. package/src/utilities/ServerRendererConfigurationFactory.ts +1 -1
  71. package/lib/ServerRendererWorker.d.ts.map +0 -1
  72. package/lib/ServerRendererWorker.js.map +0 -1
@@ -1,12 +1,8 @@
1
1
  import Browser from 'happy-dom/lib/browser/Browser.js';
2
- import HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';
3
2
  import IServerRendererConfiguration from './types/IServerRendererConfiguration.js';
4
- import FS from 'fs';
5
- import Path from 'path';
6
3
  import IServerRendererItem from './types/IServerRendererItem.js';
7
4
  import IServerRendererResult from './types/IServerRendererResult.js';
8
- import { ErrorEvent } from 'happy-dom';
9
- import BrowserWindowPolyfill from './utilities/BrowserWindowPolyfill.js';
5
+ import ServerRendererPage from './ServerRendererPage.js';
10
6
 
11
7
  /**
12
8
  * Server renderer browser.
@@ -15,7 +11,7 @@ export default class ServerRendererBrowser {
15
11
  #configuration: IServerRendererConfiguration;
16
12
  #browser: Browser;
17
13
  #isCacheLoaded: boolean = false;
18
- #createdDirectories: Set<string> = new Set();
14
+ #pageRenderer: ServerRendererPage;
19
15
 
20
16
  /**
21
17
  * Constructor.
@@ -35,6 +31,7 @@ export default class ServerRendererBrowser {
35
31
  }
36
32
  : configuration.browser;
37
33
  this.#browser = new Browser({ settings });
34
+ this.#pageRenderer = new ServerRendererPage(configuration);
38
35
  }
39
36
 
40
37
  /**
@@ -56,7 +53,7 @@ export default class ServerRendererBrowser {
56
53
  const promises = [];
57
54
  for (const url of chunk) {
58
55
  promises.push(
59
- this.#renderURL(browser, url).then((result) => {
56
+ this.#renderPage(browser, url).then((result) => {
60
57
  results.push(result);
61
58
  })
62
59
  );
@@ -66,7 +63,7 @@ export default class ServerRendererBrowser {
66
63
  } else {
67
64
  const promises = [];
68
65
  for (const url of items) {
69
- promises.push(this.#renderURL(browser, url));
66
+ promises.push(this.#renderPage(browser, url));
70
67
  }
71
68
  results = await Promise.all(promises);
72
69
  }
@@ -91,7 +88,7 @@ export default class ServerRendererBrowser {
91
88
  * @param browser Browser.
92
89
  * @param item Item.
93
90
  */
94
- async #renderURL(browser: Browser, item: IServerRendererItem): Promise<IServerRendererResult> {
91
+ async #renderPage(browser: Browser, item: IServerRendererItem): Promise<IServerRendererResult> {
95
92
  const responseCache = browser.defaultContext.responseCache;
96
93
  const preflightResponseCache = browser.defaultContext.preflightResponseCache;
97
94
  const configuration = this.#configuration;
@@ -108,148 +105,7 @@ export default class ServerRendererBrowser {
108
105
  context.preflightResponseCache = preflightResponseCache;
109
106
  }
110
107
 
111
- const pageErrors: string[] = [];
112
- const response = (await page.goto(item.url, {
113
- timeout: configuration.render.timeout,
114
- headers: item.headers,
115
- beforeContentCallback(window) {
116
- window.addEventListener('error', (event) => {
117
- if ((<ErrorEvent>event).error) {
118
- pageErrors.push((<ErrorEvent>event).error!.stack!);
119
- }
120
- });
121
- if (!configuration.render.disablePolyfills) {
122
- BrowserWindowPolyfill.applyPolyfills(window);
123
- }
124
- }
125
- }))!;
126
-
127
- const headers: { [key: string]: string } = {};
128
-
129
- for (const [key, value] of response.headers) {
130
- if (key !== 'content-encoding') {
131
- headers[key] = value;
132
- }
133
- }
134
-
135
- if (!response.ok) {
136
- const pageConsole = page.virtualConsolePrinter.readAsString();
137
- await page.close();
138
- return {
139
- url: item.url,
140
- content: null,
141
- status: response.status,
142
- statusText: response.statusText,
143
- headers,
144
- outputFile: item.outputFile ?? null,
145
- error: `Failed to render page ${item.url} (${response.status} ${response.statusText})`,
146
- pageConsole,
147
- pageErrors
148
- };
149
- }
150
-
151
- let timeoutError: string | null = null;
152
- const timeout =
153
- this.#browser.settings.debug.traceWaitUntilComplete === -1
154
- ? setTimeout(() => {
155
- timeoutError = `The page was not rendered within the defined time of ${configuration.render.timeout}ms and the operation was aborted. You can increase this value with the "render.timeout" setting.\n\nThe page may contain scripts with timer loops that prevent it from completing. You can debug open handles by setting "debug" to true, or prevent timer loops by setting "browser.timer.preventTimerLoops" to true. Read more about this in the documentation.`;
156
- page.abort();
157
- }, configuration.render.timeout)
158
- : null;
159
-
160
- try {
161
- await page.waitUntilComplete();
162
- } catch (error) {
163
- const pageConsole = page.virtualConsolePrinter.readAsString();
164
- await page.close();
165
- return {
166
- url: item.url,
167
- content: null,
168
- status: response.status,
169
- statusText: response.statusText,
170
- headers,
171
- outputFile: item.outputFile ?? null,
172
- error: (<Error>error).stack!,
173
- pageConsole,
174
- pageErrors
175
- };
176
- }
177
-
178
- // Wait for errors to be printed
179
- await new Promise((resolve) => setTimeout(resolve, 10));
180
-
181
- if (timeout) {
182
- clearTimeout(timeout);
183
- }
184
-
185
- const pageConsole = page.virtualConsolePrinter.readAsString();
186
-
187
- if (timeoutError) {
188
- page.close();
189
-
190
- return {
191
- url: item.url,
192
- content: null,
193
- status: response.status,
194
- statusText: response.statusText,
195
- headers,
196
- outputFile: null,
197
- error: timeoutError,
198
- pageConsole,
199
- pageErrors
200
- };
201
- }
202
-
203
- const serializer = new HTMLSerializer({
204
- allShadowRoots: configuration.render.allShadowRoots,
205
- serializableShadowRoots: configuration.render.serializableShadowRoots,
206
- excludeShadowRootTags: configuration.render.excludeShadowRootTags
207
- });
208
-
209
- const result = serializer.serializeToString(page.mainFrame.document);
210
-
211
- await page.close();
212
-
213
- if (!item.outputFile) {
214
- return {
215
- url: item.url,
216
- content: result,
217
- status: response.status,
218
- statusText: response.statusText,
219
- headers,
220
- outputFile: null,
221
- error: null,
222
- pageConsole,
223
- pageErrors
224
- };
225
- }
226
-
227
- const directory = Path.dirname(item.outputFile);
228
-
229
- if (!this.#createdDirectories.has(directory)) {
230
- this.#createdDirectories.add(directory);
231
- try {
232
- await FS.promises.mkdir(directory, {
233
- recursive: true
234
- });
235
- } catch {
236
- // Ignore
237
- }
238
- }
239
-
240
- await FS.promises.writeFile(item.outputFile, result);
241
-
242
- return {
243
- url: item.url,
244
- content: null,
245
- status: response.status,
246
- statusText: response.statusText,
247
- headers,
248
- outputFile: item.outputFile,
249
- error: null,
250
- pageConsole,
251
- pageErrors
252
- };
108
+ return this.#pageRenderer.render(page, item);
253
109
  }
254
110
 
255
111
  /**
@@ -5,7 +5,7 @@ import Inspector from 'node:inspector';
5
5
  /**
6
6
  * Server renderer worker.
7
7
  */
8
- export default class ServerRendererWorker {
8
+ export default class ServerRendererBrowserWorker {
9
9
  /**
10
10
  * Connects to the worker.
11
11
  */
@@ -26,4 +26,4 @@ export default class ServerRendererWorker {
26
26
  }
27
27
  }
28
28
 
29
- ServerRendererWorker.connect();
29
+ ServerRendererBrowserWorker.connect();
@@ -0,0 +1,279 @@
1
+ import HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';
2
+ import FS from 'fs';
3
+ import Path from 'path';
4
+ import IServerRendererItem from './types/IServerRendererItem.js';
5
+ import IServerRendererResult from './types/IServerRendererResult.js';
6
+ import { ErrorEvent, IBrowserPage, Response } from 'happy-dom';
7
+ import BrowserWindowPolyfill from './utilities/BrowserWindowPolyfill.js';
8
+ import IServerRendererConfiguration from './types/IServerRendererConfiguration.js';
9
+ import ServerRendererModeEnum from './enums/ServerRendererModeEnum.js';
10
+
11
+ const SET_TIMEOUT = setTimeout;
12
+ const CLEAR_TIMEOUT = clearTimeout;
13
+
14
+ /**
15
+ * Server renderer page.
16
+ */
17
+ export default class ServerRendererPage {
18
+ #configuration: IServerRendererConfiguration;
19
+ #createdDirectories: Set<string> = new Set();
20
+ #initialized: boolean = false;
21
+
22
+ /**
23
+ * Constructor.
24
+ *
25
+ * @param configuration Configuration.
26
+ */
27
+ constructor(configuration: IServerRendererConfiguration) {
28
+ this.#configuration = configuration;
29
+ }
30
+
31
+ /**
32
+ * Renders a page.
33
+ *
34
+ * @param page Browser page.
35
+ * @param item Item.
36
+ */
37
+ public async render(
38
+ page: IBrowserPage,
39
+ item: IServerRendererItem
40
+ ): Promise<IServerRendererResult> {
41
+ const configuration = this.#configuration;
42
+ const pageErrors: string[] = [];
43
+ const errorListener = (event: any): void => {
44
+ if ((<ErrorEvent>event).error) {
45
+ pageErrors.push((<ErrorEvent>event).error!.stack!);
46
+ }
47
+ };
48
+ let response: Response | null = null;
49
+ let headers: { [key: string]: string } | null = null;
50
+
51
+ if (item.html) {
52
+ if (item.url) {
53
+ page.mainFrame.url = item.url;
54
+ }
55
+
56
+ page.mainFrame.window.addEventListener('error', errorListener);
57
+
58
+ if (!this.#initialized) {
59
+ this.#initialized = true;
60
+
61
+ if (!configuration.render.disablePolyfills) {
62
+ BrowserWindowPolyfill.applyPolyfills(page.mainFrame.window);
63
+ }
64
+
65
+ if (configuration.render.setupScript) {
66
+ page.mainFrame.evaluateModule({ code: configuration.render.setupScript });
67
+ }
68
+ }
69
+
70
+ page.mainFrame.document.open();
71
+ page.mainFrame.document.write(item.html);
72
+ } else if (item.url) {
73
+ headers = {};
74
+
75
+ if (configuration.render.mode === ServerRendererModeEnum.page) {
76
+ response = await page.mainFrame.window.fetch(item.url, {
77
+ headers: item.headers || {}
78
+ });
79
+ } else {
80
+ response = (await page.goto(item.url, {
81
+ timeout: configuration.render.timeout,
82
+ headers: item.headers,
83
+ beforeContentCallback(window) {
84
+ window.addEventListener('error', errorListener);
85
+
86
+ if (!configuration.render.disablePolyfills) {
87
+ BrowserWindowPolyfill.applyPolyfills(window);
88
+ }
89
+
90
+ if (configuration.render.setupScript) {
91
+ page.evaluateModule({ code: configuration.render.setupScript });
92
+ }
93
+ }
94
+ }))!;
95
+ }
96
+
97
+ for (const [key, value] of response.headers) {
98
+ if (key !== 'content-encoding') {
99
+ headers[key] = value;
100
+ }
101
+ }
102
+
103
+ if (!response.ok) {
104
+ const pageConsole = page.virtualConsolePrinter.readAsString();
105
+
106
+ if (configuration.render.mode !== ServerRendererModeEnum.page) {
107
+ await page.close();
108
+ }
109
+
110
+ return {
111
+ url: item.url,
112
+ content: null,
113
+ status: response.status,
114
+ statusText: response.statusText,
115
+ headers,
116
+ outputFile: item.outputFile ?? null,
117
+ error: `Failed to render page ${item.url} (${response.status} ${response.statusText})`,
118
+ pageConsole,
119
+ pageErrors
120
+ };
121
+ }
122
+
123
+ if (configuration.render.mode === ServerRendererModeEnum.page) {
124
+ const text = await response.text();
125
+
126
+ page.mainFrame.window.addEventListener('error', errorListener);
127
+
128
+ if (!this.#initialized) {
129
+ this.#initialized = true;
130
+ if (!configuration.render.disablePolyfills) {
131
+ BrowserWindowPolyfill.applyPolyfills(page.mainFrame.window);
132
+ }
133
+
134
+ if (configuration.render.setupScript) {
135
+ page.mainFrame.evaluateModule({ code: configuration.render.setupScript });
136
+ }
137
+ }
138
+
139
+ page.mainFrame.document.open();
140
+ page.mainFrame.document.write(text);
141
+ }
142
+ } else {
143
+ const pageConsole = page.virtualConsolePrinter.readAsString();
144
+
145
+ if (configuration.render.mode !== ServerRendererModeEnum.page) {
146
+ await page.close();
147
+ }
148
+
149
+ return {
150
+ url: null,
151
+ content: null,
152
+ status: null,
153
+ statusText: null,
154
+ headers: null,
155
+ outputFile: item.outputFile ?? null,
156
+ error: `No "url" or "html" provided to render.`,
157
+ pageConsole,
158
+ pageErrors
159
+ };
160
+ }
161
+
162
+ let timeoutError: string | null = null;
163
+ const timeout =
164
+ configuration.browser.debug.traceWaitUntilComplete === -1
165
+ ? SET_TIMEOUT(() => {
166
+ timeoutError = `The page was not rendered within the defined time of ${configuration.render.timeout}ms and the operation was aborted. You can increase this value with the "render.timeout" setting.\n\nThe page may contain scripts with timer loops that prevent it from completing. You can debug open handles by setting "debug" to true, or prevent timer loops by setting "browser.timer.preventTimerLoops" to true. Read more about this in the documentation.`;
167
+ page.abort();
168
+ }, configuration.render.timeout)
169
+ : null;
170
+
171
+ try {
172
+ await page.waitUntilComplete();
173
+ } catch (error) {
174
+ const pageConsole = page.virtualConsolePrinter.readAsString();
175
+ const url = item.url || page.url;
176
+
177
+ if (configuration.render.mode !== ServerRendererModeEnum.page) {
178
+ await page.close();
179
+ }
180
+
181
+ return {
182
+ url,
183
+ content: null,
184
+ status: response?.status || null,
185
+ statusText: response?.statusText || null,
186
+ headers,
187
+ outputFile: item.outputFile ?? null,
188
+ error: (<Error>error).stack!,
189
+ pageConsole,
190
+ pageErrors
191
+ };
192
+ }
193
+
194
+ // Wait for errors to be printed
195
+ await new Promise((resolve) => SET_TIMEOUT(resolve, 10));
196
+
197
+ page.mainFrame.window.removeEventListener('error', errorListener);
198
+
199
+ if (timeout) {
200
+ CLEAR_TIMEOUT(timeout);
201
+ }
202
+
203
+ const pageConsole = page.virtualConsolePrinter.readAsString();
204
+
205
+ if (timeoutError) {
206
+ const url = item.url || page.url;
207
+
208
+ if (configuration.render.mode !== ServerRendererModeEnum.page) {
209
+ await page.close();
210
+ }
211
+
212
+ return {
213
+ url,
214
+ content: null,
215
+ status: response?.status || null,
216
+ statusText: response?.statusText || null,
217
+ headers,
218
+ outputFile: null,
219
+ error: timeoutError,
220
+ pageConsole,
221
+ pageErrors
222
+ };
223
+ }
224
+
225
+ const serializer = new HTMLSerializer({
226
+ allShadowRoots: configuration.render.allShadowRoots,
227
+ serializableShadowRoots: configuration.render.serializableShadowRoots,
228
+ excludeShadowRootTags: configuration.render.excludeShadowRootTags
229
+ });
230
+
231
+ const result = serializer.serializeToString(page.mainFrame.document);
232
+ const url = item.url || page.url;
233
+
234
+ if (configuration.render.mode !== ServerRendererModeEnum.page) {
235
+ await page.close();
236
+ }
237
+
238
+ if (!item.outputFile) {
239
+ return {
240
+ url,
241
+ content: result,
242
+ status: response?.status || null,
243
+ statusText: response?.statusText || null,
244
+ headers,
245
+ outputFile: null,
246
+ error: null,
247
+ pageConsole,
248
+ pageErrors
249
+ };
250
+ }
251
+
252
+ const directory = Path.dirname(item.outputFile);
253
+
254
+ if (!this.#createdDirectories.has(directory)) {
255
+ this.#createdDirectories.add(directory);
256
+ try {
257
+ await FS.promises.mkdir(directory, {
258
+ recursive: true
259
+ });
260
+ } catch {
261
+ // Ignore
262
+ }
263
+ }
264
+
265
+ await FS.promises.writeFile(item.outputFile, result);
266
+
267
+ return {
268
+ url,
269
+ content: null,
270
+ status: response?.status || null,
271
+ statusText: response?.statusText || null,
272
+ headers,
273
+ outputFile: item.outputFile,
274
+ error: null,
275
+ pageConsole,
276
+ pageErrors
277
+ };
278
+ }
279
+ }
@@ -0,0 +1,55 @@
1
+ import { parentPort, workerData } from 'worker_threads';
2
+ import Inspector from 'node:inspector';
3
+ import { GlobalRegistrator } from '@happy-dom/global-registrator';
4
+ import { Document, Window } from 'happy-dom';
5
+ import WindowBrowserContext from 'happy-dom/lib/window/WindowBrowserContext.js';
6
+ import ServerRendererPage from './ServerRendererPage.js';
7
+
8
+ declare global {
9
+ const document: Document;
10
+ const window: Window;
11
+ }
12
+
13
+ /**
14
+ * Server renderer worker.
15
+ */
16
+ export default class ServerRendererPageWorker {
17
+ /**
18
+ * Connects to the worker.
19
+ */
20
+ public static async connect(): Promise<void> {
21
+ const { configuration } = workerData;
22
+
23
+ if (configuration.inspect) {
24
+ Inspector.open();
25
+ Inspector.waitForDebugger();
26
+ }
27
+
28
+ GlobalRegistrator.register({
29
+ url: 'https://localhost:8080/',
30
+ settings: configuration.browser
31
+ });
32
+
33
+ const renderer = new ServerRendererPage(configuration);
34
+
35
+ parentPort?.on('message', async ({ items }) => {
36
+ if (items.length !== 1) {
37
+ throw new Error(
38
+ 'Failed to render page worker. Page workers can only render one item at a time.'
39
+ );
40
+ }
41
+
42
+ const page = new WindowBrowserContext(window).getBrowserPage();
43
+
44
+ if (!page) {
45
+ throw new Error('Failed to render page. No browser page available.');
46
+ }
47
+
48
+ const result = await renderer.render(page, items[0]);
49
+
50
+ parentPort?.postMessage({ status: 'done', results: [result] });
51
+ });
52
+ }
53
+ }
54
+
55
+ ServerRendererPageWorker.connect();
@@ -197,11 +197,6 @@ export default class ServerRendererServer {
197
197
  result = (
198
198
  await this.#serverRenderer.render([{ url: url.href, headers }], { keepAlive: true })
199
199
  )[0];
200
-
201
- if (this.#configuration.logLevel >= ServerRendererLogLevelEnum.info) {
202
- // eslint-disable-next-line no-console
203
- console.log(Chalk.bold(`• Rendered ${url.href}`));
204
- }
205
200
  }
206
201
 
207
202
  if (isCacheQueueEnabled) {
@@ -215,18 +210,18 @@ export default class ServerRendererServer {
215
210
  }
216
211
  }
217
212
 
218
- for (const key of Object.keys(result.headers)) {
213
+ for (const key of Object.keys(result.headers!)) {
219
214
  const lowerKey = key.toLowerCase();
220
215
  if (
221
216
  lowerKey !== 'transfer-encoding' &&
222
217
  lowerKey !== 'content-length' &&
223
218
  lowerKey !== 'content-encoding'
224
219
  ) {
225
- response.setHeader(key, result.headers[key]);
220
+ response.setHeader(key, result.headers![key]);
226
221
  }
227
222
  }
228
223
 
229
- response.statusCode = result.error ? 500 : result.status;
224
+ response.statusCode = result.error ? 500 : result.status!;
230
225
 
231
226
  if (result.error) {
232
227
  if (this.#configuration.logLevel >= ServerRendererLogLevelEnum.error) {
@@ -3,14 +3,12 @@ import ServerRendererLogLevelEnum from '../enums/ServerRendererLogLevelEnum.js';
3
3
  import type IServerRendererConfiguration from '../types/IServerRendererConfiguration.js';
4
4
  import OS from 'os';
5
5
  import { BrowserErrorCaptureEnum } from 'happy-dom';
6
+ import ServerRendererModeEnum from '../enums/ServerRendererModeEnum.js';
6
7
 
7
8
  export default <IServerRendererConfiguration>{
8
9
  browser: {
9
10
  ...DefaultBrowserSettings,
10
- errorCapture: BrowserErrorCaptureEnum.processLevel,
11
- // This is enabled by default as the entire point of this package is to server-render client side JavaScript.
12
- // "--disallow-code-generation-from-strings" is enabled on workers to prevent escape of the VM context.
13
- enableJavaScriptEvaluation: true
11
+ errorCapture: BrowserErrorCaptureEnum.processLevel
14
12
  },
15
13
  outputDirectory: './happy-dom/render',
16
14
  logLevel: ServerRendererLogLevelEnum.info,
@@ -33,9 +31,11 @@ export default <IServerRendererConfiguration>{
33
31
  serializableShadowRoots: false,
34
32
  allShadowRoots: false,
35
33
  excludeShadowRootTags: null,
36
- disablePolyfills: false
34
+ disablePolyfills: false,
35
+ setupScript: null,
36
+ mode: ServerRendererModeEnum.browser
37
37
  },
38
- urls: null,
38
+ renderItems: null,
39
39
  server: {
40
40
  start: false,
41
41
  serverURL: 'https://localhost:3000',
@@ -0,0 +1,8 @@
1
+ enum ServerRendererModeEnum {
2
+ // Use a Browser instance in each worker to render pages.
3
+ browser = 'browser',
4
+ // Render a single page in each worker without using VM isolation which may not be supported in some execution environments.
5
+ page = 'page'
6
+ }
7
+
8
+ export default ServerRendererModeEnum;
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ import IServerRendererResult from './types/IServerRendererResult.js';
5
5
  import IServerRendererConfiguration from './types/IServerRendererConfiguration.js';
6
6
  import IOptionalServerRendererConfiguration from './types/IOptionalServerRendererConfiguration.js';
7
7
  import ServerRendererLogLevelEnum from './enums/ServerRendererLogLevelEnum.js';
8
+ import ServerRendererModeEnum from './enums/ServerRendererModeEnum.js';
8
9
 
9
10
  export {
10
11
  ServerRenderer,
@@ -13,5 +14,6 @@ export {
13
14
  IServerRendererResult,
14
15
  IServerRendererConfiguration,
15
16
  IOptionalServerRendererConfiguration,
16
- ServerRendererLogLevelEnum
17
+ ServerRendererLogLevelEnum,
18
+ ServerRendererModeEnum
17
19
  };