@svton/cli 1.2.5 → 1.4.0

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.
@@ -0,0 +1,105 @@
1
+ import "./chunk-Y6FXYEAI.mjs";
2
+
3
+ // src/utils/ast-helper.ts
4
+ import * as parser from "@babel/parser";
5
+ import traverse from "@babel/traverse";
6
+ import generate from "@babel/generator";
7
+ import * as t from "@babel/types";
8
+ import fs from "fs-extra";
9
+ function addImportToFile(code, importDeclarations) {
10
+ const ast = parser.parse(code, {
11
+ sourceType: "module",
12
+ plugins: ["typescript", "decorators-legacy"]
13
+ });
14
+ let lastImportIndex = -1;
15
+ traverse(ast, {
16
+ ImportDeclaration(path) {
17
+ const start = path.node.start;
18
+ if (start !== null && start !== void 0 && start > lastImportIndex) {
19
+ lastImportIndex = start;
20
+ }
21
+ }
22
+ });
23
+ const newImports = importDeclarations.map(({ from, imports }) => {
24
+ const specifiers = imports.map(
25
+ (imp) => t.importSpecifier(t.identifier(imp), t.identifier(imp))
26
+ );
27
+ return t.importDeclaration(specifiers, t.stringLiteral(from));
28
+ });
29
+ if (lastImportIndex >= 0) {
30
+ const program = ast.program;
31
+ const lastImportNode = program.body.find(
32
+ (node) => node.type === "ImportDeclaration" && node.start !== null && node.start === lastImportIndex
33
+ );
34
+ if (lastImportNode) {
35
+ const index = program.body.indexOf(lastImportNode);
36
+ program.body.splice(index + 1, 0, ...newImports);
37
+ }
38
+ } else {
39
+ ast.program.body.unshift(...newImports);
40
+ }
41
+ const output = generate(ast, {
42
+ retainLines: false,
43
+ compact: false
44
+ });
45
+ return output.code;
46
+ }
47
+ function addModuleImports(code, moduleExpressions) {
48
+ const ast = parser.parse(code, {
49
+ sourceType: "module",
50
+ plugins: ["typescript", "decorators-legacy"]
51
+ });
52
+ let modified = false;
53
+ traverse(ast, {
54
+ Decorator(path) {
55
+ if (t.isCallExpression(path.node.expression) && t.isIdentifier(path.node.expression.callee) && path.node.expression.callee.name === "Module") {
56
+ const args = path.node.expression.arguments;
57
+ if (args.length > 0 && t.isObjectExpression(args[0])) {
58
+ const moduleConfig = args[0];
59
+ const importsProperty = moduleConfig.properties.find(
60
+ (prop) => t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === "imports"
61
+ );
62
+ if (importsProperty && t.isObjectProperty(importsProperty)) {
63
+ if (t.isArrayExpression(importsProperty.value)) {
64
+ for (const expr of moduleExpressions) {
65
+ try {
66
+ const exprAst = parser.parseExpression(expr, {
67
+ plugins: ["typescript"]
68
+ });
69
+ importsProperty.value.elements.push(exprAst);
70
+ modified = true;
71
+ } catch (error) {
72
+ console.error(`Failed to parse expression: ${expr}`, error);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ });
81
+ if (!modified) {
82
+ console.warn("Could not find @Module decorator or imports array");
83
+ return code;
84
+ }
85
+ const output = generate(ast, {
86
+ retainLines: false,
87
+ compact: false
88
+ });
89
+ return output.code;
90
+ }
91
+ async function updateAppModuleFile(filePath, imports, moduleExpressions) {
92
+ let code = await fs.readFile(filePath, "utf-8");
93
+ if (imports.length > 0) {
94
+ code = addImportToFile(code, imports);
95
+ }
96
+ if (moduleExpressions.length > 0) {
97
+ code = addModuleImports(code, moduleExpressions);
98
+ }
99
+ await fs.writeFile(filePath, code, "utf-8");
100
+ }
101
+ export {
102
+ addImportToFile,
103
+ addModuleImports,
104
+ updateAppModuleFile
105
+ };
@@ -0,0 +1,105 @@
1
+ import "./chunk-Y6FXYEAI.mjs";
2
+
3
+ // src/utils/ast-helper.ts
4
+ import * as parser from "@babel/parser";
5
+ import traverse from "@babel/traverse";
6
+ import generate from "@babel/generator";
7
+ import * as t from "@babel/types";
8
+ import fs from "fs-extra";
9
+ function addImportToFile(code, importDeclarations) {
10
+ const ast = parser.parse(code, {
11
+ sourceType: "module",
12
+ plugins: ["typescript", "decorators-legacy"]
13
+ });
14
+ let lastImportIndex = -1;
15
+ traverse(ast, {
16
+ ImportDeclaration(path) {
17
+ const start = path.node.start;
18
+ if (start !== null && start > lastImportIndex) {
19
+ lastImportIndex = start;
20
+ }
21
+ }
22
+ });
23
+ const newImports = importDeclarations.map(({ from, imports }) => {
24
+ const specifiers = imports.map(
25
+ (imp) => t.importSpecifier(t.identifier(imp), t.identifier(imp))
26
+ );
27
+ return t.importDeclaration(specifiers, t.stringLiteral(from));
28
+ });
29
+ if (lastImportIndex >= 0) {
30
+ const program = ast.program;
31
+ const lastImportNode = program.body.find(
32
+ (node) => node.type === "ImportDeclaration" && node.start === lastImportIndex
33
+ );
34
+ if (lastImportNode) {
35
+ const index = program.body.indexOf(lastImportNode);
36
+ program.body.splice(index + 1, 0, ...newImports);
37
+ }
38
+ } else {
39
+ ast.program.body.unshift(...newImports);
40
+ }
41
+ const output = generate(ast, {
42
+ retainLines: false,
43
+ compact: false
44
+ });
45
+ return output.code;
46
+ }
47
+ function addModuleImports(code, moduleExpressions) {
48
+ const ast = parser.parse(code, {
49
+ sourceType: "module",
50
+ plugins: ["typescript", "decorators-legacy"]
51
+ });
52
+ let modified = false;
53
+ traverse(ast, {
54
+ Decorator(path) {
55
+ if (t.isCallExpression(path.node.expression) && t.isIdentifier(path.node.expression.callee) && path.node.expression.callee.name === "Module") {
56
+ const args = path.node.expression.arguments;
57
+ if (args.length > 0 && t.isObjectExpression(args[0])) {
58
+ const moduleConfig = args[0];
59
+ const importsProperty = moduleConfig.properties.find(
60
+ (prop) => t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === "imports"
61
+ );
62
+ if (importsProperty && t.isObjectProperty(importsProperty)) {
63
+ if (t.isArrayExpression(importsProperty.value)) {
64
+ for (const expr of moduleExpressions) {
65
+ try {
66
+ const exprAst = parser.parseExpression(expr, {
67
+ plugins: ["typescript"]
68
+ });
69
+ importsProperty.value.elements.push(exprAst);
70
+ modified = true;
71
+ } catch (error) {
72
+ console.error(`Failed to parse expression: ${expr}`, error);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ });
81
+ if (!modified) {
82
+ console.warn("Could not find @Module decorator or imports array");
83
+ return code;
84
+ }
85
+ const output = generate(ast, {
86
+ retainLines: false,
87
+ compact: false
88
+ });
89
+ return output.code;
90
+ }
91
+ async function updateAppModuleFile(filePath, imports, moduleExpressions) {
92
+ let code = await fs.readFile(filePath, "utf-8");
93
+ if (imports.length > 0) {
94
+ code = addImportToFile(code, imports);
95
+ }
96
+ if (moduleExpressions.length > 0) {
97
+ code = addModuleImports(code, moduleExpressions);
98
+ }
99
+ await fs.writeFile(filePath, code, "utf-8");
100
+ }
101
+ export {
102
+ addImportToFile,
103
+ addModuleImports,
104
+ updateAppModuleFile
105
+ };
@@ -0,0 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __export = (target, all) => {
10
13
  for (var name in all)
11
14
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -28,6 +31,117 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
31
  ));
29
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
33
 
34
+ // src/utils/ast-helper.ts
35
+ var ast_helper_exports = {};
36
+ __export(ast_helper_exports, {
37
+ addImportToFile: () => addImportToFile,
38
+ addModuleImports: () => addModuleImports,
39
+ updateAppModuleFile: () => updateAppModuleFile
40
+ });
41
+ function addImportToFile(code, importDeclarations) {
42
+ const ast = parser.parse(code, {
43
+ sourceType: "module",
44
+ plugins: ["typescript", "decorators-legacy"]
45
+ });
46
+ let lastImportIndex = -1;
47
+ (0, import_traverse.default)(ast, {
48
+ ImportDeclaration(path5) {
49
+ const start = path5.node.start;
50
+ if (start !== null && start !== void 0 && start > lastImportIndex) {
51
+ lastImportIndex = start;
52
+ }
53
+ }
54
+ });
55
+ const newImports = importDeclarations.map(({ from, imports }) => {
56
+ const specifiers = imports.map(
57
+ (imp) => t.importSpecifier(t.identifier(imp), t.identifier(imp))
58
+ );
59
+ return t.importDeclaration(specifiers, t.stringLiteral(from));
60
+ });
61
+ if (lastImportIndex >= 0) {
62
+ const program = ast.program;
63
+ const lastImportNode = program.body.find(
64
+ (node) => node.type === "ImportDeclaration" && node.start !== null && node.start === lastImportIndex
65
+ );
66
+ if (lastImportNode) {
67
+ const index = program.body.indexOf(lastImportNode);
68
+ program.body.splice(index + 1, 0, ...newImports);
69
+ }
70
+ } else {
71
+ ast.program.body.unshift(...newImports);
72
+ }
73
+ const output = (0, import_generator.default)(ast, {
74
+ retainLines: false,
75
+ compact: false
76
+ });
77
+ return output.code;
78
+ }
79
+ function addModuleImports(code, moduleExpressions) {
80
+ const ast = parser.parse(code, {
81
+ sourceType: "module",
82
+ plugins: ["typescript", "decorators-legacy"]
83
+ });
84
+ let modified = false;
85
+ (0, import_traverse.default)(ast, {
86
+ Decorator(path5) {
87
+ if (t.isCallExpression(path5.node.expression) && t.isIdentifier(path5.node.expression.callee) && path5.node.expression.callee.name === "Module") {
88
+ const args = path5.node.expression.arguments;
89
+ if (args.length > 0 && t.isObjectExpression(args[0])) {
90
+ const moduleConfig = args[0];
91
+ const importsProperty = moduleConfig.properties.find(
92
+ (prop) => t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === "imports"
93
+ );
94
+ if (importsProperty && t.isObjectProperty(importsProperty)) {
95
+ if (t.isArrayExpression(importsProperty.value)) {
96
+ for (const expr of moduleExpressions) {
97
+ try {
98
+ const exprAst = parser.parseExpression(expr, {
99
+ plugins: ["typescript"]
100
+ });
101
+ importsProperty.value.elements.push(exprAst);
102
+ modified = true;
103
+ } catch (error) {
104
+ console.error(`Failed to parse expression: ${expr}`, error);
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ });
113
+ if (!modified) {
114
+ console.warn("Could not find @Module decorator or imports array");
115
+ return code;
116
+ }
117
+ const output = (0, import_generator.default)(ast, {
118
+ retainLines: false,
119
+ compact: false
120
+ });
121
+ return output.code;
122
+ }
123
+ async function updateAppModuleFile(filePath, imports, moduleExpressions) {
124
+ let code = await import_fs_extra4.default.readFile(filePath, "utf-8");
125
+ if (imports.length > 0) {
126
+ code = addImportToFile(code, imports);
127
+ }
128
+ if (moduleExpressions.length > 0) {
129
+ code = addModuleImports(code, moduleExpressions);
130
+ }
131
+ await import_fs_extra4.default.writeFile(filePath, code, "utf-8");
132
+ }
133
+ var parser, import_traverse, import_generator, t, import_fs_extra4;
134
+ var init_ast_helper = __esm({
135
+ "src/utils/ast-helper.ts"() {
136
+ "use strict";
137
+ parser = __toESM(require("@babel/parser"));
138
+ import_traverse = __toESM(require("@babel/traverse"));
139
+ import_generator = __toESM(require("@babel/generator"));
140
+ t = __toESM(require("@babel/types"));
141
+ import_fs_extra4 = __toESM(require("fs-extra"));
142
+ }
143
+ });
144
+
31
145
  // src/index.ts
32
146
  var index_exports = {};
33
147
  __export(index_exports, {
@@ -40,7 +154,7 @@ var import_commander = require("commander");
40
154
  var import_inquirer = __toESM(require("inquirer"));
41
155
  var import_chalk2 = __toESM(require("chalk"));
42
156
  var import_ora = __toESM(require("ora"));
43
- var import_fs_extra5 = __toESM(require("fs-extra"));
157
+ var import_fs_extra6 = __toESM(require("fs-extra"));
44
158
  var import_path4 = __toESM(require("path"));
45
159
  var import_validate_npm_package_name = __toESM(require("validate-npm-package-name"));
46
160
 
@@ -263,12 +377,12 @@ async function replacePackageNames(directory, config) {
263
377
  }
264
378
  async function findFilesToUpdate(directory) {
265
379
  const files = [];
266
- const traverse = async (dir) => {
380
+ const traverse2 = async (dir) => {
267
381
  const entries = await import_fs_extra2.default.readdir(dir, { withFileTypes: true });
268
382
  for (const entry of entries) {
269
383
  const fullPath = import_path2.default.join(dir, entry.name);
270
384
  if (entry.isDirectory()) {
271
- await traverse(fullPath);
385
+ await traverse2(fullPath);
272
386
  } else if (entry.isFile()) {
273
387
  const ext = import_path2.default.extname(entry.name);
274
388
  const shouldUpdate = [".json", ".ts", ".tsx", ".js", ".jsx", ".md", ".yaml", ".yml", ".env.example", ".tpl"].includes(ext) || entry.name.endsWith(".tpl");
@@ -278,7 +392,7 @@ async function findFilesToUpdate(directory) {
278
392
  }
279
393
  }
280
394
  };
281
- await traverse(directory);
395
+ await traverse2(directory);
282
396
  return files;
283
397
  }
284
398
  async function copyBuiltInTemplates(config) {
@@ -682,17 +796,17 @@ async function initGit(projectName) {
682
796
  }
683
797
 
684
798
  // src/utils/features.ts
685
- var import_fs_extra4 = __toESM(require("fs-extra"));
799
+ var import_fs_extra5 = __toESM(require("fs-extra"));
686
800
  var import_path3 = __toESM(require("path"));
687
801
  async function loadFeaturesConfig() {
688
802
  const configPath = import_path3.default.join(__dirname, "../features.json");
689
- if (!import_fs_extra4.default.existsSync(configPath)) {
803
+ if (!import_fs_extra5.default.existsSync(configPath)) {
690
804
  const devPath = import_path3.default.join(__dirname, "../../features.json");
691
- if (import_fs_extra4.default.existsSync(devPath)) {
692
- return await import_fs_extra4.default.readJSON(devPath);
805
+ if (import_fs_extra5.default.existsSync(devPath)) {
806
+ return await import_fs_extra5.default.readJSON(devPath);
693
807
  }
694
808
  }
695
- return await import_fs_extra4.default.readJSON(configPath);
809
+ return await import_fs_extra5.default.readJSON(configPath);
696
810
  }
697
811
  function getFeatureChoices(config) {
698
812
  return Object.entries(config.features).map(([key, feature]) => ({
@@ -729,21 +843,53 @@ function collectEnvVars(features, config) {
729
843
  }
730
844
  async function generateEnvExample(features, config, targetPath) {
731
845
  const envVars = collectEnvVars(features, config);
846
+ if (envVars.length === 0) {
847
+ return;
848
+ }
732
849
  const content = [
850
+ "# ========================================",
733
851
  "# Environment Variables",
852
+ "# ========================================",
734
853
  "# Copy this file to .env and fill in the values",
854
+ "#",
855
+ "# IMPORTANT: Never commit .env file to version control!",
856
+ "# Add .env to your .gitignore file",
857
+ "#",
735
858
  "",
736
- ...envVars.map((envVar) => {
737
- const lines = [];
859
+ "# ========================================",
860
+ "# Application Configuration",
861
+ "# ========================================",
862
+ "NODE_ENV=development",
863
+ "PORT=3000",
864
+ "",
865
+ "# ========================================",
866
+ "# Database Configuration",
867
+ "# ========================================",
868
+ "DATABASE_URL=mysql://root:root123456@localhost:3306/{{PROJECT_NAME}}",
869
+ ""
870
+ ];
871
+ const featureGroups = {};
872
+ for (const featureKey of features) {
873
+ const feature = config.features[featureKey];
874
+ if (feature && feature.envVars.length > 0) {
875
+ featureGroups[feature.name] = feature.envVars;
876
+ }
877
+ }
878
+ for (const [featureName, vars] of Object.entries(featureGroups)) {
879
+ content.push("# ========================================");
880
+ content.push(`# ${featureName} Configuration`);
881
+ content.push("# ========================================");
882
+ for (const envVar of vars) {
738
883
  if (envVar.description) {
739
- lines.push(`# ${envVar.description}`);
884
+ content.push(`# ${envVar.description}`);
740
885
  }
741
- lines.push(`${envVar.key}=${envVar.default}`);
742
- lines.push("");
743
- return lines.join("\n");
744
- })
745
- ].join("\n");
746
- await import_fs_extra4.default.writeFile(import_path3.default.join(targetPath, "apps/backend/.env.example"), content);
886
+ content.push(`${envVar.key}=${envVar.default}`);
887
+ content.push("");
888
+ }
889
+ }
890
+ const envPath = import_path3.default.join(targetPath, "apps/backend/.env.example");
891
+ await import_fs_extra5.default.ensureDir(import_path3.default.dirname(envPath));
892
+ await import_fs_extra5.default.writeFile(envPath, content.join("\n"));
747
893
  logger.info("Generated .env.example");
748
894
  }
749
895
  async function copyConfigFiles(features, config, templateDir, targetPath) {
@@ -753,9 +899,9 @@ async function copyConfigFiles(features, config, templateDir, targetPath) {
753
899
  for (const configFile of feature.configFiles) {
754
900
  const sourcePath = import_path3.default.join(templateDir, configFile.template);
755
901
  const destPath = import_path3.default.join(targetPath, configFile.path);
756
- if (await import_fs_extra4.default.pathExists(sourcePath)) {
757
- await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
758
- await import_fs_extra4.default.copy(sourcePath, destPath);
902
+ if (await import_fs_extra5.default.pathExists(sourcePath)) {
903
+ await import_fs_extra5.default.ensureDir(import_path3.default.dirname(destPath));
904
+ await import_fs_extra5.default.copy(sourcePath, destPath);
759
905
  logger.info(`Copied config: ${configFile.path}`);
760
906
  } else {
761
907
  logger.warn(`Config template not found: ${sourcePath}`);
@@ -770,9 +916,9 @@ async function copyExampleFiles(features, config, templateDir, targetPath) {
770
916
  if (feature && feature.exampleFiles) {
771
917
  const sourcePath = import_path3.default.join(templateDir, feature.exampleFiles.source);
772
918
  const destPath = import_path3.default.join(targetPath, feature.exampleFiles.target);
773
- if (await import_fs_extra4.default.pathExists(sourcePath)) {
774
- await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
775
- await import_fs_extra4.default.copy(sourcePath, destPath);
919
+ if (await import_fs_extra5.default.pathExists(sourcePath)) {
920
+ await import_fs_extra5.default.ensureDir(import_path3.default.dirname(destPath));
921
+ await import_fs_extra5.default.copy(sourcePath, destPath);
776
922
  logger.info(`Copied examples: ${feature.exampleFiles.target}`);
777
923
  } else {
778
924
  logger.warn(`Example template not found: ${sourcePath}`);
@@ -782,11 +928,11 @@ async function copyExampleFiles(features, config, templateDir, targetPath) {
782
928
  }
783
929
  async function copySkillFiles(features, config, templateDir, targetPath) {
784
930
  const skillsDir = import_path3.default.join(targetPath, ".kiro/skills");
785
- await import_fs_extra4.default.ensureDir(skillsDir);
931
+ await import_fs_extra5.default.ensureDir(skillsDir);
786
932
  const baseSkillSource = import_path3.default.join(templateDir, "skills/base.skill.md");
787
933
  const baseSkillDest = import_path3.default.join(skillsDir, "project-capabilities.md");
788
- if (await import_fs_extra4.default.pathExists(baseSkillSource)) {
789
- await import_fs_extra4.default.copy(baseSkillSource, baseSkillDest);
934
+ if (await import_fs_extra5.default.pathExists(baseSkillSource)) {
935
+ await import_fs_extra5.default.copy(baseSkillSource, baseSkillDest);
790
936
  logger.info("Copied base skill file");
791
937
  } else {
792
938
  logger.warn(`Base skill template not found: ${baseSkillSource}`);
@@ -796,9 +942,9 @@ async function copySkillFiles(features, config, templateDir, targetPath) {
796
942
  if (feature && feature.skillFile) {
797
943
  const sourcePath = import_path3.default.join(templateDir, feature.skillFile.template);
798
944
  const destPath = import_path3.default.join(targetPath, feature.skillFile.target);
799
- if (await import_fs_extra4.default.pathExists(sourcePath)) {
800
- await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
801
- await import_fs_extra4.default.copy(sourcePath, destPath);
945
+ if (await import_fs_extra5.default.pathExists(sourcePath)) {
946
+ await import_fs_extra5.default.ensureDir(import_path3.default.dirname(destPath));
947
+ await import_fs_extra5.default.copy(sourcePath, destPath);
802
948
  logger.info(`Copied skill: ${feature.skillFile.target}`);
803
949
  } else {
804
950
  logger.warn(`Skill template not found: ${sourcePath}`);
@@ -843,29 +989,39 @@ ${featuresList}
843
989
  - GitHub\uFF1Ahttps://github.com/751848178/svton
844
990
  `;
845
991
  const indexPath = import_path3.default.join(targetPath, ".kiro/skills/project-capabilities.md");
846
- await import_fs_extra4.default.writeFile(indexPath, content);
992
+ await import_fs_extra5.default.writeFile(indexPath, content);
847
993
  logger.info("Generated capabilities index");
848
994
  }
849
995
  async function updatePackageJson(features, config, targetPath) {
850
996
  const packageJsonPath = import_path3.default.join(targetPath, "apps/backend/package.json");
851
- const packageJson = await import_fs_extra4.default.readJSON(packageJsonPath);
997
+ const packageJson = await import_fs_extra5.default.readJSON(packageJsonPath);
852
998
  const dependencies = collectDependencies(features, config);
853
999
  packageJson.dependencies = {
854
1000
  ...packageJson.dependencies,
855
1001
  ...dependencies
856
1002
  };
857
- await import_fs_extra4.default.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
1003
+ await import_fs_extra5.default.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
858
1004
  logger.info("Updated package.json with feature dependencies");
859
1005
  }
860
1006
  function generateModuleImports(features, config) {
861
1007
  const imports = [];
1008
+ const seen = /* @__PURE__ */ new Set();
862
1009
  for (const featureKey of features) {
863
1010
  const feature = config.features[featureKey];
864
1011
  if (feature && feature.moduleImports) {
865
1012
  for (const moduleImport of feature.moduleImports) {
866
- imports.push(`import { ${moduleImport.import} } from '${moduleImport.from}';`);
1013
+ const importKey = `${moduleImport.from}:${moduleImport.import}`;
1014
+ if (!seen.has(importKey)) {
1015
+ imports.push(`import { ${moduleImport.import} } from '${moduleImport.from}';`);
1016
+ seen.add(importKey);
1017
+ }
867
1018
  }
868
1019
  }
1020
+ if (feature && feature.configFiles && feature.configFiles.length > 0) {
1021
+ const configFileName = import_path3.default.basename(feature.configFiles[0].path, ".ts");
1022
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
1023
+ imports.push(`import { ${configFunctionName} } from './config/${configFileName}';`);
1024
+ }
869
1025
  }
870
1026
  return imports.join("\n");
871
1027
  }
@@ -874,40 +1030,114 @@ function generateModuleRegistrations(features, config) {
874
1030
  for (const featureKey of features) {
875
1031
  const feature = config.features[featureKey];
876
1032
  if (feature && feature.moduleRegistration) {
877
- const { module: moduleName, config: moduleConfig } = feature.moduleRegistration;
878
- registrations.push(` ${moduleName}.${feature.moduleRegistration.type}({
879
- useFactory: (configService: ConfigService) => ${moduleConfig},
1033
+ const { module: moduleName, type: registrationType } = feature.moduleRegistration;
1034
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
1035
+ registrations.push(` ${moduleName}.${registrationType}({
1036
+ imports: [ConfigModule],
880
1037
  inject: [ConfigService],
1038
+ useFactory: (configService: ConfigService) => ${configFunctionName}(configService),
881
1039
  }),`);
882
1040
  }
883
1041
  }
884
1042
  return registrations.join("\n");
885
1043
  }
886
1044
  async function updateAppModule(features, config, targetPath) {
1045
+ if (features.length === 0) {
1046
+ return;
1047
+ }
887
1048
  const appModulePath = import_path3.default.join(targetPath, "apps/backend/src/app.module.ts");
888
- if (!await import_fs_extra4.default.pathExists(appModulePath)) {
1049
+ if (!await import_fs_extra5.default.pathExists(appModulePath)) {
889
1050
  logger.warn("app.module.ts not found, skipping module injection");
890
1051
  return;
891
1052
  }
892
- let content = await import_fs_extra4.default.readFile(appModulePath, "utf-8");
1053
+ try {
1054
+ const imports = [];
1055
+ const moduleExpressions = [];
1056
+ const needsConfigService = features.some((key) => {
1057
+ const feature = config.features[key];
1058
+ return feature && feature.moduleRegistration;
1059
+ });
1060
+ if (needsConfigService) {
1061
+ }
1062
+ for (const featureKey of features) {
1063
+ const feature = config.features[featureKey];
1064
+ if (!feature) continue;
1065
+ if (feature.moduleImports) {
1066
+ for (const moduleImport of feature.moduleImports) {
1067
+ imports.push({
1068
+ from: moduleImport.from,
1069
+ imports: [moduleImport.import]
1070
+ });
1071
+ }
1072
+ }
1073
+ if (feature.configFiles && feature.configFiles.length > 0) {
1074
+ const configFileName = import_path3.default.basename(feature.configFiles[0].path, ".ts");
1075
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
1076
+ imports.push({
1077
+ from: `./config/${configFileName}`,
1078
+ imports: [configFunctionName]
1079
+ });
1080
+ }
1081
+ if (feature.moduleRegistration) {
1082
+ const { module: moduleName, type: registrationType } = feature.moduleRegistration;
1083
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
1084
+ const moduleExpression = `${moduleName}.${registrationType}({
1085
+ imports: [ConfigModule],
1086
+ inject: [ConfigService],
1087
+ useFactory: (configService: ConfigService) => ${configFunctionName}(configService),
1088
+ })`;
1089
+ moduleExpressions.push(moduleExpression);
1090
+ }
1091
+ }
1092
+ const { updateAppModuleFile: updateAppModuleFile2 } = await Promise.resolve().then(() => (init_ast_helper(), ast_helper_exports));
1093
+ await updateAppModuleFile2(appModulePath, imports, moduleExpressions);
1094
+ logger.info("Successfully updated app.module.ts with feature modules");
1095
+ } catch (error) {
1096
+ logger.error(`Failed to update app.module.ts: ${error instanceof Error ? error.message : String(error)}`);
1097
+ await generateManualIntegrationGuide(features, config, targetPath);
1098
+ }
1099
+ }
1100
+ async function generateManualIntegrationGuide(features, config, targetPath) {
893
1101
  const imports = generateModuleImports(features, config);
894
1102
  const registrations = generateModuleRegistrations(features, config);
895
- const importsMatch = content.match(/imports:\s*\[([\s\S]*?)\]/);
896
- if (importsMatch) {
897
- const existingImports = importsMatch[1];
898
- const newImports = `${existingImports}
899
- ${registrations}`;
900
- content = content.replace(
901
- /imports:\s*\[([\s\S]*?)\]/,
902
- `imports: [${newImports}
903
- ]`
904
- );
905
- }
906
- const lastImportIndex = content.lastIndexOf("import ");
907
- const lastImportEnd = content.indexOf("\n", lastImportIndex);
908
- content = content.slice(0, lastImportEnd + 1) + imports + "\n" + content.slice(lastImportEnd + 1);
909
- await import_fs_extra4.default.writeFile(appModulePath, content);
910
- logger.info("Updated app.module.ts with feature modules");
1103
+ const content = `# \u529F\u80FD\u6A21\u5757\u96C6\u6210\u8BF4\u660E
1104
+
1105
+ \u26A0\uFE0F \u81EA\u52A8\u96C6\u6210\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u5B8C\u6210\u4EE5\u4E0B\u6B65\u9AA4\uFF1A
1106
+
1107
+ ## 1. \u6DFB\u52A0\u5BFC\u5165\u8BED\u53E5
1108
+
1109
+ \u5728 \`apps/backend/src/app.module.ts\` \u6587\u4EF6\u9876\u90E8\u6DFB\u52A0\u4EE5\u4E0B\u5BFC\u5165\uFF1A
1110
+
1111
+ \`\`\`typescript
1112
+ ${imports}
1113
+ \`\`\`
1114
+
1115
+ ## 2. \u6CE8\u518C\u6A21\u5757
1116
+
1117
+ \u5728 \`@Module\` \u88C5\u9970\u5668\u7684 \`imports\` \u6570\u7EC4\u4E2D\u6DFB\u52A0\u4EE5\u4E0B\u6A21\u5757\uFF1A
1118
+
1119
+ \`\`\`typescript
1120
+ @Module({
1121
+ imports: [
1122
+ // ... \u5176\u4ED6\u6A21\u5757
1123
+ ${registrations}
1124
+ ],
1125
+ // ...
1126
+ })
1127
+ export class AppModule {}
1128
+ \`\`\`
1129
+
1130
+ ## 3. \u914D\u7F6E\u6587\u4EF6
1131
+
1132
+ \u6BCF\u4E2A\u529F\u80FD\u7684\u914D\u7F6E\u6587\u4EF6\u5DF2\u751F\u6210\u5728 \`apps/backend/src/config/\` \u76EE\u5F55\u4E0B\u3002
1133
+
1134
+ ## 4. \u73AF\u5883\u53D8\u91CF
1135
+
1136
+ \u8BF7\u590D\u5236 \`.env.example\` \u4E3A \`.env\` \u5E76\u586B\u5199\u76F8\u5E94\u7684\u914D\u7F6E\u503C\u3002
1137
+ `;
1138
+ const docPath = import_path3.default.join(targetPath, "apps/backend/FEATURE_INTEGRATION.md");
1139
+ await import_fs_extra5.default.writeFile(docPath, content);
1140
+ logger.warn("Generated manual integration guide: apps/backend/FEATURE_INTEGRATION.md");
911
1141
  }
912
1142
 
913
1143
  // src/commands/create.ts
@@ -922,7 +1152,7 @@ async function createProject(projectName, options = {}) {
922
1152
  process.exit(1);
923
1153
  }
924
1154
  const projectPath = import_path4.default.resolve(process.cwd(), projectName);
925
- if (await import_fs_extra5.default.pathExists(projectPath)) {
1155
+ if (await import_fs_extra6.default.pathExists(projectPath)) {
926
1156
  logger.error(`Directory ${projectName} already exists!`);
927
1157
  process.exit(1);
928
1158
  }
@@ -1055,7 +1285,7 @@ async function getTemplateDirectory() {
1055
1285
  const cliPackageRoot = import_path4.default.dirname(__dirname);
1056
1286
  const frameworkRoot = import_path4.default.dirname(import_path4.default.dirname(cliPackageRoot));
1057
1287
  const localTemplateDir = import_path4.default.join(frameworkRoot, "templates");
1058
- if (await import_fs_extra5.default.pathExists(localTemplateDir)) {
1288
+ if (await import_fs_extra6.default.pathExists(localTemplateDir)) {
1059
1289
  logger.debug(`Using local template directory: ${localTemplateDir}`);
1060
1290
  return localTemplateDir;
1061
1291
  }
@@ -1072,7 +1302,7 @@ async function createProjectFromTemplate(config) {
1072
1302
  const spinner = (0, import_ora.default)("Creating project...").start();
1073
1303
  let templateDirToCleanup = null;
1074
1304
  try {
1075
- await import_fs_extra5.default.ensureDir(config.projectPath);
1305
+ await import_fs_extra6.default.ensureDir(config.projectPath);
1076
1306
  process.chdir(config.projectPath);
1077
1307
  spinner.text = "Generating project files...";
1078
1308
  await generateFromTemplate(config);
@@ -1115,7 +1345,7 @@ async function createProjectFromTemplate(config) {
1115
1345
  }
1116
1346
 
1117
1347
  // package.json
1118
- var version = "1.2.5";
1348
+ var version = "1.4.0";
1119
1349
 
1120
1350
  // src/index.ts
1121
1351
  async function cli() {
package/dist/index.mjs CHANGED
@@ -1,10 +1,7 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
2
+ import {
3
+ __require
4
+ } from "./chunk-Y6FXYEAI.mjs";
8
5
 
9
6
  // src/index.ts
10
7
  import { Command } from "commander";
@@ -702,21 +699,53 @@ function collectEnvVars(features, config) {
702
699
  }
703
700
  async function generateEnvExample(features, config, targetPath) {
704
701
  const envVars = collectEnvVars(features, config);
702
+ if (envVars.length === 0) {
703
+ return;
704
+ }
705
705
  const content = [
706
+ "# ========================================",
706
707
  "# Environment Variables",
708
+ "# ========================================",
707
709
  "# Copy this file to .env and fill in the values",
710
+ "#",
711
+ "# IMPORTANT: Never commit .env file to version control!",
712
+ "# Add .env to your .gitignore file",
713
+ "#",
714
+ "",
715
+ "# ========================================",
716
+ "# Application Configuration",
717
+ "# ========================================",
718
+ "NODE_ENV=development",
719
+ "PORT=3000",
708
720
  "",
709
- ...envVars.map((envVar) => {
710
- const lines = [];
721
+ "# ========================================",
722
+ "# Database Configuration",
723
+ "# ========================================",
724
+ "DATABASE_URL=mysql://root:root123456@localhost:3306/{{PROJECT_NAME}}",
725
+ ""
726
+ ];
727
+ const featureGroups = {};
728
+ for (const featureKey of features) {
729
+ const feature = config.features[featureKey];
730
+ if (feature && feature.envVars.length > 0) {
731
+ featureGroups[feature.name] = feature.envVars;
732
+ }
733
+ }
734
+ for (const [featureName, vars] of Object.entries(featureGroups)) {
735
+ content.push("# ========================================");
736
+ content.push(`# ${featureName} Configuration`);
737
+ content.push("# ========================================");
738
+ for (const envVar of vars) {
711
739
  if (envVar.description) {
712
- lines.push(`# ${envVar.description}`);
740
+ content.push(`# ${envVar.description}`);
713
741
  }
714
- lines.push(`${envVar.key}=${envVar.default}`);
715
- lines.push("");
716
- return lines.join("\n");
717
- })
718
- ].join("\n");
719
- await fs4.writeFile(path3.join(targetPath, "apps/backend/.env.example"), content);
742
+ content.push(`${envVar.key}=${envVar.default}`);
743
+ content.push("");
744
+ }
745
+ }
746
+ const envPath = path3.join(targetPath, "apps/backend/.env.example");
747
+ await fs4.ensureDir(path3.dirname(envPath));
748
+ await fs4.writeFile(envPath, content.join("\n"));
720
749
  logger.info("Generated .env.example");
721
750
  }
722
751
  async function copyConfigFiles(features, config, templateDir, targetPath) {
@@ -832,13 +861,23 @@ async function updatePackageJson(features, config, targetPath) {
832
861
  }
833
862
  function generateModuleImports(features, config) {
834
863
  const imports = [];
864
+ const seen = /* @__PURE__ */ new Set();
835
865
  for (const featureKey of features) {
836
866
  const feature = config.features[featureKey];
837
867
  if (feature && feature.moduleImports) {
838
868
  for (const moduleImport of feature.moduleImports) {
839
- imports.push(`import { ${moduleImport.import} } from '${moduleImport.from}';`);
869
+ const importKey = `${moduleImport.from}:${moduleImport.import}`;
870
+ if (!seen.has(importKey)) {
871
+ imports.push(`import { ${moduleImport.import} } from '${moduleImport.from}';`);
872
+ seen.add(importKey);
873
+ }
840
874
  }
841
875
  }
876
+ if (feature && feature.configFiles && feature.configFiles.length > 0) {
877
+ const configFileName = path3.basename(feature.configFiles[0].path, ".ts");
878
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
879
+ imports.push(`import { ${configFunctionName} } from './config/${configFileName}';`);
880
+ }
842
881
  }
843
882
  return imports.join("\n");
844
883
  }
@@ -847,40 +886,114 @@ function generateModuleRegistrations(features, config) {
847
886
  for (const featureKey of features) {
848
887
  const feature = config.features[featureKey];
849
888
  if (feature && feature.moduleRegistration) {
850
- const { module: moduleName, config: moduleConfig } = feature.moduleRegistration;
851
- registrations.push(` ${moduleName}.${feature.moduleRegistration.type}({
852
- useFactory: (configService: ConfigService) => ${moduleConfig},
889
+ const { module: moduleName, type: registrationType } = feature.moduleRegistration;
890
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
891
+ registrations.push(` ${moduleName}.${registrationType}({
892
+ imports: [ConfigModule],
853
893
  inject: [ConfigService],
894
+ useFactory: (configService: ConfigService) => ${configFunctionName}(configService),
854
895
  }),`);
855
896
  }
856
897
  }
857
898
  return registrations.join("\n");
858
899
  }
859
900
  async function updateAppModule(features, config, targetPath) {
901
+ if (features.length === 0) {
902
+ return;
903
+ }
860
904
  const appModulePath = path3.join(targetPath, "apps/backend/src/app.module.ts");
861
905
  if (!await fs4.pathExists(appModulePath)) {
862
906
  logger.warn("app.module.ts not found, skipping module injection");
863
907
  return;
864
908
  }
865
- let content = await fs4.readFile(appModulePath, "utf-8");
909
+ try {
910
+ const imports = [];
911
+ const moduleExpressions = [];
912
+ const needsConfigService = features.some((key) => {
913
+ const feature = config.features[key];
914
+ return feature && feature.moduleRegistration;
915
+ });
916
+ if (needsConfigService) {
917
+ }
918
+ for (const featureKey of features) {
919
+ const feature = config.features[featureKey];
920
+ if (!feature) continue;
921
+ if (feature.moduleImports) {
922
+ for (const moduleImport of feature.moduleImports) {
923
+ imports.push({
924
+ from: moduleImport.from,
925
+ imports: [moduleImport.import]
926
+ });
927
+ }
928
+ }
929
+ if (feature.configFiles && feature.configFiles.length > 0) {
930
+ const configFileName = path3.basename(feature.configFiles[0].path, ".ts");
931
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
932
+ imports.push({
933
+ from: `./config/${configFileName}`,
934
+ imports: [configFunctionName]
935
+ });
936
+ }
937
+ if (feature.moduleRegistration) {
938
+ const { module: moduleName, type: registrationType } = feature.moduleRegistration;
939
+ const configFunctionName = `use${featureKey.charAt(0).toUpperCase() + featureKey.slice(1)}Config`;
940
+ const moduleExpression = `${moduleName}.${registrationType}({
941
+ imports: [ConfigModule],
942
+ inject: [ConfigService],
943
+ useFactory: (configService: ConfigService) => ${configFunctionName}(configService),
944
+ })`;
945
+ moduleExpressions.push(moduleExpression);
946
+ }
947
+ }
948
+ const { updateAppModuleFile } = await import("./ast-helper-5I5LPFKU.mjs");
949
+ await updateAppModuleFile(appModulePath, imports, moduleExpressions);
950
+ logger.info("Successfully updated app.module.ts with feature modules");
951
+ } catch (error) {
952
+ logger.error(`Failed to update app.module.ts: ${error instanceof Error ? error.message : String(error)}`);
953
+ await generateManualIntegrationGuide(features, config, targetPath);
954
+ }
955
+ }
956
+ async function generateManualIntegrationGuide(features, config, targetPath) {
866
957
  const imports = generateModuleImports(features, config);
867
958
  const registrations = generateModuleRegistrations(features, config);
868
- const importsMatch = content.match(/imports:\s*\[([\s\S]*?)\]/);
869
- if (importsMatch) {
870
- const existingImports = importsMatch[1];
871
- const newImports = `${existingImports}
872
- ${registrations}`;
873
- content = content.replace(
874
- /imports:\s*\[([\s\S]*?)\]/,
875
- `imports: [${newImports}
876
- ]`
877
- );
878
- }
879
- const lastImportIndex = content.lastIndexOf("import ");
880
- const lastImportEnd = content.indexOf("\n", lastImportIndex);
881
- content = content.slice(0, lastImportEnd + 1) + imports + "\n" + content.slice(lastImportEnd + 1);
882
- await fs4.writeFile(appModulePath, content);
883
- logger.info("Updated app.module.ts with feature modules");
959
+ const content = `# \u529F\u80FD\u6A21\u5757\u96C6\u6210\u8BF4\u660E
960
+
961
+ \u26A0\uFE0F \u81EA\u52A8\u96C6\u6210\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u5B8C\u6210\u4EE5\u4E0B\u6B65\u9AA4\uFF1A
962
+
963
+ ## 1. \u6DFB\u52A0\u5BFC\u5165\u8BED\u53E5
964
+
965
+ \u5728 \`apps/backend/src/app.module.ts\` \u6587\u4EF6\u9876\u90E8\u6DFB\u52A0\u4EE5\u4E0B\u5BFC\u5165\uFF1A
966
+
967
+ \`\`\`typescript
968
+ ${imports}
969
+ \`\`\`
970
+
971
+ ## 2. \u6CE8\u518C\u6A21\u5757
972
+
973
+ \u5728 \`@Module\` \u88C5\u9970\u5668\u7684 \`imports\` \u6570\u7EC4\u4E2D\u6DFB\u52A0\u4EE5\u4E0B\u6A21\u5757\uFF1A
974
+
975
+ \`\`\`typescript
976
+ @Module({
977
+ imports: [
978
+ // ... \u5176\u4ED6\u6A21\u5757
979
+ ${registrations}
980
+ ],
981
+ // ...
982
+ })
983
+ export class AppModule {}
984
+ \`\`\`
985
+
986
+ ## 3. \u914D\u7F6E\u6587\u4EF6
987
+
988
+ \u6BCF\u4E2A\u529F\u80FD\u7684\u914D\u7F6E\u6587\u4EF6\u5DF2\u751F\u6210\u5728 \`apps/backend/src/config/\` \u76EE\u5F55\u4E0B\u3002
989
+
990
+ ## 4. \u73AF\u5883\u53D8\u91CF
991
+
992
+ \u8BF7\u590D\u5236 \`.env.example\` \u4E3A \`.env\` \u5E76\u586B\u5199\u76F8\u5E94\u7684\u914D\u7F6E\u503C\u3002
993
+ `;
994
+ const docPath = path3.join(targetPath, "apps/backend/FEATURE_INTEGRATION.md");
995
+ await fs4.writeFile(docPath, content);
996
+ logger.warn("Generated manual integration guide: apps/backend/FEATURE_INTEGRATION.md");
884
997
  }
885
998
 
886
999
  // src/commands/create.ts
@@ -1088,7 +1201,7 @@ async function createProjectFromTemplate(config) {
1088
1201
  }
1089
1202
 
1090
1203
  // package.json
1091
- var version = "1.2.5";
1204
+ var version = "1.4.0";
1092
1205
 
1093
1206
  // src/index.ts
1094
1207
  async function cli() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svton/cli",
3
- "version": "1.2.5",
3
+ "version": "1.4.0",
4
4
  "description": "Svton CLI - Create full-stack applications with NestJS, Next.js, and Taro",
5
5
  "keywords": [
6
6
  "cli",
@@ -47,16 +47,22 @@
47
47
  "node": ">=18.0.0"
48
48
  },
49
49
  "dependencies": {
50
- "commander": "^11.1.0",
51
- "inquirer": "^9.2.12",
50
+ "@babel/generator": "^7.29.0",
51
+ "@babel/parser": "^7.29.0",
52
+ "@babel/traverse": "^7.29.0",
53
+ "@babel/types": "^7.29.0",
52
54
  "chalk": "^5.3.0",
53
- "ora": "^8.0.1",
55
+ "commander": "^11.1.0",
54
56
  "fs-extra": "^11.2.0",
57
+ "inquirer": "^9.2.12",
55
58
  "mustache": "^4.2.0",
59
+ "ora": "^8.0.1",
56
60
  "validate-npm-package-name": "^5.0.0"
57
61
  },
58
62
  "devDependencies": {
59
63
  "@changesets/cli": "^2.29.8",
64
+ "@types/babel__generator": "^7.27.0",
65
+ "@types/babel__traverse": "^7.28.0",
60
66
  "@types/fs-extra": "^11.0.4",
61
67
  "@types/inquirer": "^9.0.7",
62
68
  "@types/mustache": "^4.2.5",