@mulanjs/mulanjs 1.0.1-dev.20260227135307 → 1.0.1-dev.20260227172006

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 (52) hide show
  1. package/dist/compiler/ast-parser.d.ts +48 -0
  2. package/dist/compiler/ast-parser.js +54 -9
  3. package/dist/compiler/compiler.d.ts +9 -0
  4. package/dist/compiler/compiler.js +45 -2
  5. package/dist/compiler/dom-compiler.d.ts +7 -0
  6. package/dist/compiler/dom-compiler.js +90 -33
  7. package/dist/compiler/script-compiler.d.ts +11 -0
  8. package/dist/compiler/script-compiler.js +108 -116
  9. package/dist/compiler/sfc-parser.d.ts +21 -0
  10. package/dist/compiler/ssr-compiler.d.ts +7 -0
  11. package/dist/compiler/ssr-compiler.js +142 -0
  12. package/dist/compiler/style-compiler.d.ts +8 -0
  13. package/dist/compiler/template-compiler.d.ts +8 -0
  14. package/dist/components/bloch-sphere.js +9 -3
  15. package/dist/components/infinity-list.js +7 -1
  16. package/dist/core/component.js +66 -15
  17. package/dist/core/hooks.js +53 -29
  18. package/dist/core/quantum.js +30 -17
  19. package/dist/core/query.js +11 -6
  20. package/dist/core/reactive.js +88 -7
  21. package/dist/core/renderer.js +9 -8
  22. package/dist/core/ssr.js +50 -0
  23. package/dist/core/surge.js +7 -2
  24. package/dist/core/vault.js +9 -5
  25. package/dist/index.js +63 -27
  26. package/dist/mulan.esm.js +1933 -1626
  27. package/dist/mulan.esm.js.map +1 -1
  28. package/dist/mulan.js +1890 -1590
  29. package/dist/mulan.js.map +1 -1
  30. package/dist/router/index.js +17 -10
  31. package/dist/security/sanitizer.js +5 -1
  32. package/dist/store/index.js +9 -5
  33. package/dist/types/ast-parser.d.ts +2 -0
  34. package/dist/types/compiler/ast-parser.d.ts +2 -0
  35. package/dist/types/compiler/compiler.d.ts +1 -0
  36. package/dist/types/compiler/ssr-compiler.d.ts +7 -0
  37. package/dist/types/compiler.d.ts +1 -0
  38. package/dist/types/components/bloch-sphere.d.ts +3 -1
  39. package/dist/types/components/infinity-list.d.ts +3 -1
  40. package/dist/types/core/component.d.ts +14 -0
  41. package/dist/types/core/reactive.d.ts +5 -1
  42. package/dist/types/core/renderer.d.ts +0 -1
  43. package/dist/types/core/ssr.d.ts +9 -0
  44. package/dist/types/index.d.ts +1 -0
  45. package/dist/types/ssr-compiler.d.ts +7 -0
  46. package/package.json +1 -1
  47. package/src/compiler/ast-parser.ts +62 -10
  48. package/src/compiler/compiler.ts +46 -1
  49. package/src/compiler/dom-compiler.ts +100 -34
  50. package/src/compiler/script-compiler.ts +117 -126
  51. package/src/compiler/ssr-compiler.ts +157 -0
  52. package/src/loader/index.js +12 -19
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -7,9 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
9
  });
9
10
  };
10
- import { render } from '../core/renderer';
11
- import { Security } from '../security/sanitizer';
12
- export class MuRouter {
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createRouter = exports.MuRouter = void 0;
13
+ const renderer_1 = require("../core/renderer");
14
+ const sanitizer_1 = require("../security/sanitizer");
15
+ class MuRouter {
13
16
  constructor(routes, rootContainer = null) {
14
17
  this.currentPath = '';
15
18
  this.currentComponent = null;
@@ -71,7 +74,7 @@ export class MuRouter {
71
74
  const params = {};
72
75
  paramNames.forEach((name, i) => {
73
76
  // SECURE: Automatically sanitize all route parameters
74
- params[name] = Security.sanitize(match[i + 1]);
77
+ params[name] = sanitizer_1.Security.sanitize(match[i + 1]);
75
78
  });
76
79
  return { route: r, params };
77
80
  }
@@ -86,7 +89,7 @@ export class MuRouter {
86
89
  const hash = window.location.hash.slice(1) || '/';
87
90
  const { route, params } = this.matchRoute(hash);
88
91
  // Security Check: Validate URL to prevent malicious hash injection
89
- if (hash !== '/' && !Security.validateUrl('http://dummy.com' + hash)) {
92
+ if (hash !== '/' && !sanitizer_1.Security.validateUrl('http://dummy.com' + hash)) {
90
93
  console.error("Security Alert: Malformed URL detected.");
91
94
  return;
92
95
  }
@@ -155,19 +158,19 @@ export class MuRouter {
155
158
  // Object Component (Simple SFC without script or Options API)
156
159
  const html = componentToRender.template();
157
160
  if (this.rootContainer)
158
- render(html, this.rootContainer);
161
+ (0, renderer_1.render)(html, this.rootContainer);
159
162
  this.currentComponent = null;
160
163
  }
161
164
  else {
162
165
  // Simple HTML string/template
163
166
  if (this.rootContainer)
164
- render(componentToRender, this.rootContainer);
167
+ (0, renderer_1.render)(componentToRender, this.rootContainer);
165
168
  this.currentComponent = null; // String templates don't have lifecycle instances
166
169
  }
167
170
  }
168
171
  else {
169
172
  if (this.rootContainer)
170
- render('<div style="text-align:center; padding:50px;"><h1>404</h1><p>Page Not Found</p></div>', this.rootContainer);
173
+ (0, renderer_1.render)('<div style="text-align:center; padding:50px;"><h1>404</h1><p>Page Not Found</p></div>', this.rootContainer);
171
174
  }
172
175
  });
173
176
  }
@@ -207,8 +210,9 @@ export class MuRouter {
207
210
  }
208
211
  }
209
212
  }
213
+ exports.MuRouter = MuRouter;
210
214
  // --- Factory for Easy Usage ---
211
- export const createRouter = (options) => {
215
+ const createRouter = (options) => {
212
216
  // Expose the router instance globally for <mu-link> to access
213
217
  const router = new MuRouter(options.routes, options.rootContainer);
214
218
  if (typeof window !== 'undefined') {
@@ -216,10 +220,13 @@ export const createRouter = (options) => {
216
220
  }
217
221
  return router;
218
222
  };
223
+ exports.createRouter = createRouter;
219
224
  // --- Web Component: <mu-link> ---
220
225
  // Usage: <mu-link to="/about">About Us</mu-link>
226
+ const MuLinkBase = typeof HTMLElement !== 'undefined' ? HTMLElement : class {
227
+ };
221
228
  if (typeof window !== 'undefined' && !customElements.get('mu-link')) {
222
- class MuLink extends HTMLElement {
229
+ class MuLink extends MuLinkBase {
223
230
  constructor() {
224
231
  super();
225
232
  this.addEventListener('click', (e) => {
@@ -1,4 +1,7 @@
1
- export class Security {
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Security = void 0;
4
+ class Security {
2
5
  /**
3
6
  * IRON FORTRESS PROTOCOL
4
7
  * Strictly escapes all HTML entities to prevent XSS.
@@ -50,3 +53,4 @@ export class Security {
50
53
  });
51
54
  }
52
55
  }
56
+ exports.Security = Security;
@@ -1,5 +1,8 @@
1
- import { reactive, effect } from '../core/reactive';
2
- export class MuStore {
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MuStore = void 0;
4
+ const reactive_1 = require("../core/reactive");
5
+ class MuStore {
3
6
  constructor(initialState, options) {
4
7
  this.subscribers = [];
5
8
  // Load from local storage if persist is true
@@ -16,16 +19,16 @@ export class MuStore {
16
19
  console.error("Failed to load state", e);
17
20
  }
18
21
  // Auto-save effect
19
- effect(() => {
22
+ (0, reactive_1.effect)(() => {
20
23
  localStorage.setItem(key, JSON.stringify(this.state));
21
24
  });
22
25
  }
23
- this.state = reactive(loadedState);
26
+ this.state = (0, reactive_1.reactive)(loadedState);
24
27
  }
25
28
  // Subscribe to changes
26
29
  subscribe(fn) {
27
30
  this.subscribers.push(fn);
28
- effect(() => {
31
+ (0, reactive_1.effect)(() => {
29
32
  fn(this.state);
30
33
  });
31
34
  }
@@ -40,3 +43,4 @@ export class MuStore {
40
43
  }
41
44
  }
42
45
  }
46
+ exports.MuStore = MuStore;
@@ -2,6 +2,7 @@ export type NodeType = 'Element' | 'Text' | 'Interpolation';
2
2
  export type Node = ElementNode | TextNode | InterpolationNode;
3
3
  export interface BaseNode {
4
4
  type: NodeType;
5
+ isStatic?: boolean;
5
6
  }
6
7
  export interface ElementNode extends BaseNode {
7
8
  type: 'Element';
@@ -33,6 +34,7 @@ export declare function decodeEntities(str: string): string;
33
34
  * Handles HTML tags, {{ }} interpolation, and is robust against raw < symbols in text.
34
35
  */
35
36
  export declare function parse(template: string, errors: string[]): ElementNode;
37
+ export declare function markStatic(node: Node): boolean;
36
38
  export declare function parseTag(content: string): {
37
39
  tag: string;
38
40
  props: Record<string, string>;
@@ -2,6 +2,7 @@ export type NodeType = 'Element' | 'Text' | 'Interpolation';
2
2
  export type Node = ElementNode | TextNode | InterpolationNode;
3
3
  export interface BaseNode {
4
4
  type: NodeType;
5
+ isStatic?: boolean;
5
6
  }
6
7
  export interface ElementNode extends BaseNode {
7
8
  type: 'Element';
@@ -33,6 +34,7 @@ export declare function decodeEntities(str: string): string;
33
34
  * Handles HTML tags, {{ }} interpolation, and is robust against raw < symbols in text.
34
35
  */
35
36
  export declare function parse(template: string, errors: string[]): ElementNode;
37
+ export declare function markStatic(node: Node): boolean;
36
38
  export declare function parseTag(content: string): {
37
39
  tag: string;
38
40
  props: Record<string, string>;
@@ -6,3 +6,4 @@ export interface CompileResult {
6
6
  map?: string;
7
7
  }
8
8
  export declare function compileSFC(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult>;
9
+ export declare function compileSFCForSSR(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult>;
@@ -0,0 +1,7 @@
1
+ import { SFCDescriptor } from './sfc-parser';
2
+ import { ScriptCompileResult } from './script-compiler';
3
+ export interface SSRCompileResult {
4
+ code: string;
5
+ errors: string[];
6
+ }
7
+ export declare function compileToSSR(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): SSRCompileResult;
@@ -6,3 +6,4 @@ export interface CompileResult {
6
6
  map?: string;
7
7
  }
8
8
  export declare function compileSFC(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult>;
9
+ export declare function compileSFCForSSR(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult>;
@@ -1,4 +1,5 @@
1
- export declare class MuBlochSphereElement extends HTMLElement {
1
+ declare const MuBlochBase: any;
2
+ export declare class MuBlochSphereElement extends MuBlochBase {
2
3
  private _qubit;
3
4
  private _arrow;
4
5
  private _container;
@@ -14,3 +15,4 @@ export declare class MuBlochSphereElement extends HTMLElement {
14
15
  updateArrow(theta: number, phi: number): void;
15
16
  render(): void;
16
17
  }
18
+ export {};
@@ -1,4 +1,5 @@
1
- export declare class MuInfinity extends HTMLElement {
1
+ declare const MuInfinityBase: any;
2
+ export declare class MuInfinity extends MuInfinityBase {
2
3
  private _items;
3
4
  private _itemHeight;
4
5
  private _template;
@@ -15,3 +16,4 @@ export declare class MuInfinity extends HTMLElement {
15
16
  private onScroll;
16
17
  private renderVisible;
17
18
  }
19
+ export {};
@@ -11,6 +11,8 @@ export declare abstract class MuComponent {
11
11
  _effects: Array<() => void>;
12
12
  _domEffects: Array<() => void>;
13
13
  _isDestroyed: boolean;
14
+ _isResuming: boolean;
15
+ _resumeRoot: HTMLElement | null;
14
16
  constructor(container: HTMLElement);
15
17
  setup(): void;
16
18
  abstract template(): string | HTMLElement | DocumentFragment;
@@ -23,6 +25,11 @@ export declare abstract class MuComponent {
23
25
  _e(id: string, type: string, handler: Function): string;
24
26
  bind(handler: Function, type?: string): string;
25
27
  mount(): void;
28
+ /**
29
+ * Internal method to execute the resumability pass.
30
+ * Overridden by compiler-generated code.
31
+ */
32
+ _resume(): void;
26
33
  _bindEffect(fn: () => void, targetNode?: Node): () => void;
27
34
  update(): void;
28
35
  flushProps(): void;
@@ -72,6 +79,8 @@ export declare function defineComponent(optionsOrSetup: any): {
72
79
  _effects: (() => void)[];
73
80
  _domEffects: (() => void)[];
74
81
  _isDestroyed: boolean;
82
+ _isResuming: boolean;
83
+ _resumeRoot: HTMLElement | null;
75
84
  onMount(): void;
76
85
  onUpdate(): void;
77
86
  onDestroy(): void;
@@ -81,6 +90,11 @@ export declare function defineComponent(optionsOrSetup: any): {
81
90
  _e(id: string, type: string, handler: Function): string;
82
91
  bind(handler: Function, type?: string): string;
83
92
  mount(): void;
93
+ /**
94
+ * Internal method to execute the resumability pass.
95
+ * Overridden by compiler-generated code.
96
+ */
97
+ _resume(): void;
84
98
  _bindEffect(fn: () => void, targetNode?: Node | undefined): () => void;
85
99
  update(): void;
86
100
  flushProps(): void;
@@ -1,4 +1,6 @@
1
1
  type Effect = () => void;
2
+ export declare function queueEffect(fn: Effect): void;
3
+ export declare function nextTick(): Promise<void>;
2
4
  export declare class Signal<T> {
3
5
  private _value;
4
6
  private _subscribers;
@@ -7,7 +9,9 @@ export declare class Signal<T> {
7
9
  set value(newValue: T);
8
10
  notify(): void;
9
11
  }
10
- export declare function effect(fn: Effect, targetNode?: Node): () => void;
12
+ export declare function effect(fn: Effect, targetNode?: Node, options?: {
13
+ sync?: boolean;
14
+ }): () => void;
11
15
  /**
12
16
  * Creates a reactive proxy object (Vue-compatible).
13
17
  * Now optimized to respect Mulan Cycle.
@@ -1,4 +1,3 @@
1
1
  export declare function render(template: string | HTMLElement | DocumentFragment, container: HTMLElement): void;
2
2
  export declare function hydrate(template: string | HTMLElement | DocumentFragment, container: HTMLElement): void;
3
- export declare function renderToString(template: string): string;
4
3
  export declare function sanitize(str: string): string;
@@ -0,0 +1,9 @@
1
+ export interface SSRResult {
2
+ html: string;
3
+ state: any;
4
+ envelope: string;
5
+ }
6
+ /**
7
+ * Renders a Mulan component to a static HTML string with a resumability envelope.
8
+ */
9
+ export declare function renderToString(ComponentClass: any, props?: any): SSRResult;
@@ -11,6 +11,7 @@ export * from './core/quantum';
11
11
  export * from './core/surge';
12
12
  export * from './components/bloch-sphere';
13
13
  export * from './components/infinity-list';
14
+ export * from './core/ssr';
14
15
  import { reactive, effect } from './core/reactive';
15
16
  import { MuComponent, defineComponent } from './core/component';
16
17
  import { MuRouter } from './router/index';
@@ -0,0 +1,7 @@
1
+ import { SFCDescriptor } from './sfc-parser';
2
+ import { ScriptCompileResult } from './script-compiler';
3
+ export interface SSRCompileResult {
4
+ code: string;
5
+ errors: string[];
6
+ }
7
+ export declare function compileToSSR(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): SSRCompileResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulanjs/mulanjs",
3
- "version": "1.0.1-dev.20260227135307",
3
+ "version": "1.0.1-dev.20260227172006",
4
4
  "description": "A powerful, secure, and enterprise-grade JavaScript framework.",
5
5
  "main": "dist/mulan.js",
6
6
  "module": "dist/mulan.esm.js",
@@ -7,6 +7,7 @@ export type Node = ElementNode | TextNode | InterpolationNode;
7
7
 
8
8
  export interface BaseNode {
9
9
  type: NodeType;
10
+ isStatic?: boolean;
10
11
  }
11
12
 
12
13
  export interface ElementNode extends BaseNode {
@@ -96,14 +97,9 @@ export function parse(template: string, errors: string[]): ElementNode {
96
97
  break;
97
98
  }
98
99
 
99
- let tagContent = template.slice(cursor + 1, end);
100
- const isSelfClosing = tagContent.endsWith('/') || ['img', 'br', 'input', 'hr', 'link', 'meta'].includes(tagContent.split(' ')[0].toLowerCase());
101
-
102
- if (tagContent.endsWith('/')) {
103
- tagContent = tagContent.slice(0, -1).trim();
104
- }
105
-
100
+ const tagContent = template.slice(cursor + 1, end);
106
101
  const { tag, props, directives } = parseTag(tagContent);
102
+ const isSelfClosing = tagContent.endsWith('/') || ['img', 'br', 'input', 'hr', 'link', 'meta'].includes(tag.toLowerCase());
107
103
 
108
104
  const element: ElementNode = { type: 'Element', tag, props, children: [], directives };
109
105
  stack[stack.length - 1].children.push(element);
@@ -183,16 +179,72 @@ export function parse(template: string, errors: string[]): ElementNode {
183
179
  }
184
180
  }
185
181
 
182
+ markStatic(root);
186
183
  return root;
187
184
  }
188
185
 
186
+ export function markStatic(node: Node): boolean {
187
+ if (node.type === 'Interpolation') {
188
+ node.isStatic = false;
189
+ return false;
190
+ }
191
+
192
+ if (node.type === 'Text') {
193
+ const text = node as TextNode;
194
+ node.isStatic = !text.content.includes('${');
195
+ return node.isStatic;
196
+ }
197
+
198
+ if (node.type === 'Element') {
199
+ const element = node as ElementNode;
200
+ let isStatic = true;
201
+
202
+ // 1. Directives make it dynamic
203
+ if (element.directives.vIf || element.directives.vFor) {
204
+ isStatic = false;
205
+ }
206
+
207
+ // 2. Dynamic properties or event listeners make it dynamic
208
+ if (isStatic) {
209
+ for (const key in element.props) {
210
+ const val = element.props[key];
211
+ if (key.startsWith('@') || key.startsWith('v-on:') || key.startsWith('on') || key.startsWith(':') || key.startsWith('.')) {
212
+ isStatic = false;
213
+ break;
214
+ }
215
+ if (val.includes('${')) {
216
+ isStatic = false;
217
+ break;
218
+ }
219
+ }
220
+ }
221
+
222
+ // 3. Check all children. Every child must be static for the parent to be static.
223
+ // We still need to call markStatic on ALL children, so don't short-circuit the loop early.
224
+ for (const child of element.children) {
225
+ const childStatic = markStatic(child);
226
+ if (!childStatic) {
227
+ isStatic = false;
228
+ }
229
+ }
230
+
231
+ element.isStatic = isStatic;
232
+ return isStatic;
233
+ }
234
+
235
+ return false;
236
+ }
237
+
189
238
  export function parseTag(content: string) {
190
- const parts = content.split(' ');
191
- const tag = parts[0];
239
+ // 1. Extract tag name (first word, ignoring leading whitespace)
240
+ const tagMatch = content.trim().match(/^([a-zA-Z0-9:-]+)/);
241
+ const tag = tagMatch ? tagMatch[1] : '';
242
+
192
243
  const props: Record<string, string> = {};
193
244
  const directives: ElementNode['directives'] = {};
194
245
 
195
- const attrStr = content.slice(tag.length).trim();
246
+ // 2. Remove tag name from content to parse attributes
247
+ const attrStr = content.trim().slice(tag.length).trim();
196
248
 
197
249
  let i = 0;
198
250
  while (i < attrStr.length) {
@@ -4,6 +4,7 @@ import { compileScript, CompilerOptions } from './script-compiler';
4
4
  import { compileTemplate } from './template-compiler';
5
5
  import { compileToDOM } from './dom-compiler';
6
6
  import { compileStyle } from './style-compiler';
7
+ import { compileToSSR } from './ssr-compiler';
7
8
 
8
9
  export interface CompileResult {
9
10
  code: string;
@@ -26,6 +27,9 @@ export async function compileSFC(source: string, filename: string, options?: Com
26
27
  if (!scriptCode.includes('const __component__ =')) {
27
28
  scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
28
29
  }
30
+ if (!scriptCode.includes('const __component__ =')) {
31
+ scriptCode = scriptCode.replace('exports.default =', 'const __component__ =');
32
+ }
29
33
 
30
34
  // 3. Style
31
35
  const styleResult = compileStyle(descriptor, filename, options);
@@ -173,7 +177,7 @@ if (typeof module !== 'undefined' && module.hot) {
173
177
  }
174
178
 
175
179
  // console.log('[MulanJS] Component created:', __component__);
176
- export default __component__;
180
+ module.exports = __component__;
177
181
 
178
182
  // Helper to get relative path for sourceURL
179
183
  // This ensures the generated file appears in a readable folder structure in DevTools
@@ -187,3 +191,44 @@ export default __component__;
187
191
  map: mergedMap || scriptResult.map // Fallback to script map if merge fails
188
192
  };
189
193
  }
194
+
195
+ // --- Mulan Server Components (MSC) ---
196
+ export async function compileSFCForSSR(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult> {
197
+ const descriptor = parseMUJS(source, filename);
198
+ const scriptResult = await compileScript(descriptor, options);
199
+
200
+ let scriptCode = scriptResult.code;
201
+
202
+ // Robust replacement for various export styles
203
+ if (scriptCode.includes('export default')) {
204
+ scriptCode = scriptCode.replace('export default', 'const __component__ =');
205
+ } else if (scriptCode.includes('exports.default =')) {
206
+ scriptCode = scriptCode.replace('exports.default =', 'const __component__ =');
207
+ } else if (scriptCode.includes('module.exports =')) {
208
+ scriptCode = scriptCode.replace('module.exports =', 'const __component__ =');
209
+ } else {
210
+ // Fallback: search for defineComponent if it's there
211
+ scriptCode += `\nconst __component__ = typeof exports !== 'undefined' ? (exports.default || module.exports) : {};`;
212
+ }
213
+
214
+ const templateResult = compileToSSR(descriptor, scriptResult, undefined);
215
+
216
+ const finalCode = `${scriptCode}
217
+
218
+ ${templateResult.code}
219
+
220
+ if (__component__.prototype) {
221
+ __component__.prototype.renderToString = render;
222
+ } else if (typeof __component__ === 'object') {
223
+ __component__.renderToString = render;
224
+ }
225
+
226
+ module.exports = __component__;
227
+ `;
228
+
229
+ return {
230
+ code: finalCode,
231
+ css: '', // CSS handling for SSR can be extracted separately
232
+ errors: [...scriptResult.errors, ...templateResult.errors],
233
+ };
234
+ }