@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.
@@ -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
- const source_map_1 = require("source-map");
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, options);
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, options);
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: mergedMap || scriptResult.map // Fallback to script map if merge fails
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
- const path = __importStar(require("path"));
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' || externalPath.endsWith('.ts') || externalPath.endsWith('.tsx');
44
+ const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
70
45
  if (isSetup) {
71
- return await compileSetupScript(script, isTs, descriptor, content, filename, isExternal, externalPath);
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, content, filename, isExternal, externalPath) {
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 or EXTERNAL 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
- // If External: Start at 0
210
- // If Inline: Start at script.start line
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
- // Determine relative path for source
223
- let relativePath = filename.split(/[/\\]/).pop() || 'unknown.mujs'; // Default to basename
224
- if (isExternal) {
225
- // For external files, we want Source Map to point to the actual TS file
226
- // externalPath is absolute. Make it relative to project root or something reasonable?
227
- // Webpack often likes 'webpack:///[resource-path]'
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 External: lineOffsetInBody is the line number
263
- // If Inline: linesBeforeScript + lineOffsetInBody
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: originalLine,
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
- const path = __importStar(require("path"));
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' || (style.attrs.src && (style.attrs.src.endsWith('.scss') || style.attrs.src.endsWith('.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: (style.attrs.lang === 'sass' || (style.attrs.src && style.attrs.src.endsWith('.sass'))) ? 'indented' : 'scss'
18
+ syntax: style.attrs.lang === 'sass' ? 'indented' : 'scss'
75
19
  });
76
20
  content = result.css;
77
21
  }
78
22
  catch (e) {
79
- // return { css: '', errors: [`SCSS Compilation Error: ${e.message}`] };
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
- const renderFn = `function render() {
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, '&lt;').replace(/>/g, '&gt;');
29
28
  };
30
29
  return ${code};
31
- }`;
32
- // 4. Source Map Generation
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, options?: CompilerOptions): Promise<CompileResult>;
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 interface CompilerOptions {
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, options?: CompilerOptions): StyleCompileResult;
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;
@@ -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, options?: CompilerOptions): Promise<CompileResult>;
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 interface CompilerOptions {
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, options?: CompilerOptions): StyleCompileResult;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulanjs/mulanjs",
3
- "version": "1.0.1-dev.20260219182444",
3
+ "version": "1.0.1-dev.20260219221500",
4
4
  "description": "A powerful, secure, and enterprise-grade JavaScript framework.",
5
5
  "main": "dist/mulan.js",
6
6
  "module": "dist/mulan.esm.js",
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { parseMUJS } from './sfc-parser';
3
- import { compileScript, CompilerOptions } from './script-compiler';
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
- import { SourceMapGenerator, SourceMapConsumer } from 'source-map';
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, options);
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, options);
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: mergedMap || scriptResult.map // Fallback to script map if merge fails
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 interface CompilerOptions {
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' || externalPath.endsWith('.ts') || externalPath.endsWith('.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, content, filename, isExternal, externalPath);
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 or EXTERNAL 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
- // If External: Start at 0
219
- // If Inline: Start at script.start line
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
- // Determine relative path for source
235
- let relativePath = filename.split(/[/\\]/).pop() || 'unknown.mujs'; // Default to basename
236
-
237
- if (isExternal) {
238
- // For external files, we want Source Map to point to the actual TS file
239
- // externalPath is absolute. Make it relative to project root or something reasonable?
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 External: lineOffsetInBody is the line number
279
- // If Inline: linesBeforeScript + lineOffsetInBody
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: originalLine,
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, options?: CompilerOptions): StyleCompileResult {
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' || (style.attrs.src && (style.attrs.src.endsWith('.scss') || style.attrs.src.endsWith('.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: (style.attrs.lang === 'sass' || (style.attrs.src && style.attrs.src.endsWith('.sass'))) ? 'indented' : 'scss'
26
+ syntax: style.attrs.lang === 'sass' ? 'indented' : 'scss'
50
27
  });
51
28
  content = result.css;
52
29
  } catch (e: any) {
53
- // return { css: '', errors: [`SCSS Compilation Error: ${e.message}`] };
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
- const renderFn = `function render() {
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, '&lt;').replace(/>/g, '&gt;');
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
 
@@ -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
- // REMOVED: Inlining is now handled by the compiler to support correct source maps.
13
- // We still scan for dependencies.
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
- // Regex to match <tag ... src="...">... </tag> or <tag ... src="..." /> for DEPENDENCY TRACKING ONLY
16
- const tagRegex = /<(script|style)([\s\S]*?)src=(["'])(.*?)\3/gi;
17
- let match;
18
- while ((match = tagRegex.exec(content)) !== null) {
19
- try {
20
- const srcPath = match[4];
21
- const absolutePath = path.resolve(context, srcPath);
22
- this.addDependency(absolutePath);
23
- } catch (e) {
24
- // Ignore resolution errors here, compiler will handle them
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')));