utiller 1.0.412 → 1.0.413
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 +1 -21
- package/lib/exceptioner/ERRORs.js +1 -192
- package/lib/exceptioner/index.js +1 -39
- package/lib/index.js +1 -30
- package/lib/pooller/index.js +1 -1117
- package/lib/utiller/index.js +1 -3531
- package/lib/utiller/nodeutiller.js +3 -1095
- package/package.json +1 -1
- package/template/sample.package.json +1 -1
|
@@ -1,1095 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
Object.defineProperty(exports, "__esModule", {
|
|
5
|
-
value: true
|
|
6
|
-
});
|
|
7
|
-
exports.default = void 0;
|
|
8
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _path2 = _interopRequireDefault(require("path"));
|
|
10
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
11
|
-
var _promises = _interopRequireDefault(require("fs/promises"));
|
|
12
|
-
var _lodash = _interopRequireDefault(require("lodash"));
|
|
13
|
-
var _child_process = _interopRequireDefault(require("child_process"));
|
|
14
|
-
var _configerer = require("configerer");
|
|
15
|
-
var _index = _interopRequireDefault(require("./index"));
|
|
16
|
-
var _index2 = _interopRequireDefault(require("../exceptioner/index"));
|
|
17
|
-
var _pdfParse = _interopRequireDefault(require("pdf-parse"));
|
|
18
|
-
var _del = _interopRequireDefault(require("del"));
|
|
19
|
-
var _fsExtra = _interopRequireDefault(require("fs-extra"));
|
|
20
|
-
var _prompt = _interopRequireDefault(require("prompt"));
|
|
21
|
-
class NodeUtiller extends _index.default {
|
|
22
|
-
constructor(..._args) {
|
|
23
|
-
super(..._args);
|
|
24
|
-
/**================================= only in node.js ================================= */
|
|
25
|
-
/** 是否把log message 存到 info.txt*/
|
|
26
|
-
(0, _defineProperty2.default)(this, "isPersistIntoLogFile", true);
|
|
27
|
-
/** return [...{path: ,fileName: ,extension: ,absolute: ,dirName:}]*/
|
|
28
|
-
(0, _defineProperty2.default)(this, "findFilePathByExtension", (rootpath, _extension = [], ...exclude) => {
|
|
29
|
-
const reg = new RegExp(`^[^\.].+.(${_lodash.default.join(_extension, "|")})$`);
|
|
30
|
-
return this.findFilePathBy(rootpath, item => {
|
|
31
|
-
return reg.test(item.fileNameExtension);
|
|
32
|
-
}, ...exclude);
|
|
33
|
-
});
|
|
34
|
-
(0, _defineProperty2.default)(this, "executeCommandLine", async command => {
|
|
35
|
-
const self = this;
|
|
36
|
-
this.appendInfo(`執行腳本 ${command}`);
|
|
37
|
-
return new Promise(function (resolve, reject) {
|
|
38
|
-
_child_process.default.exec(command, (error, stdout, stderr) => {
|
|
39
|
-
self.appendInfo(`${stdout}`);
|
|
40
|
-
self.appendInfo(`${stderr}`);
|
|
41
|
-
if (error) {
|
|
42
|
-
self.appendError(`執行錯誤: ${error}`);
|
|
43
|
-
reject(error);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
resolve(stdout.trim());
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
/**
|
|
51
|
-
* 將 Terser 配置對象轉換為命令行參數字符串
|
|
52
|
-
*
|
|
53
|
-
* 此函數會讀取 terser.config.js 的配置,並生成對應的 CLI 參數
|
|
54
|
-
* 用於在 shell 命令中執行 terser
|
|
55
|
-
*
|
|
56
|
-
* @param {Object} terserConfig - Terser 配置對象(來自 terser.config.js)
|
|
57
|
-
* @param {Object} terserConfig.compress - 壓縮選項
|
|
58
|
-
* @param {boolean} terserConfig.compress.drop_console - 是否移除 console
|
|
59
|
-
* @param {boolean} terserConfig.compress.drop_debugger - 是否移除 debugger
|
|
60
|
-
* @param {number} terserConfig.compress.passes - 壓縮遍數(1-3)
|
|
61
|
-
* @param {boolean} terserConfig.compress.dead_code - 是否移除死代碼
|
|
62
|
-
* @param {boolean} terserConfig.compress.unused - 是否移除未使用的變數
|
|
63
|
-
* @param {boolean} terserConfig.mangle - 是否混淆變數名
|
|
64
|
-
* @param {Object} terserConfig.format - 格式化選項
|
|
65
|
-
* @param {boolean} terserConfig.format.beautify - 是否美化代碼
|
|
66
|
-
* @param {string|boolean} terserConfig.format.comments - 註解處理方式
|
|
67
|
-
* @param {number} terserConfig.format.indent_level - 縮排層級
|
|
68
|
-
*
|
|
69
|
-
* @returns {string} Terser 命令行參數字符串
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* const config = {
|
|
73
|
-
* compress: { drop_console: true, drop_debugger: true, passes: 2 },
|
|
74
|
-
* mangle: true,
|
|
75
|
-
* format: { beautify: false, comments: false }
|
|
76
|
-
* };
|
|
77
|
-
* const args = getStringOfTerserCommandLine(config);
|
|
78
|
-
* // 返回: "--compress drop_console=true,drop_debugger=true,passes=2 --mangle --no-comments"
|
|
79
|
-
*/
|
|
80
|
-
(0, _defineProperty2.default)(this, "getStringOfTerserCommandLine", terserConfig => {
|
|
81
|
-
// 用於存儲所有命令行參數的數組
|
|
82
|
-
const args = [];
|
|
83
|
-
|
|
84
|
-
// ========================================================================
|
|
85
|
-
// 處理 compress 選項(壓縮相關配置)
|
|
86
|
-
// ========================================================================
|
|
87
|
-
if (terserConfig.compress) {
|
|
88
|
-
// 用於存儲所有 compress 子選項的數組
|
|
89
|
-
const compressOpts = [];
|
|
90
|
-
|
|
91
|
-
// 移除所有 console.* 語句
|
|
92
|
-
// 例如:console.log(), console.warn(), console.error() 等
|
|
93
|
-
if (terserConfig.compress.drop_console) {
|
|
94
|
-
compressOpts.push("drop_console=true");
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 移除所有 debugger 語句
|
|
98
|
-
if (terserConfig.compress.drop_debugger) {
|
|
99
|
-
compressOpts.push("drop_debugger=true");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 設置壓縮遍數(1-3)
|
|
103
|
-
// passes=1: 快速壓縮(推薦開發環境)
|
|
104
|
-
// passes=2: 平衡壓縮(推薦生產環境)
|
|
105
|
-
// passes=3: 最大壓縮(體積要求極高時使用)
|
|
106
|
-
if (terserConfig.compress.passes) {
|
|
107
|
-
compressOpts.push(`passes=${terserConfig.compress.passes}`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// 移除永遠不會執行的代碼(死代碼)
|
|
111
|
-
// 例如:if (false) { ... } 或 return 後的代碼
|
|
112
|
-
if (terserConfig.compress.dead_code) {
|
|
113
|
-
compressOpts.push("dead_code=true");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// 移除未使用的變數和函數
|
|
117
|
-
// 幫助減少最終打包體積
|
|
118
|
-
if (terserConfig.compress.unused) {
|
|
119
|
-
compressOpts.push("unused=true");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// 如果有任何 compress 選項,將它們組合成一個 --compress 參數
|
|
123
|
-
// 格式:--compress option1=value1,option2=value2,...
|
|
124
|
-
if (compressOpts.length > 0) {
|
|
125
|
-
args.push(`--compress ${compressOpts.join(",")}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ========================================================================
|
|
130
|
-
// 處理 mangle 選項(變數名混淆)
|
|
131
|
-
// ========================================================================
|
|
132
|
-
// mangle: true → 混淆變數名(longVariableName → a)
|
|
133
|
-
// mangle: false → 保持原始變數名
|
|
134
|
-
if (terserConfig.mangle === true) {
|
|
135
|
-
args.push("--mangle");
|
|
136
|
-
}
|
|
137
|
-
// 注意:如果 mangle: false,則不添加任何參數(默認不混淆)
|
|
138
|
-
|
|
139
|
-
// ========================================================================
|
|
140
|
-
// 處理 format 選項(代碼格式化)
|
|
141
|
-
// ========================================================================
|
|
142
|
-
if (terserConfig.format) {
|
|
143
|
-
// 是否美化代碼(保持縮排和換行)
|
|
144
|
-
// beautify: true → 代碼易讀,有格式
|
|
145
|
-
// beautify: false → 壓縮成一行,體積更小
|
|
146
|
-
if (terserConfig.format.beautify === true) {
|
|
147
|
-
args.push("--beautify");
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 處理註解的保留方式
|
|
151
|
-
if (terserConfig.format.comments === false) {
|
|
152
|
-
// comments: false → 移除所有註解
|
|
153
|
-
// 使用 --no-comments 而不是 --comments false
|
|
154
|
-
// 因為 --no-comments 在所有 Terser 版本中都更可靠
|
|
155
|
-
args.push("--no-comments");
|
|
156
|
-
} else if (terserConfig.format.comments === "all") {
|
|
157
|
-
// comments: 'all' → 保留所有註解
|
|
158
|
-
args.push("--comments all");
|
|
159
|
-
} else if (terserConfig.format.comments) {
|
|
160
|
-
// comments: /正則/ 或其他字符串 → 只保留匹配的註解
|
|
161
|
-
// 例如:comments: /@license|@preserve/
|
|
162
|
-
// 用引號包裹以防止 shell 解析問題
|
|
163
|
-
args.push(`--comments "${terserConfig.format.comments}"`);
|
|
164
|
-
}
|
|
165
|
-
// 注意:如果 comments 未設置,則使用 Terser 默認行為
|
|
166
|
-
|
|
167
|
-
// 設置縮排層級(只在 beautify: true 時有效)
|
|
168
|
-
// 例如:indent_level: 4 表示使用 4 個空格縮排
|
|
169
|
-
if (terserConfig.format.indent_level) {
|
|
170
|
-
args.push(`--format indent_level=${terserConfig.format.indent_level}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ========================================================================
|
|
175
|
-
// 返回組合後的命令行參數字符串
|
|
176
|
-
// ========================================================================
|
|
177
|
-
// 使用空格將所有參數連接起來
|
|
178
|
-
// 例如:"--compress drop_console=true --mangle --no-comments"
|
|
179
|
-
return args.join(" ");
|
|
180
|
-
});
|
|
181
|
-
/**
|
|
182
|
-
* 從絕對路徑中取出 "src/" 之後的部分(包含前置 /)
|
|
183
|
-
* @param {string} fullPath - 完整的絕對檔案路徑
|
|
184
|
-
* @param {string} folder - 針對/folder/之後作為split起點(預設為 src)
|
|
185
|
-
* @returns {string} - 以 / 開頭、從 src/ 之後開始的相對路徑
|
|
186
|
-
*/
|
|
187
|
-
(0, _defineProperty2.default)(this, "getPathAfterSpecificFolder", (fullPath, folder = "src") => {
|
|
188
|
-
const parts = fullPath.split(_path2.default.sep);
|
|
189
|
-
for (let i = parts.length - 1; i >= 0; i--) {
|
|
190
|
-
if (parts[i] === folder) {
|
|
191
|
-
return "/" + parts.slice(i + 1).join("/");
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return "";
|
|
195
|
-
});
|
|
196
|
-
/**
|
|
197
|
-
* 從絕對路徑中取出 "src/" 之後的部分(包含前置 /)
|
|
198
|
-
* @param {string} fullPath - 完整的絕對檔案路徑
|
|
199
|
-
* @returns {string} - 以 / 開頭、從 src/ 之後開始的相對路徑
|
|
200
|
-
*/
|
|
201
|
-
(0, _defineProperty2.default)(this, "getPathAfterSrc", fullPath => {
|
|
202
|
-
return this.getPathAfterSpecificFolder(fullPath);
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
findSpecificFolderByPath(path, folderName) {
|
|
206
|
-
const absolute = _path2.default.resolve(path);
|
|
207
|
-
const parts = absolute.split(_path2.default.sep);
|
|
208
|
-
while (parts.length) {
|
|
209
|
-
const joined = _path2.default.join(...parts, folderName);
|
|
210
|
-
if (_fs.default.existsSync(joined)) return joined;
|
|
211
|
-
parts.pop();
|
|
212
|
-
}
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** {numpages, numrender, info, text, version} */
|
|
217
|
-
async getPDFText(path) {
|
|
218
|
-
let dataBuffer = _fs.default.readFileSync(path);
|
|
219
|
-
return (0, _pdfParse.default)(dataBuffer).then(data => {
|
|
220
|
-
return data;
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
printf() {
|
|
224
|
-
this.appendInfo("i can use in node.js only yo yo");
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* 遞回的去找出folder每一個child file, 預設是全部檔案, 可以透過predicate做filter, 可以exclude 指定的 'folder name'
|
|
229
|
-
*
|
|
230
|
-
* predicate: predicate(pathInfo); predicate帶的參數是 pathInfo object
|
|
231
|
-
*
|
|
232
|
-
* excludes 忽略掉的資料夾名稱
|
|
233
|
-
*
|
|
234
|
-
* return [...{
|
|
235
|
-
path: 'database/index.js',
|
|
236
|
-
fileName: 'index',
|
|
237
|
-
extension: 'js',
|
|
238
|
-
dirName: database
|
|
239
|
-
absolute: '/Users/davidtu/cross-achieve/mimi19up/mimi19up-scrapy/database/index.js'}
|
|
240
|
-
] */
|
|
241
|
-
findFilePathBy(path, predicate = () => true, ...excludes) {
|
|
242
|
-
if (!_fs.default.existsSync(path)) return [];
|
|
243
|
-
const result = [];
|
|
244
|
-
const entries = _fs.default.readdirSync(path, {
|
|
245
|
-
withFileTypes: true
|
|
246
|
-
});
|
|
247
|
-
for (const entry of entries) {
|
|
248
|
-
if (excludes.includes(entry.name)) continue;
|
|
249
|
-
const fullPath = _path2.default.join(path, entry.name);
|
|
250
|
-
if (entry.isDirectory()) {
|
|
251
|
-
result.push(...this.findFilePathBy(fullPath, predicate, ...excludes));
|
|
252
|
-
} else if (entry.isFile()) {
|
|
253
|
-
const info = this.getPathInfo(fullPath);
|
|
254
|
-
if (predicate(info)) result.push(info);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return result;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/** 是一個存在的檔案 */
|
|
261
|
-
isPathExist(path) {
|
|
262
|
-
return _fs.default.existsSync(path);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/** path = a/b/c/file.js , newName = 'two'
|
|
266
|
-
* output => a/b/c/two.js
|
|
267
|
-
* */
|
|
268
|
-
renameFile(path, newName = "fileName") {
|
|
269
|
-
if (!this.isFile(path) || !newName) {
|
|
270
|
-
this.appendError(`renameFile 錯誤, path: ${path}, newName: ${newName}`);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
const dir = _path2.default.dirname(path);
|
|
274
|
-
const ext = _path2.default.extname(path);
|
|
275
|
-
const newPath = _path2.default.join(dir, `${newName}${ext}`);
|
|
276
|
-
_fs.default.renameSync(path, newPath);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
//todo 應該要改成class
|
|
280
|
-
getPathInfo(path) {
|
|
281
|
-
const absolute = _path2.default.resolve(path);
|
|
282
|
-
const obj = {
|
|
283
|
-
path,
|
|
284
|
-
absolute,
|
|
285
|
-
isFile: false,
|
|
286
|
-
isDirectory: true,
|
|
287
|
-
dirName: undefined,
|
|
288
|
-
folderName: undefined,
|
|
289
|
-
dirPath: undefined,
|
|
290
|
-
folderPath: undefined,
|
|
291
|
-
extension: undefined,
|
|
292
|
-
fileName: undefined,
|
|
293
|
-
fileNameExtension: undefined,
|
|
294
|
-
lastModifiedTime: undefined,
|
|
295
|
-
name: undefined
|
|
296
|
-
};
|
|
297
|
-
if (this.isFile(absolute)) {
|
|
298
|
-
obj["extension"] = absolute.split("\.").pop();
|
|
299
|
-
const fileNameStrings = absolute.split("\/").pop().split("\.");
|
|
300
|
-
fileNameStrings.pop();
|
|
301
|
-
/** todo 要是遇到 asd.sdsd.js 就麻煩了 */
|
|
302
|
-
obj["fileName"] = fileNameStrings.join("\.");
|
|
303
|
-
obj["name"] = fileNameStrings.join("\.");
|
|
304
|
-
obj["dirName"] = _lodash.default.nth(absolute.split("\/"), -2);
|
|
305
|
-
obj["folderName"] = _lodash.default.nth(absolute.split("\/"), -2);
|
|
306
|
-
obj["isFile"] = true;
|
|
307
|
-
obj["dirPath"] = this.getFolderPathOfSpecificPath(absolute);
|
|
308
|
-
obj["folderPath"] = this.getFolderPathOfSpecificPath(absolute);
|
|
309
|
-
obj["isDirectory"] = false;
|
|
310
|
-
obj["fileNameExtension"] = `${obj.fileName}.${obj.extension}`;
|
|
311
|
-
obj["lastModifiedTime"] = this.getFileLastModifiedTime(absolute);
|
|
312
|
-
}
|
|
313
|
-
if (this.isDirectory(absolute)) {
|
|
314
|
-
obj["dirName"] = absolute.split("\/").pop();
|
|
315
|
-
}
|
|
316
|
-
return obj;
|
|
317
|
-
}
|
|
318
|
-
syncExecuteCommandLine(command) {
|
|
319
|
-
const self = this;
|
|
320
|
-
this.appendInfo(`執行腳本 ${command}`);
|
|
321
|
-
_child_process.default.exec(`${command}`, (error, stdout, stderr) => {
|
|
322
|
-
self.appendInfo(`${stdout}`);
|
|
323
|
-
self.appendInfo(`${stderr}`);
|
|
324
|
-
if (error !== null) {
|
|
325
|
-
self.appendError(`exec error: ${error}`);
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
/** '/a/b/c.js' 把它變成真的 */
|
|
330
|
-
persistByPath(targetPath) {
|
|
331
|
-
const isAbsolute = _path2.default.isAbsolute(targetPath);
|
|
332
|
-
const parts = targetPath.split("/").filter(Boolean);
|
|
333
|
-
const lastPart = parts[parts.length - 1];
|
|
334
|
-
const isFile = _path2.default.extname(lastPart) !== ""; // ← 正規判斷副檔名
|
|
335
|
-
|
|
336
|
-
let current = isAbsolute ? _path2.default.sep : "";
|
|
337
|
-
for (let i = 0; i < parts.length; i++) {
|
|
338
|
-
current = _path2.default.join(current, parts[i]);
|
|
339
|
-
if (!_fs.default.existsSync(current)) {
|
|
340
|
-
if (i === parts.length - 1 && isFile) {
|
|
341
|
-
// 最後一個而且是檔案 → 建檔案
|
|
342
|
-
_fs.default.writeFileSync(current, "");
|
|
343
|
-
} else {
|
|
344
|
-
// 其他 → 建資料夾
|
|
345
|
-
_fs.default.mkdirSync(current, {
|
|
346
|
-
recursive: false
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return _path2.default.resolve(targetPath);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* filter:function, 就filter,(path) => {要就true}
|
|
356
|
-
* override: boolean, 要不要override檔案:default true;
|
|
357
|
-
* preserveTimestamps : boolean, 要不要保留原始檔案的時間, 不然就是cp的時間,default true;
|
|
358
|
-
* */
|
|
359
|
-
async copyFromFolderToDestFolder(from, dest, override = true, preserveTimestamps = false, filter = () => true) {
|
|
360
|
-
if (!_fs.default.existsSync(from) || !_fs.default.existsSync(from)) throw new _index2.default(8009, `${from} or ${dest} is not exist!`);
|
|
361
|
-
this.appendInfo(`正在複製ing ${from}/* => ${dest}/* succeed`);
|
|
362
|
-
_fsExtra.default.copySync(from, dest, {
|
|
363
|
-
preserveTimestamps,
|
|
364
|
-
override,
|
|
365
|
-
filter
|
|
366
|
-
});
|
|
367
|
-
this.appendInfo(`複製成功 ${from}/* => ${dest}/* succeed`);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/** remove all under dir */
|
|
371
|
-
cleanAllFiles(dir) {
|
|
372
|
-
if (this.isDirectory(dir)) {
|
|
373
|
-
this.appendInfo(`準備清除底下的所有 ${dir}`);
|
|
374
|
-
_fsExtra.default.emptyDirSync(dir);
|
|
375
|
-
this.appendInfo(`成功清除底下的所有 ${dir}`);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/** 刪掉自己, force能夠強制刪除 自己root_dir 以外的path */
|
|
380
|
-
async deleteSelfByPath(path, force) {
|
|
381
|
-
if (_fs.default.existsSync(path)) {
|
|
382
|
-
this.appendInfo(`準備刪掉 ${path},{force:${force}}`);
|
|
383
|
-
await (0, _del.default)(path, {
|
|
384
|
-
force
|
|
385
|
-
});
|
|
386
|
-
this.appendInfo(`成功刪掉了 ${path}`);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
async deleteFileOrFolder(path) {
|
|
390
|
-
this.appendInfo(`刪掉了 ${path}`);
|
|
391
|
-
await (0, _del.default)(path);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/** 刪掉自己目錄內的孩子, force能夠強制刪除 自己root_dir 以外的path,保留folder的記體體位置 */
|
|
395
|
-
async deleteChildByPath(path, force = false) {
|
|
396
|
-
const pathes = this.getChildPathByPath(path);
|
|
397
|
-
for (const path of pathes) {
|
|
398
|
-
await this.deleteSelfByPath(path.absolute, force);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/** 取得folder底下的file counts*/
|
|
403
|
-
getFileCountsOfFolder(path) {
|
|
404
|
-
if (this.isDirectory(path)) {
|
|
405
|
-
return _fs.default.readdirSync(path).length;
|
|
406
|
-
}
|
|
407
|
-
return -1;
|
|
408
|
-
}
|
|
409
|
-
async reinstallNodeModules(path = "../", ...exclude) {
|
|
410
|
-
const ex = [...exclude, "node_modules", "utiller", "configerer"];
|
|
411
|
-
/** utiller 不能刪掉,不然就爆了, configer是他的依賴也不能刪 */
|
|
412
|
-
|
|
413
|
-
const paths = this.findFilePathBy(path, each => _lodash.default.isEqual(each.fileNameExtension, "package.json"), ...ex);
|
|
414
|
-
for (const _json of paths) {
|
|
415
|
-
const path_module_root = this.getFileDirPath(_json.absolute);
|
|
416
|
-
const path_gen_node_module = `${path_module_root}node_modules`;
|
|
417
|
-
const path_lock_json = `${path_module_root}package-lock.json`;
|
|
418
|
-
await (0, _del.default)(path_lock_json);
|
|
419
|
-
this.appendInfo(`刪掉了 ${path_lock_json}`);
|
|
420
|
-
await (0, _del.default)(path_gen_node_module);
|
|
421
|
-
this.appendInfo(`刪掉了 ${path_gen_node_module}`);
|
|
422
|
-
}
|
|
423
|
-
for (const _json of paths) {
|
|
424
|
-
const path_module_root = this.getFileDirPath(_json.absolute);
|
|
425
|
-
const path_gen_node_module = `${path_module_root}node_modules`;
|
|
426
|
-
if (!_fs.default.existsSync(path_gen_node_module)) {
|
|
427
|
-
await this.executeCommandLine(`cd ${path_module_root} && npm install`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
/** 拿到目錄下的資料夾列表 */
|
|
433
|
-
getNamesOfFolderChild(path) {
|
|
434
|
-
return this.getChildPathByPath(path).filter(p => p.isDirectory).map(p => p.dirName);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/** 從給的path 找到 one level 的 file/dir Path */
|
|
438
|
-
getChildPathByPath(_path) {
|
|
439
|
-
try {
|
|
440
|
-
const files = _fs.default.readdirSync(_path);
|
|
441
|
-
return files.map(file => this.getPathInfo(_path2.default.join(_path, file)));
|
|
442
|
-
} catch (error) {
|
|
443
|
-
throw new _index2.default(8002, error);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/** from :'./template/sample.babel.config.js
|
|
448
|
-
* destDir : '/template'
|
|
449
|
-
* fileName: 'fileName.extension' => 如果fileName 是 empty ,dest就是必須包含新檔名
|
|
450
|
-
* force 就是強制覆蓋他啦
|
|
451
|
-
* */
|
|
452
|
-
copySingleFile(from, dest, fileName, force = false) {
|
|
453
|
-
const destination = fileName && fileName.trim() ? _path2.default.join(dest, fileName) : dest;
|
|
454
|
-
if (_fs.default.existsSync(destination) && !force) {
|
|
455
|
-
throw new _index2.default(8006, destination);
|
|
456
|
-
}
|
|
457
|
-
_fs.default.copyFileSync(from, destination);
|
|
458
|
-
}
|
|
459
|
-
ensureFolderExists(folderPath) {
|
|
460
|
-
const resolved = _path2.default.resolve(folderPath);
|
|
461
|
-
if (_fs.default.existsSync(resolved)) return;
|
|
462
|
-
_fs.default.mkdirSync(resolved, {
|
|
463
|
-
recursive: true
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
getNodeEnvVariable(key, defaultValue = undefined) {
|
|
467
|
-
const value = process.env[key];
|
|
468
|
-
return value === undefined ? defaultValue : value;
|
|
469
|
-
}
|
|
470
|
-
isDirectory(path) {
|
|
471
|
-
if (!this.isPathExist(path)) return false;
|
|
472
|
-
return _fs.default.lstatSync(path).isDirectory();
|
|
473
|
-
}
|
|
474
|
-
isFile(path) {
|
|
475
|
-
if (!this.isPathExist(path)) return false;
|
|
476
|
-
return _fs.default.lstatSync(path).isFile();
|
|
477
|
-
}
|
|
478
|
-
isImageFile(file) {
|
|
479
|
-
return ["svg", "png", "jpg", "jpeg"].includes(file.extension);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/** 把內容都抹掉each 是 fileName, ex:index.js*/
|
|
483
|
-
async cleanChildFiles(path, predicate = () => true, ...exclude) {
|
|
484
|
-
if (!_fs.default.existsSync(path)) return false;
|
|
485
|
-
const files = this.findFilePathBy(path, predicate, ...exclude);
|
|
486
|
-
const nonImages = files.filter(file => !this.isImageFile(file));
|
|
487
|
-
await Promise.allSettled(nonImages.map(file => {
|
|
488
|
-
this.cleanFileContent(file.absolute);
|
|
489
|
-
this.appendInfo(`成功 cleanChildFiles() -> '${file.path}'`);
|
|
490
|
-
return Promise.resolve();
|
|
491
|
-
}));
|
|
492
|
-
return files;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/** 將檔案清除乾淨, 但不刪掉檔案, 這樣hot reload的監聽才不會遺失*/
|
|
496
|
-
cleanFileContent(path) {
|
|
497
|
-
this.syncDeleteFile(path);
|
|
498
|
-
/** 太浪費時間了
|
|
499
|
-
* fs.truncateSync(path, 0);
|
|
500
|
-
this.appendInfo(`${path} 內容被清除!`);
|
|
501
|
-
* */
|
|
502
|
-
}
|
|
503
|
-
async syncWithExistPackage(path = "../") {
|
|
504
|
-
/** 產生shell_script_腳本 */
|
|
505
|
-
const paths = this.findFilePathBy(path, each => _lodash.default.isEqual(each.fileNameExtension, "package.json"), "node_modules");
|
|
506
|
-
for (let path of paths) {
|
|
507
|
-
try {
|
|
508
|
-
if (!_lodash.default.isEqual(path.dirName, "..")) this.insertShellCommand(_configerer.configerer.BASE_SHELL_SCRIPT, `cd_${path.dirName}`, `cd ${this.getFolderPathOfSpecificPath(path.absolute)}`);
|
|
509
|
-
} catch (error) {
|
|
510
|
-
this.appendInfo(error.message);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
async packageTemplatify(path, packageName) {
|
|
515
|
-
const existFolders = this.getChildPathByPath(path).map(each => each.absolute.split("\/").pop());
|
|
516
|
-
if (this.has(existFolders, packageName)) {
|
|
517
|
-
throw new _index2.default(8004, ` packageName ===> '${packageName}'`);
|
|
518
|
-
}
|
|
519
|
-
const dirPath = `${path}/${packageName}`;
|
|
520
|
-
|
|
521
|
-
/** 1.產生package folder */
|
|
522
|
-
_fs.default.mkdirSync(dirPath);
|
|
523
|
-
|
|
524
|
-
/** 2.要有babel.config.js? */
|
|
525
|
-
_fs.default.copyFileSync("./template/sample.babel.config.js", `${dirPath}/babel.config.js`);
|
|
526
|
-
_fs.default.copyFileSync("./template/sample.terser.config.js", `${dirPath}/terser.config.js`);
|
|
527
|
-
|
|
528
|
-
/** 3.要有package,json */
|
|
529
|
-
const packagejson = this.getJsonObjByFilePath("./template/sample.package.json");
|
|
530
|
-
packagejson["name"] = packageName;
|
|
531
|
-
this.writeFileInJSON(`${dirPath}/package.json`, packagejson);
|
|
532
|
-
|
|
533
|
-
/** 4.要在 src/${index.js}, dir/index.js */
|
|
534
|
-
this.persistByPath(`${dirPath}/src`);
|
|
535
|
-
const classBase = String.format(this.getFileContextInRaw(`./template/sample.src.index.js`), packageName, "明悅", new Date());
|
|
536
|
-
_fs.default.writeFileSync(`${dirPath}/src/index.js`, classBase);
|
|
537
|
-
|
|
538
|
-
/** 6.要產生webstorm run case? */
|
|
539
|
-
const ideaWorkspacePath = `${this.findSpecificFolderByPath(dirPath, ".idea")}/workspace.xml`;
|
|
540
|
-
|
|
541
|
-
/** 7.要產生cd script 腳本 **/
|
|
542
|
-
this.insertShellCommand(_configerer.configerer.BASE_SHELL_SCRIPT, `cd_${packageName}`, `cd ${_path2.default.resolve(dirPath)}`);
|
|
543
|
-
if (_fs.default.existsSync(ideaWorkspacePath)) {
|
|
544
|
-
const workspace = this.getFileContextInRaw(ideaWorkspacePath);
|
|
545
|
-
const splited = workspace.split("\n");
|
|
546
|
-
const indexOfRunManager = _lodash.default.findIndex(splited, line => this.has(line, 'name="RunManager'));
|
|
547
|
-
this.insertToArray(splited, indexOfRunManager, `<configuration name="${packageName}"
|
|
548
|
-
type="NodeJSConfigurationType"
|
|
549
|
-
path-to-node="$USER_HOME$/.nvm/versions/node/v18.19.1/bin/node"
|
|
550
|
-
node-parameters="--require @babel/register"
|
|
551
|
-
path-to-js-file="${_path2.default.resolve(dirPath)}/src/index.js"
|
|
552
|
-
working-dir="${_path2.default.resolve(dirPath)}" >`, ` <envs>`, ` <env name="self_debug" value="true" />`, ` <env name="is_node" value="true" />`, ` </envs>`, ` <method v="2" />`, `</configuration>`);
|
|
553
|
-
const indexOfList = _lodash.default.findIndex(splited, line => _lodash.default.isEqual(_lodash.default.trim(line), `<list>`), indexOfRunManager);
|
|
554
|
-
this.insertToArray(splited, indexOfList, ` <item itemvalue="Node.js.${packageName}" />`);
|
|
555
|
-
_fs.default.writeFileSync(ideaWorkspacePath, splited.join("\n"));
|
|
556
|
-
} else {
|
|
557
|
-
this.appendError(`${ideaWorkspacePath} not exist`);
|
|
558
|
-
}
|
|
559
|
-
await this.executeCommandLine(`cd ${_path2.default.resolve(dirPath)} && npm install`);
|
|
560
|
-
this.appendInfo(`build ${packageName} succeed!`);
|
|
561
|
-
}
|
|
562
|
-
appendInfo(...messages) {
|
|
563
|
-
return this.appendLog(_configerer.configerer.PATH_INFO_LOG, messages, false);
|
|
564
|
-
}
|
|
565
|
-
appendError(...messages) {
|
|
566
|
-
return this.appendLog(_configerer.configerer.PATH_ERROR_LOG, messages, true);
|
|
567
|
-
}
|
|
568
|
-
appendLog(path, messages, isError = false) {
|
|
569
|
-
const msg = `${this.getCurrentTimeFormat()} ${isError ? "ERROR" : "LOG"} : ${messages.map(this.stringifyLog).join(" ,")}`;
|
|
570
|
-
if (!this.isProductionEnvironment()) {
|
|
571
|
-
isError ? console.error(...messages) : console.log(...messages);
|
|
572
|
-
}
|
|
573
|
-
if (this.isPersistIntoLogFile) {
|
|
574
|
-
this.appendFile(path, msg);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
stringifyLog(data) {
|
|
578
|
-
return typeof data === "object" ? JSON.stringify(data) : String(data);
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/** 如果file不存在,就會產生file,force_delete 可以強制刪除cache file*/
|
|
582
|
-
appendFile(filePath, data, newline = true, forceDelete = false) {
|
|
583
|
-
try {
|
|
584
|
-
const resolvedPath = _path2.default.resolve(filePath); // <<--- 正規化路徑
|
|
585
|
-
if (forceDelete && _fs.default.existsSync(resolvedPath)) _fs.default.unlinkSync(resolvedPath);
|
|
586
|
-
if (!_fs.default.existsSync(resolvedPath)) this.persistByPath(resolvedPath);
|
|
587
|
-
const content = `${newline ? "\n" : ""}${data}`;
|
|
588
|
-
_fs.default.appendFileSync(resolvedPath, content);
|
|
589
|
-
} catch (err) {
|
|
590
|
-
throw new _index2.default(8001, err);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
disableLogMessagePersistent() {
|
|
594
|
-
this.isPersistIntoLogFile = false;
|
|
595
|
-
}
|
|
596
|
-
getLogString(datas) {
|
|
597
|
-
return datas.map(data => this.isJson(data) || _lodash.default.isObject(data) || _lodash.default.isArray(data) ? this.deepFlat(data) : data).join(" ,");
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/** 常常要把JSON的內容印出來,所以這個很方便 */
|
|
601
|
-
async persistJsonFilePrettier(path, object, ignoreP = false) {
|
|
602
|
-
path = _path2.default.resolve(path);
|
|
603
|
-
this.appendFile(path, JSON.stringify(object), true, true);
|
|
604
|
-
if (!ignoreP) await this.prettier(path, 120);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/** 快速把資料結構印出來看 */
|
|
608
|
-
printCollectionToFile(collection) {
|
|
609
|
-
const fileName = `./logs/__temp_${this.getCurrentTimeFormat()}.txt`;
|
|
610
|
-
this.persistByPath(`./logs/`);
|
|
611
|
-
this.appendFile(fileName, this.deepFlat(collection, ` \n\n, `));
|
|
612
|
-
this.appendInfo(`collectionToFile succeed, file name ==> ${fileName}`);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
/** 重複讀取file IO時,要用這個方式,不然IO太吃資源了
|
|
616
|
-
* 重要!!!!!cache可能會導致node.js的 stack memory爆掉
|
|
617
|
-
* */
|
|
618
|
-
async readFileContentByPath(path, cache = {}) {
|
|
619
|
-
return cache[path] ?? (cache[path] = await _promises.default.readFile(path, "utf-8"));
|
|
620
|
-
}
|
|
621
|
-
singleFileTemplatify(path = "./") {
|
|
622
|
-
const all = this.findFilePathByExtension(path, ["js"], "node_modules");
|
|
623
|
-
for (const file of all) {
|
|
624
|
-
const content = this.getFileContextInRaw(file.absolute).trim();
|
|
625
|
-
if (_lodash.default.isEmpty(content)) {
|
|
626
|
-
this.appendInfo(file.fileName, file.absolute);
|
|
627
|
-
const className = _lodash.default.isEqual(file.fileName, "index") ? file.dirName : file.fileName;
|
|
628
|
-
_fs.default.writeFileSync(file.absolute, String.format(this.getFileContextInRaw(`.
|
|
629
|
-
/template/s
|
|
630
|
-
ample.src.index.js`), className, "明悅", new Date()));
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
isFileEmpty(path) {
|
|
635
|
-
const content = this.getFileContextInRaw(path);
|
|
636
|
-
return !content || !content.trim();
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/** 保守的複製檔案, 如果檔案比較舊, 或是檔案是空的, 就放棄copy行為 */
|
|
640
|
-
copySingleFileConservative(destPath, latestFile) {
|
|
641
|
-
const {
|
|
642
|
-
absolute,
|
|
643
|
-
lastModifiedTime
|
|
644
|
-
} = latestFile;
|
|
645
|
-
|
|
646
|
-
// 先確認檔案是否為空
|
|
647
|
-
if (!this.isPathExist(absolute) || this.isFileEmpty(absolute)) {
|
|
648
|
-
this.appendInfo(`${absolute} is empty file, ignore copy behavior`);
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
const destExists = _fs.default.existsSync(destPath);
|
|
652
|
-
|
|
653
|
-
// 如果目的檔案存在且比來源更新,則跳過
|
|
654
|
-
if (destExists && this.getFileLastModifiedTime(destPath) > lastModifiedTime) {
|
|
655
|
-
this.appendInfo(`${destPath} is the latest, ignore this run`);
|
|
656
|
-
return;
|
|
657
|
-
}
|
|
658
|
-
if (!destExists) {
|
|
659
|
-
this.appendInfo(`${destPath} does not exist, safe to copy.`);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
// 確保目的路徑存在(遞迴建立)
|
|
663
|
-
this.ensureFolderExists(_path2.default.dirname(destPath));
|
|
664
|
-
|
|
665
|
-
// 強制複製
|
|
666
|
-
this.copySingleFile(absolute, destPath, undefined, true);
|
|
667
|
-
}
|
|
668
|
-
syncDeleteFile(path) {
|
|
669
|
-
if (_fs.default.existsSync(path)) _fs.default.unlinkSync(path);
|
|
670
|
-
}
|
|
671
|
-
getFileContextInJSON(path) {
|
|
672
|
-
try {
|
|
673
|
-
if (_fs.default.existsSync(path)) {
|
|
674
|
-
return JSON.parse(_fs.default.readFileSync(path, "utf-8"));
|
|
675
|
-
}
|
|
676
|
-
} catch (error) {
|
|
677
|
-
throw new _index2.default(9999, error.message);
|
|
678
|
-
}
|
|
679
|
-
return {};
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/** 讀取path,然後用utf-8的方式 */
|
|
683
|
-
getFileContextInRaw(path) {
|
|
684
|
-
if (!_fs.default.existsSync(path)) return "";
|
|
685
|
-
return _fs.default.readFileSync(path, "utf-8");
|
|
686
|
-
}
|
|
687
|
-
writeFileInJSON(path, param) {
|
|
688
|
-
let data = JSON.stringify(param, null, 2);
|
|
689
|
-
_fs.default.writeFileSync(path, data);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/** 用來pack lib_project, 不然其他import lib_project的專案會無法讀懂es6
|
|
693
|
-
* release folder 會被自動ignore到
|
|
694
|
-
* exclude 裡面可以放專案名稱, 例如 free_marker,question_update */
|
|
695
|
-
async generatePackage(path = "./", deployToNPMServer = false, forceInstallNodeModule = true, ...exclude) {
|
|
696
|
-
let packagejsons = this.findFilePathByExtension(path, ["json"], "node_modules", "release");
|
|
697
|
-
packagejsons = _lodash.default.filter(packagejsons, each => _lodash.default.isEqual(each.fileName, "package"));
|
|
698
|
-
packagejsons = packagejsons.map(each => this.getFolderPathOfSpecificPath(each.absolute));
|
|
699
|
-
for (const path of packagejsons) {
|
|
700
|
-
if (this.isAndEquals(...exclude.map(projectName => () => !this.has(path, projectName)))) {
|
|
701
|
-
/** 產生去掉if(debug) { command } 字樣的 */
|
|
702
|
-
const tempFolderPath = await this.generateTempFolderWithCleanSrc(path);
|
|
703
|
-
|
|
704
|
-
/** 產生release資料夾 */
|
|
705
|
-
await this.deleteSelfByPath(_path2.default.join(path, "release"), true);
|
|
706
|
-
const release = this.persistByPath(_path2.default.join(path, "release"));
|
|
707
|
-
/** 利用babel 產生出 es5相容性高的src file */
|
|
708
|
-
await this.executeCommandLine(`cd ${path} && babel ./temp --out-dir ./release/lib --config-file ./babel.config.js`);
|
|
709
|
-
// Step 2: Terser
|
|
710
|
-
const terserConfig = require(_path2.default.join(path, "./terser.config.js"));
|
|
711
|
-
const terserArgs = this.getStringOfTerserCommandLine(terserConfig);
|
|
712
|
-
|
|
713
|
-
// ✅ find 會自動遞迴查找所有子目錄下的 .js 文件
|
|
714
|
-
// . 表示當前目錄及其所有子目錄
|
|
715
|
-
await this.executeCommandLine(`cd ${path}/release/lib && find . -type f -name "*.js" -exec terser {} -o {} ${terserArgs} \\;`);
|
|
716
|
-
const pathOfPackageJson = _path2.default.join(path, "package.json");
|
|
717
|
-
try {
|
|
718
|
-
const indexFileName = "sample.npm.module.index.js";
|
|
719
|
-
/** 複製公版的index.js */
|
|
720
|
-
this.copySingleFile(`/Users/davidtu/cross-achieve/high/idea-inventer/utiller/template/${indexFileName}`, release, "index.js", true);
|
|
721
|
-
|
|
722
|
-
/** 將公版的index.js也terser一波 */
|
|
723
|
-
const filePath = `${path}/release/index.js`;
|
|
724
|
-
await this.executeCommandLine(`terser ${filePath} -o ${filePath} ${terserArgs}`);
|
|
725
|
-
|
|
726
|
-
/** template就是樣板的概念 */
|
|
727
|
-
const templatePath = _path2.default.join(path, "template");
|
|
728
|
-
if (this.isPathExist(templatePath)) {
|
|
729
|
-
await this.copyFromFolderToDestFolder(templatePath, this.persistByPath(_path2.default.join(release, "template")));
|
|
730
|
-
}
|
|
731
|
-
if (deployToNPMServer) {
|
|
732
|
-
/** 升級package.json的版號 */
|
|
733
|
-
const {
|
|
734
|
-
moduleName,
|
|
735
|
-
version
|
|
736
|
-
} = await this.upgradePackageJsonVersion(pathOfPackageJson);
|
|
737
|
-
|
|
738
|
-
/** 把所有樣板的版號都提升 */
|
|
739
|
-
await this.updateVersionOfTemplate(moduleName, version);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/** 把package.json release放進去 */
|
|
743
|
-
this.copySingleFile(pathOfPackageJson, _path2.default.join(release, "package.json"), undefined, true);
|
|
744
|
-
|
|
745
|
-
/** 安裝一個沒有devDependency 的node_module */
|
|
746
|
-
if (forceInstallNodeModule || !this.isPathExist(_path2.default.join(release, "node_module"))) {
|
|
747
|
-
await this.executeCommandLine(`cd ${release} && yarn install --production`);
|
|
748
|
-
} else {
|
|
749
|
-
this.appendInfo(`ignore node-module install behavior`);
|
|
750
|
-
}
|
|
751
|
-
this.appendInfo(`build ${path} succeed`);
|
|
752
|
-
|
|
753
|
-
/** 部署到 local server*/
|
|
754
|
-
if (deployToNPMServer) {
|
|
755
|
-
/** 升級package.json的版號 */
|
|
756
|
-
await this.executeCommandLine(`cd ${release} && npm publish`);
|
|
757
|
-
/** await this.executeCommandLine(`cd ${release} && npm publish --registry http://localhost:4873`) */
|
|
758
|
-
}
|
|
759
|
-
} catch (error) {
|
|
760
|
-
await this.deleteSelfByPath(release, true);
|
|
761
|
-
throw new _index2.default(9999, `generatePackage 報錯, ${error.message}`);
|
|
762
|
-
} finally {
|
|
763
|
-
await this.deleteSelfByPath(tempFolderPath, true);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
/** 用來更新樣板裡面的模組版本 */
|
|
769
|
-
async updateVersionOfTemplate(dependency, newVersion) {
|
|
770
|
-
const paths = ["/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/template/admin.package.json.mustache", "/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/template/web.package.json.mustache", "/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/template/functions.package.json.mustache", "/Users/davidtu/cross-achieve/high/idea-inventer/utiller/template/sample.package.json", "/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/package.json"];
|
|
771
|
-
for (const path of paths) {
|
|
772
|
-
if (this.isPathExist(path)) {
|
|
773
|
-
let succeedOfPersistFile = false;
|
|
774
|
-
const json = this.getJsonObjByFilePath(path);
|
|
775
|
-
if (json && json.dependencies && json.dependencies[dependency]) {
|
|
776
|
-
json.dependencies[dependency] = `^${newVersion}`;
|
|
777
|
-
try {
|
|
778
|
-
await this.writeJsonThanPrettier(path, json);
|
|
779
|
-
succeedOfPersistFile = true;
|
|
780
|
-
} catch (error) {
|
|
781
|
-
succeedOfPersistFile = true;
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
if (!succeedOfPersistFile) {
|
|
785
|
-
await this.updateFileOfSpecificLine(path, line => ` "${dependency}":"^${newVersion}"${_lodash.default.endsWith(_lodash.default.trim(line), ",") ? "," : ""}`, each => _lodash.default.startsWith(_lodash.default.trim(each), `"${dependency}"`));
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
await this.copyFromFolderToDestFolder("/Users/davidtu/cross-achieve/high/idea-inventer/utiller/template/", "/Users/davidtu/cross-achieve/high/idea-inventer/newp/template/", true, true);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
/** 把一份文件split(\n),然後透過predicate找出特定的line,再replace成contentOfUpdated
|
|
793
|
-
*
|
|
794
|
-
* 例如一份package.json
|
|
795
|
-
* utiller:1.0.1 => utiller:1.0.1
|
|
796
|
-
*
|
|
797
|
-
* */
|
|
798
|
-
async updateFileOfSpecificLine(pathOfFile, contentOfUpdated = line => "updated", predicate = line => true) {
|
|
799
|
-
const context = this.getFileContextInRaw(pathOfFile);
|
|
800
|
-
const lines = context.split("\n");
|
|
801
|
-
const index = lines.findIndex(predicate);
|
|
802
|
-
if (index === -1) return; // 無匹配行則略過
|
|
803
|
-
lines[index] = contentOfUpdated(lines[index]);
|
|
804
|
-
this.appendFile(pathOfFile, lines.join("\n"), true, true);
|
|
805
|
-
await this.prettier(pathOfFile);
|
|
806
|
-
}
|
|
807
|
-
async writeJsonThanPrettier(path, json) {
|
|
808
|
-
this.writeFileInJSON(path, json);
|
|
809
|
-
await this.prettier(path);
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
/** 用來豐富package.json的功能 */
|
|
813
|
-
async enrichEachPackageJson(rootPath) {
|
|
814
|
-
const validNames = new Set(["package", "admin.package", "web.package", "functions.package"]);
|
|
815
|
-
const jsonFiles = this.findFilePathByExtension(rootPath, ["json"], "gen", "node_modules", "release");
|
|
816
|
-
const packageFiles = jsonFiles.filter(file => validNames.has(file.fileName));
|
|
817
|
-
if (packageFiles.length === 0) return;
|
|
818
|
-
for (const {
|
|
819
|
-
absolute
|
|
820
|
-
} of packageFiles) {
|
|
821
|
-
const json = this.getJsonObjByFilePath(absolute);
|
|
822
|
-
json.scripts ||= {};
|
|
823
|
-
json.scripts.updateConfigerer = "npm update configerer --save";
|
|
824
|
-
await this.writeJsonThanPrettier(absolute, json);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
insertShellCommand(shellPath = _configerer.configerer.BASE_SHELL_SCRIPT, alias, command) {
|
|
828
|
-
if (this.isStringContainInLines(this.getFileContextInRaw(shellPath), alias)) {
|
|
829
|
-
throw new _index2.default(8007, `alias ${alias} is exist`);
|
|
830
|
-
} else {
|
|
831
|
-
const line = `alias ${alias}='${command}'`;
|
|
832
|
-
this.appendFile(shellPath, line);
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
getAdminCredential() {
|
|
836
|
-
return this.getJsonObjByFilePath("/Users/davidtu/cross-achieve/mimi/idea-inventer/firebaser/key/mimi19up-firebase-adminsdk.json");
|
|
837
|
-
}
|
|
838
|
-
isEmptyFile(path) {
|
|
839
|
-
return !this.isPathExist(path) || _lodash.default.isEmpty(this.getFileContextInRaw(path).trim());
|
|
840
|
-
}
|
|
841
|
-
isEmptyFolder(path) {
|
|
842
|
-
return _fs.default.readdirSync(path).length === 0;
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
/** 把檔案弄得好看一點
|
|
846
|
-
* width 是指一行能塞下多少的字元
|
|
847
|
-
* preitter真的很花時間,所以做個enable
|
|
848
|
-
* */
|
|
849
|
-
async prettier(path, width = 200) {
|
|
850
|
-
await this.executeCommandLine(`cd ${_path2.default.resolve(".")} && npx prettier --write ${_path2.default.resolve(path)} --print-width ${width}`);
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* 檔案最後編輯時間!
|
|
855
|
-
console.log('older ==> ',utiller.getFileLastModifiedTime('./folderOfTestUsage/history_older.js'));
|
|
856
|
-
console.log('latest ==> ',utiller.getFileLastModifiedTime('./folderOfTestUsage/history_latest.js'));
|
|
857
|
-
console.log('compare latestTime > olderTime ?? ==> ',utiller.getFileLastModifiedTime('./folderOfTestUsage/history_latest.js') > utiller.getFileLastModifiedTime('./folderOfTestUsage/history_older.js'));
|
|
858
|
-
*/
|
|
859
|
-
getFileLastModifiedTime(path) {
|
|
860
|
-
/**
|
|
861
|
-
* console.log(`File Data Last Modified: ${stats.mtime}`);
|
|
862
|
-
* console.log(`File Status Last Modified: ${stats.ctime}`);
|
|
863
|
-
*/
|
|
864
|
-
return _fs.default.statSync(path).mtimeMs;
|
|
865
|
-
}
|
|
866
|
-
getJsonObjByFilePath(path) {
|
|
867
|
-
this.appendInfo(`ready to json path:${path}`);
|
|
868
|
-
return JSON.parse(this.getFileContextInRaw(path));
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
/** increment version number, 回傳latest version, name */
|
|
872
|
-
async upgradePackageJsonVersion(path) {
|
|
873
|
-
if (_lodash.default.isEqual("json", this.getPathInfo(path).extension)) {
|
|
874
|
-
const json = this.getJsonObjByFilePath(path);
|
|
875
|
-
json.version = this.getStringOfVersionIncrement(json.version);
|
|
876
|
-
await this.writeJsonThanPrettier(path, json);
|
|
877
|
-
return {
|
|
878
|
-
version: json.version,
|
|
879
|
-
moduleName: json.name
|
|
880
|
-
};
|
|
881
|
-
} else {
|
|
882
|
-
throw new _index2.default(8020, `path is not package.json, which is ${path}`);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
/** rewrite file of *.json with attributes => {version:'1.0.1'}, {name:'david'}
|
|
887
|
-
*
|
|
888
|
-
* console.log(await utiller.reWriteJsonAttribute(`./test.package.json`,{name:'ugly'},{version:'2.6.101'}));
|
|
889
|
-
* */
|
|
890
|
-
async reWriteJsonAttribute(filePath, ...attrs) {
|
|
891
|
-
const {
|
|
892
|
-
extension
|
|
893
|
-
} = this.getPathInfo(filePath);
|
|
894
|
-
if (extension !== "json") {
|
|
895
|
-
throw new _index2.default(9999, `reWriteJsonAttribute() => path is not package.json, which is ${filePath}`);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// 驗證所有 attr 必須是 object
|
|
899
|
-
const invalidAttr = attrs.find(attr => typeof attr !== "object" || attr === null);
|
|
900
|
-
if (invalidAttr) {
|
|
901
|
-
throw new _index2.default(9999, `84451515 attr is not object, which is 'type=${typeof invalidAttr} => ${invalidAttr}'`);
|
|
902
|
-
}
|
|
903
|
-
const json = this.getJsonObjByFilePath(filePath);
|
|
904
|
-
for (const attr of attrs) {
|
|
905
|
-
json[this.getObjectKey(attr)] = this.getObjectValue(attr);
|
|
906
|
-
}
|
|
907
|
-
await this.writeJsonThanPrettier(filePath, json);
|
|
908
|
-
return {
|
|
909
|
-
version: json.version,
|
|
910
|
-
moduleName: json.name
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
|
-
getVersionOfPackageJson(path) {
|
|
914
|
-
return this.getAttributeValueOfJson(path, "version", "1.0.0");
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
/** 取得*.json 裡面的file*/
|
|
918
|
-
getAttributeValueOfJson(path, key, defaultValue = undefined) {
|
|
919
|
-
if (_lodash.default.isEqual("json", this.getPathInfo(path).extension)) {
|
|
920
|
-
const json = this.getJsonObjByFilePath(path);
|
|
921
|
-
return json[key] ?? defaultValue;
|
|
922
|
-
} else {
|
|
923
|
-
throw new _index2.default(8020, `path is not package.json, which is ${path}`);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
/** 找到 js file 裡面宣告version的value ==> version:'1.0.60'} */
|
|
928
|
-
getVersionOfJsFile(path) {
|
|
929
|
-
return this.getAttributeValueOfJsFile(path, "version", "project without version notice");
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
/** 找到 js file 裡面宣告attribute的 value ==> 例:version:'1.0.60'} */
|
|
933
|
-
getAttributeValueOfJsFile(path, key, defaultValue = undefined) {
|
|
934
|
-
if (_lodash.default.isEqual(this.getExtensionFromPath(path), "js")) {
|
|
935
|
-
const source = require(_path2.default.resolve(path)).default;
|
|
936
|
-
return source[key] ?? defaultValue;
|
|
937
|
-
} else {
|
|
938
|
-
throw new _index2.default(8020, `path is not js file, which is ${path}`);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
/** 更新js file裏面attribute
|
|
943
|
-
* attr => {verison:1.0.32}
|
|
944
|
-
* console.log(await utiller.rewriteAttributeOfSourceJs(`./test.source.js`, {name: 'ugly-tu'}, {version: '3.9.123'}));
|
|
945
|
-
* */
|
|
946
|
-
async rewriteAttributeOfSourceJs(path, ...attrs) {
|
|
947
|
-
if (!this.isPathExist(path)) {
|
|
948
|
-
throw new _index2.default(9999, `4849813 ${path} is not exist`);
|
|
949
|
-
}
|
|
950
|
-
for (const attr of attrs) {
|
|
951
|
-
if (!_lodash.default.isObject(attr)) {
|
|
952
|
-
throw new _index2.default(9999, `4984651 attr is not object, which is 'type=${typeof attr} => ${attr}'`);
|
|
953
|
-
}
|
|
954
|
-
const key = this.getObjectKey(attr);
|
|
955
|
-
const value = this.getObjectValue(attr);
|
|
956
|
-
const contents = this.getFileContextInRaw(path).split(`\n`);
|
|
957
|
-
const index = _lodash.default.findIndex(contents, each => _lodash.default.startsWith(_lodash.default.trim(each), `${key}`));
|
|
958
|
-
/** 故意空4格 */
|
|
959
|
-
contents[index] = ` ${key}: '${value}',`;
|
|
960
|
-
this.appendFile(path, contents.join(`\n`), true, true);
|
|
961
|
-
}
|
|
962
|
-
return attrs;
|
|
963
|
-
}
|
|
964
|
-
async getAnswerFromPromptQ(configs = [{
|
|
965
|
-
name: "name",
|
|
966
|
-
require: true,
|
|
967
|
-
description: "type the name"
|
|
968
|
-
}]) {
|
|
969
|
-
_prompt.default.start();
|
|
970
|
-
return await _prompt.default.get(configs);
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
/**
|
|
974
|
-
* [{
|
|
975
|
-
* name: 'name',
|
|
976
|
-
* require: true,
|
|
977
|
-
* description: 'type the name',
|
|
978
|
-
* },{
|
|
979
|
-
* name: 'age',
|
|
980
|
-
* require: true,
|
|
981
|
-
* description: 'type the age',
|
|
982
|
-
* }]
|
|
983
|
-
*
|
|
984
|
-
* result:{ name: 'david', age: '18' }
|
|
985
|
-
* */
|
|
986
|
-
async getObjectFromPromptQ(...configs) {
|
|
987
|
-
_prompt.default.start();
|
|
988
|
-
return await _prompt.default.get(configs);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/** 產出一個/temp,然後把/src 複製過去, 再把裡面每一個file的 if(DEBUG)給去除掉,再加上prettier */
|
|
992
|
-
/** 找出if (configerer) 當作start */
|
|
993
|
-
/** 找出 } 當作 end */
|
|
994
|
-
/** 刪除掉 if(configerer.DEBUG) {...........} */
|
|
995
|
-
|
|
996
|
-
async generateTempFolderWithCleanSrc(basePath) {
|
|
997
|
-
this.appendInfo("generateTempFolderWithCleanSrc", basePath);
|
|
998
|
-
const sourceFile = _path2.default.join(basePath, "src");
|
|
999
|
-
const tempFolderPath = _path2.default.join(basePath, "temp");
|
|
1000
|
-
if (!_fs.default.existsSync(sourceFile)) return tempFolderPath;
|
|
1001
|
-
this.appendInfo("generateTempFolderWithCleanSrc", "source", sourceFile);
|
|
1002
|
-
this.persistByPath(tempFolderPath);
|
|
1003
|
-
await this.copyFromFolderToDestFolder(sourceFile, tempFolderPath);
|
|
1004
|
-
const filePaths = this.findFilePathBy(tempFolderPath);
|
|
1005
|
-
for (const {
|
|
1006
|
-
absolute: tempFilePath
|
|
1007
|
-
} of filePaths) {
|
|
1008
|
-
const rawLines = this.getFileContextInRaw(tempFilePath).split("\n");
|
|
1009
|
-
const trimmedLines = rawLines.map(line => line.trim());
|
|
1010
|
-
const start = trimmedLines.findIndex(line => line.startsWith("if (configerer.DEBUG_MODE)"));
|
|
1011
|
-
const end = trimmedLines.lastIndexOf("}");
|
|
1012
|
-
if (start >= 0 && end > start) {
|
|
1013
|
-
// 移除 DEBUG 區塊
|
|
1014
|
-
rawLines.splice(start, end - start + 1);
|
|
1015
|
-
const updatedContent = rawLines.join("\n");
|
|
1016
|
-
this.appendFile(tempFilePath, updatedContent, true, true);
|
|
1017
|
-
|
|
1018
|
-
// 美化代碼
|
|
1019
|
-
await this.executeCommandLine(`cd ${_path2.default.dirname(tempFilePath)} && npx prettier --write "${tempFilePath}"`);
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
return tempFolderPath;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
/**
|
|
1026
|
-
* from, destination
|
|
1027
|
-
*
|
|
1028
|
-
* 讓file content清除後,在寫入資料, 避免to(destination) file address改變*/
|
|
1029
|
-
rewriteFile2File(from, to) {
|
|
1030
|
-
const content = this.getFileContextInRaw(from);
|
|
1031
|
-
if (!content.trim()) throw new _index2.default(9999, `${from} 為空,避免覆蓋`);
|
|
1032
|
-
this.appendFile(to, content, true, true);
|
|
1033
|
-
this.appendInfo(`rewrite from:${from} => to:${to} 成功`);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
/** 取得file第一行statement */
|
|
1037
|
-
getStringOfHeadOfFile(path) {
|
|
1038
|
-
if (this.isPathExist(path)) {
|
|
1039
|
-
const context = this.getFileContextInRaw(path);
|
|
1040
|
-
return _lodash.default.head(context.split("\n"));
|
|
1041
|
-
}
|
|
1042
|
-
return "";
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
/** 因為code gen有很多要js file要執行 persistent, 沒有修改過{const edit = true}的index persist就不要理它 */
|
|
1046
|
-
isFileEditSucceed(filePath) {
|
|
1047
|
-
if (!this.isPathExist(filePath)) return false;
|
|
1048
|
-
const file = this.getPathInfo(filePath);
|
|
1049
|
-
const context = this.getFileContextInRaw(filePath).trim();
|
|
1050
|
-
if (context === "") {
|
|
1051
|
-
this.appendInfo(`74985465 path ${file.path} is empty file, file would not persist`);
|
|
1052
|
-
return false;
|
|
1053
|
-
}
|
|
1054
|
-
try {
|
|
1055
|
-
const firstLine = context.split("\n")[0];
|
|
1056
|
-
|
|
1057
|
-
/**
|
|
1058
|
-
* const\s+:匹配 const 與其後至少一個空白。
|
|
1059
|
-
* ([a-zA-Z_]\w*):匹配合法變數名稱(第一個字為英文字母或底線,其後可為英文字母、數字、底線)。
|
|
1060
|
-
* \s*=\s*:匹配等號兩邊的空白。
|
|
1061
|
-
* (true|false):匹配布林值。
|
|
1062
|
-
* \s*;?:匹配可有可無的分號及其前空白。
|
|
1063
|
-
*/
|
|
1064
|
-
const match = firstLine.match(/const\s+([a-zA-Z_]\w*)\s*=\s*(true|false)\s*;?/);
|
|
1065
|
-
if (!match || _lodash.default.size(match) < 3) return false; /** ['const bear = true','bear','true',index: 0,input: 'const bear = true', groups:undefined] */
|
|
1066
|
-
const editValue = match[2] === "true";
|
|
1067
|
-
if (editValue === true) {
|
|
1068
|
-
return true;
|
|
1069
|
-
}
|
|
1070
|
-
} catch (error) {
|
|
1071
|
-
this.appendError(`66445411 ${error.message}`);
|
|
1072
|
-
return false;
|
|
1073
|
-
}
|
|
1074
|
-
return false;
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* console.log(joinRespectingDot('./temp', 'scs', 'qqq', 'as.js'));
|
|
1078
|
-
* // ./temp/scs/qqq/as.js
|
|
1079
|
-
*
|
|
1080
|
-
* console.log(joinRespectingDot('temp', 'scs', 'qqq', 'as.js'));
|
|
1081
|
-
* // temp/scs/qqq/as.js
|
|
1082
|
-
*
|
|
1083
|
-
* console.log(joinRespectingDot('/usr', 'local', 'bin'));
|
|
1084
|
-
* // /usr/local/bin
|
|
1085
|
-
*
|
|
1086
|
-
* 讓./組合的path 不要因為join被拿掉 產生在本機根目錄 /src 的權限問題
|
|
1087
|
-
*/
|
|
1088
|
-
joinRespectingDot(...args) {
|
|
1089
|
-
const shouldHaveDotSlash = args[0]?.startsWith("./");
|
|
1090
|
-
const cleanArgs = args[0]?.startsWith("./") ? [args[0].slice(2), ...args.slice(1)] : args;
|
|
1091
|
-
const joined = _path2.default.join(...cleanArgs);
|
|
1092
|
-
return shouldHaveDotSlash && !_path2.default.isAbsolute(joined) ? `./${joined}` : joined;
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
var _default = exports.default = NodeUtiller;
|
|
1
|
+
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty")),_path2=_interopRequireDefault(require("path")),_fs=_interopRequireDefault(require("fs")),_promises=_interopRequireDefault(require("fs/promises")),_lodash=_interopRequireDefault(require("lodash")),_child_process=_interopRequireDefault(require("child_process")),_configerer=require("configerer"),_index=_interopRequireDefault(require("./index")),_index2=_interopRequireDefault(require("../exceptioner/index")),_pdfParse=_interopRequireDefault(require("pdf-parse")),_del=_interopRequireDefault(require("del")),_fsExtra=_interopRequireDefault(require("fs-extra")),_prompt=_interopRequireDefault(require("prompt"));class NodeUtiller extends _index.default{constructor(...e){super(...e),(0,_defineProperty2.default)(this,"isPersistIntoLogFile",!0),(0,_defineProperty2.default)(this,"findFilePathByExtension",(e,t=[],...i)=>{const s=new RegExp(`^[^.].+.(${_lodash.default.join(t,"|")})$`);return this.findFilePathBy(e,e=>s.test(e.fileNameExtension),...i)}),(0,_defineProperty2.default)(this,"executeCommandLine",async e=>{const t=this;return this.appendInfo(`執行腳本 ${e}`),new Promise(function(i,s){_child_process.default.exec(e,(e,a,n)=>{if(t.appendInfo(`${a}`),t.appendInfo(`${n}`),e)return t.appendError(`執行錯誤: ${e}`),void s(e);i(a.trim())})})}),(0,_defineProperty2.default)(this,"getStringOfTerserCommandLine",e=>{const t=[];if(e.compress){const i=[];e.compress.drop_console&&i.push("drop_console=true"),e.compress.drop_debugger&&i.push("drop_debugger=true"),e.compress.passes&&i.push(`passes=${e.compress.passes}`),e.compress.dead_code&&i.push("dead_code=true"),e.compress.unused&&i.push("unused=true"),i.length>0&&t.push(`--compress ${i.join(",")}`)}return!0===e.mangle&&t.push("--mangle"),e.format&&(!0===e.format.beautify&&t.push("--beautify"),!1===e.format.comments?t.push("--no-comments"):"all"===e.format.comments?t.push("--comments all"):e.format.comments&&
|
|
2
|
+
// 例如:comments: /@license|@preserve/
|
|
3
|
+
t.push(`--comments "${e.format.comments}"`),e.format.indent_level&&t.push(`--format indent_level=${e.format.indent_level}`)),t.join(" ")}),(0,_defineProperty2.default)(this,"getPathAfterSpecificFolder",(e,t="src")=>{const i=e.split(_path2.default.sep);for(let e=i.length-1;e>=0;e--)if(i[e]===t)return"/"+i.slice(e+1).join("/");return""}),(0,_defineProperty2.default)(this,"getPathAfterSrc",e=>this.getPathAfterSpecificFolder(e))}findSpecificFolderByPath(e,t){const i=_path2.default.resolve(e).split(_path2.default.sep);for(;i.length;){const e=_path2.default.join(...i,t);if(_fs.default.existsSync(e))return e;i.pop()}return null}async getPDFText(e){let t=_fs.default.readFileSync(e);return(0,_pdfParse.default)(t).then(e=>e)}printf(){this.appendInfo("i can use in node.js only yo yo")}findFilePathBy(e,t=()=>!0,...i){if(!_fs.default.existsSync(e))return[];const s=[],a=_fs.default.readdirSync(e,{withFileTypes:!0});for(const n of a){if(i.includes(n.name))continue;const a=_path2.default.join(e,n.name);if(n.isDirectory())s.push(...this.findFilePathBy(a,t,...i));else if(n.isFile()){const e=this.getPathInfo(a);t(e)&&s.push(e)}}return s}isPathExist(e){return _fs.default.existsSync(e)}renameFile(e,t="fileName"){if(!this.isFile(e)||!t)return void this.appendError(`renameFile 錯誤, path: ${e}, newName: ${t}`);const i=_path2.default.dirname(e),s=_path2.default.extname(e),a=_path2.default.join(i,`${t}${s}`);_fs.default.renameSync(e,a)}getPathInfo(e){const t=_path2.default.resolve(e),i={path:e,absolute:t,isFile:!1,isDirectory:!0,dirName:void 0,folderName:void 0,dirPath:void 0,folderPath:void 0,extension:void 0,fileName:void 0,fileNameExtension:void 0,lastModifiedTime:void 0,name:void 0};if(this.isFile(t)){i.extension=t.split(".").pop();const e=t.split("/").pop().split(".");e.pop(),i.fileName=e.join("."),i.name=e.join("."),i.dirName=_lodash.default.nth(t.split("/"),-2),i.folderName=_lodash.default.nth(t.split("/"),-2),i.isFile=!0,i.dirPath=this.getFolderPathOfSpecificPath(t),i.folderPath=this.getFolderPathOfSpecificPath(t),i.isDirectory=!1,i.fileNameExtension=`${i.fileName}.${i.extension}`,i.lastModifiedTime=this.getFileLastModifiedTime(t)}return this.isDirectory(t)&&(i.dirName=t.split("/").pop()),i}syncExecuteCommandLine(e){const t=this;this.appendInfo(`執行腳本 ${e}`),_child_process.default.exec(`${e}`,(e,i,s)=>{t.appendInfo(`${i}`),t.appendInfo(`${s}`),null!==e&&t.appendError(`exec error: ${e}`)})}persistByPath(e){const t=_path2.default.isAbsolute(e),i=e.split("/").filter(Boolean),s=i[i.length-1],a=""!==_path2.default.extname(s);let n=t?_path2.default.sep:"";for(let e=0;e<i.length;e++)n=_path2.default.join(n,i[e]),_fs.default.existsSync(n)||(e===i.length-1&&a?_fs.default.writeFileSync(n,""):_fs.default.mkdirSync(n,{recursive:!1}));return _path2.default.resolve(e)}async copyFromFolderToDestFolder(e,t,i=!0,s=!1,a=()=>!0){if(!_fs.default.existsSync(e)||!_fs.default.existsSync(e))throw new _index2.default(8009,`${e} or ${t} is not exist!`);this.appendInfo(`正在複製ing ${e}/* => ${t}/* succeed`),_fsExtra.default.copySync(e,t,{preserveTimestamps:s,override:i,filter:a}),this.appendInfo(`複製成功 ${e}/* => ${t}/* succeed`)}cleanAllFiles(e){this.isDirectory(e)&&(this.appendInfo(`準備清除底下的所有 ${e}`),_fsExtra.default.emptyDirSync(e),this.appendInfo(`成功清除底下的所有 ${e}`))}async deleteSelfByPath(e,t){_fs.default.existsSync(e)&&(this.appendInfo(`準備刪掉 ${e},{force:${t}}`),await(0,_del.default)(e,{force:t}),this.appendInfo(`成功刪掉了 ${e}`))}async deleteFileOrFolder(e){this.appendInfo(`刪掉了 ${e}`),await(0,_del.default)(e)}async deleteChildByPath(e,t=!1){const i=this.getChildPathByPath(e);for(const e of i)await this.deleteSelfByPath(e.absolute,t)}getFileCountsOfFolder(e){return this.isDirectory(e)?_fs.default.readdirSync(e).length:-1}async reinstallNodeModules(e="../",...t){const i=[...t,"node_modules","utiller","configerer"],s=this.findFilePathBy(e,e=>_lodash.default.isEqual(e.fileNameExtension,"package.json"),...i);for(const e of s){const t=this.getFileDirPath(e.absolute),i=`${t}node_modules`,s=`${t}package-lock.json`;await(0,_del.default)(s),this.appendInfo(`刪掉了 ${s}`),await(0,_del.default)(i),this.appendInfo(`刪掉了 ${i}`)}for(const e of s){const t=this.getFileDirPath(e.absolute),i=`${t}node_modules`;_fs.default.existsSync(i)||await this.executeCommandLine(`cd ${t} && npm install`)}}getNamesOfFolderChild(e){return this.getChildPathByPath(e).filter(e=>e.isDirectory).map(e=>e.dirName)}getChildPathByPath(e){try{return _fs.default.readdirSync(e).map(t=>this.getPathInfo(_path2.default.join(e,t)))}catch(e){throw new _index2.default(8002,e)}}copySingleFile(e,t,i,s=!1){const a=i&&i.trim()?_path2.default.join(t,i):t;if(_fs.default.existsSync(a)&&!s)throw new _index2.default(8006,a);_fs.default.copyFileSync(e,a)}ensureFolderExists(e){const t=_path2.default.resolve(e);_fs.default.existsSync(t)||_fs.default.mkdirSync(t,{recursive:!0})}getNodeEnvVariable(e,t=void 0){const i=process.env[e];return void 0===i?t:i}isDirectory(e){return!!this.isPathExist(e)&&_fs.default.lstatSync(e).isDirectory()}isFile(e){return!!this.isPathExist(e)&&_fs.default.lstatSync(e).isFile()}isImageFile(e){return["svg","png","jpg","jpeg"].includes(e.extension)}async cleanChildFiles(e,t=()=>!0,...i){if(!_fs.default.existsSync(e))return!1;const s=this.findFilePathBy(e,t,...i),a=s.filter(e=>!this.isImageFile(e));return await Promise.allSettled(a.map(e=>(this.cleanFileContent(e.absolute),this.appendInfo(`成功 cleanChildFiles() -> '${e.path}'`),Promise.resolve()))),s}cleanFileContent(e){this.syncDeleteFile(e)}async syncWithExistPackage(e="../"){const t=this.findFilePathBy(e,e=>_lodash.default.isEqual(e.fileNameExtension,"package.json"),"node_modules");for(let e of t)try{_lodash.default.isEqual(e.dirName,"..")||this.insertShellCommand(_configerer.configerer.BASE_SHELL_SCRIPT,`cd_${e.dirName}`,`cd ${this.getFolderPathOfSpecificPath(e.absolute)}`)}catch(e){this.appendInfo(e.message)}}async packageTemplatify(e,t){const i=this.getChildPathByPath(e).map(e=>e.absolute.split("/").pop());if(this.has(i,t))throw new _index2.default(8004,` packageName ===> '${t}'`);const s=`${e}/${t}`;_fs.default.mkdirSync(s),_fs.default.copyFileSync("./template/sample.babel.config.js",`${s}/babel.config.js`),_fs.default.copyFileSync("./template/sample.terser.config.js",`${s}/terser.config.js`);const a=this.getJsonObjByFilePath("./template/sample.package.json");a.name=t,this.writeFileInJSON(`${s}/package.json`,a),this.persistByPath(`${s}/src`);const n=String.format(this.getFileContextInRaw("./template/sample.src.index.js"),t,"明悅",new Date);_fs.default.writeFileSync(`${s}/src/index.js`,n);const r=`${this.findSpecificFolderByPath(s,".idea")}/workspace.xml`;if(this.insertShellCommand(_configerer.configerer.BASE_SHELL_SCRIPT,`cd_${t}`,`cd ${_path2.default.resolve(s)}`),_fs.default.existsSync(r)){const e=this.getFileContextInRaw(r).split("\n"),i=_lodash.default.findIndex(e,e=>this.has(e,'name="RunManager'));this.insertToArray(e,i,`<configuration name="${t}" \n type="NodeJSConfigurationType" \n path-to-node="$USER_HOME$/.nvm/versions/node/v18.19.1/bin/node" \n node-parameters="--require @babel/register" \n path-to-js-file="${_path2.default.resolve(s)}/src/index.js" \n working-dir="${_path2.default.resolve(s)}" >`," <envs>",' <env name="self_debug" value="true" />',' <env name="is_node" value="true" />'," </envs>",' <method v="2" />',"</configuration>");const a=_lodash.default.findIndex(e,e=>_lodash.default.isEqual(_lodash.default.trim(e),"<list>"),i);this.insertToArray(e,a,` <item itemvalue="Node.js.${t}" />`),_fs.default.writeFileSync(r,e.join("\n"))}else this.appendError(`${r} not exist`);await this.executeCommandLine(`cd ${_path2.default.resolve(s)} && npm install`),this.appendInfo(`build ${t} succeed!`)}appendInfo(...e){return this.appendLog(_configerer.configerer.PATH_INFO_LOG,e,!1)}appendError(...e){return this.appendLog(_configerer.configerer.PATH_ERROR_LOG,e,!0)}appendLog(e,t,i=!1){const s=`${this.getCurrentTimeFormat()} ${i?"ERROR":"LOG"} : ${t.map(this.stringifyLog).join(" ,")}`;this.isProductionEnvironment(),this.isPersistIntoLogFile&&this.appendFile(e,s)}stringifyLog(e){return"object"==typeof e?JSON.stringify(e):String(e)}appendFile(e,t,i=!0,s=!1){try{const a=_path2.default.resolve(e);s&&_fs.default.existsSync(a)&&_fs.default.unlinkSync(a),_fs.default.existsSync(a)||this.persistByPath(a);const n=`${i?"\n":""}${t}`;_fs.default.appendFileSync(a,n)}catch(e){throw new _index2.default(8001,e)}}disableLogMessagePersistent(){this.isPersistIntoLogFile=!1}getLogString(e){return e.map(e=>this.isJson(e)||_lodash.default.isObject(e)||_lodash.default.isArray(e)?this.deepFlat(e):e).join(" ,")}async persistJsonFilePrettier(e,t,i=!1){e=_path2.default.resolve(e),this.appendFile(e,JSON.stringify(t),!0,!0),i||await this.prettier(e,120)}printCollectionToFile(e){const t=`./logs/__temp_${this.getCurrentTimeFormat()}.txt`;this.persistByPath("./logs/"),this.appendFile(t,this.deepFlat(e," \n\n, ")),this.appendInfo(`collectionToFile succeed, file name ==> ${t}`)}async readFileContentByPath(e,t={}){return t[e]??(t[e]=await _promises.default.readFile(e,"utf-8"))}singleFileTemplatify(e="./"){const t=this.findFilePathByExtension(e,["js"],"node_modules");for(const e of t){const t=this.getFileContextInRaw(e.absolute).trim();if(_lodash.default.isEmpty(t)){this.appendInfo(e.fileName,e.absolute);const t=_lodash.default.isEqual(e.fileName,"index")?e.dirName:e.fileName;_fs.default.writeFileSync(e.absolute,String.format(this.getFileContextInRaw(".\n /template/s\n ample.src.index.js"),t,"明悅",new Date))}}}isFileEmpty(e){const t=this.getFileContextInRaw(e);return!t||!t.trim()}copySingleFileConservative(e,t){const{absolute:i,lastModifiedTime:s}=t;if(!this.isPathExist(i)||this.isFileEmpty(i))return void this.appendInfo(`${i} is empty file, ignore copy behavior`);const a=_fs.default.existsSync(e);a&&this.getFileLastModifiedTime(e)>s?this.appendInfo(`${e} is the latest, ignore this run`):(a||this.appendInfo(`${e} does not exist, safe to copy.`),this.ensureFolderExists(_path2.default.dirname(e)),this.copySingleFile(i,e,void 0,!0))}syncDeleteFile(e){_fs.default.existsSync(e)&&_fs.default.unlinkSync(e)}getFileContextInJSON(e){try{if(_fs.default.existsSync(e))return JSON.parse(_fs.default.readFileSync(e,"utf-8"))}catch(e){throw new _index2.default(9999,e.message)}return{}}getFileContextInRaw(e){return _fs.default.existsSync(e)?_fs.default.readFileSync(e,"utf-8"):""}writeFileInJSON(e,t){let i=JSON.stringify(t,null,2);_fs.default.writeFileSync(e,i)}async generatePackage(e="./",t=!1,i=!0,...s){let a=this.findFilePathByExtension(e,["json"],"node_modules","release");a=_lodash.default.filter(a,e=>_lodash.default.isEqual(e.fileName,"package")),a=a.map(e=>this.getFolderPathOfSpecificPath(e.absolute));for(const e of a)if(this.isAndEquals(...s.map(t=>()=>!this.has(e,t)))){const s=await this.generateTempFolderWithCleanSrc(e);await this.deleteSelfByPath(_path2.default.join(e,"release"),!0);const a=this.persistByPath(_path2.default.join(e,"release"));await this.executeCommandLine(`cd ${e} && babel ./temp --out-dir ./release/lib --config-file ./babel.config.js`);const n=require(_path2.default.join(e,"./terser.config.js")),r=this.getStringOfTerserCommandLine(n);await this.executeCommandLine(`cd ${e}/release/lib && find . -type f -name "*.js" -exec terser {} -o {} ${r} \\;`);const o=_path2.default.join(e,"package.json");try{const s="sample.npm.module.index.js";this.copySingleFile(`/Users/davidtu/cross-achieve/high/idea-inventer/utiller/template/${s}`,a,"index.js",!0);const n=`${e}/release/index.js`;await this.executeCommandLine(`terser ${n} -o ${n} ${r}`);const l=_path2.default.join(e,"template");if(this.isPathExist(l)&&await this.copyFromFolderToDestFolder(l,this.persistByPath(_path2.default.join(a,"template"))),t){const{moduleName:e,version:t}=await this.upgradePackageJsonVersion(o);await this.updateVersionOfTemplate(e,t)}this.copySingleFile(o,_path2.default.join(a,"package.json"),void 0,!0),i||!this.isPathExist(_path2.default.join(a,"node_module"))?await this.executeCommandLine(`cd ${a} && yarn install --production`):this.appendInfo("ignore node-module install behavior"),this.appendInfo(`build ${e} succeed`),t&&await this.executeCommandLine(`cd ${a} && npm publish`)}catch(e){throw await this.deleteSelfByPath(a,!0),new _index2.default(9999,`generatePackage 報錯, ${e.message}`)}finally{await this.deleteSelfByPath(s,!0)}}}async updateVersionOfTemplate(e,t){const i=["/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/template/admin.package.json.mustache","/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/template/web.package.json.mustache","/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/template/functions.package.json.mustache","/Users/davidtu/cross-achieve/high/idea-inventer/utiller/template/sample.package.json","/Users/davidtu/cross-achieve/high/idea-inventer/free_marker/package.json"];for(const s of i)if(this.isPathExist(s)){let i=!1;const a=this.getJsonObjByFilePath(s);if(a&&a.dependencies&&a.dependencies[e]){a.dependencies[e]=`^${t}`;try{await this.writeJsonThanPrettier(s,a),i=!0}catch(e){i=!0}}i||await this.updateFileOfSpecificLine(s,i=>` "${e}":"^${t}"${_lodash.default.endsWith(_lodash.default.trim(i),",")?",":""}`,t=>_lodash.default.startsWith(_lodash.default.trim(t),`"${e}"`))}await this.copyFromFolderToDestFolder("/Users/davidtu/cross-achieve/high/idea-inventer/utiller/template/","/Users/davidtu/cross-achieve/high/idea-inventer/newp/template/",!0,!0)}async updateFileOfSpecificLine(e,t=e=>"updated",i=e=>!0){const s=this.getFileContextInRaw(e).split("\n"),a=s.findIndex(i);-1!==a&&(s[a]=t(s[a]),this.appendFile(e,s.join("\n"),!0,!0),await this.prettier(e))}async writeJsonThanPrettier(e,t){this.writeFileInJSON(e,t),await this.prettier(e)}async enrichEachPackageJson(e){const t=new Set(["package","admin.package","web.package","functions.package"]),i=this.findFilePathByExtension(e,["json"],"gen","node_modules","release").filter(e=>t.has(e.fileName));if(0!==i.length)for(const{absolute:e}of i){const t=this.getJsonObjByFilePath(e);t.scripts||={},t.scripts.updateConfigerer="npm update configerer --save",await this.writeJsonThanPrettier(e,t)}}insertShellCommand(e=_configerer.configerer.BASE_SHELL_SCRIPT,t,i){if(this.isStringContainInLines(this.getFileContextInRaw(e),t))throw new _index2.default(8007,`alias ${t} is exist`);{const s=`alias ${t}='${i}'`;this.appendFile(e,s)}}getAdminCredential(){return this.getJsonObjByFilePath("/Users/davidtu/cross-achieve/mimi/idea-inventer/firebaser/key/mimi19up-firebase-adminsdk.json")}isEmptyFile(e){return!this.isPathExist(e)||_lodash.default.isEmpty(this.getFileContextInRaw(e).trim())}isEmptyFolder(e){return 0===_fs.default.readdirSync(e).length}async prettier(e,t=200){await this.executeCommandLine(`cd ${_path2.default.resolve(".")} && npx prettier --write ${_path2.default.resolve(e)} --print-width ${t}`)}getFileLastModifiedTime(e){return _fs.default.statSync(e).mtimeMs}getJsonObjByFilePath(e){return this.appendInfo(`ready to json path:${e}`),JSON.parse(this.getFileContextInRaw(e))}async upgradePackageJsonVersion(e){if(_lodash.default.isEqual("json",this.getPathInfo(e).extension)){const t=this.getJsonObjByFilePath(e);return t.version=this.getStringOfVersionIncrement(t.version),await this.writeJsonThanPrettier(e,t),{version:t.version,moduleName:t.name}}throw new _index2.default(8020,`path is not package.json, which is ${e}`)}async reWriteJsonAttribute(e,...t){const{extension:i}=this.getPathInfo(e);if("json"!==i)throw new _index2.default(9999,`reWriteJsonAttribute() => path is not package.json, which is ${e}`);const s=t.find(e=>"object"!=typeof e||null===e);if(s)throw new _index2.default(9999,`84451515 attr is not object, which is 'type=${typeof s} => ${s}'`);const a=this.getJsonObjByFilePath(e);for(const e of t)a[this.getObjectKey(e)]=this.getObjectValue(e);return await this.writeJsonThanPrettier(e,a),{version:a.version,moduleName:a.name}}getVersionOfPackageJson(e){return this.getAttributeValueOfJson(e,"version","1.0.0")}getAttributeValueOfJson(e,t,i=void 0){if(_lodash.default.isEqual("json",this.getPathInfo(e).extension))return this.getJsonObjByFilePath(e)[t]??i;throw new _index2.default(8020,`path is not package.json, which is ${e}`)}getVersionOfJsFile(e){return this.getAttributeValueOfJsFile(e,"version","project without version notice")}getAttributeValueOfJsFile(e,t,i=void 0){if(_lodash.default.isEqual(this.getExtensionFromPath(e),"js"))return require(_path2.default.resolve(e)).default[t]??i;throw new _index2.default(8020,`path is not js file, which is ${e}`)}async rewriteAttributeOfSourceJs(e,...t){if(!this.isPathExist(e))throw new _index2.default(9999,`4849813 ${e} is not exist`);for(const i of t){if(!_lodash.default.isObject(i))throw new _index2.default(9999,`4984651 attr is not object, which is 'type=${typeof i} => ${i}'`);const t=this.getObjectKey(i),s=this.getObjectValue(i),a=this.getFileContextInRaw(e).split("\n");a[_lodash.default.findIndex(a,e=>_lodash.default.startsWith(_lodash.default.trim(e),`${t}`))]=` ${t}: '${s}',`,this.appendFile(e,a.join("\n"),!0,!0)}return t}async getAnswerFromPromptQ(e=[{name:"name",require:!0,description:"type the name"}]){return _prompt.default.start(),await _prompt.default.get(e)}async getObjectFromPromptQ(...e){return _prompt.default.start(),await _prompt.default.get(e)}async generateTempFolderWithCleanSrc(e){this.appendInfo("generateTempFolderWithCleanSrc",e);const t=_path2.default.join(e,"src"),i=_path2.default.join(e,"temp");if(!_fs.default.existsSync(t))return i;this.appendInfo("generateTempFolderWithCleanSrc","source",t),this.persistByPath(i),await this.copyFromFolderToDestFolder(t,i);const s=this.findFilePathBy(i);for(const{absolute:e}of s){const t=this.getFileContextInRaw(e).split("\n"),i=t.map(e=>e.trim()),s=i.findIndex(e=>e.startsWith("if (configerer.DEBUG_MODE)")),a=i.lastIndexOf("}");if(s>=0&&a>s){t.splice(s,a-s+1);const i=t.join("\n");this.appendFile(e,i,!0,!0),await this.executeCommandLine(`cd ${_path2.default.dirname(e)} && npx prettier --write "${e}"`)}}return i}rewriteFile2File(e,t){const i=this.getFileContextInRaw(e);if(!i.trim())throw new _index2.default(9999,`${e} 為空,避免覆蓋`);this.appendFile(t,i,!0,!0),this.appendInfo(`rewrite from:${e} => to:${t} 成功`)}getStringOfHeadOfFile(e){if(this.isPathExist(e)){const t=this.getFileContextInRaw(e);return _lodash.default.head(t.split("\n"))}return""}isFileEditSucceed(e){if(!this.isPathExist(e))return!1;const t=this.getPathInfo(e),i=this.getFileContextInRaw(e).trim();if(""===i)return this.appendInfo(`74985465 path ${t.path} is empty file, file would not persist`),!1;try{const e=i.split("\n")[0].match(/const\s+([a-zA-Z_]\w*)\s*=\s*(true|false)\s*;?/);if(!e||_lodash.default.size(e)<3)return!1;if(!0==("true"===e[2]))return!0}catch(e){return this.appendError(`66445411 ${e.message}`),!1}return!1}joinRespectingDot(...e){const t=e[0]?.startsWith("./"),i=e[0]?.startsWith("./")?[e[0].slice(2),...e.slice(1)]:e,s=_path2.default.join(...i);return t&&!_path2.default.isAbsolute(s)?`./${s}`:s}}var _default=exports.default=NodeUtiller;
|