@buenojs/bueno 0.8.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.
- package/.env.example +109 -0
- package/.github/workflows/ci.yml +31 -0
- package/LICENSE +21 -0
- package/README.md +892 -0
- package/architecture.md +652 -0
- package/bun.lock +70 -0
- package/dist/cli/index.js +3233 -0
- package/dist/index.js +9014 -0
- package/package.json +77 -0
- package/src/cache/index.ts +795 -0
- package/src/cli/ARCHITECTURE.md +837 -0
- package/src/cli/bin.ts +10 -0
- package/src/cli/commands/build.ts +425 -0
- package/src/cli/commands/dev.ts +248 -0
- package/src/cli/commands/generate.ts +541 -0
- package/src/cli/commands/help.ts +55 -0
- package/src/cli/commands/index.ts +112 -0
- package/src/cli/commands/migration.ts +355 -0
- package/src/cli/commands/new.ts +804 -0
- package/src/cli/commands/start.ts +208 -0
- package/src/cli/core/args.ts +283 -0
- package/src/cli/core/console.ts +349 -0
- package/src/cli/core/index.ts +60 -0
- package/src/cli/core/prompt.ts +424 -0
- package/src/cli/core/spinner.ts +265 -0
- package/src/cli/index.ts +135 -0
- package/src/cli/templates/deploy.ts +295 -0
- package/src/cli/templates/docker.ts +307 -0
- package/src/cli/templates/index.ts +24 -0
- package/src/cli/utils/fs.ts +428 -0
- package/src/cli/utils/index.ts +8 -0
- package/src/cli/utils/strings.ts +197 -0
- package/src/config/env.ts +408 -0
- package/src/config/index.ts +506 -0
- package/src/config/loader.ts +329 -0
- package/src/config/merge.ts +285 -0
- package/src/config/types.ts +320 -0
- package/src/config/validation.ts +441 -0
- package/src/container/forward-ref.ts +143 -0
- package/src/container/index.ts +386 -0
- package/src/context/index.ts +360 -0
- package/src/database/index.ts +1142 -0
- package/src/database/migrations/index.ts +371 -0
- package/src/database/schema/index.ts +619 -0
- package/src/frontend/api-routes.ts +640 -0
- package/src/frontend/bundler.ts +643 -0
- package/src/frontend/console-client.ts +419 -0
- package/src/frontend/console-stream.ts +587 -0
- package/src/frontend/dev-server.ts +846 -0
- package/src/frontend/file-router.ts +611 -0
- package/src/frontend/frameworks/index.ts +106 -0
- package/src/frontend/frameworks/react.ts +85 -0
- package/src/frontend/frameworks/solid.ts +104 -0
- package/src/frontend/frameworks/svelte.ts +110 -0
- package/src/frontend/frameworks/vue.ts +92 -0
- package/src/frontend/hmr-client.ts +663 -0
- package/src/frontend/hmr.ts +728 -0
- package/src/frontend/index.ts +342 -0
- package/src/frontend/islands.ts +552 -0
- package/src/frontend/isr.ts +555 -0
- package/src/frontend/layout.ts +475 -0
- package/src/frontend/ssr/react.ts +446 -0
- package/src/frontend/ssr/solid.ts +523 -0
- package/src/frontend/ssr/svelte.ts +546 -0
- package/src/frontend/ssr/vue.ts +504 -0
- package/src/frontend/ssr.ts +699 -0
- package/src/frontend/types.ts +2274 -0
- package/src/health/index.ts +604 -0
- package/src/index.ts +410 -0
- package/src/lock/index.ts +587 -0
- package/src/logger/index.ts +444 -0
- package/src/logger/transports/index.ts +969 -0
- package/src/metrics/index.ts +494 -0
- package/src/middleware/built-in.ts +360 -0
- package/src/middleware/index.ts +94 -0
- package/src/modules/filters.ts +458 -0
- package/src/modules/guards.ts +405 -0
- package/src/modules/index.ts +1256 -0
- package/src/modules/interceptors.ts +574 -0
- package/src/modules/lazy.ts +418 -0
- package/src/modules/lifecycle.ts +478 -0
- package/src/modules/metadata.ts +90 -0
- package/src/modules/pipes.ts +626 -0
- package/src/router/index.ts +339 -0
- package/src/router/linear.ts +371 -0
- package/src/router/regex.ts +292 -0
- package/src/router/tree.ts +562 -0
- package/src/rpc/index.ts +1263 -0
- package/src/security/index.ts +436 -0
- package/src/ssg/index.ts +631 -0
- package/src/storage/index.ts +456 -0
- package/src/telemetry/index.ts +1097 -0
- package/src/testing/index.ts +1586 -0
- package/src/types/index.ts +236 -0
- package/src/types/optional-deps.d.ts +219 -0
- package/src/validation/index.ts +276 -0
- package/src/websocket/index.ts +1004 -0
- package/tests/integration/cli.test.ts +1016 -0
- package/tests/integration/fullstack.test.ts +234 -0
- package/tests/unit/cache.test.ts +174 -0
- package/tests/unit/cli-commands.test.ts +892 -0
- package/tests/unit/cli.test.ts +1258 -0
- package/tests/unit/container.test.ts +279 -0
- package/tests/unit/context.test.ts +221 -0
- package/tests/unit/database.test.ts +183 -0
- package/tests/unit/linear-router.test.ts +280 -0
- package/tests/unit/lock.test.ts +336 -0
- package/tests/unit/middleware.test.ts +184 -0
- package/tests/unit/modules.test.ts +142 -0
- package/tests/unit/pubsub.test.ts +257 -0
- package/tests/unit/regex-router.test.ts +265 -0
- package/tests/unit/router.test.ts +373 -0
- package/tests/unit/rpc.test.ts +1248 -0
- package/tests/unit/security.test.ts +174 -0
- package/tests/unit/telemetry.test.ts +371 -0
- package/tests/unit/test-cache.test.ts +110 -0
- package/tests/unit/test-database.test.ts +282 -0
- package/tests/unit/tree-router.test.ts +325 -0
- package/tests/unit/validation.test.ts +794 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React SSR Renderer
|
|
3
|
+
*
|
|
4
|
+
* Provides server-side rendering for React components using:
|
|
5
|
+
* - renderToPipeableStream / renderToReadableStream for streaming
|
|
6
|
+
* - React Helmet for head management
|
|
7
|
+
* - Suspense boundary support
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
SSRContext,
|
|
12
|
+
SSRElement,
|
|
13
|
+
SSRPage,
|
|
14
|
+
FrameworkSSRRenderer,
|
|
15
|
+
SSRHydrationData,
|
|
16
|
+
} from "../types.js";
|
|
17
|
+
|
|
18
|
+
// React types (dynamically imported)
|
|
19
|
+
interface ReactElement {
|
|
20
|
+
type: unknown;
|
|
21
|
+
props: Record<string, unknown>;
|
|
22
|
+
key: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ReactComponent {
|
|
26
|
+
(props: Record<string, unknown>): ReactElement | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Head element storage (similar to React Helmet)
|
|
30
|
+
let headElements: SSRElement[] = [];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reset head elements for a new render
|
|
34
|
+
*/
|
|
35
|
+
export function resetHead(): void {
|
|
36
|
+
headElements = [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get collected head elements
|
|
41
|
+
*/
|
|
42
|
+
export function getHeadElements(): SSRElement[] {
|
|
43
|
+
return [...headElements];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Add a head element
|
|
48
|
+
*/
|
|
49
|
+
export function addHeadElement(element: SSRElement): void {
|
|
50
|
+
headElements.push(element);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a title element for head
|
|
55
|
+
*/
|
|
56
|
+
export function title(text: string): SSRElement {
|
|
57
|
+
return { tag: "title", attrs: {}, children: [{ tag: "#text", attrs: {}, innerHTML: text }] };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create a meta element for head
|
|
62
|
+
*/
|
|
63
|
+
export function meta(attrs: Record<string, string>): SSRElement {
|
|
64
|
+
return { tag: "meta", attrs };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a link element for head
|
|
69
|
+
*/
|
|
70
|
+
export function link(attrs: Record<string, string>): SSRElement {
|
|
71
|
+
return { tag: "link", attrs };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create a script element for head
|
|
76
|
+
*/
|
|
77
|
+
export function script(attrs: Record<string, string>, innerHTML?: string): SSRElement {
|
|
78
|
+
return { tag: "script", attrs, innerHTML };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create a style element for head
|
|
83
|
+
*/
|
|
84
|
+
export function style(innerHTML: string, attrs?: Record<string, string>): SSRElement {
|
|
85
|
+
return { tag: "style", attrs: attrs || {}, innerHTML };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* React SSR Renderer implementation
|
|
90
|
+
*/
|
|
91
|
+
export class ReactSSRRenderer implements FrameworkSSRRenderer {
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
private react: any = null;
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
private reactDomServer: any = null;
|
|
96
|
+
private initialized = false;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Initialize React modules
|
|
100
|
+
*/
|
|
101
|
+
async init(): Promise<void> {
|
|
102
|
+
if (this.initialized) return;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
this.react = await import("react");
|
|
106
|
+
this.reactDomServer = await import("react-dom/server");
|
|
107
|
+
this.initialized = true;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"React is not installed. Install it with: bun add react react-dom"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Render a component to HTML string
|
|
117
|
+
*/
|
|
118
|
+
async renderToString(component: unknown, context: SSRContext): Promise<string> {
|
|
119
|
+
await this.init();
|
|
120
|
+
|
|
121
|
+
if (!this.reactDomServer) {
|
|
122
|
+
throw new Error("React DOM Server not initialized");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
resetHead();
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
// Use renderToString for non-streaming
|
|
129
|
+
const renderToStringFn = (this.reactDomServer as unknown as { renderToString: (el: unknown) => string }).renderToString;
|
|
130
|
+
const html = renderToStringFn(component as ReactElement);
|
|
131
|
+
return html;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
134
|
+
throw new Error(`React renderToString failed: ${errorMessage}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Render a component to a stream
|
|
140
|
+
*/
|
|
141
|
+
renderToStream(component: unknown, context: SSRContext): ReadableStream<Uint8Array> {
|
|
142
|
+
// Create a promise-based initialization
|
|
143
|
+
const encoder = new TextEncoder();
|
|
144
|
+
|
|
145
|
+
return new ReadableStream<Uint8Array>({
|
|
146
|
+
start: async (controller) => {
|
|
147
|
+
try {
|
|
148
|
+
await this.init();
|
|
149
|
+
|
|
150
|
+
if (!this.reactDomServer) {
|
|
151
|
+
controller.error(new Error("React DOM Server not initialized"));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
resetHead();
|
|
156
|
+
|
|
157
|
+
// Check if renderToReadableStream is available (modern API)
|
|
158
|
+
if ("renderToReadableStream" in this.reactDomServer) {
|
|
159
|
+
const stream = await this.reactDomServer.renderToReadableStream(
|
|
160
|
+
component as ReactElement,
|
|
161
|
+
{
|
|
162
|
+
bootstrapScripts: [],
|
|
163
|
+
onError: (error: Error) => {
|
|
164
|
+
console.error("React streaming error:", error);
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const reader = stream.getReader();
|
|
170
|
+
|
|
171
|
+
while (true) {
|
|
172
|
+
const { done, value } = await reader.read();
|
|
173
|
+
if (done) {
|
|
174
|
+
controller.close();
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
controller.enqueue(value);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
// Fallback to renderToString for older React versions
|
|
181
|
+
const html = this.reactDomServer.renderToString(component as ReactElement);
|
|
182
|
+
controller.enqueue(encoder.encode(html));
|
|
183
|
+
controller.close();
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
187
|
+
controller.error(new Error(`React renderToStream failed: ${errorMessage}`));
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get head elements from component
|
|
195
|
+
*/
|
|
196
|
+
getHeadElements(context: SSRContext): SSRElement[] {
|
|
197
|
+
return getHeadElements();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Create the framework-specific component
|
|
202
|
+
*/
|
|
203
|
+
createComponent(page: SSRPage, context: SSRContext): unknown {
|
|
204
|
+
// Return the page render function wrapped in React context
|
|
205
|
+
return {
|
|
206
|
+
page,
|
|
207
|
+
context,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Render with Suspense support
|
|
213
|
+
*/
|
|
214
|
+
async renderWithSuspense(
|
|
215
|
+
component: unknown,
|
|
216
|
+
context: SSRContext,
|
|
217
|
+
fallback: string = "<div>Loading...</div>"
|
|
218
|
+
): Promise<string> {
|
|
219
|
+
await this.init();
|
|
220
|
+
|
|
221
|
+
if (!this.react || !this.reactDomServer) {
|
|
222
|
+
throw new Error("React not initialized");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
resetHead();
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
// Wrap with Suspense if available
|
|
229
|
+
const suspenseWrapper = this.react.createElement(
|
|
230
|
+
this.react.Suspense,
|
|
231
|
+
{ fallback },
|
|
232
|
+
component as ReactElement
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const html = this.reactDomServer.renderToString(suspenseWrapper);
|
|
236
|
+
return html;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
239
|
+
throw new Error(`React renderWithSuspense failed: ${errorMessage}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Render to stream with all callbacks
|
|
245
|
+
*/
|
|
246
|
+
async renderToStreamWithCallbacks(
|
|
247
|
+
component: unknown,
|
|
248
|
+
context: SSRContext,
|
|
249
|
+
options: {
|
|
250
|
+
onAllReady?: () => void;
|
|
251
|
+
onShellReady?: () => void;
|
|
252
|
+
onShellError?: (error: Error) => void;
|
|
253
|
+
onError?: (error: Error) => void;
|
|
254
|
+
} = {}
|
|
255
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
256
|
+
await this.init();
|
|
257
|
+
|
|
258
|
+
if (!this.reactDomServer) {
|
|
259
|
+
throw new Error("React DOM Server not initialized");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
resetHead();
|
|
263
|
+
|
|
264
|
+
const encoder = new TextEncoder();
|
|
265
|
+
|
|
266
|
+
return new ReadableStream<Uint8Array>({
|
|
267
|
+
start: async (controller) => {
|
|
268
|
+
try {
|
|
269
|
+
if ("renderToReadableStream" in this.reactDomServer!) {
|
|
270
|
+
const stream = await this.reactDomServer.renderToReadableStream(
|
|
271
|
+
component as ReactElement,
|
|
272
|
+
{
|
|
273
|
+
bootstrapScripts: [],
|
|
274
|
+
onError: (error: Error) => {
|
|
275
|
+
options.onError?.(error);
|
|
276
|
+
console.error("React streaming error:", error);
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Wait for shell to be ready
|
|
282
|
+
await stream.allReady;
|
|
283
|
+
options.onAllReady?.();
|
|
284
|
+
|
|
285
|
+
const reader = stream.getReader();
|
|
286
|
+
|
|
287
|
+
while (true) {
|
|
288
|
+
const { done, value } = await reader.read();
|
|
289
|
+
if (done) {
|
|
290
|
+
controller.close();
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
controller.enqueue(value);
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
// Fallback
|
|
297
|
+
const html = this.reactDomServer!.renderToString(component as ReactElement);
|
|
298
|
+
options.onAllReady?.();
|
|
299
|
+
controller.enqueue(encoder.encode(html));
|
|
300
|
+
controller.close();
|
|
301
|
+
}
|
|
302
|
+
} catch (error) {
|
|
303
|
+
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
304
|
+
options.onShellError?.(err);
|
|
305
|
+
controller.error(err);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Create a React SSR renderer
|
|
314
|
+
*/
|
|
315
|
+
export function createReactSSRRenderer(): ReactSSRRenderer {
|
|
316
|
+
return new ReactSSRRenderer();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Convert SSRElement to HTML string
|
|
321
|
+
*/
|
|
322
|
+
export function ssrElementToString(element: SSRElement): string {
|
|
323
|
+
if (element.tag === "#text") {
|
|
324
|
+
return escapeHtml(element.innerHTML || "");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const attrs = Object.entries(element.attrs)
|
|
328
|
+
.map(([key, value]) => `${key}="${escapeHtml(value)}"`)
|
|
329
|
+
.join(" ");
|
|
330
|
+
|
|
331
|
+
const openTag = attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
|
|
332
|
+
|
|
333
|
+
if (element.innerHTML) {
|
|
334
|
+
return `${openTag}${element.innerHTML}</${element.tag}>`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (element.children && element.children.length > 0) {
|
|
338
|
+
const children = element.children.map(ssrElementToString).join("");
|
|
339
|
+
return `${openTag}${children}</${element.tag}>`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Self-closing tags
|
|
343
|
+
const voidElements = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"];
|
|
344
|
+
if (voidElements.includes(element.tag)) {
|
|
345
|
+
return attrs ? `<${element.tag} ${attrs}>` : `<${element.tag}>`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return `${openTag}</${element.tag}>`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Escape HTML special characters
|
|
353
|
+
*/
|
|
354
|
+
function escapeHtml(str: string): string {
|
|
355
|
+
return str
|
|
356
|
+
.replace(/&/g, "\x26amp;")
|
|
357
|
+
.replace(/</g, "\x26lt;")
|
|
358
|
+
.replace(/>/g, "\x26gt;")
|
|
359
|
+
.replace(/"/g, "\x26quot;")
|
|
360
|
+
.replace(/'/g, "'");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* React Helmet-like head management
|
|
365
|
+
*/
|
|
366
|
+
export class ReactHelmet {
|
|
367
|
+
private static instance: ReactHelmet;
|
|
368
|
+
private title = "";
|
|
369
|
+
private metaTags: SSRElement[] = [];
|
|
370
|
+
private linkTags: SSRElement[] = [];
|
|
371
|
+
private scriptTags: SSRElement[] = [];
|
|
372
|
+
private styleTags: SSRElement[] = [];
|
|
373
|
+
|
|
374
|
+
static getInstance(): ReactHelmet {
|
|
375
|
+
if (!ReactHelmet.instance) {
|
|
376
|
+
ReactHelmet.instance = new ReactHelmet();
|
|
377
|
+
}
|
|
378
|
+
return ReactHelmet.instance;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
setTitle(title: string): this {
|
|
382
|
+
this.title = title;
|
|
383
|
+
addHeadElement({ tag: "title", attrs: {}, children: [{ tag: "#text", attrs: {}, innerHTML: title }] });
|
|
384
|
+
return this;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
addMeta(attrs: Record<string, string>): this {
|
|
388
|
+
this.metaTags.push({ tag: "meta", attrs });
|
|
389
|
+
addHeadElement({ tag: "meta", attrs });
|
|
390
|
+
return this;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
addLink(attrs: Record<string, string>): this {
|
|
394
|
+
this.linkTags.push({ tag: "link", attrs });
|
|
395
|
+
addHeadElement({ tag: "link", attrs });
|
|
396
|
+
return this;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
addScript(attrs: Record<string, string>, innerHTML?: string): this {
|
|
400
|
+
this.scriptTags.push({ tag: "script", attrs, innerHTML });
|
|
401
|
+
addHeadElement({ tag: "script", attrs, innerHTML });
|
|
402
|
+
return this;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
addStyle(innerHTML: string, attrs?: Record<string, string>): this {
|
|
406
|
+
this.styleTags.push({ tag: "style", attrs: attrs || {}, innerHTML });
|
|
407
|
+
addHeadElement({ tag: "style", attrs: attrs || {}, innerHTML });
|
|
408
|
+
return this;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
reset(): void {
|
|
412
|
+
this.title = "";
|
|
413
|
+
this.metaTags = [];
|
|
414
|
+
this.linkTags = [];
|
|
415
|
+
this.scriptTags = [];
|
|
416
|
+
this.styleTags = [];
|
|
417
|
+
resetHead();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
getTitle(): string {
|
|
421
|
+
return this.title;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
getMetaTags(): SSRElement[] {
|
|
425
|
+
return [...this.metaTags];
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
getLinkTags(): SSRElement[] {
|
|
429
|
+
return [...this.linkTags];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
getScriptTags(): SSRElement[] {
|
|
433
|
+
return [...this.scriptTags];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
getStyleTags(): SSRElement[] {
|
|
437
|
+
return [...this.styleTags];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Get React Helmet instance
|
|
443
|
+
*/
|
|
444
|
+
export function helmet(): ReactHelmet {
|
|
445
|
+
return ReactHelmet.getInstance();
|
|
446
|
+
}
|