pug-site-core 3.0.18 → 3.0.20

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
@@ -11,7 +11,9 @@ import {
11
11
  pathSymbol,
12
12
  obfuscateJavaScript,
13
13
  addTemplateScopeIsolation,
14
- getJsonData
14
+ getJsonData,
15
+ isLinux,
16
+ getLanguageListFromApi
15
17
  } from "./utils.js";
16
18
  import _ from "lodash";
17
19
  import async from "async";
@@ -265,7 +267,14 @@ export async function fetchDataToJsonFile(args) {
265
267
 
266
268
  const pugFilePathList = await getPagesPugFilePathArr();
267
269
 
268
- 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;
269
278
 
270
279
  // 创建一个全局任务队列控制整体并发数
271
280
  const queue = async.queue(async (task) => {
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 - 使用 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") {
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.18",
3
+ "version": "3.0.20",
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": "siteAbbr可选配置,添加会在common.langCommon.ABTestInfo中添加ABTestInfo数据开启abtest,否则不会添加",
55
+ "description": "npm run lang all=true 翻译所有语言改为npm run lang all",
56
56
  "files": [
57
57
  "lib/",
58
58
  "index.js"