@svton/cli 1.1.0 → 1.2.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.
- package/dist/index.js +246 -6
- package/dist/index.mjs +246 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -40,8 +40,8 @@ var import_commander = require("commander");
|
|
|
40
40
|
var import_inquirer = __toESM(require("inquirer"));
|
|
41
41
|
var import_chalk2 = __toESM(require("chalk"));
|
|
42
42
|
var import_ora = __toESM(require("ora"));
|
|
43
|
-
var
|
|
44
|
-
var
|
|
43
|
+
var import_fs_extra5 = __toESM(require("fs-extra"));
|
|
44
|
+
var import_path4 = __toESM(require("path"));
|
|
45
45
|
var import_validate_npm_package_name = __toESM(require("validate-npm-package-name"));
|
|
46
46
|
|
|
47
47
|
// src/utils/template.ts
|
|
@@ -681,6 +681,219 @@ async function initGit(projectName) {
|
|
|
681
681
|
}
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
+
// src/utils/features.ts
|
|
685
|
+
var import_fs_extra4 = __toESM(require("fs-extra"));
|
|
686
|
+
var import_path3 = __toESM(require("path"));
|
|
687
|
+
async function loadFeaturesConfig() {
|
|
688
|
+
const configPath = import_path3.default.join(__dirname, "../../features.json");
|
|
689
|
+
return await import_fs_extra4.default.readJSON(configPath);
|
|
690
|
+
}
|
|
691
|
+
function getFeatureChoices(config) {
|
|
692
|
+
return Object.entries(config.features).map(([key, feature]) => ({
|
|
693
|
+
name: `${feature.name} - ${feature.description}`,
|
|
694
|
+
value: key,
|
|
695
|
+
checked: false
|
|
696
|
+
}));
|
|
697
|
+
}
|
|
698
|
+
function collectDependencies(features, config) {
|
|
699
|
+
const dependencies = {};
|
|
700
|
+
for (const featureKey of features) {
|
|
701
|
+
const feature = config.features[featureKey];
|
|
702
|
+
if (feature) {
|
|
703
|
+
Object.assign(dependencies, feature.packages.dependencies);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return dependencies;
|
|
707
|
+
}
|
|
708
|
+
function collectEnvVars(features, config) {
|
|
709
|
+
const envVars = [];
|
|
710
|
+
const seen = /* @__PURE__ */ new Set();
|
|
711
|
+
for (const featureKey of features) {
|
|
712
|
+
const feature = config.features[featureKey];
|
|
713
|
+
if (feature) {
|
|
714
|
+
for (const envVar of feature.envVars) {
|
|
715
|
+
if (!seen.has(envVar.key)) {
|
|
716
|
+
envVars.push(envVar);
|
|
717
|
+
seen.add(envVar.key);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return envVars;
|
|
723
|
+
}
|
|
724
|
+
async function generateEnvExample(features, config, targetPath) {
|
|
725
|
+
const envVars = collectEnvVars(features, config);
|
|
726
|
+
const content = [
|
|
727
|
+
"# Environment Variables",
|
|
728
|
+
"# Copy this file to .env and fill in the values",
|
|
729
|
+
"",
|
|
730
|
+
...envVars.map((envVar) => {
|
|
731
|
+
const lines = [];
|
|
732
|
+
if (envVar.description) {
|
|
733
|
+
lines.push(`# ${envVar.description}`);
|
|
734
|
+
}
|
|
735
|
+
lines.push(`${envVar.key}=${envVar.default}`);
|
|
736
|
+
lines.push("");
|
|
737
|
+
return lines.join("\n");
|
|
738
|
+
})
|
|
739
|
+
].join("\n");
|
|
740
|
+
await import_fs_extra4.default.writeFile(import_path3.default.join(targetPath, ".env.example"), content);
|
|
741
|
+
logger.info("Generated .env.example");
|
|
742
|
+
}
|
|
743
|
+
async function copyConfigFiles(features, config, templatePath, targetPath) {
|
|
744
|
+
for (const featureKey of features) {
|
|
745
|
+
const feature = config.features[featureKey];
|
|
746
|
+
if (feature && feature.configFiles) {
|
|
747
|
+
for (const configFile of feature.configFiles) {
|
|
748
|
+
const sourcePath = import_path3.default.join(templatePath, configFile.template);
|
|
749
|
+
const destPath = import_path3.default.join(targetPath, configFile.path);
|
|
750
|
+
await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
|
|
751
|
+
await import_fs_extra4.default.copy(sourcePath, destPath);
|
|
752
|
+
logger.info(`Copied config: ${configFile.path}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
async function copyExampleFiles(features, config, templatePath, targetPath) {
|
|
758
|
+
for (const featureKey of features) {
|
|
759
|
+
const feature = config.features[featureKey];
|
|
760
|
+
if (feature && feature.exampleFiles) {
|
|
761
|
+
const sourcePath = import_path3.default.join(templatePath, feature.exampleFiles.source);
|
|
762
|
+
const destPath = import_path3.default.join(targetPath, feature.exampleFiles.target);
|
|
763
|
+
if (await import_fs_extra4.default.pathExists(sourcePath)) {
|
|
764
|
+
await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
|
|
765
|
+
await import_fs_extra4.default.copy(sourcePath, destPath);
|
|
766
|
+
logger.info(`Copied examples: ${feature.exampleFiles.target}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
async function copySkillFiles(features, config, templatePath, targetPath) {
|
|
772
|
+
const skillsDir = import_path3.default.join(targetPath, ".kiro/skills");
|
|
773
|
+
await import_fs_extra4.default.ensureDir(skillsDir);
|
|
774
|
+
const baseSkillSource = import_path3.default.join(templatePath, "skills/base.skill.md");
|
|
775
|
+
const baseSkillDest = import_path3.default.join(skillsDir, "project-capabilities.md");
|
|
776
|
+
if (await import_fs_extra4.default.pathExists(baseSkillSource)) {
|
|
777
|
+
await import_fs_extra4.default.copy(baseSkillSource, baseSkillDest);
|
|
778
|
+
logger.info("Copied base skill file");
|
|
779
|
+
}
|
|
780
|
+
for (const featureKey of features) {
|
|
781
|
+
const feature = config.features[featureKey];
|
|
782
|
+
if (feature && feature.skillFile) {
|
|
783
|
+
const sourcePath = import_path3.default.join(templatePath, feature.skillFile.template);
|
|
784
|
+
const destPath = import_path3.default.join(targetPath, feature.skillFile.target);
|
|
785
|
+
if (await import_fs_extra4.default.pathExists(sourcePath)) {
|
|
786
|
+
await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
|
|
787
|
+
await import_fs_extra4.default.copy(sourcePath, destPath);
|
|
788
|
+
logger.info(`Copied skill: ${feature.skillFile.target}`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
await generateCapabilitiesIndex(features, config, targetPath);
|
|
793
|
+
}
|
|
794
|
+
async function generateCapabilitiesIndex(features, config, targetPath) {
|
|
795
|
+
const featuresList = features.map((featureKey) => {
|
|
796
|
+
const feature = config.features[featureKey];
|
|
797
|
+
if (!feature) return "";
|
|
798
|
+
const packages = Object.keys(feature.packages.dependencies).join(", ");
|
|
799
|
+
return `### ${feature.name}
|
|
800
|
+
|
|
801
|
+
${feature.description}
|
|
802
|
+
|
|
803
|
+
- \u{1F4E6} \u5305\uFF1A${packages}
|
|
804
|
+
- \u{1F4DD} \u793A\u4F8B\u4EE3\u7801\uFF1A\`${feature.exampleFiles.target}\`
|
|
805
|
+
- \u{1F4DA} \u8BE6\u7EC6\u6587\u6863\uFF1A\u67E5\u770B \`.kiro/skills/${featureKey}.md\`
|
|
806
|
+
`;
|
|
807
|
+
}).join("\n");
|
|
808
|
+
const content = `# \u9879\u76EE\u80FD\u529B\u7D22\u5F15
|
|
809
|
+
|
|
810
|
+
\u672C\u9879\u76EE\u57FA\u4E8E Svton \u6846\u67B6\u521B\u5EFA\uFF0C\u5DF2\u96C6\u6210\u4EE5\u4E0B\u529F\u80FD\u6A21\u5757\uFF1A
|
|
811
|
+
|
|
812
|
+
## \u5DF2\u542F\u7528\u7684\u529F\u80FD
|
|
813
|
+
|
|
814
|
+
${featuresList}
|
|
815
|
+
|
|
816
|
+
## \u4F7F\u7528\u5EFA\u8BAE
|
|
817
|
+
|
|
818
|
+
\u5F53\u4F60\u9700\u8981\u4F7F\u7528\u67D0\u4E2A\u529F\u80FD\u65F6\uFF0C\u53EF\u4EE5\uFF1A
|
|
819
|
+
|
|
820
|
+
1. \u67E5\u770B\u5BF9\u5E94\u7684 skill \u6587\u6863\u4E86\u89E3 API \u548C\u6700\u4F73\u5B9E\u8DF5
|
|
821
|
+
2. \u53C2\u8003 \`src/examples/\` \u76EE\u5F55\u4E0B\u7684\u793A\u4F8B\u4EE3\u7801
|
|
822
|
+
3. \u67E5\u770B\u5B98\u65B9\u6587\u6863\u83B7\u53D6\u66F4\u591A\u4FE1\u606F
|
|
823
|
+
|
|
824
|
+
## \u6587\u6863\u8D44\u6E90
|
|
825
|
+
|
|
826
|
+
- Svton \u5B98\u65B9\u6587\u6863\uFF1Ahttps://751848178.github.io/svton
|
|
827
|
+
- GitHub\uFF1Ahttps://github.com/751848178/svton
|
|
828
|
+
`;
|
|
829
|
+
const indexPath = import_path3.default.join(targetPath, ".kiro/skills/project-capabilities.md");
|
|
830
|
+
await import_fs_extra4.default.writeFile(indexPath, content);
|
|
831
|
+
logger.info("Generated capabilities index");
|
|
832
|
+
}
|
|
833
|
+
async function updatePackageJson(features, config, targetPath) {
|
|
834
|
+
const packageJsonPath = import_path3.default.join(targetPath, "package.json");
|
|
835
|
+
const packageJson = await import_fs_extra4.default.readJSON(packageJsonPath);
|
|
836
|
+
const dependencies = collectDependencies(features, config);
|
|
837
|
+
packageJson.dependencies = {
|
|
838
|
+
...packageJson.dependencies,
|
|
839
|
+
...dependencies
|
|
840
|
+
};
|
|
841
|
+
await import_fs_extra4.default.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
|
|
842
|
+
logger.info("Updated package.json with feature dependencies");
|
|
843
|
+
}
|
|
844
|
+
function generateModuleImports(features, config) {
|
|
845
|
+
const imports = [];
|
|
846
|
+
for (const featureKey of features) {
|
|
847
|
+
const feature = config.features[featureKey];
|
|
848
|
+
if (feature && feature.moduleImports) {
|
|
849
|
+
for (const moduleImport of feature.moduleImports) {
|
|
850
|
+
imports.push(`import { ${moduleImport.import} } from '${moduleImport.from}';`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return imports.join("\n");
|
|
855
|
+
}
|
|
856
|
+
function generateModuleRegistrations(features, config) {
|
|
857
|
+
const registrations = [];
|
|
858
|
+
for (const featureKey of features) {
|
|
859
|
+
const feature = config.features[featureKey];
|
|
860
|
+
if (feature && feature.moduleRegistration) {
|
|
861
|
+
const { module: moduleName, config: moduleConfig } = feature.moduleRegistration;
|
|
862
|
+
registrations.push(` ${moduleName}.${feature.moduleRegistration.type}({
|
|
863
|
+
useFactory: (configService: ConfigService) => ${moduleConfig},
|
|
864
|
+
inject: [ConfigService],
|
|
865
|
+
}),`);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return registrations.join("\n");
|
|
869
|
+
}
|
|
870
|
+
async function updateAppModule(features, config, targetPath) {
|
|
871
|
+
const appModulePath = import_path3.default.join(targetPath, "src/app.module.ts");
|
|
872
|
+
if (!await import_fs_extra4.default.pathExists(appModulePath)) {
|
|
873
|
+
logger.warn("app.module.ts not found, skipping module injection");
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
let content = await import_fs_extra4.default.readFile(appModulePath, "utf-8");
|
|
877
|
+
const imports = generateModuleImports(features, config);
|
|
878
|
+
const registrations = generateModuleRegistrations(features, config);
|
|
879
|
+
const importsMatch = content.match(/imports:\s*\[([\s\S]*?)\]/);
|
|
880
|
+
if (importsMatch) {
|
|
881
|
+
const existingImports = importsMatch[1];
|
|
882
|
+
const newImports = `${existingImports}
|
|
883
|
+
${registrations}`;
|
|
884
|
+
content = content.replace(
|
|
885
|
+
/imports:\s*\[([\s\S]*?)\]/,
|
|
886
|
+
`imports: [${newImports}
|
|
887
|
+
]`
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
const lastImportIndex = content.lastIndexOf("import ");
|
|
891
|
+
const lastImportEnd = content.indexOf("\n", lastImportIndex);
|
|
892
|
+
content = content.slice(0, lastImportEnd + 1) + imports + "\n" + content.slice(lastImportEnd + 1);
|
|
893
|
+
await import_fs_extra4.default.writeFile(appModulePath, content);
|
|
894
|
+
logger.info("Updated app.module.ts with feature modules");
|
|
895
|
+
}
|
|
896
|
+
|
|
684
897
|
// src/commands/create.ts
|
|
685
898
|
async function createProject(projectName, options = {}) {
|
|
686
899
|
try {
|
|
@@ -692,18 +905,21 @@ async function createProject(projectName, options = {}) {
|
|
|
692
905
|
}
|
|
693
906
|
process.exit(1);
|
|
694
907
|
}
|
|
695
|
-
const projectPath =
|
|
696
|
-
if (await
|
|
908
|
+
const projectPath = import_path4.default.resolve(process.cwd(), projectName);
|
|
909
|
+
if (await import_fs_extra5.default.pathExists(projectPath)) {
|
|
697
910
|
logger.error(`Directory ${projectName} already exists!`);
|
|
698
911
|
process.exit(1);
|
|
699
912
|
}
|
|
700
913
|
logger.info(import_chalk2.default.blue("\u{1F680} Welcome to Svton App Generator!"));
|
|
701
914
|
logger.info("");
|
|
915
|
+
const featuresConfig = await loadFeaturesConfig();
|
|
702
916
|
let answers;
|
|
703
917
|
if (options.yes) {
|
|
704
918
|
answers = {
|
|
705
919
|
org: options.org || projectName,
|
|
706
920
|
template: options.template || "full-stack",
|
|
921
|
+
features: [],
|
|
922
|
+
// 默认不选择额外功能
|
|
707
923
|
packageManager: options.packageManager || "pnpm",
|
|
708
924
|
installDeps: !options.skipInstall,
|
|
709
925
|
initGit: !options.skipGit
|
|
@@ -732,6 +948,13 @@ async function createProject(projectName, options = {}) {
|
|
|
732
948
|
],
|
|
733
949
|
default: options.template || "full-stack"
|
|
734
950
|
},
|
|
951
|
+
{
|
|
952
|
+
type: "checkbox",
|
|
953
|
+
name: "features",
|
|
954
|
+
message: "Select features to include (use space to select, enter to confirm):",
|
|
955
|
+
choices: getFeatureChoices(featuresConfig),
|
|
956
|
+
when: (answers2) => answers2.template === "backend-only" || answers2.template === "full-stack"
|
|
957
|
+
},
|
|
735
958
|
{
|
|
736
959
|
type: "list",
|
|
737
960
|
name: "packageManager",
|
|
@@ -757,6 +980,7 @@ async function createProject(projectName, options = {}) {
|
|
|
757
980
|
projectName,
|
|
758
981
|
orgName: answers.org.startsWith("@") ? answers.org : `@${answers.org}`,
|
|
759
982
|
template: answers.template,
|
|
983
|
+
features: answers.features || [],
|
|
760
984
|
packageManager: answers.packageManager,
|
|
761
985
|
installDeps: answers.installDeps,
|
|
762
986
|
initGit: answers.initGit,
|
|
@@ -767,6 +991,9 @@ async function createProject(projectName, options = {}) {
|
|
|
767
991
|
logger.info(` Project Name: ${import_chalk2.default.white(config.projectName)}`);
|
|
768
992
|
logger.info(` Organization: ${import_chalk2.default.white(config.orgName)}`);
|
|
769
993
|
logger.info(` Template: ${import_chalk2.default.white(config.template)}`);
|
|
994
|
+
if (config.features.length > 0) {
|
|
995
|
+
logger.info(` Features: ${import_chalk2.default.white(config.features.join(", "))}`);
|
|
996
|
+
}
|
|
770
997
|
logger.info(` Package Manager: ${import_chalk2.default.white(config.packageManager)}`);
|
|
771
998
|
logger.info(` Install Dependencies: ${import_chalk2.default.white(config.installDeps ? "Yes" : "No")}`);
|
|
772
999
|
logger.info(` Initialize Git: ${import_chalk2.default.white(config.initGit ? "Yes" : "No")}`);
|
|
@@ -811,10 +1038,23 @@ async function createProject(projectName, options = {}) {
|
|
|
811
1038
|
async function createProjectFromTemplate(config) {
|
|
812
1039
|
const spinner = (0, import_ora.default)("Creating project...").start();
|
|
813
1040
|
try {
|
|
814
|
-
await
|
|
1041
|
+
await import_fs_extra5.default.ensureDir(config.projectPath);
|
|
815
1042
|
process.chdir(config.projectPath);
|
|
816
1043
|
spinner.text = "Generating project files...";
|
|
817
1044
|
await generateFromTemplate(config);
|
|
1045
|
+
if (config.features.length > 0) {
|
|
1046
|
+
spinner.text = "Integrating selected features...";
|
|
1047
|
+
const featuresConfig = await loadFeaturesConfig();
|
|
1048
|
+
const templatePath = import_path4.default.join(__dirname, "../../../templates");
|
|
1049
|
+
await updatePackageJson(config.features, featuresConfig, config.projectPath);
|
|
1050
|
+
await copyConfigFiles(config.features, featuresConfig, templatePath, config.projectPath);
|
|
1051
|
+
await copyExampleFiles(config.features, featuresConfig, templatePath, config.projectPath);
|
|
1052
|
+
await copySkillFiles(config.features, featuresConfig, templatePath, config.projectPath);
|
|
1053
|
+
await generateEnvExample(config.features, featuresConfig, config.projectPath);
|
|
1054
|
+
if (config.template === "backend-only" || config.template === "full-stack") {
|
|
1055
|
+
await updateAppModule(config.features, featuresConfig, config.projectPath);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
818
1058
|
if (config.installDeps) {
|
|
819
1059
|
spinner.text = "Installing dependencies...";
|
|
820
1060
|
await installDependencies(config.packageManager);
|
|
@@ -831,7 +1071,7 @@ async function createProjectFromTemplate(config) {
|
|
|
831
1071
|
}
|
|
832
1072
|
|
|
833
1073
|
// package.json
|
|
834
|
-
var version = "1.
|
|
1074
|
+
var version = "1.2.0";
|
|
835
1075
|
|
|
836
1076
|
// src/index.ts
|
|
837
1077
|
async function cli() {
|
package/dist/index.mjs
CHANGED
|
@@ -13,8 +13,8 @@ import { Command } from "commander";
|
|
|
13
13
|
import inquirer from "inquirer";
|
|
14
14
|
import chalk2 from "chalk";
|
|
15
15
|
import ora from "ora";
|
|
16
|
-
import
|
|
17
|
-
import
|
|
16
|
+
import fs5 from "fs-extra";
|
|
17
|
+
import path4 from "path";
|
|
18
18
|
import validateNpmPackageName from "validate-npm-package-name";
|
|
19
19
|
|
|
20
20
|
// src/utils/template.ts
|
|
@@ -654,6 +654,219 @@ async function initGit(projectName) {
|
|
|
654
654
|
}
|
|
655
655
|
}
|
|
656
656
|
|
|
657
|
+
// src/utils/features.ts
|
|
658
|
+
import fs4 from "fs-extra";
|
|
659
|
+
import path3 from "path";
|
|
660
|
+
async function loadFeaturesConfig() {
|
|
661
|
+
const configPath = path3.join(__dirname, "../../features.json");
|
|
662
|
+
return await fs4.readJSON(configPath);
|
|
663
|
+
}
|
|
664
|
+
function getFeatureChoices(config) {
|
|
665
|
+
return Object.entries(config.features).map(([key, feature]) => ({
|
|
666
|
+
name: `${feature.name} - ${feature.description}`,
|
|
667
|
+
value: key,
|
|
668
|
+
checked: false
|
|
669
|
+
}));
|
|
670
|
+
}
|
|
671
|
+
function collectDependencies(features, config) {
|
|
672
|
+
const dependencies = {};
|
|
673
|
+
for (const featureKey of features) {
|
|
674
|
+
const feature = config.features[featureKey];
|
|
675
|
+
if (feature) {
|
|
676
|
+
Object.assign(dependencies, feature.packages.dependencies);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return dependencies;
|
|
680
|
+
}
|
|
681
|
+
function collectEnvVars(features, config) {
|
|
682
|
+
const envVars = [];
|
|
683
|
+
const seen = /* @__PURE__ */ new Set();
|
|
684
|
+
for (const featureKey of features) {
|
|
685
|
+
const feature = config.features[featureKey];
|
|
686
|
+
if (feature) {
|
|
687
|
+
for (const envVar of feature.envVars) {
|
|
688
|
+
if (!seen.has(envVar.key)) {
|
|
689
|
+
envVars.push(envVar);
|
|
690
|
+
seen.add(envVar.key);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return envVars;
|
|
696
|
+
}
|
|
697
|
+
async function generateEnvExample(features, config, targetPath) {
|
|
698
|
+
const envVars = collectEnvVars(features, config);
|
|
699
|
+
const content = [
|
|
700
|
+
"# Environment Variables",
|
|
701
|
+
"# Copy this file to .env and fill in the values",
|
|
702
|
+
"",
|
|
703
|
+
...envVars.map((envVar) => {
|
|
704
|
+
const lines = [];
|
|
705
|
+
if (envVar.description) {
|
|
706
|
+
lines.push(`# ${envVar.description}`);
|
|
707
|
+
}
|
|
708
|
+
lines.push(`${envVar.key}=${envVar.default}`);
|
|
709
|
+
lines.push("");
|
|
710
|
+
return lines.join("\n");
|
|
711
|
+
})
|
|
712
|
+
].join("\n");
|
|
713
|
+
await fs4.writeFile(path3.join(targetPath, ".env.example"), content);
|
|
714
|
+
logger.info("Generated .env.example");
|
|
715
|
+
}
|
|
716
|
+
async function copyConfigFiles(features, config, templatePath, targetPath) {
|
|
717
|
+
for (const featureKey of features) {
|
|
718
|
+
const feature = config.features[featureKey];
|
|
719
|
+
if (feature && feature.configFiles) {
|
|
720
|
+
for (const configFile of feature.configFiles) {
|
|
721
|
+
const sourcePath = path3.join(templatePath, configFile.template);
|
|
722
|
+
const destPath = path3.join(targetPath, configFile.path);
|
|
723
|
+
await fs4.ensureDir(path3.dirname(destPath));
|
|
724
|
+
await fs4.copy(sourcePath, destPath);
|
|
725
|
+
logger.info(`Copied config: ${configFile.path}`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
async function copyExampleFiles(features, config, templatePath, targetPath) {
|
|
731
|
+
for (const featureKey of features) {
|
|
732
|
+
const feature = config.features[featureKey];
|
|
733
|
+
if (feature && feature.exampleFiles) {
|
|
734
|
+
const sourcePath = path3.join(templatePath, feature.exampleFiles.source);
|
|
735
|
+
const destPath = path3.join(targetPath, feature.exampleFiles.target);
|
|
736
|
+
if (await fs4.pathExists(sourcePath)) {
|
|
737
|
+
await fs4.ensureDir(path3.dirname(destPath));
|
|
738
|
+
await fs4.copy(sourcePath, destPath);
|
|
739
|
+
logger.info(`Copied examples: ${feature.exampleFiles.target}`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
async function copySkillFiles(features, config, templatePath, targetPath) {
|
|
745
|
+
const skillsDir = path3.join(targetPath, ".kiro/skills");
|
|
746
|
+
await fs4.ensureDir(skillsDir);
|
|
747
|
+
const baseSkillSource = path3.join(templatePath, "skills/base.skill.md");
|
|
748
|
+
const baseSkillDest = path3.join(skillsDir, "project-capabilities.md");
|
|
749
|
+
if (await fs4.pathExists(baseSkillSource)) {
|
|
750
|
+
await fs4.copy(baseSkillSource, baseSkillDest);
|
|
751
|
+
logger.info("Copied base skill file");
|
|
752
|
+
}
|
|
753
|
+
for (const featureKey of features) {
|
|
754
|
+
const feature = config.features[featureKey];
|
|
755
|
+
if (feature && feature.skillFile) {
|
|
756
|
+
const sourcePath = path3.join(templatePath, feature.skillFile.template);
|
|
757
|
+
const destPath = path3.join(targetPath, feature.skillFile.target);
|
|
758
|
+
if (await fs4.pathExists(sourcePath)) {
|
|
759
|
+
await fs4.ensureDir(path3.dirname(destPath));
|
|
760
|
+
await fs4.copy(sourcePath, destPath);
|
|
761
|
+
logger.info(`Copied skill: ${feature.skillFile.target}`);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
await generateCapabilitiesIndex(features, config, targetPath);
|
|
766
|
+
}
|
|
767
|
+
async function generateCapabilitiesIndex(features, config, targetPath) {
|
|
768
|
+
const featuresList = features.map((featureKey) => {
|
|
769
|
+
const feature = config.features[featureKey];
|
|
770
|
+
if (!feature) return "";
|
|
771
|
+
const packages = Object.keys(feature.packages.dependencies).join(", ");
|
|
772
|
+
return `### ${feature.name}
|
|
773
|
+
|
|
774
|
+
${feature.description}
|
|
775
|
+
|
|
776
|
+
- \u{1F4E6} \u5305\uFF1A${packages}
|
|
777
|
+
- \u{1F4DD} \u793A\u4F8B\u4EE3\u7801\uFF1A\`${feature.exampleFiles.target}\`
|
|
778
|
+
- \u{1F4DA} \u8BE6\u7EC6\u6587\u6863\uFF1A\u67E5\u770B \`.kiro/skills/${featureKey}.md\`
|
|
779
|
+
`;
|
|
780
|
+
}).join("\n");
|
|
781
|
+
const content = `# \u9879\u76EE\u80FD\u529B\u7D22\u5F15
|
|
782
|
+
|
|
783
|
+
\u672C\u9879\u76EE\u57FA\u4E8E Svton \u6846\u67B6\u521B\u5EFA\uFF0C\u5DF2\u96C6\u6210\u4EE5\u4E0B\u529F\u80FD\u6A21\u5757\uFF1A
|
|
784
|
+
|
|
785
|
+
## \u5DF2\u542F\u7528\u7684\u529F\u80FD
|
|
786
|
+
|
|
787
|
+
${featuresList}
|
|
788
|
+
|
|
789
|
+
## \u4F7F\u7528\u5EFA\u8BAE
|
|
790
|
+
|
|
791
|
+
\u5F53\u4F60\u9700\u8981\u4F7F\u7528\u67D0\u4E2A\u529F\u80FD\u65F6\uFF0C\u53EF\u4EE5\uFF1A
|
|
792
|
+
|
|
793
|
+
1. \u67E5\u770B\u5BF9\u5E94\u7684 skill \u6587\u6863\u4E86\u89E3 API \u548C\u6700\u4F73\u5B9E\u8DF5
|
|
794
|
+
2. \u53C2\u8003 \`src/examples/\` \u76EE\u5F55\u4E0B\u7684\u793A\u4F8B\u4EE3\u7801
|
|
795
|
+
3. \u67E5\u770B\u5B98\u65B9\u6587\u6863\u83B7\u53D6\u66F4\u591A\u4FE1\u606F
|
|
796
|
+
|
|
797
|
+
## \u6587\u6863\u8D44\u6E90
|
|
798
|
+
|
|
799
|
+
- Svton \u5B98\u65B9\u6587\u6863\uFF1Ahttps://751848178.github.io/svton
|
|
800
|
+
- GitHub\uFF1Ahttps://github.com/751848178/svton
|
|
801
|
+
`;
|
|
802
|
+
const indexPath = path3.join(targetPath, ".kiro/skills/project-capabilities.md");
|
|
803
|
+
await fs4.writeFile(indexPath, content);
|
|
804
|
+
logger.info("Generated capabilities index");
|
|
805
|
+
}
|
|
806
|
+
async function updatePackageJson(features, config, targetPath) {
|
|
807
|
+
const packageJsonPath = path3.join(targetPath, "package.json");
|
|
808
|
+
const packageJson = await fs4.readJSON(packageJsonPath);
|
|
809
|
+
const dependencies = collectDependencies(features, config);
|
|
810
|
+
packageJson.dependencies = {
|
|
811
|
+
...packageJson.dependencies,
|
|
812
|
+
...dependencies
|
|
813
|
+
};
|
|
814
|
+
await fs4.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
|
|
815
|
+
logger.info("Updated package.json with feature dependencies");
|
|
816
|
+
}
|
|
817
|
+
function generateModuleImports(features, config) {
|
|
818
|
+
const imports = [];
|
|
819
|
+
for (const featureKey of features) {
|
|
820
|
+
const feature = config.features[featureKey];
|
|
821
|
+
if (feature && feature.moduleImports) {
|
|
822
|
+
for (const moduleImport of feature.moduleImports) {
|
|
823
|
+
imports.push(`import { ${moduleImport.import} } from '${moduleImport.from}';`);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return imports.join("\n");
|
|
828
|
+
}
|
|
829
|
+
function generateModuleRegistrations(features, config) {
|
|
830
|
+
const registrations = [];
|
|
831
|
+
for (const featureKey of features) {
|
|
832
|
+
const feature = config.features[featureKey];
|
|
833
|
+
if (feature && feature.moduleRegistration) {
|
|
834
|
+
const { module: moduleName, config: moduleConfig } = feature.moduleRegistration;
|
|
835
|
+
registrations.push(` ${moduleName}.${feature.moduleRegistration.type}({
|
|
836
|
+
useFactory: (configService: ConfigService) => ${moduleConfig},
|
|
837
|
+
inject: [ConfigService],
|
|
838
|
+
}),`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return registrations.join("\n");
|
|
842
|
+
}
|
|
843
|
+
async function updateAppModule(features, config, targetPath) {
|
|
844
|
+
const appModulePath = path3.join(targetPath, "src/app.module.ts");
|
|
845
|
+
if (!await fs4.pathExists(appModulePath)) {
|
|
846
|
+
logger.warn("app.module.ts not found, skipping module injection");
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
let content = await fs4.readFile(appModulePath, "utf-8");
|
|
850
|
+
const imports = generateModuleImports(features, config);
|
|
851
|
+
const registrations = generateModuleRegistrations(features, config);
|
|
852
|
+
const importsMatch = content.match(/imports:\s*\[([\s\S]*?)\]/);
|
|
853
|
+
if (importsMatch) {
|
|
854
|
+
const existingImports = importsMatch[1];
|
|
855
|
+
const newImports = `${existingImports}
|
|
856
|
+
${registrations}`;
|
|
857
|
+
content = content.replace(
|
|
858
|
+
/imports:\s*\[([\s\S]*?)\]/,
|
|
859
|
+
`imports: [${newImports}
|
|
860
|
+
]`
|
|
861
|
+
);
|
|
862
|
+
}
|
|
863
|
+
const lastImportIndex = content.lastIndexOf("import ");
|
|
864
|
+
const lastImportEnd = content.indexOf("\n", lastImportIndex);
|
|
865
|
+
content = content.slice(0, lastImportEnd + 1) + imports + "\n" + content.slice(lastImportEnd + 1);
|
|
866
|
+
await fs4.writeFile(appModulePath, content);
|
|
867
|
+
logger.info("Updated app.module.ts with feature modules");
|
|
868
|
+
}
|
|
869
|
+
|
|
657
870
|
// src/commands/create.ts
|
|
658
871
|
async function createProject(projectName, options = {}) {
|
|
659
872
|
try {
|
|
@@ -665,18 +878,21 @@ async function createProject(projectName, options = {}) {
|
|
|
665
878
|
}
|
|
666
879
|
process.exit(1);
|
|
667
880
|
}
|
|
668
|
-
const projectPath =
|
|
669
|
-
if (await
|
|
881
|
+
const projectPath = path4.resolve(process.cwd(), projectName);
|
|
882
|
+
if (await fs5.pathExists(projectPath)) {
|
|
670
883
|
logger.error(`Directory ${projectName} already exists!`);
|
|
671
884
|
process.exit(1);
|
|
672
885
|
}
|
|
673
886
|
logger.info(chalk2.blue("\u{1F680} Welcome to Svton App Generator!"));
|
|
674
887
|
logger.info("");
|
|
888
|
+
const featuresConfig = await loadFeaturesConfig();
|
|
675
889
|
let answers;
|
|
676
890
|
if (options.yes) {
|
|
677
891
|
answers = {
|
|
678
892
|
org: options.org || projectName,
|
|
679
893
|
template: options.template || "full-stack",
|
|
894
|
+
features: [],
|
|
895
|
+
// 默认不选择额外功能
|
|
680
896
|
packageManager: options.packageManager || "pnpm",
|
|
681
897
|
installDeps: !options.skipInstall,
|
|
682
898
|
initGit: !options.skipGit
|
|
@@ -705,6 +921,13 @@ async function createProject(projectName, options = {}) {
|
|
|
705
921
|
],
|
|
706
922
|
default: options.template || "full-stack"
|
|
707
923
|
},
|
|
924
|
+
{
|
|
925
|
+
type: "checkbox",
|
|
926
|
+
name: "features",
|
|
927
|
+
message: "Select features to include (use space to select, enter to confirm):",
|
|
928
|
+
choices: getFeatureChoices(featuresConfig),
|
|
929
|
+
when: (answers2) => answers2.template === "backend-only" || answers2.template === "full-stack"
|
|
930
|
+
},
|
|
708
931
|
{
|
|
709
932
|
type: "list",
|
|
710
933
|
name: "packageManager",
|
|
@@ -730,6 +953,7 @@ async function createProject(projectName, options = {}) {
|
|
|
730
953
|
projectName,
|
|
731
954
|
orgName: answers.org.startsWith("@") ? answers.org : `@${answers.org}`,
|
|
732
955
|
template: answers.template,
|
|
956
|
+
features: answers.features || [],
|
|
733
957
|
packageManager: answers.packageManager,
|
|
734
958
|
installDeps: answers.installDeps,
|
|
735
959
|
initGit: answers.initGit,
|
|
@@ -740,6 +964,9 @@ async function createProject(projectName, options = {}) {
|
|
|
740
964
|
logger.info(` Project Name: ${chalk2.white(config.projectName)}`);
|
|
741
965
|
logger.info(` Organization: ${chalk2.white(config.orgName)}`);
|
|
742
966
|
logger.info(` Template: ${chalk2.white(config.template)}`);
|
|
967
|
+
if (config.features.length > 0) {
|
|
968
|
+
logger.info(` Features: ${chalk2.white(config.features.join(", "))}`);
|
|
969
|
+
}
|
|
743
970
|
logger.info(` Package Manager: ${chalk2.white(config.packageManager)}`);
|
|
744
971
|
logger.info(` Install Dependencies: ${chalk2.white(config.installDeps ? "Yes" : "No")}`);
|
|
745
972
|
logger.info(` Initialize Git: ${chalk2.white(config.initGit ? "Yes" : "No")}`);
|
|
@@ -784,10 +1011,23 @@ async function createProject(projectName, options = {}) {
|
|
|
784
1011
|
async function createProjectFromTemplate(config) {
|
|
785
1012
|
const spinner = ora("Creating project...").start();
|
|
786
1013
|
try {
|
|
787
|
-
await
|
|
1014
|
+
await fs5.ensureDir(config.projectPath);
|
|
788
1015
|
process.chdir(config.projectPath);
|
|
789
1016
|
spinner.text = "Generating project files...";
|
|
790
1017
|
await generateFromTemplate(config);
|
|
1018
|
+
if (config.features.length > 0) {
|
|
1019
|
+
spinner.text = "Integrating selected features...";
|
|
1020
|
+
const featuresConfig = await loadFeaturesConfig();
|
|
1021
|
+
const templatePath = path4.join(__dirname, "../../../templates");
|
|
1022
|
+
await updatePackageJson(config.features, featuresConfig, config.projectPath);
|
|
1023
|
+
await copyConfigFiles(config.features, featuresConfig, templatePath, config.projectPath);
|
|
1024
|
+
await copyExampleFiles(config.features, featuresConfig, templatePath, config.projectPath);
|
|
1025
|
+
await copySkillFiles(config.features, featuresConfig, templatePath, config.projectPath);
|
|
1026
|
+
await generateEnvExample(config.features, featuresConfig, config.projectPath);
|
|
1027
|
+
if (config.template === "backend-only" || config.template === "full-stack") {
|
|
1028
|
+
await updateAppModule(config.features, featuresConfig, config.projectPath);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
791
1031
|
if (config.installDeps) {
|
|
792
1032
|
spinner.text = "Installing dependencies...";
|
|
793
1033
|
await installDependencies(config.packageManager);
|
|
@@ -804,7 +1044,7 @@ async function createProjectFromTemplate(config) {
|
|
|
804
1044
|
}
|
|
805
1045
|
|
|
806
1046
|
// package.json
|
|
807
|
-
var version = "1.
|
|
1047
|
+
var version = "1.2.0";
|
|
808
1048
|
|
|
809
1049
|
// src/index.ts
|
|
810
1050
|
async function cli() {
|