@morda-dev/create-sdk 1.2.1 → 1.3.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/bin/create-sdk.js CHANGED
@@ -13,7 +13,7 @@ const CURRENT_NODE_MAJOR = Number(process.versions.node.split(".")[0]);
13
13
  if (CURRENT_NODE_MAJOR < REQUIRED_NODE_MAJOR) {
14
14
  console.error(
15
15
  `❌ Node.js ${REQUIRED_NODE_MAJOR}+ is required.\n` +
16
- ` You are using Node.js ${process.versions.node}`
16
+ ` You are using Node.js ${process.versions.node}`
17
17
  );
18
18
  process.exit(1);
19
19
  }
@@ -35,7 +35,27 @@ const flags = {
35
35
  const projectName = rawArgs.find((a) => !a.startsWith("-"));
36
36
 
37
37
  /* ============================================================================
38
- * Interactive mode
38
+ * Help (EARLY EXIT, SNAPSHOT SAFE)
39
+ * ============================================================================
40
+ */
41
+ if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
42
+ console.log(
43
+ `Usage:
44
+ create-sdk <project-name> [options]
45
+
46
+ Options:
47
+ --yes Skip all prompts
48
+ --no-git Do not initialize git repository
49
+ --no-install Do not install dependencies
50
+ --dry-run Show what would be done without writing files
51
+ --force Overwrite existing directory
52
+ --help, -h Show this help message`
53
+ );
54
+ process.exit(0);
55
+ }
56
+
57
+ /* ============================================================================
58
+ * Interactive / Non-interactive resolution
39
59
  * ============================================================================
40
60
  */
41
61
  let name = projectName;
@@ -66,7 +86,7 @@ if (!flags.yes && !name) {
66
86
  ],
67
87
  {
68
88
  onCancel: () => {
69
- console.log("\n❌ Cancelled");
89
+ console.error("❌ Cancelled");
70
90
  process.exit(1);
71
91
  },
72
92
  }
@@ -77,19 +97,39 @@ if (!flags.yes && !name) {
77
97
  installDeps = answers.installDeps;
78
98
  }
79
99
 
100
+ /* ============================================================================
101
+ * Validation
102
+ * ============================================================================
103
+ */
80
104
  if (!name) {
81
105
  console.error("❌ Project name is required");
82
106
  process.exit(1);
83
107
  }
84
108
 
85
109
  /* ============================================================================
86
- * Execute scaffold
110
+ * Execute scaffold (CLI CONTRACT OWNER)
87
111
  * ============================================================================
88
112
  */
89
- await scaffold({
90
- projectName: name,
91
- initGit,
92
- installDeps,
93
- dryRun: flags.dryRun,
94
- force: flags.force,
95
- });
113
+ try {
114
+ await scaffold({
115
+ projectName: name,
116
+ initGit,
117
+ installDeps,
118
+ dryRun: flags.dryRun,
119
+ force: flags.force,
120
+ });
121
+
122
+ if (flags.dryRun) {
123
+ console.log("ℹ️ Dry run completed. No files were written.");
124
+ } else {
125
+ console.log("🎉 SDK scaffolded successfully!");
126
+ }
127
+
128
+ process.exit(0);
129
+ } catch (err) {
130
+ const message =
131
+ err instanceof Error ? err.message : "Unexpected error occurred";
132
+
133
+ console.error(`❌ ${message}`);
134
+ process.exit(1);
135
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morda-dev/create-sdk",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "CLI to scaffold a production-ready TypeScript SDK with a strict public API",
5
5
  "license": "ISC",
6
6
  "type": "module",
@@ -8,6 +8,10 @@ const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = path.dirname(__filename);
9
9
  const templateDir = path.resolve(__dirname, "../../template");
10
10
 
11
+ const isTest =
12
+ process.env.NODE_ENV === "test" ||
13
+ process.env.VITEST === "true";
14
+
11
15
  export async function scaffold({
12
16
  projectName,
13
17
  initGit = true,
@@ -26,7 +30,14 @@ export async function scaffold({
26
30
  const targetDir = path.resolve(process.cwd(), dirName);
27
31
 
28
32
  // ─────────────────────────────────────────────
29
- // 🧪 DRY-RUNABSOLUTE NO-SIDE-EFFECT MODE
33
+ // 🔒 CLI CONTRACT EARLY FAILURE
34
+ // ─────────────────────────────────────────────
35
+ if (fs.existsSync(targetDir) && !force) {
36
+ throw new Error(`Directory "${dirName}" already exists`);
37
+ }
38
+
39
+ // ─────────────────────────────────────────────
40
+ // 🧪 DRY-RUN — NO SIDE EFFECTS
30
41
  // ─────────────────────────────────────────────
31
42
  if (dryRun) {
32
43
  console.log("🧪 [dry-run] Creating SDK:", projectName);
@@ -35,26 +46,22 @@ export async function scaffold({
35
46
  if (initGit) console.log("🧪 [dry-run] Would init git");
36
47
  if (installDeps) console.log("🧪 [dry-run] Would install dependencies");
37
48
  console.log("🧪 [dry-run] Done (NO filesystem changes)");
38
- return;
49
+ return { targetDir };
39
50
  }
40
51
 
41
52
  // ─────────────────────────────────────────────
42
- // REAL MODE — FILESYSTEM IS TOUCHED ONLY HERE
53
+ // REAL MODE — FS MUTATIONS
43
54
  // ─────────────────────────────────────────────
44
55
  if (!fs.existsSync(templateDir)) {
45
56
  throw new Error(`Template directory not found: ${templateDir}`);
46
57
  }
47
58
 
48
- if (fs.existsSync(targetDir)) {
49
- if (!force) {
50
- throw new Error(`Directory "${dirName}" already exists`);
51
- }
59
+ if (fs.existsSync(targetDir) && force) {
52
60
  fs.rmSync(targetDir, { recursive: true, force: true });
53
61
  }
54
62
 
55
63
  console.log("🚀 Creating SDK:", projectName);
56
64
 
57
- // copy template
58
65
  copyDir(templateDir, targetDir);
59
66
 
60
67
  // package.json
@@ -63,11 +70,8 @@ export async function scaffold({
63
70
 
64
71
  pkg.name = projectName;
65
72
  pkg.version = "0.1.0";
66
-
67
- // шаблон НЕ должен быть private
68
73
  delete pkg.private;
69
74
 
70
- // exports добавляются ТОЛЬКО тут
71
75
  pkg.main = "./dist/index.js";
72
76
  pkg.types = "./dist/index.d.ts";
73
77
  pkg.exports = {
@@ -88,20 +92,20 @@ export async function scaffold({
88
92
  fs.writeFileSync(readmePath, updated);
89
93
  }
90
94
 
91
- // git
92
- if (initGit) {
95
+ // git (disabled in tests)
96
+ if (initGit && !isTest) {
93
97
  try {
94
98
  execSync("git init", { cwd: targetDir, stdio: "ignore" });
95
99
  } catch {}
96
100
  }
97
101
 
98
- // deps
99
- if (installDeps) {
102
+ // deps (disabled in tests)
103
+ if (installDeps && !isTest) {
100
104
  execSync("npm install", {
101
105
  cwd: targetDir,
102
106
  stdio: "inherit",
103
107
  });
104
108
  }
105
109
 
106
- console.log("🎉 SDK scaffolded successfully!");
110
+ return { targetDir };
107
111
  }