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