@lazycatcloud/lzc-cli 2.0.0-pre.2 → 2.0.0-pre.4

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/changelog.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.0-pre.4](https://gitee.com/linakesi/lzc-cli/compare/v2.0.0-pre.3...v2.0.0-pre.4) (2026-03-14)
4
+
5
+ ### Features
6
+
7
+ - support build package metadata overrides
8
+
9
+ ## [2.0.0-pre.3](https://gitee.com/linakesi/lzc-cli/compare/v2.0.0-pre.2...v2.0.0-pre.3) (2026-03-12)
10
+
11
+ ### Features
12
+
13
+ - refresh the backend template assets and set `mini_os_version` to `v1.5.0`
14
+
15
+ ### Bug Fixes
16
+
17
+ - use top-level override semantics for dev build config
18
+
3
19
  ## [2.0.0-pre.2](https://gitee.com/linakesi/lzc-cli/compare/v2.0.0-pre.1...v2.0.0-pre.2) (2026-03-12)
4
20
 
5
21
  ### Bug Fixes
@@ -11,7 +11,6 @@ import yaml from 'js-yaml';
11
11
  import { buildConfiguredImagesToTempDir } from './lpk_build_images.js';
12
12
  import { pipeline } from 'node:stream/promises';
13
13
  import { t } from '../i18n/index.js';
14
- import mergeWith from 'lodash.mergewith';
15
14
 
16
15
  async function archiveFolderTo(appDir, out, format = 'zip') {
17
16
  return new Promise(async (resolve, reject) => {
@@ -298,24 +297,45 @@ function formatBytes(bytes) {
298
297
  }
299
298
 
300
299
  function mergeBuildOptions(baseOptions, topOptions) {
301
- return mergeWith({}, baseOptions ?? {}, topOptions ?? {}, (objValue, srcValue) => {
302
- if (Array.isArray(srcValue)) {
303
- return srcValue;
304
- }
305
- return undefined;
306
- });
300
+ return {
301
+ ...(baseOptions ?? {}),
302
+ ...(topOptions ?? {}),
303
+ };
307
304
  }
308
305
 
309
- function applyPkgIdSuffix(packageId, suffix) {
310
- const normalizedPackageId = String(packageId ?? '').trim();
311
- const normalizedSuffix = String(suffix ?? '').trim();
312
- if (!normalizedSuffix) {
313
- return normalizedPackageId;
306
+ function normalizePackageMetadataOverride(options, fieldName) {
307
+ if (!Object.prototype.hasOwnProperty.call(options ?? {}, fieldName)) {
308
+ return '';
314
309
  }
315
- if (!normalizedPackageId) {
316
- throw new Error('package is empty, cannot apply pkg_id_suffix');
310
+ const normalizedValue = String(options[fieldName] ?? '').trim();
311
+ if (!normalizedValue) {
312
+ throw new Error(`${fieldName} cannot be empty when specified`);
317
313
  }
318
- return `${normalizedPackageId}.${normalizedSuffix}`;
314
+ return normalizedValue;
315
+ }
316
+
317
+ function applyPackageMetadataOverrides(manifest, packageInfo, options) {
318
+ const pkgId = normalizePackageMetadataOverride(options, 'pkg_id');
319
+ const pkgName = normalizePackageMetadataOverride(options, 'pkg_name');
320
+ const hasPackageMetadataOverrides = !!pkgId || !!pkgName;
321
+ let nextManifest = { ...(manifest ?? {}) };
322
+ let nextPackageInfo = packageInfo;
323
+
324
+ if (pkgId) {
325
+ nextManifest = { ...nextManifest, package: pkgId };
326
+ nextPackageInfo = { ...(nextPackageInfo ?? {}), package: pkgId };
327
+ }
328
+
329
+ if (pkgName) {
330
+ nextManifest = { ...nextManifest, name: pkgName };
331
+ nextPackageInfo = { ...(nextPackageInfo ?? {}), name: pkgName };
332
+ }
333
+
334
+ return {
335
+ manifest: nextManifest,
336
+ packageInfo: nextPackageInfo,
337
+ hasPackageMetadataOverrides,
338
+ };
319
339
  }
320
340
 
321
341
  export function validatePackageFileForArchiveFormat(hasPackageFile, archiveFormat) {
@@ -331,8 +351,8 @@ function hasBuildEnvEntries(envEntries) {
331
351
  return Array.isArray(envEntries) && envEntries.length > 0;
332
352
  }
333
353
 
334
- export function shouldUseV2PackageLayout({ forceV2 = false, hasPackageFile = false, hasImagesConfig = false, buildEnvEntries = [] } = {}) {
335
- return !!forceV2 || !!hasPackageFile || !!hasImagesConfig || hasBuildEnvEntries(buildEnvEntries);
354
+ export function shouldUseV2PackageLayout({ forceV2 = false, hasPackageFile = false, hasImagesConfig = false, buildEnvEntries = [], hasPackageMetadataOverrides = false } = {}) {
355
+ return !!forceV2 || !!hasPackageFile || !!hasImagesConfig || hasBuildEnvEntries(buildEnvEntries) || !!hasPackageMetadataOverrides;
336
356
  }
337
357
 
338
358
  function logDeprecatedStaticFieldWarning(manifestPath, fields) {
@@ -350,6 +370,16 @@ function normalizeBuildOptions(options) {
350
370
  return normalized;
351
371
  }
352
372
 
373
+ function normalizeContentDir(contentdir) {
374
+ if (contentdir === undefined || contentdir === null) {
375
+ return '';
376
+ }
377
+ if (typeof contentdir === 'string') {
378
+ return contentdir.trim();
379
+ }
380
+ return contentdir;
381
+ }
382
+
353
383
  function loadYamlIfExists(filePath) {
354
384
  if (!filePath || !isFileExist(filePath)) {
355
385
  return {};
@@ -399,6 +429,7 @@ export class LpkBuild {
399
429
  this.sourceManifest = null;
400
430
  this.packageInfo = null;
401
431
  this.hasPackageFile = false;
432
+ this.hasPackageMetadataOverrides = false;
402
433
  this.buildVars = null;
403
434
  this.preprocessedManifestText = '';
404
435
 
@@ -427,6 +458,7 @@ export class LpkBuild {
427
458
  this.sourceManifest = null;
428
459
  this.packageInfo = null;
429
460
  this.hasPackageFile = false;
461
+ this.hasPackageMetadataOverrides = false;
430
462
  this.buildVars = buildVarsFromEnvEntries(this.options['envs']);
431
463
  this.preprocessedManifestText = '';
432
464
  return this;
@@ -479,15 +511,10 @@ export class LpkBuild {
479
511
  this.sourceManifest = loaded.sourceManifest;
480
512
  this.packageInfo = loaded.packageInfo;
481
513
  this.hasPackageFile = loaded.hasPackageFile;
482
- this.manifest = loaded.manifest;
483
-
484
- const packageWithSuffix = applyPkgIdSuffix(this.manifest['package'], this.options['pkg_id_suffix']);
485
- if (packageWithSuffix !== this.manifest['package']) {
486
- this.manifest = { ...this.manifest, package: packageWithSuffix };
487
- if (this.hasPackageFile) {
488
- this.packageInfo = { ...(this.packageInfo ?? {}), package: packageWithSuffix };
489
- }
490
- }
514
+ const packageMetadata = applyPackageMetadataOverrides(loaded.manifest, loaded.packageInfo, this.options);
515
+ this.manifest = packageMetadata.manifest;
516
+ this.packageInfo = packageMetadata.packageInfo;
517
+ this.hasPackageMetadataOverrides = packageMetadata.hasPackageMetadataOverrides;
491
518
 
492
519
  if (!isValidPackageName(this.manifest['package'])) {
493
520
  throw t('lzc_cli.lib.app.lpk_build.get_manifest_package_name_fail', `{{ package }} 含有非法字符,请使用正确的包名格式(java的包名格式),如:cloud.lazycat.apps.video`, {
@@ -541,7 +568,7 @@ export class LpkBuild {
541
568
  }
542
569
 
543
570
  const tempDir = fs.mkdtempSync('.lzc-cli-build');
544
- let contentdir = this.options['contentdir'];
571
+ let contentdir = normalizeContentDir(this.options['contentdir']);
545
572
  let browserExtension = this.options['browser-extension'];
546
573
  let aiPodService = this.options['ai-pod-service'];
547
574
  try {
@@ -652,6 +679,7 @@ export class LpkBuild {
652
679
  hasPackageFile: this.hasPackageFile,
653
680
  hasImagesConfig,
654
681
  buildEnvEntries: this.options['envs'],
682
+ hasPackageMetadataOverrides: this.hasPackageMetadataOverrides,
655
683
  });
656
684
  if (useV2PackageLayout) {
657
685
  logDeprecatedStaticFieldWarning(this.manifestFilePath, findManifestStaticFields(this.sourceManifest));
@@ -212,17 +212,18 @@ export class TemplateInit {
212
212
  this.type = normalizeTemplateType(type);
213
213
  this.lpkManifest = new LpkManifest(name);
214
214
  this.lpkPackageFile = new LpkPackageFile(name);
215
+ this.templateVars = null;
215
216
  }
216
217
 
217
218
  async init() {
218
219
  this.type = await chooseTemplate(this.type);
219
220
 
220
- const templateRoot = path.join(contextDirname(import.meta.url), '..', '..', 'template', '_lpk');
221
- const typeTemplatePath = path.join(templateRoot, `${this.type}.manifest.yml.in`);
222
- const manifestTemplatePath = isFileExist(typeTemplatePath) ? typeTemplatePath : path.join(templateRoot, 'manifest.yml.in');
223
- const packageTemplatePath = path.join(templateRoot, 'package.yml.in');
224
- const templateVars = await this.lpkManifest.init(manifestTemplatePath, true);
225
- await this.lpkPackageFile.init(packageTemplatePath, true, templateVars);
221
+ const templateRoot = path.join(contextDirname(import.meta.url), '..', '..', 'template', '_lpk');
222
+ const typeTemplatePath = path.join(templateRoot, `${this.type}.manifest.yml.in`);
223
+ const manifestTemplatePath = isFileExist(typeTemplatePath) ? typeTemplatePath : path.join(templateRoot, 'manifest.yml.in');
224
+ const packageTemplatePath = path.join(templateRoot, 'package.yml.in');
225
+ this.templateVars = await this.lpkManifest.init(manifestTemplatePath, true);
226
+ await this.lpkPackageFile.init(packageTemplatePath, true, this.templateVars);
226
227
  }
227
228
 
228
229
  async create() {
@@ -286,7 +287,7 @@ class LpkCreate {
286
287
  ),
287
288
  );
288
289
  }
289
- await TemplateConfig[type].build(path.resolve(this.cwd, this.name));
290
+ await TemplateConfig[type].build(path.resolve(this.cwd, this.name), { templateVars: template.templateVars });
290
291
 
291
292
  console.log(chalk.green(t('lzc_cli.lib.app.lpk_create.exec_init_project_success_tips', `✨ 初始化懒猫云应用`)));
292
293
  await TemplateConfig[type].after(this.name, this.cwd);
@@ -8,8 +8,23 @@ import logger from 'loglevel';
8
8
  import { t } from '../i18n/index.js';
9
9
 
10
10
  const __dirname = contextDirname(import.meta.url);
11
+ const PROJECT_CREATE_PACKAGE_DEV_PLACEHOLDER = '__PROJECT_CREATE_PACKAGE_DEV__';
11
12
 
12
- async function loadFiles(dirPath, prefix = '') {
13
+ function renderProjectCreateFileContent(filePath, content, opts = {}) {
14
+ if (typeof content !== 'string' || path.basename(filePath) !== 'lzc-build.dev.yml') {
15
+ return content;
16
+ }
17
+ if (!content.includes(PROJECT_CREATE_PACKAGE_DEV_PLACEHOLDER)) {
18
+ return content;
19
+ }
20
+ const packageId = String(opts.templateVars?.package ?? '').trim();
21
+ if (!packageId) {
22
+ throw new Error(`templateVars.package is required for ${filePath}`);
23
+ }
24
+ return content.replaceAll(PROJECT_CREATE_PACKAGE_DEV_PLACEHOLDER, `${packageId}.dev`);
25
+ }
26
+
27
+ async function loadFiles(dirPath, prefix = '', opts = {}) {
13
28
  const templateRoot = path.join(__dirname, '..', '..', 'template');
14
29
  const templateDir = path.join(templateRoot, dirPath);
15
30
 
@@ -26,15 +41,16 @@ async function loadFiles(dirPath, prefix = '') {
26
41
  } else {
27
42
  content = fs.readFileSync(sourcePath, 'utf8');
28
43
  }
29
- if (p.startsWith('_')) {
30
- p = p.replace('_', '.');
31
- }
44
+ if (p.startsWith('_')) {
45
+ p = p.replace('_', '.');
46
+ }
32
47
 
33
- files[path.join(prefix, p)] = {
34
- content,
35
- mode,
36
- };
37
- }
48
+ const outputPath = path.join(prefix, p);
49
+ files[outputPath] = {
50
+ content: renderProjectCreateFileContent(outputPath, content, opts),
51
+ mode,
52
+ };
53
+ }
38
54
  return files;
39
55
  }
40
56
 
@@ -50,7 +66,7 @@ function writeFileTree(target, files) {
50
66
  }
51
67
 
52
68
  export async function generate(type, to, opts = {}) {
53
- const files = await loadFiles(type, opts.prefix);
69
+ const files = await loadFiles(type, opts.prefix, opts);
54
70
  writeFileTree(to, files);
55
71
  }
56
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazycatcloud/lzc-cli",
3
- "version": "2.0.0-pre.2",
3
+ "version": "2.0.0-pre.4",
4
4
  "description": "lazycat cloud developer kit",
5
5
  "scripts": {
6
6
  "release": "release-it patch",
@@ -9,6 +9,7 @@ lzc-cli project info
9
9
  默认情况下,project 命令在存在 `lzc-build.dev.yml` 时会优先使用它。
10
10
  每个命令都会打印实际使用的 `Build config`。
11
11
  如果要操作 `lzc-build.yml`,请显式加上 `--release`。
12
+ `lzc-build.dev.yml` 中出现的同名顶层字段会整体覆盖 `lzc-build.yml`,不会做递归 merge。
12
13
  如果是前端模板,`project deploy` 会按 `buildscript` 自动安装依赖并完成构建,不需要额外先执行 `npm install`。
13
14
 
14
15
  ## 构建
@@ -1,4 +1,7 @@
1
1
  # 开发态覆盖配置
2
2
 
3
- # pkg_id_suffix: append an extra package id segment for dev deploys
4
- pkg_id_suffix: dev
3
+ # 覆盖开发态包名,避免覆盖 release 安装包
4
+ pkg_id: cloud.lazycat.app.helloworld.dev
5
+
6
+ # 覆盖 release 配置中的 contentdir;留空表示开发态不打包内容目录
7
+ contentdir:
@@ -1,7 +1,10 @@
1
1
  # 开发态覆盖配置
2
2
 
3
- # pkg_id_suffix: append an extra package id segment for dev deploys
4
- pkg_id_suffix: dev
3
+ # 覆盖开发态包名,避免覆盖 release 安装包
4
+ pkg_id: __PROJECT_CREATE_PACKAGE_DEV__
5
+
6
+ # 覆盖 release 配置中的 contentdir;留空表示开发态不打包内容目录
7
+ contentdir:
5
8
 
6
9
  envs:
7
10
  - DEV_MODE=1
@@ -1,4 +1,7 @@
1
1
  # 开发态覆盖配置
2
2
 
3
- # pkg_id_suffix: append an extra package id segment for dev deploys
4
- pkg_id_suffix: dev
3
+ # 覆盖开发态包名,避免覆盖 release 安装包
4
+ pkg_id: __PROJECT_CREATE_PACKAGE_DEV__
5
+
6
+ # 覆盖 release 配置中的 contentdir;留空表示开发态不打包内容目录
7
+ contentdir:
@@ -1,7 +1,10 @@
1
1
  # 开发态覆盖配置
2
2
 
3
- # pkg_id_suffix: append an extra package id segment for dev deploys
4
- pkg_id_suffix: dev
3
+ # 覆盖开发态包名,避免覆盖 release 安装包
4
+ pkg_id: __PROJECT_CREATE_PACKAGE_DEV__
5
+
6
+ # 覆盖 release 配置中的 contentdir;留空表示开发态不打包内容目录
7
+ contentdir:
5
8
 
6
9
  envs:
7
10
  - DEV_MODE=1
@@ -1,7 +1,10 @@
1
1
  # 开发态覆盖配置
2
2
 
3
- # pkg_id_suffix: append an extra package id segment for dev deploys
4
- pkg_id_suffix: dev
3
+ # 覆盖开发态包名,避免覆盖 release 安装包
4
+ pkg_id: __PROJECT_CREATE_PACKAGE_DEV__
5
+
6
+ # 覆盖 release 配置中的 contentdir;留空表示开发态不打包内容目录
7
+ contentdir:
5
8
 
6
9
  envs:
7
10
  - DEV_MODE=1
@@ -1,7 +1,10 @@
1
1
  # 开发态覆盖配置
2
2
 
3
- # pkg_id_suffix: append an extra package id segment for dev deploys
4
- pkg_id_suffix: dev
3
+ # 覆盖开发态包名,避免覆盖 release 安装包
4
+ pkg_id: __PROJECT_CREATE_PACKAGE_DEV__
5
+
6
+ # 覆盖 release 配置中的 contentdir;留空表示开发态不打包内容目录
7
+ contentdir:
5
8
 
6
9
  envs:
7
10
  - DEV_MODE=1
@@ -1,7 +1,10 @@
1
1
  # 开发态覆盖配置
2
2
 
3
- # pkg_id_suffix: append an extra package id segment for dev deploys
4
- pkg_id_suffix: dev
3
+ # 覆盖开发态包名,避免覆盖 release 安装包
4
+ pkg_id: __PROJECT_CREATE_PACKAGE_DEV__
5
+
6
+ # 覆盖 release 配置中的 contentdir;留空表示开发态不打包内容目录
7
+ contentdir:
5
8
 
6
9
  envs:
7
10
  - DEV_MODE=1