@mulanjs/mulanjs 1.0.1-dev.20260219161219 → 1.0.1-dev.20260219182444
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 +39 -19
- package/dist/compiler/compiler.js +81 -4
- package/dist/compiler/script-compiler.js +158 -21
- package/dist/compiler/style-compiler.js +61 -4
- package/dist/compiler/template-compiler.js +44 -4
- package/dist/types/compiler/compiler.d.ts +2 -1
- package/dist/types/compiler/script-compiler.d.ts +4 -1
- package/dist/types/compiler/style-compiler.d.ts +2 -1
- package/dist/types/compiler/template-compiler.d.ts +1 -0
- package/dist/types/compiler.d.ts +2 -1
- package/dist/types/script-compiler.d.ts +4 -1
- package/dist/types/style-compiler.d.ts +2 -1
- package/dist/types/template-compiler.d.ts +1 -0
- package/package.json +1 -1
- package/src/compiler/compiler.ts +90 -5
- package/src/compiler/script-compiler.ts +189 -21
- package/src/compiler/style-compiler.ts +28 -4
- package/src/compiler/template-compiler.ts +52 -4
- package/src/loader/index.js +16 -41
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@mulanjs/mulanjs)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
> **"
|
|
6
|
+
> **"The Framework of Power. Zero Runtime Overhead. Pure ASTR Evolution."**
|
|
7
7
|
|
|
8
8
|
MulanJS is a high-performance, next-generation web framework designed to bridge the gap between classic web development and the future of quantum-inspired state management. It is powered by a custom-built Signal Reactivity Engine and ASTR-Q+ architecture.
|
|
9
9
|
|
|
@@ -16,6 +16,7 @@ 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`
|
|
19
20
|
|
|
20
21
|
Want to try the latest features before they are stable? Use the development build.
|
|
21
22
|
|
|
@@ -29,16 +30,35 @@ npx @mulanjs/mulanjs@dev init my-project
|
|
|
29
30
|
npm install @mulanjs/mulanjs@dev
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
*Note: The dev version is updated frequently. You can check the latest version number by running `npm view @mulanjs/mulanjs dist-tags.dev`.*
|
|
33
|
-
|
|
34
33
|
## ⚡ Core Features
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
* **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.
|
|
37
|
+
* **Iron Fortress Security** 🛡️: Built-in "Iron Fortress" security module that auto-sanitizes inputs and generates CSP headers.
|
|
38
|
+
* **Mulan Cycle** 🔄: A redefined component lifecycle (Init, Mount, Destroy) for predictable state management.
|
|
39
|
+
* **TypeScript First** 🟦: Built from the ground up with TypeScript for world-class type safety.
|
|
40
|
+
* **Mulan Power Server** 🚀: Instant, state-preserving Hot Module Replacement (HMR) powered by the Compiler.
|
|
41
|
+
* **Genesis Directives** ✨: Custom syntax (`mu-for`, `mu-if`) compiled directly to high-performance JavaScript.
|
|
42
|
+
|
|
43
|
+
## 🌟 Advanced Capabilities
|
|
44
|
+
|
|
45
|
+
### Mulan Genesis Static (SSG) 🌍
|
|
46
|
+
Pre-render your world with `mulan static`. Perfect for SEO and instant content.
|
|
47
|
+
|
|
48
|
+
### Mulan Anima (FLIP Physics) 🎬
|
|
49
|
+
Just add `mu-animate` to any list for zero-config, 60fps layout transitions.
|
|
50
|
+
|
|
51
|
+
### Mulan Infinity (Big Data) 📈
|
|
52
|
+
Built-in virtualization to render 1,000,000+ rows without performance loss.
|
|
53
|
+
|
|
54
|
+
### Mulan Vault (Persistence) 💾
|
|
55
|
+
State that survives refreshes using `muVault`. Your data, secured in the Iron Fortress.
|
|
56
|
+
|
|
57
|
+
### Mulan Router (Time Travel) 🧭
|
|
58
|
+
Zero-config hash routing with `<mu-link>` for instant, single-page navigation.
|
|
41
59
|
|
|
60
|
+
### Native SCSS Support 🎨
|
|
61
|
+
Built-in support for `<style lang="scss">`. No config required. Just design.
|
|
42
62
|
## 🚀 Quick Start Example
|
|
43
63
|
|
|
44
64
|
MulanJS components (`.mujs`) allow you to write powerful, reactive UI with minimal boilerplate.
|
|
@@ -59,16 +79,16 @@ const debugClick = () => {
|
|
|
59
79
|
<mu-template>
|
|
60
80
|
<div class="page">
|
|
61
81
|
<div class="hero">
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
<h1>Powerful. Fast. MulanJS.</h1>
|
|
83
|
+
<p>The next generation framework for modern web applications.</p>
|
|
84
|
+
|
|
85
|
+
<div class="counter-box">
|
|
86
|
+
<p>Interactive Counter:</p>
|
|
87
|
+
<!-- Native Event Binding -->
|
|
88
|
+
<button class="mu-btn" @click="debugClick()">
|
|
89
|
+
Count is: ${state.count}
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
72
92
|
</div>
|
|
73
93
|
</div>
|
|
74
94
|
</mu-template>
|
|
@@ -103,7 +123,7 @@ p { font-size: 1.2rem; color: #888; max-width: 600px; margin: 0 auto 40px; }
|
|
|
103
123
|
```
|
|
104
124
|
|
|
105
125
|
## Created By
|
|
106
|
-
**Nitin Kumar** (@nitinkumardev09) - *Creator of .mujs & MulanJS Framework*
|
|
126
|
+
**Nitin Kumar** (@nitinkumardev09) - *Creator of .mujs & MulanJS Framework*
|
|
107
127
|
|
|
108
128
|
## License
|
|
109
129
|
MIT
|
|
@@ -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
|
-
|
|
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 //
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
|
209
|
-
// If lineOffsetInBody is
|
|
210
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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, '<').replace(/>/g, '>');
|
|
28
29
|
};
|
|
29
30
|
return ${code};
|
|
30
|
-
}
|
|
31
|
-
|
|
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) ---
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { CompilerOptions } from './script-compiler';
|
|
1
2
|
export interface CompileResult {
|
|
2
3
|
code: string;
|
|
3
4
|
css: string;
|
|
4
5
|
errors: string[];
|
|
5
6
|
map?: string;
|
|
6
7
|
}
|
|
7
|
-
export declare function compileSFC(source: string, filename: string): Promise<CompileResult>;
|
|
8
|
+
export declare function compileSFC(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult>;
|
|
@@ -5,4 +5,7 @@ export interface ScriptCompileResult {
|
|
|
5
5
|
errors: string[];
|
|
6
6
|
map?: string;
|
|
7
7
|
}
|
|
8
|
-
export
|
|
8
|
+
export interface CompilerOptions {
|
|
9
|
+
readFileSync?: (file: string) => string;
|
|
10
|
+
}
|
|
11
|
+
export declare function compileScript(descriptor: SFCDescriptor, options?: CompilerOptions): Promise<ScriptCompileResult>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { SFCDescriptor } from './sfc-parser';
|
|
2
|
+
import { CompilerOptions } from './script-compiler';
|
|
2
3
|
export interface StyleCompileResult {
|
|
3
4
|
css: string;
|
|
4
5
|
scopedId?: string;
|
|
5
6
|
errors: string[];
|
|
6
7
|
}
|
|
7
|
-
export declare function compileStyle(descriptor: SFCDescriptor, filename: string): StyleCompileResult;
|
|
8
|
+
export declare function compileStyle(descriptor: SFCDescriptor, filename: string, options?: CompilerOptions): StyleCompileResult;
|
|
@@ -3,5 +3,6 @@ import { ScriptCompileResult } from './script-compiler';
|
|
|
3
3
|
export interface TemplateCompileResult {
|
|
4
4
|
code: string;
|
|
5
5
|
errors: string[];
|
|
6
|
+
map?: string;
|
|
6
7
|
}
|
|
7
8
|
export declare function compileTemplate(descriptor: SFCDescriptor, scriptResult: ScriptCompileResult, scopedId?: string): TemplateCompileResult;
|
package/dist/types/compiler.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { CompilerOptions } from './script-compiler';
|
|
1
2
|
export interface CompileResult {
|
|
2
3
|
code: string;
|
|
3
4
|
css: string;
|
|
4
5
|
errors: string[];
|
|
5
6
|
map?: string;
|
|
6
7
|
}
|
|
7
|
-
export declare function compileSFC(source: string, filename: string): Promise<CompileResult>;
|
|
8
|
+
export declare function compileSFC(source: string, filename: string, options?: CompilerOptions): Promise<CompileResult>;
|