@mulanjs/mulanjs 1.0.1-dev.20260227091247 → 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 (54) hide show
  1. package/dist/compiler/ast-parser.d.ts +48 -0
  2. package/dist/compiler/ast-parser.js +266 -0
  3. package/dist/compiler/compiler.d.ts +9 -0
  4. package/dist/compiler/compiler.js +46 -3
  5. package/dist/compiler/dom-compiler.d.ts +7 -0
  6. package/dist/compiler/dom-compiler.js +95 -159
  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/compiler/template-compiler.js +11 -238
  15. package/dist/components/bloch-sphere.js +9 -3
  16. package/dist/components/infinity-list.js +7 -1
  17. package/dist/core/component.js +66 -15
  18. package/dist/core/hooks.js +53 -29
  19. package/dist/core/quantum.js +30 -17
  20. package/dist/core/query.js +11 -6
  21. package/dist/core/reactive.js +88 -7
  22. package/dist/core/renderer.js +9 -8
  23. package/dist/core/ssr.js +50 -0
  24. package/dist/core/surge.js +7 -2
  25. package/dist/core/vault.js +9 -5
  26. package/dist/index.js +63 -27
  27. package/dist/mulan.esm.js +1933 -1626
  28. package/dist/mulan.esm.js.map +1 -1
  29. package/dist/mulan.js +1890 -1590
  30. package/dist/mulan.js.map +1 -1
  31. package/dist/router/index.js +17 -10
  32. package/dist/security/sanitizer.js +5 -1
  33. package/dist/store/index.js +9 -5
  34. package/dist/types/ast-parser.d.ts +48 -0
  35. package/dist/types/compiler/ast-parser.d.ts +48 -0
  36. package/dist/types/compiler/compiler.d.ts +1 -0
  37. package/dist/types/compiler/ssr-compiler.d.ts +7 -0
  38. package/dist/types/compiler.d.ts +1 -0
  39. package/dist/types/components/bloch-sphere.d.ts +3 -1
  40. package/dist/types/components/infinity-list.d.ts +3 -1
  41. package/dist/types/core/component.d.ts +14 -0
  42. package/dist/types/core/reactive.d.ts +5 -1
  43. package/dist/types/core/renderer.d.ts +0 -1
  44. package/dist/types/core/ssr.d.ts +9 -0
  45. package/dist/types/index.d.ts +1 -0
  46. package/dist/types/ssr-compiler.d.ts +7 -0
  47. package/package.json +1 -1
  48. package/src/compiler/ast-parser.ts +310 -0
  49. package/src/compiler/compiler.ts +47 -2
  50. package/src/compiler/dom-compiler.ts +105 -167
  51. package/src/compiler/script-compiler.ts +117 -126
  52. package/src/compiler/ssr-compiler.ts +157 -0
  53. package/src/compiler/template-compiler.ts +11 -283
  54. package/src/loader/index.js +12 -19
@@ -0,0 +1,48 @@
1
+ export type NodeType = 'Element' | 'Text' | 'Interpolation';
2
+ export type Node = ElementNode | TextNode | InterpolationNode;
3
+ export interface BaseNode {
4
+ type: NodeType;
5
+ isStatic?: boolean;
6
+ }
7
+ export interface ElementNode extends BaseNode {
8
+ type: 'Element';
9
+ tag: string;
10
+ props: Record<string, string>;
11
+ children: Node[];
12
+ directives: {
13
+ vFor?: {
14
+ item: string;
15
+ list: string;
16
+ };
17
+ vIf?: string;
18
+ };
19
+ }
20
+ export interface TextNode extends BaseNode {
21
+ type: 'Text';
22
+ content: string;
23
+ }
24
+ export interface InterpolationNode extends BaseNode {
25
+ type: 'Interpolation';
26
+ content: string;
27
+ }
28
+ /**
29
+ * Decodes standard HTML entities.
30
+ */
31
+ export declare function decodeEntities(str: string): string;
32
+ /**
33
+ * Unified MulanJS Template Parser
34
+ * Handles HTML tags, {{ }} interpolation, and is robust against raw < symbols in text.
35
+ */
36
+ export declare function parse(template: string, errors: string[]): ElementNode;
37
+ export declare function markStatic(node: Node): boolean;
38
+ export declare function parseTag(content: string): {
39
+ tag: string;
40
+ props: Record<string, string>;
41
+ directives: {
42
+ vFor?: {
43
+ item: string;
44
+ list: string;
45
+ };
46
+ vIf?: string;
47
+ };
48
+ };
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ // --- AST Definitions ---
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.parseTag = exports.markStatic = exports.parse = exports.decodeEntities = void 0;
5
+ /**
6
+ * Decodes standard HTML entities.
7
+ */
8
+ function decodeEntities(str) {
9
+ return str
10
+ .replace(/&lt;/g, '<')
11
+ .replace(/&gt;/g, '>')
12
+ .replace(/&amp;/g, '&')
13
+ .replace(/&quot;/g, '"')
14
+ .replace(/&#039;/g, "'")
15
+ .replace(/&nbsp;/g, '\u00A0');
16
+ }
17
+ exports.decodeEntities = decodeEntities;
18
+ /**
19
+ * Unified MulanJS Template Parser
20
+ * Handles HTML tags, {{ }} interpolation, and is robust against raw < symbols in text.
21
+ */
22
+ function parse(template, errors) {
23
+ const root = {
24
+ type: 'Element',
25
+ tag: 'fragment',
26
+ props: {},
27
+ children: [],
28
+ directives: {}
29
+ };
30
+ const stack = [root];
31
+ let cursor = 0;
32
+ while (cursor < template.length) {
33
+ const char = template[cursor];
34
+ // 1. Comments
35
+ if (template.startsWith('<!--', cursor)) {
36
+ const end = template.indexOf('-->', cursor);
37
+ if (end === -1) {
38
+ errors.push('Unclosed HTML comment.');
39
+ break;
40
+ }
41
+ cursor = end + 3;
42
+ }
43
+ // 2. Tags (with lookahead check for robustness)
44
+ else if (char === '<' && /[\/a-zA-Z!]/.test(template[cursor + 1])) {
45
+ if (template[cursor + 1] === '/') {
46
+ // Closing Tag
47
+ const end = template.indexOf('>', cursor);
48
+ if (end === -1) {
49
+ errors.push('Unclosed closing tag.');
50
+ break;
51
+ }
52
+ const tagName = template.slice(cursor + 2, end).trim();
53
+ // Find matching element in stack to pop
54
+ if (stack.length > 1 && stack[stack.length - 1].tag === tagName) {
55
+ stack.pop();
56
+ }
57
+ else {
58
+ errors.push(`Mismatched closing tag </${tagName}>.`);
59
+ }
60
+ cursor = end + 1;
61
+ }
62
+ else {
63
+ // Opening Tag
64
+ const end = template.indexOf('>', cursor);
65
+ if (end === -1) {
66
+ errors.push('Unclosed opening tag.');
67
+ break;
68
+ }
69
+ const tagContent = template.slice(cursor + 1, end);
70
+ const { tag, props, directives } = parseTag(tagContent);
71
+ const isSelfClosing = tagContent.endsWith('/') || ['img', 'br', 'input', 'hr', 'link', 'meta'].includes(tag.toLowerCase());
72
+ const element = { type: 'Element', tag, props, children: [], directives };
73
+ stack[stack.length - 1].children.push(element);
74
+ if (!isSelfClosing) {
75
+ stack.push(element);
76
+ }
77
+ cursor = end + 1;
78
+ }
79
+ }
80
+ // 3. Interpolation {{ }}
81
+ else if (template.startsWith('{{', cursor)) {
82
+ const end = template.indexOf('}}', cursor);
83
+ if (end === -1) {
84
+ errors.push('Unclosed interpolation {{ }}.');
85
+ break;
86
+ }
87
+ const content = template.slice(cursor + 2, end).trim();
88
+ stack[stack.length - 1].children.push({ type: 'Interpolation', content });
89
+ cursor = end + 2;
90
+ }
91
+ // 4. Native Template Literals ${ } (Protection)
92
+ else if (template.startsWith('${', cursor)) {
93
+ // Find end of ${ } while respecting nested braces
94
+ let innerCursor = cursor + 2;
95
+ let depth = 1;
96
+ while (innerCursor < template.length && depth > 0) {
97
+ if (template[innerCursor] === '{')
98
+ depth++;
99
+ if (template[innerCursor] === '}')
100
+ depth--;
101
+ innerCursor++;
102
+ }
103
+ const content = template.slice(cursor, innerCursor);
104
+ // Treat as text but this block is now "atomic" and won't be split by < checks
105
+ const lastChild = stack[stack.length - 1].children[stack[stack.length - 1].children.length - 1];
106
+ if (lastChild && lastChild.type === 'Text') {
107
+ lastChild.content += content;
108
+ }
109
+ else {
110
+ stack[stack.length - 1].children.push({ type: 'Text', content });
111
+ }
112
+ cursor = innerCursor;
113
+ }
114
+ // 5. Text Nodes
115
+ else {
116
+ let nextTag = template.indexOf('<', cursor);
117
+ let nextInterp = template.indexOf('{{', cursor);
118
+ let nextNative = template.indexOf('${', cursor);
119
+ let end = template.length;
120
+ // Heuristic for nextTag: it must look like a tag start
121
+ while (nextTag !== -1) {
122
+ if (/[\/a-zA-Z!]/.test(template[nextTag + 1])) {
123
+ break;
124
+ }
125
+ nextTag = template.indexOf('<', nextTag + 1);
126
+ }
127
+ if (nextTag !== -1 && nextTag < end)
128
+ end = nextTag;
129
+ if (nextInterp !== -1 && nextInterp < end)
130
+ end = nextInterp;
131
+ if (nextNative !== -1 && nextNative < end)
132
+ end = nextNative;
133
+ const rawContent = template.slice(cursor, end);
134
+ const content = decodeEntities(rawContent);
135
+ if (content) {
136
+ const lastChild = stack[stack.length - 1].children[stack[stack.length - 1].children.length - 1];
137
+ if (lastChild && lastChild.type === 'Text') {
138
+ lastChild.content += content;
139
+ }
140
+ else {
141
+ stack[stack.length - 1].children.push({ type: 'Text', content });
142
+ }
143
+ }
144
+ cursor = end;
145
+ }
146
+ }
147
+ markStatic(root);
148
+ return root;
149
+ }
150
+ exports.parse = parse;
151
+ function markStatic(node) {
152
+ if (node.type === 'Interpolation') {
153
+ node.isStatic = false;
154
+ return false;
155
+ }
156
+ if (node.type === 'Text') {
157
+ const text = node;
158
+ node.isStatic = !text.content.includes('${');
159
+ return node.isStatic;
160
+ }
161
+ if (node.type === 'Element') {
162
+ const element = node;
163
+ let isStatic = true;
164
+ // 1. Directives make it dynamic
165
+ if (element.directives.vIf || element.directives.vFor) {
166
+ isStatic = false;
167
+ }
168
+ // 2. Dynamic properties or event listeners make it dynamic
169
+ if (isStatic) {
170
+ for (const key in element.props) {
171
+ const val = element.props[key];
172
+ if (key.startsWith('@') || key.startsWith('v-on:') || key.startsWith('on') || key.startsWith(':') || key.startsWith('.')) {
173
+ isStatic = false;
174
+ break;
175
+ }
176
+ if (val.includes('${')) {
177
+ isStatic = false;
178
+ break;
179
+ }
180
+ }
181
+ }
182
+ // 3. Check all children. Every child must be static for the parent to be static.
183
+ // We still need to call markStatic on ALL children, so don't short-circuit the loop early.
184
+ for (const child of element.children) {
185
+ const childStatic = markStatic(child);
186
+ if (!childStatic) {
187
+ isStatic = false;
188
+ }
189
+ }
190
+ element.isStatic = isStatic;
191
+ return isStatic;
192
+ }
193
+ return false;
194
+ }
195
+ exports.markStatic = markStatic;
196
+ function parseTag(content) {
197
+ // 1. Extract tag name (first word, ignoring leading whitespace)
198
+ const tagMatch = content.trim().match(/^([a-zA-Z0-9:-]+)/);
199
+ const tag = tagMatch ? tagMatch[1] : '';
200
+ const props = {};
201
+ const directives = {};
202
+ // 2. Remove tag name from content to parse attributes
203
+ const attrStr = content.trim().slice(tag.length).trim();
204
+ let i = 0;
205
+ while (i < attrStr.length) {
206
+ if (/\s/.test(attrStr[i])) {
207
+ i++;
208
+ continue;
209
+ }
210
+ const keyStart = i;
211
+ while (i < attrStr.length && !/\s|=/.test(attrStr[i])) {
212
+ i++;
213
+ }
214
+ const key = attrStr.slice(keyStart, i);
215
+ let value = 'true';
216
+ let peek = i;
217
+ while (peek < attrStr.length && /\s/.test(attrStr[peek]))
218
+ peek++;
219
+ if (peek < attrStr.length && attrStr[peek] === '=') {
220
+ i = peek + 1;
221
+ while (i < attrStr.length && /\s/.test(attrStr[i]))
222
+ i++;
223
+ if (i < attrStr.length && (attrStr[i] === '"' || attrStr[i] === "'")) {
224
+ const quote = attrStr[i];
225
+ i++;
226
+ const valStart = i;
227
+ while (i < attrStr.length && attrStr[i] !== quote) {
228
+ if (attrStr[i] === '\\' && attrStr[i + 1] === quote) {
229
+ i += 2;
230
+ }
231
+ else {
232
+ i++;
233
+ }
234
+ }
235
+ value = attrStr.slice(valStart, i);
236
+ i++;
237
+ }
238
+ else {
239
+ const valStart = i;
240
+ while (i < attrStr.length && !/\s/.test(attrStr[i])) {
241
+ i++;
242
+ }
243
+ value = attrStr.slice(valStart, i);
244
+ }
245
+ }
246
+ if (key === 'v-if' || key === 'mu-if') {
247
+ directives.vIf = value;
248
+ }
249
+ else if (key === 'v-for' || key === 'mu-for') {
250
+ const parts = value.split(' in ');
251
+ if (parts.length < 2) {
252
+ directives.vFor = { item: '_item', list: '[]' };
253
+ }
254
+ else {
255
+ const item = parts[0];
256
+ const list = parts.slice(1).join(' in ');
257
+ directives.vFor = { item: item.trim(), list: list.trim() };
258
+ }
259
+ }
260
+ else if (key) {
261
+ props[key] = value;
262
+ }
263
+ }
264
+ return { tag, props, directives };
265
+ }
266
+ exports.parseTag = parseTag;
@@ -0,0 +1,9 @@
1
+ import { CompilerOptions } from './script-compiler';
2
+ export interface CompileResult {
3
+ code: string;
4
+ css: string;
5
+ errors: string[];
6
+ map?: string;
7
+ }
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,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compileSFC = void 0;
3
+ exports.compileSFCForSSR = exports.compileSFC = void 0;
4
4
  const sfc_parser_1 = require("./sfc-parser");
5
5
  const script_compiler_1 = require("./script-compiler");
6
6
  const dom_compiler_1 = require("./dom-compiler");
7
7
  const style_compiler_1 = require("./style-compiler");
8
+ const ssr_compiler_1 = require("./ssr-compiler");
8
9
  const source_map_1 = require("source-map");
9
10
  async function compileSFC(source, filename, options) {
10
11
  // 1. Parse
@@ -17,9 +18,12 @@ async function compileSFC(source, filename, options) {
17
18
  if (!scriptCode.includes('const __component__ =')) {
18
19
  scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
19
20
  }
21
+ if (!scriptCode.includes('const __component__ =')) {
22
+ scriptCode = scriptCode.replace('exports.default =', 'const __component__ =');
23
+ }
20
24
  // 3. Style
21
25
  const styleResult = (0, style_compiler_1.compileStyle)(descriptor, filename, options);
22
- // 4. Template (Use the new No-VDOM compiler!)
26
+ // 4. Template (Use the new unified No-VDOM compiler!)
23
27
  const templateResult = (0, dom_compiler_1.compileToDOM)(descriptor, scriptResult, styleResult.scopedId);
24
28
  // Calculate offsets for source map merging
25
29
  // 1. Script Code
@@ -153,7 +157,7 @@ if (typeof module !== 'undefined' && module.hot) {
153
157
  }
154
158
 
155
159
  // console.log('[MulanJS] Component created:', __component__);
156
- export default __component__;
160
+ module.exports = __component__;
157
161
 
158
162
  // Helper to get relative path for sourceURL
159
163
  // This ensures the generated file appears in a readable folder structure in DevTools
@@ -167,3 +171,42 @@ export default __component__;
167
171
  };
168
172
  }
169
173
  exports.compileSFC = compileSFC;
174
+ // --- Mulan Server Components (MSC) ---
175
+ async function compileSFCForSSR(source, filename, options) {
176
+ const descriptor = (0, sfc_parser_1.parseMUJS)(source, filename);
177
+ const scriptResult = await (0, script_compiler_1.compileScript)(descriptor, options);
178
+ let scriptCode = scriptResult.code;
179
+ // Robust replacement for various export styles
180
+ if (scriptCode.includes('export default')) {
181
+ scriptCode = scriptCode.replace('export default', 'const __component__ =');
182
+ }
183
+ else if (scriptCode.includes('exports.default =')) {
184
+ scriptCode = scriptCode.replace('exports.default =', 'const __component__ =');
185
+ }
186
+ else if (scriptCode.includes('module.exports =')) {
187
+ scriptCode = scriptCode.replace('module.exports =', 'const __component__ =');
188
+ }
189
+ else {
190
+ // Fallback: search for defineComponent if it's there
191
+ scriptCode += `\nconst __component__ = typeof exports !== 'undefined' ? (exports.default || module.exports) : {};`;
192
+ }
193
+ const templateResult = (0, ssr_compiler_1.compileToSSR)(descriptor, scriptResult, undefined);
194
+ const finalCode = `${scriptCode}
195
+
196
+ ${templateResult.code}
197
+
198
+ if (__component__.prototype) {
199
+ __component__.prototype.renderToString = render;
200
+ } else if (typeof __component__ === 'object') {
201
+ __component__.renderToString = render;
202
+ }
203
+
204
+ module.exports = __component__;
205
+ `;
206
+ return {
207
+ code: finalCode,
208
+ css: '', // CSS handling for SSR can be extracted separately
209
+ errors: [...scriptResult.errors, ...templateResult.errors],
210
+ };
211
+ }
212
+ exports.compileSFCForSSR = compileSFCForSSR;
@@ -0,0 +1,7 @@
1
+ import { SFCDescriptor } from './sfc-parser';
2
+ import { ScriptCompileResult } from './script-compiler';
3
+ export interface DOMCompileResult {
4
+ code: string;
5
+ errors: string[];
6
+ }
7
+ export declare function compileToDOM(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): DOMCompileResult;