pug-site-core 1.0.1
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 +4 -0
- package/lib/devServer.js +232 -0
- package/lib/generate.js +674 -0
- package/lib/pugRuntime.js +1 -0
- package/lib/translate.js +98 -0
- package/lib/utils.js +374 -0
- package/lib/watchFile.js +32 -0
- package/package.json +34 -0
package/lib/translate.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { v2 } from "@google-cloud/translate";
|
|
2
|
+
import fse from "fs-extra";
|
|
3
|
+
import languageData from "./languageData.js";
|
|
4
|
+
import async from "async";
|
|
5
|
+
import { config } from "../config.js";
|
|
6
|
+
import path from "path";
|
|
7
|
+
|
|
8
|
+
const key = "AIzaSyAbhPvlSAAJnH_IIRbdwjXOX9c0plvlH_k";
|
|
9
|
+
const projectId = "3nm Game Site";
|
|
10
|
+
let translate = new v2.Translate({ projectId, key });
|
|
11
|
+
|
|
12
|
+
let orginLang = "us";
|
|
13
|
+
|
|
14
|
+
let args = process.argv.slice(2);
|
|
15
|
+
let filParms = [];
|
|
16
|
+
let filCountry = [];
|
|
17
|
+
args.forEach((item) => {
|
|
18
|
+
const [key, value] = item.split("=");
|
|
19
|
+
if (value) {
|
|
20
|
+
if (key === "k") {
|
|
21
|
+
filParms = value.split(",");
|
|
22
|
+
}
|
|
23
|
+
if (key === "c") {
|
|
24
|
+
filCountry = value.split(",");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let targetLangList = filCountry.length > 0 ? filCountry : config.languageList;
|
|
30
|
+
|
|
31
|
+
//国家映射到语言
|
|
32
|
+
const countryLanguageMap = {
|
|
33
|
+
xp: "en",
|
|
34
|
+
us: "en",
|
|
35
|
+
us2: "en",
|
|
36
|
+
kr: "ko",
|
|
37
|
+
jp: "ja",
|
|
38
|
+
vn: "vi",
|
|
39
|
+
tw: "zh-TW",
|
|
40
|
+
gb: "en",
|
|
41
|
+
br: "pt",
|
|
42
|
+
in: "hi"
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
async function translateStr(str, targetLanguage) {
|
|
46
|
+
let [result] = await translate.translate(str, targetLanguage);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function main() {
|
|
51
|
+
let orginData = languageData[orginLang];
|
|
52
|
+
console.log("开始翻译");
|
|
53
|
+
let startTime = Date.now();
|
|
54
|
+
await async.each(targetLangList, async (language) => {
|
|
55
|
+
if (language === orginLang) return;
|
|
56
|
+
console.log(language);
|
|
57
|
+
languageData[language] = languageData[language] || {};
|
|
58
|
+
// 创建一个栈来模拟递归
|
|
59
|
+
let stack = [{ original: orginData, copy: languageData[language] }];
|
|
60
|
+
while (stack.length) {
|
|
61
|
+
let { original, copy } = stack.pop();
|
|
62
|
+
for (let key in original) {
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(original, key)) {
|
|
64
|
+
//第一层的key过滤
|
|
65
|
+
if (
|
|
66
|
+
original === orginData &&
|
|
67
|
+
filParms.length > 0 &&
|
|
68
|
+
!filParms.includes(key)
|
|
69
|
+
) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
let value = original[key];
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
// 如果属性是字符串类型,进行翻译
|
|
75
|
+
copy[key] = await translateStr(
|
|
76
|
+
value,
|
|
77
|
+
countryLanguageMap[language] || language
|
|
78
|
+
);
|
|
79
|
+
} else if (typeof value === "object" && value !== null) {
|
|
80
|
+
// 如果属性是对象类型,创建一个新对象并入栈
|
|
81
|
+
copy[key] = Array.isArray(value) ? [] : {};
|
|
82
|
+
stack.push({ original: value, copy: copy[key] });
|
|
83
|
+
} else {
|
|
84
|
+
// 其他类型的属性直接赋值
|
|
85
|
+
copy[key] = value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
fse.writeFileSync(
|
|
92
|
+
path.join(path.resolve(), "langConfig", "languageData.js"),
|
|
93
|
+
"export default" + JSON.stringify(languageData)
|
|
94
|
+
);
|
|
95
|
+
console.log("翻译完成花费:", (Date.now() - startTime) / 1000 + "s");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
main();
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import fse from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import tcpPortUsed from "tcp-port-used";
|
|
4
|
+
import JavaScriptObfuscator from "javascript-obfuscator";
|
|
5
|
+
import { pathToFileURL } from "url";
|
|
6
|
+
|
|
7
|
+
// 获取当前模块的目录名
|
|
8
|
+
const __dirname = process.cwd();
|
|
9
|
+
const configPath = pathToFileURL(path.resolve(__dirname, "config.js")).href;
|
|
10
|
+
const { config } = await import(configPath);
|
|
11
|
+
|
|
12
|
+
// 根据操作系统设置路径分隔符
|
|
13
|
+
export const pathSymbol = process.platform.startsWith("win") ? "\\" : "/";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 获取pages目录下所有pug文件的路径数组
|
|
17
|
+
* @param {boolean} isFilter - 是否需要过滤路径(去除语言和设备类型目录)
|
|
18
|
+
* @returns {Promise<string[]>} 返回pug文件路径数组
|
|
19
|
+
*/
|
|
20
|
+
export async function getPagesPugFilePathArr(isFilter) {
|
|
21
|
+
let pagesPugFilePathArr = (
|
|
22
|
+
await fse.readdir(path.join(__dirname, "/template/pages"), {
|
|
23
|
+
recursive: true
|
|
24
|
+
})
|
|
25
|
+
).filter((fileName) => fileName.endsWith(".pug"));
|
|
26
|
+
|
|
27
|
+
if (isFilter) {
|
|
28
|
+
pagesPugFilePathArr = pagesPugFilePathArr.map((fileName) => {
|
|
29
|
+
return pagesPathFilter(fileName);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
pagesPugFilePathArr = Array.from(new Set(pagesPugFilePathArr));
|
|
33
|
+
return pagesPugFilePathArr;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 过滤pug文件路径,移除语言和设备类型目录
|
|
38
|
+
* @param {string} pugPath - pug文件路径
|
|
39
|
+
* @returns {string} 过滤后的路径
|
|
40
|
+
*/
|
|
41
|
+
export function pagesPathFilter(pugPath) {
|
|
42
|
+
pugPath = pugPath
|
|
43
|
+
.split(pathSymbol)
|
|
44
|
+
.filter((item) => !config.languageList.includes(item))
|
|
45
|
+
.join(pathSymbol);
|
|
46
|
+
pugPath = pugPath
|
|
47
|
+
.split(pathSymbol)
|
|
48
|
+
.filter((item) => !["pc", "mobile", "ipad"].includes(item))
|
|
49
|
+
.join(pathSymbol);
|
|
50
|
+
return pugPath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 获取pug编译过滤器
|
|
55
|
+
* @returns {Object} 返回过滤器对象
|
|
56
|
+
*/
|
|
57
|
+
export function getCompilePugFilter() {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 比较两个路径是否相同(忽略斜杠方向)
|
|
63
|
+
* @param {string} path1 - 第一个路径
|
|
64
|
+
* @param {string} path2 - 第二个路径
|
|
65
|
+
* @returns {boolean} 如果路径相同返回true,否则返回false
|
|
66
|
+
*/
|
|
67
|
+
export function pathIsSame(path1, path2) {
|
|
68
|
+
return (
|
|
69
|
+
path1.replaceAll("/", "").replaceAll("\\", "") ===
|
|
70
|
+
path2.replaceAll("/", "").replaceAll("\\", "")
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 延迟执行
|
|
76
|
+
* @param {number} ms - 延迟毫秒数
|
|
77
|
+
* @returns {Promise<void>}
|
|
78
|
+
*/
|
|
79
|
+
export async function sleep(ms) {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
setTimeout(resolve, ms);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 获取可用的端口号
|
|
87
|
+
* @param {number} port - 起始端口号
|
|
88
|
+
* @param {string} Ip - IP地址
|
|
89
|
+
* @returns {Promise<number>} 返回可用的端口号
|
|
90
|
+
*/
|
|
91
|
+
export async function getIdleProt(port, Ip) {
|
|
92
|
+
while (1) {
|
|
93
|
+
if (!(await tcpPortUsed.check(port, Ip))) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
port++;
|
|
97
|
+
}
|
|
98
|
+
return port;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 函数防抖
|
|
103
|
+
* @param {Function} func - 需要防抖的函数
|
|
104
|
+
* @param {number} delay - 延迟时间(毫秒)
|
|
105
|
+
* @returns {Function} 返回防抖后的函数
|
|
106
|
+
*/
|
|
107
|
+
export function debounce(func, delay) {
|
|
108
|
+
let timeoutId;
|
|
109
|
+
|
|
110
|
+
return function () {
|
|
111
|
+
const context = this;
|
|
112
|
+
const args = arguments;
|
|
113
|
+
|
|
114
|
+
clearTimeout(timeoutId);
|
|
115
|
+
|
|
116
|
+
timeoutId = setTimeout(() => {
|
|
117
|
+
func.apply(context, args);
|
|
118
|
+
}, delay);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 匹配并处理ESI标签
|
|
124
|
+
* @param {string} body - 页面内容
|
|
125
|
+
* @param {Object} data - 数据对象
|
|
126
|
+
* @returns {Promise<string>} 返回处理后的内容
|
|
127
|
+
*/
|
|
128
|
+
export async function matchESI(body, data) {
|
|
129
|
+
/**
|
|
130
|
+
* 处理单个 ESI
|
|
131
|
+
* @param {*} content 需要替换的文本
|
|
132
|
+
* @param {*} data 数据
|
|
133
|
+
* @returns
|
|
134
|
+
*/
|
|
135
|
+
async function setVar(content, data) {
|
|
136
|
+
return await content.replace(/\^\^esi:(.*?)\^\^/g, (match, key) => {
|
|
137
|
+
// 填充 adsense channel id 和 gam key-value
|
|
138
|
+
// if(PLACEMENT_KEY.includes(key)){
|
|
139
|
+
// return placement[key]
|
|
140
|
+
// }
|
|
141
|
+
// 填充主体内容
|
|
142
|
+
let res = data[key.trim()];
|
|
143
|
+
return typeof res == "object"
|
|
144
|
+
? JSON.stringify(res).replace(/\"/g, """)
|
|
145
|
+
: res;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
let newBody = "";
|
|
150
|
+
let current = 0;
|
|
151
|
+
let esiReg = /<esi:for(.*?)>([\s\S]*?)<\/esi:for>/gm;
|
|
152
|
+
var res = esiReg.exec(body);
|
|
153
|
+
|
|
154
|
+
for (; res != null; res = esiReg.exec(body)) {
|
|
155
|
+
// for 循环
|
|
156
|
+
let regStr = res[0]; // 完整的for str
|
|
157
|
+
// 获取开始标签
|
|
158
|
+
var kvReg = /(\w+)="(\w+|\-?\d+)"/gm;
|
|
159
|
+
var kv = {};
|
|
160
|
+
var temKv;
|
|
161
|
+
while ((temKv = kvReg.exec(res[1]))) {
|
|
162
|
+
kv[temKv[1]] = temKv[2];
|
|
163
|
+
}
|
|
164
|
+
const { key, start, end, index_floor } = kv;
|
|
165
|
+
let dom = res[2];
|
|
166
|
+
let data_;
|
|
167
|
+
data_ = data[key].slice(start, end);
|
|
168
|
+
newBody += body.slice(current, res.index);
|
|
169
|
+
for (var i = 0; i < data_.length; i++) {
|
|
170
|
+
let tmpData = data_[i];
|
|
171
|
+
tmpData.index = index_floor ? i + parseInt(index_floor) : i;
|
|
172
|
+
newBody += await setVar(dom, tmpData);
|
|
173
|
+
}
|
|
174
|
+
current = res.index + regStr.length;
|
|
175
|
+
}
|
|
176
|
+
newBody += body.slice(current, body.length);
|
|
177
|
+
return await setVar(newBody, data);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.log(error);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 混淆 JavaScript 代码(优化后的混淆配置)
|
|
185
|
+
* @param {string} filePath - JS文件路径或目录路径
|
|
186
|
+
* @param {Object} options - 混淆选项,可选
|
|
187
|
+
* @param {string[]} excludePaths - 要排除的文件或目录路径数组
|
|
188
|
+
* @returns {Promise<void>}
|
|
189
|
+
*/
|
|
190
|
+
export async function obfuscateJavaScript(
|
|
191
|
+
filePath,
|
|
192
|
+
options = {},
|
|
193
|
+
excludePaths = []
|
|
194
|
+
) {
|
|
195
|
+
// 优化后的混淆配置
|
|
196
|
+
const defaultOptions = {
|
|
197
|
+
// 基础设置
|
|
198
|
+
compact: true,
|
|
199
|
+
controlFlowFlattening: true,
|
|
200
|
+
controlFlowFlatteningThreshold: 0.5, // 降低到0.5,减少代码膨胀
|
|
201
|
+
|
|
202
|
+
// 移除死代码注入,因为会显著增加文件大小
|
|
203
|
+
deadCodeInjection: false,
|
|
204
|
+
|
|
205
|
+
// 保留调试保护但降低频率
|
|
206
|
+
debugProtection: true,
|
|
207
|
+
debugProtectionInterval: 2000, // 降低检查频率
|
|
208
|
+
|
|
209
|
+
// 禁用控制台
|
|
210
|
+
disableConsoleOutput: true,
|
|
211
|
+
|
|
212
|
+
// 标识符混淆
|
|
213
|
+
identifierNamesGenerator: "hexadecimal",
|
|
214
|
+
identifiersPrefix: "_", // 使用更短的前缀
|
|
215
|
+
|
|
216
|
+
// 字符串保护
|
|
217
|
+
stringArray: true,
|
|
218
|
+
stringArrayEncoding: ["base64"], // 改用base64,比rc4更轻量
|
|
219
|
+
stringArrayThreshold: 0.75, // 降低阈值,减少处理的字符串数量
|
|
220
|
+
|
|
221
|
+
// 禁用字符串分割,因为会显著增加大小
|
|
222
|
+
splitStrings: false,
|
|
223
|
+
|
|
224
|
+
// 保留关键的混淆选项
|
|
225
|
+
transformObjectKeys: true,
|
|
226
|
+
numbersToExpressions: true,
|
|
227
|
+
simplify: true,
|
|
228
|
+
|
|
229
|
+
// 随机种子
|
|
230
|
+
seed: Math.random(),
|
|
231
|
+
|
|
232
|
+
// 移除自我防护,因为会增加代码大小
|
|
233
|
+
selfDefending: false,
|
|
234
|
+
|
|
235
|
+
// 基础设置
|
|
236
|
+
renameGlobals: false,
|
|
237
|
+
sourceMap: false,
|
|
238
|
+
|
|
239
|
+
// 字符串数组处理
|
|
240
|
+
rotateStringArray: true,
|
|
241
|
+
shuffleStringArray: true,
|
|
242
|
+
|
|
243
|
+
// 禁用Unicode转义,因为会增加文件大小
|
|
244
|
+
unicodeEscapeSequence: false,
|
|
245
|
+
|
|
246
|
+
// 移除注释
|
|
247
|
+
removeComments: true,
|
|
248
|
+
|
|
249
|
+
// 禁用正则表达式混淆,因为会增加大小
|
|
250
|
+
regexesObfuscation: false,
|
|
251
|
+
|
|
252
|
+
// 新增:目标环境优化
|
|
253
|
+
target: "browser",
|
|
254
|
+
|
|
255
|
+
// 新增:优化选项
|
|
256
|
+
reservedStrings: [], // 不混淆的字符串
|
|
257
|
+
reservedNames: [], // 不混淆的标识符
|
|
258
|
+
|
|
259
|
+
// 新增:代码压缩选项
|
|
260
|
+
compress: {
|
|
261
|
+
sequences: true,
|
|
262
|
+
dead_code: true,
|
|
263
|
+
conditionals: true,
|
|
264
|
+
booleans: true,
|
|
265
|
+
unused: true,
|
|
266
|
+
if_return: true,
|
|
267
|
+
join_vars: true,
|
|
268
|
+
drop_console: true
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// 合并配置选项
|
|
273
|
+
const obfuscatorOptions = { ...defaultOptions, ...options };
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 检查路径是否应该被排除
|
|
277
|
+
* @param {string} targetPath - 要检查的路径
|
|
278
|
+
* @returns {boolean} 是否应该被排除
|
|
279
|
+
*/
|
|
280
|
+
function shouldExclude(targetPath) {
|
|
281
|
+
// 获取相对于基础路径的相对路径
|
|
282
|
+
const relativePath = path.relative(filePath, targetPath);
|
|
283
|
+
|
|
284
|
+
return excludePaths.some((excludePath) => {
|
|
285
|
+
// 将排除路径标准化(处理不同操作系统的路径分隔符)
|
|
286
|
+
const normalizedExcludePath = excludePath.split("/").join(pathSymbol);
|
|
287
|
+
const normalizedRelativePath = relativePath.split("/").join(pathSymbol);
|
|
288
|
+
|
|
289
|
+
// 检查是否匹配排除路径
|
|
290
|
+
return (
|
|
291
|
+
normalizedRelativePath.startsWith(normalizedExcludePath) ||
|
|
292
|
+
normalizedRelativePath === normalizedExcludePath
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 混淆单个JS文件
|
|
299
|
+
* @param {string} jsFilePath - JS文件路径
|
|
300
|
+
*/
|
|
301
|
+
async function obfuscateFile(jsFilePath) {
|
|
302
|
+
try {
|
|
303
|
+
// 检查是否应该排除此文件
|
|
304
|
+
if (shouldExclude(jsFilePath)) {
|
|
305
|
+
console.log(`Skipped (excluded): ${jsFilePath}`);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// 读取文件内容
|
|
310
|
+
const code = await fse.readFile(jsFilePath, "utf-8");
|
|
311
|
+
|
|
312
|
+
// 混淆代码
|
|
313
|
+
const obfuscationResult = JavaScriptObfuscator.obfuscate(
|
|
314
|
+
code,
|
|
315
|
+
obfuscatorOptions
|
|
316
|
+
);
|
|
317
|
+
const obfuscatedCode = obfuscationResult.getObfuscatedCode();
|
|
318
|
+
|
|
319
|
+
// 写回原文件
|
|
320
|
+
await fse.writeFile(jsFilePath, obfuscatedCode, "utf-8");
|
|
321
|
+
console.log(`Obfuscated: ${jsFilePath}`);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error(`Error obfuscating ${jsFilePath}:`, error);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* 递归处理目录
|
|
329
|
+
* @param {string} dirPath - 目录路径
|
|
330
|
+
*/
|
|
331
|
+
async function processDirectory(dirPath) {
|
|
332
|
+
try {
|
|
333
|
+
// 检查是否应该排除此目录
|
|
334
|
+
if (shouldExclude(dirPath)) {
|
|
335
|
+
console.log(`Skipped directory (excluded): ${dirPath}`);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const files = await fse.readdir(dirPath);
|
|
340
|
+
|
|
341
|
+
for (const file of files) {
|
|
342
|
+
const fullPath = path.join(dirPath, file);
|
|
343
|
+
const stats = await fse.stat(fullPath);
|
|
344
|
+
|
|
345
|
+
if (stats.isDirectory()) {
|
|
346
|
+
// 递归处理子目录
|
|
347
|
+
await processDirectory(fullPath);
|
|
348
|
+
} else if (file.endsWith(".js")) {
|
|
349
|
+
// 处理JS文件
|
|
350
|
+
await obfuscateFile(fullPath);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
} catch (error) {
|
|
354
|
+
console.error(`Error processing directory ${dirPath}:`, error);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
const stats = await fse.stat(filePath);
|
|
360
|
+
|
|
361
|
+
if (stats.isDirectory()) {
|
|
362
|
+
// 如果是目录,递归处理
|
|
363
|
+
await processDirectory(filePath);
|
|
364
|
+
} else if (filePath.endsWith(".js")) {
|
|
365
|
+
// 如果是JS文件,直接处理
|
|
366
|
+
await obfuscateFile(filePath);
|
|
367
|
+
} else {
|
|
368
|
+
console.warn("Not a JavaScript file or directory:", filePath);
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error("Error processing path:", error);
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
}
|
package/lib/watchFile.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import chokidar from "chokidar";
|
|
2
|
+
import fse from "fs-extra";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { exec } from "child_process";
|
|
5
|
+
import { debounce } from "./utils.js";
|
|
6
|
+
import { config } from "./config.js";
|
|
7
|
+
const __dirname = path.resolve();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 更改/template刷新网页
|
|
11
|
+
*/
|
|
12
|
+
function watchTemplate() {
|
|
13
|
+
const refreshPagFn = debounce(() => {
|
|
14
|
+
exec(`curl http://${process.env._localIp}:${process.env._port}/_refresh`);
|
|
15
|
+
}, 300);
|
|
16
|
+
let watch = chokidar.watch("./template", {
|
|
17
|
+
persistent: true
|
|
18
|
+
// ignored: [/node_modules/, /\.git/]
|
|
19
|
+
});
|
|
20
|
+
watch
|
|
21
|
+
.on("error", (error) => {
|
|
22
|
+
console.error(`Watcher error: ${error}`);
|
|
23
|
+
})
|
|
24
|
+
.on("add", refreshPagFn)
|
|
25
|
+
.on("change", refreshPagFn)
|
|
26
|
+
.on("unlink", refreshPagFn)
|
|
27
|
+
.on("unlinkDir", refreshPagFn);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
(() => {
|
|
31
|
+
watchTemplate();
|
|
32
|
+
})();
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pug-site-core",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "xy",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@google-cloud/translate": "^8.5.0",
|
|
13
|
+
"async": "^3.2.6",
|
|
14
|
+
"axios": "^1.7.7",
|
|
15
|
+
"chokidar": "^3.6.0",
|
|
16
|
+
"express": "^4.19.2",
|
|
17
|
+
"express-useragent": "^1.0.15",
|
|
18
|
+
"fs-extra": "^11.2.0",
|
|
19
|
+
"ip": "^2.0.1",
|
|
20
|
+
"javascript-obfuscator": "^4.1.1",
|
|
21
|
+
"jstransformer-autoprefixer": "^2.0.0",
|
|
22
|
+
"jstransformer-less": "^2.3.0",
|
|
23
|
+
"jstransformer-scss": "^2.0.0",
|
|
24
|
+
"less": "^4.2.0",
|
|
25
|
+
"lodash": "^4.17.21",
|
|
26
|
+
"nodemon": "^3.1.4",
|
|
27
|
+
"pug": "^3.0.3",
|
|
28
|
+
"tcp-port-used": "^1.0.2",
|
|
29
|
+
"uglify-js": "^3.19.3",
|
|
30
|
+
"ws": "^8.18.0"
|
|
31
|
+
},
|
|
32
|
+
"license": "ISC",
|
|
33
|
+
"description": ""
|
|
34
|
+
}
|