Skip to content

Commit e52ecbe

Browse files
authored
Merge pull request #12 from YashVerma-code/conflictfix
Conflictfix
2 parents efde6f6 + 3e859c7 commit e52ecbe

File tree

36 files changed

+2049
-672
lines changed

36 files changed

+2049
-672
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# ensure shell scripts and binaries always use LF line endings
2+
bin/index.js text eol=lf
3+
*.sh text eol=lf

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
```bash
3838
npm i -g hackpack-cli
3939
```
40+
Node.js >= 18.7.0 (recommended)
4041

4142
## Features
4243

bin/index.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { handleAddCommand, handleUninstallCommand } from '../lib/commands/uiLibr
1010
import { printHelp, parseArgs } from '../lib/commands/utils.js';
1111
import { runCli } from '../lib/interactive/wizard.js';
1212
import { installAutocomplete, uninstallAutocomplete, handleCompletionRequest } from '../lib/autocomplete.js';
13+
import { startTunnel } from '../lib/localXpose/utils.js';
1314

1415
const args = process.argv.slice(2);
1516
if (args[0] === '--get-completions') {
@@ -20,12 +21,12 @@ if (args[0] === '--get-completions') {
2021

2122
// Handle Ctrl+C gracefully
2223
process.on('SIGINT', () => {
23-
console.log(chalk.yellow('\n\n👋 Thank you for using hackpack! Goodbye!'));
24+
console.log(chalk.yellow('\n\n Thank you for using hackpack!'));
2425
process.exit(0);
2526
});
2627

2728
process.on('SIGTERM', () => {
28-
console.log(chalk.yellow('\n\n👋 Thank you for using hackpack! Goodbye!'));
29+
console.log(chalk.yellow('\n\nThank you for using hackpack!'));
2930
process.exit(0);
3031
});
3132

@@ -41,7 +42,8 @@ const VALID_COMMANDS = [
4142
'add',
4243
'uninstall',
4344
'migrate',
44-
'autocomplete'
45+
'autocomplete',
46+
'expose'
4547
];
4648

4749
// ---- Command router ----
@@ -121,10 +123,44 @@ async function handleSubcommands() {
121123
break;
122124

123125
case 'migrate':
124-
console.log(chalk.yellow('🚧 Migration Command Beta'));
126+
console.log(chalk.yellow('Migration Command Beta'));
125127
console.log(chalk.cyan('The project migration feature is currently in development.'));
126128
console.log(chalk.gray('\nFollow our releases for updates on this feature!'));
127129
process.exit(0);
130+
131+
case 'expose':
132+
const target = args[1];
133+
if (!target) {
134+
console.log(chalk.yellow('Usage: hp expose <url(including port)>'));
135+
process.exit(1);
136+
}
137+
try {
138+
const localUrl = target.startsWith('http') ? target : `http://localhost:${target}`;
139+
console.log(chalk.cyan(`Setting up secure tunnel for ${localUrl}...`));
140+
141+
const { url, process: tunnelProcess } = await startTunnel(localUrl);
142+
const boxWidth = 80;
143+
const contentWidth = boxWidth -2;
144+
145+
console.log('\n' + chalk.green('┌' + '─'.repeat(boxWidth) + '┐'));
146+
console.log(chalk.green('│ ') + chalk.bold('Your project is live at:'.padEnd(contentWidth)) + chalk.green(' │'));
147+
console.log(chalk.green('│ ') + chalk.cyan(url.padEnd(contentWidth)) + chalk.green(' │'));
148+
console.log(chalk.green('└' + '─'.repeat(boxWidth) + '┘'));
149+
150+
const cleanup = () => {
151+
if (tunnelProcess) tunnelProcess.kill('SIGINT');
152+
console.log(chalk.yellow('\nTunnel closed!'));``
153+
process.exit(0);
154+
};
155+
156+
process.removeAllListeners('SIGINT');
157+
process.on('SIGINT', cleanup);
158+
process.on('SIGTERM', cleanup);
159+
} catch (error) {
160+
console.error(chalk.red(`\nError: ${error.message}`));
161+
process.exit(1);
162+
}
163+
return true;
128164

129165
case 'deactivate':
130166
const isWindows = process.platform === 'win32';

lib/authentication/index.js

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ import { setupClerkNuxt } from "./utils/clerknuxt.js";
1414
export async function setupAuthjs(state) {
1515
const { framework, projectName, language } = state;
1616

17-
console.log(chalk.blue(`🔐 Setting up Auth.js for ${framework} project: ${projectName} \n📝 Language: ${language}`));
17+
console.log(chalk.blue(`Setting up Auth.js for ${framework} project: ${projectName} \n Language: ${language}`));
1818

1919
// Validate framework support
2020
if (!isFrameworkSupported(framework)) {
21-
console.log(chalk.red(`Framework '${framework}' is not supported for Auth.js setup.`));
22-
console.log(chalk.yellow('Supported frameworks: next, svelte, vue, vite-react, astro, nuxt'));
21+
console.log(chalk.red(`Framework '${framework}' is not supported for Auth.js setup.`));
22+
console.log(chalk.yellow('Supported frameworks: next'));
2323
return;
2424
}
25+
2526
if (framework === 'next') {
2627
try {
2728
const targetDir = path.resolve(projectName);
@@ -30,18 +31,15 @@ export async function setupAuthjs(state) {
3031
if (currentDir !== targetDir) {
3132
if (fs.existsSync(targetDir)) {
3233
process.chdir(targetDir);
33-
console.log(chalk.blue(`Changed working directory to project: ${projectName}`));
3434
} else {
3535
console.error(chalk.red(`Directory "${projectName}" does not exist.`));
3636
// Optional: process.exit(1);
3737
}
3838
}
39-
console.log(chalk.blue(`Installing next-auth package...`));
4039
const rootDir = path.resolve(process.cwd());
4140
await execa("npm", ["install", "next-auth@beta"], { cwd: rootDir, stdio: "inherit" });
4241
await execa("npx", ["auth", "secret"], { cwd: rootDir, stdio: "inherit" });
4342

44-
console.log(chalk.green(`✅ next-auth package installed successfully.`));
4543
console.log(chalk.blue(`Configuring Next.js project for Auth.js...`));
4644

4745

@@ -53,7 +51,6 @@ export async function setupAuthjs(state) {
5351
fs.mkdirSync(dir, { recursive: true });
5452
});
5553

56-
// --- New: handle src/app/api/[...nextauth]/route ---
5754
const srcDir = path.join(rootDir, 'src');
5855

5956
const authFilePath = path.join(actionPath, `auth.${language === 'ts' ? 'ts' : 'js'}`);
@@ -81,13 +78,12 @@ export async function setupAuthjs(state) {
8178

8279
if (!fs.existsSync(routeFilePath)) {
8380
fs.writeFileSync(routeFilePath, `import { handlers } from "auth";\n\nexport const { GET, POST } = handlers;\n`);
84-
console.log(chalk.green(`✅ Created ${routeFilePath}`));
8581
}
8682

8783
const layoutPath = path.join(rootDir, "src", "app", `layout.${language === 'ts' ? 'tsx' : ('js')}`);
8884

8985
if (!fs.existsSync(layoutPath)) {
90-
console.log(chalk.red(`layout.${language === 'ts' ? 'tsx' : ('js')} not found at ${layoutPath}`));
86+
console.log(chalk.red(`layout.${language === 'ts' ? 'tsx' : ('js')} not found at ${layoutPath}`));
9187
return;
9288
}
9389

@@ -98,24 +94,22 @@ export async function setupAuthjs(state) {
9894
let content = fs.readFileSync(layoutPath, "utf-8");
9995
let changed = false;
10096

101-
// 1️⃣ Add import for SessionProvider if missing
97+
// Add import for SessionProvider if missing
10298
if (!content.includes("SessionProvider")) {
10399
const importSession = `import { SessionProvider } from "next-auth/react";\n`;
104100
content = importSession + content;
105101
changed = true;
106-
console.log(chalk.green("✅ Added SessionProvider import"));
107102
}
108103

109-
// 2️⃣ Add import for AppBar if missing
104+
// Add import for AppBar if missing
110105
if (!content.includes("AppBar")) {
111106
const importAppbar = `import AppBar from "@/components/AppBar";\n`;
112107
// Insert after other imports for cleanliness
113108
content = importAppbar + content;
114109
changed = true;
115-
console.log(chalk.green("✅ Added AppBar import"));
116110
}
117111

118-
// 3️⃣ Wrap children with <SessionProvider> if not present
112+
// Wrap children with <SessionProvider> if not present
119113
if (!content.includes("<SessionProvider")) {
120114
// Very naive wrap — assumes a single <body> tag exists
121115
content = content.replace(
@@ -127,20 +121,18 @@ export async function setupAuthjs(state) {
127121
`{children}\n </SessionProvider>`
128122
);
129123
changed = true;
130-
console.log(chalk.green("✅ Wrapped body with <SessionProvider> & <AppBar />"));
131124
}
132125

133126
if (changed) {
134127
fs.writeFileSync(layoutPath, content, "utf-8");
135-
console.log(chalk.blue(`✍️ layout.${language === 'ts' ? 'tsx' : ('js')} updated successfully.`));
136128
} else {
137-
console.log(chalk.yellow("ℹ️ SessionProvider and AppBar already present. No changes made."));
129+
console.log(chalk.yellow("SessionProvider and AppBar already present. No changes made."));
138130
}
139131

140132
const HomePagePath = path.join(rootDir, "src", "app", `page.${language === 'ts' ? 'tsx' : ('js')}`);
141133

142134
if (!fs.existsSync(HomePagePath)) {
143-
console.log(chalk.red(`layout.${language === 'ts' ? 'tsx' : ('js')} not found at ${HomePagePath}`));
135+
console.log(chalk.red(`layout.${language === 'ts' ? 'tsx' : ('js')} not found at ${HomePagePath}`));
144136
return;
145137
}
146138
let homePageContent = fs.readFileSync(HomePagePath, "utf-8");
@@ -153,7 +145,6 @@ export async function setupAuthjs(state) {
153145
// Insert after other imports for cleanliness
154146
homePageContent = homePageContent.replace(/"use client"/, importauth);
155147
homePageChanged = true;
156-
console.log(chalk.green("✅ Added useEFfect,signIn and useSession import"));
157148
}
158149
const sessionGuard = `const { data: session, status } = useSession();\nuseEffect(() => {\n\tif (status === "unauthenticated") {\n\tsignIn(); // or signIn("google"), or signIn(undefined, { callbackUrl: "/dashboard" })\n}\n}, [status]);\n\nif (status === "loading" || status === "unauthenticated") return <p>Loading...</p>;`;
159150

@@ -163,30 +154,28 @@ export async function setupAuthjs(state) {
163154
`$1\n${sessionGuard}`
164155
);
165156
homePageChanged = true;
166-
console.log(chalk.green("✅ Injected session guard code inside Home()"));
167157
}
168158

169159
if (homePageChanged) {
170160
fs.writeFileSync(HomePagePath, homePageContent, "utf8");
171-
console.log(chalk.green(`🎉 Updated ${HomePagePath}`));
172161
} else {
173-
console.log(chalk.yellow("ℹ️ No changes needed – code already present"));
162+
console.log(chalk.yellow("No changes needed – code already present"));
174163
}
175164

176165
// --- Jsconfig.json ---
177166
if (language !== 'ts') {
178167
const jsconfigPath = path.join(rootDir, "jsconfig.json");
179168

180169
if (!fs.existsSync(jsconfigPath)) {
181-
console.error(chalk.red("jsconfig.json not found."));
170+
console.error(chalk.red("jsconfig.json not found."));
182171
} else {
183172
const raw = fs.readFileSync(jsconfigPath, "utf-8");
184173
let config;
185174

186175
try {
187176
config = JSON.parse(raw);
188177
} catch (err) {
189-
console.error(chalk.red("Failed to parse jsconfig.json:"), err);
178+
console.error(chalk.red("Failed to parse jsconfig.json:"), err);
190179
process.exit(1);
191180
}
192181

@@ -200,25 +189,24 @@ export async function setupAuthjs(state) {
200189

201190
// Pretty-print with 2-space indentation
202191
fs.writeFileSync(jsconfigPath, JSON.stringify(config, null, 2));
203-
console.log(chalk.green("✅ Added 'auth' path alias to jsconfig.json"));
204192
} else {
205-
console.log(chalk.yellow("ℹ️ 'auth' path alias already present, skipping."));
193+
console.log(chalk.yellow("'auth' path alias already present, skipping."));
206194
}
207195
}
208196
}
209197
else {
210198
const tsconfigPath = path.join(rootDir, "tsconfig.json");
211199

212200
if (!fs.existsSync(tsconfigPath)) {
213-
console.error(chalk.red("tsconfig.json not found."));
201+
console.error(chalk.red("tsconfig.json not found."));
214202
} else {
215203
const raw = fs.readFileSync(tsconfigPath, "utf-8");
216204
let config;
217205

218206
try {
219207
config = JSON.parse(raw);
220208
} catch (err) {
221-
console.error(chalk.red("Failed to parse tsconfig.json:"), err);
209+
console.error(chalk.red("Failed to parse tsconfig.json:"), err);
222210
process.exit(1);
223211
}
224212

@@ -231,61 +219,67 @@ export async function setupAuthjs(state) {
231219
config.compilerOptions.paths["auth"] = ["./lib/actions/auth.ts"];
232220

233221
fs.writeFileSync(tsconfigPath, JSON.stringify(config, null, 2));
234-
console.log(chalk.green("✅ Added 'auth' path alias to tsconfig.json"));
235222
} else {
236-
console.log(chalk.yellow("ℹ️ 'auth' path alias already exists, skipping."));
223+
console.log(chalk.yellow("'auth' path alias already exists, skipping."));
237224
}
238225
}
239226
}
240227

241228
} else {
242-
console.log(chalk.yellow('⚠️ src folder not found, skipping app/api creation.'));
229+
console.log(chalk.yellow('src folder not found, skipping app/api creation.'));
243230
}
244231

245232
} catch (error) {
246233
console.error(chalk.red("Error setting up Authjs for authentication"), error.message);
247234
console.log(chalk.yellow("You may need to finish authjs setup manually."));
235+
} finally {
236+
console.log(chalk.blue("For localhost testing, you may need an HTTPS proxy."));
237+
console.log(chalk.blue("Run the following command for temporary public URL:"));
238+
console.log(chalk.cyan("hp expose"));
248239
}
249240
} else {
250-
console.log(chalk.yellow(`⚠️ Auth.js setup for framework '${framework}' is not yet automated. Please refer to https://authjs.dev/ for manual setup instructions.`));
241+
console.log(chalk.yellow(`Auth.js setup for framework '${framework}' is not yet automated. Please refer to https://authjs.dev/ for manual setup instructions.`));
251242
}
252243
return;
253244
}
254245

255246
export async function setupClerk(state) {
256-
const { framework, projectName, language, styling } = state;
247+
const { framework, projectName, language, styling, database } = state;
257248

258-
console.log(chalk.blue(`🔐 Setting up clerk for ${framework} project: ${projectName} \n📝 Language: ${language}`));
249+
console.log(chalk.blue(`Setting up clerk for ${framework} project: ${projectName}`));
259250
const useTailwind = (styling === "tailwind");
260251
// Validate framework support
261252
if (!isFrameworkSupported(framework)) {
262-
console.log(chalk.red(`Framework '${framework}' is not supported for Auth.js setup.`));
263-
console.log(chalk.yellow('Supported frameworks: next, svelte, vue, vite-react, astro, nuxt, react'));
253+
console.log(chalk.red(`Framework '${framework}' is not supported for clerk setup.`));
254+
console.log(chalk.yellow('Supported frameworks: next, svelte, vue, vite-react, astro, nuxt'));
264255
return;
265256
}
266257
if (framework === 'next') {
267258
await setupNextClerk(state);
268-
return;
269259
} else if (framework === 'vue') {
270-
// Delegate Vue-specific Clerk setup to helper
271260
await setupVueClerk(state);
272-
return;
273-
274261
} else if (framework === 'svelte') {
275262
await setupSvelteClerk(state);
276-
return;
277-
278263
} else if (framework === 'vite-react') {
279264
await setupViteReactClerk(framework, projectName, language, styling,useTailwind, state.uiLibrary);
280-
return;
281265
} else if (framework === 'astro') {
282266
await setupClerkAstro(state);
283-
return;
284267
} else if (framework === "nuxt") {
285268
await setupClerkNuxt(state);
286-
return;
287269
} else {
288-
console.log(chalk.yellow(`clerk setup for framework '${framework}' is not yet automated. Please refer to https://clerk.com/docs for manual setup instructions.`));
270+
console.log(chalk.yellow(`Clerk setup for framework '${framework}' is not yet automated. Please refer to https://clerk.com/docs for manual setup instructions.`));
271+
}
272+
try {
273+
const mdPath = path.join('CLERKSETUP.md');
274+
let md = `# Clerk Setup\n\nFirst-time setup:\n1. Sign in to your Clerk dashboard and create a new application.\n2. Copy your 'CLERK_PUBLISHABLE_KEY' and 'CLERK_SECRET_KEY' into your .env | .env.local file.\n3. `;
275+
if (database === 'mongodb') {
276+
md += `\n4. Set up Clerk webhooks for DBsync:\n - In the Clerk dashboard, go to Webhooks and create a new webhook with the URL: <HTTPS_URL>/api/webhooks or <HTTPS_URL>/api/webhooks/clerk\n - Subscribe to events: user.created, user.updated, user.deleted\n - Also add CLERK_WEBHOOK_SIGNING_SECRET to your environment variables.`;
277+
}
278+
md += `\n\nFor localhost testing, you may need an HTTPS proxy.\nRun the following command for temporary public URL:\n\n hp expose\n`;
279+
280+
fs.writeFileSync(mdPath, md, 'utf8');
281+
} catch (err) {
282+
console.log(chalk.yellow('Could not write CLERKSETUP.md:'), err && err.message ? err.message : err);
289283
}
290284

291285
}
@@ -297,12 +291,15 @@ export async function setupAuth0(state) {
297291

298292
// Validate framework support
299293
if (!isFrameworkSupported(framework)) {
300-
console.log(chalk.red(`Framework '${framework}' is not supported for Auth.js setup.`));
301-
console.log(chalk.yellow('Supported frameworks: next, svelte, vue, vite-react, astro, nuxt'));
294+
console.log(chalk.red(`Framework '${framework}' is not supported for Auth0 setup.`));
295+
console.log(chalk.yellow('Supported frameworks: angular'));
302296
return;
303297
}
304298
if (framework === 'angular') {
305299
await setupAuth0Angular(state);
300+
console.log(chalk.blue("For localhost testing, you may need an HTTPS proxy."));
301+
console.log(chalk.blue("Run the following command for temporary public URL:"));
302+
console.log(chalk.cyan("hp expose"));
306303
return;
307304
} else {
308305
console.log(chalk.yellow(`Auth0 setup for framework '${framework}' is not yet automated. Please refer to https://auth0.com/docs/quickstart/spa/angular for manual setup instructions.`));

0 commit comments

Comments
 (0)