@mulanjs/mulanjs 1.0.1-dev.20260219221500 → 1.0.1-dev.20260220121828

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/README.md CHANGED
@@ -16,7 +16,6 @@ MulanJS is a high-performance, next-generation web framework designed to bridge
16
16
  > Coming soon!
17
17
 
18
18
  ### 🚧 Development Release (Bleeding Edge)
19
- **Current Version**: `1.0.1-dev.20260219221500`
20
19
 
21
20
  Want to try the latest features before they are stable? Use the development build.
22
21
 
@@ -33,7 +32,7 @@ npm install @mulanjs/mulanjs@dev
33
32
  ## ⚡ Core Features
34
33
 
35
34
  * **Signal Reactivity Engine** ⚡: Fine-grained reactivity that tracks exact dependencies, eliminating the need for a Virtual DOM.
36
- * **ASTR-Q+ Engine** 🌌: Simulated quantum registers for probabilistic state management and complex logic branching.
35
+ * **ASTR-Q+ Engine** 🌌: Simulated quantum registers with **Quantum Loops** (`muParallel`, `muSwitch`) for complex probabilistic branching.
37
36
  * **Iron Fortress Security** 🛡️: Built-in "Iron Fortress" security module that auto-sanitizes inputs and generates CSP headers.
38
37
  * **Mulan Cycle** 🔄: A redefined component lifecycle (Init, Mount, Destroy) for predictable state management.
39
38
  * **TypeScript First** 🟦: Built from the ground up with TypeScript for world-class type safety.
@@ -57,6 +56,9 @@ State that survives refreshes using `muVault`. Your data, secured in the Iron Fo
57
56
  ### Mulan Router (Time Travel) 🧭
58
57
  Zero-config hash routing with `<mu-link>` for instant, single-page navigation.
59
58
 
59
+ ### Mulan Surge (High Performance) 🚀
60
+ Adaptive non-blocking loops (`muBurst`) and true multicore parallelism (`muSurge`) to handle billions of records at 60fps.
61
+
60
62
  ### Native SCSS Support 🎨
61
63
  Built-in support for `<style lang="scss">`. No config required. Just design.
62
64
  ## 🚀 Quick Start Example
@@ -5,11 +5,12 @@ 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
- async function compileSFC(source, filename) {
8
+ const source_map_1 = require("source-map");
9
+ async function compileSFC(source, filename, options) {
9
10
  // 1. Parse
10
11
  const descriptor = (0, sfc_parser_1.parseMUJS)(source, filename);
11
12
  // 2. Script
12
- const scriptResult = await (0, script_compiler_1.compileScript)(descriptor);
13
+ const scriptResult = await (0, script_compiler_1.compileScript)(descriptor, options);
13
14
  // Replace export default to capture the component
14
15
  // If the script already has 'export default', we intercept it.
15
16
  let scriptCode = scriptResult.code.replace('export default', 'const __component__ =');
@@ -17,9 +18,85 @@ async function compileSFC(source, filename) {
17
18
  scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
18
19
  }
19
20
  // 3. Style
20
- const styleResult = (0, style_compiler_1.compileStyle)(descriptor, filename);
21
+ const styleResult = (0, style_compiler_1.compileStyle)(descriptor, filename, options);
21
22
  // 4. Template
22
23
  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
+ }
23
100
  // 5. Assembly
24
101
  const cssInjection = styleResult.css ? `
25
102
  if (typeof document !== 'undefined') {
@@ -86,6 +163,6 @@ export default __component__;
86
163
  code: finalCode,
87
164
  css: styleResult.css,
88
165
  errors: [...scriptResult.errors, ...templateResult.errors],
89
- map: scriptResult.map // Pass the map
166
+ map: mergedMap || scriptResult.map // Fallback to script map if merge fails
90
167
  };
91
168
  }
@@ -36,27 +36,55 @@ 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
- async function compileScript(descriptor) {
39
+ const path = __importStar(require("path"));
40
+ async function compileScript(descriptor, options) {
40
41
  const script = descriptor.script;
41
42
  if (!script)
42
43
  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
+ }
43
68
  const isSetup = !!script.attrs.setup;
44
- const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
69
+ const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx' || externalPath.endsWith('.ts') || externalPath.endsWith('.tsx');
45
70
  if (isSetup) {
46
- return await compileSetupScript(script, isTs, descriptor);
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);
47
75
  }
48
76
  else {
49
77
  // Standard Options API - just pass through, assuming it has export default
50
- return { code: script.content, errors: [] };
78
+ return { code: content, errors: [] };
51
79
  }
52
80
  }
53
81
  // Global list of Mulan hooks for auto-imports and reactivity exemption
54
82
  const COMMON_HOOKS = ['muState', 'onMuMount', 'onMuInit', 'onMuDestroy', 'muEffect', 'muMemo', 'muQubit', 'muGate', 'muMeasure', 'muRegister', 'muEntangle', 'persistent'];
55
- async function compileSetupScript(script, isTs, descriptor) {
83
+ async function compileSetupScript(script, isTs, descriptor, content, filename, isExternal, externalPath) {
56
84
  var _a;
57
85
  // 1. Pre-process: Natural Reactivity Transformation ($ syntax)
58
86
  // We do this BEFORE extraction so that the rest of the compiler sees standard 'muState' code.
59
- const transformedContent = transformNaturalReactivity(script.content, isTs);
87
+ const transformedContent = transformNaturalReactivity(content, isTs);
60
88
  // 2. Parse the TRANSFORMED code
61
89
  const sourceFile = ts.createSourceFile('script.' + (isTs ? 'ts' : 'js'), transformedContent, ts.ScriptTarget.Latest, true);
62
90
  const imports = [];
@@ -129,7 +157,6 @@ async function compileSetupScript(script, isTs, descriptor) {
129
157
  const autoImportStmt = autoImports.length > 0
130
158
  ? `import { ${autoImports.join(', ')} } from '@mulanjs/mulanjs';`
131
159
  : '';
132
- const filename = descriptor.filename;
133
160
  const componentName = filename ? ((_a = filename.split(/[/\\]/).pop()) === null || _a === void 0 ? void 0 : _a.split('.')[0].replace(/\W/g, '')) || 'App' : 'App';
134
161
  const bindingString = bindings.length > 0
135
162
  ? `
@@ -171,27 +198,53 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
171
198
  });
172
199
  // Fix: Remove the //# sourceMappingURL= comment added by TS
173
200
  const codeWithoutMapComment = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
174
- // Enhance Source Map to show ORIGINAL .mujs content
201
+ // Enhance Source Map to show ORIGINAL .mujs content or EXTERNAL content
175
202
  let finalMap = result.sourceMapText;
176
203
  if (finalMap) {
177
204
  try {
178
205
  // Determine offsets
179
206
  const preambleLines = preamble.split('\n').length - 1;
180
207
  // scriptBody starts at line `preambleLines` (0-indexed) in finalCode.
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;
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
+ }
184
217
  const consumer = await new source_map_1.SourceMapConsumer(finalMap);
185
218
  const generator = new source_map_1.SourceMapGenerator({
186
219
  file: filename,
187
220
  sourceRoot: ''
188
221
  });
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';
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
195
248
  generator.setSourceContent(relativePath, originalSource);
196
249
  consumer.eachMapping(m => {
197
250
  if (m.originalLine === null)
@@ -205,9 +258,10 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
205
258
  if (lineInFinalCode > preambleLines) {
206
259
  // Calculate offset inside scriptBody
207
260
  const lineOffsetInBody = lineInFinalCode - preambleLines; // 1-based offset (1st line of body is 1)
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;
261
+ // Calculate original line
262
+ // If External: lineOffsetInBody is the line number
263
+ // If Inline: linesBeforeScript + lineOffsetInBody
264
+ const originalLine = linesBeforeScript + lineOffsetInBody;
211
265
  // Add mapping
212
266
  generator.addMapping({
213
267
  generated: {
@@ -215,7 +269,7 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
215
269
  column: m.generatedColumn
216
270
  },
217
271
  original: {
218
- line: originalLineInMujs,
272
+ line: originalLine,
219
273
  column: m.originalColumn // Column should be preserved if we didn't change indentation
220
274
  },
221
275
  source: relativePath,
@@ -244,6 +298,89 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
244
298
  map: undefined
245
299
  };
246
300
  }
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
+ }
247
384
  // --- Natural Reactivity Transformer ---
248
385
  function transformNaturalReactivity(code, isTs) {
249
386
  // If no '$(' or '$q(' or '$qr(' usage, return original code for safety/speed
@@ -1,7 +1,41 @@
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
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.compileStyle = compileStyle;
4
- function compileStyle(descriptor, filename) {
37
+ const path = __importStar(require("path"));
38
+ function compileStyle(descriptor, filename, options) {
5
39
  const styles = descriptor.styles;
6
40
  if (!styles || styles.length === 0)
7
41
  return { css: '', errors: [] };
@@ -9,18 +43,41 @@ function compileStyle(descriptor, filename) {
9
43
  const scopedId = 'data-v-' + hash(filename);
10
44
  styles.forEach(style => {
11
45
  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
+ }
12
68
  if (style.attrs.scoped) {
13
69
  // SCSS Compilation
14
- if (style.attrs.lang === 'scss' || style.attrs.lang === 'sass') {
70
+ if (style.attrs.lang === 'scss' || style.attrs.lang === 'sass' || (style.attrs.src && (style.attrs.src.endsWith('.scss') || style.attrs.src.endsWith('.sass')))) {
15
71
  try {
16
72
  const sass = require('sass');
17
73
  const result = sass.compileString(content, {
18
- syntax: style.attrs.lang === 'sass' ? 'indented' : 'scss'
74
+ syntax: (style.attrs.lang === 'sass' || (style.attrs.src && style.attrs.src.endsWith('.sass'))) ? 'indented' : 'scss'
19
75
  });
20
76
  content = result.css;
21
77
  }
22
78
  catch (e) {
23
- return { css: '', errors: [`SCSS Compilation Error: ${e.message}`] };
79
+ // return { css: '', errors: [`SCSS Compilation Error: ${e.message}`] };
80
+ content = `/* SCSS Compilation Error: ${e.message} */`;
24
81
  }
25
82
  }
26
83
  // Rewrite selectors
@@ -1,6 +1,8 @@
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");
4
6
  function compileTemplate(descriptor, scriptResult, scopedId) {
5
7
  console.log(`[MulanJS Compiler v1.0.1-dev.2] Compiling template for: ${descriptor.filename || 'anonymous'}`);
6
8
  const template = descriptor.template;
@@ -14,8 +16,7 @@ function compileTemplate(descriptor, scriptResult, scopedId) {
14
16
  transform(ast, scriptResult, scopedId, [], descriptor.filename);
15
17
  // 3. Codegen Phase (AST -> JS Function)
16
18
  const code = generate(ast, scriptResult.bindings || []);
17
- return {
18
- code: `function render() {
19
+ const renderFn = `function render() {
19
20
  const _s = (v) => (v && typeof v === 'object' && 'value' in v) ? v.value : v;
20
21
  const _h = (v, raw) => {
21
22
  const val = _s(v);
@@ -27,8 +28,47 @@ function compileTemplate(descriptor, scriptResult, scopedId) {
27
28
  return val.replace(/</g, '&lt;').replace(/>/g, '&gt;');
28
29
  };
29
30
  return ${code};
30
- }`,
31
- errors
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
32
72
  };
33
73
  }
34
74
  // --- Parser (Recursive Descent) ---