@mulanjs/mulanjs 1.0.1-dev.20260218170441 → 1.0.1-dev.20260219055338

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,13 @@ 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
- function compileSFC(source, filename) {
8
+ async function compileSFC(source, filename) {
9
9
  // 1. Parse
10
10
  const descriptor = (0, sfc_parser_1.parseMUJS)(source, filename);
11
11
  // 2. Script
12
- const scriptResult = (0, script_compiler_1.compileScript)(descriptor);
12
+ const scriptResult = await (0, script_compiler_1.compileScript)(descriptor);
13
13
  // Replace export default to capture the component
14
+ // If the script already has 'export default', we intercept it.
14
15
  let scriptCode = scriptResult.code.replace('export default', 'const __component__ =');
15
16
  if (!scriptCode.includes('const __component__ =')) {
16
17
  scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
@@ -35,14 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.compileScript = compileScript;
37
37
  const ts = __importStar(require("typescript"));
38
- function compileScript(descriptor) {
38
+ const source_map_1 = require("source-map");
39
+ async function compileScript(descriptor) {
39
40
  const script = descriptor.script;
40
41
  if (!script)
41
42
  return { code: 'export default {}', errors: [] };
42
43
  const isSetup = !!script.attrs.setup;
43
44
  const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
44
45
  if (isSetup) {
45
- return compileSetupScript(script, isTs, descriptor);
46
+ return await compileSetupScript(script, isTs, descriptor);
46
47
  }
47
48
  else {
48
49
  // Standard Options API - just pass through, assuming it has export default
@@ -51,7 +52,7 @@ function compileScript(descriptor) {
51
52
  }
52
53
  // Global list of Mulan hooks for auto-imports and reactivity exemption
53
54
  const COMMON_HOOKS = ['muState', 'onMuMount', 'onMuInit', 'onMuDestroy', 'muEffect', 'muMemo', 'muQubit', 'muGate', 'muMeasure', 'muRegister', 'muEntangle', 'persistent'];
54
- function compileSetupScript(script, isTs, descriptor) {
55
+ async function compileSetupScript(script, isTs, descriptor) {
55
56
  var _a;
56
57
  // 1. Pre-process: Natural Reactivity Transformation ($ syntax)
57
58
  // We do this BEFORE extraction so that the rest of the compiler sees standard 'muState' code.
@@ -59,14 +60,31 @@ function compileSetupScript(script, isTs, descriptor) {
59
60
  // 2. Parse the TRANSFORMED code
60
61
  const sourceFile = ts.createSourceFile('script.' + (isTs ? 'ts' : 'js'), transformedContent, ts.ScriptTarget.Latest, true);
61
62
  const imports = [];
62
- const statements = [];
63
+ // We will build the body by replacing imports with newlines in the original content
64
+ // This preserves line numbers for the user's code relative to the script block start.
65
+ let scriptBody = transformedContent;
66
+ // Collect imports and bindings
63
67
  const bindings = [];
68
+ // We need to remove imports from scriptBody but keep newlines.
69
+ // We'll use a replacement list and apply it end-to-start to avoid index shifting
70
+ const replacements = [];
64
71
  sourceFile.statements.forEach(stmt => {
65
72
  if (ts.isImportDeclaration(stmt)) {
66
- imports.push(transformedContent.substring(stmt.pos, stmt.end).trim());
73
+ const importText = transformedContent.substring(stmt.pos, stmt.end);
74
+ // Add to imports list (trimmed for cleanliness in preamble)
75
+ imports.push(importText.trim());
76
+ // Calculate number of newlines in this import statement to preserve layout
77
+ const numNewlines = (importText.match(/\n/g) || []).length;
78
+ const replacement = '\n'.repeat(numNewlines) + ' '.repeat(importText.length - numNewlines); // Keep length/lines roughly same? actually just newlines is enough for line mapping
79
+ // Better: we just want to preserve LINE COUNT.
80
+ // If we replace the whole range with newlines, we keep the vertical layout.
81
+ replacements.push({
82
+ start: stmt.pos,
83
+ end: stmt.end,
84
+ text: '\n'.repeat(numNewlines) || ' ' // at least a space if no newlines, to avoid merging lines?
85
+ });
67
86
  }
68
87
  else {
69
- statements.push(transformedContent.substring(stmt.pos, stmt.end).trim());
70
88
  // Extract top-level declarations for template exposure
71
89
  if (ts.isVariableStatement(stmt)) {
72
90
  stmt.declarationList.declarations.forEach(decl => {
@@ -83,16 +101,21 @@ function compileSetupScript(script, isTs, descriptor) {
83
101
  }
84
102
  }
85
103
  });
104
+ // Apply replacements (reverse order)
105
+ replacements.sort((a, b) => b.start - a.start);
106
+ replacements.forEach(r => {
107
+ scriptBody = scriptBody.substring(0, r.start) + r.text + scriptBody.substring(r.end);
108
+ });
86
109
  // Reconstruct
87
- // We import defineComponent from mulanjs if not present?
110
+ // We import defineComponent from @mulanjs/mulanjs if not present?
88
111
  // Ideally user imports what they need, but for 'setup' wrapping we need defineComponent.
89
112
  // Let's assume user might not have imported it, so we import it as _defineComponent to avoid collision.
90
113
  // Check if defineComponent is imported
91
114
  const hasDefineComponent = imports.some(i => i.includes('defineComponent'));
92
- const bootImports = hasDefineComponent ? '' : `import { defineComponent as _defineComponent } from 'mulanjs';`;
115
+ const bootImports = hasDefineComponent ? '' : `import { defineComponent as _defineComponent } from '@mulanjs/mulanjs';`;
93
116
  // ADDED: Check if we introduced 'muState' but it wasn't imported (because user used $)
94
117
  const usesMuState = transformedContent.includes('muState');
95
- const hasMuStateImport = imports.some(i => i.includes('muState') || i.includes('mulanjs'));
118
+ const hasMuStateImport = imports.some(i => i.includes('muState') || i.includes('@mulanjs/mulanjs'));
96
119
  let autoImports = (usesMuState && !hasMuStateImport) ? [`muState`] : [];
97
120
  // Auto-import common hooks
98
121
  COMMON_HOOKS.forEach(hook => {
@@ -104,7 +127,7 @@ function compileSetupScript(script, isTs, descriptor) {
104
127
  }
105
128
  });
106
129
  const autoImportStmt = autoImports.length > 0
107
- ? `import { ${autoImports.join(', ')} } from 'mulanjs';`
130
+ ? `import { ${autoImports.join(', ')} } from '@mulanjs/mulanjs';`
108
131
  : '';
109
132
  const filename = descriptor.filename;
110
133
  const componentName = filename ? ((_a = filename.split(/[/\\]/).pop()) === null || _a === void 0 ? void 0 : _a.split('.')[0].replace(/\W/g, '')) || 'App' : 'App';
@@ -117,29 +140,24 @@ function compileSetupScript(script, isTs, descriptor) {
117
140
  return exposed;
118
141
  `
119
142
  : 'return {};';
120
- const finalCode = `
143
+ // Construct final code with preserved body layout
144
+ const preamble = `
121
145
  ${bootImports}
122
146
  ${autoImportStmt}
123
147
  ${imports.join('\n')}
124
148
 
125
149
  export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
126
150
  setup() {
127
- ${statements.join('\n')}
151
+ `;
152
+ // We intentionally put scriptBody on a new line.
153
+ // Note: scriptBody has imports replaced by whitespace/newlines, so its internal line numbers relative to its start are preserved.
154
+ const postamble = `
128
155
  ${bindingString}
129
156
  }
130
157
  });
131
158
  `;
159
+ const finalCode = preamble + scriptBody + postamble;
132
160
  if (isTs) {
133
- // Calculate line offset for Source Maps (Padding Strategy)
134
- // Find how many newlines are before the script content in the original source
135
- // This is a rough estimation but better than nothing for 1.0 dev
136
- const linesBefore = descriptor.source.substring(0, script.start).split('\n').length - 1;
137
- const padding = '\n'.repeat(linesBefore);
138
- // Pad the content so line numbers match original file
139
- // Note: This only works perfectly if we are transpiling the RAW content.
140
- // Since we are extracting/transforming (Natural Reactivity), line numbers might shift.
141
- // For accurate 1-to-1 mapping, we rely on TS source maps of the 'finalCode'.
142
- // To make 'finalCode' map back to 'filename', we need to construct it carefully.
143
161
  // Transpile extracted TS code to JS
144
162
  const result = ts.transpileModule(finalCode, {
145
163
  compilerOptions: {
@@ -149,7 +167,7 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
149
167
  inlineSources: true,
150
168
  sourceRoot: '/',
151
169
  },
152
- fileName: filename // Important for source map
170
+ fileName: filename // We ask TS to map to filename, but it maps 'finalCode' -> 'filename'
153
171
  });
154
172
  // Fix: Remove the //# sourceMappingURL= comment added by TS
155
173
  const codeWithoutMapComment = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
@@ -157,19 +175,56 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
157
175
  let finalMap = result.sourceMapText;
158
176
  if (finalMap) {
159
177
  try {
160
- const mapObj = JSON.parse(finalMap);
161
- // Try to get relative path from 'src' to preserve folder structure in DevTools
178
+ // Determine offsets
179
+ const preambleLines = preamble.split('\n').length - 1;
180
+ // 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;
184
+ const consumer = await new source_map_1.SourceMapConsumer(finalMap);
185
+ const generator = new source_map_1.SourceMapGenerator({
186
+ file: filename,
187
+ sourceRoot: ''
188
+ });
189
+ // If inside src, use path starting from src. Else just basename.
162
190
  const normalizedPath = filename.replace(/\\/g, '/');
163
191
  const srcIndex = normalizedPath.indexOf('/src/');
164
- // If inside src, use path starting from src. Else just basename.
165
- // Example: c:/.../src/pages/Home.mujs -> src/pages/Home.mujs
166
192
  let relativePath = srcIndex !== -1
167
193
  ? normalizedPath.substring(srcIndex + 1)
168
194
  : filename.split(/[/\\]/).pop() || 'unknown.mujs';
169
- // Use relative path directly. Webpack will handle the namespace.
170
- mapObj.sources = [relativePath];
171
- mapObj.sourcesContent = [descriptor.source];
172
- finalMap = JSON.stringify(mapObj);
195
+ generator.setSourceContent(relativePath, originalSource);
196
+ consumer.eachMapping(m => {
197
+ if (m.originalLine === null)
198
+ return;
199
+ // m.originalLine is the line in `finalCode` (1-based because SourceMapConsumer uses 1-based)
200
+ // We need to convert it to line in `.mujs`.
201
+ // Check if this line belongs to scriptBody
202
+ // scriptBody spans from (preambleLines + 1) to (preambleLines + scriptBodyLines)
203
+ const lineInFinalCode = m.originalLine;
204
+ // Check if strictly inside body
205
+ if (lineInFinalCode > preambleLines) {
206
+ // Calculate offset inside scriptBody
207
+ 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;
211
+ // Add mapping
212
+ generator.addMapping({
213
+ generated: {
214
+ line: m.generatedLine,
215
+ column: m.generatedColumn
216
+ },
217
+ original: {
218
+ line: originalLineInMujs,
219
+ column: m.originalColumn // Column should be preserved if we didn't change indentation
220
+ },
221
+ source: relativePath,
222
+ name: m.name
223
+ });
224
+ }
225
+ });
226
+ finalMap = generator.toString();
227
+ consumer.destroy();
173
228
  }
174
229
  catch (e) {
175
230
  console.warn('[MulanJS] Failed to patch source map:', e);
@@ -4,4 +4,4 @@ export interface CompileResult {
4
4
  errors: string[];
5
5
  map?: string;
6
6
  }
7
- export declare function compileSFC(source: string, filename: string): CompileResult;
7
+ export declare function compileSFC(source: string, filename: string): Promise<CompileResult>;
@@ -5,4 +5,4 @@ export interface ScriptCompileResult {
5
5
  errors: string[];
6
6
  map?: string;
7
7
  }
8
- export declare function compileScript(descriptor: SFCDescriptor): ScriptCompileResult;
8
+ export declare function compileScript(descriptor: SFCDescriptor): Promise<ScriptCompileResult>;
@@ -4,4 +4,4 @@ export interface CompileResult {
4
4
  errors: string[];
5
5
  map?: string;
6
6
  }
7
- export declare function compileSFC(source: string, filename: string): CompileResult;
7
+ export declare function compileSFC(source: string, filename: string): Promise<CompileResult>;
@@ -5,4 +5,4 @@ export interface ScriptCompileResult {
5
5
  errors: string[];
6
6
  map?: string;
7
7
  }
8
- export declare function compileScript(descriptor: SFCDescriptor): ScriptCompileResult;
8
+ export declare function compileScript(descriptor: SFCDescriptor): Promise<ScriptCompileResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulanjs/mulanjs",
3
- "version": "1.0.1-dev.20260218170441",
3
+ "version": "1.0.1-dev.20260219055338",
4
4
  "description": "A powerful, secure, and enterprise-grade JavaScript framework.",
5
5
  "main": "dist/mulan.js",
6
6
  "module": "dist/mulan.esm.js",
@@ -52,7 +52,8 @@
52
52
  "webpack-cli": "^6.0.1",
53
53
  "webpack-dev-server": "^5.2.0",
54
54
  "ts-loader": "^9.5.4",
55
- "typescript": "^5.0.0"
55
+ "typescript": "^5.0.0",
56
+ "source-map": "^0.7.4"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@types/fs-extra": "^11.0.4",
package/src/cli/index.js CHANGED
@@ -207,6 +207,7 @@ module.exports = {
207
207
  path: path.resolve(__dirname, 'dist'),
208
208
  clean: true,
209
209
  },
210
+ devtool: 'source-map',
210
211
  resolve: {
211
212
  extensions: ['.ts', '.js', '.mujs']
212
213
  },
@@ -11,13 +11,14 @@ export interface CompileResult {
11
11
  map?: string; // Source Map
12
12
  }
13
13
 
14
- export function compileSFC(source: string, filename: string): CompileResult {
14
+ export async function compileSFC(source: string, filename: string): Promise<CompileResult> {
15
15
  // 1. Parse
16
16
  const descriptor = parseMUJS(source, filename);
17
17
 
18
18
  // 2. Script
19
- const scriptResult = compileScript(descriptor);
19
+ const scriptResult = await compileScript(descriptor);
20
20
  // Replace export default to capture the component
21
+ // If the script already has 'export default', we intercept it.
21
22
  let scriptCode = scriptResult.code.replace('export default', 'const __component__ =');
22
23
  if (!scriptCode.includes('const __component__ =')) {
23
24
  scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
@@ -0,0 +1,369 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.compileScript = compileScript;
37
+ const ts = __importStar(require("typescript"));
38
+ const source_map_1 = require("source-map");
39
+ async function compileScript(descriptor) {
40
+ const script = descriptor.script;
41
+ if (!script)
42
+ return { code: 'export default {}', errors: [] };
43
+ const isSetup = !!script.attrs.setup;
44
+ const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
45
+ if (isSetup) {
46
+ return await compileSetupScript(script, isTs, descriptor);
47
+ }
48
+ else {
49
+ // Standard Options API - just pass through, assuming it has export default
50
+ return { code: script.content, errors: [] };
51
+ }
52
+ }
53
+ // Global list of Mulan hooks for auto-imports and reactivity exemption
54
+ const COMMON_HOOKS = ['muState', 'onMuMount', 'onMuInit', 'onMuDestroy', 'muEffect', 'muMemo', 'muQubit', 'muGate', 'muMeasure', 'muRegister', 'muEntangle', 'persistent'];
55
+ async function compileSetupScript(script, isTs, descriptor) {
56
+ var _a;
57
+ // 1. Pre-process: Natural Reactivity Transformation ($ syntax)
58
+ // We do this BEFORE extraction so that the rest of the compiler sees standard 'muState' code.
59
+ const transformedContent = transformNaturalReactivity(script.content, isTs);
60
+ // 2. Parse the TRANSFORMED code
61
+ const sourceFile = ts.createSourceFile('script.' + (isTs ? 'ts' : 'js'), transformedContent, ts.ScriptTarget.Latest, true);
62
+ const imports = [];
63
+ // We will build the body by replacing imports with newlines in the original content
64
+ // This preserves line numbers for the user's code relative to the script block start.
65
+ let scriptBody = transformedContent;
66
+ // Collect imports and bindings
67
+ const bindings = [];
68
+ // We need to remove imports from scriptBody but keep newlines.
69
+ // We'll use a replacement list and apply it end-to-start to avoid index shifting
70
+ const replacements = [];
71
+ sourceFile.statements.forEach(stmt => {
72
+ if (ts.isImportDeclaration(stmt)) {
73
+ const importText = transformedContent.substring(stmt.pos, stmt.end);
74
+ // Add to imports list (trimmed for cleanliness in preamble)
75
+ imports.push(importText.trim());
76
+ // Calculate number of newlines in this import statement to preserve layout
77
+ const numNewlines = (importText.match(/\n/g) || []).length;
78
+ const replacement = '\n'.repeat(numNewlines) + ' '.repeat(importText.length - numNewlines); // Keep length/lines roughly same? actually just newlines is enough for line mapping
79
+ // Better: we just want to preserve LINE COUNT.
80
+ // If we replace the whole range with newlines, we keep the vertical layout.
81
+ replacements.push({
82
+ start: stmt.pos,
83
+ end: stmt.end,
84
+ text: '\n'.repeat(numNewlines) || ' ' // at least a space if no newlines, to avoid merging lines?
85
+ });
86
+ }
87
+ else {
88
+ // Extract top-level declarations for template exposure
89
+ if (ts.isVariableStatement(stmt)) {
90
+ stmt.declarationList.declarations.forEach(decl => {
91
+ if (ts.isIdentifier(decl.name)) {
92
+ bindings.push(decl.name.text);
93
+ }
94
+ });
95
+ }
96
+ else if (ts.isFunctionDeclaration(stmt) && stmt.name) {
97
+ bindings.push(stmt.name.text);
98
+ }
99
+ else if (ts.isClassDeclaration(stmt) && stmt.name) {
100
+ bindings.push(stmt.name.text);
101
+ }
102
+ }
103
+ });
104
+ // Apply replacements (reverse order)
105
+ replacements.sort((a, b) => b.start - a.start);
106
+ replacements.forEach(r => {
107
+ scriptBody = scriptBody.substring(0, r.start) + r.text + scriptBody.substring(r.end);
108
+ });
109
+ // Reconstruct
110
+ // We import defineComponent from @mulanjs/mulanjs if not present?
111
+ // Ideally user imports what they need, but for 'setup' wrapping we need defineComponent.
112
+ // Let's assume user might not have imported it, so we import it as _defineComponent to avoid collision.
113
+ // Check if defineComponent is imported
114
+ const hasDefineComponent = imports.some(i => i.includes('defineComponent'));
115
+ const bootImports = hasDefineComponent ? '' : `import { defineComponent as _defineComponent } from '@mulanjs/mulanjs';`;
116
+ // ADDED: Check if we introduced 'muState' but it wasn't imported (because user used $)
117
+ const usesMuState = transformedContent.includes('muState');
118
+ const hasMuStateImport = imports.some(i => i.includes('muState') || i.includes('@mulanjs/mulanjs'));
119
+ let autoImports = (usesMuState && !hasMuStateImport) ? [`muState`] : [];
120
+ // Auto-import common hooks
121
+ COMMON_HOOKS.forEach(hook => {
122
+ if (transformedContent.includes(hook) && !imports.some(i => i.includes(hook))) {
123
+ // Avoid duplicates in autoImports
124
+ if (!autoImports.includes(hook)) {
125
+ autoImports.push(hook);
126
+ }
127
+ }
128
+ });
129
+ const autoImportStmt = autoImports.length > 0
130
+ ? `import { ${autoImports.join(', ')} } from '@mulanjs/mulanjs';`
131
+ : '';
132
+ const filename = descriptor.filename;
133
+ const componentName = filename ? ((_a = filename.split(/[/\\]/).pop()) === null || _a === void 0 ? void 0 : _a.split('.')[0].replace(/\W/g, '')) || 'App' : 'App';
134
+ const bindingString = bindings.length > 0
135
+ ? `
136
+ const exposed = { ${bindings.join(', ')} };
137
+ if (typeof window !== 'undefined') {
138
+ window["${componentName}"] = exposed;
139
+ }
140
+ return exposed;
141
+ `
142
+ : 'return {};';
143
+ // Construct final code with preserved body layout
144
+ const preamble = `
145
+ ${bootImports}
146
+ ${autoImportStmt}
147
+ ${imports.join('\n')}
148
+
149
+ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
150
+ setup() {
151
+ `;
152
+ // We intentionally put scriptBody on a new line.
153
+ // Note: scriptBody has imports replaced by whitespace/newlines, so its internal line numbers relative to its start are preserved.
154
+ const postamble = `
155
+ ${bindingString}
156
+ }
157
+ });
158
+ `;
159
+ const finalCode = preamble + scriptBody + postamble;
160
+ if (isTs) {
161
+ // Transpile extracted TS code to JS
162
+ const result = ts.transpileModule(finalCode, {
163
+ compilerOptions: {
164
+ module: ts.ModuleKind.ESNext,
165
+ target: ts.ScriptTarget.ES2019,
166
+ sourceMap: true, // ENABLE SOURCE MAPS
167
+ inlineSources: true,
168
+ sourceRoot: '/',
169
+ },
170
+ fileName: filename // We ask TS to map to filename, but it maps 'finalCode' -> 'filename'
171
+ });
172
+ // Fix: Remove the //# sourceMappingURL= comment added by TS
173
+ const codeWithoutMapComment = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
174
+ // Enhance Source Map to show ORIGINAL .mujs content
175
+ let finalMap = result.sourceMapText;
176
+ if (finalMap) {
177
+ try {
178
+ // Determine offsets
179
+ const preambleLines = preamble.split('\n').length - 1;
180
+ // 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;
184
+ const consumer = await new source_map_1.SourceMapConsumer(finalMap);
185
+ const generator = new source_map_1.SourceMapGenerator({
186
+ file: filename,
187
+ sourceRoot: ''
188
+ });
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';
195
+ generator.setSourceContent(relativePath, originalSource);
196
+ consumer.eachMapping(m => {
197
+ if (m.originalLine === null)
198
+ return;
199
+ // m.originalLine is the line in `finalCode` (1-based because SourceMapConsumer uses 1-based)
200
+ // We need to convert it to line in `.mujs`.
201
+ // Check if this line belongs to scriptBody
202
+ // scriptBody spans from (preambleLines + 1) to (preambleLines + scriptBodyLines)
203
+ const lineInFinalCode = m.originalLine;
204
+ // Check if strictly inside body
205
+ if (lineInFinalCode > preambleLines) {
206
+ // Calculate offset inside scriptBody
207
+ 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;
211
+ // Add mapping
212
+ generator.addMapping({
213
+ generated: {
214
+ line: m.generatedLine,
215
+ column: m.generatedColumn
216
+ },
217
+ original: {
218
+ line: originalLineInMujs,
219
+ column: m.originalColumn // Column should be preserved if we didn't change indentation
220
+ },
221
+ source: relativePath,
222
+ name: m.name
223
+ });
224
+ }
225
+ });
226
+ finalMap = generator.toString();
227
+ consumer.destroy();
228
+ }
229
+ catch (e) {
230
+ console.warn('[MulanJS] Failed to patch source map:', e);
231
+ }
232
+ }
233
+ return {
234
+ code: codeWithoutMapComment,
235
+ bindings,
236
+ errors: [],
237
+ map: finalMap // Return the enhanced map
238
+ };
239
+ }
240
+ return {
241
+ code: finalCode,
242
+ bindings,
243
+ errors: [],
244
+ map: undefined
245
+ };
246
+ }
247
+ // --- Natural Reactivity Transformer ---
248
+ function transformNaturalReactivity(code, isTs) {
249
+ // If no '$(' or '$q(' or '$qr(' usage, return original code for safety/speed
250
+ if (!code.includes('$(') && !code.includes('$q(') && !code.includes('$qr('))
251
+ return code;
252
+ const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);
253
+ const reactiveVars = new Set();
254
+ const quantumVars = new Set();
255
+ const registerVars = new Set();
256
+ // Pass 1: Identification
257
+ function findReactiveVars(node) {
258
+ if (ts.isVariableStatement(node)) {
259
+ node.declarationList.declarations.forEach(decl => {
260
+ if (decl.initializer && ts.isCallExpression(decl.initializer)) {
261
+ const expr = decl.initializer.expression;
262
+ if (ts.isIdentifier(expr)) {
263
+ if (expr.text === '$') {
264
+ if (ts.isIdentifier(decl.name))
265
+ reactiveVars.add(decl.name.text);
266
+ }
267
+ else if (expr.text === '$q') {
268
+ if (ts.isIdentifier(decl.name))
269
+ quantumVars.add(decl.name.text);
270
+ }
271
+ else if (expr.text === '$qr') {
272
+ if (ts.isIdentifier(decl.name))
273
+ registerVars.add(decl.name.text);
274
+ }
275
+ }
276
+ }
277
+ });
278
+ }
279
+ ts.forEachChild(node, findReactiveVars);
280
+ }
281
+ findReactiveVars(sourceFile);
282
+ if (reactiveVars.size === 0 && quantumVars.size === 0 && registerVars.size === 0)
283
+ return code;
284
+ // Pass 2: Transformation
285
+ const transformerFactory = (context) => {
286
+ return (rootNode) => {
287
+ function visit(node) {
288
+ if (ts.isVariableStatement(node)) {
289
+ const newDecls = [];
290
+ let changed = false;
291
+ node.declarationList.declarations.forEach(decl => {
292
+ if (ts.isIdentifier(decl.name)) {
293
+ const name = decl.name.text;
294
+ if (reactiveVars.has(name) || quantumVars.has(name) || registerVars.has(name)) {
295
+ changed = true;
296
+ let hookName = 'muState';
297
+ if (quantumVars.has(name))
298
+ hookName = 'muQubit';
299
+ else if (registerVars.has(name))
300
+ hookName = 'muRegister';
301
+ const init = decl.initializer;
302
+ newDecls.push(ts.factory.updateVariableDeclaration(decl, decl.name, decl.exclamationToken, decl.type, ts.factory.createCallExpression(ts.factory.createIdentifier(hookName), undefined, init.arguments)));
303
+ }
304
+ else {
305
+ newDecls.push(decl);
306
+ }
307
+ }
308
+ else {
309
+ newDecls.push(decl);
310
+ }
311
+ });
312
+ if (changed) {
313
+ return ts.factory.createVariableStatement(node.modifiers, ts.factory.createVariableDeclarationList(newDecls, ts.NodeFlags.Const));
314
+ }
315
+ }
316
+ if (ts.isCallExpression(node)) {
317
+ const expr = node.expression;
318
+ if (ts.isIdentifier(expr)) {
319
+ const name = expr.text;
320
+ const isMacro = name === '$' || name === '$q' || name === '$qr';
321
+ if (isMacro) {
322
+ // If this call is the initializer of a variable declaration being handled,
323
+ // it will be transformed by the VariableStatement logic above.
324
+ // However, we want to transform it here if it's used as a STANDALONE expression (e.g. assignment).
325
+ const parent = node.parent;
326
+ const isDeclarationInit = ts.isVariableDeclaration(parent) && parent.initializer === node;
327
+ if (!isDeclarationInit) {
328
+ let hookName = 'muState';
329
+ if (name === '$q')
330
+ hookName = 'muQubit';
331
+ else if (name === '$qr')
332
+ hookName = 'muRegister';
333
+ // Transform $(x) -> muState(x).value
334
+ return ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createIdentifier(hookName), undefined, node.arguments), ts.factory.createIdentifier('value'));
335
+ }
336
+ }
337
+ }
338
+ }
339
+ if (ts.isIdentifier(node)) {
340
+ const name = node.text;
341
+ if (reactiveVars.has(name) || quantumVars.has(name) || registerVars.has(name)) {
342
+ const parent = node.parent;
343
+ if (ts.isVariableDeclaration(parent) && parent.name === node)
344
+ return node;
345
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node)
346
+ return node;
347
+ if (ts.isPropertyAccessExpression(parent) && parent.expression === node && parent.name.text === 'value')
348
+ return node;
349
+ if (ts.isPropertyAssignment(parent) && parent.name === node)
350
+ return node;
351
+ // EXEMPTION: Do not add .value if the identifier is a direct argument to a Mulan hook
352
+ // These hooks (muGate, muMeasure, etc.) expect the signal wrapper.
353
+ if (ts.isCallExpression(parent) && ts.isIdentifier(parent.expression)) {
354
+ const hook = parent.expression.text;
355
+ if (COMMON_HOOKS.includes(hook))
356
+ return node;
357
+ }
358
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), ts.factory.createIdentifier('value'));
359
+ }
360
+ }
361
+ return ts.visitEachChild(node, visit, context);
362
+ }
363
+ return ts.visitNode(rootNode, visit);
364
+ };
365
+ };
366
+ const result = ts.transform(sourceFile, [transformerFactory]);
367
+ const printer = ts.createPrinter();
368
+ return printer.printFile(result.transformed[0]);
369
+ }
@@ -1,6 +1,7 @@
1
1
 
2
2
  import { SFCDescriptor, SFCBlock } from './sfc-parser';
3
3
  import * as ts from 'typescript';
4
+ import { SourceMapConsumer, SourceMapGenerator } from 'source-map';
4
5
 
5
6
  export interface ScriptCompileResult {
6
7
  code: string;
@@ -9,7 +10,7 @@ export interface ScriptCompileResult {
9
10
  map?: string; // Source Map
10
11
  }
11
12
 
12
- export function compileScript(descriptor: SFCDescriptor): ScriptCompileResult {
13
+ export async function compileScript(descriptor: SFCDescriptor): Promise<ScriptCompileResult> {
13
14
  const script = descriptor.script;
14
15
  if (!script) return { code: 'export default {}', errors: [] };
15
16
 
@@ -17,7 +18,7 @@ export function compileScript(descriptor: SFCDescriptor): ScriptCompileResult {
17
18
  const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
18
19
 
19
20
  if (isSetup) {
20
- return compileSetupScript(script, isTs, descriptor);
21
+ return await compileSetupScript(script, isTs, descriptor);
21
22
  } else {
22
23
  // Standard Options API - just pass through, assuming it has export default
23
24
  return { code: script.content, errors: [] };
@@ -27,7 +28,7 @@ export function compileScript(descriptor: SFCDescriptor): ScriptCompileResult {
27
28
  // Global list of Mulan hooks for auto-imports and reactivity exemption
28
29
  const COMMON_HOOKS = ['muState', 'onMuMount', 'onMuInit', 'onMuDestroy', 'muEffect', 'muMemo', 'muQubit', 'muGate', 'muMeasure', 'muRegister', 'muEntangle', 'persistent'];
29
30
 
30
- function compileSetupScript(script: SFCBlock, isTs: boolean, descriptor: SFCDescriptor): ScriptCompileResult {
31
+ async function compileSetupScript(script: SFCBlock, isTs: boolean, descriptor: SFCDescriptor): Promise<ScriptCompileResult> {
31
32
  // 1. Pre-process: Natural Reactivity Transformation ($ syntax)
32
33
  // We do this BEFORE extraction so that the rest of the compiler sees standard 'muState' code.
33
34
  const transformedContent = transformNaturalReactivity(script.content, isTs);
@@ -41,15 +42,34 @@ function compileSetupScript(script: SFCBlock, isTs: boolean, descriptor: SFCDesc
41
42
  );
42
43
 
43
44
  const imports: string[] = [];
44
- const statements: string[] = [];
45
+ // We will build the body by replacing imports with newlines in the original content
46
+ // This preserves line numbers for the user's code relative to the script block start.
47
+ let scriptBody = transformedContent;
48
+
49
+ // Collect imports and bindings
45
50
  const bindings: string[] = [];
46
51
 
52
+ // We need to remove imports from scriptBody but keep newlines.
53
+ // We'll use a replacement list and apply it end-to-start to avoid index shifting
54
+ const replacements: { start: number, end: number, text: string }[] = [];
55
+
47
56
  sourceFile.statements.forEach(stmt => {
48
57
  if (ts.isImportDeclaration(stmt)) {
49
- imports.push(transformedContent.substring(stmt.pos, stmt.end).trim());
58
+ const importText = transformedContent.substring(stmt.pos, stmt.end);
59
+ // Add to imports list (trimmed for cleanliness in preamble)
60
+ imports.push(importText.trim());
61
+
62
+ // Calculate number of newlines in this import statement to preserve layout
63
+ const numNewlines = (importText.match(/\n/g) || []).length;
64
+ const replacement = '\n'.repeat(numNewlines) + ' '.repeat(importText.length - numNewlines); // Keep length/lines roughly same? actually just newlines is enough for line mapping
65
+ // Better: we just want to preserve LINE COUNT.
66
+ // If we replace the whole range with newlines, we keep the vertical layout.
67
+ replacements.push({
68
+ start: stmt.pos,
69
+ end: stmt.end,
70
+ text: '\n'.repeat(numNewlines) || ' ' // at least a space if no newlines, to avoid merging lines?
71
+ });
50
72
  } else {
51
- statements.push(transformedContent.substring(stmt.pos, stmt.end).trim());
52
-
53
73
  // Extract top-level declarations for template exposure
54
74
  if (ts.isVariableStatement(stmt)) {
55
75
  stmt.declarationList.declarations.forEach(decl => {
@@ -65,18 +85,24 @@ function compileSetupScript(script: SFCBlock, isTs: boolean, descriptor: SFCDesc
65
85
  }
66
86
  });
67
87
 
88
+ // Apply replacements (reverse order)
89
+ replacements.sort((a, b) => b.start - a.start);
90
+ replacements.forEach(r => {
91
+ scriptBody = scriptBody.substring(0, r.start) + r.text + scriptBody.substring(r.end);
92
+ });
93
+
68
94
  // Reconstruct
69
- // We import defineComponent from mulanjs if not present?
95
+ // We import defineComponent from @mulanjs/mulanjs if not present?
70
96
  // Ideally user imports what they need, but for 'setup' wrapping we need defineComponent.
71
97
  // Let's assume user might not have imported it, so we import it as _defineComponent to avoid collision.
72
98
 
73
99
  // Check if defineComponent is imported
74
100
  const hasDefineComponent = imports.some(i => i.includes('defineComponent'));
75
- const bootImports = hasDefineComponent ? '' : `import { defineComponent as _defineComponent } from 'mulanjs';`;
101
+ const bootImports = hasDefineComponent ? '' : `import { defineComponent as _defineComponent } from '@mulanjs/mulanjs';`;
76
102
 
77
103
  // ADDED: Check if we introduced 'muState' but it wasn't imported (because user used $)
78
104
  const usesMuState = transformedContent.includes('muState');
79
- const hasMuStateImport = imports.some(i => i.includes('muState') || i.includes('mulanjs'));
105
+ const hasMuStateImport = imports.some(i => i.includes('muState') || i.includes('@mulanjs/mulanjs'));
80
106
  let autoImports = (usesMuState && !hasMuStateImport) ? [`muState`] : [];
81
107
 
82
108
  // Auto-import common hooks
@@ -90,7 +116,7 @@ function compileSetupScript(script: SFCBlock, isTs: boolean, descriptor: SFCDesc
90
116
  });
91
117
 
92
118
  const autoImportStmt = autoImports.length > 0
93
- ? `import { ${autoImports.join(', ')} } from 'mulanjs';`
119
+ ? `import { ${autoImports.join(', ')} } from '@mulanjs/mulanjs';`
94
120
  : '';
95
121
 
96
122
  const filename = descriptor.filename;
@@ -106,32 +132,27 @@ function compileSetupScript(script: SFCBlock, isTs: boolean, descriptor: SFCDesc
106
132
  `
107
133
  : 'return {};';
108
134
 
109
- const finalCode = `
135
+ // Construct final code with preserved body layout
136
+ const preamble = `
110
137
  ${bootImports}
111
138
  ${autoImportStmt}
112
139
  ${imports.join('\n')}
113
140
 
114
141
  export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
115
142
  setup() {
116
- ${statements.join('\n')}
143
+ `;
144
+ // We intentionally put scriptBody on a new line.
145
+ // Note: scriptBody has imports replaced by whitespace/newlines, so its internal line numbers relative to its start are preserved.
146
+
147
+ const postamble = `
117
148
  ${bindingString}
118
149
  }
119
150
  });
120
151
  `;
121
152
 
122
- if (isTs) {
123
- // Calculate line offset for Source Maps (Padding Strategy)
124
- // Find how many newlines are before the script content in the original source
125
- // This is a rough estimation but better than nothing for 1.0 dev
126
- const linesBefore = descriptor.source.substring(0, script.start).split('\n').length - 1;
127
- const padding = '\n'.repeat(linesBefore);
128
-
129
- // Pad the content so line numbers match original file
130
- // Note: This only works perfectly if we are transpiling the RAW content.
131
- // Since we are extracting/transforming (Natural Reactivity), line numbers might shift.
132
- // For accurate 1-to-1 mapping, we rely on TS source maps of the 'finalCode'.
133
- // To make 'finalCode' map back to 'filename', we need to construct it carefully.
153
+ const finalCode = preamble + scriptBody + postamble;
134
154
 
155
+ if (isTs) {
135
156
  // Transpile extracted TS code to JS
136
157
  const result = ts.transpileModule(finalCode, {
137
158
  compilerOptions: {
@@ -141,7 +162,7 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
141
162
  inlineSources: true,
142
163
  sourceRoot: '/',
143
164
  },
144
- fileName: filename // Important for source map
165
+ fileName: filename // We ask TS to map to filename, but it maps 'finalCode' -> 'filename'
145
166
  });
146
167
 
147
168
  // Fix: Remove the //# sourceMappingURL= comment added by TS
@@ -151,23 +172,67 @@ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
151
172
  let finalMap = result.sourceMapText;
152
173
  if (finalMap) {
153
174
  try {
154
- const mapObj = JSON.parse(finalMap);
175
+ // Determine offsets
176
+ const preambleLines = preamble.split('\n').length - 1;
177
+ // scriptBody starts at line `preambleLines` (0-indexed) in finalCode.
178
+
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;
182
+
183
+ const consumer = await new SourceMapConsumer(finalMap);
184
+ const generator = new SourceMapGenerator({
185
+ file: filename,
186
+ sourceRoot: ''
187
+ });
155
188
 
156
- // Try to get relative path from 'src' to preserve folder structure in DevTools
189
+ // If inside src, use path starting from src. Else just basename.
157
190
  const normalizedPath = filename.replace(/\\/g, '/');
158
191
  const srcIndex = normalizedPath.indexOf('/src/');
159
-
160
- // If inside src, use path starting from src. Else just basename.
161
- // Example: c:/.../src/pages/Home.mujs -> src/pages/Home.mujs
162
192
  let relativePath = srcIndex !== -1
163
193
  ? normalizedPath.substring(srcIndex + 1)
164
194
  : filename.split(/[/\\]/).pop() || 'unknown.mujs';
165
195
 
166
- // Use relative path directly. Webpack will handle the namespace.
167
- mapObj.sources = [relativePath];
196
+ generator.setSourceContent(relativePath, originalSource);
197
+
198
+ consumer.eachMapping(m => {
199
+ if (m.originalLine === null) return;
200
+
201
+ // m.originalLine is the line in `finalCode` (1-based because SourceMapConsumer uses 1-based)
202
+ // We need to convert it to line in `.mujs`.
203
+
204
+ // Check if this line belongs to scriptBody
205
+ // scriptBody spans from (preambleLines + 1) to (preambleLines + scriptBodyLines)
206
+ const lineInFinalCode = m.originalLine;
207
+
208
+ // Check if strictly inside body
209
+ if (lineInFinalCode > preambleLines) {
210
+ // Calculate offset inside scriptBody
211
+ const lineOffsetInBody = lineInFinalCode - preambleLines; // 1-based offset (1st line of body is 1)
212
+
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;
216
+
217
+ // Add mapping
218
+ generator.addMapping({
219
+ generated: {
220
+ line: m.generatedLine,
221
+ column: m.generatedColumn
222
+ },
223
+ original: {
224
+ line: originalLineInMujs,
225
+ column: m.originalColumn // Column should be preserved if we didn't change indentation
226
+ },
227
+ source: relativePath,
228
+ name: m.name
229
+ });
230
+ }
231
+ });
232
+
233
+ finalMap = generator.toString();
234
+ consumer.destroy();
168
235
 
169
- mapObj.sourcesContent = [descriptor.source];
170
- finalMap = JSON.stringify(mapObj);
171
236
  } catch (e) {
172
237
  console.warn('[MulanJS] Failed to patch source map:', e);
173
238
  }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseMUJS = parseMUJS;
4
+ /**
5
+ * Standardized State-Machine Parser for MulanJS
6
+ * Parses .mujs files safely, handling attributes, quotes, and nested content.
7
+ */
8
+ function parseMUJS(source, filename) {
9
+ const descriptor = {
10
+ filename,
11
+ source,
12
+ template: null,
13
+ script: null,
14
+ scripts: [],
15
+ styles: [],
16
+ customBlocks: []
17
+ };
18
+ let cursor = 0;
19
+ const length = source.length;
20
+ while (cursor < length) {
21
+ // Look for start tag '<'
22
+ const start = source.indexOf('<', cursor);
23
+ if (start === -1)
24
+ break; // End of file
25
+ // Must be safe start (not in comment/string - simplified for top-level blocks)
26
+ // We assume SFC top-level blocks are valid HTML-like tags.
27
+ // Check if closing tag (ignore)
28
+ if (source[start + 1] === '/') {
29
+ cursor = start + 1;
30
+ continue;
31
+ }
32
+ // Parse Tag Name
33
+ let nameEnd = start + 1;
34
+ while (nameEnd < length && !/\s|>/.test(source[nameEnd])) {
35
+ nameEnd++;
36
+ }
37
+ const tagName = source.slice(start + 1, nameEnd);
38
+ // Parse Attributes
39
+ let attrStart = nameEnd;
40
+ const attrs = {};
41
+ let tagEnd = source.indexOf('>', attrStart);
42
+ if (tagEnd === -1)
43
+ break;
44
+ const attrString = source.slice(attrStart, tagEnd);
45
+ // Simple regex for attrs is safe *inside* the tag definition
46
+ const attrRegex = /([a-zA-Z0-9:-]+)(?:=["']([^"']*)["'])?/g;
47
+ let match;
48
+ while ((match = attrRegex.exec(attrString)) !== null) {
49
+ attrs[match[1]] = match[2] || 'true';
50
+ }
51
+ // Find Closing Tag
52
+ // We need to handle nested content.
53
+ // For <template>, we just look for </template>.
54
+ // For <script>, same.
55
+ // "Naive" approach: indexOf('</' + tagName) is actually standard for SFC parsers
56
+ // because top-level blocks shouldn't contain their own closing tag as text
57
+ // (except unless escaped, which we can handle if needed, but rarely an issue for top-level).
58
+ const contentStart = tagEnd + 1;
59
+ const closeTag = `</${tagName}>`;
60
+ const contentEnd = source.indexOf(closeTag, contentStart);
61
+ if (contentEnd === -1) {
62
+ // Unclosed block, invalid SFC.
63
+ console.warn(`[MulanJS Parser] Unclosed block: <${tagName}>`);
64
+ break;
65
+ }
66
+ const content = source.slice(contentStart, contentEnd);
67
+ const block = {
68
+ type: tagName,
69
+ content: content.trim(), // Trim content for cleanliness
70
+ attrs,
71
+ start: start,
72
+ end: contentEnd + closeTag.length
73
+ };
74
+ // Add to descriptor
75
+ if (tagName === 'template' || tagName === 'mu-template') {
76
+ descriptor.template = block;
77
+ }
78
+ else if (tagName === 'script') {
79
+ descriptor.scripts.push(block);
80
+ if (attrs.setup || !descriptor.script) {
81
+ descriptor.script = block;
82
+ }
83
+ }
84
+ else if (tagName === 'style') {
85
+ descriptor.styles.push(block);
86
+ }
87
+ else {
88
+ descriptor.customBlocks.push(block);
89
+ }
90
+ cursor = contentEnd + closeTag.length;
91
+ }
92
+ return descriptor;
93
+ }
@@ -58,9 +58,11 @@ module.exports = function (content) {
58
58
  const compilerUrl = pathToFileURL(compilerRef).href;
59
59
 
60
60
  // Use dynamic import to load ESM compiler from CommonJS loader
61
- import(compilerUrl).then(compiler => {
61
+
62
+ // Use dynamic import to load ESM compiler from CommonJS loader
63
+ import(compilerUrl).then(async compiler => {
62
64
  try {
63
- const result = compiler.compileSFC(content, filePath);
65
+ const result = await compiler.compileSFC(content, filePath);
64
66
 
65
67
  if (result.errors && result.errors.length > 0) {
66
68
  this.emitError(new Error(result.errors.join('\n')));