catylst 1.0.5 → 1.0.6
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/package.json +1 -1
- package/postinstall.js +67 -13
package/package.json
CHANGED
package/postinstall.js
CHANGED
|
@@ -28,11 +28,62 @@ const CATYLST_DIR = path.join(os.homedir(), ".catylst");
|
|
|
28
28
|
const TEMPLATE_DIR = path.join(CATYLST_DIR, "template");
|
|
29
29
|
const JAR_PATH = path.join(CATYLST_DIR, "catylst-cli.jar");
|
|
30
30
|
|
|
31
|
-
const green
|
|
31
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
32
32
|
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
33
|
-
const cyan
|
|
34
|
-
const bold
|
|
35
|
-
const dim
|
|
33
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
34
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
35
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
36
|
+
const purple = (s) => `\x1b[35m${s}\x1b[0m`;
|
|
37
|
+
|
|
38
|
+
// ── Tips & jokes shown while cloning / downloading ───────────────────────────
|
|
39
|
+
|
|
40
|
+
const MESSAGES = [
|
|
41
|
+
"tip Room 3.1 auto-generates all your DAO queries at compile time.",
|
|
42
|
+
"tip Navigation3 uses type-safe routes — no more string typos in nav graphs.",
|
|
43
|
+
"tip Swap AI providers by changing one line in AppModule.kt.",
|
|
44
|
+
"tip bloom-build helps you scaffold a full feature in seconds with Claude Code.",
|
|
45
|
+
"tip Material 3 Expressive ships spring-based motion out of the box.",
|
|
46
|
+
"tip Run ./gradlew :composeApp:kspAndroidMain after every Entity change.",
|
|
47
|
+
"tip Koin multiplatform means one DI graph for Android, iOS, and Desktop.",
|
|
48
|
+
"tip Use bloom-navigate to cleanly remove any feature you do not need.",
|
|
49
|
+
"tip AGP 9 brings predictive back gesture support by default.",
|
|
50
|
+
"tip commonMain code compiles to all targets — write once, ship everywhere.",
|
|
51
|
+
"joke Why do Kotlin developers stay calm? Because they know how to handle exceptions.",
|
|
52
|
+
"joke A null pointer walks into a bar. The bartender says: we don't serve your type here.",
|
|
53
|
+
"joke Why did the Android developer quit? Too many fragments.",
|
|
54
|
+
"joke Kotlin: where semicolons go to retire.",
|
|
55
|
+
"joke iOS dev asks: what is Gradle? Android dev weeps softly.",
|
|
56
|
+
"joke There are only 10 types of developers: those who understand binary and those who do not.",
|
|
57
|
+
"joke A git push a day keeps the merge conflicts away. Usually.",
|
|
58
|
+
"joke My code works. I have no idea why. Shipping it anyway.",
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
let tipTimer = null;
|
|
62
|
+
let tipIndex = 0;
|
|
63
|
+
|
|
64
|
+
function startTips() {
|
|
65
|
+
// Shuffle so order is different each install
|
|
66
|
+
const msgs = [...MESSAGES].sort(() => Math.random() - 0.5);
|
|
67
|
+
tipIndex = 0;
|
|
68
|
+
|
|
69
|
+
function showNext() {
|
|
70
|
+
const msg = msgs[tipIndex % msgs.length];
|
|
71
|
+
const kind = msg.startsWith("joke") ? purple("joke ") : cyan("tip ");
|
|
72
|
+
const text = msg.replace(/^(tip|joke)\s+/, "");
|
|
73
|
+
process.stdout.write(`\r ${kind} ${dim(text)}${" ".repeat(10)}`);
|
|
74
|
+
tipIndex++;
|
|
75
|
+
tipTimer = setTimeout(showNext, 3000);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
showNext();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function stopTips(finalLine) {
|
|
82
|
+
if (tipTimer) { clearTimeout(tipTimer); tipTimer = null; }
|
|
83
|
+
process.stdout.write(`\r${finalLine}${" ".repeat(30)}\n`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── Header ───────────────────────────────────────────────────────────────────
|
|
36
87
|
|
|
37
88
|
console.log("");
|
|
38
89
|
console.log(bold(" Catylst KMP Project Generator"));
|
|
@@ -67,33 +118,33 @@ function setupTemplate() {
|
|
|
67
118
|
const isGitRepo = fs.existsSync(path.join(TEMPLATE_DIR, ".git"));
|
|
68
119
|
|
|
69
120
|
if (isGitRepo) {
|
|
70
|
-
|
|
71
|
-
// Use spawn array form — no shell interpolation, no injection risk
|
|
121
|
+
startTips();
|
|
72
122
|
const result = spawnSync("git", ["pull", "--quiet", "--rebase"], {
|
|
73
123
|
cwd: TEMPLATE_DIR,
|
|
74
124
|
stdio: "pipe",
|
|
75
125
|
});
|
|
76
126
|
if (result.status === 0) {
|
|
77
|
-
|
|
127
|
+
stopTips(green(" ✓ Template updated"));
|
|
78
128
|
} else {
|
|
79
|
-
|
|
129
|
+
stopTips(yellow(" ⚠ Could not update template (offline?). Using existing."));
|
|
80
130
|
}
|
|
81
131
|
} else {
|
|
82
|
-
console.log(
|
|
132
|
+
console.log(dim(" ↓ Cloning template — hang tight...\n"));
|
|
133
|
+
startTips();
|
|
83
134
|
fs.rmSync(TEMPLATE_DIR, { recursive: true, force: true });
|
|
84
|
-
// Use spawn array form — REPO_URL and TEMPLATE_DIR are never shell-interpolated
|
|
85
135
|
const result = spawnSync(
|
|
86
136
|
"git",
|
|
87
137
|
["clone", "--depth", "1", "--quiet", REPO_URL, TEMPLATE_DIR],
|
|
88
138
|
{ stdio: "pipe" }
|
|
89
139
|
);
|
|
90
140
|
if (result.status !== 0) {
|
|
141
|
+
stopTips("");
|
|
91
142
|
const msg = result.stderr ? result.stderr.toString().trim() : "unknown error";
|
|
92
143
|
console.error(yellow(" ✗ Failed to clone template. Is git installed?"));
|
|
93
144
|
console.error(dim(` ${msg}`));
|
|
94
145
|
process.exit(1);
|
|
95
146
|
}
|
|
96
|
-
|
|
147
|
+
stopTips(green(" ✓ Template ready"));
|
|
97
148
|
}
|
|
98
149
|
}
|
|
99
150
|
|
|
@@ -124,7 +175,8 @@ function downloadJar() {
|
|
|
124
175
|
}
|
|
125
176
|
|
|
126
177
|
return new Promise((resolve, reject) => {
|
|
127
|
-
|
|
178
|
+
console.log(dim(" ↓ Downloading CLI — almost there...\n"));
|
|
179
|
+
startTips();
|
|
128
180
|
|
|
129
181
|
// Write to a temp file first — atomic rename prevents race conditions
|
|
130
182
|
const tmpPath = JAR_PATH + ".tmp." + process.pid;
|
|
@@ -167,18 +219,20 @@ function downloadJar() {
|
|
|
167
219
|
}
|
|
168
220
|
|
|
169
221
|
const digest = hash.digest("hex");
|
|
170
|
-
|
|
222
|
+
stopTips(green(" ✓ CLI ready"));
|
|
171
223
|
console.log(dim(` SHA-256: ${digest}`));
|
|
172
224
|
resolve();
|
|
173
225
|
});
|
|
174
226
|
});
|
|
175
227
|
|
|
176
228
|
file.on("error", (err) => {
|
|
229
|
+
stopTips("");
|
|
177
230
|
fs.unlink(tmpPath, () => {});
|
|
178
231
|
reject(err);
|
|
179
232
|
});
|
|
180
233
|
})
|
|
181
234
|
.on("error", (err) => {
|
|
235
|
+
stopTips("");
|
|
182
236
|
fs.unlink(tmpPath, () => {});
|
|
183
237
|
reject(err);
|
|
184
238
|
});
|