pug-site-core 2.0.22 → 3.0.0

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.
@@ -0,0 +1,548 @@
1
+ import express from "express";
2
+ import fse from "fs-extra";
3
+ import path from "path";
4
+ import paths from "../paths.js";
5
+
6
+ /**
7
+ * 调试路由处理模块
8
+ * 专门处理调试相关的API路由和业务逻辑
9
+ */
10
+
11
+ /**
12
+ * 处理来自客户端的调试信息
13
+ * @param {Object} debugData - 调试数据
14
+ * @returns {Object} 处理结果
15
+ */
16
+ export function handleDebugInfo(debugData) {
17
+ console.log('收到调试信息:', {
18
+ 时间: debugData.timestamp,
19
+ 页面: debugData.url,
20
+ 文件: debugData.file,
21
+ 行号: debugData.line,
22
+ 标签: debugData.tag,
23
+ ID: debugData.id,
24
+ 类名: debugData.className,
25
+ 文本内容: debugData.textContent,
26
+ 编辑标识: debugData.editableKey
27
+ });
28
+
29
+ // 这里可以添加更多的处理逻辑,比如:
30
+ // 1. 记录到日志文件
31
+ // 2. 发送到外部监控系统
32
+ // 3. 触发 VS Code 等编辑器打开对应文件
33
+ // 4. 存储到数据库等
34
+
35
+ return {
36
+ success: true,
37
+ message: '调试信息已记录',
38
+ data: debugData
39
+ };
40
+ }
41
+
42
+ /**
43
+ * 处理来自客户端的文本编辑请求
44
+ * @param {Object} editData - 编辑数据
45
+ * @returns {Object} 处理结果
46
+ */
47
+ export async function handleTextEdit(editData) {
48
+ console.log('收到文本编辑请求:', {
49
+ 时间: editData.timestamp,
50
+ 页面: editData.url,
51
+ 文件: editData.file,
52
+ 行号: editData.line,
53
+ 编辑标识: editData.editableKey,
54
+ 原始文本: editData.originalText,
55
+ 新文本: editData.newText,
56
+ });
57
+
58
+ try {
59
+ if (editData.editableKey) {
60
+ if (editData.editableKey === 'true') {
61
+ // 普通pug模板文件处理
62
+ return updateTemplateFile(editData);
63
+ } else {
64
+ return await updateLanguageFile(editData.editableKey, editData.newText, editData.originalText);
65
+ }
66
+ }
67
+
68
+ return {
69
+ success: false,
70
+ message: '无法解析编辑标识,无法更新文件',
71
+ data: editData
72
+ };
73
+
74
+ } catch (error) {
75
+ console.error('处理文本编辑时出错:', error);
76
+ return {
77
+ success: false,
78
+ message: '文件更新失败: ' + error.message,
79
+ error: error.message
80
+ };
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 更新pug模板文件
86
+ * @param {Object} editData - 编辑数据
87
+ * @returns {Object} 处理结果
88
+ */
89
+ function updateTemplateFile(editData) {
90
+ try {
91
+ // 构建文件路径,基于template-debug
92
+ const filePath = path.join(paths.template.debug, editData.file);
93
+
94
+ if (!fse.existsSync(filePath)) {
95
+ return {
96
+ success: false,
97
+ message: `文件不存在: ${filePath}`
98
+ };
99
+ }
100
+
101
+ // 读取文件内容
102
+ const fileContent = fse.readFileSync(filePath, 'utf8');
103
+ const lines = fileContent.split('\n');
104
+
105
+ // 检查行号是否有效
106
+ const lineIndex = parseInt(editData.line) - 1; // 转换为0基索引
107
+ if (lineIndex < 0 || lineIndex >= lines.length) {
108
+ return {
109
+ success: false,
110
+ message: `行号无效: ${editData.line},文件共有 ${lines.length} 行`
111
+ };
112
+ }
113
+
114
+ // 替换指定行中的文本
115
+ const originalLine = lines[lineIndex];
116
+ const newLine = originalLine.replace(editData.originalText, editData.newText);
117
+
118
+ if (originalLine === newLine) {
119
+ return {
120
+ success: false,
121
+ message: `在第 ${editData.line} 行未找到要替换的文本: "${editData.originalText}"`
122
+ };
123
+ }
124
+
125
+ lines[lineIndex] = newLine;
126
+
127
+ // 写回文件
128
+ const newContent = lines.join('\n');
129
+ fse.writeFileSync(filePath, newContent, 'utf8');
130
+
131
+ console.log(`成功更新模板文件: ${filePath}`);
132
+ console.log(`第 ${editData.line} 行: "${originalLine.trimStart()}" -> "${newLine.trimStart()}"`);
133
+
134
+ return {
135
+ success: true,
136
+ message: `模板文件已更新: "${editData.originalText}" → "${editData.newText}"`,
137
+ data: {
138
+ filePath,
139
+ line: editData.line,
140
+ oldLine: originalLine,
141
+ newLine: newLine
142
+ }
143
+ };
144
+
145
+ } catch (error) {
146
+ console.error('更新模板文件时出错:', error);
147
+ return {
148
+ success: false,
149
+ message: '更新模板文件失败: ' + error.message,
150
+ error: error.message
151
+ };
152
+ }
153
+ }
154
+
155
+ /**
156
+ * 更新语言数据文件
157
+ * @param {string} editableKey - 键路径,如 'us,video,recommendTitle'
158
+ * @param {string} newText - 新文本
159
+ * @param {string} originalText - 原始文本
160
+ * @returns {Object} 处理结果
161
+ */
162
+ async function updateLanguageFile(editableKey, newText, originalText) {
163
+ try {
164
+ const keyArray = editableKey.split(',');
165
+ // 直接从languageData.js导入语言数据对象
166
+ let languageData = (await import(paths.languageData)).default;
167
+ let data = languageData[keyArray.shift()];
168
+ while(keyArray.length > 0) {
169
+ data = data[keyArray.shift()];
170
+ if (data === 'undefined') {
171
+ throw new Error(`键"${editableKey}"的内容不存在`);
172
+ }
173
+ }
174
+ // 验证原始文本是否匹配
175
+ if (data !== originalText) {
176
+ throw new Error(`键"${editableKey}"的内容不匹配。期望: "${originalText}",实际: "${data}"`);
177
+ }
178
+ // 重新导航并修改值
179
+ const originalKeyArray = editableKey.split(',');
180
+ let current = languageData;
181
+ for (let i = 0; i < originalKeyArray.length - 1; i++) {
182
+ current = current[originalKeyArray[i]];
183
+ }
184
+ const lastKey = originalKeyArray[originalKeyArray.length - 1];
185
+ current[lastKey] = newText;
186
+
187
+ // 生成新的文件内容
188
+ const newFileContent = generateLanguageFileContent(languageData);
189
+
190
+ // 构建languageData.js文件路径
191
+ const languageFilePath = paths.resolveRoot("languageData.js");
192
+
193
+ // 写回文件
194
+ fse.writeFileSync(languageFilePath, newFileContent, 'utf8');
195
+
196
+ console.log(`成功更新语言文件: ${languageFilePath}`);
197
+ console.log(`键路径 ${editableKey}: "${originalText}" -> "${newText}"`);
198
+
199
+ return {
200
+ success: true,
201
+ message: `语言文件已更新: "${originalText}" → "${newText}"`,
202
+ data: {
203
+ filePath: languageFilePath,
204
+ keyPath: editableKey,
205
+ oldValue: originalText,
206
+ newValue: newText
207
+ }
208
+ };
209
+
210
+ } catch (error) {
211
+ console.error('更新语言文件时出错:', error);
212
+ return {
213
+ success: false,
214
+ message: '更新语言文件失败: ' + error.message,
215
+ error: error.message
216
+ };
217
+ }
218
+ }
219
+
220
+ /**
221
+ * 生成语言文件内容
222
+ * @param {Object} data - 完整的语言数据对象,包含所有语言
223
+ * @returns {string} 文件内容
224
+ */
225
+ function generateLanguageFileContent(data) {
226
+ const jsonString = JSON.stringify(data, null, 2);
227
+ return `export default ${jsonString};\n`;
228
+ }
229
+
230
+ /**
231
+ * 创建调试信息接收的Express路由处理器
232
+ * @returns {Function} Express路由处理函数
233
+ */
234
+ export function createDebugRoute() {
235
+ return (req, res) => {
236
+ try {
237
+ const debugData = req.body;
238
+ const result = handleDebugInfo(debugData);
239
+ res.json(result);
240
+ } catch (error) {
241
+ console.error('处理调试信息时出错:', error);
242
+ res.status(500).json({
243
+ success: false,
244
+ message: '处理调试信息失败',
245
+ error: error.message
246
+ });
247
+ }
248
+ };
249
+ }
250
+
251
+ /**
252
+ * 创建文本编辑接收的Express路由处理器
253
+ * @returns {Function} Express路由处理函数
254
+ */
255
+ export function createDebugEditRoute() {
256
+ return async (req, res) => {
257
+ try {
258
+ const editData = req.body;
259
+ const result = await handleTextEdit(editData);
260
+ res.json(result);
261
+ } catch (error) {
262
+ console.error('处理文本编辑时出错:', error);
263
+ res.status(500).json({
264
+ success: false,
265
+ message: '处理文本编辑失败',
266
+ error: error.message
267
+ });
268
+ }
269
+ };
270
+ }
271
+
272
+ /**
273
+ * 处理来自客户端的样式修改请求
274
+ * @param {Object} styleData - 样式修改数据
275
+ * @returns {Object} 处理结果
276
+ */
277
+ export async function handleStyleEdit(styleData) {
278
+ console.log('收到样式修改请求:', {
279
+ 时间: styleData.timestamp,
280
+ 页面: styleData.url,
281
+ 文件: styleData.file,
282
+ 行号: styleData.line,
283
+ 元素标签: styleData.element?.tag,
284
+ 元素ID: styleData.element?.id,
285
+ 元素类名: styleData.element?.className,
286
+ 修改的样式: styleData.styles,
287
+ 样式数量: Object.keys(styleData.styles || {}).length
288
+ });
289
+
290
+ try {
291
+ // 如果有文件和行号信息,尝试写入行内样式
292
+ if (styleData.file && styleData.line && styleData.styles) {
293
+ return await updateTemplateWithInlineStyles(styleData);
294
+ }
295
+
296
+ // 如果没有足够信息,只记录样式修改
297
+ return {
298
+ success: true,
299
+ message: '样式修改信息已记录',
300
+ };
301
+
302
+ } catch (error) {
303
+ console.error('处理样式修改时出错:', error);
304
+ return {
305
+ success: false,
306
+ message: '样式修改处理失败: ' + error.message,
307
+ error: error.message
308
+ };
309
+ }
310
+ }
311
+
312
+ /**
313
+ * 更新模板文件的行内样式
314
+ * @param {Object} styleData - 样式修改数据
315
+ * @returns {Object} 处理结果
316
+ */
317
+ async function updateTemplateWithInlineStyles(styleData) {
318
+ try {
319
+ // 构建文件路径,基于template-debug
320
+ const filePath = path.join(paths.template.debug, styleData.file);
321
+
322
+ if (!fse.existsSync(filePath)) {
323
+ return {
324
+ success: false,
325
+ message: `文件不存在: ${filePath}`
326
+ };
327
+ }
328
+
329
+ // 读取文件内容
330
+ const fileContent = fse.readFileSync(filePath, 'utf8');
331
+ const lines = fileContent.split('\n');
332
+
333
+ // 检查行号是否有效
334
+ const lineIndex = parseInt(styleData.line) - 1; // 转换为0基索引
335
+ if (lineIndex < 0 || lineIndex >= lines.length) {
336
+ return {
337
+ success: false,
338
+ message: `行号无效: ${styleData.line},文件共有 ${lines.length} 行`
339
+ };
340
+ }
341
+
342
+ const originalLine = lines[lineIndex];
343
+
344
+ // 验证输入的样式数据
345
+ if (!styleData.styles || typeof styleData.styles !== 'object') {
346
+ return {
347
+ success: false,
348
+ message: '样式数据格式无效'
349
+ };
350
+ }
351
+
352
+ // 将样式对象转换为CSS字符串,并处理特殊字符
353
+ const newStylesCSS = Object.entries(styleData.styles)
354
+ .map(([property, value]) => {
355
+ // 将驼峰命名转换为短横线命名
356
+ const cssProperty = property.replace(/([A-Z])/g, '-$1').toLowerCase();
357
+ // 确保值是字符串并转义可能的问题字符
358
+ const cleanValue = String(value).replace(/"/g, '\\"');
359
+ return `${cssProperty}: ${cleanValue}`;
360
+ })
361
+ .join('; ');
362
+
363
+ // 解析并更新style属性的内部函数
364
+ const parseAndUpdateStyleAttribute = (line, stylesCSS, styles) => {
365
+ // 更精确的style属性匹配正则,支持各种引号格式
366
+ const styleRegex = /\bstyle\s*=\s*(['"`])([^\1]*?)\1/i;
367
+ const styleMatch = line.match(styleRegex);
368
+
369
+ if (styleMatch) {
370
+ // 已有style属性,合并新样式
371
+ const quote = styleMatch[1]; // 保持原有的引号类型
372
+ const existingStyles = styleMatch[2];
373
+
374
+ // 解析现有样式的内部函数
375
+ const parseExistingStyles = (stylesStr) => {
376
+ const stylesObj = {};
377
+
378
+ if (!stylesStr || typeof stylesStr !== 'string') {
379
+ return stylesObj;
380
+ }
381
+
382
+ // 分割样式声明,处理可能包含分号的值
383
+ const declarations = stylesStr.split(';').filter(decl => decl.trim());
384
+
385
+ declarations.forEach(declaration => {
386
+ const colonIndex = declaration.indexOf(':');
387
+ if (colonIndex > 0) {
388
+ const property = declaration.substring(0, colonIndex).trim();
389
+ const value = declaration.substring(colonIndex + 1).trim();
390
+
391
+ if (property && value) {
392
+ stylesObj[property] = value;
393
+ }
394
+ }
395
+ });
396
+
397
+ return stylesObj;
398
+ };
399
+
400
+ // 解析现有样式,更安全的处理方式
401
+ const existingStylesObj = parseExistingStyles(existingStyles);
402
+
403
+ // 合并新样式(新样式会覆盖现有的同名样式)
404
+ Object.entries(styles).forEach(([property, value]) => {
405
+ const cssProperty = property.replace(/([A-Z])/g, '-$1').toLowerCase();
406
+ existingStylesObj[cssProperty] = String(value);
407
+ });
408
+
409
+ // 重新生成样式字符串
410
+ const mergedStyles = Object.entries(existingStylesObj)
411
+ .filter(([property, value]) => property && value) // 过滤空值
412
+ .map(([property, value]) => `${property}: ${value}`)
413
+ .join('; ');
414
+
415
+ // 保持原有引号类型,但转义内容中的相同引号
416
+ const escapedStyles = mergedStyles.replace(new RegExp(quote, 'g'), `\\${quote}`);
417
+ const newLine = line.replace(styleRegex, `style=${quote}${escapedStyles}${quote}`);
418
+
419
+ return { success: true, newLine };
420
+ } else {
421
+ // 没有style属性,需要添加 - 添加style属性的内部函数
422
+ const addStyleAttributeToLine = (line, stylesCSS) => {
423
+ // 查找pug标签的属性括号,直接对原始行进行匹配以保持缩进
424
+ const attributeRegex = /^(\s*)([a-zA-Z0-9\-_.#]+)(\([^)]*\))?(.*)$/;
425
+ const match = line.match(attributeRegex);
426
+
427
+ if (!match) {
428
+ return {
429
+ success: false,
430
+ message: '无法解析标签结构,可能不是有效的pug标签行'
431
+ };
432
+ }
433
+
434
+ const [, indent, tagName, attributes, rest] = match;
435
+
436
+ if (attributes) {
437
+ // 已有属性括号,在括号内添加style属性
438
+ const attributesContent = attributes.slice(1, -1); // 去掉括号
439
+
440
+ // 检查是否需要添加分隔符
441
+ const separator = attributesContent.trim() ? ', ' : '';
442
+ const newAttributes = `(${attributesContent}${separator}style="${stylesCSS}")`;
443
+
444
+ return {
445
+ success: true,
446
+ newLine: `${indent}${tagName}${newAttributes}${rest}`
447
+ };
448
+ } else {
449
+ // 没有属性括号,添加新的括号和style属性
450
+ return {
451
+ success: true,
452
+ newLine: `${indent}${tagName}(style="${stylesCSS}")${rest}`
453
+ };
454
+ }
455
+ };
456
+
457
+ return addStyleAttributeToLine(line, stylesCSS);
458
+ }
459
+ };
460
+
461
+ // 更安全的style属性检测和解析
462
+ const result = parseAndUpdateStyleAttribute(originalLine, newStylesCSS, styleData.styles);
463
+
464
+ if (!result.success) {
465
+ return {
466
+ success: false,
467
+ message: result.message
468
+ };
469
+ }
470
+
471
+ const newLine = result.newLine;
472
+
473
+ // 如果没有变化,不需要更新
474
+ if (originalLine === newLine) {
475
+ return {
476
+ success: true,
477
+ message: '样式没有变化,无需更新文件'
478
+ };
479
+ }
480
+
481
+ lines[lineIndex] = newLine;
482
+
483
+ // 写回文件
484
+ const newContent = lines.join('\n');
485
+ fse.writeFileSync(filePath, newContent, 'utf8');
486
+
487
+ console.log(`成功更新模板文件样式: ${filePath}`);
488
+ console.log(`第 ${styleData.line} 行:`);
489
+ console.log(` 原始: ${originalLine.trim()}`);
490
+ console.log(` 更新: ${newLine.trim()}`);
491
+
492
+ return {
493
+ success: true,
494
+ message: `已将样式写入文件第 ${styleData.line} 行`,
495
+ data: {
496
+ filePath,
497
+ line: styleData.line,
498
+ oldLine: originalLine.trim(),
499
+ newLine: newLine.trim(),
500
+ addedStyles: styleData.styles
501
+ }
502
+ };
503
+
504
+ } catch (error) {
505
+ console.error('更新模板文件样式时出错:', error);
506
+ return {
507
+ success: false,
508
+ message: '更新模板文件样式失败: ' + error.message,
509
+ error: error.message
510
+ };
511
+ }
512
+ }
513
+
514
+ /**
515
+ * 创建样式编辑接收的Express路由处理器
516
+ * @returns {Function} Express路由处理函数
517
+ */
518
+ export function createDebugStyleRoute() {
519
+ return async (req, res) => {
520
+ try {
521
+ const styleData = req.body;
522
+ const result = await handleStyleEdit(styleData);
523
+ res.json(result);
524
+ } catch (error) {
525
+ console.error('处理样式修改时出错:', error);
526
+ res.status(500).json({
527
+ success: false,
528
+ message: '处理样式修改失败',
529
+ error: error.message
530
+ });
531
+ }
532
+ };
533
+ }
534
+
535
+ /**
536
+ * 设置调试相关的路由
537
+ * @param {express.Application} app - Express应用实例
538
+ */
539
+ export function setupDebugRoutes(app) {
540
+ // 添加调试信息接收API路由
541
+ app.post('/api/pug-debug', express.json(), createDebugRoute());
542
+
543
+ // 添加文本编辑接收API路由
544
+ app.post('/api/pug-debug/edit', express.json(), createDebugEditRoute());
545
+
546
+ // 添加样式修改接收API路由
547
+ app.post('/api/pug-debug/style', express.json(), createDebugStyleRoute());
548
+ }