mm_statics 1.6.7 → 1.6.9
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/index.js +13 -13
- package/package.json +7 -6
- package/vue_compiler.js +276 -0
package/index.js
CHANGED
|
@@ -265,19 +265,19 @@ Static.prototype._toAmd = async function (file_type, text) {
|
|
|
265
265
|
* @param {string} filePath 文件路径
|
|
266
266
|
* @returns {Promise<string>} 可直接调用的JavaScript组件代码
|
|
267
267
|
*/
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
268
|
+
Static.prototype._compileVue = async function (code, filePath = '') {
|
|
269
|
+
try {
|
|
270
|
+
// 使用修复后的 Vue 编译器
|
|
271
|
+
const { code: compiledCode } = await vueCompiler.toJS(code, filePath);
|
|
272
|
+
|
|
273
|
+
// Vue 编译后的代码是 JavaScript,应该使用 babel 解析器格式化
|
|
274
|
+
const result = await this._formatCode(compiledCode, 'babel');
|
|
275
|
+
return result;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error('Vue编译失败:', error);
|
|
278
|
+
return `console.error('Vue编译失败: ${error.message}');\nexport default { name: 'ErrorComponent' };`;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
281
|
|
|
282
282
|
/**
|
|
283
283
|
* 发送文件
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_statics",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.9",
|
|
4
4
|
"description": "这是超级美眉statics函数模块,用于web服务端statics缓存",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -32,11 +32,12 @@
|
|
|
32
32
|
"homepage": "https://gitee.com/qiuwenwu91/mm_statics#readme",
|
|
33
33
|
"files": [
|
|
34
34
|
"index.js",
|
|
35
|
+
"vue_compiler.js",
|
|
35
36
|
"eslint.config.js",
|
|
36
37
|
"static/"
|
|
37
38
|
],
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@vue/compiler-sfc": "^3.5.
|
|
40
|
+
"@vue/compiler-sfc": "^3.5.27",
|
|
40
41
|
"abstract-syntax-tree": "^2.22.0",
|
|
41
42
|
"chokidar": "^5.0.0",
|
|
42
43
|
"koa": "^3.1.1",
|
|
@@ -44,13 +45,13 @@
|
|
|
44
45
|
"marked": "^17.0.1",
|
|
45
46
|
"mm_cache": "^1.4.8",
|
|
46
47
|
"mm_es6_to_amd": "^1.4.7",
|
|
47
|
-
"prettier": "^3.
|
|
48
|
+
"prettier": "^3.8.1"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"eslint": "^9.39.2",
|
|
51
|
-
"eslint-plugin-jsdoc": "^
|
|
52
|
-
"mm_eslint": "^1.
|
|
52
|
+
"eslint-plugin-jsdoc": "^62.4.1",
|
|
53
|
+
"mm_eslint": "^1.5.7",
|
|
53
54
|
"mocha": "^11.7.5",
|
|
54
|
-
"supertest": "^7.
|
|
55
|
+
"supertest": "^7.2.2"
|
|
55
56
|
}
|
|
56
57
|
}
|
package/vue_compiler.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// vue-sfc-compiler.js - 修复版
|
|
2
|
+
const { parse, compileScript, compileTemplate, compileStyleAsync } = require('@vue/compiler-sfc');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const prettier = require('prettier');
|
|
5
|
+
|
|
6
|
+
class VueSFCCompiler {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.options = {
|
|
9
|
+
isProduction: process.env.NODE_ENV === 'production',
|
|
10
|
+
sourceMap: true,
|
|
11
|
+
...options
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 编译 .vue 文件为 JS 模块
|
|
17
|
+
* @param {string} source - .vue 文件内容
|
|
18
|
+
* @param {string} filename - 文件名(用于错误提示)
|
|
19
|
+
* @returns {Promise<{code: string, map?: any}>}
|
|
20
|
+
*/
|
|
21
|
+
async toJS(source, filename) {
|
|
22
|
+
// 1. 解析 SFC
|
|
23
|
+
const { descriptor, errors } = parse(source, {
|
|
24
|
+
filename,
|
|
25
|
+
sourceMap: this.options.sourceMap
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (errors.length) {
|
|
29
|
+
throw new Error(`Vue SFC parse error: ${errors[0].message}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 2. 生成组件 ID
|
|
33
|
+
descriptor.id = this.generateComponentId(filename, source);
|
|
34
|
+
|
|
35
|
+
// 3. 编译脚本部分
|
|
36
|
+
const scriptCode = await this.compileScript(descriptor);
|
|
37
|
+
|
|
38
|
+
// 4. 编译模板部分
|
|
39
|
+
const templateCode = await this.compileTemplate(descriptor, filename);
|
|
40
|
+
|
|
41
|
+
// 5. 编译样式部分
|
|
42
|
+
const styleCode = await this.compileStyles(descriptor, filename);
|
|
43
|
+
|
|
44
|
+
// 6. 组合成最终模块
|
|
45
|
+
const code = await this.assembleModule(descriptor, scriptCode, templateCode, styleCode);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
code,
|
|
49
|
+
errors: []
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 生成组件 ID(基于文件内容哈希)
|
|
55
|
+
*/
|
|
56
|
+
generateComponentId(filename, source) {
|
|
57
|
+
const hash = crypto.createHash('sha256');
|
|
58
|
+
hash.update(filename + source);
|
|
59
|
+
return hash.digest('hex').substring(0, 8);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 编译脚本部分
|
|
64
|
+
*/
|
|
65
|
+
async compileScript(descriptor) {
|
|
66
|
+
if (!descriptor.script && !descriptor.scriptSetup) {
|
|
67
|
+
return 'const __script = {};';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const result = compileScript(descriptor, {
|
|
71
|
+
id: descriptor.id,
|
|
72
|
+
isProd: this.options.isProduction,
|
|
73
|
+
sourceMap: this.options.sourceMap,
|
|
74
|
+
inlineTemplate: false
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// 检查是否已经使用了 defineComponent
|
|
78
|
+
const hasDefineComponent = result.content.includes('defineComponent');
|
|
79
|
+
|
|
80
|
+
// 检查是否已经导入了 defineComponent
|
|
81
|
+
const hasDefineComponentImport = result.content.includes('import { defineComponent }') ||
|
|
82
|
+
result.content.includes('import defineComponent');
|
|
83
|
+
|
|
84
|
+
// 移除 export default,改为赋值给 __script 变量
|
|
85
|
+
let scriptContent = result.content || '';
|
|
86
|
+
if (scriptContent.includes('export default')) {
|
|
87
|
+
if (hasDefineComponent && scriptContent.includes('export default defineComponent')) {
|
|
88
|
+
// 如果已经是 export default defineComponent(...),说明组件已经正确包装
|
|
89
|
+
// 直接替换为 const __script = defineComponent(...)
|
|
90
|
+
scriptContent = scriptContent.replace('export default defineComponent', 'const __script = defineComponent');
|
|
91
|
+
} else {
|
|
92
|
+
scriptContent = scriptContent.replace('export default', 'const __script =');
|
|
93
|
+
}
|
|
94
|
+
} else if (scriptContent.includes('export')) {
|
|
95
|
+
// 处理命名导出
|
|
96
|
+
scriptContent = `const __script = {\n${scriptContent}\n};`;
|
|
97
|
+
} else {
|
|
98
|
+
scriptContent = `const __script = {};\n${scriptContent}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return scriptContent;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 编译模板部分
|
|
106
|
+
*/
|
|
107
|
+
async compileTemplate(descriptor, filename) {
|
|
108
|
+
const templateBlock = descriptor.template;
|
|
109
|
+
if (!templateBlock) {
|
|
110
|
+
return '';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const result = compileTemplate({
|
|
114
|
+
source: templateBlock.content,
|
|
115
|
+
filename,
|
|
116
|
+
id: descriptor.id,
|
|
117
|
+
scoped: descriptor.styles.some(s => s.scoped),
|
|
118
|
+
slotted: descriptor.slotted,
|
|
119
|
+
isProd: this.options.isProduction,
|
|
120
|
+
compilerOptions: {
|
|
121
|
+
sourceMap: this.options.sourceMap,
|
|
122
|
+
mode: 'module'
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (result.errors.length) {
|
|
127
|
+
throw new Error(`Template compile error: ${result.errors[0]}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result.code;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 编译样式部分
|
|
135
|
+
*/
|
|
136
|
+
async compileStyles(descriptor, filename) {
|
|
137
|
+
if (!descriptor.styles.length) {
|
|
138
|
+
return '';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let styleCode = '';
|
|
142
|
+
for (let i = 0; i < descriptor.styles.length; i++) {
|
|
143
|
+
const style = descriptor.styles[i];
|
|
144
|
+
const result = await compileStyleAsync({
|
|
145
|
+
source: style.content,
|
|
146
|
+
filename,
|
|
147
|
+
id: style.scoped ? `data-v-${descriptor.id}` : undefined,
|
|
148
|
+
scoped: style.scoped,
|
|
149
|
+
isProd: this.options.isProduction
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (result.errors.length) {
|
|
153
|
+
console.warn(`Style compile warning: ${result.errors[0]}`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 对于 scoped 样式,添加 scopeId 到组件
|
|
158
|
+
if (style.scoped) {
|
|
159
|
+
styleCode += `
|
|
160
|
+
// Scoped styles for ${descriptor.id}
|
|
161
|
+
const __style_${i} = \`${result.code.replace(/`/g, '\\`')}\`;
|
|
162
|
+
`;
|
|
163
|
+
} else {
|
|
164
|
+
styleCode += `
|
|
165
|
+
// Global styles
|
|
166
|
+
const __style_${i} = \`${result.code.replace(/`/g, '\\`')}\`;
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return styleCode;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 组装成最终模块
|
|
176
|
+
*/
|
|
177
|
+
async assembleModule(descriptor, scriptCode, templateCode, styleCode) {
|
|
178
|
+
const hasScopedStyle = descriptor.styles.some(s => s.scoped);
|
|
179
|
+
const scopeId = hasScopedStyle ? `data-v-${descriptor.id}` : null;
|
|
180
|
+
|
|
181
|
+
// 检查脚本部分是否已经导入了 defineComponent
|
|
182
|
+
const hasDefineComponentImport = scriptCode.includes('import { defineComponent }') ||
|
|
183
|
+
scriptCode.includes('import defineComponent');
|
|
184
|
+
|
|
185
|
+
// 检查脚本部分是否已经使用了 defineComponent 函数
|
|
186
|
+
const hasDefineComponentFunction = scriptCode.includes('defineComponent(');
|
|
187
|
+
|
|
188
|
+
// 组装最终代码
|
|
189
|
+
let assembledCode = '';
|
|
190
|
+
|
|
191
|
+
// 添加脚本部分
|
|
192
|
+
if (scriptCode) {
|
|
193
|
+
assembledCode += scriptCode;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 添加模板部分
|
|
197
|
+
if (templateCode) {
|
|
198
|
+
assembledCode += '\n' + templateCode;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 添加样式部分
|
|
202
|
+
if (styleCode) {
|
|
203
|
+
assembledCode += '\n' + styleCode;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 添加组件组装逻辑 - 将 render 函数合并到组件对象中
|
|
207
|
+
assembledCode += `
|
|
208
|
+
// 组装组件
|
|
209
|
+
${scopeId ? `__script.__scopeId = "${scopeId}";` : ''}
|
|
210
|
+
|
|
211
|
+
// 文件信息(开发环境)
|
|
212
|
+
${!this.options.isProduction ? `__script.__file = ${JSON.stringify(descriptor.filename)};` : ''}
|
|
213
|
+
|
|
214
|
+
// 样式注入函数
|
|
215
|
+
function injectStyles() {
|
|
216
|
+
if (typeof document === 'undefined') return;
|
|
217
|
+
|
|
218
|
+
const styleId = 'vue-component-style-' + ${JSON.stringify(descriptor.id)};
|
|
219
|
+
|
|
220
|
+
// 检查样式是否已经注入
|
|
221
|
+
if (document.getElementById(styleId)) return;
|
|
222
|
+
|
|
223
|
+
// 创建 style 元素
|
|
224
|
+
const styleEl = document.createElement('style');
|
|
225
|
+
styleEl.id = styleId;
|
|
226
|
+
styleEl.textContent = __style_0;
|
|
227
|
+
|
|
228
|
+
// 插入到 head
|
|
229
|
+
document.head.appendChild(styleEl);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// 注入样式
|
|
233
|
+
injectStyles();
|
|
234
|
+
|
|
235
|
+
// 将 render 函数合并到组件对象中
|
|
236
|
+
__script.render = render;
|
|
237
|
+
|
|
238
|
+
// 导出组件
|
|
239
|
+
${!hasDefineComponentImport && !hasDefineComponentFunction ? 'import { defineComponent } from \'vue\';\nexport default defineComponent(__script);' : 'export default __script;'}
|
|
240
|
+
`;
|
|
241
|
+
|
|
242
|
+
// 对最终组合代码进行整体格式化
|
|
243
|
+
return await this._formatCode(assembledCode, 'babel');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 格式化代码
|
|
248
|
+
*/
|
|
249
|
+
async _formatCode(code, parser) {
|
|
250
|
+
if (!code || code.trim() === '') {
|
|
251
|
+
return code;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const formattedCode = await prettier.format(code, {
|
|
256
|
+
parser: parser,
|
|
257
|
+
semi: true,
|
|
258
|
+
singleQuote: true,
|
|
259
|
+
trailingComma: 'es5',
|
|
260
|
+
printWidth: 100,
|
|
261
|
+
tabWidth: 2,
|
|
262
|
+
useTabs: false,
|
|
263
|
+
bracketSpacing: true,
|
|
264
|
+
arrowParens: 'avoid'
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return formattedCode;
|
|
268
|
+
} catch (error) {
|
|
269
|
+
// 格式化失败时返回原代码
|
|
270
|
+
console.warn(`格式化代码失败: ${error.message}`);
|
|
271
|
+
return code;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
module.exports = VueSFCCompiler;
|