@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.
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/dist/compiler/compiler.js +90 -0
- package/dist/compiler/script-compiler.js +314 -0
- package/dist/compiler/sfc-parser.js +93 -0
- package/dist/compiler/style-compiler.js +56 -0
- package/dist/compiler/template-compiler.js +442 -0
- package/dist/components/bloch-sphere.js +252 -0
- package/dist/core/component.js +145 -0
- package/dist/core/hooks.js +229 -0
- package/dist/core/quantum.js +284 -0
- package/dist/core/query.js +63 -0
- package/dist/core/reactive.js +105 -0
- package/dist/core/renderer.js +70 -0
- package/dist/core/vault.js +81 -0
- package/dist/index.js +52 -0
- package/dist/mulan.esm.js +1948 -0
- package/dist/mulan.js +215 -0
- package/dist/router/index.js +210 -0
- package/dist/security/sanitizer.js +47 -0
- package/dist/store/index.js +42 -0
- package/dist/types/compiler/compiler.d.ts +7 -0
- package/dist/types/compiler/script-compiler.d.ts +8 -0
- package/dist/types/compiler/sfc-parser.d.ts +21 -0
- package/dist/types/compiler/style-compiler.d.ts +7 -0
- package/dist/types/compiler/template-compiler.d.ts +7 -0
- package/dist/types/compiler.d.ts +7 -0
- package/dist/types/components/bloch-sphere.d.ts +16 -0
- package/dist/types/core/component.d.ts +54 -0
- package/dist/types/core/hooks.d.ts +49 -0
- package/dist/types/core/quantum.d.ts +50 -0
- package/dist/types/core/query.d.ts +14 -0
- package/dist/types/core/reactive.d.ts +21 -0
- package/dist/types/core/renderer.d.ts +4 -0
- package/dist/types/core/vault.d.ts +12 -0
- package/dist/types/index.d.ts +70 -0
- package/dist/types/router/index.d.ts +24 -0
- package/dist/types/script-compiler.d.ts +8 -0
- package/dist/types/security/sanitizer.d.ts +17 -0
- package/dist/types/sfc-parser.d.ts +21 -0
- package/dist/types/store/index.d.ts +10 -0
- package/dist/types/style-compiler.d.ts +7 -0
- package/dist/types/template-compiler.d.ts +7 -0
- package/package.json +64 -0
- package/src/cli/extensions/mulanjs-vscode-1.0.0.vsix +0 -0
- package/src/cli/index.js +600 -0
- package/src/compiler/compiler.ts +102 -0
- package/src/compiler/script-compiler.ts +336 -0
- package/src/compiler/sfc-parser.ts +118 -0
- package/src/compiler/style-compiler.ts +66 -0
- package/src/compiler/template-compiler.ts +519 -0
- package/src/compiler/tsconfig.json +13 -0
- package/src/loader/index.js +81 -0
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
const readline = require('readline');
|
|
8
|
+
|
|
9
|
+
const program = new Command();
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name('mulan')
|
|
13
|
+
.description('MulanJS CLI - The World\'s Most Powerful ASTR-Q Framework CLI')
|
|
14
|
+
.version('1.0.1-dev.0');
|
|
15
|
+
|
|
16
|
+
const LOGO_SVG = `<svg width="50" height="50" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg" class="logo">
|
|
17
|
+
<circle cx="60" cy="60" r="40" fill="#FF4081" opacity="0.1"/>
|
|
18
|
+
<g transform="rotate(0 60 60)">
|
|
19
|
+
<ellipse cx="60" cy="30" rx="15" ry="25" fill="#E91E63" opacity="0.8"/>
|
|
20
|
+
<ellipse cx="60" cy="90" rx="15" ry="25" fill="#E91E63" opacity="0.8"/>
|
|
21
|
+
<ellipse cx="30" cy="60" rx="25" ry="15" fill="#9C27B0" opacity="0.8"/>
|
|
22
|
+
<ellipse cx="90" cy="60" rx="25" ry="15" fill="#9C27B0" opacity="0.8"/>
|
|
23
|
+
</g>
|
|
24
|
+
<g transform="rotate(45 60 60)">
|
|
25
|
+
<ellipse cx="60" cy="30" rx="15" ry="25" fill="#2196F3" opacity="0.8"/>
|
|
26
|
+
<ellipse cx="60" cy="90" rx="15" ry="25" fill="#2196F3" opacity="0.8"/>
|
|
27
|
+
<ellipse cx="30" cy="60" rx="25" ry="15" fill="#3F51B5" opacity="0.8"/>
|
|
28
|
+
<ellipse cx="90" cy="60" rx="25" ry="15" fill="#3F51B5" opacity="0.8"/>
|
|
29
|
+
</g>
|
|
30
|
+
<circle cx="60" cy="60" r="15" fill="#FFC107"/>
|
|
31
|
+
<rect x="55" y="55" width="10" height="3" fill="#000"/>
|
|
32
|
+
<rect x="55" y="60" width="5" height="3" fill="#000"/>
|
|
33
|
+
<rect x="55" y="65" width="10" height="3" fill="#000"/>
|
|
34
|
+
<circle cx="45" cy="45" r="2" fill="#FF9800"/>
|
|
35
|
+
<circle cx="75" cy="45" r="2" fill="#FF9800"/>
|
|
36
|
+
<circle cx="45" cy="75" r="2" fill="#FF9800"/>
|
|
37
|
+
<circle cx="75" cy="75" r="2" fill="#FF9800"/>
|
|
38
|
+
</svg>`;
|
|
39
|
+
|
|
40
|
+
function printBanner() {
|
|
41
|
+
console.log(`
|
|
42
|
+
\x1b[35m█▀▄▀█ █░█ █░░ ▄▀█ █▄░█ \x1b[36m░░█ █▀\x1b[0m
|
|
43
|
+
\x1b[35m█░▀░█ █▄█ █▄▄ █▀█ █░▀█ \x1b[36m█▄█ ▄█\x1b[0m
|
|
44
|
+
|
|
45
|
+
\x1b[33m⚡ THE WORLD'S MOST POWERFUL ASTR-Q FRAMEWORK ⚡\x1b[0m
|
|
46
|
+
`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function askQuestion(query) {
|
|
50
|
+
const rl = readline.createInterface({
|
|
51
|
+
input: process.stdin,
|
|
52
|
+
output: process.stdout,
|
|
53
|
+
});
|
|
54
|
+
return new Promise(resolve => rl.question(query, ans => {
|
|
55
|
+
rl.close();
|
|
56
|
+
resolve(ans);
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
program
|
|
61
|
+
.command('init <project-name>')
|
|
62
|
+
.description('Initialize a new MulanJS project')
|
|
63
|
+
.option('--ts', 'Initialize as TypeScript project')
|
|
64
|
+
.option('--js', 'Initialize as JavaScript project')
|
|
65
|
+
.action(async (projectName, options) => {
|
|
66
|
+
printBanner();
|
|
67
|
+
console.log(`\n🚀 Unleashing MulanJS Power: Preparing ${projectName}...\n`);
|
|
68
|
+
|
|
69
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
70
|
+
const publicPath = path.join(projectPath, 'public');
|
|
71
|
+
const srcPath = path.join(projectPath, 'src');
|
|
72
|
+
const compPath = path.join(srcPath, 'components');
|
|
73
|
+
const pagesPath = path.join(srcPath, 'pages');
|
|
74
|
+
|
|
75
|
+
if (fs.existsSync(projectPath)) {
|
|
76
|
+
console.error(`❌ Error: Directory ${projectName} already exists.`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Language/Mode Selection
|
|
81
|
+
let mode = 0;
|
|
82
|
+
|
|
83
|
+
if (options.ts) {
|
|
84
|
+
mode = 1;
|
|
85
|
+
} else if (options.js) {
|
|
86
|
+
mode = 2;
|
|
87
|
+
} else {
|
|
88
|
+
console.log("Select your Power Level:");
|
|
89
|
+
console.log("1) MulanJS (.mujs + TypeScript) [Recommended]");
|
|
90
|
+
console.log("2) TypeScript (Classic)");
|
|
91
|
+
console.log("3) JavaScript (Legacy)");
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const answer = await askQuestion("Enter choice (1/2/3) [1]: ");
|
|
95
|
+
const choice = answer.trim();
|
|
96
|
+
if (choice === '2') mode = 1;
|
|
97
|
+
else if (choice === '3') mode = 2;
|
|
98
|
+
else mode = 0;
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.warn("Input failed, defaulting to MulanJS.");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const isMuJS = mode === 0;
|
|
105
|
+
const isTS = mode === 0 || mode === 1;
|
|
106
|
+
const fileExt = isMuJS ? 'mujs' : (isTS ? 'ts' : 'js');
|
|
107
|
+
const mainExt = isTS ? 'ts' : 'js';
|
|
108
|
+
|
|
109
|
+
console.log(`\n⚡ Setting up Environment: ${isMuJS ? 'MulanJS' : (isTS ? 'TypeScript' : 'JavaScript')}...`);
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
// 1. Create Directory Structure (Ensure for ALL modes)
|
|
113
|
+
await fs.ensureDir(publicPath);
|
|
114
|
+
await fs.ensureDir(srcPath);
|
|
115
|
+
await fs.ensureDir(compPath);
|
|
116
|
+
await fs.ensureDir(pagesPath);
|
|
117
|
+
|
|
118
|
+
// 2. config files
|
|
119
|
+
const packageJson = {
|
|
120
|
+
name: projectName,
|
|
121
|
+
version: "1.0.0",
|
|
122
|
+
description: "Powered by MulanJS",
|
|
123
|
+
dependencies: {},
|
|
124
|
+
devDependencies: {
|
|
125
|
+
"webpack": "^5.104.1",
|
|
126
|
+
"webpack-cli": "^6.0.1",
|
|
127
|
+
"webpack-dev-server": "^5.2.0",
|
|
128
|
+
"css-loader": "^6.8.1",
|
|
129
|
+
"style-loader": "^3.3.3",
|
|
130
|
+
"sass": "^1.83.0",
|
|
131
|
+
"sass-loader": "^16.0.0"
|
|
132
|
+
},
|
|
133
|
+
scripts: {
|
|
134
|
+
"start": "mulan dev",
|
|
135
|
+
"dev": "mulan dev",
|
|
136
|
+
"build": "mulan build",
|
|
137
|
+
"generate": "mulan generate"
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
if (isTS) {
|
|
142
|
+
packageJson.devDependencies["typescript"] = "^5.0.0";
|
|
143
|
+
packageJson.devDependencies["ts-loader"] = "^9.5.4";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await fs.outputJson(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
147
|
+
|
|
148
|
+
// VS Code Support
|
|
149
|
+
const vscodePath = path.join(projectPath, '.vscode');
|
|
150
|
+
await fs.ensureDir(vscodePath);
|
|
151
|
+
await fs.outputJson(path.join(vscodePath, 'extensions.json'), {
|
|
152
|
+
"recommendations": [
|
|
153
|
+
"mulanjs.mulanjs-vscode"
|
|
154
|
+
]
|
|
155
|
+
}, { spaces: 2 });
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// tsconfig
|
|
159
|
+
if (isTS) {
|
|
160
|
+
const tsConfig = {
|
|
161
|
+
"compilerOptions": {
|
|
162
|
+
"target": "es2016",
|
|
163
|
+
"module": "esnext",
|
|
164
|
+
"moduleResolution": "node",
|
|
165
|
+
"strict": true,
|
|
166
|
+
"esModuleInterop": true,
|
|
167
|
+
"skipLibCheck": true,
|
|
168
|
+
"forceConsistentCasingInFileNames": true,
|
|
169
|
+
"outDir": "./dist"
|
|
170
|
+
},
|
|
171
|
+
"include": ["src/**/*"]
|
|
172
|
+
};
|
|
173
|
+
await fs.outputJson(path.join(projectPath, 'tsconfig.json'), tsConfig, { spaces: 2 });
|
|
174
|
+
|
|
175
|
+
// d.ts for .mujs
|
|
176
|
+
const dts = `declare module '*.mujs' {
|
|
177
|
+
import { MuComponent } from '@mulanjs/mulanjs';
|
|
178
|
+
const component: new (...args: any[]) => MuComponent;
|
|
179
|
+
export default component;
|
|
180
|
+
}`;
|
|
181
|
+
await fs.writeFile(path.join(srcPath, 'mulan-env.d.ts'), dts);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// mulan.config.js
|
|
185
|
+
const mulanConfig = `module.exports = {
|
|
186
|
+
entry: 'src/main.${mainExt}',
|
|
187
|
+
outputDir: 'dist',
|
|
188
|
+
port: 1016
|
|
189
|
+
};`;
|
|
190
|
+
await fs.writeFile(path.join(projectPath, 'mulan.config.js'), mulanConfig);
|
|
191
|
+
|
|
192
|
+
// webpack.config.js
|
|
193
|
+
const ruleTS = isTS ? `{ test: /\\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ },` : '';
|
|
194
|
+
const ruleMuJS = `{ test: /\\.mujs$/, use: '@mulanjs/mulanjs/loader' },`;
|
|
195
|
+
|
|
196
|
+
const webpackConfig = `const path = require('path');
|
|
197
|
+
|
|
198
|
+
module.exports = {
|
|
199
|
+
mode: 'development',
|
|
200
|
+
entry: './src/main.${mainExt}',
|
|
201
|
+
output: {
|
|
202
|
+
filename: 'bundle.js',
|
|
203
|
+
path: path.resolve(__dirname, 'dist'),
|
|
204
|
+
clean: true,
|
|
205
|
+
},
|
|
206
|
+
resolve: {
|
|
207
|
+
extensions: ['.ts', '.js', '.mujs']
|
|
208
|
+
},
|
|
209
|
+
devServer: {
|
|
210
|
+
static: {
|
|
211
|
+
directory: path.join(__dirname, 'public'),
|
|
212
|
+
},
|
|
213
|
+
port: 1016,
|
|
214
|
+
open: true,
|
|
215
|
+
hot: true,
|
|
216
|
+
historyApiFallback: true,
|
|
217
|
+
},
|
|
218
|
+
module: {
|
|
219
|
+
rules: [
|
|
220
|
+
${ruleTS}
|
|
221
|
+
${ruleMuJS}
|
|
222
|
+
{ test: /\.s[ac]ss$/i, use: ["style-loader", "css-loader", "sass-loader"] },
|
|
223
|
+
{ test: /\\.css$/i, use: ["style-loader", "css-loader"] },
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
};`;
|
|
227
|
+
await fs.writeFile(path.join(projectPath, 'webpack.config.js'), webpackConfig);
|
|
228
|
+
|
|
229
|
+
// Index HTML
|
|
230
|
+
const indexHtml = `<!DOCTYPE html>
|
|
231
|
+
<html lang="en">
|
|
232
|
+
<head>
|
|
233
|
+
<meta charset="UTF-8">
|
|
234
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
235
|
+
<title>${projectName} - MulanJS</title>
|
|
236
|
+
</head>
|
|
237
|
+
<body>
|
|
238
|
+
<div id="app"></div>
|
|
239
|
+
<div id="router-outlet"></div>
|
|
240
|
+
<script src="/bundle.js"></script>
|
|
241
|
+
</body>
|
|
242
|
+
</html>`;
|
|
243
|
+
await fs.writeFile(path.join(publicPath, 'index.html'), indexHtml);
|
|
244
|
+
|
|
245
|
+
// Styles
|
|
246
|
+
const styleCss = `
|
|
247
|
+
body { font-family: 'Inter', sans-serif; background: #0a0a0a; color: #fff; margin: 0; }
|
|
248
|
+
.mu-btn {
|
|
249
|
+
background: linear-gradient(45deg, #e94560, #9c27b0);
|
|
250
|
+
border: none; color: white; padding: 10px 20px;
|
|
251
|
+
border-radius: 6px; cursor: pointer; font-size: 1rem;
|
|
252
|
+
transition: transform 0.1s;
|
|
253
|
+
}
|
|
254
|
+
.mu-btn:active { transform: scale(0.95); }
|
|
255
|
+
`;
|
|
256
|
+
await fs.writeFile(path.join(srcPath, 'style.css'), styleCss);
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
// --- COMPONENT GENERATION (Unified Templates) ---
|
|
260
|
+
|
|
261
|
+
const headerTemplate = (isMuJS) ?
|
|
262
|
+
`<script setup lang="ts">
|
|
263
|
+
</script>
|
|
264
|
+
<mu-template>
|
|
265
|
+
<header>
|
|
266
|
+
<div class="logo-container">
|
|
267
|
+
${LOGO_SVG}
|
|
268
|
+
<div class="brand">MulanJS</div>
|
|
269
|
+
</div>
|
|
270
|
+
<nav>
|
|
271
|
+
<mu-link to="/">Home</mu-link>
|
|
272
|
+
<mu-link to="/about">About</mu-link>
|
|
273
|
+
</nav>
|
|
274
|
+
</header>
|
|
275
|
+
</mu-template>
|
|
276
|
+
<style scoped>
|
|
277
|
+
header {
|
|
278
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
279
|
+
padding: 15px 30px; background: rgba(20, 20, 20, 0.95);
|
|
280
|
+
border-bottom: 1px solid #333; backdrop-filter: blur(10px);
|
|
281
|
+
position: sticky; top: 0; z-index: 100;
|
|
282
|
+
}
|
|
283
|
+
.logo-container { display: flex; align-items: center; gap: 15px; }
|
|
284
|
+
.brand { font-weight: bold; font-size: 1.5rem; letter-spacing: -0.5px; }
|
|
285
|
+
nav mu-link { color: #aaa; margin-left: 20px; text-decoration: none; font-weight: 500; transition: color 0.2s; display: inline-block; }
|
|
286
|
+
nav mu-link:hover { color: #e94560; }
|
|
287
|
+
.logo { height: 40px; width: 40px; animation: spin 20s linear infinite; }
|
|
288
|
+
@keyframes spin { 100% { transform: rotate(360deg); } }
|
|
289
|
+
</style>` : `import { Component } from '@mulanjs/mulanjs';
|
|
290
|
+
|
|
291
|
+
export class Header extends Component {
|
|
292
|
+
template${isTS ? '(): string' : ''} {
|
|
293
|
+
return \`
|
|
294
|
+
<header>
|
|
295
|
+
<div class="logo-container">
|
|
296
|
+
${LOGO_SVG}
|
|
297
|
+
<div class="brand">MulanJS ${isTS ? 'TS' : 'JS'}</div>
|
|
298
|
+
</div>
|
|
299
|
+
<nav>
|
|
300
|
+
<mu-link to="/">Home</mu-link>
|
|
301
|
+
<mu-link to="/about">About</mu-link>
|
|
302
|
+
</nav>
|
|
303
|
+
</header>
|
|
304
|
+
<style>
|
|
305
|
+
header {
|
|
306
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
307
|
+
padding: 15px 30px; background: rgba(20, 20, 20, 0.95);
|
|
308
|
+
border-bottom: 1px solid #333; backdrop-filter: blur(10px);
|
|
309
|
+
position: sticky; top: 0; z-index: 100;
|
|
310
|
+
}
|
|
311
|
+
.logo-container { display: flex; align-items: center; gap: 15px; }
|
|
312
|
+
.brand { font-weight: bold; font-size: 1.5rem; letter-spacing: -0.5px; }
|
|
313
|
+
nav mu-link { color: #aaa; margin-left: 20px; text-decoration: none; font-weight: 500; transition: color 0.2s; display: inline-block; }
|
|
314
|
+
nav mu-link:hover { color: #e94560; }
|
|
315
|
+
.logo { height: 40px; width: 40px; animation: spin 20s linear infinite; }
|
|
316
|
+
@keyframes spin { 100% { transform: rotate(360deg); } }
|
|
317
|
+
</style>
|
|
318
|
+
\`;
|
|
319
|
+
}
|
|
320
|
+
}`;
|
|
321
|
+
|
|
322
|
+
// Home Page with Counter
|
|
323
|
+
const homeTemplate = (isMuJS) ?
|
|
324
|
+
`<script setup lang="ts">
|
|
325
|
+
import { muState } from '@mulanjs/mulanjs';
|
|
326
|
+
const state = muState({ count: 0 });
|
|
327
|
+
</script>
|
|
328
|
+
<mu-template>
|
|
329
|
+
<div class="page">
|
|
330
|
+
<div class="hero">
|
|
331
|
+
<h1>Powerful. Fast. MulanJS.</h1>
|
|
332
|
+
<p>The next generation framework for modern web applications.</p>
|
|
333
|
+
|
|
334
|
+
<div class="counter-box">
|
|
335
|
+
<p>Interactive Counter:</p>
|
|
336
|
+
<button class="mu-btn" @click="state.count++">
|
|
337
|
+
Count is: \${state.count}
|
|
338
|
+
</button>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
</mu-template>
|
|
343
|
+
<style scoped>
|
|
344
|
+
.page { padding: 60px 20px; text-align: center; }
|
|
345
|
+
h1 { font-size: 4rem; background: linear-gradient(to right, #e94560, #9c27b0); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 20px; }
|
|
346
|
+
p { font-size: 1.2rem; color: #888; max-width: 600px; margin: 0 auto 40px; }
|
|
347
|
+
.counter-box { background: #1a1a1a; padding: 30px; border-radius: 12px; display: inline-block; border: 1px solid #333; }
|
|
348
|
+
</style>` : `import { Component, muState } from '@mulanjs/mulanjs';
|
|
349
|
+
|
|
350
|
+
export class Home extends Component {
|
|
351
|
+
constructor(container${isTS ? ': HTMLElement' : ''}) {
|
|
352
|
+
super(container);
|
|
353
|
+
this.state = muState({ count: 0 });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
template${isTS ? '(): string' : ''} {
|
|
357
|
+
return \`
|
|
358
|
+
<div class="page">
|
|
359
|
+
<div class="hero">
|
|
360
|
+
<h1>Powerful. Fast. MulanJS.</h1>
|
|
361
|
+
<p>Welcome to the Classic ${isTS ? 'TypeScript' : 'JavaScript'} Era.</p>
|
|
362
|
+
|
|
363
|
+
<div class="counter-box">
|
|
364
|
+
<p>Interactive Counter:</p>
|
|
365
|
+
<button class="mu-btn" onclick="\${this.bind(() => this.state.count++)}">
|
|
366
|
+
Count is: \${this.state.count}
|
|
367
|
+
</button>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
<style>
|
|
372
|
+
.page { padding: 60px 20px; text-align: center; }
|
|
373
|
+
h1 { font-size: 4rem; background: linear-gradient(to right, #e94560, #9c27b0); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 20px; }
|
|
374
|
+
p { font-size: 1.2rem; color: #888; max-width: 600px; margin: 0 auto 40px; }
|
|
375
|
+
.counter-box { background: #1a1a1a; padding: 30px; border-radius: 12px; display: inline-block; border: 1px solid #333; }
|
|
376
|
+
</style>
|
|
377
|
+
\`;
|
|
378
|
+
}
|
|
379
|
+
}`;
|
|
380
|
+
|
|
381
|
+
// About Page
|
|
382
|
+
const aboutTemplate = (isMuJS) ?
|
|
383
|
+
`<mu-template>
|
|
384
|
+
<div class="page">
|
|
385
|
+
<h1>About Us</h1>
|
|
386
|
+
<p>Building the future with MulanJS.</p>
|
|
387
|
+
</div>
|
|
388
|
+
</mu-template>
|
|
389
|
+
<style scoped src="./About.css"></style>
|
|
390
|
+
` : `import { Component } from '@mulanjs/mulanjs';
|
|
391
|
+
|
|
392
|
+
export class About extends Component {
|
|
393
|
+
template${isTS ? '(): string' : ''} {
|
|
394
|
+
return \`
|
|
395
|
+
<div class="page">
|
|
396
|
+
<div style="text-align:center; padding: 40px;">
|
|
397
|
+
<h1>About Us</h1>
|
|
398
|
+
<p>Building the future with MulanJS Classic.</p>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
\`;
|
|
402
|
+
}
|
|
403
|
+
}`;
|
|
404
|
+
|
|
405
|
+
// Write Component Files
|
|
406
|
+
await fs.writeFile(path.join(compPath, `Header.${fileExt}`), headerTemplate);
|
|
407
|
+
await fs.writeFile(path.join(pagesPath, `Home.${fileExt}`), homeTemplate);
|
|
408
|
+
await fs.writeFile(path.join(pagesPath, `About.${fileExt}`), aboutTemplate);
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
if (isMuJS) {
|
|
412
|
+
const aboutCss = `.page { padding: 40px; text-align: center; }`;
|
|
413
|
+
await fs.writeFile(path.join(pagesPath, 'About.css'), aboutCss);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Main Entry Point (Unified)
|
|
417
|
+
const mainContent = (isMuJS) ?
|
|
418
|
+
`import { Router } from '@mulanjs/mulanjs';
|
|
419
|
+
import Home from './pages/Home.mujs';
|
|
420
|
+
import About from './pages/About.mujs';
|
|
421
|
+
import Header from './components/Header.mujs';
|
|
422
|
+
import './style.css';
|
|
423
|
+
|
|
424
|
+
const app = document.getElementById('app');
|
|
425
|
+
if (app) {
|
|
426
|
+
app.innerHTML = '<div id="header-mount"></div><div id="router-mount"></div>';
|
|
427
|
+
|
|
428
|
+
const headerMount = document.getElementById('header-mount');
|
|
429
|
+
if (headerMount) new Header(headerMount).mount();
|
|
430
|
+
|
|
431
|
+
const routerMount = document.getElementById('router-mount');
|
|
432
|
+
if (routerMount) {
|
|
433
|
+
new Router([
|
|
434
|
+
{ path: '/', component: Home },
|
|
435
|
+
{ path: '/about', component: About }
|
|
436
|
+
], routerMount);
|
|
437
|
+
}
|
|
438
|
+
}` :
|
|
439
|
+
`import { Router } from '@mulanjs/mulanjs';
|
|
440
|
+
import { Home } from './pages/Home';
|
|
441
|
+
import { About } from './pages/About';
|
|
442
|
+
import { Header } from './components/Header';
|
|
443
|
+
import './style.css';
|
|
444
|
+
|
|
445
|
+
const app = document.getElementById('app');
|
|
446
|
+
if (app) {
|
|
447
|
+
app.innerHTML = '<div id="header-mount"></div><div id="router-mount"></div>';
|
|
448
|
+
|
|
449
|
+
// Mount Header
|
|
450
|
+
const headerMount = document.getElementById('header-mount');
|
|
451
|
+
if (headerMount) new Header(headerMount).mount();
|
|
452
|
+
|
|
453
|
+
const routerMount = document.getElementById('router-mount');
|
|
454
|
+
if (routerMount) {
|
|
455
|
+
new Router([
|
|
456
|
+
{ path: '/', component: Home },
|
|
457
|
+
{ path: '/about', component: About }
|
|
458
|
+
], routerMount);
|
|
459
|
+
}
|
|
460
|
+
}`;
|
|
461
|
+
await fs.writeFile(path.join(srcPath, `main.${mainExt}`), mainContent);
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
console.log(`\n✅ Project ${projectName} created successfully!`);
|
|
465
|
+
console.log(`\n👉 Next Steps:`);
|
|
466
|
+
console.log(` 1. cd ${projectName}`);
|
|
467
|
+
console.log(` 2. npm install`);
|
|
468
|
+
console.log(` 3. npm link @mulanjs/mulanjs`);
|
|
469
|
+
console.log(` 4. npm run dev`);
|
|
470
|
+
|
|
471
|
+
// Auto-install VS Code Extension
|
|
472
|
+
try {
|
|
473
|
+
const vsixPath = path.join(__dirname, 'extensions', 'mulanjs-vscode-1.0.0.vsix');
|
|
474
|
+
if (fs.existsSync(vsixPath)) {
|
|
475
|
+
console.log('\n📦 Installing MulanJS VS Code Extension...');
|
|
476
|
+
execSync(`code --install-extension "${vsixPath}" --force`, { stdio: 'inherit' });
|
|
477
|
+
console.log('✅ Extension installed successfully!');
|
|
478
|
+
}
|
|
479
|
+
} catch (e) {
|
|
480
|
+
console.warn('⚠️ Could not install VS Code extension (VS Code CLI not found or failed).');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error('Failed to create project:', error);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const net = require('net');
|
|
490
|
+
|
|
491
|
+
async function findAvailablePort(startPort) {
|
|
492
|
+
const isPortAvailable = (port) => {
|
|
493
|
+
return new Promise((resolve) => {
|
|
494
|
+
const server = net.createServer();
|
|
495
|
+
server.once('error', () => resolve(false));
|
|
496
|
+
server.once('listening', () => {
|
|
497
|
+
server.close();
|
|
498
|
+
resolve(true);
|
|
499
|
+
});
|
|
500
|
+
server.listen(port);
|
|
501
|
+
});
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
let port = startPort;
|
|
505
|
+
while (!(await isPortAvailable(port))) {
|
|
506
|
+
port++;
|
|
507
|
+
}
|
|
508
|
+
return port;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
program
|
|
512
|
+
.command('dev [args...]')
|
|
513
|
+
.description('Start the Mulan Development Server')
|
|
514
|
+
.action(async () => {
|
|
515
|
+
try {
|
|
516
|
+
const port = await findAvailablePort(1016);
|
|
517
|
+
console.log(`🚀 Starting Mulan Power Server on port ${port}...`);
|
|
518
|
+
|
|
519
|
+
if (!fs.existsSync(path.join(process.cwd(), 'webpack.config.js'))) {
|
|
520
|
+
console.warn("⚠️ webpack.config.js not found.");
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Pass the port to webpack-dev-server via --port flag.
|
|
524
|
+
// Note: This overrides the port in webpack.config.js if set there.
|
|
525
|
+
execSync(`npx webpack serve --port ${port}`, { stdio: 'inherit' });
|
|
526
|
+
} catch (e) {
|
|
527
|
+
console.error('❌ Failed to start server:', e.message);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
program
|
|
532
|
+
.command('build [args...]')
|
|
533
|
+
.description('Build for production')
|
|
534
|
+
.action(() => {
|
|
535
|
+
try {
|
|
536
|
+
execSync('npx webpack --mode production', { stdio: 'inherit' });
|
|
537
|
+
} catch (e) {
|
|
538
|
+
console.error('❌ Build failed:', e.message);
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
program
|
|
543
|
+
.command('generate <type> <name>')
|
|
544
|
+
.alias('g')
|
|
545
|
+
.description('Generate a new MulanJS resource')
|
|
546
|
+
.action(async (type, name) => {
|
|
547
|
+
if (type === 'component') {
|
|
548
|
+
const compName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
549
|
+
console.log(`⚡ Generating Component: ${compName}...`);
|
|
550
|
+
const ext = 'mujs';
|
|
551
|
+
const content = `<script setup lang="ts">
|
|
552
|
+
import { muState } from '@mulanjs/mulanjs';
|
|
553
|
+
const state = muState({ count: 0 });
|
|
554
|
+
</script>
|
|
555
|
+
<mu-template>
|
|
556
|
+
<div class="mu-comp">
|
|
557
|
+
<h2>${compName} Component</h2>
|
|
558
|
+
</div>
|
|
559
|
+
</mu-template>
|
|
560
|
+
<style scoped>
|
|
561
|
+
.mu-comp { padding: 20px; }
|
|
562
|
+
</style>`;
|
|
563
|
+
const filePath = path.join(process.cwd(), 'src', 'components', `${compName}.${ext}`);
|
|
564
|
+
try {
|
|
565
|
+
await fs.ensureDir(path.join(process.cwd(), 'src', 'components'));
|
|
566
|
+
if (await fs.pathExists(filePath)) return console.error('Exists!');
|
|
567
|
+
await fs.writeFile(filePath, content);
|
|
568
|
+
console.log(`✅ Created: ${filePath}`);
|
|
569
|
+
} catch (e) {
|
|
570
|
+
console.error('❌ Generation failed:', e);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
program
|
|
576
|
+
.command('install-extension')
|
|
577
|
+
.description('Install the MulanJS VS Code Extension for icons and syntax highlighting')
|
|
578
|
+
.action(async () => {
|
|
579
|
+
console.log('📦 Locating MulanJS VS Code Extension...');
|
|
580
|
+
try {
|
|
581
|
+
// Find where mulanjs is installed. If run via npx, __dirname is the cli folder.
|
|
582
|
+
const vsixPath = path.join(__dirname, 'extensions', 'mulanjs-vscode-1.0.0.vsix');
|
|
583
|
+
|
|
584
|
+
if (!fs.existsSync(vsixPath)) {
|
|
585
|
+
console.error('❌ Error: Extension file not found at:', vsixPath);
|
|
586
|
+
console.log('Ensure you have a full installation of the mulanjs package.');
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
console.log('🚀 Installing extension via VS Code CLI...');
|
|
591
|
+
execSync(`code --install-extension "${vsixPath}" --force`, { stdio: 'inherit' });
|
|
592
|
+
console.log('✅ Success! Restart VS Code to see your new .mujs icons.');
|
|
593
|
+
} catch (e) {
|
|
594
|
+
console.error('\n❌ Failed to install extension.');
|
|
595
|
+
console.log('Make sure "code" (VS Code CLI) is in your PATH.');
|
|
596
|
+
console.log('Manual Install: You can also drag the .vsix file from your node_modules/mulanjs/src/cli/extensions into VS Code.');
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
|
|
2
|
+
import { parseMUJS } from './sfc-parser';
|
|
3
|
+
import { compileScript } from './script-compiler';
|
|
4
|
+
import { compileTemplate } from './template-compiler';
|
|
5
|
+
import { compileStyle } from './style-compiler';
|
|
6
|
+
|
|
7
|
+
export interface CompileResult {
|
|
8
|
+
code: string;
|
|
9
|
+
css: string;
|
|
10
|
+
errors: string[];
|
|
11
|
+
map?: string; // Source Map
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function compileSFC(source: string, filename: string): CompileResult {
|
|
15
|
+
// 1. Parse
|
|
16
|
+
const descriptor = parseMUJS(source, filename);
|
|
17
|
+
|
|
18
|
+
// 2. Script
|
|
19
|
+
const scriptResult = compileScript(descriptor);
|
|
20
|
+
// Replace export default to capture the component
|
|
21
|
+
let scriptCode = scriptResult.code.replace('export default', 'const __component__ =');
|
|
22
|
+
if (!scriptCode.includes('const __component__ =')) {
|
|
23
|
+
scriptCode = scriptCode.replace(/export\s+default/, 'const __component__ =');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 3. Style
|
|
27
|
+
const styleResult = compileStyle(descriptor, filename);
|
|
28
|
+
|
|
29
|
+
// 4. Template
|
|
30
|
+
const templateResult = compileTemplate(descriptor, scriptResult, styleResult.scopedId);
|
|
31
|
+
|
|
32
|
+
// 5. Assembly
|
|
33
|
+
const cssInjection = styleResult.css ? `
|
|
34
|
+
if (typeof document !== 'undefined') {
|
|
35
|
+
const style = document.createElement('style');
|
|
36
|
+
style.textContent = \`${styleResult.css}\`;
|
|
37
|
+
document.head.appendChild(style);
|
|
38
|
+
}
|
|
39
|
+
` : '';
|
|
40
|
+
|
|
41
|
+
const finalCode = `${scriptCode}
|
|
42
|
+
|
|
43
|
+
${templateResult.code}
|
|
44
|
+
|
|
45
|
+
// Attach template to component
|
|
46
|
+
if (__component__.prototype) {
|
|
47
|
+
// Class component
|
|
48
|
+
__component__.prototype.template = render;
|
|
49
|
+
} else if (typeof __component__ === 'object') {
|
|
50
|
+
// Options object
|
|
51
|
+
__component__.template = render;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
${cssInjection}
|
|
55
|
+
|
|
56
|
+
// --- MulanJS HMR Engine ---
|
|
57
|
+
if (typeof module !== 'undefined' && module.hot) {
|
|
58
|
+
const hmrId = "${filename.replace(/\\/g, '/')}";
|
|
59
|
+
window.__MULAN_HMR__ = window.__MULAN_HMR__ || {
|
|
60
|
+
records: new Map(),
|
|
61
|
+
notify: (id, newComp) => {
|
|
62
|
+
const oldRecord = window.__MULAN_HMR__.records.get(id);
|
|
63
|
+
if (oldRecord) {
|
|
64
|
+
console.log('[MulanJS HMR] Patching:', id);
|
|
65
|
+
if (oldRecord.prototype && newComp.prototype) {
|
|
66
|
+
oldRecord.prototype.template = newComp.prototype.template;
|
|
67
|
+
// Also patch setup if available (for Functional components)
|
|
68
|
+
if (newComp._setup) oldRecord._setup = newComp._setup;
|
|
69
|
+
} else {
|
|
70
|
+
Object.assign(oldRecord, newComp);
|
|
71
|
+
}
|
|
72
|
+
if (window.__MULAN_REFRESH__) {
|
|
73
|
+
console.log('[MulanJS HMR] Triggering Deep Refresh');
|
|
74
|
+
window.__MULAN_REFRESH__();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (window.__MULAN_HMR__.records.has(hmrId)) {
|
|
81
|
+
window.__MULAN_HMR__.notify(hmrId, __component__);
|
|
82
|
+
} else {
|
|
83
|
+
window.__MULAN_HMR__.records.set(hmrId, __component__);
|
|
84
|
+
}
|
|
85
|
+
module.hot.accept();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// console.log('[MulanJS] Component created:', __component__);
|
|
89
|
+
export default __component__;
|
|
90
|
+
|
|
91
|
+
// Helper to get relative path for sourceURL
|
|
92
|
+
// This ensures the generated file appears in a readable folder structure in DevTools
|
|
93
|
+
// SourceURL removed. Using standard Source Maps only.
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
code: finalCode,
|
|
98
|
+
css: styleResult.css,
|
|
99
|
+
errors: [...scriptResult.errors, ...templateResult.errors],
|
|
100
|
+
map: scriptResult.map // Pass the map
|
|
101
|
+
};
|
|
102
|
+
}
|