Skip to content
This repository was archived by the owner on Nov 10, 2025. It is now read-only.

Commit a1aca1a

Browse files
committed
feat: add update notifier, better errors, and debug mode v0.3.0
- Add update-notifier to alert users of new versions - Add typo suggestions using fuzzy matching (fastest-levenshtein) - Add debug mode with --verbose flag for detailed logging - Fix ES module issues by using CommonJS for CLI tool - Test confirmed: hello-world setup works correctly
1 parent 3bceff8 commit a1aca1a

File tree

5 files changed

+101
-7
lines changed

5 files changed

+101
-7
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "create-mn-app",
3-
"version": "0.2.4",
3+
"version": "0.3.0",
44
"description": "Create Midnight Network applications with zero configuration",
55
"main": "dist/index.js",
66
"bin": {
@@ -34,11 +34,13 @@
3434
"chalk": "^4.1.2",
3535
"commander": "^11.0.0",
3636
"cross-spawn": "^7.0.3",
37+
"fastest-levenshtein": "^1.0.16",
3738
"fs-extra": "^11.1.1",
3839
"mustache": "^4.2.0",
3940
"ora": "^5.4.1",
4041
"prompts": "^2.4.2",
4142
"semver": "^7.5.4",
43+
"update-notifier": "^7.3.1",
4244
"validate-npm-package-name": "^5.0.0"
4345
},
4446
"devDependencies": {
@@ -47,6 +49,7 @@
4749
"@types/mustache": "^4.2.2",
4850
"@types/node": "^20.0.0",
4951
"@types/prompts": "^2.4.9",
52+
"@types/update-notifier": "^6.0.8",
5053
"@types/validate-npm-package-name": "^4.0.2",
5154
"typescript": "^5.0.0"
5255
},

src/cli.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
import { Command } from "commander";
22
import chalk from "chalk";
3-
import { createApp } from "./create-app.js";
4-
import { ErrorHandler } from "./utils/error-handler.js";
3+
import updateNotifier from "update-notifier";
4+
import { createApp } from "./create-app";
5+
import { ErrorHandler } from "./utils/error-handler";
6+
import * as path from "path";
7+
import * as fs from "fs";
8+
9+
const pkg = JSON.parse(
10+
fs.readFileSync(path.join(__dirname, "../package.json"), "utf-8")
11+
);
12+
13+
// Check for updates
14+
updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 * 24 }).notify({
15+
isGlobal: true,
16+
});
517

618
const program = new Command();
719

@@ -11,7 +23,7 @@ ErrorHandler.checkNodeVersion(22);
1123
program
1224
.name("create-mn-app")
1325
.description("Create a new Midnight Network application")
14-
.version("0.2.4")
26+
.version("0.3.0")
1527
.argument("[project-directory]", "Directory name for your project")
1628
.option(
1729
"-t, --template <name>",
@@ -25,7 +37,7 @@ program
2537
.option("--skip-git", "Skip git repository initialization")
2638
.option("--verbose", "Show detailed output")
2739
.action(async (projectDirectory, options) => {
28-
console.log(chalk.bold.cyan("\ncreate-mn-app") + chalk.gray(" v0.2.4\n"));
40+
console.log(chalk.bold.cyan("\ncreate-mn-app") + chalk.gray(" v0.3.0\n"));
2941

3042
try {
3143
await createApp(projectDirectory, options);

src/create-app.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import fs from "fs-extra";
33
import chalk from "chalk";
44
import ora from "ora";
55
import prompts from "prompts";
6+
import { distance } from "fastest-levenshtein";
67
import { validateProjectName } from "./utils/validation.js";
78
import { PackageInstaller } from "./installers/package-installer.js";
89
import { TemplateManager } from "./utils/template-manager.js";
@@ -12,6 +13,7 @@ import { GitUtils } from "./utils/git-utils.js";
1213
import { GitCloner } from "./utils/git-cloner.js";
1314
import { RequirementChecker } from "./utils/requirement-checker.js";
1415
import { SetupGuide } from "./utils/setup-guide.js";
16+
import { debug, enableDebug } from "./utils/debug.js";
1517
import {
1618
detectPackageManager,
1719
getPackageManagerInfo,
@@ -34,14 +36,46 @@ export interface CreateAppOptions {
3436
verbose?: boolean;
3537
}
3638

39+
/**
40+
* Find the closest matching template name for typo suggestions
41+
*/
42+
function findSimilarTemplate(input: string): string | null {
43+
const templates = getAllTemplates()
44+
.filter((t) => t.available)
45+
.map((t) => t.name);
46+
47+
let closest = templates[0];
48+
let minDistance = distance(input.toLowerCase(), closest.toLowerCase());
49+
50+
for (const template of templates) {
51+
const d = distance(input.toLowerCase(), template.toLowerCase());
52+
if (d < minDistance) {
53+
minDistance = d;
54+
closest = template;
55+
}
56+
}
57+
58+
// Only suggest if it's close enough (threshold: 3 characters difference)
59+
return minDistance <= 3 ? closest : null;
60+
}
61+
3762
export async function createApp(
3863
projectDirectory: string | undefined,
3964
options: CreateAppOptions
4065
): Promise<void> {
66+
// Enable debug mode if verbose flag is set
67+
if (options.verbose) {
68+
enableDebug();
69+
debug("Debug mode enabled");
70+
debug("Options:", options);
71+
}
72+
4173
let projectName = projectDirectory;
4274
let selectedTemplate = options.template || "hello-world";
4375
let packageManager: PackageManager;
4476

77+
debug("Starting project creation", { projectName, selectedTemplate });
78+
4579
// Interactive mode if no project name provided
4680
if (!projectName) {
4781
const response = await prompts({
@@ -96,7 +130,7 @@ export async function createApp(
96130
const template = getTemplate(selectedTemplate);
97131
if (template && template.comingSoon) {
98132
console.error(
99-
chalk.red(`✖ Template "${selectedTemplate}" is coming soon!`)
133+
chalk.red(`\n✖ Template "${selectedTemplate}" is coming soon!`)
100134
);
101135
console.log(chalk.yellow("\n📢 Available templates:"));
102136
getAllTemplates()
@@ -105,31 +139,53 @@ export async function createApp(
105139
console.log(` • ${chalk.cyan(t.name)} - ${t.description}`);
106140
});
107141
} else {
108-
console.error(chalk.red(`✖ Template "${selectedTemplate}" not found.`));
142+
console.error(chalk.red(`\n✖ Template "${selectedTemplate}" not found.`));
143+
144+
// Suggest similar template if typo detected
145+
const suggestion = findSimilarTemplate(selectedTemplate);
146+
if (suggestion) {
147+
console.log(
148+
chalk.yellow(`\n💡 Did you mean "${chalk.cyan(suggestion)}"?`)
149+
);
150+
}
151+
152+
console.log(chalk.gray("\nAvailable templates:"));
153+
getAllTemplates()
154+
.filter((t) => t.available)
155+
.forEach((t) => {
156+
console.log(` • ${chalk.cyan(t.name)} - ${t.description}`);
157+
});
109158
}
110159
process.exit(1);
111160
}
112161

113162
// Detect or select package manager
114163
if (options.useNpm) {
115164
packageManager = "npm";
165+
debug("Package manager set to npm via --use-npm flag");
116166
} else if (options.useYarn) {
117167
packageManager = "yarn";
168+
debug("Package manager set to yarn via --use-yarn flag");
118169
} else if (options.usePnpm) {
119170
packageManager = "pnpm";
171+
debug("Package manager set to pnpm via --use-pnpm flag");
120172
} else if (options.useBun) {
121173
packageManager = "bun";
174+
debug("Package manager set to bun via --use-bun flag");
122175
} else {
123176
packageManager = detectPackageManager();
177+
debug("Auto-detected package manager:", packageManager);
124178
console.log(
125179
chalk.bold("[" + chalk.blue("i") + "] ") +
126180
chalk.gray(`package manager: ${chalk.cyan(packageManager)}\n`)
127181
);
128182
}
129183

130184
const pmInfo = getPackageManagerInfo(packageManager);
185+
debug("Package manager info:", pmInfo);
131186

132187
const validation = validateProjectName(projectName!);
188+
debug("Project name validation:", validation);
133189
if (!validation.valid) {
134190
console.error(
135191
chalk.red(`✖ Invalid project name: ${validation.problems![0]}`)
@@ -138,9 +194,11 @@ export async function createApp(
138194
}
139195

140196
const projectPath = path.resolve(projectName!);
197+
debug("Project path resolved:", projectPath);
141198

142199
// Check if directory exists
143200
if (fs.existsSync(projectPath)) {
201+
debug("Directory already exists, prompting for overwrite");
144202
const { overwrite } = await prompts({
145203
type: "confirm",
146204
name: "overwrite",

src/utils/debug.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import chalk from "chalk";
2+
3+
let debugEnabled = false;
4+
5+
export function enableDebug() {
6+
debugEnabled = true;
7+
}
8+
9+
export function debug(message: string, data?: any) {
10+
if (!debugEnabled) return;
11+
12+
console.log(chalk.gray("[debug]"), message);
13+
if (data !== undefined) {
14+
console.log(chalk.gray(JSON.stringify(data, null, 2)));
15+
}
16+
}
17+
18+
export function isDebugEnabled(): boolean {
19+
return debugEnabled;
20+
}

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"compilerOptions": {
33
"target": "ES2020",
44
"module": "CommonJS",
5+
"moduleResolution": "node",
56
"lib": ["ES2020"],
67
"outDir": "./dist",
78
"rootDir": "./src",

0 commit comments

Comments
 (0)