@lazycatcloud/lzc-cli 1.2.26 → 1.2.28

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/lib/utils.js CHANGED
@@ -1,25 +1,26 @@
1
- import path from "path";
2
- import fs from "fs";
3
- import os from "os";
4
- import glob from "fast-glob";
5
- import yaml from "js-yaml";
6
- import mergeWith from "lodash.mergewith";
7
- import { dirname } from "path";
8
- import { fileURLToPath } from "url";
9
- import ignore from "ignore";
10
- import { createHash } from "node:crypto";
11
- import https from "node:https";
12
- import http from "node:http";
13
- import zlib from "node:zlib";
14
- import process from "node:process";
15
- import { spawnSync } from "node:child_process";
16
- import logger from "loglevel";
17
- import * as tar from "tar";
1
+ import path from "path"
2
+ import fs from "fs"
3
+ import os from "os"
4
+ import glob from "fast-glob"
5
+ import yaml from "js-yaml"
6
+ import mergeWith from "lodash.mergewith"
7
+ import { dirname } from "path"
8
+ import { fileURLToPath } from "url"
9
+ import ignore from "ignore"
10
+ import { createHash } from "node:crypto"
11
+ import https from "node:https"
12
+ import http from "node:http"
13
+ import zlib from "node:zlib"
14
+ import process from "node:process"
15
+ import { spawnSync } from "node:child_process"
16
+ import logger from "loglevel"
17
+ import * as tar from "tar"
18
+ import dns from "node:dns/promises"
18
19
 
19
20
  export const envsubstr = async (templateContents, args) => {
20
- const parse = await importDefault("envsub/js/envsub-parser.js");
21
- return parse(templateContents, args);
22
- };
21
+ const parse = await importDefault("envsub/js/envsub-parser.js")
22
+ return parse(templateContents, args)
23
+ }
23
24
 
24
25
  /**
25
26
  * 确保文件夹存在
@@ -27,31 +28,70 @@ export const envsubstr = async (templateContents, args) => {
27
28
  *
28
29
  */
29
30
  export function ensureDir(filePath) {
30
- let dirPath;
31
+ let dirPath
31
32
  if (filePath.endsWith("/")) {
32
- dirPath = filePath;
33
+ dirPath = filePath
33
34
  } else {
34
- dirPath = path.dirname(filePath);
35
+ dirPath = path.dirname(filePath)
35
36
  }
36
37
  if (!fs.existsSync(dirPath)) {
37
- fs.mkdirSync(dirPath, { recursive: true });
38
+ fs.mkdirSync(dirPath, { recursive: true })
38
39
  }
39
40
  }
40
41
 
42
+ export function ensureDirectoryExists(filePath, isRenameOrClear) {
43
+ let result = { isExists: false, renamedFileName: undefined }
44
+ try {
45
+ const stats = fs.statSync(filePath) // 获取文件或目录的状态
46
+ result.isExists = true
47
+ if (isRenameOrClear) {
48
+ if (stats.isDirectory()) {
49
+ const newPath = getUniquePath(filePath)
50
+ result.renamedFileName = path.basename(newPath)
51
+ fs.renameSync(filePath, newPath)
52
+ } else {
53
+ fs.rmSync(filePath)
54
+ }
55
+ fs.mkdirSync(filePath, { recursive: true })
56
+ }
57
+ } catch (err) {
58
+ if (err.code === "ENOENT") {
59
+ fs.mkdirSync(filePath, { recursive: true })
60
+ } else {
61
+ throw `创建文件夹${filePath}错误: ${err}`
62
+ }
63
+ }
64
+ return result
65
+ }
66
+
67
+ // 如果该路径已存在,则返回带递增数字的路径
68
+ function getUniquePath(basePath) {
69
+ let dirName = path.basename(basePath)
70
+ let dirPath = basePath
71
+ let index = 1
72
+ // 检查是否存在同名目录
73
+ while (fs.existsSync(dirPath)) {
74
+ dirName = `${path.basename(basePath, path.extname(basePath))}_${index}`
75
+ dirPath = path.join(path.dirname(basePath), dirName)
76
+ index++
77
+ }
78
+ return dirPath
79
+ }
80
+
41
81
  export async function createTemplateFileCommon(templateFile, outputFile, env) {
42
82
  const options = {
43
83
  envs: toPair(env),
44
84
  syntax: "default",
45
- protect: false,
46
- };
85
+ protect: false
86
+ }
47
87
  const output = await envsubstr(fs.readFileSync(templateFile, "utf-8"), {
48
- options,
49
- });
50
- fs.writeFileSync(outputFile, output);
88
+ options
89
+ })
90
+ fs.writeFileSync(outputFile, output)
51
91
  }
52
92
 
53
93
  export function loadFromYaml(file) {
54
- return yaml.load(fs.readFileSync(file, "utf8"));
94
+ return yaml.load(fs.readFileSync(file, "utf8"))
55
95
  }
56
96
 
57
97
  export function dumpToYaml(template, target) {
@@ -59,113 +99,113 @@ export function dumpToYaml(template, target) {
59
99
  target,
60
100
  yaml.dump(template, {
61
101
  styles: {
62
- "!!null": "empty", // dump null as ""
63
- },
64
- }),
65
- );
102
+ "!!null": "empty" // dump null as ""
103
+ }
104
+ })
105
+ )
66
106
  }
67
107
 
68
108
  export function toPair(object) {
69
109
  return Object.keys(object).map((key) => {
70
- let value = object[key] ? object[key].toString() : "";
71
- key = key.replace(/-/g, "_");
110
+ let value = object[key] ? object[key].toString() : ""
111
+ key = key.replace(/-/g, "_")
72
112
  return {
73
113
  name: key,
74
- value,
75
- };
76
- });
114
+ value
115
+ }
116
+ })
77
117
  }
78
118
 
79
119
  export async function envTemplateFile(templateFile, env) {
80
- const template = yaml.load(fs.readFileSync(templateFile, "utf8"));
120
+ const template = yaml.load(fs.readFileSync(templateFile, "utf8"))
81
121
  const options = {
82
122
  envs: toPair(env),
83
123
  syntax: "default",
84
- protect: false,
85
- };
124
+ protect: false
125
+ }
86
126
  return await envsubstr(
87
127
  yaml.dump(template, {
88
128
  styles: {
89
- "!!null": "empty", // dump null as ""
90
- },
129
+ "!!null": "empty" // dump null as ""
130
+ }
91
131
  }),
92
- { options },
93
- );
132
+ { options }
133
+ )
94
134
  }
95
135
 
96
136
  export function mergeYamlInMemory(args) {
97
137
  if (args.length == 0) {
98
- return {};
138
+ return {}
99
139
  } else if (args.length == 1) {
100
- return args[0];
140
+ return args[0]
101
141
  }
102
142
  return args.reduce((prev, curr) => {
103
143
  let result = mergeWith(prev, curr, (objValue, srcValue) => {
104
144
  if (Array.isArray(objValue)) {
105
- return objValue.concat(srcValue);
145
+ return objValue.concat(srcValue)
106
146
  }
107
- });
108
- return result;
109
- }, {});
147
+ })
148
+ return result
149
+ }, {})
110
150
  }
111
151
 
112
152
  export function contextDirname(url = import.meta.url) {
113
- return dirname(fileURLToPath(url));
153
+ return dirname(fileURLToPath(url))
114
154
  }
115
155
 
116
156
  export async function importDefault(pkgPath) {
117
- let mod = await import(pkgPath);
118
- return mod.default;
157
+ let mod = await import(pkgPath)
158
+ return mod.default
119
159
  }
120
160
 
121
161
  export class GitIgnore {
122
162
  constructor(root) {
123
- this.root = root;
124
- this.ignores = [];
163
+ this.root = root
164
+ this.ignores = []
125
165
  }
126
166
 
127
167
  async collect() {
128
168
  const files = await glob(["**/.gitignore"], {
129
169
  cwd: this.root,
130
170
  dot: true,
131
- deep: 3,
132
- });
171
+ deep: 3
172
+ })
133
173
 
134
- files.forEach((f) => this._add(f));
174
+ files.forEach((f) => this._add(f))
135
175
  }
136
176
 
137
177
  _add(ignoreFile) {
138
- let data = fs.readFileSync(ignoreFile, "utf8");
139
- let ig = ignore({ allowRelativePaths: true });
140
- ig.add(data.split("\n"));
178
+ let data = fs.readFileSync(ignoreFile, "utf8")
179
+ let ig = ignore({ allowRelativePaths: true })
180
+ ig.add(data.split("\n"))
141
181
  this.ignores.push({
142
182
  ig,
143
- dir: path.dirname(ignoreFile),
144
- });
183
+ dir: path.dirname(ignoreFile)
184
+ })
145
185
  }
146
186
 
147
187
  contain(filepath) {
148
188
  return this.ignores.some(({ ig, dir }) => {
149
189
  // 去除不应该计算ignore 的文件
150
190
  if (!filepath.startsWith(dir)) {
151
- return false;
191
+ return false
152
192
  }
153
193
 
154
- return ig.ignores(path.relative(dir, filepath));
155
- });
194
+ return ig.ignores(path.relative(dir, filepath))
195
+ })
156
196
  }
157
197
  }
158
198
 
159
199
  export function isDirSync(path) {
160
- let stat = fs.statSync(path);
161
- return stat.isDirectory();
200
+ let stat = fs.statSync(path)
201
+ return stat.isDirectory()
162
202
  }
163
203
 
164
204
  export function isDirExist(path) {
165
205
  try {
166
- return isDirSync(path);
206
+ return isDirSync(path)
167
207
  } catch {
168
- return false;
208
+ return false
169
209
  }
170
210
  }
171
211
 
@@ -173,173 +213,182 @@ export function isFileExist(path) {
173
213
  try {
174
214
  fs.accessSync(
175
215
  path,
176
- fs.constants.W_OK | fs.constants.R_OK | fs.constants.F_OK,
177
- );
178
- return true;
216
+ fs.constants.W_OK | fs.constants.R_OK | fs.constants.F_OK
217
+ )
218
+ return true
179
219
  } catch {
180
- return false;
220
+ return false
181
221
  }
182
222
  }
183
223
 
184
224
  export async function sleep(ms) {
185
225
  return new Promise((resolve) => {
186
- setTimeout(resolve, ms);
187
- });
226
+ setTimeout(resolve, ms)
227
+ })
188
228
  }
189
229
 
190
230
  export async function md5String(str) {
191
- const hash = createHash("md5");
192
- hash.update(str);
193
- return hash.digest("hex");
231
+ const hash = createHash("md5")
232
+ hash.update(str)
233
+ return hash.digest("hex")
194
234
  }
195
235
 
196
236
  export async function md5File(filepath) {
197
- const hash = createHash("md5");
198
- const input = fs.createReadStream(filepath);
237
+ const hash = createHash("md5")
238
+ const input = fs.createReadStream(filepath)
199
239
  return new Promise((resolve) => {
200
240
  input.on("readable", () => {
201
- const data = input.read();
241
+ const data = input.read()
202
242
  if (data) {
203
- hash.update(data);
243
+ hash.update(data)
204
244
  } else {
205
- resolve(hash.digest("hex"));
245
+ resolve(hash.digest("hex"))
206
246
  }
207
- });
208
- });
247
+ })
248
+ })
209
249
  }
210
250
 
211
251
  export class Downloader {
212
252
  constructor() {}
213
253
 
214
254
  showDownloadingProgress(received, total) {
215
- let percentage = ((received * 100) / total).toFixed(2);
216
- process.stdout.write("\r");
255
+ let percentage = ((received * 100) / total).toFixed(2)
256
+ process.stdout.write("\r")
217
257
  process.stdout.write(
218
258
  percentage +
219
259
  "% | " +
220
260
  received +
221
261
  " bytes downloaded out of " +
222
262
  total +
223
- " bytes.",
224
- );
263
+ " bytes."
264
+ )
225
265
  }
226
266
 
227
267
  download(url, savePath, enableGzip = false) {
228
- let tmpPath = savePath + ".tmp";
229
- const options = new URL(url);
230
- let request = url.startsWith("https") ? https.request : http.request;
268
+ let tmpPath = savePath + ".tmp"
269
+ const options = new URL(url)
270
+ let request = url.startsWith("https") ? https.request : http.request
231
271
 
232
272
  return new Promise((resolve, reject) => {
233
273
  const req = request(options, (res) => {
234
274
  if (res.statusCode != 200) {
235
- reject(`下载 ${url} 失败`);
236
- return;
275
+ reject(`下载 ${url} 失败`)
276
+ return
237
277
  }
238
278
 
239
- let total = parseInt(res.headers["content-length"]);
240
- let recive = 0;
279
+ let total = parseInt(res.headers["content-length"])
280
+ let recive = 0
241
281
  res.on("data", (chunk) => {
242
- recive += chunk.length;
243
- this.showDownloadingProgress(recive, total);
244
- });
282
+ recive += chunk.length
283
+ this.showDownloadingProgress(recive, total)
284
+ })
245
285
 
246
- let outputFile = fs.createWriteStream(tmpPath, { flags: "w+" });
286
+ let outputFile = fs.createWriteStream(tmpPath, { flags: "w+" })
247
287
  if (enableGzip) {
248
- let decoder = zlib.createUnzip();
249
- res.pipe(decoder).pipe(outputFile);
288
+ let decoder = zlib.createUnzip()
289
+ res.pipe(decoder).pipe(outputFile)
250
290
  } else {
251
- res.pipe(outputFile);
291
+ res.pipe(outputFile)
252
292
  }
253
293
 
254
- outputFile.on("error", reject);
294
+ outputFile.on("error", reject)
255
295
  outputFile.on("finish", () => {
256
- fs.renameSync(tmpPath, savePath);
257
- process.stdout.write("\n");
258
- resolve();
259
- });
260
- });
261
- req.on("error", reject);
262
- req.end();
263
- });
296
+ fs.renameSync(tmpPath, savePath)
297
+ process.stdout.write("\n")
298
+ resolve()
299
+ })
300
+ })
301
+ req.on("error", reject)
302
+ req.end()
303
+ })
264
304
  }
265
305
  }
266
306
 
267
307
  export class FileLocker {
268
308
  constructor(id) {
269
- this.filename = undefined;
270
- this.fd = undefined;
271
- this.id = id;
309
+ this.filename = undefined
310
+ this.fd = undefined
311
+ this.id = id
272
312
  }
273
313
  lock() {
274
- this.filename = path.resolve(os.tmpdir(), "lzc-cli-file-lock", this.id);
275
- ensureDir(this.filename);
314
+ this.filename = path.resolve(os.tmpdir(), "lzc-cli-file-lock", this.id)
315
+ ensureDir(this.filename)
276
316
 
277
317
  try {
278
- this.fd = fs.openSync(this.filename, "wx+");
318
+ this.fd = fs.openSync(this.filename, "wx+")
279
319
  } catch (e) {
280
320
  if (e.code == "EEXIST" && !this.withOpen(this.filename)) {
281
- logger.debug("filelock exist open with w+");
282
- this.fd = fs.openSync(this.filename, "w+");
321
+ logger.debug("filelock exist open with w+")
322
+ this.fd = fs.openSync(this.filename, "w+")
283
323
  } else {
284
- throw e;
324
+ throw e
285
325
  }
286
326
  }
287
327
  }
288
328
  unlock() {
289
329
  if (this.fd) {
290
- fs.closeSync(this.fd);
291
- fs.rmSync(this.filename);
292
- this.fd = undefined;
293
- this.filename = undefined;
330
+ fs.closeSync(this.fd)
331
+ fs.rmSync(this.filename)
332
+ this.fd = undefined
333
+ this.filename = undefined
294
334
  }
295
335
  }
296
336
  withOpen(filename) {
297
- const p = spawnSync("lsof", ["-w", "-t", filename]);
337
+ const p = spawnSync("lsof", ["-w", "-t", filename])
298
338
  if (p.status === 0) {
299
- return true;
339
+ return true
300
340
  } else {
301
- return false;
341
+ return false
302
342
  }
303
343
  }
304
344
  }
305
345
 
346
+ export function isValidAppId(appId) {
347
+ // 暂时去掉 - 的支持
348
+ // const regex = new RegExp("^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$")
349
+ const regex = new RegExp("^[a-z][a-z0-9]{0,61}[a-zA-Z0-9]$")
350
+ return regex.test(appId)
351
+ }
352
+
306
353
  export function isValidPackageName(packageName) {
307
- const regex = new RegExp("^(?:[a-z][a-z0-9_]*\\.)+[a-z][a-z0-9_]*$");
308
- return regex.test(packageName);
354
+ // 暂时去掉 - 的支持
355
+ // const regex = new RegExp("^(?:[a-z][a-z0-9-]*\\.)+[a-z][a-z0-9-]*$")
356
+ const regex = new RegExp("^(?:[a-z][a-z0-9]*\\.)+[a-z][a-z0-9]*$")
357
+ return regex.test(packageName)
309
358
  }
310
359
 
311
360
  export function isUserApp(manifest) {
312
- return !!manifest["application"]["user_app"];
361
+ return !!manifest["application"]["user_app"]
313
362
  }
314
363
 
315
364
  export async function tarContentDir(from, to, cwd = "./") {
316
365
  return new Promise((resolve, reject) => {
317
- const dest = fs.createWriteStream(to);
366
+ const dest = fs.createWriteStream(to)
318
367
  tar
319
368
  .c(
320
369
  {
321
370
  cwd: cwd,
322
371
  filter: (filePath) => {
323
- logger.debug(`tar gz ${filePath}`);
324
- return true;
372
+ logger.debug(`tar gz ${filePath}`)
373
+ return true
325
374
  },
326
375
  sync: true,
327
376
  portable: {
328
377
  uid: 0,
329
- gid: 0,
330
- },
378
+ gid: 0
379
+ }
331
380
  },
332
- from,
381
+ from
333
382
  )
334
383
  .pipe(dest)
335
384
  .on("close", () => {
336
- logger.debug(`pack: ${dest.path}`);
337
- resolve(dest.path);
385
+ logger.debug(`pack: ${dest.path}`)
386
+ resolve(dest.path)
338
387
  })
339
388
  .on("error", (err) => {
340
- reject(err);
341
- });
342
- });
389
+ reject(err)
390
+ })
391
+ })
343
392
  }
344
393
 
345
394
  /**
@@ -350,7 +399,7 @@ export async function tarContentDir(from, to, cwd = "./") {
350
399
  */
351
400
  function isPngWithBuffer(buffer) {
352
401
  if (!buffer || buffer.length < 8) {
353
- return false;
402
+ return false
354
403
  }
355
404
  return (
356
405
  buffer[0] === 0x89 &&
@@ -361,15 +410,43 @@ function isPngWithBuffer(buffer) {
361
410
  buffer[5] === 0x0a &&
362
411
  buffer[6] === 0x1a &&
363
412
  buffer[7] === 0x0a
364
- );
413
+ )
365
414
  }
366
415
 
367
416
  export function isPngWithFile(filepath) {
368
- if (!isFileExist(filepath)) return false;
369
- const buf = fs.readFileSync(filepath);
370
- return isPngWithBuffer(buf);
417
+ if (!isFileExist(filepath)) return false
418
+ const buf = fs.readFileSync(filepath)
419
+ return isPngWithBuffer(buf)
371
420
  }
372
421
 
373
422
  export function isDebugMode() {
374
- return logger.getLevel() <= logger.levels.DEBUG;
423
+ return logger.getLevel() <= logger.levels.DEBUG
424
+ }
425
+
426
+ export async function resolveDomain(domain) {
427
+ try {
428
+ // Set machine's dns server as defalut dns server
429
+ dns.setServers(["[fc03:1136:3800::1]"])
430
+ const [ipv6Addresses, ipv4Addresses] = await Promise.allSettled([
431
+ dns.resolve6(domain),
432
+ dns.resolve4(domain)
433
+ ])
434
+ let resolvedAddress
435
+ if (
436
+ ipv6Addresses.status === "fulfilled" &&
437
+ ipv6Addresses.value.length > 0
438
+ ) {
439
+ resolvedAddress = ipv6Addresses.value[0]
440
+ } else if (
441
+ ipv4Addresses.status === "fulfilled" &&
442
+ ipv4Addresses.value.length > 0
443
+ ) {
444
+ resolvedAddress = ipv4Addresses.value[0]
445
+ } else {
446
+ throw new Error(`无法解析域名 ${domain}`)
447
+ }
448
+ return resolvedAddress
449
+ } catch (error) {
450
+ throw new Error(`无法解析域名 ${domain}: ${error.message}`)
451
+ }
375
452
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazycatcloud/lzc-cli",
3
- "version": "1.2.26",
3
+ "version": "1.2.28",
4
4
  "description": "lazycat cloud developer kit",
5
5
  "files": [
6
6
  "template",
@@ -44,6 +44,12 @@
44
44
  "@types/command-exists": "^1.2.3",
45
45
  "prettier": "^3.3.3"
46
46
  },
47
+ "prettier": {
48
+ "bracketSameLine": true,
49
+ "htmlWhitespaceSensitivity": "ignore",
50
+ "semi": false,
51
+ "trailingComma": "none"
52
+ },
47
53
  "publishConfig": {
48
54
  "registry": "https://registry.npmjs.org",
49
55
  "access": "public"