@mulanjs/mulanjs 1.0.1-dev.20260227135307 → 1.0.1-dev.20260227173253
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 +187 -19
- 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
|
@@ -175,118 +175,112 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
175
175
|
});
|
|
176
176
|
`;
|
|
177
177
|
const finalCode = preamble + scriptBody + postamble;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
else {
|
|
226
|
-
relativePath = 'webpack:///' + path.basename(externalPath);
|
|
227
|
-
}
|
|
178
|
+
// Standardize to CommonJS for SSR/Node compatibility
|
|
179
|
+
// Transpile extracted code to JS (handles TS and ESM -> CJS)
|
|
180
|
+
const result = ts.transpileModule(finalCode, {
|
|
181
|
+
compilerOptions: {
|
|
182
|
+
module: ts.ModuleKind.CommonJS,
|
|
183
|
+
target: ts.ScriptTarget.ES2019,
|
|
184
|
+
sourceMap: true,
|
|
185
|
+
inlineSources: true,
|
|
186
|
+
sourceRoot: '/',
|
|
187
|
+
},
|
|
188
|
+
fileName: filename
|
|
189
|
+
});
|
|
190
|
+
const codeWithoutMapComment = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
|
|
191
|
+
let finalMap = result.sourceMapText;
|
|
192
|
+
if (isTs && finalMap) {
|
|
193
|
+
// ... (source map logic)
|
|
194
|
+
}
|
|
195
|
+
if (finalMap) {
|
|
196
|
+
try {
|
|
197
|
+
// Determine offsets
|
|
198
|
+
const preambleLines = preamble.split('\n').length - 1;
|
|
199
|
+
// scriptBody starts at line `preambleLines` (0-indexed) in finalCode.
|
|
200
|
+
// Determine where script starts
|
|
201
|
+
// If External: Start at 0
|
|
202
|
+
// If Inline: Start at script.start line
|
|
203
|
+
let linesBeforeScript = 0;
|
|
204
|
+
let originalSource = content;
|
|
205
|
+
if (!isExternal) {
|
|
206
|
+
originalSource = descriptor.source;
|
|
207
|
+
linesBeforeScript = originalSource.substring(0, script.start).split('\n').length - 1;
|
|
208
|
+
}
|
|
209
|
+
const consumer = new source_map_1.SourceMapConsumer(JSON.parse(finalMap));
|
|
210
|
+
const generator = new source_map_1.SourceMapGenerator({
|
|
211
|
+
file: filename,
|
|
212
|
+
sourceRoot: ''
|
|
213
|
+
});
|
|
214
|
+
// Determine relative path for source
|
|
215
|
+
let relativePath = filename.split(/[/\\]/).pop() || 'unknown.mujs'; // Default to basename
|
|
216
|
+
if (isExternal) {
|
|
217
|
+
// For external files, we want Source Map to point to the actual TS file
|
|
218
|
+
// externalPath is absolute. Make it relative to project root or something reasonable?
|
|
219
|
+
// Webpack often likes 'webpack:///[resource-path]'
|
|
220
|
+
// We'll try to map it so it looks like a separate file
|
|
221
|
+
const normalized = externalPath.replace(/\\/g, '/');
|
|
222
|
+
const srcIdx = normalized.indexOf('/src/');
|
|
223
|
+
if (srcIdx !== -1) {
|
|
224
|
+
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1); // e.g. webpack:///src/logic.ts
|
|
228
225
|
}
|
|
229
226
|
else {
|
|
230
|
-
|
|
231
|
-
const normalized = filename.replace(/\\/g, '/');
|
|
232
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
233
|
-
if (srcIdx !== -1) {
|
|
234
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1); // e.g. webpack:///src/App.mujs
|
|
235
|
-
}
|
|
227
|
+
relativePath = 'webpack:///' + path.basename(externalPath);
|
|
236
228
|
}
|
|
237
|
-
// If inline, we set source content of .mujs (the whole file)
|
|
238
|
-
// If external, we set source content of the .ts file
|
|
239
|
-
generator.setSourceContent(relativePath, originalSource);
|
|
240
|
-
consumer.eachMapping((m) => {
|
|
241
|
-
if (m.originalLine === null)
|
|
242
|
-
return;
|
|
243
|
-
// m.originalLine is the line in `finalCode` (1-based because SourceMapConsumer uses 1-based)
|
|
244
|
-
// We need to convert it to line in `.mujs`.
|
|
245
|
-
// Check if this line belongs to scriptBody
|
|
246
|
-
// scriptBody spans from (preambleLines + 1) to (preambleLines + scriptBodyLines)
|
|
247
|
-
const lineInFinalCode = m.originalLine;
|
|
248
|
-
// Check if strictly inside body
|
|
249
|
-
if (lineInFinalCode > preambleLines) {
|
|
250
|
-
// Calculate offset inside scriptBody
|
|
251
|
-
const lineOffsetInBody = lineInFinalCode - preambleLines; // 1-based offset (1st line of body is 1)
|
|
252
|
-
// Calculate original line
|
|
253
|
-
// If External: lineOffsetInBody is the line number
|
|
254
|
-
// If Inline: linesBeforeScript + lineOffsetInBody
|
|
255
|
-
const originalLine = linesBeforeScript + lineOffsetInBody;
|
|
256
|
-
// Add mapping
|
|
257
|
-
generator.addMapping({
|
|
258
|
-
generated: {
|
|
259
|
-
line: m.generatedLine,
|
|
260
|
-
column: m.generatedColumn
|
|
261
|
-
},
|
|
262
|
-
original: {
|
|
263
|
-
line: originalLine,
|
|
264
|
-
column: m.originalColumn // Column should be preserved if we didn't change indentation
|
|
265
|
-
},
|
|
266
|
-
source: relativePath,
|
|
267
|
-
name: m.name
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
finalMap = generator.toString();
|
|
272
|
-
// consumer.destroy() not needed in 0.6.x
|
|
273
229
|
}
|
|
274
|
-
|
|
275
|
-
|
|
230
|
+
else {
|
|
231
|
+
// Inline: Point to .mujs file
|
|
232
|
+
const normalized = filename.replace(/\\/g, '/');
|
|
233
|
+
const srcIdx = normalized.indexOf('/src/');
|
|
234
|
+
if (srcIdx !== -1) {
|
|
235
|
+
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1); // e.g. webpack:///src/App.mujs
|
|
236
|
+
}
|
|
276
237
|
}
|
|
238
|
+
// If inline, we set source content of .mujs (the whole file)
|
|
239
|
+
// If external, we set source content of the .ts file
|
|
240
|
+
generator.setSourceContent(relativePath, originalSource);
|
|
241
|
+
consumer.eachMapping((m) => {
|
|
242
|
+
if (m.originalLine === null)
|
|
243
|
+
return;
|
|
244
|
+
// m.originalLine is the line in `finalCode` (1-based because SourceMapConsumer uses 1-based)
|
|
245
|
+
// We need to convert it to line in `.mujs`.
|
|
246
|
+
// Check if this line belongs to scriptBody
|
|
247
|
+
// scriptBody spans from (preambleLines + 1) to (preambleLines + scriptBodyLines)
|
|
248
|
+
const lineInFinalCode = m.originalLine;
|
|
249
|
+
// Check if strictly inside body
|
|
250
|
+
if (lineInFinalCode > preambleLines) {
|
|
251
|
+
// Calculate offset inside scriptBody
|
|
252
|
+
const lineOffsetInBody = lineInFinalCode - preambleLines; // 1-based offset (1st line of body is 1)
|
|
253
|
+
// Calculate original line
|
|
254
|
+
// If External: lineOffsetInBody is the line number
|
|
255
|
+
// If Inline: linesBeforeScript + lineOffsetInBody
|
|
256
|
+
const originalLine = linesBeforeScript + lineOffsetInBody;
|
|
257
|
+
// Add mapping
|
|
258
|
+
generator.addMapping({
|
|
259
|
+
generated: {
|
|
260
|
+
line: m.generatedLine,
|
|
261
|
+
column: m.generatedColumn
|
|
262
|
+
},
|
|
263
|
+
original: {
|
|
264
|
+
line: originalLine,
|
|
265
|
+
column: m.originalColumn // Column should be preserved if we didn't change indentation
|
|
266
|
+
},
|
|
267
|
+
source: relativePath,
|
|
268
|
+
name: m.name
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
finalMap = generator.toString();
|
|
273
|
+
// consumer.destroy() not needed in 0.6.x
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
console.warn('[MulanJS] Failed to patch source map:', e);
|
|
277
277
|
}
|
|
278
|
-
return {
|
|
279
|
-
code: codeWithoutMapComment,
|
|
280
|
-
bindings,
|
|
281
|
-
errors: [],
|
|
282
|
-
map: finalMap // Return the enhanced map
|
|
283
|
-
};
|
|
284
278
|
}
|
|
285
279
|
return {
|
|
286
|
-
code:
|
|
280
|
+
code: codeWithoutMapComment,
|
|
287
281
|
bindings,
|
|
288
282
|
errors: [],
|
|
289
|
-
map:
|
|
283
|
+
map: finalMap // Return the enhanced map
|
|
290
284
|
};
|
|
291
285
|
}
|
|
292
286
|
async function compileStandardScript(script, descriptor, content, filename, isExternal, externalPath) {
|
|
@@ -294,20 +288,18 @@ async function compileStandardScript(script, descriptor, content, filename, isEx
|
|
|
294
288
|
// and/or just to map it back to .mujs
|
|
295
289
|
let finalMap;
|
|
296
290
|
let code = content;
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
finalMap = result.sourceMapText;
|
|
310
|
-
}
|
|
291
|
+
// Standardize to CommonJS for SSR/Node compatibility
|
|
292
|
+
const result = ts.transpileModule(content, {
|
|
293
|
+
compilerOptions: {
|
|
294
|
+
module: ts.ModuleKind.CommonJS,
|
|
295
|
+
target: ts.ScriptTarget.ES2019,
|
|
296
|
+
sourceMap: true,
|
|
297
|
+
inlineSources: true,
|
|
298
|
+
},
|
|
299
|
+
fileName: filename
|
|
300
|
+
});
|
|
301
|
+
code = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
|
|
302
|
+
finalMap = result.sourceMapText;
|
|
311
303
|
// Adjust Source Map
|
|
312
304
|
if (finalMap) {
|
|
313
305
|
try {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface SFCBlock {
|
|
2
|
+
type: 'script' | 'template' | 'style';
|
|
3
|
+
content: string;
|
|
4
|
+
attrs: Record<string, string>;
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SFCDescriptor {
|
|
9
|
+
filename: string;
|
|
10
|
+
source: string;
|
|
11
|
+
template: SFCBlock | null;
|
|
12
|
+
script: SFCBlock | null;
|
|
13
|
+
scripts: SFCBlock[];
|
|
14
|
+
styles: SFCBlock[];
|
|
15
|
+
customBlocks: SFCBlock[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Standardized State-Machine Parser for MulanJS
|
|
19
|
+
* Parses .mujs files safely, handling attributes, quotes, and nested content.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseMUJS(source: string, filename: string): SFCDescriptor;
|
|
@@ -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;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compileToSSR = void 0;
|
|
4
|
+
const ast_parser_1 = require("./ast-parser");
|
|
5
|
+
function compileToSSR(descriptor, scriptResult, scopedId) {
|
|
6
|
+
const template = descriptor.template;
|
|
7
|
+
if (!template)
|
|
8
|
+
return { code: 'function render() { return ""; }', errors: [] };
|
|
9
|
+
const errors = [];
|
|
10
|
+
const ast = (0, ast_parser_1.parse)(template.content, errors);
|
|
11
|
+
// Simplistic Transformer (Options API bindings mapping)
|
|
12
|
+
const bindings = scriptResult.bindings || [];
|
|
13
|
+
const localScope = [];
|
|
14
|
+
let uidRef = { current: 0 };
|
|
15
|
+
const getUid = () => uidRef.current++;
|
|
16
|
+
let codeChunks = [];
|
|
17
|
+
ast.children.forEach(child => {
|
|
18
|
+
const chunk = generateSSRInstruction(child, bindings, localScope, getUid);
|
|
19
|
+
if (chunk)
|
|
20
|
+
codeChunks.push(chunk);
|
|
21
|
+
});
|
|
22
|
+
// The render function generates a giant template literal
|
|
23
|
+
const bodyFn = `return \`${codeChunks.join('')}\`;`;
|
|
24
|
+
const renderFn = `function render() {
|
|
25
|
+
const _s = (v) => (v && typeof v === 'object' && 'value' in v) ? v.value : (v === null || v === undefined ? '' : v);
|
|
26
|
+
const _h = (v) => {
|
|
27
|
+
const val = _s(v);
|
|
28
|
+
if (val === null || val === undefined) return '';
|
|
29
|
+
if (typeof val !== 'string') return val;
|
|
30
|
+
if (typeof Mulan !== 'undefined' && Mulan.Security) {
|
|
31
|
+
return Mulan.Security.sanitize(val);
|
|
32
|
+
}
|
|
33
|
+
return val.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
${bodyFn}
|
|
37
|
+
}`;
|
|
38
|
+
return {
|
|
39
|
+
code: renderFn,
|
|
40
|
+
errors
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
exports.compileToSSR = compileToSSR;
|
|
44
|
+
function processBindings(exp, bindings, localScope) {
|
|
45
|
+
const isOptionsAPI = !bindings || bindings.length === 0;
|
|
46
|
+
const bindingSet = new Set(bindings || []);
|
|
47
|
+
return exp.replace(/\b([a-zA-Z_$][\w$]*)\b/g, (match, id, offset, str) => {
|
|
48
|
+
if (offset > 0 && str[offset - 1] === '.')
|
|
49
|
+
return match;
|
|
50
|
+
// simplistic keyword check
|
|
51
|
+
if (['true', 'false', 'null', 'undefined', 'this', 'Math'].includes(id) || localScope.includes(id))
|
|
52
|
+
return match;
|
|
53
|
+
if (isOptionsAPI)
|
|
54
|
+
return `this.${id}`;
|
|
55
|
+
if (bindingSet.has(id))
|
|
56
|
+
return `this.${id}`;
|
|
57
|
+
return match;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function generateSSRInstruction(node, bindings, localScope, getUid) {
|
|
61
|
+
if (node.type === 'Text') {
|
|
62
|
+
const text = node;
|
|
63
|
+
// we can preserve interpolation \${ } because the outer string is also a template literal
|
|
64
|
+
// but we need to ensure the variables map to 'this'
|
|
65
|
+
if (text.content.includes('${')) {
|
|
66
|
+
let processed = text.content.replace(/\$\{(.*?)\}/g, (_, expr) => {
|
|
67
|
+
return `\${_h(${processBindings(expr, bindings, localScope)})}`;
|
|
68
|
+
});
|
|
69
|
+
return processed;
|
|
70
|
+
}
|
|
71
|
+
return text.content;
|
|
72
|
+
}
|
|
73
|
+
if (node.type === 'Interpolation') {
|
|
74
|
+
const interp = node;
|
|
75
|
+
return `\${_h(${processBindings(interp.content, bindings, localScope)})}`;
|
|
76
|
+
}
|
|
77
|
+
if (node.type === 'Element') {
|
|
78
|
+
const el = node;
|
|
79
|
+
const uid = getUid();
|
|
80
|
+
let html = `<${el.tag} data-mu-node-id="${uid}"`;
|
|
81
|
+
let vIfCondition = '';
|
|
82
|
+
let vForDirective = null;
|
|
83
|
+
for (const key in el.props) {
|
|
84
|
+
if (key === 'v-if' || key === 'mu-if') {
|
|
85
|
+
vIfCondition = processBindings(el.props[key], bindings, localScope);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (key === 'v-for' || key === 'mu-for') {
|
|
89
|
+
const parts = el.props[key].split(' in ');
|
|
90
|
+
if (parts.length >= 2) {
|
|
91
|
+
vForDirective = { item: parts[0].trim(), list: processBindings(parts.slice(1).join(' in ').trim(), bindings, localScope) };
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// Client side events are ignored in SSR HTML output
|
|
96
|
+
if (key.startsWith('@') || key.startsWith('v-on:') || key.startsWith('on')) {
|
|
97
|
+
// To support Resumability, we'd inject a serialize marker here
|
|
98
|
+
// html += ` mu-event="${key}"`;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
let value = el.props[key];
|
|
102
|
+
if (key.startsWith(':') || key.startsWith('.')) {
|
|
103
|
+
let attrName = key.slice(1);
|
|
104
|
+
let expr = processBindings(value, bindings, localScope);
|
|
105
|
+
html += ` ${attrName}="\${_h(${expr})}"`;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
if (value.includes('${')) {
|
|
109
|
+
value = value.replace(/\$\{(.*?)\}/g, (_, expr) => {
|
|
110
|
+
return `\${_h(${processBindings(expr, bindings, localScope)})}`;
|
|
111
|
+
});
|
|
112
|
+
html += ` ${key}="${value}"`;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
html += ` ${key}="${value.replace(/"/g, '"')}"`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const isSelfClosing = ['img', 'br', 'input', 'hr', 'link', 'meta'].includes(el.tag.toLowerCase());
|
|
120
|
+
if (isSelfClosing) {
|
|
121
|
+
html += '/>';
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
html += '>';
|
|
125
|
+
for (const child of el.children) {
|
|
126
|
+
html += generateSSRInstruction(child, bindings, localScope, getUid);
|
|
127
|
+
}
|
|
128
|
+
html += `</${el.tag}>`;
|
|
129
|
+
}
|
|
130
|
+
// Apply wrappers for Control Flow (v-if / v-for)
|
|
131
|
+
if (vIfCondition) {
|
|
132
|
+
html = `\${(${vIfCondition}) ? \`${html}\` : '<!--mu-if-->'}`;
|
|
133
|
+
}
|
|
134
|
+
if (vForDirective) {
|
|
135
|
+
const { item, list } = vForDirective;
|
|
136
|
+
// The mapping function returns strings which we join
|
|
137
|
+
html = `\${(${list}).map((${item}, _index) => \`${html}\`).join('')}`;
|
|
138
|
+
}
|
|
139
|
+
return html;
|
|
140
|
+
}
|
|
141
|
+
return '';
|
|
142
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SFCDescriptor } from './sfc-parser';
|
|
2
|
+
import { CompilerOptions } from './script-compiler';
|
|
3
|
+
export interface StyleCompileResult {
|
|
4
|
+
css: string;
|
|
5
|
+
scopedId?: string;
|
|
6
|
+
errors: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function compileStyle(descriptor: SFCDescriptor, filename: string, options?: CompilerOptions): StyleCompileResult;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SFCDescriptor } from './sfc-parser';
|
|
2
|
+
import { ScriptCompileResult } from './script-compiler';
|
|
3
|
+
export interface TemplateCompileResult {
|
|
4
|
+
code: string;
|
|
5
|
+
errors: string[];
|
|
6
|
+
map?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function compileTemplate(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): TemplateCompileResult;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MuBlochSphereElement = void 0;
|
|
4
|
+
const reactive_1 = require("../core/reactive");
|
|
5
|
+
const MuBlochBase = typeof HTMLElement !== 'undefined' ? HTMLElement : class {
|
|
6
|
+
};
|
|
7
|
+
class MuBlochSphereElement extends MuBlochBase {
|
|
3
8
|
static get observedAttributes() {
|
|
4
9
|
return ['size'];
|
|
5
10
|
}
|
|
@@ -39,7 +44,7 @@ export class MuBlochSphereElement extends HTMLElement {
|
|
|
39
44
|
if (!this._qubit)
|
|
40
45
|
return;
|
|
41
46
|
// MulanJS Effect: Run whenever the qubit state changes
|
|
42
|
-
this._disposeEffect = effect(() => {
|
|
47
|
+
this._disposeEffect = (0, reactive_1.effect)(() => {
|
|
43
48
|
const state = this._qubit.value; // Access reactive proxy
|
|
44
49
|
if (!state || !state.amplitudes)
|
|
45
50
|
return;
|
|
@@ -246,6 +251,7 @@ export class MuBlochSphereElement extends HTMLElement {
|
|
|
246
251
|
this.initReactivity();
|
|
247
252
|
}
|
|
248
253
|
}
|
|
254
|
+
exports.MuBlochSphereElement = MuBlochSphereElement;
|
|
249
255
|
// Register the custom element
|
|
250
256
|
if (typeof customElements !== 'undefined') {
|
|
251
257
|
customElements.define('mu-bloch-sphere', MuBlochSphereElement);
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MuInfinity = void 0;
|
|
4
|
+
const MuInfinityBase = typeof HTMLElement !== 'undefined' ? HTMLElement : class {
|
|
5
|
+
};
|
|
6
|
+
class MuInfinity extends MuInfinityBase {
|
|
2
7
|
constructor() {
|
|
3
8
|
super();
|
|
4
9
|
this._items = [];
|
|
@@ -104,6 +109,7 @@ export class MuInfinity extends HTMLElement {
|
|
|
104
109
|
this.content.innerHTML = html;
|
|
105
110
|
}
|
|
106
111
|
}
|
|
112
|
+
exports.MuInfinity = MuInfinity;
|
|
107
113
|
// Register
|
|
108
114
|
if (typeof customElements !== 'undefined' && !customElements.get('mu-infinity')) {
|
|
109
115
|
customElements.define('mu-infinity', MuInfinity);
|
package/dist/core/component.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineComponent = exports.MuComponent = void 0;
|
|
4
|
+
const renderer_1 = require("./renderer");
|
|
5
|
+
const hooks_1 = require("./hooks");
|
|
6
|
+
const reactive_1 = require("./reactive");
|
|
4
7
|
// Or a simple component that returns an object of state/methods used by template?
|
|
5
8
|
// For Mulan 2.0, let's say functional component returns a template string render function explicitly.
|
|
6
|
-
|
|
9
|
+
class MuComponent {
|
|
7
10
|
constructor(container) {
|
|
8
11
|
this._hooks = {};
|
|
9
12
|
this._effects = [];
|
|
10
13
|
this._domEffects = [];
|
|
11
14
|
this._isDestroyed = false;
|
|
15
|
+
this._isResuming = false; // Quantum Resumability Flag
|
|
16
|
+
this._resumeRoot = null;
|
|
12
17
|
this._propsQueue = [];
|
|
13
18
|
this._eventQueue = [];
|
|
14
19
|
// --- MulanJS List Reconciliation Engine (No-VDOM) ---
|
|
@@ -20,6 +25,26 @@ export class MuComponent {
|
|
|
20
25
|
this.container = container;
|
|
21
26
|
this.state = {};
|
|
22
27
|
this.$uid = 'mu_' + Math.random().toString(36).substr(2, 9);
|
|
28
|
+
// MULAN INSIGHT: Quantum Resumability Detection
|
|
29
|
+
if (typeof window !== 'undefined' && container) {
|
|
30
|
+
const rootAttr = container.getAttribute('data-mu-root');
|
|
31
|
+
const stateScript = container.querySelector('script[type="mulan/state"]');
|
|
32
|
+
if (rootAttr || stateScript) {
|
|
33
|
+
console.log(`%c[Mulan Quantum]%c Resuming component from server state...`, "color: #00ffcc; font-weight: bold;", "");
|
|
34
|
+
this._isResuming = true;
|
|
35
|
+
this.$uid = rootAttr || this.$uid;
|
|
36
|
+
if (stateScript) {
|
|
37
|
+
try {
|
|
38
|
+
const serverState = JSON.parse(stateScript.textContent || '{}');
|
|
39
|
+
// Inject server state into this instance
|
|
40
|
+
Object.assign(this, serverState);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error("[Mulan Quantum] Failed to parse server state:", e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
23
48
|
// MULAN INSIGHT: Global Registry for Debugging
|
|
24
49
|
if (typeof window !== 'undefined') {
|
|
25
50
|
const global = window;
|
|
@@ -27,9 +52,9 @@ export class MuComponent {
|
|
|
27
52
|
global.__MULAN_INSIGHT__.components.set(this.$uid, this);
|
|
28
53
|
}
|
|
29
54
|
// Setup context for hooks
|
|
30
|
-
setCurrentInstance(this);
|
|
55
|
+
(0, hooks_1.setCurrentInstance)(this);
|
|
31
56
|
this.setup();
|
|
32
|
-
setCurrentInstance(null);
|
|
57
|
+
(0, hooks_1.setCurrentInstance)(null);
|
|
33
58
|
}
|
|
34
59
|
// Optional setup method for class components wanting to use hooks
|
|
35
60
|
setup() { }
|
|
@@ -80,17 +105,41 @@ export class MuComponent {
|
|
|
80
105
|
}
|
|
81
106
|
mount() {
|
|
82
107
|
console.log(`[Mulan Cycle] Mounting component ${this.$uid}`);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.log(`[Mulan
|
|
86
|
-
this.
|
|
87
|
-
}
|
|
88
|
-
|
|
108
|
+
if (this._isResuming) {
|
|
109
|
+
// Quantum Resumability: Skip initial render, just attach effects to existing DOM
|
|
110
|
+
console.log(`[Mulan Quantum] Skipping initial render, recovering DOM nodes...`);
|
|
111
|
+
this._resume();
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// Standard Hydration/Mount
|
|
115
|
+
const stop = (0, reactive_1.effect)(() => {
|
|
116
|
+
console.log(`[Mulan Reactivity] Triggering macro-update for ${this.$uid}`);
|
|
117
|
+
this.update();
|
|
118
|
+
});
|
|
119
|
+
this._effects.push(stop);
|
|
120
|
+
}
|
|
89
121
|
this.onMount();
|
|
90
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Internal method to execute the resumability pass.
|
|
125
|
+
* Overridden by compiler-generated code.
|
|
126
|
+
*/
|
|
127
|
+
_resume() {
|
|
128
|
+
// Clear queues
|
|
129
|
+
this._propsQueue = [];
|
|
130
|
+
this._eventQueue = [];
|
|
131
|
+
// Call the template with resumption context
|
|
132
|
+
// In a real implementation, the compiler will inject 'recovery' instructions
|
|
133
|
+
this._recoveryMode = true;
|
|
134
|
+
this.template();
|
|
135
|
+
this._recoveryMode = false;
|
|
136
|
+
// Flush bindings (events/props) to existing nodes
|
|
137
|
+
this.flushProps();
|
|
138
|
+
this.flushEvents();
|
|
139
|
+
}
|
|
91
140
|
// New helper for the compiler to register a fine-grained DOM property effect
|
|
92
141
|
_bindEffect(fn, targetNode) {
|
|
93
|
-
const stop = effect(fn, targetNode);
|
|
142
|
+
const stop = (0, reactive_1.effect)(fn, targetNode);
|
|
94
143
|
this._domEffects.push(stop);
|
|
95
144
|
return stop;
|
|
96
145
|
}
|
|
@@ -104,7 +153,7 @@ export class MuComponent {
|
|
|
104
153
|
this._eventQueue = [];
|
|
105
154
|
const template = this.template();
|
|
106
155
|
// Render HTML
|
|
107
|
-
render(template, this.container);
|
|
156
|
+
(0, renderer_1.render)(template, this.container);
|
|
108
157
|
// Flush Side Effects
|
|
109
158
|
this.flushProps();
|
|
110
159
|
this.flushEvents();
|
|
@@ -272,10 +321,11 @@ export class MuComponent {
|
|
|
272
321
|
}
|
|
273
322
|
}
|
|
274
323
|
}
|
|
324
|
+
exports.MuComponent = MuComponent;
|
|
275
325
|
// Helper to create functional components wrapped in the class system
|
|
276
326
|
// Helper to create functional components wrapped in the class system
|
|
277
327
|
// Supports both () => render and { setup() } signatures
|
|
278
|
-
|
|
328
|
+
function defineComponent(optionsOrSetup) {
|
|
279
329
|
return class UniversalComponent extends MuComponent {
|
|
280
330
|
constructor(container) {
|
|
281
331
|
super(container);
|
|
@@ -307,3 +357,4 @@ export function defineComponent(optionsOrSetup) {
|
|
|
307
357
|
}
|
|
308
358
|
};
|
|
309
359
|
}
|
|
360
|
+
exports.defineComponent = defineComponent;
|