@svton/cli 1.2.2 → 1.2.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/dist/index.js +58 -27
- package/dist/index.mjs +58 -27
- package/package.json +1 -3
- package/templates/apps/admin/next-env.d.ts +0 -2
- package/templates/apps/admin/next.config.js +0 -15
- package/templates/apps/admin/package.json.tpl +0 -54
- package/templates/apps/admin/postcss.config.js +0 -6
- package/templates/apps/admin/src/app/globals.css +0 -37
- package/templates/apps/admin/src/app/layout.tsx +0 -19
- package/templates/apps/admin/src/app/login/page.tsx +0 -96
- package/templates/apps/admin/src/app/page.tsx +0 -8
- package/templates/apps/admin/src/app/users/page.tsx +0 -165
- package/templates/apps/admin/src/components/ui/switch.tsx +0 -29
- package/templates/apps/admin/src/hooks/useAPI.ts +0 -130
- package/templates/apps/admin/src/lib/api-client.ts +0 -112
- package/templates/apps/admin/src/lib/api-server.ts +0 -95
- package/templates/apps/admin/tailwind.config.js +0 -54
- package/templates/apps/admin/tsconfig.json +0 -22
- package/templates/apps/backend/.env.example +0 -29
- package/templates/apps/backend/nest-cli.json +0 -8
- package/templates/apps/backend/package.json.tpl +0 -57
- package/templates/apps/backend/prisma/schema.prisma +0 -72
- package/templates/apps/backend/prisma/seed.ts +0 -32
- package/templates/apps/backend/src/app.controller.ts +0 -15
- package/templates/apps/backend/src/app.module.ts +0 -85
- package/templates/apps/backend/src/app.service.ts +0 -12
- package/templates/apps/backend/src/auth/auth.controller.ts +0 -31
- package/templates/apps/backend/src/auth/auth.module.ts +0 -27
- package/templates/apps/backend/src/auth/auth.service.ts +0 -89
- package/templates/apps/backend/src/auth/jwt-auth.guard.ts +0 -5
- package/templates/apps/backend/src/auth/jwt.strategy.ts +0 -27
- package/templates/apps/backend/src/config/env.schema.ts +0 -35
- package/templates/apps/backend/src/main.ts +0 -51
- package/templates/apps/backend/src/object-storage/object-storage.controller.ts +0 -114
- package/templates/apps/backend/src/object-storage/object-storage.module.ts +0 -7
- package/templates/apps/backend/src/prisma/prisma.module.ts +0 -9
- package/templates/apps/backend/src/prisma/prisma.service.ts +0 -13
- package/templates/apps/backend/src/user/user.controller.ts +0 -50
- package/templates/apps/backend/src/user/user.module.ts +0 -12
- package/templates/apps/backend/src/user/user.service.ts +0 -117
- package/templates/apps/backend/tsconfig.json +0 -23
- package/templates/apps/mobile/babel.config.js +0 -8
- package/templates/apps/mobile/config/index.ts +0 -65
- package/templates/apps/mobile/package.json.tpl +0 -48
- package/templates/apps/mobile/project.config.json.tpl +0 -17
- package/templates/apps/mobile/src/app.config.ts +0 -9
- package/templates/apps/mobile/src/app.scss +0 -4
- package/templates/apps/mobile/src/app.ts +0 -8
- package/templates/apps/mobile/src/hooks/useAPI.ts +0 -285
- package/templates/apps/mobile/src/pages/index/index.scss +0 -7
- package/templates/apps/mobile/src/pages/index/index.tsx +0 -49
- package/templates/apps/mobile/src/services/api.ts +0 -155
- package/templates/apps/mobile/src/services/upload.service.ts +0 -41
- package/templates/apps/mobile/tsconfig.json +0 -21
- package/templates/configs/authz.config.ts +0 -10
- package/templates/configs/cache.config.ts +0 -14
- package/templates/configs/oauth.config.ts +0 -20
- package/templates/configs/payment.config.ts +0 -44
- package/templates/configs/queue.config.ts +0 -21
- package/templates/configs/rate-limit.config.ts +0 -16
- package/templates/configs/sms.config.ts +0 -11
- package/templates/configs/storage.config.ts +0 -14
- package/templates/examples/README.md +0 -258
- package/templates/examples/authz/README.md +0 -273
- package/templates/examples/authz/roles.guard.ts +0 -37
- package/templates/examples/authz/user.controller.ts +0 -116
- package/templates/examples/cache/README.md +0 -82
- package/templates/examples/cache/user.controller.ts +0 -42
- package/templates/examples/cache/user.service.ts +0 -78
- package/templates/examples/oauth/README.md +0 -192
- package/templates/examples/oauth/auth.controller.ts +0 -99
- package/templates/examples/oauth/auth.service.ts +0 -97
- package/templates/examples/payment/README.md +0 -151
- package/templates/examples/payment/order.controller.ts +0 -56
- package/templates/examples/payment/order.service.ts +0 -132
- package/templates/examples/payment/webhook.controller.ts +0 -73
- package/templates/examples/queue/README.md +0 -134
- package/templates/examples/queue/email.controller.ts +0 -34
- package/templates/examples/queue/email.processor.ts +0 -68
- package/templates/examples/queue/email.service.ts +0 -64
- package/templates/examples/rate-limit/README.md +0 -249
- package/templates/examples/rate-limit/api.controller.ts +0 -113
- package/templates/examples/sms/README.md +0 -121
- package/templates/examples/sms/sms.service.ts +0 -69
- package/templates/examples/sms/verification.controller.ts +0 -100
- package/templates/examples/storage/README.md +0 -224
- package/templates/examples/storage/upload.controller.ts +0 -117
- package/templates/examples/storage/upload.service.ts +0 -123
- package/templates/packages/types/package.json.tpl +0 -16
- package/templates/packages/types/src/api.ts +0 -88
- package/templates/packages/types/src/common.ts +0 -89
- package/templates/packages/types/src/index.ts +0 -3
- package/templates/packages/types/tsconfig.json +0 -16
- package/templates/skills/authz.skill.md +0 -42
- package/templates/skills/base.skill.md +0 -57
- package/templates/skills/cache.skill.md +0 -88
- package/templates/skills/oauth.skill.md +0 -41
- package/templates/skills/payment.skill.md +0 -129
- package/templates/skills/queue.skill.md +0 -140
- package/templates/skills/rate-limit.skill.md +0 -38
- package/templates/skills/sms.skill.md +0 -39
- package/templates/skills/storage.skill.md +0 -42
package/dist/index.js
CHANGED
|
@@ -132,18 +132,11 @@ async function copyTemplateFiles(config) {
|
|
|
132
132
|
let templateDir = null;
|
|
133
133
|
let needCleanup = false;
|
|
134
134
|
const cliPackageRoot = import_path2.default.dirname(__dirname);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (!templateDir) {
|
|
141
|
-
const frameworkRoot = import_path2.default.dirname(import_path2.default.dirname(cliPackageRoot));
|
|
142
|
-
const localTemplateDir = import_path2.default.join(frameworkRoot, "templates");
|
|
143
|
-
if (await import_fs_extra2.default.pathExists(localTemplateDir)) {
|
|
144
|
-
templateDir = localTemplateDir;
|
|
145
|
-
logger.debug(`Using local template directory: ${templateDir}`);
|
|
146
|
-
}
|
|
135
|
+
const frameworkRoot = import_path2.default.dirname(import_path2.default.dirname(cliPackageRoot));
|
|
136
|
+
const localTemplateDir = import_path2.default.join(frameworkRoot, "templates");
|
|
137
|
+
if (await import_fs_extra2.default.pathExists(localTemplateDir)) {
|
|
138
|
+
templateDir = localTemplateDir;
|
|
139
|
+
logger.debug(`Using local template directory: ${templateDir}`);
|
|
147
140
|
}
|
|
148
141
|
if (!templateDir) {
|
|
149
142
|
logger.info("Downloading templates from GitHub...");
|
|
@@ -753,52 +746,62 @@ async function generateEnvExample(features, config, targetPath) {
|
|
|
753
746
|
await import_fs_extra4.default.writeFile(import_path3.default.join(targetPath, ".env.example"), content);
|
|
754
747
|
logger.info("Generated .env.example");
|
|
755
748
|
}
|
|
756
|
-
async function copyConfigFiles(features, config,
|
|
749
|
+
async function copyConfigFiles(features, config, templateDir, targetPath) {
|
|
757
750
|
for (const featureKey of features) {
|
|
758
751
|
const feature = config.features[featureKey];
|
|
759
752
|
if (feature && feature.configFiles) {
|
|
760
753
|
for (const configFile of feature.configFiles) {
|
|
761
|
-
const sourcePath = import_path3.default.join(
|
|
754
|
+
const sourcePath = import_path3.default.join(templateDir, configFile.template);
|
|
762
755
|
const destPath = import_path3.default.join(targetPath, configFile.path);
|
|
763
|
-
await import_fs_extra4.default.
|
|
764
|
-
|
|
765
|
-
|
|
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);
|
|
759
|
+
logger.info(`Copied config: ${configFile.path}`);
|
|
760
|
+
} else {
|
|
761
|
+
logger.warn(`Config template not found: ${sourcePath}`);
|
|
762
|
+
}
|
|
766
763
|
}
|
|
767
764
|
}
|
|
768
765
|
}
|
|
769
766
|
}
|
|
770
|
-
async function copyExampleFiles(features, config,
|
|
767
|
+
async function copyExampleFiles(features, config, templateDir, targetPath) {
|
|
771
768
|
for (const featureKey of features) {
|
|
772
769
|
const feature = config.features[featureKey];
|
|
773
770
|
if (feature && feature.exampleFiles) {
|
|
774
|
-
const sourcePath = import_path3.default.join(
|
|
771
|
+
const sourcePath = import_path3.default.join(templateDir, feature.exampleFiles.source);
|
|
775
772
|
const destPath = import_path3.default.join(targetPath, feature.exampleFiles.target);
|
|
776
773
|
if (await import_fs_extra4.default.pathExists(sourcePath)) {
|
|
777
774
|
await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
|
|
778
775
|
await import_fs_extra4.default.copy(sourcePath, destPath);
|
|
779
776
|
logger.info(`Copied examples: ${feature.exampleFiles.target}`);
|
|
777
|
+
} else {
|
|
778
|
+
logger.warn(`Example template not found: ${sourcePath}`);
|
|
780
779
|
}
|
|
781
780
|
}
|
|
782
781
|
}
|
|
783
782
|
}
|
|
784
|
-
async function copySkillFiles(features, config,
|
|
783
|
+
async function copySkillFiles(features, config, templateDir, targetPath) {
|
|
785
784
|
const skillsDir = import_path3.default.join(targetPath, ".kiro/skills");
|
|
786
785
|
await import_fs_extra4.default.ensureDir(skillsDir);
|
|
787
|
-
const baseSkillSource = import_path3.default.join(
|
|
786
|
+
const baseSkillSource = import_path3.default.join(templateDir, "skills/base.skill.md");
|
|
788
787
|
const baseSkillDest = import_path3.default.join(skillsDir, "project-capabilities.md");
|
|
789
788
|
if (await import_fs_extra4.default.pathExists(baseSkillSource)) {
|
|
790
789
|
await import_fs_extra4.default.copy(baseSkillSource, baseSkillDest);
|
|
791
790
|
logger.info("Copied base skill file");
|
|
791
|
+
} else {
|
|
792
|
+
logger.warn(`Base skill template not found: ${baseSkillSource}`);
|
|
792
793
|
}
|
|
793
794
|
for (const featureKey of features) {
|
|
794
795
|
const feature = config.features[featureKey];
|
|
795
796
|
if (feature && feature.skillFile) {
|
|
796
|
-
const sourcePath = import_path3.default.join(
|
|
797
|
+
const sourcePath = import_path3.default.join(templateDir, feature.skillFile.template);
|
|
797
798
|
const destPath = import_path3.default.join(targetPath, feature.skillFile.target);
|
|
798
799
|
if (await import_fs_extra4.default.pathExists(sourcePath)) {
|
|
799
800
|
await import_fs_extra4.default.ensureDir(import_path3.default.dirname(destPath));
|
|
800
801
|
await import_fs_extra4.default.copy(sourcePath, destPath);
|
|
801
802
|
logger.info(`Copied skill: ${feature.skillFile.target}`);
|
|
803
|
+
} else {
|
|
804
|
+
logger.warn(`Skill template not found: ${sourcePath}`);
|
|
802
805
|
}
|
|
803
806
|
}
|
|
804
807
|
}
|
|
@@ -1048,8 +1051,26 @@ async function createProject(projectName, options = {}) {
|
|
|
1048
1051
|
process.exit(1);
|
|
1049
1052
|
}
|
|
1050
1053
|
}
|
|
1054
|
+
async function getTemplateDirectory() {
|
|
1055
|
+
const cliPackageRoot = import_path4.default.dirname(__dirname);
|
|
1056
|
+
const frameworkRoot = import_path4.default.dirname(import_path4.default.dirname(cliPackageRoot));
|
|
1057
|
+
const localTemplateDir = import_path4.default.join(frameworkRoot, "templates");
|
|
1058
|
+
if (await import_fs_extra5.default.pathExists(localTemplateDir)) {
|
|
1059
|
+
logger.debug(`Using local template directory: ${localTemplateDir}`);
|
|
1060
|
+
return localTemplateDir;
|
|
1061
|
+
}
|
|
1062
|
+
logger.debug("Downloading templates from GitHub for feature integration...");
|
|
1063
|
+
try {
|
|
1064
|
+
const templateDir = await downloadTemplateFromGitHub();
|
|
1065
|
+
logger.debug("Templates downloaded successfully");
|
|
1066
|
+
return templateDir;
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
throw new Error(`Failed to download templates from GitHub: ${error}`);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1051
1071
|
async function createProjectFromTemplate(config) {
|
|
1052
1072
|
const spinner = (0, import_ora.default)("Creating project...").start();
|
|
1073
|
+
let templateDirToCleanup = null;
|
|
1053
1074
|
try {
|
|
1054
1075
|
await import_fs_extra5.default.ensureDir(config.projectPath);
|
|
1055
1076
|
process.chdir(config.projectPath);
|
|
@@ -1058,11 +1079,17 @@ async function createProjectFromTemplate(config) {
|
|
|
1058
1079
|
if (config.features.length > 0) {
|
|
1059
1080
|
spinner.text = "Integrating selected features...";
|
|
1060
1081
|
const featuresConfig = await loadFeaturesConfig();
|
|
1061
|
-
const
|
|
1082
|
+
const templateDir = await getTemplateDirectory();
|
|
1083
|
+
const cliPackageRoot = import_path4.default.dirname(__dirname);
|
|
1084
|
+
const frameworkRoot = import_path4.default.dirname(import_path4.default.dirname(cliPackageRoot));
|
|
1085
|
+
const localTemplateDir = import_path4.default.join(frameworkRoot, "templates");
|
|
1086
|
+
if (templateDir !== localTemplateDir) {
|
|
1087
|
+
templateDirToCleanup = templateDir;
|
|
1088
|
+
}
|
|
1062
1089
|
await updatePackageJson(config.features, featuresConfig, config.projectPath);
|
|
1063
|
-
await copyConfigFiles(config.features, featuresConfig,
|
|
1064
|
-
await copyExampleFiles(config.features, featuresConfig,
|
|
1065
|
-
await copySkillFiles(config.features, featuresConfig,
|
|
1090
|
+
await copyConfigFiles(config.features, featuresConfig, templateDir, config.projectPath);
|
|
1091
|
+
await copyExampleFiles(config.features, featuresConfig, templateDir, config.projectPath);
|
|
1092
|
+
await copySkillFiles(config.features, featuresConfig, templateDir, config.projectPath);
|
|
1066
1093
|
await generateEnvExample(config.features, featuresConfig, config.projectPath);
|
|
1067
1094
|
if (config.template === "backend-only" || config.template === "full-stack") {
|
|
1068
1095
|
await updateAppModule(config.features, featuresConfig, config.projectPath);
|
|
@@ -1080,11 +1107,15 @@ async function createProjectFromTemplate(config) {
|
|
|
1080
1107
|
} catch (error) {
|
|
1081
1108
|
spinner.fail("Failed to create project");
|
|
1082
1109
|
throw error;
|
|
1110
|
+
} finally {
|
|
1111
|
+
if (templateDirToCleanup) {
|
|
1112
|
+
await cleanupTemplateDir(templateDirToCleanup);
|
|
1113
|
+
}
|
|
1083
1114
|
}
|
|
1084
1115
|
}
|
|
1085
1116
|
|
|
1086
1117
|
// package.json
|
|
1087
|
-
var version = "1.2.
|
|
1118
|
+
var version = "1.2.4";
|
|
1088
1119
|
|
|
1089
1120
|
// src/index.ts
|
|
1090
1121
|
async function cli() {
|
package/dist/index.mjs
CHANGED
|
@@ -105,18 +105,11 @@ async function copyTemplateFiles(config) {
|
|
|
105
105
|
let templateDir = null;
|
|
106
106
|
let needCleanup = false;
|
|
107
107
|
const cliPackageRoot = path2.dirname(__dirname);
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (!templateDir) {
|
|
114
|
-
const frameworkRoot = path2.dirname(path2.dirname(cliPackageRoot));
|
|
115
|
-
const localTemplateDir = path2.join(frameworkRoot, "templates");
|
|
116
|
-
if (await fs2.pathExists(localTemplateDir)) {
|
|
117
|
-
templateDir = localTemplateDir;
|
|
118
|
-
logger.debug(`Using local template directory: ${templateDir}`);
|
|
119
|
-
}
|
|
108
|
+
const frameworkRoot = path2.dirname(path2.dirname(cliPackageRoot));
|
|
109
|
+
const localTemplateDir = path2.join(frameworkRoot, "templates");
|
|
110
|
+
if (await fs2.pathExists(localTemplateDir)) {
|
|
111
|
+
templateDir = localTemplateDir;
|
|
112
|
+
logger.debug(`Using local template directory: ${templateDir}`);
|
|
120
113
|
}
|
|
121
114
|
if (!templateDir) {
|
|
122
115
|
logger.info("Downloading templates from GitHub...");
|
|
@@ -726,52 +719,62 @@ async function generateEnvExample(features, config, targetPath) {
|
|
|
726
719
|
await fs4.writeFile(path3.join(targetPath, ".env.example"), content);
|
|
727
720
|
logger.info("Generated .env.example");
|
|
728
721
|
}
|
|
729
|
-
async function copyConfigFiles(features, config,
|
|
722
|
+
async function copyConfigFiles(features, config, templateDir, targetPath) {
|
|
730
723
|
for (const featureKey of features) {
|
|
731
724
|
const feature = config.features[featureKey];
|
|
732
725
|
if (feature && feature.configFiles) {
|
|
733
726
|
for (const configFile of feature.configFiles) {
|
|
734
|
-
const sourcePath = path3.join(
|
|
727
|
+
const sourcePath = path3.join(templateDir, configFile.template);
|
|
735
728
|
const destPath = path3.join(targetPath, configFile.path);
|
|
736
|
-
await fs4.
|
|
737
|
-
|
|
738
|
-
|
|
729
|
+
if (await fs4.pathExists(sourcePath)) {
|
|
730
|
+
await fs4.ensureDir(path3.dirname(destPath));
|
|
731
|
+
await fs4.copy(sourcePath, destPath);
|
|
732
|
+
logger.info(`Copied config: ${configFile.path}`);
|
|
733
|
+
} else {
|
|
734
|
+
logger.warn(`Config template not found: ${sourcePath}`);
|
|
735
|
+
}
|
|
739
736
|
}
|
|
740
737
|
}
|
|
741
738
|
}
|
|
742
739
|
}
|
|
743
|
-
async function copyExampleFiles(features, config,
|
|
740
|
+
async function copyExampleFiles(features, config, templateDir, targetPath) {
|
|
744
741
|
for (const featureKey of features) {
|
|
745
742
|
const feature = config.features[featureKey];
|
|
746
743
|
if (feature && feature.exampleFiles) {
|
|
747
|
-
const sourcePath = path3.join(
|
|
744
|
+
const sourcePath = path3.join(templateDir, feature.exampleFiles.source);
|
|
748
745
|
const destPath = path3.join(targetPath, feature.exampleFiles.target);
|
|
749
746
|
if (await fs4.pathExists(sourcePath)) {
|
|
750
747
|
await fs4.ensureDir(path3.dirname(destPath));
|
|
751
748
|
await fs4.copy(sourcePath, destPath);
|
|
752
749
|
logger.info(`Copied examples: ${feature.exampleFiles.target}`);
|
|
750
|
+
} else {
|
|
751
|
+
logger.warn(`Example template not found: ${sourcePath}`);
|
|
753
752
|
}
|
|
754
753
|
}
|
|
755
754
|
}
|
|
756
755
|
}
|
|
757
|
-
async function copySkillFiles(features, config,
|
|
756
|
+
async function copySkillFiles(features, config, templateDir, targetPath) {
|
|
758
757
|
const skillsDir = path3.join(targetPath, ".kiro/skills");
|
|
759
758
|
await fs4.ensureDir(skillsDir);
|
|
760
|
-
const baseSkillSource = path3.join(
|
|
759
|
+
const baseSkillSource = path3.join(templateDir, "skills/base.skill.md");
|
|
761
760
|
const baseSkillDest = path3.join(skillsDir, "project-capabilities.md");
|
|
762
761
|
if (await fs4.pathExists(baseSkillSource)) {
|
|
763
762
|
await fs4.copy(baseSkillSource, baseSkillDest);
|
|
764
763
|
logger.info("Copied base skill file");
|
|
764
|
+
} else {
|
|
765
|
+
logger.warn(`Base skill template not found: ${baseSkillSource}`);
|
|
765
766
|
}
|
|
766
767
|
for (const featureKey of features) {
|
|
767
768
|
const feature = config.features[featureKey];
|
|
768
769
|
if (feature && feature.skillFile) {
|
|
769
|
-
const sourcePath = path3.join(
|
|
770
|
+
const sourcePath = path3.join(templateDir, feature.skillFile.template);
|
|
770
771
|
const destPath = path3.join(targetPath, feature.skillFile.target);
|
|
771
772
|
if (await fs4.pathExists(sourcePath)) {
|
|
772
773
|
await fs4.ensureDir(path3.dirname(destPath));
|
|
773
774
|
await fs4.copy(sourcePath, destPath);
|
|
774
775
|
logger.info(`Copied skill: ${feature.skillFile.target}`);
|
|
776
|
+
} else {
|
|
777
|
+
logger.warn(`Skill template not found: ${sourcePath}`);
|
|
775
778
|
}
|
|
776
779
|
}
|
|
777
780
|
}
|
|
@@ -1021,8 +1024,26 @@ async function createProject(projectName, options = {}) {
|
|
|
1021
1024
|
process.exit(1);
|
|
1022
1025
|
}
|
|
1023
1026
|
}
|
|
1027
|
+
async function getTemplateDirectory() {
|
|
1028
|
+
const cliPackageRoot = path4.dirname(__dirname);
|
|
1029
|
+
const frameworkRoot = path4.dirname(path4.dirname(cliPackageRoot));
|
|
1030
|
+
const localTemplateDir = path4.join(frameworkRoot, "templates");
|
|
1031
|
+
if (await fs5.pathExists(localTemplateDir)) {
|
|
1032
|
+
logger.debug(`Using local template directory: ${localTemplateDir}`);
|
|
1033
|
+
return localTemplateDir;
|
|
1034
|
+
}
|
|
1035
|
+
logger.debug("Downloading templates from GitHub for feature integration...");
|
|
1036
|
+
try {
|
|
1037
|
+
const templateDir = await downloadTemplateFromGitHub();
|
|
1038
|
+
logger.debug("Templates downloaded successfully");
|
|
1039
|
+
return templateDir;
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
throw new Error(`Failed to download templates from GitHub: ${error}`);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1024
1044
|
async function createProjectFromTemplate(config) {
|
|
1025
1045
|
const spinner = ora("Creating project...").start();
|
|
1046
|
+
let templateDirToCleanup = null;
|
|
1026
1047
|
try {
|
|
1027
1048
|
await fs5.ensureDir(config.projectPath);
|
|
1028
1049
|
process.chdir(config.projectPath);
|
|
@@ -1031,11 +1052,17 @@ async function createProjectFromTemplate(config) {
|
|
|
1031
1052
|
if (config.features.length > 0) {
|
|
1032
1053
|
spinner.text = "Integrating selected features...";
|
|
1033
1054
|
const featuresConfig = await loadFeaturesConfig();
|
|
1034
|
-
const
|
|
1055
|
+
const templateDir = await getTemplateDirectory();
|
|
1056
|
+
const cliPackageRoot = path4.dirname(__dirname);
|
|
1057
|
+
const frameworkRoot = path4.dirname(path4.dirname(cliPackageRoot));
|
|
1058
|
+
const localTemplateDir = path4.join(frameworkRoot, "templates");
|
|
1059
|
+
if (templateDir !== localTemplateDir) {
|
|
1060
|
+
templateDirToCleanup = templateDir;
|
|
1061
|
+
}
|
|
1035
1062
|
await updatePackageJson(config.features, featuresConfig, config.projectPath);
|
|
1036
|
-
await copyConfigFiles(config.features, featuresConfig,
|
|
1037
|
-
await copyExampleFiles(config.features, featuresConfig,
|
|
1038
|
-
await copySkillFiles(config.features, featuresConfig,
|
|
1063
|
+
await copyConfigFiles(config.features, featuresConfig, templateDir, config.projectPath);
|
|
1064
|
+
await copyExampleFiles(config.features, featuresConfig, templateDir, config.projectPath);
|
|
1065
|
+
await copySkillFiles(config.features, featuresConfig, templateDir, config.projectPath);
|
|
1039
1066
|
await generateEnvExample(config.features, featuresConfig, config.projectPath);
|
|
1040
1067
|
if (config.template === "backend-only" || config.template === "full-stack") {
|
|
1041
1068
|
await updateAppModule(config.features, featuresConfig, config.projectPath);
|
|
@@ -1053,11 +1080,15 @@ async function createProjectFromTemplate(config) {
|
|
|
1053
1080
|
} catch (error) {
|
|
1054
1081
|
spinner.fail("Failed to create project");
|
|
1055
1082
|
throw error;
|
|
1083
|
+
} finally {
|
|
1084
|
+
if (templateDirToCleanup) {
|
|
1085
|
+
await cleanupTemplateDir(templateDirToCleanup);
|
|
1086
|
+
}
|
|
1056
1087
|
}
|
|
1057
1088
|
}
|
|
1058
1089
|
|
|
1059
1090
|
// package.json
|
|
1060
|
-
var version = "1.2.
|
|
1091
|
+
var version = "1.2.4";
|
|
1061
1092
|
|
|
1062
1093
|
// src/index.ts
|
|
1063
1094
|
async function cli() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svton/cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Svton CLI - Create full-stack applications with NestJS, Next.js, and Taro",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -31,12 +31,10 @@
|
|
|
31
31
|
"dist",
|
|
32
32
|
"bin",
|
|
33
33
|
"features.json",
|
|
34
|
-
"templates",
|
|
35
34
|
"README.md",
|
|
36
35
|
"LICENSE"
|
|
37
36
|
],
|
|
38
37
|
"scripts": {
|
|
39
|
-
"prebuild": "node scripts/copy-templates.js",
|
|
40
38
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
41
39
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
42
40
|
"type-check": "tsc --noEmit",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/** @type {import('next').NextConfig} */
|
|
2
|
-
const nextConfig = {
|
|
3
|
-
reactStrictMode: true,
|
|
4
|
-
transpilePackages: ['@svton/hooks'],
|
|
5
|
-
images: {
|
|
6
|
-
remotePatterns: [
|
|
7
|
-
{
|
|
8
|
-
protocol: 'https',
|
|
9
|
-
hostname: '**',
|
|
10
|
-
},
|
|
11
|
-
],
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
module.exports = nextConfig;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{ORG_NAME}}/admin",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "{{PROJECT_NAME}} 管理后台",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"dev": "next dev -p 3001",
|
|
7
|
-
"build": "next build",
|
|
8
|
-
"start": "next start -p 3001",
|
|
9
|
-
"lint": "next lint",
|
|
10
|
-
"type-check": "tsc --noEmit",
|
|
11
|
-
"clean": "rm -rf .next"
|
|
12
|
-
},
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"@hookform/resolvers": "^3.3.3",
|
|
15
|
-
"@radix-ui/react-avatar": "^1.0.4",
|
|
16
|
-
"@radix-ui/react-dialog": "^1.0.5",
|
|
17
|
-
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
|
18
|
-
"@radix-ui/react-label": "^2.0.2",
|
|
19
|
-
"@radix-ui/react-select": "^2.0.0",
|
|
20
|
-
"@radix-ui/react-separator": "^1.0.3",
|
|
21
|
-
"@radix-ui/react-slot": "^1.0.2",
|
|
22
|
-
"@radix-ui/react-switch": "^1.2.6",
|
|
23
|
-
"@radix-ui/react-tabs": "^1.0.4",
|
|
24
|
-
"@radix-ui/react-toast": "^1.1.5",
|
|
25
|
-
"@svton/api-client": "^1.0.0",
|
|
26
|
-
"@svton/hooks": "^1.0.0",
|
|
27
|
-
"{{ORG_NAME}}/types": "workspace:*",
|
|
28
|
-
"axios": "^1.7.9",
|
|
29
|
-
"class-variance-authority": "^0.7.1",
|
|
30
|
-
"clsx": "^2.1.1",
|
|
31
|
-
"dayjs": "^1.11.13",
|
|
32
|
-
"lucide-react": "^0.462.0",
|
|
33
|
-
"next": "^15.0.0",
|
|
34
|
-
"react": "^19.0.0",
|
|
35
|
-
"react-dom": "^19.0.0",
|
|
36
|
-
"react-hook-form": "^7.49.0",
|
|
37
|
-
"swr": "^2.2.5",
|
|
38
|
-
"tailwind-merge": "^3.0.0",
|
|
39
|
-
"zod": "^3.22.4",
|
|
40
|
-
"zustand": "^5.0.0"
|
|
41
|
-
},
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"@types/node": "^22.0.0",
|
|
44
|
-
"@types/react": "^19.0.0",
|
|
45
|
-
"@types/react-dom": "^19.0.0",
|
|
46
|
-
"autoprefixer": "^10.4.22",
|
|
47
|
-
"eslint": "^9.0.0",
|
|
48
|
-
"eslint-config-next": "^15.0.0",
|
|
49
|
-
"postcss": "^8.5.0",
|
|
50
|
-
"tailwindcss": "^3.4.0",
|
|
51
|
-
"tailwindcss-animate": "^1.0.7",
|
|
52
|
-
"typescript": "^5.7.0"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
@tailwind base;
|
|
2
|
-
@tailwind components;
|
|
3
|
-
@tailwind utilities;
|
|
4
|
-
|
|
5
|
-
@layer base {
|
|
6
|
-
:root {
|
|
7
|
-
--background: 0 0% 100%;
|
|
8
|
-
--foreground: 222.2 84% 4.9%;
|
|
9
|
-
--card: 0 0% 100%;
|
|
10
|
-
--card-foreground: 222.2 84% 4.9%;
|
|
11
|
-
--popover: 0 0% 100%;
|
|
12
|
-
--popover-foreground: 222.2 84% 4.9%;
|
|
13
|
-
--primary: 222.2 47.4% 11.2%;
|
|
14
|
-
--primary-foreground: 210 40% 98%;
|
|
15
|
-
--secondary: 210 40% 96.1%;
|
|
16
|
-
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
17
|
-
--muted: 210 40% 96.1%;
|
|
18
|
-
--muted-foreground: 215.4 16.3% 46.9%;
|
|
19
|
-
--accent: 210 40% 96.1%;
|
|
20
|
-
--accent-foreground: 222.2 47.4% 11.2%;
|
|
21
|
-
--destructive: 0 84.2% 60.2%;
|
|
22
|
-
--destructive-foreground: 210 40% 98%;
|
|
23
|
-
--border: 214.3 31.8% 91.4%;
|
|
24
|
-
--input: 214.3 31.8% 91.4%;
|
|
25
|
-
--ring: 222.2 84% 4.9%;
|
|
26
|
-
--radius: 0.5rem;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@layer base {
|
|
31
|
-
* {
|
|
32
|
-
@apply border-border;
|
|
33
|
-
}
|
|
34
|
-
body {
|
|
35
|
-
@apply bg-background text-foreground;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Metadata } from 'next';
|
|
2
|
-
import './globals.css';
|
|
3
|
-
|
|
4
|
-
export const metadata: Metadata = {
|
|
5
|
-
title: '管理后台',
|
|
6
|
-
description: '项目管理后台',
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export default function RootLayout({
|
|
10
|
-
children,
|
|
11
|
-
}: {
|
|
12
|
-
children: React.ReactNode;
|
|
13
|
-
}) {
|
|
14
|
-
return (
|
|
15
|
-
<html lang="zh-CN">
|
|
16
|
-
<body>{children}</body>
|
|
17
|
-
</html>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { useRouter } from 'next/navigation';
|
|
5
|
-
import { usePersistFn } from '@svton/hooks';
|
|
6
|
-
import type { LoginDto } from '{{ORG_NAME}}/types';
|
|
7
|
-
|
|
8
|
-
export default function LoginPage() {
|
|
9
|
-
const router = useRouter();
|
|
10
|
-
const [form, setForm] = useState<LoginDto>({ phone: '', password: '' });
|
|
11
|
-
const [loading, setLoading] = useState(false);
|
|
12
|
-
const [error, setError] = useState('');
|
|
13
|
-
|
|
14
|
-
const handleSubmit = usePersistFn(async (e: React.FormEvent) => {
|
|
15
|
-
e.preventDefault();
|
|
16
|
-
setError('');
|
|
17
|
-
setLoading(true);
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
// 这里应该使用 @svton/api-client 的 API
|
|
21
|
-
// const response = await apiClient.auth.login(form);
|
|
22
|
-
// localStorage.setItem('token', response.data.accessToken);
|
|
23
|
-
|
|
24
|
-
// 模拟登录成功
|
|
25
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
26
|
-
router.push('/users');
|
|
27
|
-
} catch (err: any) {
|
|
28
|
-
setError(err.message || '登录失败');
|
|
29
|
-
} finally {
|
|
30
|
-
setLoading(false);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
36
|
-
<div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
|
|
37
|
-
<div>
|
|
38
|
-
<h2 className="text-center text-3xl font-bold text-gray-900">
|
|
39
|
-
管理后台
|
|
40
|
-
</h2>
|
|
41
|
-
<p className="mt-2 text-center text-sm text-gray-600">
|
|
42
|
-
请登录您的账号
|
|
43
|
-
</p>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
|
|
47
|
-
{error && (
|
|
48
|
-
<div className="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded">
|
|
49
|
-
{error}
|
|
50
|
-
</div>
|
|
51
|
-
)}
|
|
52
|
-
|
|
53
|
-
<div className="space-y-4">
|
|
54
|
-
<div>
|
|
55
|
-
<label htmlFor="phone" className="block text-sm font-medium text-gray-700">
|
|
56
|
-
手机号
|
|
57
|
-
</label>
|
|
58
|
-
<input
|
|
59
|
-
id="phone"
|
|
60
|
-
type="tel"
|
|
61
|
-
required
|
|
62
|
-
value={form.phone}
|
|
63
|
-
onChange={(e) => setForm({ ...form, phone: e.target.value })}
|
|
64
|
-
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
|
65
|
-
placeholder="请输入手机号"
|
|
66
|
-
/>
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
|
-
<div>
|
|
70
|
-
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
|
|
71
|
-
密码
|
|
72
|
-
</label>
|
|
73
|
-
<input
|
|
74
|
-
id="password"
|
|
75
|
-
type="password"
|
|
76
|
-
required
|
|
77
|
-
value={form.password}
|
|
78
|
-
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
|
79
|
-
className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
|
|
80
|
-
placeholder="请输入密码"
|
|
81
|
-
/>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<button
|
|
86
|
-
type="submit"
|
|
87
|
-
disabled={loading}
|
|
88
|
-
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
89
|
-
>
|
|
90
|
-
{loading ? '登录中...' : '登录'}
|
|
91
|
-
</button>
|
|
92
|
-
</form>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
);
|
|
96
|
-
}
|