pug-site-core 3.0.17 → 3.0.19

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 CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  fetchDataToJsonFile,
8
8
  buildFn,
9
9
  buildStatic,
10
- createDebugTemplate,
10
+ createDebugTemplate
11
11
  } from "./lib/generate.js";
12
12
 
13
13
  export const pugSiteCore = {
@@ -19,7 +19,7 @@ export const pugSiteCore = {
19
19
  buildStatic,
20
20
  translateLanguageData,
21
21
  processImagemin,
22
- createDebugTemplate,
22
+ createDebugTemplate
23
23
  };
24
24
 
25
25
  let curCmd = process.env.npm_lifecycle_event;
package/lib/generate.js CHANGED
@@ -12,6 +12,8 @@ import {
12
12
  obfuscateJavaScript,
13
13
  addTemplateScopeIsolation,
14
14
  getJsonData,
15
+ isLinux,
16
+ getLanguageListFromApi
15
17
  } from "./utils.js";
16
18
  import _ from "lodash";
17
19
  import async from "async";
@@ -73,7 +75,7 @@ export async function compilePagesPugToFn(pugPath) {
73
75
  basedir: paths.template.root,
74
76
  compileDebug: true,
75
77
  name: funName,
76
- filters: getCompilePugFilter(),
78
+ filters: getCompilePugFilter()
77
79
  });
78
80
 
79
81
  // 提取函数定义部分
@@ -154,7 +156,11 @@ export async function generateGetDataFn() {
154
156
  await async.each(pagesPugFilePathArr, async (fileName) => {
155
157
  const funName =
156
158
  "get_" +
157
- fileName.split(pathSymbol).join("_").slice(0, -4).replaceAll(/[-]/g, "_") +
159
+ fileName
160
+ .split(pathSymbol)
161
+ .join("_")
162
+ .slice(0, -4)
163
+ .replaceAll(/[-]/g, "_") +
158
164
  "_data";
159
165
 
160
166
  // 使用正则表达式检查特定的数据获取函数是否存在
@@ -163,7 +169,10 @@ export async function generateGetDataFn() {
163
169
  );
164
170
  if (!dataFnRegex.test(getDataFile)) {
165
171
  const templateFn = config.getDataFnTemplate.toString();
166
- const dataFn = `export async ${templateFn.replace("template", funName)}`;
172
+ const dataFn = `export async ${templateFn.replace(
173
+ "template",
174
+ funName
175
+ )}`;
167
176
  await fse.appendFile(getDataPath, dataFn + "\n");
168
177
  }
169
178
  });
@@ -212,15 +221,23 @@ export async function fetchDataToJsonFile(args) {
212
221
  let checkData = (data, language, funName) => {
213
222
  if (data === null || typeof data !== "object") {
214
223
  return Promise.reject(
215
- new Error(`${language} ${funName} 期望返回数组、对象类型返回: ${data}`)
224
+ new Error(
225
+ `${language} ${funName} 期望返回数组、对象类型返回: ${data}`
226
+ )
216
227
  );
217
228
  }
218
229
  if (Array.isArray(data)) {
219
230
  if (data.length === 0) {
220
- return Promise.reject(new Error(`${language} ${funName} 数据为空数组`));
231
+ return Promise.reject(
232
+ new Error(`${language} ${funName} 数据为空数组`)
233
+ );
221
234
  }
222
235
  data.forEach((item, index) => {
223
- if (item === null || typeof item !== "object" || Array.isArray(item)) {
236
+ if (
237
+ item === null ||
238
+ typeof item !== "object" ||
239
+ Array.isArray(item)
240
+ ) {
224
241
  return Promise.reject(
225
242
  new Error(
226
243
  `${language} ${funName} 返回的数据不为对象数组类型返回: ${item} 下标为${index}`
@@ -250,7 +267,14 @@ export async function fetchDataToJsonFile(args) {
250
267
 
251
268
  const pugFilePathList = await getPagesPugFilePathArr();
252
269
 
253
- const { languageList, customBuildData, fetchDataConcurrencyLimit } = config;
270
+ let languageList;
271
+ if (isLinux()) {
272
+ languageList = await getLanguageListFromApi();
273
+ } else {
274
+ languageList = config.languageList;
275
+ }
276
+
277
+ const { customBuildData, fetchDataConcurrencyLimit } = config;
254
278
 
255
279
  // 创建一个全局任务队列控制整体并发数
256
280
  const queue = async.queue(async (task) => {
@@ -277,7 +301,9 @@ export async function fetchDataToJsonFile(args) {
277
301
  allTasks.push(async () => {
278
302
  console.log(language, commonFuncName, "开始写入json文件");
279
303
  const commonData = await getData[commonFuncName](language);
280
- let languageData = (await import(paths.languageData)).default[language];
304
+ let languageData = (await import(paths.languageData)).default[
305
+ language
306
+ ];
281
307
  commonData.lang = _.merge(commonData.lang, languageData);
282
308
  await fse.outputJSON(
283
309
  paths.resolveRoot("jsonData", language, "_common.json"),
@@ -299,7 +325,9 @@ export async function fetchDataToJsonFile(args) {
299
325
 
300
326
  let dataFn = getData[obj.getDataFn];
301
327
  if (!dataFn || typeof dataFn !== "function") {
302
- return Promise.reject(new Error(obj.getDataFn + "获取数据函数不存在"));
328
+ return Promise.reject(
329
+ new Error(obj.getDataFn + "获取数据函数不存在")
330
+ );
303
331
  }
304
332
 
305
333
  if (!isFillFun(obj.getDataFn)) {
@@ -347,7 +375,11 @@ export async function fetchDataToJsonFile(args) {
347
375
  for (const fileName of pugFilePathList) {
348
376
  let funName =
349
377
  "get_" +
350
- fileName.split(pathSymbol).join("_").slice(0, -4).replaceAll(/[-]/g, "_") +
378
+ fileName
379
+ .split(pathSymbol)
380
+ .join("_")
381
+ .slice(0, -4)
382
+ .replaceAll(/[-]/g, "_") +
351
383
  "_data";
352
384
 
353
385
  let jsonFilePath = fileName.slice(0, -4).split(pathSymbol);
@@ -373,7 +405,11 @@ export async function fetchDataToJsonFile(args) {
373
405
  item.page_name
374
406
  );
375
407
  } else {
376
- console.warn("下标:", index, "无page_name属性,使用index作为文件名");
408
+ console.warn(
409
+ "下标:",
410
+ index,
411
+ "无page_name属性,使用index作为文件名"
412
+ );
377
413
  lastJsonFilePath =
378
414
  paths.join(language, ...jsonFilePath) +
379
415
  "_" +
@@ -406,7 +442,11 @@ export async function fetchDataToJsonFile(args) {
406
442
  // 等待所有任务完成
407
443
  return new Promise((resolve, reject) => {
408
444
  queue.drain(() => {
409
- console.log("获取数据并写入完成花费:", (Date.now() - starTime) / 1000, "s");
445
+ console.log(
446
+ "获取数据并写入完成花费:",
447
+ (Date.now() - starTime) / 1000,
448
+ "s"
449
+ );
410
450
  resolve();
411
451
  });
412
452
 
@@ -450,14 +490,22 @@ export async function buildFn() {
450
490
  totalCommonData.langCommon = config.commonData;
451
491
  let languageData = (await import(paths.languageData)).default;
452
492
  await async.each(config.languageList, async (lang) => {
453
- let commonData = await (await import(paths.getData))["get_common_data"](lang);
493
+ let commonData = await (
494
+ await import(paths.getData)
495
+ )["get_common_data"](lang);
454
496
  commonData.lang = _.merge(commonData.lang, languageData[lang]);
455
497
  totalCommonData[lang] = commonData;
456
498
  });
457
499
 
458
- const ABTestInfo = await fetchABTestInfo(config.siteConfig.siteAbbr);
459
- if (ABTestInfo) {
460
- totalCommonData.langCommon.ABTestInfo = ABTestInfo;
500
+ if (config.siteConfig?.siteAbbr?.length > 0) {
501
+ const ABTestInfo = await fetchABTestInfo(config.siteConfig.siteAbbr);
502
+ if (ABTestInfo) {
503
+ totalCommonData.langCommon.ABTestInfo = ABTestInfo;
504
+ }
505
+ } else {
506
+ console.warn(
507
+ "config.siteConfig.siteAbbr 配置不存在,无法获取ABTestInfo 将不会开启abtest"
508
+ );
461
509
  }
462
510
 
463
511
  // await fse.copy(jsonDataPath, paths.resolveRoot(outputPath, "data"), {
@@ -478,11 +526,13 @@ export async function buildFn() {
478
526
  }
479
527
  if (config.buildStaticDirArr && config.buildStaticDirArr.length > 0) {
480
528
  return !!config.buildStaticDirArr.find((item) => {
481
- return src.startsWith(paths.resolveRoot(paths.template.static, item));
529
+ return src.startsWith(
530
+ paths.resolveRoot(paths.template.static, item)
531
+ );
482
532
  });
483
533
  }
484
534
  return true;
485
- },
535
+ }
486
536
  }
487
537
  );
488
538
 
@@ -522,7 +572,7 @@ export async function buildStatic() {
522
572
  useShortDoctype: true,
523
573
  removeEmptyAttributes: true,
524
574
  removeOptionalTags: true,
525
- caseSensitive: false,
575
+ caseSensitive: false
526
576
  };
527
577
 
528
578
  /**
@@ -585,7 +635,7 @@ export async function buildStatic() {
585
635
  return pagesPugToFn[funName]({
586
636
  data,
587
637
  _pagePath: pagePath,
588
- common: commonData,
638
+ common: commonData
589
639
  });
590
640
  }
591
641
 
@@ -614,7 +664,9 @@ export async function buildStatic() {
614
664
  await async.eachLimit(data, 128, async (dataItem, index) => {
615
665
  const fileName = dataItem[property];
616
666
  if (!fileName) {
617
- throw new Error(`数据项索引 ${index} 中缺少属性 ${property} 或值为空`);
667
+ throw new Error(
668
+ `数据项索引 ${index} 中缺少属性 ${property} 或值为空`
669
+ );
618
670
  }
619
671
 
620
672
  const htmlPath = buildHtmlPath(lang, obj.outPutHtmlPath, fileName);
@@ -630,7 +682,13 @@ export async function buildStatic() {
630
682
  });
631
683
  } else {
632
684
  const htmlPath = buildHtmlPath(lang, obj.outPutHtmlPath);
633
- const html = generateHtml(pagesPugToFn, funName, data, pagePath, commonData);
685
+ const html = generateHtml(
686
+ pagesPugToFn,
687
+ funName,
688
+ data,
689
+ pagePath,
690
+ commonData
691
+ );
634
692
  const compressedHtml = await compressHtml(html, htmlPath);
635
693
  await fse.outputFile(htmlPath, compressedHtml);
636
694
  }
@@ -657,7 +715,13 @@ export async function buildStatic() {
657
715
  ) {
658
716
  try {
659
717
  const htmlPath = buildHtmlPath(lang, obj.outPutHtmlPath);
660
- const html = generateHtml(pagesPugToFn, funName, data, pagePath, commonData);
718
+ const html = generateHtml(
719
+ pagesPugToFn,
720
+ funName,
721
+ data,
722
+ pagePath,
723
+ commonData
724
+ );
661
725
  const compressedHtml = await compressHtml(html, htmlPath);
662
726
  await fse.outputFile(htmlPath, compressedHtml);
663
727
  } catch (error) {
@@ -698,7 +762,7 @@ export async function buildStatic() {
698
762
  jsonPath = paths.join(lang, jsonPath);
699
763
  let data = await getJsonData(jsonPath);
700
764
  return data;
701
- },
765
+ }
702
766
  });
703
767
  const data = await getDataFn(lang);
704
768
 
@@ -734,7 +798,9 @@ export async function buildStatic() {
734
798
  }
735
799
  } catch (error) {
736
800
  console.error(
737
- `处理自定义HTML构建失败 [lang: ${lang}, config: ${JSON.stringify(obj)}]:`,
801
+ `处理自定义HTML构建失败 [lang: ${lang}, config: ${JSON.stringify(
802
+ obj
803
+ )}]:`,
738
804
  error
739
805
  );
740
806
  throw error;
@@ -826,11 +892,13 @@ export async function buildStatic() {
826
892
  }
827
893
  if (config.buildStaticDirArr && config.buildStaticDirArr.length > 0) {
828
894
  return !!config.buildStaticDirArr.find((item) => {
829
- return src.startsWith(paths.resolveRoot(paths.template.static, item));
895
+ return src.startsWith(
896
+ paths.resolveRoot(paths.template.static, item)
897
+ );
830
898
  });
831
899
  }
832
900
  return true;
833
- },
901
+ }
834
902
  }
835
903
  );
836
904
 
@@ -992,7 +1060,7 @@ export async function createDebugTemplate() {
992
1060
  /[+\-*/%]/,
993
1061
  /\brandom\b|\bfloor\b|\bceil\b|\bround\b/,
994
1062
  /\.\w+\(/, // 方法调用
995
- /\$\{[^}]*[+\-*/%.][^}]*\}/, // 模板字符串中的计算
1063
+ /\$\{[^}]*[+\-*/%.][^}]*\}/ // 模板字符串中的计算
996
1064
  ];
997
1065
 
998
1066
  return dynamicPatterns.some((pattern) => pattern.test(code));
@@ -1017,7 +1085,9 @@ export async function createDebugTemplate() {
1017
1085
  }
1018
1086
 
1019
1087
  // 匹配带反引号的模板字符串 `${common.lang.xxx}`
1020
- const backquoteLangMatch = code.match(/`.*\$\{common\.lang\.([^}]+)\}.*`/);
1088
+ const backquoteLangMatch = code.match(
1089
+ /`.*\$\{common\.lang\.([^}]+)\}.*`/
1090
+ );
1021
1091
  if (backquoteLangMatch) {
1022
1092
  return backquoteLangMatch[1];
1023
1093
  }
@@ -1048,7 +1118,7 @@ export async function createDebugTemplate() {
1048
1118
  textContent: "",
1049
1119
  langPath: "",
1050
1120
  hasStaticText: false,
1051
- hasLangText: false,
1121
+ hasLangText: false
1052
1122
  };
1053
1123
 
1054
1124
  // 分析标签的子节点来确定文本内容
@@ -1144,7 +1214,7 @@ export async function createDebugTemplate() {
1144
1214
  "script",
1145
1215
  "base",
1146
1216
  "noscript",
1147
- "template",
1217
+ "template"
1148
1218
  ]);
1149
1219
 
1150
1220
  // 处理每一行
@@ -1175,7 +1245,7 @@ export async function createDebugTemplate() {
1175
1245
  // 构建调试属性
1176
1246
  const debugAttrs = [
1177
1247
  `data-debug-file="${relativePath}"`,
1178
- `data-debug-line="${lineNumber}"`,
1248
+ `data-debug-line="${lineNumber}"`
1179
1249
  ];
1180
1250
 
1181
1251
  // 只有可编辑的元素才添加 data-debug-editable 属性
@@ -1217,8 +1287,12 @@ export async function createDebugTemplate() {
1217
1287
  const lastParenIndex = afterFirstParen.lastIndexOf(")");
1218
1288
 
1219
1289
  if (lastParenIndex !== -1) {
1220
- const existingAttrs = afterFirstParen.substring(0, lastParenIndex);
1221
- const afterLastParen = afterFirstParen.substring(lastParenIndex);
1290
+ const existingAttrs = afterFirstParen.substring(
1291
+ 0,
1292
+ lastParenIndex
1293
+ );
1294
+ const afterLastParen =
1295
+ afterFirstParen.substring(lastParenIndex);
1222
1296
 
1223
1297
  const separator = existingAttrs.trim() ? ", " : "";
1224
1298
  processedLine = `${indent}${beforeParen}${existingAttrs}${separator}${debugAttrsString}${afterLastParen}`;
@@ -1244,7 +1318,10 @@ export async function createDebugTemplate() {
1244
1318
  const parenEnd = findMatchingParen(trimmedLine, tagEndIndex);
1245
1319
 
1246
1320
  if (parenEnd !== -1) {
1247
- const tagWithModifiers = trimmedLine.substring(0, tagEndIndex);
1321
+ const tagWithModifiers = trimmedLine.substring(
1322
+ 0,
1323
+ tagEndIndex
1324
+ );
1248
1325
  const existingAttrs = trimmedLine.substring(
1249
1326
  tagEndIndex + 1,
1250
1327
  parenEnd
package/lib/translate.js CHANGED
@@ -19,9 +19,15 @@ let translate = new v2.Translate({ projectId, key });
19
19
 
20
20
  let orginLang = "us";
21
21
 
22
+ // 命令行参数说明:
23
+ // k=key1,key2 - 指定要翻译的键
24
+ // c=us,kr,jp - 指定目标国家/语言
25
+ // all=true - 使用 allLang 中的所有语言进行翻译
22
26
  let args = process.argv.slice(2);
23
27
  let filParms = [];
24
28
  let filCountry = [];
29
+ let useAllLang = false;
30
+
25
31
  args.forEach((item) => {
26
32
  const [key, value] = item.split("=");
27
33
  if (value) {
@@ -32,9 +38,42 @@ args.forEach((item) => {
32
38
  filCountry = value.split(",");
33
39
  }
34
40
  }
41
+ if (key === "all" && value === "true") {
42
+ useAllLang = true;
43
+ }
35
44
  });
36
45
 
37
- let targetLangList = filCountry.length > 0 ? filCountry : config.languageList;
46
+ const allLang = [
47
+ "us",
48
+ "us2",
49
+ "ar",
50
+ "br",
51
+ "de",
52
+ "es",
53
+ "fr",
54
+ "id",
55
+ "it",
56
+ "jp",
57
+ "kr",
58
+ "nl",
59
+ "pl",
60
+ "th",
61
+ "tr",
62
+ "tw",
63
+ "vn",
64
+ "ru",
65
+ "pt",
66
+ "sv",
67
+ "fi",
68
+ "ms",
69
+ "in"
70
+ ];
71
+
72
+ let targetLangList = useAllLang
73
+ ? allLang
74
+ : filCountry.length > 0
75
+ ? filCountry
76
+ : config.languageList;
38
77
 
39
78
  //国家映射到语言
40
79
  const countryLanguageMap = {
@@ -48,7 +87,7 @@ const countryLanguageMap = {
48
87
  tw: "zh-TW",
49
88
  gb: "en",
50
89
  br: "pt",
51
- in: "hi",
90
+ in: "hi"
52
91
  };
53
92
 
54
93
  async function translateStr(str, targetLanguage) {
package/lib/utils.js CHANGED
@@ -5,12 +5,19 @@ import { paths } from "./paths.js";
5
5
  import detectPort from "detect-port";
6
6
  import axios from "axios";
7
7
 
8
-
9
8
  const { config } = await import(paths.config);
10
9
 
11
10
  // 根据操作系统设置路径分隔符
12
11
  export const pathSymbol = process.platform.startsWith("win") ? "\\" : "/";
13
12
 
13
+ /**
14
+ * 判断当前环境是否为Linux系统
15
+ * @returns {boolean} 如果是Linux系统返回true,否则返回false
16
+ */
17
+ export function isLinux() {
18
+ return process.platform === "linux";
19
+ }
20
+
14
21
  /**
15
22
  * 获取pages目录下所有pug文件的路径数组
16
23
  * @returns {Promise<string[]>} 返回pug文件路径数组
@@ -18,7 +25,7 @@ export const pathSymbol = process.platform.startsWith("win") ? "\\" : "/";
18
25
  export async function getPagesPugFilePathArr() {
19
26
  let pagesPugFilePathArr = (
20
27
  await fse.readdir(paths.template.pages, {
21
- recursive: true,
28
+ recursive: true
22
29
  })
23
30
  ).filter((fileName) => fileName.endsWith(".pug"));
24
31
 
@@ -262,8 +269,8 @@ export async function obfuscateJavaScript(
262
269
  unused: true,
263
270
  if_return: true,
264
271
  join_vars: true,
265
- drop_console: true,
266
- },
272
+ drop_console: true
273
+ }
267
274
  };
268
275
 
269
276
  // 合并配置选项
@@ -393,14 +400,21 @@ export async function getJsonData(jsonDataPath) {
393
400
  * @param {boolean} options.preserveComments - 是否保留注释,默认为true
394
401
  * @returns {string} 处理后的HTML字符串
395
402
  */
396
- export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", options = {}) {
403
+ export function addTemplateScopeIsolation(
404
+ htmlString,
405
+ scopePrefix = "xy",
406
+ options = {}
407
+ ) {
397
408
  // 参数验证
398
409
  if (!htmlString || typeof htmlString !== "string") {
399
410
  return htmlString;
400
411
  }
401
412
 
402
413
  // 快速检查,避免不必要的处理
403
- if (!htmlString.includes("<template>") || !htmlString.includes("</template>")) {
414
+ if (
415
+ !htmlString.includes("<template>") ||
416
+ !htmlString.includes("</template>")
417
+ ) {
404
418
  return htmlString;
405
419
  }
406
420
 
@@ -424,15 +438,15 @@ export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", option
424
438
  if (!selector || !selector.trim()) {
425
439
  return selector;
426
440
  }
427
-
441
+
428
442
  selector = selector.trim();
429
-
443
+
430
444
  // 处理伪类和伪元素(更完善的正则)
431
445
  const pseudoMatch = selector.match(/^([^:]+?)(::?[^[]+)?$/);
432
446
  if (pseudoMatch) {
433
- const [, base, pseudo = ''] = pseudoMatch;
447
+ const [, base, pseudo = ""] = pseudoMatch;
434
448
  // 处理属性选择器
435
- if (base.includes('[')) {
449
+ if (base.includes("[")) {
436
450
  const attrMatch = base.match(/^([^[]+)(\[.+\])$/);
437
451
  if (attrMatch) {
438
452
  return `${attrMatch[1]}[data-${scopeId}]${attrMatch[2]}${pseudo}`;
@@ -440,7 +454,7 @@ export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", option
440
454
  }
441
455
  return `${base}[data-${scopeId}]${pseudo}`;
442
456
  }
443
-
457
+
444
458
  return `${selector}[data-${scopeId}]`;
445
459
  }
446
460
 
@@ -448,52 +462,57 @@ export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", option
448
462
  function parseHTML(html, depth = 0) {
449
463
  // 检查递归深度
450
464
  if (depth >= config.maxDepth) {
451
- console.warn(`Maximum recursion depth (${config.maxDepth}) reached in template processing`);
465
+ console.warn(
466
+ `Maximum recursion depth (${config.maxDepth}) reached in template processing`
467
+ );
452
468
  return html;
453
469
  }
454
-
470
+
455
471
  // 使用更精确的模板匹配(避免贪婪匹配问题)
456
472
  const templates = [];
457
473
  let tempHtml = html;
458
474
  let placeholder = 0;
459
-
475
+
460
476
  // 先收集所有template标签的位置和内容
461
477
  const templateRegex = /<template([^>]*)>([\s\S]*?)<\/template>/gi;
462
478
  let match;
463
-
479
+
464
480
  while ((match = templateRegex.exec(html)) !== null) {
465
481
  templates.push({
466
482
  full: match[0],
467
- attributes: match[1] || '',
468
- content: match[2] || '',
483
+ attributes: match[1] || "",
484
+ content: match[2] || "",
469
485
  start: match.index,
470
486
  placeholder: `__TEMPLATE_PLACEHOLDER_${placeholder++}__`
471
487
  });
472
488
  }
473
-
489
+
474
490
  // 从后往前替换,避免索引偏移问题
475
491
  for (let i = templates.length - 1; i >= 0; i--) {
476
492
  const template = templates[i];
477
493
  const scopeId = generateScopeId(scopePrefix);
478
-
494
+
479
495
  // 处理内容
480
496
  const processedContent = addScopeToHTML(template.content, scopeId);
481
-
497
+
482
498
  // 创建新的div标签,保留原有属性
483
- const attributesStr = template.attributes ? ` ${template.attributes.trim()}` : '';
499
+ const attributesStr = template.attributes
500
+ ? ` ${template.attributes.trim()}`
501
+ : "";
484
502
  const newDiv = `<div data-${scopeId}=""${attributesStr}>${processedContent}</div>`;
485
-
503
+
486
504
  // 替换原始template
487
- tempHtml = tempHtml.substring(0, template.start) +
488
- newDiv +
489
- tempHtml.substring(template.start + template.full.length);
505
+ tempHtml =
506
+ tempHtml.substring(0, template.start) +
507
+ newDiv +
508
+ tempHtml.substring(template.start + template.full.length);
490
509
  }
491
-
510
+
492
511
  // 检查是否还有嵌套的template需要处理
493
- if (tempHtml.includes('<template>') && depth < config.maxDepth - 1) {
512
+ if (tempHtml.includes("<template>") && depth < config.maxDepth - 1) {
494
513
  return parseHTML(tempHtml, depth + 1);
495
514
  }
496
-
515
+
497
516
  return tempHtml;
498
517
  }
499
518
 
@@ -501,41 +520,45 @@ export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", option
501
520
  function addScopeToHTML(html, scopeId) {
502
521
  // 处理HTML标签(改进的正则,支持自闭合标签)
503
522
  let processedHtml = html.replace(
504
- /<(\w+)([^>]*?)(\/?)>/g,
523
+ /<(\w+)([^>]*?)(\/?)>/g,
505
524
  (match, tagName, attributes, selfClosing) => {
506
525
  // 跳过已经有作用域属性的标签
507
- if (attributes.includes('data-v-') ||
508
- attributes.includes(`data-${scopeId}`) ||
509
- attributes.includes(`data-${scopePrefix}-`)) {
526
+ if (
527
+ attributes.includes("data-v-") ||
528
+ attributes.includes(`data-${scopeId}`) ||
529
+ attributes.includes(`data-${scopePrefix}-`)
530
+ ) {
510
531
  return match;
511
532
  }
512
-
533
+
513
534
  // 处理特殊标签(不需要作用域的)
514
- const skipTags = ['script', 'style', 'template'];
535
+ const skipTags = ["script", "style", "template"];
515
536
  if (skipTags.includes(tagName.toLowerCase())) {
516
537
  return match;
517
538
  }
518
-
539
+
519
540
  // 为标签添加作用域属性
520
541
  return `<${tagName} data-${scopeId}=""${attributes}${selfClosing}>`;
521
542
  }
522
543
  );
523
-
544
+
524
545
  // 处理style标签中的CSS
525
546
  processedHtml = processedHtml.replace(
526
- /<style([^>]*?)>([\s\S]*?)<\/style>/gi,
547
+ /<style([^>]*?)>([\s\S]*?)<\/style>/gi,
527
548
  (match, styleAttrs, cssContent) => {
528
549
  // 跳过已处理的CSS
529
- if (cssContent.includes(`[data-${scopeId}]`) ||
530
- cssContent.includes(`[data-${scopePrefix}-`)) {
550
+ if (
551
+ cssContent.includes(`[data-${scopeId}]`) ||
552
+ cssContent.includes(`[data-${scopePrefix}-`)
553
+ ) {
531
554
  return match;
532
555
  }
533
-
556
+
534
557
  const scopedCSS = addScopeToCSS(cssContent, scopeId);
535
558
  return `<style${styleAttrs}>${scopedCSS}</style>`;
536
559
  }
537
560
  );
538
-
561
+
539
562
  return processedHtml;
540
563
  }
541
564
 
@@ -544,7 +567,7 @@ export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", option
544
567
  // 保存注释
545
568
  const comments = [];
546
569
  let commentIndex = 0;
547
-
570
+
548
571
  if (config.preserveComments) {
549
572
  cssContent = cssContent.replace(/\/\*[\s\S]*?\*\//g, (match) => {
550
573
  const placeholder = `__CSS_COMMENT_${commentIndex++}__`;
@@ -552,86 +575,91 @@ export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", option
552
575
  return placeholder;
553
576
  });
554
577
  }
555
-
578
+
556
579
  // 改进的CSS规则处理
557
580
  function processCSS(css, level = 0) {
558
581
  if (level > 5) return css; // 防止过深的嵌套
559
-
582
+
560
583
  // 匹配CSS规则块
561
584
  const ruleRegex = /([^{}]+)\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/g;
562
-
585
+
563
586
  return css.replace(ruleRegex, (match, selectors, content) => {
564
587
  const trimmedSelectors = selectors.trim();
565
-
588
+
566
589
  // 处理@规则
567
- if (trimmedSelectors.startsWith('@')) {
590
+ if (trimmedSelectors.startsWith("@")) {
568
591
  // @keyframes不需要作用域
569
- if (trimmedSelectors.startsWith('@keyframes')) {
592
+ if (trimmedSelectors.startsWith("@keyframes")) {
570
593
  return match;
571
594
  }
572
-
595
+
573
596
  // @media, @supports等需要递归处理内部规则
574
- if (content.includes('{')) {
597
+ if (content.includes("{")) {
575
598
  const processedContent = processCSS(content, level + 1);
576
599
  return `${selectors} {${processedContent}}`;
577
600
  }
578
-
601
+
579
602
  return match;
580
603
  }
581
-
604
+
582
605
  // 处理普通选择器
583
606
  const processedSelectors = trimmedSelectors
584
- .split(',')
585
- .map(selector => {
607
+ .split(",")
608
+ .map((selector) => {
586
609
  selector = selector.trim();
587
-
610
+
588
611
  // 跳过特殊情况
589
- if (!selector ||
590
- selector.startsWith('@') ||
591
- /^(from|to|\d+%)$/.test(selector) ||
592
- selector.includes(`[data-${scopeId}]`)) {
612
+ if (
613
+ !selector ||
614
+ selector.startsWith("@") ||
615
+ /^(from|to|\d+%)$/.test(selector) ||
616
+ selector.includes(`[data-${scopeId}]`)
617
+ ) {
593
618
  return selector;
594
619
  }
595
-
620
+
596
621
  // 清理现有的作用域属性
597
- selector = selector.replace(/\[data-[a-z0-9-]+\]/gi, '').trim();
598
-
622
+ selector = selector.replace(/\[data-[a-z0-9-]+\]/gi, "").trim();
623
+
599
624
  // 处理复杂选择器
600
625
  const selectorParts = selector.split(/\s+/);
601
-
626
+
602
627
  // 处理第一个选择器部分
603
628
  if (selectorParts.length > 0) {
604
629
  // 处理组合选择器(如 .class1.class2)
605
630
  const firstPart = selectorParts[0];
606
631
  const combinedSelectors = firstPart.split(/(?=[.#[])/);
607
-
632
+
608
633
  if (combinedSelectors.length > 1) {
609
634
  // 在第一个实际选择器后添加作用域
610
- combinedSelectors[0] = addScopeToSimpleSelector(combinedSelectors[0], scopeId);
611
- selectorParts[0] = combinedSelectors.join('');
635
+ combinedSelectors[0] = addScopeToSimpleSelector(
636
+ combinedSelectors[0],
637
+ scopeId
638
+ );
639
+ selectorParts[0] = combinedSelectors.join("");
612
640
  } else {
613
641
  selectorParts[0] = addScopeToSimpleSelector(firstPart, scopeId);
614
642
  }
615
643
  }
616
-
617
- return selectorParts.join(' ');
644
+
645
+ return selectorParts.join(" ");
618
646
  })
619
- .join(', ');
620
-
647
+ .join(", ");
648
+
621
649
  return `${processedSelectors} {${content}}`;
622
650
  });
623
651
  }
624
-
652
+
625
653
  // 处理CSS
626
654
  let processedCSS = processCSS(cssContent);
627
-
655
+
628
656
  // 恢复注释
629
657
  if (config.preserveComments) {
630
658
  comments.forEach(({ placeholder, content }) => {
631
659
  processedCSS = processedCSS.replace(placeholder, content);
632
660
  });
633
661
  }
634
-
662
+
635
663
  return processedCSS;
636
664
  }
637
665
 
@@ -639,7 +667,7 @@ export function addTemplateScopeIsolation(htmlString, scopePrefix = "xy", option
639
667
  try {
640
668
  return parseHTML(htmlString);
641
669
  } catch (error) {
642
- console.error('Error in addTemplateScopeIsolation:', error);
670
+ console.error("Error in addTemplateScopeIsolation:", error);
643
671
  return htmlString; // 出错时返回原始内容
644
672
  }
645
673
  }
@@ -653,14 +681,14 @@ export async function getFilePathByFunctionName(functionName) {
653
681
  try {
654
682
  // 获取所有pug文件路径
655
683
  const pagesPugFilePathArr = await getPagesPugFilePathArr();
656
-
684
+
657
685
  // 清理函数名,移除get_前缀和_data后缀(如果存在)
658
686
  let cleanFunctionName = functionName;
659
687
  if (functionName.startsWith("get_") && functionName.endsWith("_data")) {
660
688
  // 数据获取函数名格式:get_xxx_data
661
689
  cleanFunctionName = functionName.slice(4, -5); // 移除 "get_" 和 "_data"
662
690
  }
663
-
691
+
664
692
  // 遍历所有pug文件,找到匹配的文件
665
693
  for (const fileName of pagesPugFilePathArr) {
666
694
  // 根据fileName生成函数名(与generate.js中的逻辑保持一致)
@@ -669,13 +697,16 @@ export async function getFilePathByFunctionName(functionName) {
669
697
  .join("_")
670
698
  .slice(0, -4) // 移除.pug扩展名
671
699
  .replaceAll(/[-]/g, "_");
672
-
700
+
673
701
  // 检查是否匹配
674
- if (generatedFunctionName === cleanFunctionName || generatedFunctionName === functionName) {
702
+ if (
703
+ generatedFunctionName === cleanFunctionName ||
704
+ generatedFunctionName === functionName
705
+ ) {
675
706
  return fileName;
676
707
  }
677
708
  }
678
-
709
+
679
710
  return null;
680
711
  } catch (error) {
681
712
  console.error("根据函数名查找文件路径失败:", error);
@@ -691,11 +722,43 @@ export async function getFilePathByFunctionName(functionName) {
691
722
  export async function fetchABTestInfo(siteAbbr) {
692
723
  try {
693
724
  const res = await axios.get("http://new.sp.com/open-api/abtest-info", {
694
- params: { site: siteAbbr },
725
+ params: { site: siteAbbr }
695
726
  });
696
- return (res && res.data && res.data.result) ? res.data.result : null;
727
+ return res && res.data && res.data.result ? res.data.result : null;
697
728
  } catch (error) {
698
729
  console.error("获取ABTestInfo失败:", error);
699
730
  return null;
700
731
  }
701
- }
732
+ }
733
+
734
+ /**
735
+ * 从接口获取语言列表
736
+ * @returns {Promise<string[]>} 返回语言列表
737
+ */
738
+ export async function getLanguageListFromApi() {
739
+ try {
740
+ console.log("正在从接口获取语言列表...");
741
+ const response = await axios.get(
742
+ `http://new.sp.com/open-api/get_site_online_languages?site=${config.siteConfig.siteName}`
743
+ );
744
+
745
+ if (
746
+ response.data &&
747
+ response.data.success &&
748
+ response.data.result?.length
749
+ ) {
750
+ const languageList = response.data.result;
751
+ console.log("成功获取语言列表:", languageList);
752
+ return languageList;
753
+ } else {
754
+ console.warn(
755
+ "接口返回数据格式不正确:",
756
+ response.data,
757
+ "将使用config中的语言列表"
758
+ );
759
+ return config.languageList;
760
+ }
761
+ } catch (error) {
762
+ throw new Error("从接口获取语言列表失败:" + error);
763
+ }
764
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pug-site-core",
3
- "version": "3.0.17",
3
+ "version": "3.0.19",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -52,7 +52,7 @@
52
52
  "ws": "^8.18.0"
53
53
  },
54
54
  "license": "ISC",
55
- "description": "修改ABTest数据获取的接口",
55
+ "description": "增加从接口获取语言列表、增加翻译所有语言指令、增加linux系统判断",
56
56
  "files": [
57
57
  "lib/",
58
58
  "index.js"
@@ -60,4 +60,4 @@
60
60
  "exports": {
61
61
  ".": "./index.js"
62
62
  }
63
- }
63
+ }