@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.
- package/dist/compiler/ast-parser.d.ts +48 -0
- package/dist/compiler/ast-parser.js +54 -9
- package/dist/compiler/compiler.d.ts +9 -0
- package/dist/compiler/compiler.js +45 -2
- package/dist/compiler/dom-compiler.d.ts +7 -0
- package/dist/compiler/dom-compiler.js +90 -33
- package/dist/compiler/script-compiler.d.ts +11 -0
- package/dist/compiler/script-compiler.js +108 -116
- package/dist/compiler/sfc-parser.d.ts +21 -0
- package/dist/compiler/ssr-compiler.d.ts +7 -0
- package/dist/compiler/ssr-compiler.js +142 -0
- package/dist/compiler/style-compiler.d.ts +8 -0
- package/dist/compiler/template-compiler.d.ts +8 -0
- package/dist/components/bloch-sphere.js +9 -3
- package/dist/components/infinity-list.js +7 -1
- package/dist/core/component.js +66 -15
- package/dist/core/hooks.js +53 -29
- package/dist/core/quantum.js +30 -17
- package/dist/core/query.js +11 -6
- package/dist/core/reactive.js +88 -7
- package/dist/core/renderer.js +9 -8
- package/dist/core/ssr.js +50 -0
- package/dist/core/surge.js +7 -2
- package/dist/core/vault.js +9 -5
- package/dist/index.js +63 -27
- package/dist/mulan.esm.js +1933 -1626
- package/dist/mulan.esm.js.map +1 -1
- package/dist/mulan.js +1890 -1590
- package/dist/mulan.js.map +1 -1
- package/dist/router/index.js +17 -10
- package/dist/security/sanitizer.js +5 -1
- package/dist/store/index.js +9 -5
- package/dist/types/ast-parser.d.ts +2 -0
- package/dist/types/compiler/ast-parser.d.ts +2 -0
- package/dist/types/compiler/compiler.d.ts +1 -0
- package/dist/types/compiler/ssr-compiler.d.ts +7 -0
- package/dist/types/compiler.d.ts +1 -0
- package/dist/types/components/bloch-sphere.d.ts +3 -1
- package/dist/types/components/infinity-list.d.ts +3 -1
- package/dist/types/core/component.d.ts +14 -0
- package/dist/types/core/reactive.d.ts +5 -1
- package/dist/types/core/renderer.d.ts +0 -1
- package/dist/types/core/ssr.d.ts +9 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/ssr-compiler.d.ts +7 -0
- package/package.json +1 -1
- package/src/compiler/ast-parser.ts +62 -10
- package/src/compiler/compiler.ts +46 -1
- package/src/compiler/dom-compiler.ts +100 -34
- package/src/compiler/script-compiler.ts +117 -126
- package/src/compiler/ssr-compiler.ts +157 -0
- package/src/loader/index.js +12 -19
package/dist/router/index.js
CHANGED
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
|
229
|
+
class MuLink extends MuLinkBase {
|
|
223
230
|
constructor() {
|
|
224
231
|
super();
|
|
225
232
|
this.addEventListener('click', (e) => {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
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;
|
package/dist/store/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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;
|
package/dist/types/compiler.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
@@ -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
|
-
|
|
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
|
-
|
|
191
|
-
const
|
|
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
|
-
|
|
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) {
|
package/src/compiler/compiler.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|