@mulanjs/mulanjs 1.0.1-dev.20260219182444 → 1.0.1-dev.20260219221500
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/compiler.js +4 -81
- package/dist/compiler/script-compiler.js +21 -158
- package/dist/compiler/style-compiler.js +4 -61
- package/dist/compiler/template-compiler.js +4 -44
- package/dist/types/compiler/compiler.d.ts +1 -2
- package/dist/types/compiler/script-compiler.d.ts +1 -4
- package/dist/types/compiler/style-compiler.d.ts +1 -2
- package/dist/types/compiler/template-compiler.d.ts +0 -1
- package/dist/types/compiler.d.ts +1 -2
- package/dist/types/script-compiler.d.ts +1 -4
- package/dist/types/style-compiler.d.ts +1 -2
- package/dist/types/template-compiler.d.ts +0 -1
- package/package.json +1 -1
- package/src/compiler/compiler.ts +5 -90
- package/src/compiler/script-compiler.ts +21 -189
- package/src/compiler/style-compiler.ts +4 -28
- package/src/compiler/template-compiler.ts +4 -52
- package/src/loader/index.js +41 -16
|
@@ -5,12 +5,11 @@ const sfc_parser_1 = require("./sfc-parser");
|
|
|
5
5
|
const script_compiler_1 = require("./script-compiler");
|
|
6
6
|
const template_compiler_1 = require("./template-compiler");
|
|
7
7
|
const style_compiler_1 = require("./style-compiler");
|
|
8
|
-
|
|
9
|
-
async function compileSFC(source, filename, options) {
|
|
8
|
+
async function compileSFC(source, filename) {
|
|
10
9
|
// 1. Parse
|
|
11
10
|
const descriptor = (0, sfc_parser_1.parseMUJS)(source, filename);
|
|
12
11
|
// 2. Script
|
|
13
|
-
const scriptResult = await (0, script_compiler_1.compileScript)(descriptor
|
|
12
|
+
const scriptResult = await (0, script_compiler_1.compileScript)(descriptor);
|
|
14
13
|
// Replace export default to capture the component
|
|
15
14
|
// If the script already has 'export default', we intercept it.
|
|
16
15
|
let scriptCode = scriptResult.code.replace('export default', 'const __component__ =');
|
|
@@ -18,85 +17,9 @@ async function compileSFC(source, filename, options) {
|
|
|
18
17
|
scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
|
|
19
18
|
}
|
|
20
19
|
// 3. Style
|
|
21
|
-
const styleResult = (0, style_compiler_1.compileStyle)(descriptor, filename
|
|
20
|
+
const styleResult = (0, style_compiler_1.compileStyle)(descriptor, filename);
|
|
22
21
|
// 4. Template
|
|
23
22
|
const templateResult = (0, template_compiler_1.compileTemplate)(descriptor, scriptResult, styleResult.scopedId);
|
|
24
|
-
// Calculate offsets for source map merging
|
|
25
|
-
// 1. Script Code
|
|
26
|
-
// 2. Padding (newlines)
|
|
27
|
-
// 3. Template Code
|
|
28
|
-
// We need to count lines in scriptCode to know where template code starts in the final file
|
|
29
|
-
const scriptLineCount = scriptCode.split(/\r?\n/).length;
|
|
30
|
-
const paddingLines = 2; // Two newlines between script and template
|
|
31
|
-
const templateOffset = scriptLineCount + paddingLines;
|
|
32
|
-
let mergedMap;
|
|
33
|
-
// Merge Maps
|
|
34
|
-
if (filename) {
|
|
35
|
-
const generator = new source_map_1.SourceMapGenerator({
|
|
36
|
-
file: filename + '.js',
|
|
37
|
-
});
|
|
38
|
-
// 1. Apply Script Map
|
|
39
|
-
if (scriptResult.map) {
|
|
40
|
-
const scriptConsumer = await new source_map_1.SourceMapConsumer(JSON.parse(scriptResult.map));
|
|
41
|
-
scriptConsumer.eachMapping(mapping => {
|
|
42
|
-
if (mapping.source) {
|
|
43
|
-
generator.addMapping({
|
|
44
|
-
generated: {
|
|
45
|
-
line: mapping.generatedLine, // Script is at the top, so no offset needed
|
|
46
|
-
column: mapping.generatedColumn
|
|
47
|
-
},
|
|
48
|
-
original: {
|
|
49
|
-
line: mapping.originalLine,
|
|
50
|
-
column: mapping.originalColumn
|
|
51
|
-
},
|
|
52
|
-
source: mapping.source,
|
|
53
|
-
name: mapping.name
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
// Copy source content from script map
|
|
58
|
-
if (scriptConsumer.sources) {
|
|
59
|
-
scriptConsumer.sources.forEach(sourceFile => {
|
|
60
|
-
const content = scriptConsumer.sourceContentFor(sourceFile);
|
|
61
|
-
if (content) {
|
|
62
|
-
generator.setSourceContent(sourceFile, content);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
scriptConsumer.destroy();
|
|
67
|
-
}
|
|
68
|
-
// 2. Apply Template Map
|
|
69
|
-
if (templateResult.map) {
|
|
70
|
-
const templateConsumer = await new source_map_1.SourceMapConsumer(JSON.parse(templateResult.map));
|
|
71
|
-
templateConsumer.eachMapping(mapping => {
|
|
72
|
-
if (mapping.source) {
|
|
73
|
-
generator.addMapping({
|
|
74
|
-
generated: {
|
|
75
|
-
line: mapping.generatedLine + templateOffset, // Shift by script length
|
|
76
|
-
column: mapping.generatedColumn
|
|
77
|
-
},
|
|
78
|
-
original: {
|
|
79
|
-
line: mapping.originalLine, // Original line in .mujs
|
|
80
|
-
column: mapping.originalColumn
|
|
81
|
-
},
|
|
82
|
-
source: mapping.source, // This should be the .mujs file
|
|
83
|
-
name: mapping.name
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
// Copy source content from template map
|
|
88
|
-
if (templateConsumer.sources) {
|
|
89
|
-
templateConsumer.sources.forEach(sourceFile => {
|
|
90
|
-
const content = templateConsumer.sourceContentFor(sourceFile);
|
|
91
|
-
if (content) {
|
|
92
|
-
generator.setSourceContent(sourceFile, content);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
templateConsumer.destroy();
|
|
97
|
-
}
|
|
98
|
-
mergedMap = generator.toString();
|
|
99
|
-
}
|
|
100
23
|
// 5. Assembly
|
|
101
24
|
const cssInjection = styleResult.css ? `
|
|
102
25
|
if (typeof document !== 'undefined') {
|
|
@@ -163,6 +86,6 @@ export default __component__;
|
|
|
163
86
|
code: finalCode,
|
|
164
87
|
css: styleResult.css,
|
|
165
88
|
errors: [...scriptResult.errors, ...templateResult.errors],
|
|
166
|
-
map:
|
|
89
|
+
map: scriptResult.map // Pass the map
|
|
167
90
|
};
|
|
168
91
|
}
|
|
@@ -36,55 +36,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.compileScript = compileScript;
|
|
37
37
|
const ts = __importStar(require("typescript"));
|
|
38
38
|
const source_map_1 = require("source-map");
|
|
39
|
-
|
|
40
|
-
async function compileScript(descriptor, options) {
|
|
39
|
+
async function compileScript(descriptor) {
|
|
41
40
|
const script = descriptor.script;
|
|
42
41
|
if (!script)
|
|
43
42
|
return { code: 'export default {}', errors: [] };
|
|
44
|
-
let content = script.content;
|
|
45
|
-
let filename = descriptor.filename;
|
|
46
|
-
let isExternal = false;
|
|
47
|
-
let externalPath = '';
|
|
48
|
-
// Handle External Script
|
|
49
|
-
if (script.attrs.src) {
|
|
50
|
-
if (options && options.readFileSync) {
|
|
51
|
-
try {
|
|
52
|
-
// Resolve path relative to the SFC file
|
|
53
|
-
const sfcDir = path.dirname(descriptor.filename);
|
|
54
|
-
externalPath = path.resolve(sfcDir, script.attrs.src);
|
|
55
|
-
content = options.readFileSync(externalPath);
|
|
56
|
-
// Keep filename as .mujs for now, but we will use externalPath for source mapping of the content
|
|
57
|
-
// Actually, if we want the external file to show up as ITSELF in devtools, we should map to externalPath.
|
|
58
|
-
isExternal = true;
|
|
59
|
-
}
|
|
60
|
-
catch (e) {
|
|
61
|
-
return { code: 'export default {}', errors: [`Failed to read external script: ${e.message}`] };
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
return { code: 'export default {}', errors: [`External script found but no readFileSync option provided.`] };
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
43
|
const isSetup = !!script.attrs.setup;
|
|
69
|
-
const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx'
|
|
44
|
+
const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
|
|
70
45
|
if (isSetup) {
|
|
71
|
-
return await compileSetupScript(script, isTs, descriptor
|
|
72
|
-
}
|
|
73
|
-
else if (isTs) {
|
|
74
|
-
return await compileStandardScript(script, descriptor, content, filename, isExternal, externalPath);
|
|
46
|
+
return await compileSetupScript(script, isTs, descriptor);
|
|
75
47
|
}
|
|
76
48
|
else {
|
|
77
49
|
// Standard Options API - just pass through, assuming it has export default
|
|
78
|
-
return { code: content, errors: [] };
|
|
50
|
+
return { code: script.content, errors: [] };
|
|
79
51
|
}
|
|
80
52
|
}
|
|
81
53
|
// Global list of Mulan hooks for auto-imports and reactivity exemption
|
|
82
54
|
const COMMON_HOOKS = ['muState', 'onMuMount', 'onMuInit', 'onMuDestroy', 'muEffect', 'muMemo', 'muQubit', 'muGate', 'muMeasure', 'muRegister', 'muEntangle', 'persistent'];
|
|
83
|
-
async function compileSetupScript(script, isTs, descriptor
|
|
55
|
+
async function compileSetupScript(script, isTs, descriptor) {
|
|
84
56
|
var _a;
|
|
85
57
|
// 1. Pre-process: Natural Reactivity Transformation ($ syntax)
|
|
86
58
|
// We do this BEFORE extraction so that the rest of the compiler sees standard 'muState' code.
|
|
87
|
-
const transformedContent = transformNaturalReactivity(content, isTs);
|
|
59
|
+
const transformedContent = transformNaturalReactivity(script.content, isTs);
|
|
88
60
|
// 2. Parse the TRANSFORMED code
|
|
89
61
|
const sourceFile = ts.createSourceFile('script.' + (isTs ? 'ts' : 'js'), transformedContent, ts.ScriptTarget.Latest, true);
|
|
90
62
|
const imports = [];
|
|
@@ -157,6 +129,7 @@ async function compileSetupScript(script, isTs, descriptor, content, filename, i
|
|
|
157
129
|
const autoImportStmt = autoImports.length > 0
|
|
158
130
|
? `import { ${autoImports.join(', ')} } from '@mulanjs/mulanjs';`
|
|
159
131
|
: '';
|
|
132
|
+
const filename = descriptor.filename;
|
|
160
133
|
const componentName = filename ? ((_a = filename.split(/[/\\]/).pop()) === null || _a === void 0 ? void 0 : _a.split('.')[0].replace(/\W/g, '')) || 'App' : 'App';
|
|
161
134
|
const bindingString = bindings.length > 0
|
|
162
135
|
? `
|
|
@@ -198,53 +171,27 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
198
171
|
});
|
|
199
172
|
// Fix: Remove the //# sourceMappingURL= comment added by TS
|
|
200
173
|
const codeWithoutMapComment = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
|
|
201
|
-
// Enhance Source Map to show ORIGINAL .mujs content
|
|
174
|
+
// Enhance Source Map to show ORIGINAL .mujs content
|
|
202
175
|
let finalMap = result.sourceMapText;
|
|
203
176
|
if (finalMap) {
|
|
204
177
|
try {
|
|
205
178
|
// Determine offsets
|
|
206
179
|
const preambleLines = preamble.split('\n').length - 1;
|
|
207
180
|
// scriptBody starts at line `preambleLines` (0-indexed) in finalCode.
|
|
208
|
-
// Determine where script starts
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
let linesBeforeScript = 0;
|
|
212
|
-
let originalSource = content;
|
|
213
|
-
if (!isExternal) {
|
|
214
|
-
originalSource = descriptor.source;
|
|
215
|
-
linesBeforeScript = originalSource.substring(0, script.start).split('\n').length - 1;
|
|
216
|
-
}
|
|
181
|
+
// Determine where script starts in original .mujs file
|
|
182
|
+
const originalSource = descriptor.source;
|
|
183
|
+
const linesBeforeScript = originalSource.substring(0, script.start).split('\n').length - 1;
|
|
217
184
|
const consumer = await new source_map_1.SourceMapConsumer(finalMap);
|
|
218
185
|
const generator = new source_map_1.SourceMapGenerator({
|
|
219
186
|
file: filename,
|
|
220
187
|
sourceRoot: ''
|
|
221
188
|
});
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// We'll try to map it so it looks like a separate file
|
|
229
|
-
const normalized = externalPath.replace(/\\/g, '/');
|
|
230
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
231
|
-
if (srcIdx !== -1) {
|
|
232
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1); // e.g. webpack:///src/logic.ts
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
relativePath = 'webpack:///' + path.basename(externalPath);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
// Inline: Point to .mujs file
|
|
240
|
-
const normalized = filename.replace(/\\/g, '/');
|
|
241
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
242
|
-
if (srcIdx !== -1) {
|
|
243
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1); // e.g. webpack:///src/App.mujs
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
// If inline, we set source content of .mujs (the whole file)
|
|
247
|
-
// If external, we set source content of the .ts file
|
|
189
|
+
// If inside src, use path starting from src. Else just basename.
|
|
190
|
+
const normalizedPath = filename.replace(/\\/g, '/');
|
|
191
|
+
const srcIndex = normalizedPath.indexOf('/src/');
|
|
192
|
+
let relativePath = srcIndex !== -1
|
|
193
|
+
? normalizedPath.substring(srcIndex + 1)
|
|
194
|
+
: filename.split(/[/\\]/).pop() || 'unknown.mujs';
|
|
248
195
|
generator.setSourceContent(relativePath, originalSource);
|
|
249
196
|
consumer.eachMapping(m => {
|
|
250
197
|
if (m.originalLine === null)
|
|
@@ -258,10 +205,9 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
258
205
|
if (lineInFinalCode > preambleLines) {
|
|
259
206
|
// Calculate offset inside scriptBody
|
|
260
207
|
const lineOffsetInBody = lineInFinalCode - preambleLines; // 1-based offset (1st line of body is 1)
|
|
261
|
-
// Calculate original line
|
|
262
|
-
// If
|
|
263
|
-
|
|
264
|
-
const originalLine = linesBeforeScript + lineOffsetInBody;
|
|
208
|
+
// Calculate original line in .mujs
|
|
209
|
+
// If lineOffsetInBody is 1 (first line of body), it corresponds to (linesBeforeScript + 1)
|
|
210
|
+
const originalLineInMujs = linesBeforeScript + lineOffsetInBody;
|
|
265
211
|
// Add mapping
|
|
266
212
|
generator.addMapping({
|
|
267
213
|
generated: {
|
|
@@ -269,7 +215,7 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
269
215
|
column: m.generatedColumn
|
|
270
216
|
},
|
|
271
217
|
original: {
|
|
272
|
-
line:
|
|
218
|
+
line: originalLineInMujs,
|
|
273
219
|
column: m.originalColumn // Column should be preserved if we didn't change indentation
|
|
274
220
|
},
|
|
275
221
|
source: relativePath,
|
|
@@ -298,89 +244,6 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
298
244
|
map: undefined
|
|
299
245
|
};
|
|
300
246
|
}
|
|
301
|
-
async function compileStandardScript(script, descriptor, content, filename, isExternal, externalPath) {
|
|
302
|
-
// For standard script, we also might need source map if we transpiled it (e.g. from TS)
|
|
303
|
-
// and/or just to map it back to .mujs
|
|
304
|
-
let finalMap;
|
|
305
|
-
let code = content;
|
|
306
|
-
// Transpile if TS
|
|
307
|
-
if (externalPath.endsWith('.ts') || script.attrs.lang === 'ts') {
|
|
308
|
-
const result = ts.transpileModule(content, {
|
|
309
|
-
compilerOptions: {
|
|
310
|
-
module: ts.ModuleKind.ESNext,
|
|
311
|
-
target: ts.ScriptTarget.ES2019,
|
|
312
|
-
sourceMap: true,
|
|
313
|
-
inlineSources: true,
|
|
314
|
-
},
|
|
315
|
-
fileName: filename
|
|
316
|
-
});
|
|
317
|
-
code = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
|
|
318
|
-
finalMap = result.sourceMapText;
|
|
319
|
-
}
|
|
320
|
-
// Adjust Source Map
|
|
321
|
-
if (finalMap) {
|
|
322
|
-
try {
|
|
323
|
-
// Standard script content is usually 1:1 with source lines if we ignore transpilation shifts (which TS handles)
|
|
324
|
-
// But if it's inline, TS thinks it starts at line 0. We need to shift it by `linesBeforeScript`.
|
|
325
|
-
let linesBeforeScript = 0;
|
|
326
|
-
let originalSource = content;
|
|
327
|
-
if (!isExternal) {
|
|
328
|
-
originalSource = descriptor.source;
|
|
329
|
-
linesBeforeScript = descriptor.source.substring(0, script.start).split('\n').length - 1;
|
|
330
|
-
}
|
|
331
|
-
const consumer = await new source_map_1.SourceMapConsumer(finalMap);
|
|
332
|
-
const generator = new source_map_1.SourceMapGenerator({
|
|
333
|
-
file: filename,
|
|
334
|
-
sourceRoot: ''
|
|
335
|
-
});
|
|
336
|
-
// Determine relative path for source (same logic as Setup)
|
|
337
|
-
let relativePath = filename.split(/[/\\]/).pop() || 'unknown.mujs';
|
|
338
|
-
if (isExternal) {
|
|
339
|
-
const normalized = externalPath.replace(/\\/g, '/');
|
|
340
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
341
|
-
if (srcIdx !== -1) {
|
|
342
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1);
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
relativePath = 'webpack:///' + path.basename(externalPath);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
const normalized = filename.replace(/\\/g, '/');
|
|
350
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
351
|
-
if (srcIdx !== -1) {
|
|
352
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
generator.setSourceContent(relativePath, originalSource);
|
|
356
|
-
consumer.eachMapping(m => {
|
|
357
|
-
if (m.originalLine === null)
|
|
358
|
-
return;
|
|
359
|
-
const originalLine = m.originalLine + linesBeforeScript;
|
|
360
|
-
generator.addMapping({
|
|
361
|
-
generated: {
|
|
362
|
-
line: m.generatedLine,
|
|
363
|
-
column: m.generatedColumn
|
|
364
|
-
},
|
|
365
|
-
original: {
|
|
366
|
-
line: originalLine,
|
|
367
|
-
column: m.originalColumn
|
|
368
|
-
},
|
|
369
|
-
source: relativePath,
|
|
370
|
-
name: m.name
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
finalMap = generator.toString();
|
|
374
|
-
consumer.destroy();
|
|
375
|
-
}
|
|
376
|
-
catch (e) { /* ignore */ }
|
|
377
|
-
}
|
|
378
|
-
return {
|
|
379
|
-
code: code,
|
|
380
|
-
errors: [],
|
|
381
|
-
map: finalMap
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
247
|
// --- Natural Reactivity Transformer ---
|
|
385
248
|
function transformNaturalReactivity(code, isTs) {
|
|
386
249
|
// If no '$(' or '$q(' or '$qr(' usage, return original code for safety/speed
|
|
@@ -1,41 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.compileStyle = compileStyle;
|
|
37
|
-
|
|
38
|
-
function compileStyle(descriptor, filename, options) {
|
|
4
|
+
function compileStyle(descriptor, filename) {
|
|
39
5
|
const styles = descriptor.styles;
|
|
40
6
|
if (!styles || styles.length === 0)
|
|
41
7
|
return { css: '', errors: [] };
|
|
@@ -43,41 +9,18 @@ function compileStyle(descriptor, filename, options) {
|
|
|
43
9
|
const scopedId = 'data-v-' + hash(filename);
|
|
44
10
|
styles.forEach(style => {
|
|
45
11
|
let content = style.content;
|
|
46
|
-
// Handle External Style
|
|
47
|
-
if (style.attrs.src) {
|
|
48
|
-
if (options && options.readFileSync) {
|
|
49
|
-
try {
|
|
50
|
-
const sfcDir = path.dirname(filename);
|
|
51
|
-
const externalPath = path.resolve(sfcDir, style.attrs.src);
|
|
52
|
-
content = options.readFileSync(externalPath);
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
// Start error with "Style Compilation Error" to be consistent? Or just add to errors list.
|
|
56
|
-
// But we are inside forEach, so we can't return easily. We'll add a comment in CSS or log.
|
|
57
|
-
// Better to collect errors.
|
|
58
|
-
// But function signature returns { errors: string[] }.
|
|
59
|
-
// We'll just append to errors? But we need to change forEach loop or use functional approach.
|
|
60
|
-
// For now, let's just ignore/log in CSS content?
|
|
61
|
-
content = `/* Failed to read external style: ${e.message} */`;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
content = `/* External style found but no readFileSync option provided */`;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
12
|
if (style.attrs.scoped) {
|
|
69
13
|
// SCSS Compilation
|
|
70
|
-
if (style.attrs.lang === 'scss' || style.attrs.lang === 'sass'
|
|
14
|
+
if (style.attrs.lang === 'scss' || style.attrs.lang === 'sass') {
|
|
71
15
|
try {
|
|
72
16
|
const sass = require('sass');
|
|
73
17
|
const result = sass.compileString(content, {
|
|
74
|
-
syntax:
|
|
18
|
+
syntax: style.attrs.lang === 'sass' ? 'indented' : 'scss'
|
|
75
19
|
});
|
|
76
20
|
content = result.css;
|
|
77
21
|
}
|
|
78
22
|
catch (e) {
|
|
79
|
-
|
|
80
|
-
content = `/* SCSS Compilation Error: ${e.message} */`;
|
|
23
|
+
return { css: '', errors: [`SCSS Compilation Error: ${e.message}`] };
|
|
81
24
|
}
|
|
82
25
|
}
|
|
83
26
|
// Rewrite selectors
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.compileTemplate = compileTemplate;
|
|
4
|
-
// --- Compile Function ---
|
|
5
|
-
const source_map_1 = require("source-map");
|
|
6
4
|
function compileTemplate(descriptor, scriptResult, scopedId) {
|
|
7
5
|
console.log(`[MulanJS Compiler v1.0.1-dev.2] Compiling template for: ${descriptor.filename || 'anonymous'}`);
|
|
8
6
|
const template = descriptor.template;
|
|
@@ -16,7 +14,8 @@ function compileTemplate(descriptor, scriptResult, scopedId) {
|
|
|
16
14
|
transform(ast, scriptResult, scopedId, [], descriptor.filename);
|
|
17
15
|
// 3. Codegen Phase (AST -> JS Function)
|
|
18
16
|
const code = generate(ast, scriptResult.bindings || []);
|
|
19
|
-
|
|
17
|
+
return {
|
|
18
|
+
code: `function render() {
|
|
20
19
|
const _s = (v) => (v && typeof v === 'object' && 'value' in v) ? v.value : v;
|
|
21
20
|
const _h = (v, raw) => {
|
|
22
21
|
const val = _s(v);
|
|
@@ -28,47 +27,8 @@ function compileTemplate(descriptor, scriptResult, scopedId) {
|
|
|
28
27
|
return val.replace(/</g, '<').replace(/>/g, '>');
|
|
29
28
|
};
|
|
30
29
|
return ${code};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// We map the entire render function to the starting line of the template in the source file
|
|
34
|
-
let map = undefined;
|
|
35
|
-
if (descriptor.filename) {
|
|
36
|
-
const generator = new source_map_1.SourceMapGenerator({
|
|
37
|
-
file: descriptor.filename + '.template.js', // Virtual file name
|
|
38
|
-
});
|
|
39
|
-
// Find start line of template in original source
|
|
40
|
-
// descriptor.template.start is the index of content start
|
|
41
|
-
const sourceBefore = descriptor.source.substring(0, template.start);
|
|
42
|
-
const startLine = sourceBefore.split(/\r?\n/).length;
|
|
43
|
-
// Simple mapping: One-to-one mapping isn't easy without AST location tracking
|
|
44
|
-
// But we can map the 'return' statement to the template start
|
|
45
|
-
// The render function has about 12 lines of preamble.
|
|
46
|
-
// Let's just mapping the whole block to the start line for now.
|
|
47
|
-
// This ensures the file shows up in devtools.
|
|
48
|
-
generator.addMapping({
|
|
49
|
-
generated: { line: 1, column: 0 },
|
|
50
|
-
source: descriptor.filename, // This should be the .mujs file path
|
|
51
|
-
original: { line: startLine, column: 0 }
|
|
52
|
-
});
|
|
53
|
-
// Also map the return line (approx line 12)
|
|
54
|
-
generator.addMapping({
|
|
55
|
-
generated: { line: 12, column: 0 },
|
|
56
|
-
source: descriptor.filename,
|
|
57
|
-
original: { line: startLine, column: 0 }
|
|
58
|
-
});
|
|
59
|
-
// We must include the "source content" so DevTools can display it!
|
|
60
|
-
// IMPORTANT: We want the FULL .mujs content here, so it matches what Webpack sees?
|
|
61
|
-
// Actually, if we use the same filename as script-compiler, they might conflict or merge.
|
|
62
|
-
// Script compiler uses 'webpack:///...'
|
|
63
|
-
// Let's use the same convention.
|
|
64
|
-
// But we just use the filename here, compiler.ts will handle the final path adjustment if needed.
|
|
65
|
-
generator.setSourceContent(descriptor.filename, descriptor.source);
|
|
66
|
-
map = generator.toString();
|
|
67
|
-
}
|
|
68
|
-
return {
|
|
69
|
-
code: renderFn,
|
|
70
|
-
errors,
|
|
71
|
-
map
|
|
30
|
+
}`,
|
|
31
|
+
errors
|
|
72
32
|
};
|
|
73
33
|
}
|
|
74
34
|
// --- Parser (Recursive Descent) ---
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { CompilerOptions } from './script-compiler';
|
|
2
1
|
export interface CompileResult {
|
|
3
2
|
code: string;
|
|
4
3
|
css: string;
|
|
5
4
|
errors: string[];
|
|
6
5
|
map?: string;
|
|
7
6
|
}
|
|
8
|
-
export declare function compileSFC(source: string, filename: string
|
|
7
|
+
export declare function compileSFC(source: string, filename: string): Promise<CompileResult>;
|
|
@@ -5,7 +5,4 @@ export interface ScriptCompileResult {
|
|
|
5
5
|
errors: string[];
|
|
6
6
|
map?: string;
|
|
7
7
|
}
|
|
8
|
-
export
|
|
9
|
-
readFileSync?: (file: string) => string;
|
|
10
|
-
}
|
|
11
|
-
export declare function compileScript(descriptor: SFCDescriptor, options?: CompilerOptions): Promise<ScriptCompileResult>;
|
|
8
|
+
export declare function compileScript(descriptor: SFCDescriptor): Promise<ScriptCompileResult>;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { SFCDescriptor } from './sfc-parser';
|
|
2
|
-
import { CompilerOptions } from './script-compiler';
|
|
3
2
|
export interface StyleCompileResult {
|
|
4
3
|
css: string;
|
|
5
4
|
scopedId?: string;
|
|
6
5
|
errors: string[];
|
|
7
6
|
}
|
|
8
|
-
export declare function compileStyle(descriptor: SFCDescriptor, filename: string
|
|
7
|
+
export declare function compileStyle(descriptor: SFCDescriptor, filename: string): StyleCompileResult;
|
|
@@ -3,6 +3,5 @@ import { ScriptCompileResult } from './script-compiler';
|
|
|
3
3
|
export interface TemplateCompileResult {
|
|
4
4
|
code: string;
|
|
5
5
|
errors: string[];
|
|
6
|
-
map?: string;
|
|
7
6
|
}
|
|
8
7
|
export declare function compileTemplate(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): TemplateCompileResult;
|
package/dist/types/compiler.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { CompilerOptions } from './script-compiler';
|
|
2
1
|
export interface CompileResult {
|
|
3
2
|
code: string;
|
|
4
3
|
css: string;
|
|
5
4
|
errors: string[];
|
|
6
5
|
map?: string;
|
|
7
6
|
}
|
|
8
|
-
export declare function compileSFC(source: string, filename: string
|
|
7
|
+
export declare function compileSFC(source: string, filename: string): Promise<CompileResult>;
|
|
@@ -5,7 +5,4 @@ export interface ScriptCompileResult {
|
|
|
5
5
|
errors: string[];
|
|
6
6
|
map?: string;
|
|
7
7
|
}
|
|
8
|
-
export
|
|
9
|
-
readFileSync?: (file: string) => string;
|
|
10
|
-
}
|
|
11
|
-
export declare function compileScript(descriptor: SFCDescriptor, options?: CompilerOptions): Promise<ScriptCompileResult>;
|
|
8
|
+
export declare function compileScript(descriptor: SFCDescriptor): Promise<ScriptCompileResult>;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { SFCDescriptor } from './sfc-parser';
|
|
2
|
-
import { CompilerOptions } from './script-compiler';
|
|
3
2
|
export interface StyleCompileResult {
|
|
4
3
|
css: string;
|
|
5
4
|
scopedId?: string;
|
|
6
5
|
errors: string[];
|
|
7
6
|
}
|
|
8
|
-
export declare function compileStyle(descriptor: SFCDescriptor, filename: string
|
|
7
|
+
export declare function compileStyle(descriptor: SFCDescriptor, filename: string): StyleCompileResult;
|
|
@@ -3,6 +3,5 @@ import { ScriptCompileResult } from './script-compiler';
|
|
|
3
3
|
export interface TemplateCompileResult {
|
|
4
4
|
code: string;
|
|
5
5
|
errors: string[];
|
|
6
|
-
map?: string;
|
|
7
6
|
}
|
|
8
7
|
export declare function compileTemplate(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): TemplateCompileResult;
|
package/package.json
CHANGED
package/src/compiler/compiler.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { parseMUJS } from './sfc-parser';
|
|
3
|
-
import { compileScript
|
|
3
|
+
import { compileScript } from './script-compiler';
|
|
4
4
|
import { compileTemplate } from './template-compiler';
|
|
5
5
|
import { compileStyle } from './style-compiler';
|
|
6
6
|
|
|
@@ -11,14 +11,12 @@ export interface CompileResult {
|
|
|
11
11
|
map?: string; // Source Map
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export async function compileSFC(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult> {
|
|
14
|
+
export async function compileSFC(source: string, filename: string): Promise<CompileResult> {
|
|
17
15
|
// 1. Parse
|
|
18
16
|
const descriptor = parseMUJS(source, filename);
|
|
19
17
|
|
|
20
18
|
// 2. Script
|
|
21
|
-
const scriptResult = await compileScript(descriptor
|
|
19
|
+
const scriptResult = await compileScript(descriptor);
|
|
22
20
|
// Replace export default to capture the component
|
|
23
21
|
// If the script already has 'export default', we intercept it.
|
|
24
22
|
let scriptCode = scriptResult.code.replace('export default', 'const __component__ =');
|
|
@@ -27,94 +25,11 @@ export async function compileSFC(source: string, filename: string, options?: Com
|
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
// 3. Style
|
|
30
|
-
const styleResult = compileStyle(descriptor, filename
|
|
28
|
+
const styleResult = compileStyle(descriptor, filename);
|
|
31
29
|
|
|
32
30
|
// 4. Template
|
|
33
31
|
const templateResult = compileTemplate(descriptor, scriptResult, styleResult.scopedId);
|
|
34
32
|
|
|
35
|
-
// Calculate offsets for source map merging
|
|
36
|
-
// 1. Script Code
|
|
37
|
-
// 2. Padding (newlines)
|
|
38
|
-
// 3. Template Code
|
|
39
|
-
|
|
40
|
-
// We need to count lines in scriptCode to know where template code starts in the final file
|
|
41
|
-
const scriptLineCount = scriptCode.split(/\r?\n/).length;
|
|
42
|
-
const paddingLines = 2; // Two newlines between script and template
|
|
43
|
-
const templateOffset = scriptLineCount + paddingLines;
|
|
44
|
-
|
|
45
|
-
let mergedMap: string | undefined;
|
|
46
|
-
|
|
47
|
-
// Merge Maps
|
|
48
|
-
if (filename) {
|
|
49
|
-
const generator = new SourceMapGenerator({
|
|
50
|
-
file: filename + '.js',
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// 1. Apply Script Map
|
|
54
|
-
if (scriptResult.map) {
|
|
55
|
-
const scriptConsumer = await new SourceMapConsumer(JSON.parse(scriptResult.map));
|
|
56
|
-
scriptConsumer.eachMapping(mapping => {
|
|
57
|
-
if (mapping.source) {
|
|
58
|
-
generator.addMapping({
|
|
59
|
-
generated: {
|
|
60
|
-
line: mapping.generatedLine, // Script is at the top, so no offset needed
|
|
61
|
-
column: mapping.generatedColumn
|
|
62
|
-
},
|
|
63
|
-
original: {
|
|
64
|
-
line: mapping.originalLine,
|
|
65
|
-
column: mapping.originalColumn
|
|
66
|
-
},
|
|
67
|
-
source: mapping.source,
|
|
68
|
-
name: mapping.name
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
// Copy source content from script map
|
|
73
|
-
if (scriptConsumer.sources) {
|
|
74
|
-
scriptConsumer.sources.forEach(sourceFile => {
|
|
75
|
-
const content = scriptConsumer.sourceContentFor(sourceFile);
|
|
76
|
-
if (content) {
|
|
77
|
-
generator.setSourceContent(sourceFile, content);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
scriptConsumer.destroy();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// 2. Apply Template Map
|
|
85
|
-
if (templateResult.map) {
|
|
86
|
-
const templateConsumer = await new SourceMapConsumer(JSON.parse(templateResult.map));
|
|
87
|
-
templateConsumer.eachMapping(mapping => {
|
|
88
|
-
if (mapping.source) {
|
|
89
|
-
generator.addMapping({
|
|
90
|
-
generated: {
|
|
91
|
-
line: mapping.generatedLine + templateOffset, // Shift by script length
|
|
92
|
-
column: mapping.generatedColumn
|
|
93
|
-
},
|
|
94
|
-
original: {
|
|
95
|
-
line: mapping.originalLine, // Original line in .mujs
|
|
96
|
-
column: mapping.originalColumn
|
|
97
|
-
},
|
|
98
|
-
source: mapping.source, // This should be the .mujs file
|
|
99
|
-
name: mapping.name
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
// Copy source content from template map
|
|
104
|
-
if (templateConsumer.sources) {
|
|
105
|
-
templateConsumer.sources.forEach(sourceFile => {
|
|
106
|
-
const content = templateConsumer.sourceContentFor(sourceFile);
|
|
107
|
-
if (content) {
|
|
108
|
-
generator.setSourceContent(sourceFile, content);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
templateConsumer.destroy();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
mergedMap = generator.toString();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
33
|
// 5. Assembly
|
|
119
34
|
const cssInjection = styleResult.css ? `
|
|
120
35
|
if (typeof document !== 'undefined') {
|
|
@@ -183,6 +98,6 @@ export default __component__;
|
|
|
183
98
|
code: finalCode,
|
|
184
99
|
css: styleResult.css,
|
|
185
100
|
errors: [...scriptResult.errors, ...templateResult.errors],
|
|
186
|
-
map:
|
|
101
|
+
map: scriptResult.map // Pass the map
|
|
187
102
|
};
|
|
188
103
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { SFCDescriptor, SFCBlock } from './sfc-parser';
|
|
3
3
|
import * as ts from 'typescript';
|
|
4
4
|
import { SourceMapConsumer, SourceMapGenerator } from 'source-map';
|
|
5
|
-
import * as path from 'path';
|
|
6
5
|
|
|
7
6
|
export interface ScriptCompileResult {
|
|
8
7
|
code: string;
|
|
@@ -11,66 +10,28 @@ export interface ScriptCompileResult {
|
|
|
11
10
|
map?: string; // Source Map
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
export
|
|
15
|
-
readFileSync?: (file: string) => string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function compileScript(descriptor: SFCDescriptor, options?: CompilerOptions): Promise<ScriptCompileResult> {
|
|
13
|
+
export async function compileScript(descriptor: SFCDescriptor): Promise<ScriptCompileResult> {
|
|
19
14
|
const script = descriptor.script;
|
|
20
15
|
if (!script) return { code: 'export default {}', errors: [] };
|
|
21
16
|
|
|
22
|
-
let content = script.content;
|
|
23
|
-
let filename = descriptor.filename;
|
|
24
|
-
let isExternal = false;
|
|
25
|
-
let externalPath = '';
|
|
26
|
-
|
|
27
|
-
// Handle External Script
|
|
28
|
-
if (script.attrs.src) {
|
|
29
|
-
if (options && options.readFileSync) {
|
|
30
|
-
try {
|
|
31
|
-
// Resolve path relative to the SFC file
|
|
32
|
-
const sfcDir = path.dirname(descriptor.filename);
|
|
33
|
-
externalPath = path.resolve(sfcDir, script.attrs.src);
|
|
34
|
-
content = options.readFileSync(externalPath);
|
|
35
|
-
// Keep filename as .mujs for now, but we will use externalPath for source mapping of the content
|
|
36
|
-
// Actually, if we want the external file to show up as ITSELF in devtools, we should map to externalPath.
|
|
37
|
-
isExternal = true;
|
|
38
|
-
} catch (e: any) {
|
|
39
|
-
return { code: 'export default {}', errors: [`Failed to read external script: ${e.message}`] };
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
return { code: 'export default {}', errors: [`External script found but no readFileSync option provided.`] };
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
17
|
const isSetup = !!script.attrs.setup;
|
|
47
|
-
const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx'
|
|
18
|
+
const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
|
|
48
19
|
|
|
49
20
|
if (isSetup) {
|
|
50
|
-
return await compileSetupScript(script, isTs, descriptor
|
|
51
|
-
} else if (isTs) {
|
|
52
|
-
return await compileStandardScript(script, descriptor, content, filename, isExternal, externalPath);
|
|
21
|
+
return await compileSetupScript(script, isTs, descriptor);
|
|
53
22
|
} else {
|
|
54
23
|
// Standard Options API - just pass through, assuming it has export default
|
|
55
|
-
return { code: content, errors: [] };
|
|
24
|
+
return { code: script.content, errors: [] };
|
|
56
25
|
}
|
|
57
26
|
}
|
|
58
27
|
|
|
59
28
|
// Global list of Mulan hooks for auto-imports and reactivity exemption
|
|
60
29
|
const COMMON_HOOKS = ['muState', 'onMuMount', 'onMuInit', 'onMuDestroy', 'muEffect', 'muMemo', 'muQubit', 'muGate', 'muMeasure', 'muRegister', 'muEntangle', 'persistent'];
|
|
61
30
|
|
|
62
|
-
async function compileSetupScript(
|
|
63
|
-
script: SFCBlock,
|
|
64
|
-
isTs: boolean,
|
|
65
|
-
descriptor: SFCDescriptor,
|
|
66
|
-
content: string,
|
|
67
|
-
filename: string,
|
|
68
|
-
isExternal: boolean,
|
|
69
|
-
externalPath: string
|
|
70
|
-
): Promise<ScriptCompileResult> {
|
|
31
|
+
async function compileSetupScript(script: SFCBlock, isTs: boolean, descriptor: SFCDescriptor): Promise<ScriptCompileResult> {
|
|
71
32
|
// 1. Pre-process: Natural Reactivity Transformation ($ syntax)
|
|
72
33
|
// We do this BEFORE extraction so that the rest of the compiler sees standard 'muState' code.
|
|
73
|
-
const transformedContent = transformNaturalReactivity(content, isTs);
|
|
34
|
+
const transformedContent = transformNaturalReactivity(script.content, isTs);
|
|
74
35
|
|
|
75
36
|
// 2. Parse the TRANSFORMED code
|
|
76
37
|
const sourceFile = ts.createSourceFile(
|
|
@@ -158,6 +119,7 @@ async function compileSetupScript(
|
|
|
158
119
|
? `import { ${autoImports.join(', ')} } from '@mulanjs/mulanjs';`
|
|
159
120
|
: '';
|
|
160
121
|
|
|
122
|
+
const filename = descriptor.filename;
|
|
161
123
|
const componentName = filename ? filename.split(/[/\\]/).pop()?.split('.')[0].replace(/\W/g, '') || 'App' : 'App';
|
|
162
124
|
|
|
163
125
|
const bindingString = bindings.length > 0
|
|
@@ -206,7 +168,7 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
206
168
|
// Fix: Remove the //# sourceMappingURL= comment added by TS
|
|
207
169
|
const codeWithoutMapComment = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
|
|
208
170
|
|
|
209
|
-
// Enhance Source Map to show ORIGINAL .mujs content
|
|
171
|
+
// Enhance Source Map to show ORIGINAL .mujs content
|
|
210
172
|
let finalMap = result.sourceMapText;
|
|
211
173
|
if (finalMap) {
|
|
212
174
|
try {
|
|
@@ -214,16 +176,9 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
214
176
|
const preambleLines = preamble.split('\n').length - 1;
|
|
215
177
|
// scriptBody starts at line `preambleLines` (0-indexed) in finalCode.
|
|
216
178
|
|
|
217
|
-
// Determine where script starts
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
let linesBeforeScript = 0;
|
|
221
|
-
let originalSource = content;
|
|
222
|
-
|
|
223
|
-
if (!isExternal) {
|
|
224
|
-
originalSource = descriptor.source;
|
|
225
|
-
linesBeforeScript = originalSource.substring(0, script.start).split('\n').length - 1;
|
|
226
|
-
}
|
|
179
|
+
// Determine where script starts in original .mujs file
|
|
180
|
+
const originalSource = descriptor.source;
|
|
181
|
+
const linesBeforeScript = originalSource.substring(0, script.start).split('\n').length - 1;
|
|
227
182
|
|
|
228
183
|
const consumer = await new SourceMapConsumer(finalMap);
|
|
229
184
|
const generator = new SourceMapGenerator({
|
|
@@ -231,32 +186,13 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
231
186
|
sourceRoot: ''
|
|
232
187
|
});
|
|
233
188
|
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
// Webpack often likes 'webpack:///[resource-path]'
|
|
241
|
-
// We'll try to map it so it looks like a separate file
|
|
242
|
-
const normalized = externalPath.replace(/\\/g, '/');
|
|
243
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
244
|
-
if (srcIdx !== -1) {
|
|
245
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1); // e.g. webpack:///src/logic.ts
|
|
246
|
-
} else {
|
|
247
|
-
relativePath = 'webpack:///' + path.basename(externalPath);
|
|
248
|
-
}
|
|
249
|
-
} else {
|
|
250
|
-
// Inline: Point to .mujs file
|
|
251
|
-
const normalized = filename.replace(/\\/g, '/');
|
|
252
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
253
|
-
if (srcIdx !== -1) {
|
|
254
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1); // e.g. webpack:///src/App.mujs
|
|
255
|
-
}
|
|
256
|
-
}
|
|
189
|
+
// If inside src, use path starting from src. Else just basename.
|
|
190
|
+
const normalizedPath = filename.replace(/\\/g, '/');
|
|
191
|
+
const srcIndex = normalizedPath.indexOf('/src/');
|
|
192
|
+
let relativePath = srcIndex !== -1
|
|
193
|
+
? normalizedPath.substring(srcIndex + 1)
|
|
194
|
+
: filename.split(/[/\\]/).pop() || 'unknown.mujs';
|
|
257
195
|
|
|
258
|
-
// If inline, we set source content of .mujs (the whole file)
|
|
259
|
-
// If external, we set source content of the .ts file
|
|
260
196
|
generator.setSourceContent(relativePath, originalSource);
|
|
261
197
|
|
|
262
198
|
consumer.eachMapping(m => {
|
|
@@ -274,10 +210,9 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
274
210
|
// Calculate offset inside scriptBody
|
|
275
211
|
const lineOffsetInBody = lineInFinalCode - preambleLines; // 1-based offset (1st line of body is 1)
|
|
276
212
|
|
|
277
|
-
// Calculate original line
|
|
278
|
-
// If
|
|
279
|
-
|
|
280
|
-
const originalLine = linesBeforeScript + lineOffsetInBody;
|
|
213
|
+
// Calculate original line in .mujs
|
|
214
|
+
// If lineOffsetInBody is 1 (first line of body), it corresponds to (linesBeforeScript + 1)
|
|
215
|
+
const originalLineInMujs = linesBeforeScript + lineOffsetInBody;
|
|
281
216
|
|
|
282
217
|
// Add mapping
|
|
283
218
|
generator.addMapping({
|
|
@@ -286,7 +221,7 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
286
221
|
column: m.generatedColumn
|
|
287
222
|
},
|
|
288
223
|
original: {
|
|
289
|
-
line:
|
|
224
|
+
line: originalLineInMujs,
|
|
290
225
|
column: m.originalColumn // Column should be preserved if we didn't change indentation
|
|
291
226
|
},
|
|
292
227
|
source: relativePath,
|
|
@@ -319,109 +254,6 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
|
|
|
319
254
|
};
|
|
320
255
|
}
|
|
321
256
|
|
|
322
|
-
async function compileStandardScript(
|
|
323
|
-
script: SFCBlock,
|
|
324
|
-
descriptor: SFCDescriptor,
|
|
325
|
-
content: string,
|
|
326
|
-
filename: string,
|
|
327
|
-
isExternal: boolean,
|
|
328
|
-
externalPath: string
|
|
329
|
-
): Promise<ScriptCompileResult> {
|
|
330
|
-
|
|
331
|
-
// For standard script, we also might need source map if we transpiled it (e.g. from TS)
|
|
332
|
-
// and/or just to map it back to .mujs
|
|
333
|
-
|
|
334
|
-
let finalMap: string | undefined;
|
|
335
|
-
let code = content;
|
|
336
|
-
|
|
337
|
-
// Transpile if TS
|
|
338
|
-
if (externalPath.endsWith('.ts') || script.attrs.lang === 'ts') {
|
|
339
|
-
const result = ts.transpileModule(content, {
|
|
340
|
-
compilerOptions: {
|
|
341
|
-
module: ts.ModuleKind.ESNext,
|
|
342
|
-
target: ts.ScriptTarget.ES2019,
|
|
343
|
-
sourceMap: true,
|
|
344
|
-
inlineSources: true,
|
|
345
|
-
},
|
|
346
|
-
fileName: filename
|
|
347
|
-
});
|
|
348
|
-
code = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
|
|
349
|
-
finalMap = result.sourceMapText;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// Adjust Source Map
|
|
353
|
-
if (finalMap) {
|
|
354
|
-
try {
|
|
355
|
-
// Standard script content is usually 1:1 with source lines if we ignore transpilation shifts (which TS handles)
|
|
356
|
-
// But if it's inline, TS thinks it starts at line 0. We need to shift it by `linesBeforeScript`.
|
|
357
|
-
|
|
358
|
-
let linesBeforeScript = 0;
|
|
359
|
-
let originalSource = content;
|
|
360
|
-
|
|
361
|
-
if (!isExternal) {
|
|
362
|
-
originalSource = descriptor.source;
|
|
363
|
-
linesBeforeScript = descriptor.source.substring(0, script.start).split('\n').length - 1;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const consumer = await new SourceMapConsumer(finalMap);
|
|
367
|
-
const generator = new SourceMapGenerator({
|
|
368
|
-
file: filename,
|
|
369
|
-
sourceRoot: ''
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// Determine relative path for source (same logic as Setup)
|
|
373
|
-
let relativePath = filename.split(/[/\\]/).pop() || 'unknown.mujs';
|
|
374
|
-
|
|
375
|
-
if (isExternal) {
|
|
376
|
-
const normalized = externalPath.replace(/\\/g, '/');
|
|
377
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
378
|
-
if (srcIdx !== -1) {
|
|
379
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1);
|
|
380
|
-
} else {
|
|
381
|
-
relativePath = 'webpack:///' + path.basename(externalPath);
|
|
382
|
-
}
|
|
383
|
-
} else {
|
|
384
|
-
const normalized = filename.replace(/\\/g, '/');
|
|
385
|
-
const srcIdx = normalized.indexOf('/src/');
|
|
386
|
-
if (srcIdx !== -1) {
|
|
387
|
-
relativePath = 'webpack:///' + normalized.substring(srcIdx + 1);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
generator.setSourceContent(relativePath, originalSource);
|
|
392
|
-
|
|
393
|
-
consumer.eachMapping(m => {
|
|
394
|
-
if (m.originalLine === null) return;
|
|
395
|
-
|
|
396
|
-
const originalLine = m.originalLine + linesBeforeScript;
|
|
397
|
-
|
|
398
|
-
generator.addMapping({
|
|
399
|
-
generated: {
|
|
400
|
-
line: m.generatedLine,
|
|
401
|
-
column: m.generatedColumn
|
|
402
|
-
},
|
|
403
|
-
original: {
|
|
404
|
-
line: originalLine,
|
|
405
|
-
column: m.originalColumn
|
|
406
|
-
},
|
|
407
|
-
source: relativePath,
|
|
408
|
-
name: m.name
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
finalMap = generator.toString();
|
|
413
|
-
consumer.destroy();
|
|
414
|
-
|
|
415
|
-
} catch (e) { /* ignore */ }
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
return {
|
|
419
|
-
code: code,
|
|
420
|
-
errors: [],
|
|
421
|
-
map: finalMap
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
|
|
425
257
|
// --- Natural Reactivity Transformer ---
|
|
426
258
|
|
|
427
259
|
function transformNaturalReactivity(code: string, isTs: boolean): string {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import * as path from 'path';
|
|
3
2
|
import { SFCDescriptor } from './sfc-parser';
|
|
4
|
-
import { CompilerOptions } from './script-compiler'; // Re-use options interface
|
|
5
3
|
|
|
6
4
|
export interface StyleCompileResult {
|
|
7
5
|
css: string;
|
|
@@ -9,7 +7,7 @@ export interface StyleCompileResult {
|
|
|
9
7
|
errors: string[];
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
export function compileStyle(descriptor: SFCDescriptor, filename: string
|
|
10
|
+
export function compileStyle(descriptor: SFCDescriptor, filename: string): StyleCompileResult {
|
|
13
11
|
const styles = descriptor.styles;
|
|
14
12
|
if (!styles || styles.length === 0) return { css: '', errors: [] };
|
|
15
13
|
|
|
@@ -19,39 +17,17 @@ export function compileStyle(descriptor: SFCDescriptor, filename: string, option
|
|
|
19
17
|
styles.forEach(style => {
|
|
20
18
|
let content = style.content;
|
|
21
19
|
|
|
22
|
-
// Handle External Style
|
|
23
|
-
if (style.attrs.src) {
|
|
24
|
-
if (options && options.readFileSync) {
|
|
25
|
-
try {
|
|
26
|
-
const sfcDir = path.dirname(filename);
|
|
27
|
-
const externalPath = path.resolve(sfcDir, style.attrs.src);
|
|
28
|
-
content = options.readFileSync(externalPath);
|
|
29
|
-
} catch (e: any) {
|
|
30
|
-
// Start error with "Style Compilation Error" to be consistent? Or just add to errors list.
|
|
31
|
-
// But we are inside forEach, so we can't return easily. We'll add a comment in CSS or log.
|
|
32
|
-
// Better to collect errors.
|
|
33
|
-
// But function signature returns { errors: string[] }.
|
|
34
|
-
// We'll just append to errors? But we need to change forEach loop or use functional approach.
|
|
35
|
-
// For now, let's just ignore/log in CSS content?
|
|
36
|
-
content = `/* Failed to read external style: ${e.message} */`;
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
content = `/* External style found but no readFileSync option provided */`;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
20
|
if (style.attrs.scoped) {
|
|
44
21
|
// SCSS Compilation
|
|
45
|
-
if (style.attrs.lang === 'scss' || style.attrs.lang === 'sass'
|
|
22
|
+
if (style.attrs.lang === 'scss' || style.attrs.lang === 'sass') {
|
|
46
23
|
try {
|
|
47
24
|
const sass = require('sass');
|
|
48
25
|
const result = sass.compileString(content, {
|
|
49
|
-
syntax:
|
|
26
|
+
syntax: style.attrs.lang === 'sass' ? 'indented' : 'scss'
|
|
50
27
|
});
|
|
51
28
|
content = result.css;
|
|
52
29
|
} catch (e: any) {
|
|
53
|
-
|
|
54
|
-
content = `/* SCSS Compilation Error: ${e.message} */`;
|
|
30
|
+
return { css: '', errors: [`SCSS Compilation Error: ${e.message}`] };
|
|
55
31
|
}
|
|
56
32
|
}
|
|
57
33
|
|
|
@@ -34,12 +34,9 @@ interface InterpolationNode extends BaseNode {
|
|
|
34
34
|
|
|
35
35
|
// --- Compile Function ---
|
|
36
36
|
|
|
37
|
-
import { SourceMapGenerator } from 'source-map';
|
|
38
|
-
|
|
39
37
|
export interface TemplateCompileResult {
|
|
40
38
|
code: string;
|
|
41
39
|
errors: string[];
|
|
42
|
-
map?: string;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
export function compileTemplate(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): TemplateCompileResult {
|
|
@@ -59,7 +56,8 @@ export function compileTemplate(descriptor: SFCDescriptor, scriptResult: ScriptC
|
|
|
59
56
|
// 3. Codegen Phase (AST -> JS Function)
|
|
60
57
|
const code = generate(ast, scriptResult.bindings || []);
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
return {
|
|
60
|
+
code: `function render() {
|
|
63
61
|
const _s = (v) => (v && typeof v === 'object' && 'value' in v) ? v.value : v;
|
|
64
62
|
const _h = (v, raw) => {
|
|
65
63
|
const val = _s(v);
|
|
@@ -71,54 +69,8 @@ export function compileTemplate(descriptor: SFCDescriptor, scriptResult: ScriptC
|
|
|
71
69
|
return val.replace(/</g, '<').replace(/>/g, '>');
|
|
72
70
|
};
|
|
73
71
|
return ${code};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// 4. Source Map Generation
|
|
77
|
-
// We map the entire render function to the starting line of the template in the source file
|
|
78
|
-
let map = undefined;
|
|
79
|
-
if (descriptor.filename) {
|
|
80
|
-
const generator = new SourceMapGenerator({
|
|
81
|
-
file: descriptor.filename + '.template.js', // Virtual file name
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Find start line of template in original source
|
|
85
|
-
// descriptor.template.start is the index of content start
|
|
86
|
-
const sourceBefore = descriptor.source.substring(0, template.start);
|
|
87
|
-
const startLine = sourceBefore.split(/\r?\n/).length;
|
|
88
|
-
|
|
89
|
-
// Simple mapping: One-to-one mapping isn't easy without AST location tracking
|
|
90
|
-
// But we can map the 'return' statement to the template start
|
|
91
|
-
// The render function has about 12 lines of preamble.
|
|
92
|
-
// Let's just mapping the whole block to the start line for now.
|
|
93
|
-
// This ensures the file shows up in devtools.
|
|
94
|
-
generator.addMapping({
|
|
95
|
-
generated: { line: 1, column: 0 },
|
|
96
|
-
source: descriptor.filename, // This should be the .mujs file path
|
|
97
|
-
original: { line: startLine, column: 0 }
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Also map the return line (approx line 12)
|
|
101
|
-
generator.addMapping({
|
|
102
|
-
generated: { line: 12, column: 0 },
|
|
103
|
-
source: descriptor.filename,
|
|
104
|
-
original: { line: startLine, column: 0 }
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// We must include the "source content" so DevTools can display it!
|
|
108
|
-
// IMPORTANT: We want the FULL .mujs content here, so it matches what Webpack sees?
|
|
109
|
-
// Actually, if we use the same filename as script-compiler, they might conflict or merge.
|
|
110
|
-
// Script compiler uses 'webpack:///...'
|
|
111
|
-
// Let's use the same convention.
|
|
112
|
-
// But we just use the filename here, compiler.ts will handle the final path adjustment if needed.
|
|
113
|
-
generator.setSourceContent(descriptor.filename, descriptor.source);
|
|
114
|
-
|
|
115
|
-
map = generator.toString();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
code: renderFn,
|
|
120
|
-
errors,
|
|
121
|
-
map
|
|
72
|
+
}`,
|
|
73
|
+
errors
|
|
122
74
|
};
|
|
123
75
|
}
|
|
124
76
|
|
package/src/loader/index.js
CHANGED
|
@@ -9,20 +9,45 @@ module.exports = function (content) {
|
|
|
9
9
|
const context = this.context || path.dirname(filePath);
|
|
10
10
|
|
|
11
11
|
// --- External File Processor ---
|
|
12
|
-
//
|
|
13
|
-
|
|
12
|
+
// Inlines <script src="..."> and <style src="..."> before compilation
|
|
13
|
+
const inlineExternalFiles = (source) => {
|
|
14
|
+
// Regex to match <tag ... src="...">... </tag> or <tag ... src="..." />
|
|
15
|
+
// Captures: 1=TagName, 2=Attributes before src, 3=Quote, 4=Path, 5=Attributes after src
|
|
16
|
+
const tagRegex = /<(script|style)([\s\S]*?)src=(["'])(.*?)\3([\s\S]*?)>(?:<\/\1>|\s*\/>)?/gi;
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
return source.replace(tagRegex, (match, tagName, attrsBefore, quote, srcPath, attrsAfter) => {
|
|
19
|
+
try {
|
|
20
|
+
// Resolve path
|
|
21
|
+
const absolutePath = path.resolve(context, srcPath);
|
|
22
|
+
|
|
23
|
+
// Add as dependency for Webpack HMR/Watch
|
|
24
|
+
this.addDependency(absolutePath);
|
|
25
|
+
|
|
26
|
+
// Read content
|
|
27
|
+
const externalContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
28
|
+
|
|
29
|
+
console.log(`[MulanJS Loader] Inlined external ${tagName}: ${srcPath}`);
|
|
30
|
+
|
|
31
|
+
// Auto-detect TypeScript env
|
|
32
|
+
let finalAttrs = attrsBefore + attrsAfter;
|
|
33
|
+
if (srcPath.endsWith('.ts') && !finalAttrs.includes('lang=')) {
|
|
34
|
+
finalAttrs += ' lang="ts"';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Return new tag with content, removing the src attribute
|
|
38
|
+
return `<${tagName}${finalAttrs}>\n${externalContent}\n</${tagName}>`;
|
|
39
|
+
} catch (err) {
|
|
40
|
+
this.emitError(new Error(`[MulanJS Loader] Failed to load external file: ${srcPath}\n${err.message}`));
|
|
41
|
+
return match; // Return original on error to likely fail later or show error
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
content = inlineExternalFiles(content);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
callback(e);
|
|
50
|
+
return;
|
|
26
51
|
}
|
|
27
52
|
|
|
28
53
|
// Resolve path to the compiled compiler module (ESM)
|
|
@@ -32,12 +57,12 @@ module.exports = function (content) {
|
|
|
32
57
|
const compilerRef = path.resolve(__dirname, '../../dist/compiler/compiler.js');
|
|
33
58
|
const compilerUrl = pathToFileURL(compilerRef).href;
|
|
34
59
|
|
|
60
|
+
// Use dynamic import to load ESM compiler from CommonJS loader
|
|
61
|
+
|
|
35
62
|
// Use dynamic import to load ESM compiler from CommonJS loader
|
|
36
63
|
import(compilerUrl).then(async compiler => {
|
|
37
64
|
try {
|
|
38
|
-
const result = await compiler.compileSFC(content, filePath
|
|
39
|
-
readFileSync: (file) => fs.readFileSync(file, 'utf-8')
|
|
40
|
-
});
|
|
65
|
+
const result = await compiler.compileSFC(content, filePath);
|
|
41
66
|
|
|
42
67
|
if (result.errors && result.errors.length > 0) {
|
|
43
68
|
this.emitError(new Error(result.errors.join('\n')));
|