@mulanjs/mulanjs 1.0.1-dev.20260212143840

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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/dist/compiler/compiler.js +90 -0
  4. package/dist/compiler/script-compiler.js +314 -0
  5. package/dist/compiler/sfc-parser.js +93 -0
  6. package/dist/compiler/style-compiler.js +56 -0
  7. package/dist/compiler/template-compiler.js +442 -0
  8. package/dist/components/bloch-sphere.js +252 -0
  9. package/dist/core/component.js +145 -0
  10. package/dist/core/hooks.js +229 -0
  11. package/dist/core/quantum.js +284 -0
  12. package/dist/core/query.js +63 -0
  13. package/dist/core/reactive.js +105 -0
  14. package/dist/core/renderer.js +70 -0
  15. package/dist/core/vault.js +81 -0
  16. package/dist/index.js +52 -0
  17. package/dist/mulan.esm.js +1948 -0
  18. package/dist/mulan.js +215 -0
  19. package/dist/router/index.js +210 -0
  20. package/dist/security/sanitizer.js +47 -0
  21. package/dist/store/index.js +42 -0
  22. package/dist/types/compiler/compiler.d.ts +7 -0
  23. package/dist/types/compiler/script-compiler.d.ts +8 -0
  24. package/dist/types/compiler/sfc-parser.d.ts +21 -0
  25. package/dist/types/compiler/style-compiler.d.ts +7 -0
  26. package/dist/types/compiler/template-compiler.d.ts +7 -0
  27. package/dist/types/compiler.d.ts +7 -0
  28. package/dist/types/components/bloch-sphere.d.ts +16 -0
  29. package/dist/types/core/component.d.ts +54 -0
  30. package/dist/types/core/hooks.d.ts +49 -0
  31. package/dist/types/core/quantum.d.ts +50 -0
  32. package/dist/types/core/query.d.ts +14 -0
  33. package/dist/types/core/reactive.d.ts +21 -0
  34. package/dist/types/core/renderer.d.ts +4 -0
  35. package/dist/types/core/vault.d.ts +12 -0
  36. package/dist/types/index.d.ts +70 -0
  37. package/dist/types/router/index.d.ts +24 -0
  38. package/dist/types/script-compiler.d.ts +8 -0
  39. package/dist/types/security/sanitizer.d.ts +17 -0
  40. package/dist/types/sfc-parser.d.ts +21 -0
  41. package/dist/types/store/index.d.ts +10 -0
  42. package/dist/types/style-compiler.d.ts +7 -0
  43. package/dist/types/template-compiler.d.ts +7 -0
  44. package/package.json +64 -0
  45. package/src/cli/extensions/mulanjs-vscode-1.0.0.vsix +0 -0
  46. package/src/cli/index.js +600 -0
  47. package/src/compiler/compiler.ts +102 -0
  48. package/src/compiler/script-compiler.ts +336 -0
  49. package/src/compiler/sfc-parser.ts +118 -0
  50. package/src/compiler/style-compiler.ts +66 -0
  51. package/src/compiler/template-compiler.ts +519 -0
  52. package/src/compiler/tsconfig.json +13 -0
  53. package/src/loader/index.js +81 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nitinkumardev09
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # mulanjs_framework
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileSFC = compileSFC;
4
+ const sfc_parser_1 = require("./sfc-parser");
5
+ const script_compiler_1 = require("./script-compiler");
6
+ const template_compiler_1 = require("./template-compiler");
7
+ const style_compiler_1 = require("./style-compiler");
8
+ function compileSFC(source, filename) {
9
+ // 1. Parse
10
+ const descriptor = (0, sfc_parser_1.parseMUJS)(source, filename);
11
+ // 2. Script
12
+ const scriptResult = (0, script_compiler_1.compileScript)(descriptor);
13
+ // Replace export default to capture the component
14
+ let scriptCode = scriptResult.code.replace('export default', 'const __component__ =');
15
+ if (!scriptCode.includes('const __component__ =')) {
16
+ scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
17
+ }
18
+ // 3. Style
19
+ const styleResult = (0, style_compiler_1.compileStyle)(descriptor, filename);
20
+ // 4. Template
21
+ const templateResult = (0, template_compiler_1.compileTemplate)(descriptor, scriptResult, styleResult.scopedId);
22
+ // 5. Assembly
23
+ const cssInjection = styleResult.css ? `
24
+ if (typeof document !== 'undefined') {
25
+ const style = document.createElement('style');
26
+ style.textContent = \`${styleResult.css}\`;
27
+ document.head.appendChild(style);
28
+ }
29
+ ` : '';
30
+ const finalCode = `${scriptCode}
31
+
32
+ ${templateResult.code}
33
+
34
+ // Attach template to component
35
+ if (__component__.prototype) {
36
+ // Class component
37
+ __component__.prototype.template = render;
38
+ } else if (typeof __component__ === 'object') {
39
+ // Options object
40
+ __component__.template = render;
41
+ }
42
+
43
+ ${cssInjection}
44
+
45
+ // --- MulanJS HMR Engine ---
46
+ if (typeof module !== 'undefined' && module.hot) {
47
+ const hmrId = "${filename.replace(/\\/g, '/')}";
48
+ window.__MULAN_HMR__ = window.__MULAN_HMR__ || {
49
+ records: new Map(),
50
+ notify: (id, newComp) => {
51
+ const oldRecord = window.__MULAN_HMR__.records.get(id);
52
+ if (oldRecord) {
53
+ console.log('[MulanJS HMR] Patching:', id);
54
+ if (oldRecord.prototype && newComp.prototype) {
55
+ oldRecord.prototype.template = newComp.prototype.template;
56
+ // Also patch setup if available (for Functional components)
57
+ if (newComp._setup) oldRecord._setup = newComp._setup;
58
+ } else {
59
+ Object.assign(oldRecord, newComp);
60
+ }
61
+ if (window.__MULAN_REFRESH__) {
62
+ console.log('[MulanJS HMR] Triggering Deep Refresh');
63
+ window.__MULAN_REFRESH__();
64
+ }
65
+ }
66
+ }
67
+ };
68
+
69
+ if (window.__MULAN_HMR__.records.has(hmrId)) {
70
+ window.__MULAN_HMR__.notify(hmrId, __component__);
71
+ } else {
72
+ window.__MULAN_HMR__.records.set(hmrId, __component__);
73
+ }
74
+ module.hot.accept();
75
+ }
76
+
77
+ // console.log('[MulanJS] Component created:', __component__);
78
+ export default __component__;
79
+
80
+ // Helper to get relative path for sourceURL
81
+ // This ensures the generated file appears in a readable folder structure in DevTools
82
+ // SourceURL removed. Using standard Source Maps only.
83
+ `;
84
+ return {
85
+ code: finalCode,
86
+ css: styleResult.css,
87
+ errors: [...scriptResult.errors, ...templateResult.errors],
88
+ map: scriptResult.map // Pass the map
89
+ };
90
+ }
@@ -0,0 +1,314 @@
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
+ function compileScript(descriptor) {
39
+ const script = descriptor.script;
40
+ if (!script)
41
+ return { code: 'export default {}', errors: [] };
42
+ const isSetup = !!script.attrs.setup;
43
+ const isTs = script.attrs.lang === 'ts' || script.attrs.lang === 'tsx';
44
+ if (isSetup) {
45
+ return compileSetupScript(script, isTs, descriptor);
46
+ }
47
+ else {
48
+ // Standard Options API - just pass through, assuming it has export default
49
+ return { code: script.content, errors: [] };
50
+ }
51
+ }
52
+ // Global list of Mulan hooks for auto-imports and reactivity exemption
53
+ const COMMON_HOOKS = ['muState', 'onMuMount', 'onMuInit', 'onMuDestroy', 'muEffect', 'muMemo', 'muQubit', 'muGate', 'muMeasure', 'muRegister', 'muEntangle', 'persistent'];
54
+ function compileSetupScript(script, isTs, descriptor) {
55
+ var _a;
56
+ // 1. Pre-process: Natural Reactivity Transformation ($ syntax)
57
+ // We do this BEFORE extraction so that the rest of the compiler sees standard 'muState' code.
58
+ const transformedContent = transformNaturalReactivity(script.content, isTs);
59
+ // 2. Parse the TRANSFORMED code
60
+ const sourceFile = ts.createSourceFile('script.' + (isTs ? 'ts' : 'js'), transformedContent, ts.ScriptTarget.Latest, true);
61
+ const imports = [];
62
+ const statements = [];
63
+ const bindings = [];
64
+ sourceFile.statements.forEach(stmt => {
65
+ if (ts.isImportDeclaration(stmt)) {
66
+ imports.push(transformedContent.substring(stmt.pos, stmt.end).trim());
67
+ }
68
+ else {
69
+ statements.push(transformedContent.substring(stmt.pos, stmt.end).trim());
70
+ // Extract top-level declarations for template exposure
71
+ if (ts.isVariableStatement(stmt)) {
72
+ stmt.declarationList.declarations.forEach(decl => {
73
+ if (ts.isIdentifier(decl.name)) {
74
+ bindings.push(decl.name.text);
75
+ }
76
+ });
77
+ }
78
+ else if (ts.isFunctionDeclaration(stmt) && stmt.name) {
79
+ bindings.push(stmt.name.text);
80
+ }
81
+ else if (ts.isClassDeclaration(stmt) && stmt.name) {
82
+ bindings.push(stmt.name.text);
83
+ }
84
+ }
85
+ });
86
+ // Reconstruct
87
+ // We import defineComponent from mulanjs if not present?
88
+ // Ideally user imports what they need, but for 'setup' wrapping we need defineComponent.
89
+ // Let's assume user might not have imported it, so we import it as _defineComponent to avoid collision.
90
+ // Check if defineComponent is imported
91
+ const hasDefineComponent = imports.some(i => i.includes('defineComponent'));
92
+ const bootImports = hasDefineComponent ? '' : `import { defineComponent as _defineComponent } from 'mulanjs';`;
93
+ // ADDED: Check if we introduced 'muState' but it wasn't imported (because user used $)
94
+ const usesMuState = transformedContent.includes('muState');
95
+ const hasMuStateImport = imports.some(i => i.includes('muState') || i.includes('mulanjs'));
96
+ let autoImports = (usesMuState && !hasMuStateImport) ? [`muState`] : [];
97
+ // Auto-import common hooks
98
+ COMMON_HOOKS.forEach(hook => {
99
+ if (transformedContent.includes(hook) && !imports.some(i => i.includes(hook))) {
100
+ // Avoid duplicates in autoImports
101
+ if (!autoImports.includes(hook)) {
102
+ autoImports.push(hook);
103
+ }
104
+ }
105
+ });
106
+ const autoImportStmt = autoImports.length > 0
107
+ ? `import { ${autoImports.join(', ')} } from 'mulanjs';`
108
+ : '';
109
+ const filename = descriptor.filename;
110
+ const componentName = filename ? ((_a = filename.split(/[/\\]/).pop()) === null || _a === void 0 ? void 0 : _a.split('.')[0].replace(/\W/g, '')) || 'App' : 'App';
111
+ const bindingString = bindings.length > 0
112
+ ? `
113
+ const exposed = { ${bindings.join(', ')} };
114
+ if (typeof window !== 'undefined') {
115
+ window["${componentName}"] = exposed;
116
+ }
117
+ return exposed;
118
+ `
119
+ : 'return {};';
120
+ const finalCode = `
121
+ ${bootImports}
122
+ ${autoImportStmt}
123
+ ${imports.join('\n')}
124
+
125
+ export default ${hasDefineComponent ? 'defineComponent' : '_defineComponent'}({
126
+ setup() {
127
+ ${statements.join('\n')}
128
+ ${bindingString}
129
+ }
130
+ });
131
+ `;
132
+ 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
+ // Transpile extracted TS code to JS
144
+ const result = ts.transpileModule(finalCode, {
145
+ compilerOptions: {
146
+ module: ts.ModuleKind.ESNext,
147
+ target: ts.ScriptTarget.ES2019,
148
+ sourceMap: true, // ENABLE SOURCE MAPS
149
+ inlineSources: true,
150
+ sourceRoot: '/',
151
+ },
152
+ fileName: filename // Important for source map
153
+ });
154
+ // Fix: Remove the //# sourceMappingURL= comment added by TS
155
+ const codeWithoutMapComment = result.outputText.replace(/\/\/# sourceMappingURL=.*$/gm, '');
156
+ // Enhance Source Map to show ORIGINAL .mujs content
157
+ let finalMap = result.sourceMapText;
158
+ if (finalMap) {
159
+ try {
160
+ const mapObj = JSON.parse(finalMap);
161
+ // Try to get relative path from 'src' to preserve folder structure in DevTools
162
+ const normalizedPath = filename.replace(/\\/g, '/');
163
+ 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
+ let relativePath = srcIndex !== -1
167
+ ? normalizedPath.substring(srcIndex + 1)
168
+ : 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);
173
+ }
174
+ catch (e) {
175
+ console.warn('[MulanJS] Failed to patch source map:', e);
176
+ }
177
+ }
178
+ return {
179
+ code: codeWithoutMapComment,
180
+ bindings,
181
+ errors: [],
182
+ map: finalMap // Return the enhanced map
183
+ };
184
+ }
185
+ return {
186
+ code: finalCode,
187
+ bindings,
188
+ errors: [],
189
+ map: undefined
190
+ };
191
+ }
192
+ // --- Natural Reactivity Transformer ---
193
+ function transformNaturalReactivity(code, isTs) {
194
+ // If no '$(' or '$q(' or '$qr(' usage, return original code for safety/speed
195
+ if (!code.includes('$(') && !code.includes('$q(') && !code.includes('$qr('))
196
+ return code;
197
+ const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);
198
+ const reactiveVars = new Set();
199
+ const quantumVars = new Set();
200
+ const registerVars = new Set();
201
+ // Pass 1: Identification
202
+ function findReactiveVars(node) {
203
+ if (ts.isVariableStatement(node)) {
204
+ node.declarationList.declarations.forEach(decl => {
205
+ if (decl.initializer && ts.isCallExpression(decl.initializer)) {
206
+ const expr = decl.initializer.expression;
207
+ if (ts.isIdentifier(expr)) {
208
+ if (expr.text === '$') {
209
+ if (ts.isIdentifier(decl.name))
210
+ reactiveVars.add(decl.name.text);
211
+ }
212
+ else if (expr.text === '$q') {
213
+ if (ts.isIdentifier(decl.name))
214
+ quantumVars.add(decl.name.text);
215
+ }
216
+ else if (expr.text === '$qr') {
217
+ if (ts.isIdentifier(decl.name))
218
+ registerVars.add(decl.name.text);
219
+ }
220
+ }
221
+ }
222
+ });
223
+ }
224
+ ts.forEachChild(node, findReactiveVars);
225
+ }
226
+ findReactiveVars(sourceFile);
227
+ if (reactiveVars.size === 0 && quantumVars.size === 0 && registerVars.size === 0)
228
+ return code;
229
+ // Pass 2: Transformation
230
+ const transformerFactory = (context) => {
231
+ return (rootNode) => {
232
+ function visit(node) {
233
+ if (ts.isVariableStatement(node)) {
234
+ const newDecls = [];
235
+ let changed = false;
236
+ node.declarationList.declarations.forEach(decl => {
237
+ if (ts.isIdentifier(decl.name)) {
238
+ const name = decl.name.text;
239
+ if (reactiveVars.has(name) || quantumVars.has(name) || registerVars.has(name)) {
240
+ changed = true;
241
+ let hookName = 'muState';
242
+ if (quantumVars.has(name))
243
+ hookName = 'muQubit';
244
+ else if (registerVars.has(name))
245
+ hookName = 'muRegister';
246
+ const init = decl.initializer;
247
+ newDecls.push(ts.factory.updateVariableDeclaration(decl, decl.name, decl.exclamationToken, decl.type, ts.factory.createCallExpression(ts.factory.createIdentifier(hookName), undefined, init.arguments)));
248
+ }
249
+ else {
250
+ newDecls.push(decl);
251
+ }
252
+ }
253
+ else {
254
+ newDecls.push(decl);
255
+ }
256
+ });
257
+ if (changed) {
258
+ return ts.factory.createVariableStatement(node.modifiers, ts.factory.createVariableDeclarationList(newDecls, ts.NodeFlags.Const));
259
+ }
260
+ }
261
+ if (ts.isCallExpression(node)) {
262
+ const expr = node.expression;
263
+ if (ts.isIdentifier(expr)) {
264
+ const name = expr.text;
265
+ const isMacro = name === '$' || name === '$q' || name === '$qr';
266
+ if (isMacro) {
267
+ // If this call is the initializer of a variable declaration being handled,
268
+ // it will be transformed by the VariableStatement logic above.
269
+ // However, we want to transform it here if it's used as a STANDALONE expression (e.g. assignment).
270
+ const parent = node.parent;
271
+ const isDeclarationInit = ts.isVariableDeclaration(parent) && parent.initializer === node;
272
+ if (!isDeclarationInit) {
273
+ let hookName = 'muState';
274
+ if (name === '$q')
275
+ hookName = 'muQubit';
276
+ else if (name === '$qr')
277
+ hookName = 'muRegister';
278
+ // Transform $(x) -> muState(x).value
279
+ return ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createIdentifier(hookName), undefined, node.arguments), ts.factory.createIdentifier('value'));
280
+ }
281
+ }
282
+ }
283
+ }
284
+ if (ts.isIdentifier(node)) {
285
+ const name = node.text;
286
+ if (reactiveVars.has(name) || quantumVars.has(name) || registerVars.has(name)) {
287
+ const parent = node.parent;
288
+ if (ts.isVariableDeclaration(parent) && parent.name === node)
289
+ return node;
290
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node)
291
+ return node;
292
+ if (ts.isPropertyAccessExpression(parent) && parent.expression === node && parent.name.text === 'value')
293
+ return node;
294
+ if (ts.isPropertyAssignment(parent) && parent.name === node)
295
+ return node;
296
+ // EXEMPTION: Do not add .value if the identifier is a direct argument to a Mulan hook
297
+ // These hooks (muGate, muMeasure, etc.) expect the signal wrapper.
298
+ if (ts.isCallExpression(parent) && ts.isIdentifier(parent.expression)) {
299
+ const hook = parent.expression.text;
300
+ if (COMMON_HOOKS.includes(hook))
301
+ return node;
302
+ }
303
+ return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(name), ts.factory.createIdentifier('value'));
304
+ }
305
+ }
306
+ return ts.visitEachChild(node, visit, context);
307
+ }
308
+ return ts.visitNode(rootNode, visit);
309
+ };
310
+ };
311
+ const result = ts.transform(sourceFile, [transformerFactory]);
312
+ const printer = ts.createPrinter();
313
+ return printer.printFile(result.transformed[0]);
314
+ }
@@ -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
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compileStyle = compileStyle;
4
+ function compileStyle(descriptor, filename) {
5
+ const styles = descriptor.styles;
6
+ if (!styles || styles.length === 0)
7
+ return { css: '', errors: [] };
8
+ let combinedCss = '';
9
+ const scopedId = 'data-v-' + hash(filename);
10
+ styles.forEach(style => {
11
+ let content = style.content;
12
+ if (style.attrs.scoped) {
13
+ // SCSS Compilation
14
+ if (style.attrs.lang === 'scss' || style.attrs.lang === 'sass') {
15
+ try {
16
+ const sass = require('sass');
17
+ const result = sass.compileString(content, {
18
+ syntax: style.attrs.lang === 'sass' ? 'indented' : 'scss'
19
+ });
20
+ content = result.css;
21
+ }
22
+ catch (e) {
23
+ return { css: '', errors: [`SCSS Compilation Error: ${e.message}`] };
24
+ }
25
+ }
26
+ // Rewrite selectors
27
+ // Very naive regex replacer: "selector {" -> "selector[data-v-id] {"
28
+ // This is fragile but works for a PoC.
29
+ content = content.replace(/([^\r\n,{}]+)(?=\{)/g, (match) => {
30
+ const selectors = match.split(',');
31
+ return selectors.map(s => {
32
+ const selector = s.trim();
33
+ if (selector.startsWith('@') || selector === 'from' || selector === 'to')
34
+ return selector; // Skip keyframes/media
35
+ return `${selector}[${scopedId}]`;
36
+ }).join(', ');
37
+ });
38
+ }
39
+ combinedCss += content + '\n';
40
+ });
41
+ return {
42
+ css: combinedCss,
43
+ scopedId,
44
+ errors: []
45
+ };
46
+ }
47
+ // Simple hash for ID generation
48
+ function hash(str) {
49
+ let hash = 0;
50
+ for (let i = 0; i < str.length; i++) {
51
+ const char = str.charCodeAt(i);
52
+ hash = (hash << 5) - hash + char;
53
+ hash &= hash; // Convert to 32bit integer
54
+ }
55
+ return Math.abs(hash).toString(36);
56
+ }