hale-commenting-system 1.0.7 → 2.0.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/README.md +154 -150
- package/cli/dist/index.js +100 -825
- package/cli/dist/index.js.map +1 -1
- package/dist/index.d.mts +6 -95
- package/dist/index.d.ts +6 -95
- package/dist/index.js +140 -1640
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +127 -1639
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -10
package/cli/dist/index.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk4 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/commands/init.ts
|
|
8
|
-
import
|
|
8
|
+
import chalk3 from "chalk";
|
|
9
9
|
import ora from "ora";
|
|
10
|
-
import
|
|
10
|
+
import inquirer from "inquirer";
|
|
11
11
|
|
|
12
12
|
// src/utils/detect.ts
|
|
13
13
|
import fs from "fs/promises";
|
|
@@ -52,177 +52,20 @@ async function detectBuildTool(cwd, deps) {
|
|
|
52
52
|
}
|
|
53
53
|
return "Unknown";
|
|
54
54
|
}
|
|
55
|
-
async function detectPlatform(cwd) {
|
|
56
|
-
try {
|
|
57
|
-
await fs.access(path.join(cwd, "vercel.json"));
|
|
58
|
-
return "vercel";
|
|
59
|
-
} catch {
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
await fs.access(path.join(cwd, "netlify.toml"));
|
|
63
|
-
return "netlify";
|
|
64
|
-
} catch {
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
55
|
|
|
69
56
|
// src/utils/logger.ts
|
|
70
57
|
import chalk from "chalk";
|
|
71
58
|
function printWelcome() {
|
|
72
|
-
console.log(chalk.bold.cyan("\n\u{1F680} Hale Commenting System Setup
|
|
73
|
-
|
|
74
|
-
function printSetupInstructions(platform, project) {
|
|
75
|
-
console.log(chalk.bold("\n\u{1F4DD} Manual Setup Required:\n"));
|
|
76
|
-
console.log(chalk.cyan("1. Add the CommentProvider to your App.tsx:\n"));
|
|
77
|
-
console.log(chalk.white(`import {
|
|
78
|
-
CommentProvider,
|
|
79
|
-
VersionProvider,
|
|
80
|
-
GitHubAuthProvider,
|
|
81
|
-
GitLabAuthProvider,
|
|
82
|
-
CommentOverlay,
|
|
83
|
-
CommentDrawer
|
|
84
|
-
} from 'hale-commenting-system';
|
|
85
|
-
import { BrowserRouter as Router } from 'react-router-dom';
|
|
86
|
-
import haleCommentsConfig from '../../hale-comments.config.json';
|
|
87
|
-
import React from 'react';
|
|
88
|
-
|
|
89
|
-
function App() {
|
|
90
|
-
const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<Router>
|
|
94
|
-
<GitHubAuthProvider config={haleCommentsConfig}>
|
|
95
|
-
<GitLabAuthProvider>
|
|
96
|
-
<VersionProvider>
|
|
97
|
-
<CommentProvider>
|
|
98
|
-
<CommentDrawer
|
|
99
|
-
selectedThreadId={selectedThreadId}
|
|
100
|
-
onThreadSelect={setSelectedThreadId}
|
|
101
|
-
>
|
|
102
|
-
<CommentOverlay
|
|
103
|
-
selectedThreadId={selectedThreadId}
|
|
104
|
-
onThreadSelect={setSelectedThreadId}
|
|
105
|
-
/>
|
|
106
|
-
{/* Your app content */}
|
|
107
|
-
</CommentDrawer>
|
|
108
|
-
</CommentProvider>
|
|
109
|
-
</VersionProvider>
|
|
110
|
-
</GitLabAuthProvider>
|
|
111
|
-
</GitHubAuthProvider>
|
|
112
|
-
</Router>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
`));
|
|
116
|
-
console.log(chalk.cyan("\n2. Deploy your serverless functions:\n"));
|
|
117
|
-
if (platform === "vercel") {
|
|
118
|
-
console.log(chalk.white(" vercel --prod"));
|
|
119
|
-
} else if (platform === "netlify") {
|
|
120
|
-
console.log(chalk.white(" netlify deploy --prod"));
|
|
121
|
-
} else {
|
|
122
|
-
console.log(chalk.white(" Follow your platform's deployment guide"));
|
|
123
|
-
}
|
|
124
|
-
console.log(chalk.cyan("\n3. Update your GitHub OAuth App callback URL:\n"));
|
|
125
|
-
console.log(chalk.white(" https://your-domain.com/api/github-oauth-callback\n"));
|
|
126
|
-
}
|
|
127
|
-
function printSuccess() {
|
|
128
|
-
console.log(chalk.green.bold("\n\u2713 Setup complete!\n"));
|
|
129
|
-
}
|
|
130
|
-
function printNextSteps() {
|
|
131
|
-
console.log(chalk.cyan("\nNext steps:"));
|
|
132
|
-
console.log(chalk.white("1. Review generated files in your project"));
|
|
133
|
-
console.log(chalk.white("2. Add the CommentProvider to your App.tsx (see instructions above)"));
|
|
134
|
-
console.log(chalk.white("3. Deploy your serverless functions"));
|
|
135
|
-
console.log(chalk.white("4. Run: npm run dev\n"));
|
|
136
|
-
console.log(chalk.dim('Run "hale-commenting-system validate" to verify your setup.\n'));
|
|
59
|
+
console.log(chalk.bold.cyan("\n\u{1F680} Hale Commenting System - Local Setup\n"));
|
|
60
|
+
console.log(chalk.dim("Setting up a localStorage-based commenting system for your app.\n"));
|
|
137
61
|
}
|
|
138
62
|
function printWarning(message) {
|
|
139
63
|
console.log(chalk.yellow(`\u26A0\uFE0F ${message}`));
|
|
140
64
|
}
|
|
141
65
|
|
|
142
|
-
// src/
|
|
143
|
-
import inquirer from "inquirer";
|
|
144
|
-
import chalk2 from "chalk";
|
|
145
|
-
async function promptPlatform(projectRoot) {
|
|
146
|
-
const detectedPlatform = await detectPlatform(projectRoot);
|
|
147
|
-
if (detectedPlatform) {
|
|
148
|
-
console.log(chalk2.green(`\u2713 Detected ${detectedPlatform} configuration`));
|
|
149
|
-
const { usePlatform } = await inquirer.prompt([
|
|
150
|
-
{
|
|
151
|
-
type: "confirm",
|
|
152
|
-
name: "usePlatform",
|
|
153
|
-
message: `Use ${detectedPlatform} for serverless functions?`,
|
|
154
|
-
default: true
|
|
155
|
-
}
|
|
156
|
-
]);
|
|
157
|
-
if (usePlatform) {
|
|
158
|
-
return detectedPlatform;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const { platform } = await inquirer.prompt([
|
|
162
|
-
{
|
|
163
|
-
type: "list",
|
|
164
|
-
name: "platform",
|
|
165
|
-
message: "Select your deployment platform:",
|
|
166
|
-
choices: [
|
|
167
|
-
{ name: "Local development (runs locally only)", value: "local" },
|
|
168
|
-
{ name: "Vercel", value: "vercel" },
|
|
169
|
-
{ name: "Netlify", value: "netlify" },
|
|
170
|
-
{ name: "Manual (I will configure myself)", value: "manual" }
|
|
171
|
-
]
|
|
172
|
-
}
|
|
173
|
-
]);
|
|
174
|
-
return platform;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// src/prompts/github.ts
|
|
178
|
-
import inquirer2 from "inquirer";
|
|
179
|
-
import chalk3 from "chalk";
|
|
180
|
-
async function promptGitHubConfig(skipPrompts = false) {
|
|
181
|
-
if (skipPrompts) {
|
|
182
|
-
console.log(chalk3.yellow("Using default/environment values..."));
|
|
183
|
-
return {
|
|
184
|
-
clientId: process.env.GITHUB_CLIENT_ID || "",
|
|
185
|
-
clientSecret: process.env.GITHUB_CLIENT_SECRET || "",
|
|
186
|
-
owner: process.env.GITHUB_OWNER || "",
|
|
187
|
-
repo: process.env.GITHUB_REPO || ""
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
console.log(chalk3.bold("\n\u{1F4CB} GitHub OAuth Configuration\n"));
|
|
191
|
-
console.log(chalk3.dim("You need to create a GitHub OAuth App:"));
|
|
192
|
-
console.log(chalk3.dim("https://github.com/settings/developers\n"));
|
|
193
|
-
const answers = await inquirer2.prompt([
|
|
194
|
-
{
|
|
195
|
-
type: "input",
|
|
196
|
-
name: "clientId",
|
|
197
|
-
message: "GitHub OAuth Client ID:",
|
|
198
|
-
validate: (input) => input.length > 0 || "Client ID is required"
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
type: "password",
|
|
202
|
-
name: "clientSecret",
|
|
203
|
-
message: "GitHub OAuth Client Secret:",
|
|
204
|
-
mask: "*",
|
|
205
|
-
validate: (input) => input.length > 0 || "Client Secret is required"
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
type: "input",
|
|
209
|
-
name: "owner",
|
|
210
|
-
message: "GitHub Repository Owner (user or org):",
|
|
211
|
-
validate: (input) => input.length > 0 || "Owner is required"
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
type: "input",
|
|
215
|
-
name: "repo",
|
|
216
|
-
message: "GitHub Repository Name:",
|
|
217
|
-
validate: (input) => input.length > 0 || "Repository name is required"
|
|
218
|
-
}
|
|
219
|
-
]);
|
|
220
|
-
return answers;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// src/generators/serverless.ts
|
|
66
|
+
// src/generators/code.ts
|
|
224
67
|
import path2 from "path";
|
|
225
|
-
import
|
|
68
|
+
import chalk2 from "chalk";
|
|
226
69
|
|
|
227
70
|
// src/utils/fs.ts
|
|
228
71
|
import fs2 from "fs/promises";
|
|
@@ -234,12 +77,6 @@ async function fileExists(filePath) {
|
|
|
234
77
|
return false;
|
|
235
78
|
}
|
|
236
79
|
}
|
|
237
|
-
async function ensureDir(dirPath) {
|
|
238
|
-
await fs2.mkdir(dirPath, { recursive: true });
|
|
239
|
-
}
|
|
240
|
-
async function appendToFile(filePath, content) {
|
|
241
|
-
await fs2.appendFile(filePath, content, "utf-8");
|
|
242
|
-
}
|
|
243
80
|
async function readFile(filePath) {
|
|
244
81
|
return fs2.readFile(filePath, "utf-8");
|
|
245
82
|
}
|
|
@@ -247,371 +84,7 @@ async function writeFile(filePath, content) {
|
|
|
247
84
|
await fs2.writeFile(filePath, content, "utf-8");
|
|
248
85
|
}
|
|
249
86
|
|
|
250
|
-
// src/templates/vercel-auth.ts
|
|
251
|
-
var vercelAuthLoginTemplate = `import { VercelRequest, VercelResponse } from '@vercel/node';
|
|
252
|
-
|
|
253
|
-
export default async function handler(req: VercelRequest, res: VercelResponse): Promise<void> {
|
|
254
|
-
try {
|
|
255
|
-
const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
|
|
256
|
-
|
|
257
|
-
if (!clientId) {
|
|
258
|
-
res.status(500).json({ error: 'GitHub OAuth not configured - missing client ID' });
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Get the base URL from the request
|
|
263
|
-
const protocol = (req.headers['x-forwarded-proto'] as string) || 'https';
|
|
264
|
-
const host = (req.headers.host || req.headers['x-forwarded-host']) as string;
|
|
265
|
-
|
|
266
|
-
if (!host) {
|
|
267
|
-
res.status(500).json({ error: 'Could not determine host' });
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const baseUrl = \`\${protocol}://\${host}\`;
|
|
272
|
-
const redirectUri = \`\${baseUrl}/api/github-oauth-callback\`;
|
|
273
|
-
|
|
274
|
-
// Redirect to GitHub OAuth
|
|
275
|
-
// Scope: public_repo allows read/write access to public repositories only
|
|
276
|
-
const githubAuthUrl = \`https://github.com/login/oauth/authorize?client_id=\${clientId}&redirect_uri=\${encodeURIComponent(redirectUri)}&scope=public_repo\`;
|
|
277
|
-
|
|
278
|
-
res.redirect(302, githubAuthUrl);
|
|
279
|
-
} catch (error: any) {
|
|
280
|
-
res.status(500).json({
|
|
281
|
-
error: 'Internal server error',
|
|
282
|
-
details: error.message
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
`;
|
|
287
|
-
var vercelAuthCallbackTemplate = `import { VercelRequest, VercelResponse } from '@vercel/node';
|
|
288
|
-
import axios from 'axios';
|
|
289
|
-
|
|
290
|
-
export default async function handler(req: VercelRequest, res: VercelResponse): Promise<void> {
|
|
291
|
-
try {
|
|
292
|
-
const code = req.query.code as string;
|
|
293
|
-
const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
|
|
294
|
-
const clientSecret = process.env.GITHUB_CLIENT_SECRET;
|
|
295
|
-
|
|
296
|
-
if (!code) {
|
|
297
|
-
res.status(400).json({ error: 'No code provided' });
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (!clientId || !clientSecret) {
|
|
302
|
-
res.status(500).json({ error: 'GitHub OAuth not configured properly' });
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Exchange code for access token
|
|
307
|
-
const tokenResponse = await axios.post(
|
|
308
|
-
'https://github.com/login/oauth/access_token',
|
|
309
|
-
{
|
|
310
|
-
client_id: clientId,
|
|
311
|
-
client_secret: clientSecret,
|
|
312
|
-
code,
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
headers: {
|
|
316
|
-
Accept: 'application/json',
|
|
317
|
-
},
|
|
318
|
-
}
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
const accessToken = tokenResponse.data.access_token;
|
|
322
|
-
|
|
323
|
-
if (!accessToken) {
|
|
324
|
-
throw new Error('No access token received');
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Get user info
|
|
328
|
-
const userResponse = await axios.get('https://api.github.com/user', {
|
|
329
|
-
headers: {
|
|
330
|
-
Authorization: \`token \${accessToken}\`,
|
|
331
|
-
},
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
const user = userResponse.data;
|
|
335
|
-
|
|
336
|
-
// Redirect back to the app with token in URL fragment (client-side only)
|
|
337
|
-
const protocol = req.headers['x-forwarded-proto'] || 'https';
|
|
338
|
-
const host = req.headers.host || req.headers['x-forwarded-host'];
|
|
339
|
-
const baseUrl = \`\${protocol}://\${host}\`;
|
|
340
|
-
const redirectUrl = \`\${baseUrl}/#/auth-callback?token=\${accessToken}&login=\${user.login}&avatar=\${encodeURIComponent(user.avatar_url)}\`;
|
|
341
|
-
|
|
342
|
-
res.redirect(302, redirectUrl);
|
|
343
|
-
} catch (error: any) {
|
|
344
|
-
res.status(500).json({
|
|
345
|
-
error: 'Failed to exchange code for token',
|
|
346
|
-
details: error.message,
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
`;
|
|
351
|
-
|
|
352
|
-
// src/templates/netlify-auth.ts
|
|
353
|
-
var netlifyAuthLoginTemplate = `import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';
|
|
354
|
-
import axios from 'axios';
|
|
355
|
-
|
|
356
|
-
const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
|
|
357
|
-
try {
|
|
358
|
-
const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
|
|
359
|
-
|
|
360
|
-
if (!clientId) {
|
|
361
|
-
return {
|
|
362
|
-
statusCode: 500,
|
|
363
|
-
body: JSON.stringify({ error: 'GitHub OAuth not configured - missing client ID' })
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Get the base URL from the request
|
|
368
|
-
const protocol = event.headers['x-forwarded-proto'] || 'https';
|
|
369
|
-
const host = event.headers.host;
|
|
370
|
-
|
|
371
|
-
if (!host) {
|
|
372
|
-
return {
|
|
373
|
-
statusCode: 500,
|
|
374
|
-
body: JSON.stringify({ error: 'Could not determine host' })
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const baseUrl = \`\${protocol}://\${host}\`;
|
|
379
|
-
const redirectUri = \`\${baseUrl}/.netlify/functions/github-oauth-callback\`;
|
|
380
|
-
|
|
381
|
-
// Redirect to GitHub OAuth
|
|
382
|
-
const githubAuthUrl = \`https://github.com/login/oauth/authorize?client_id=\${clientId}&redirect_uri=\${encodeURIComponent(redirectUri)}&scope=public_repo\`;
|
|
383
|
-
|
|
384
|
-
return {
|
|
385
|
-
statusCode: 302,
|
|
386
|
-
headers: {
|
|
387
|
-
Location: githubAuthUrl
|
|
388
|
-
},
|
|
389
|
-
body: ''
|
|
390
|
-
};
|
|
391
|
-
} catch (error: any) {
|
|
392
|
-
return {
|
|
393
|
-
statusCode: 500,
|
|
394
|
-
body: JSON.stringify({
|
|
395
|
-
error: 'Internal server error',
|
|
396
|
-
details: error.message
|
|
397
|
-
})
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
export { handler };
|
|
403
|
-
`;
|
|
404
|
-
var netlifyAuthCallbackTemplate = `import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';
|
|
405
|
-
import axios from 'axios';
|
|
406
|
-
|
|
407
|
-
const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
|
|
408
|
-
try {
|
|
409
|
-
const code = event.queryStringParameters?.code;
|
|
410
|
-
const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
|
|
411
|
-
const clientSecret = process.env.GITHUB_CLIENT_SECRET;
|
|
412
|
-
|
|
413
|
-
if (!code) {
|
|
414
|
-
return {
|
|
415
|
-
statusCode: 400,
|
|
416
|
-
body: JSON.stringify({ error: 'No code provided' })
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (!clientId || !clientSecret) {
|
|
421
|
-
return {
|
|
422
|
-
statusCode: 500,
|
|
423
|
-
body: JSON.stringify({ error: 'GitHub OAuth not configured properly' })
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Exchange code for access token
|
|
428
|
-
const tokenResponse = await axios.post(
|
|
429
|
-
'https://github.com/login/oauth/access_token',
|
|
430
|
-
{
|
|
431
|
-
client_id: clientId,
|
|
432
|
-
client_secret: clientSecret,
|
|
433
|
-
code,
|
|
434
|
-
},
|
|
435
|
-
{
|
|
436
|
-
headers: {
|
|
437
|
-
Accept: 'application/json',
|
|
438
|
-
},
|
|
439
|
-
}
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
const accessToken = tokenResponse.data.access_token;
|
|
443
|
-
|
|
444
|
-
if (!accessToken) {
|
|
445
|
-
throw new Error('No access token received');
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Get user info
|
|
449
|
-
const userResponse = await axios.get('https://api.github.com/user', {
|
|
450
|
-
headers: {
|
|
451
|
-
Authorization: \`token \${accessToken}\`,
|
|
452
|
-
},
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
const user = userResponse.data;
|
|
456
|
-
|
|
457
|
-
// Redirect back to the app with token in URL fragment
|
|
458
|
-
const protocol = event.headers['x-forwarded-proto'] || 'https';
|
|
459
|
-
const host = event.headers.host;
|
|
460
|
-
const baseUrl = \`\${protocol}://\${host}\`;
|
|
461
|
-
const redirectUrl = \`\${baseUrl}/#/auth-callback?token=\${accessToken}&login=\${user.login}&avatar=\${encodeURIComponent(user.avatar_url)}\`;
|
|
462
|
-
|
|
463
|
-
return {
|
|
464
|
-
statusCode: 302,
|
|
465
|
-
headers: {
|
|
466
|
-
Location: redirectUrl
|
|
467
|
-
},
|
|
468
|
-
body: ''
|
|
469
|
-
};
|
|
470
|
-
} catch (error: any) {
|
|
471
|
-
return {
|
|
472
|
-
statusCode: 500,
|
|
473
|
-
body: JSON.stringify({
|
|
474
|
-
error: 'Failed to exchange code for token',
|
|
475
|
-
details: error.message,
|
|
476
|
-
})
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
export { handler };
|
|
482
|
-
`;
|
|
483
|
-
|
|
484
|
-
// src/generators/serverless.ts
|
|
485
|
-
async function generateServerless(platform, projectRoot) {
|
|
486
|
-
if (platform === "vercel" || platform === "local") {
|
|
487
|
-
const apiDir = path2.join(projectRoot, "api");
|
|
488
|
-
await ensureDir(apiDir);
|
|
489
|
-
await writeFile(
|
|
490
|
-
path2.join(apiDir, "github-oauth-login.ts"),
|
|
491
|
-
vercelAuthLoginTemplate
|
|
492
|
-
);
|
|
493
|
-
await writeFile(
|
|
494
|
-
path2.join(apiDir, "github-oauth-callback.ts"),
|
|
495
|
-
vercelAuthCallbackTemplate
|
|
496
|
-
);
|
|
497
|
-
console.log(chalk4.dim(" \u2192 Created api/github-oauth-login.ts"));
|
|
498
|
-
console.log(chalk4.dim(" \u2192 Created api/github-oauth-callback.ts"));
|
|
499
|
-
} else if (platform === "netlify") {
|
|
500
|
-
const functionsDir = path2.join(projectRoot, "netlify", "functions");
|
|
501
|
-
await ensureDir(functionsDir);
|
|
502
|
-
await writeFile(
|
|
503
|
-
path2.join(functionsDir, "github-oauth-login.ts"),
|
|
504
|
-
netlifyAuthLoginTemplate
|
|
505
|
-
);
|
|
506
|
-
await writeFile(
|
|
507
|
-
path2.join(functionsDir, "github-oauth-callback.ts"),
|
|
508
|
-
netlifyAuthCallbackTemplate
|
|
509
|
-
);
|
|
510
|
-
console.log(chalk4.dim(" \u2192 Created netlify/functions/github-oauth-login.ts"));
|
|
511
|
-
console.log(chalk4.dim(" \u2192 Created netlify/functions/github-oauth-callback.ts"));
|
|
512
|
-
} else {
|
|
513
|
-
console.log(chalk4.yellow(" Manual platform selected. You must create serverless functions yourself."));
|
|
514
|
-
console.log(chalk4.dim(" See: https://github.com/apollo/commenting-system/blob/main/docs/manual-setup.md"));
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// src/generators/env.ts
|
|
519
|
-
import path3 from "path";
|
|
520
|
-
import chalk5 from "chalk";
|
|
521
|
-
async function generateEnvFiles(githubConfig, platform, projectRoot) {
|
|
522
|
-
const envContent = `
|
|
523
|
-
# Apollo Commenting System Configuration
|
|
524
|
-
# Generated by apollo-comments CLI
|
|
525
|
-
|
|
526
|
-
# GitHub OAuth
|
|
527
|
-
GITHUB_CLIENT_ID=${githubConfig.clientId}
|
|
528
|
-
GITHUB_CLIENT_SECRET=${githubConfig.clientSecret}
|
|
529
|
-
GITHUB_OWNER=${githubConfig.owner}
|
|
530
|
-
GITHUB_REPO=${githubConfig.repo}
|
|
531
|
-
|
|
532
|
-
# Vite (if using Vite)
|
|
533
|
-
VITE_GITHUB_CLIENT_ID=${githubConfig.clientId}
|
|
534
|
-
VITE_GITHUB_OWNER=${githubConfig.owner}
|
|
535
|
-
VITE_GITHUB_REPO=${githubConfig.repo}
|
|
536
|
-
`;
|
|
537
|
-
const envLocalPath = path3.join(projectRoot, ".env.local");
|
|
538
|
-
const envPath = path3.join(projectRoot, ".env");
|
|
539
|
-
const envLocalExists = await fileExists(envLocalPath);
|
|
540
|
-
if (envLocalExists) {
|
|
541
|
-
const existingEnv = await readFile(envLocalPath);
|
|
542
|
-
if (existingEnv.includes("GITHUB_CLIENT_ID")) {
|
|
543
|
-
console.log(chalk5.yellow(" .env.local already contains GitHub config. Skipping..."));
|
|
544
|
-
} else {
|
|
545
|
-
await appendToFile(envLocalPath, envContent);
|
|
546
|
-
console.log(chalk5.dim(" \u2192 Updated .env.local"));
|
|
547
|
-
}
|
|
548
|
-
} else {
|
|
549
|
-
await writeFile(envLocalPath, envContent.trim());
|
|
550
|
-
console.log(chalk5.dim(" \u2192 Created .env.local"));
|
|
551
|
-
}
|
|
552
|
-
const envExampleContent = `# Apollo Commenting System Configuration
|
|
553
|
-
|
|
554
|
-
# GitHub OAuth (get from https://github.com/settings/developers)
|
|
555
|
-
GITHUB_CLIENT_ID=your_client_id_here
|
|
556
|
-
GITHUB_CLIENT_SECRET=your_client_secret_here
|
|
557
|
-
GITHUB_OWNER=your_github_org
|
|
558
|
-
GITHUB_REPO=your_repo_name
|
|
559
|
-
|
|
560
|
-
# Vite (if using Vite)
|
|
561
|
-
VITE_GITHUB_CLIENT_ID=your_client_id_here
|
|
562
|
-
VITE_GITHUB_OWNER=your_github_org
|
|
563
|
-
VITE_GITHUB_REPO=your_repo_name
|
|
564
|
-
`;
|
|
565
|
-
await writeFile(path3.join(projectRoot, ".env.example"), envExampleContent);
|
|
566
|
-
console.log(chalk5.dim(" \u2192 Created .env.example"));
|
|
567
|
-
const gitignorePath = path3.join(projectRoot, ".gitignore");
|
|
568
|
-
if (await fileExists(gitignorePath)) {
|
|
569
|
-
const gitignoreContent = await readFile(gitignorePath);
|
|
570
|
-
if (!gitignoreContent.includes(".env.local")) {
|
|
571
|
-
await appendToFile(gitignorePath, "\n# Apollo Commenting System\n.env.local\n");
|
|
572
|
-
console.log(chalk5.dim(" \u2192 Updated .gitignore"));
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// src/generators/config.ts
|
|
578
|
-
import path4 from "path";
|
|
579
|
-
import chalk6 from "chalk";
|
|
580
|
-
async function generateConfig(githubConfig, platform, projectRoot) {
|
|
581
|
-
let redirectUri = "http://localhost:9000/api/github-oauth-callback";
|
|
582
|
-
if (platform === "netlify") {
|
|
583
|
-
redirectUri = "https://your-domain.com/.netlify/functions/github-oauth-callback";
|
|
584
|
-
} else if (platform === "vercel") {
|
|
585
|
-
redirectUri = "https://your-domain.com/api/github-oauth-callback";
|
|
586
|
-
}
|
|
587
|
-
const config = {
|
|
588
|
-
version: "1.0.0",
|
|
589
|
-
platform,
|
|
590
|
-
github: {
|
|
591
|
-
owner: githubConfig.owner,
|
|
592
|
-
repo: githubConfig.repo,
|
|
593
|
-
clientId: githubConfig.clientId
|
|
594
|
-
},
|
|
595
|
-
redirectUri,
|
|
596
|
-
features: {
|
|
597
|
-
aiSummarization: true,
|
|
598
|
-
versionTracking: true,
|
|
599
|
-
gitlabIntegration: false
|
|
600
|
-
},
|
|
601
|
-
labels: [
|
|
602
|
-
{ name: "comment", color: "0075ca", description: "User feedback comment" },
|
|
603
|
-
{ name: "prototype-feedback", color: "d93f0b", description: "Prototype feedback" },
|
|
604
|
-
{ name: "needs-review", color: "fbca04", description: "Needs team review" }
|
|
605
|
-
]
|
|
606
|
-
};
|
|
607
|
-
const configPath = path4.join(projectRoot, "hale-comments.config.json");
|
|
608
|
-
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
609
|
-
console.log(chalk6.dim(" \u2192 Created hale-comments.config.json"));
|
|
610
|
-
}
|
|
611
|
-
|
|
612
87
|
// src/generators/code.ts
|
|
613
|
-
import path5 from "path";
|
|
614
|
-
import chalk7 from "chalk";
|
|
615
88
|
async function integrateProviders(projectRoot) {
|
|
616
89
|
const possiblePaths = [
|
|
617
90
|
"src/app/index.tsx",
|
|
@@ -621,7 +94,7 @@ async function integrateProviders(projectRoot) {
|
|
|
621
94
|
];
|
|
622
95
|
let appFilePath = null;
|
|
623
96
|
for (const p of possiblePaths) {
|
|
624
|
-
const fullPath =
|
|
97
|
+
const fullPath = path2.join(projectRoot, p);
|
|
625
98
|
if (await fileExists(fullPath)) {
|
|
626
99
|
appFilePath = fullPath;
|
|
627
100
|
break;
|
|
@@ -636,11 +109,11 @@ async function integrateProviders(projectRoot) {
|
|
|
636
109
|
}
|
|
637
110
|
try {
|
|
638
111
|
const content = await readFile(appFilePath);
|
|
639
|
-
if (content.includes("hale-commenting-system") || content.includes("
|
|
112
|
+
if (content.includes("hale-commenting-system") || content.includes("CommentProvider")) {
|
|
640
113
|
return {
|
|
641
114
|
success: false,
|
|
642
115
|
filePath: appFilePath,
|
|
643
|
-
message: "
|
|
116
|
+
message: "Commenting system already integrated."
|
|
644
117
|
};
|
|
645
118
|
}
|
|
646
119
|
const modifiedContent = injectProviders(content);
|
|
@@ -655,7 +128,7 @@ async function integrateProviders(projectRoot) {
|
|
|
655
128
|
return {
|
|
656
129
|
success: true,
|
|
657
130
|
filePath: appFilePath,
|
|
658
|
-
message: `Successfully integrated providers into ${
|
|
131
|
+
message: `Successfully integrated providers into ${path2.relative(projectRoot, appFilePath)}`
|
|
659
132
|
};
|
|
660
133
|
} catch (error) {
|
|
661
134
|
return {
|
|
@@ -668,13 +141,9 @@ async function integrateProviders(projectRoot) {
|
|
|
668
141
|
function injectProviders(content) {
|
|
669
142
|
const imports = `import {
|
|
670
143
|
CommentProvider,
|
|
671
|
-
VersionProvider,
|
|
672
|
-
GitHubAuthProvider,
|
|
673
|
-
GitLabAuthProvider,
|
|
674
144
|
CommentOverlay,
|
|
675
145
|
CommentDrawer
|
|
676
|
-
} from 'hale-commenting-system'
|
|
677
|
-
import haleCommentsConfig from '../../hale-comments.config.json';`;
|
|
146
|
+
} from 'hale-commenting-system';`;
|
|
678
147
|
const importRegex = /import\s+.*?from\s+['"].*?['"];?/g;
|
|
679
148
|
const matches = content.match(importRegex);
|
|
680
149
|
if (!matches || matches.length === 0) {
|
|
@@ -717,68 +186,50 @@ import haleCommentsConfig from '../../hale-comments.config.json';`;
|
|
|
717
186
|
const innerContent = routerMatch[2];
|
|
718
187
|
wrappedReturn = `
|
|
719
188
|
<${routerType}>
|
|
720
|
-
<
|
|
721
|
-
<
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
${innerContent.trim()}
|
|
733
|
-
</CommentDrawer>
|
|
734
|
-
</CommentProvider>
|
|
735
|
-
</VersionProvider>
|
|
736
|
-
</GitLabAuthProvider>
|
|
737
|
-
</GitHubAuthProvider>
|
|
189
|
+
<CommentProvider>
|
|
190
|
+
<CommentDrawer
|
|
191
|
+
selectedThreadId={selectedThreadId}
|
|
192
|
+
onThreadSelect={setSelectedThreadId}
|
|
193
|
+
>
|
|
194
|
+
<CommentOverlay
|
|
195
|
+
selectedThreadId={selectedThreadId}
|
|
196
|
+
onThreadSelect={setSelectedThreadId}
|
|
197
|
+
/>
|
|
198
|
+
${innerContent.trim()}
|
|
199
|
+
</CommentDrawer>
|
|
200
|
+
</CommentProvider>
|
|
738
201
|
</${routerType}>
|
|
739
202
|
`;
|
|
740
203
|
} else {
|
|
741
204
|
wrappedReturn = `
|
|
742
|
-
<
|
|
743
|
-
<
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
${originalReturn}
|
|
755
|
-
</CommentDrawer>
|
|
756
|
-
</CommentProvider>
|
|
757
|
-
</VersionProvider>
|
|
758
|
-
</GitLabAuthProvider>
|
|
759
|
-
</GitHubAuthProvider>
|
|
205
|
+
<CommentProvider>
|
|
206
|
+
<CommentDrawer
|
|
207
|
+
selectedThreadId={selectedThreadId}
|
|
208
|
+
onThreadSelect={setSelectedThreadId}
|
|
209
|
+
>
|
|
210
|
+
<CommentOverlay
|
|
211
|
+
selectedThreadId={selectedThreadId}
|
|
212
|
+
onThreadSelect={setSelectedThreadId}
|
|
213
|
+
/>
|
|
214
|
+
${originalReturn}
|
|
215
|
+
</CommentDrawer>
|
|
216
|
+
</CommentProvider>
|
|
760
217
|
`;
|
|
761
218
|
}
|
|
762
219
|
} else {
|
|
763
220
|
wrappedReturn = `
|
|
764
|
-
<
|
|
765
|
-
<
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
${originalReturn}
|
|
777
|
-
</CommentDrawer>
|
|
778
|
-
</CommentProvider>
|
|
779
|
-
</VersionProvider>
|
|
780
|
-
</GitLabAuthProvider>
|
|
781
|
-
</GitHubAuthProvider>
|
|
221
|
+
<CommentProvider>
|
|
222
|
+
<CommentDrawer
|
|
223
|
+
selectedThreadId={selectedThreadId}
|
|
224
|
+
onThreadSelect={setSelectedThreadId}
|
|
225
|
+
>
|
|
226
|
+
<CommentOverlay
|
|
227
|
+
selectedThreadId={selectedThreadId}
|
|
228
|
+
onThreadSelect={setSelectedThreadId}
|
|
229
|
+
/>
|
|
230
|
+
${originalReturn}
|
|
231
|
+
</CommentDrawer>
|
|
232
|
+
</CommentProvider>
|
|
782
233
|
`;
|
|
783
234
|
}
|
|
784
235
|
content = content.slice(0, returnStart) + wrappedReturn + content.slice(returnEnd);
|
|
@@ -786,73 +237,15 @@ import haleCommentsConfig from '../../hale-comments.config.json';`;
|
|
|
786
237
|
return content;
|
|
787
238
|
}
|
|
788
239
|
function showIntegrationDiff(filePath) {
|
|
789
|
-
console.log(
|
|
790
|
-
console.log(
|
|
791
|
-
console.log(
|
|
792
|
-
console.log(
|
|
793
|
-
console.log(
|
|
794
|
-
console.log(
|
|
240
|
+
console.log(chalk2.cyan("\nChanges made to your App file:"));
|
|
241
|
+
console.log(chalk2.white("\u2022 Added commenting system imports"));
|
|
242
|
+
console.log(chalk2.white("\u2022 Added state hook for comment thread selection"));
|
|
243
|
+
console.log(chalk2.white("\u2022 Wrapped app with CommentProvider"));
|
|
244
|
+
console.log(chalk2.white("\u2022 Added CommentDrawer and CommentOverlay components\n"));
|
|
245
|
+
console.log(chalk2.dim(`Modified: ${filePath}
|
|
795
246
|
`));
|
|
796
247
|
}
|
|
797
248
|
|
|
798
|
-
// src/validators/github.ts
|
|
799
|
-
import axios from "axios";
|
|
800
|
-
async function validateGitHubConnection(config) {
|
|
801
|
-
try {
|
|
802
|
-
const response = await axios.get("https://api.github.com/rate_limit", {
|
|
803
|
-
headers: {
|
|
804
|
-
"Accept": "application/vnd.github.v3+json"
|
|
805
|
-
},
|
|
806
|
-
timeout: 5e3
|
|
807
|
-
});
|
|
808
|
-
return response.status === 200;
|
|
809
|
-
} catch (error) {
|
|
810
|
-
console.error("GitHub connection error:", error);
|
|
811
|
-
return false;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
// src/validators/repo.ts
|
|
816
|
-
import axios2 from "axios";
|
|
817
|
-
import chalk8 from "chalk";
|
|
818
|
-
async function validateRepoAccess(config) {
|
|
819
|
-
try {
|
|
820
|
-
const response = await axios2.get(
|
|
821
|
-
`https://api.github.com/repos/${config.owner}/${config.repo}`,
|
|
822
|
-
{
|
|
823
|
-
headers: {
|
|
824
|
-
"Accept": "application/vnd.github.v3+json"
|
|
825
|
-
},
|
|
826
|
-
timeout: 5e3
|
|
827
|
-
}
|
|
828
|
-
);
|
|
829
|
-
return response.status === 200;
|
|
830
|
-
} catch (error) {
|
|
831
|
-
if (error.response?.status === 404) {
|
|
832
|
-
console.log(chalk8.yellow(` Repository ${config.owner}/${config.repo} not found or is private`));
|
|
833
|
-
console.log(chalk8.yellow(" Note: Private repos require authentication at runtime"));
|
|
834
|
-
return true;
|
|
835
|
-
}
|
|
836
|
-
console.error(" Repo access error:", error.message);
|
|
837
|
-
return false;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// src/validators/labels.ts
|
|
842
|
-
import chalk9 from "chalk";
|
|
843
|
-
var REQUIRED_LABELS = [
|
|
844
|
-
{ name: "comment", color: "0075ca", description: "User feedback comment" },
|
|
845
|
-
{ name: "prototype-feedback", color: "d93f0b", description: "Prototype feedback" },
|
|
846
|
-
{ name: "needs-review", color: "fbca04", description: "Needs team review" }
|
|
847
|
-
];
|
|
848
|
-
async function ensureLabels(config) {
|
|
849
|
-
console.log(chalk9.dim("\n Required labels for GitHub issues:"));
|
|
850
|
-
REQUIRED_LABELS.forEach((label) => {
|
|
851
|
-
console.log(chalk9.dim(` - ${label.name} (#${label.color}): ${label.description}`));
|
|
852
|
-
});
|
|
853
|
-
console.log(chalk9.dim(" These will be created automatically at runtime with proper authentication.\n"));
|
|
854
|
-
}
|
|
855
|
-
|
|
856
249
|
// src/commands/init.ts
|
|
857
250
|
async function initCommand(options) {
|
|
858
251
|
printWelcome();
|
|
@@ -867,13 +260,13 @@ async function initCommand(options) {
|
|
|
867
260
|
}
|
|
868
261
|
if (!project.isReact) {
|
|
869
262
|
spinner.fail("This package requires a React project");
|
|
870
|
-
console.log(
|
|
263
|
+
console.log(chalk3.red("\n\u2717 Hale Commenting System requires React.\n"));
|
|
871
264
|
process.exit(1);
|
|
872
265
|
}
|
|
873
266
|
if (!project.hasPatternFly) {
|
|
874
267
|
printWarning("PatternFly not detected. This package is optimized for PatternFly projects.");
|
|
875
268
|
if (!options.yes) {
|
|
876
|
-
const { continueAnyway } = await
|
|
269
|
+
const { continueAnyway } = await inquirer.prompt([
|
|
877
270
|
{
|
|
878
271
|
type: "confirm",
|
|
879
272
|
name: "continueAnyway",
|
|
@@ -882,185 +275,67 @@ async function initCommand(options) {
|
|
|
882
275
|
}
|
|
883
276
|
]);
|
|
884
277
|
if (!continueAnyway) {
|
|
885
|
-
console.log(
|
|
278
|
+
console.log(chalk3.dim("\nSetup cancelled.\n"));
|
|
886
279
|
process.exit(0);
|
|
887
280
|
}
|
|
888
281
|
}
|
|
889
282
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
process.exit(1);
|
|
904
|
-
}
|
|
905
|
-
spinner.succeed("Repository access confirmed");
|
|
906
|
-
spinner.start("Checking issue labels...");
|
|
907
|
-
await ensureLabels(githubConfig);
|
|
908
|
-
spinner.succeed("Issue labels documented");
|
|
909
|
-
spinner.start("Generating serverless functions...");
|
|
910
|
-
try {
|
|
911
|
-
await generateServerless(platform, project.root);
|
|
912
|
-
spinner.succeed("Serverless functions created");
|
|
913
|
-
} catch (error) {
|
|
914
|
-
spinner.fail(`Failed to generate serverless functions: ${error.message}`);
|
|
915
|
-
process.exit(1);
|
|
916
|
-
}
|
|
917
|
-
spinner.start("Creating environment files...");
|
|
918
|
-
try {
|
|
919
|
-
await generateEnvFiles(githubConfig, platform, project.root);
|
|
920
|
-
spinner.succeed("Environment files created");
|
|
921
|
-
} catch (error) {
|
|
922
|
-
spinner.fail(`Failed to create environment files: ${error.message}`);
|
|
923
|
-
process.exit(1);
|
|
924
|
-
}
|
|
925
|
-
spinner.start("Creating hale-comments.config.json...");
|
|
926
|
-
try {
|
|
927
|
-
await generateConfig(githubConfig, platform, project.root);
|
|
928
|
-
spinner.succeed("Configuration file created");
|
|
929
|
-
} catch (error) {
|
|
930
|
-
spinner.fail(`Failed to create config file: ${error.message}`);
|
|
931
|
-
process.exit(1);
|
|
932
|
-
}
|
|
933
|
-
printSuccess();
|
|
934
|
-
if (!options.yes) {
|
|
935
|
-
const { autoIntegrate } = await inquirer3.prompt([
|
|
936
|
-
{
|
|
937
|
-
type: "confirm",
|
|
938
|
-
name: "autoIntegrate",
|
|
939
|
-
message: "Automatically integrate providers into your App file?",
|
|
940
|
-
default: true
|
|
941
|
-
}
|
|
942
|
-
]);
|
|
943
|
-
if (autoIntegrate) {
|
|
944
|
-
spinner.start("Integrating providers...");
|
|
945
|
-
const result = await integrateProviders(project.root);
|
|
946
|
-
if (result.success) {
|
|
947
|
-
spinner.succeed("Providers integrated!");
|
|
948
|
-
showIntegrationDiff(result.filePath);
|
|
949
|
-
console.log(chalk10.green("\u2713 Your app is ready to use the commenting system!\n"));
|
|
950
|
-
console.log(chalk10.cyan("Next steps:"));
|
|
951
|
-
console.log(chalk10.white("1. Review the changes in your App file"));
|
|
952
|
-
console.log(chalk10.white("2. Start your dev server: npm run start:dev"));
|
|
953
|
-
console.log(chalk10.white("3. Open http://localhost:9000\n"));
|
|
954
|
-
} else {
|
|
955
|
-
spinner.warn(result.message);
|
|
956
|
-
console.log(chalk10.yellow("\nFalling back to manual integration:\n"));
|
|
957
|
-
printSetupInstructions(platform, project);
|
|
958
|
-
printNextSteps();
|
|
959
|
-
}
|
|
960
|
-
} else {
|
|
961
|
-
printSetupInstructions(platform, project);
|
|
962
|
-
printNextSteps();
|
|
963
|
-
}
|
|
283
|
+
console.log(chalk3.cyan("\n\u{1F4E6} Setting up local commenting system...\n"));
|
|
284
|
+
spinner.start("Integrating commenting system into your app...");
|
|
285
|
+
const result = await integrateProviders(project.root);
|
|
286
|
+
if (result.success) {
|
|
287
|
+
spinner.succeed("Commenting system integrated!");
|
|
288
|
+
showIntegrationDiff(result.filePath);
|
|
289
|
+
console.log(chalk3.green("\n\u2713 Setup complete! Your app is ready to use the commenting system.\n"));
|
|
290
|
+
console.log(chalk3.cyan("Next steps:"));
|
|
291
|
+
console.log(chalk3.white("1. Review the changes in your App file"));
|
|
292
|
+
console.log(chalk3.white("2. Start your dev server (e.g., npm run start:dev)"));
|
|
293
|
+
console.log(chalk3.white("3. Open your app in the browser"));
|
|
294
|
+
console.log(chalk3.white("4. Click anywhere to add comments (stored in localStorage)\n"));
|
|
295
|
+
console.log(chalk3.dim("\u{1F4A1} Tip: Comments persist across page refreshes and are stored locally in your browser.\n"));
|
|
964
296
|
} else {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
}
|
|
297
|
+
spinner.warn(result.message);
|
|
298
|
+
console.log(chalk3.yellow("\n\u26A0\uFE0F Could not automatically integrate the commenting system.\n"));
|
|
299
|
+
console.log(chalk3.white("Please manually add the following to your App component:\n"));
|
|
300
|
+
console.log(chalk3.dim(`import { CommentProvider, CommentOverlay, CommentDrawer } from 'hale-commenting-system';
|
|
301
|
+
import { BrowserRouter as Router } from 'react-router-dom';
|
|
302
|
+
import React from 'react';
|
|
969
303
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
} catch (error) {
|
|
992
|
-
spinner.fail("Invalid configuration file");
|
|
993
|
-
console.log(chalk11.red("\n\u2717 Could not parse hale-comments.config.json\n"));
|
|
994
|
-
process.exit(1);
|
|
995
|
-
}
|
|
996
|
-
spinner.start("Checking environment files...");
|
|
997
|
-
const envPath = path6.join(cwd, ".env.local");
|
|
998
|
-
const hasEnv = await fileExists(envPath);
|
|
999
|
-
if (!hasEnv) {
|
|
1000
|
-
spinner.warn("Environment file not found (.env.local)");
|
|
1001
|
-
} else {
|
|
1002
|
-
spinner.succeed("Environment file found");
|
|
1003
|
-
}
|
|
1004
|
-
spinner.start("Checking serverless functions...");
|
|
1005
|
-
let hasServerless = false;
|
|
1006
|
-
if (config.platform === "vercel") {
|
|
1007
|
-
const apiDir = path6.join(cwd, "api");
|
|
1008
|
-
const loginPath = path6.join(apiDir, "github-oauth-login.ts");
|
|
1009
|
-
const callbackPath = path6.join(apiDir, "github-oauth-callback.ts");
|
|
1010
|
-
hasServerless = await fileExists(loginPath) && await fileExists(callbackPath);
|
|
1011
|
-
} else if (config.platform === "netlify") {
|
|
1012
|
-
const functionsDir = path6.join(cwd, "netlify", "functions");
|
|
1013
|
-
const loginPath = path6.join(functionsDir, "github-oauth-login.ts");
|
|
1014
|
-
const callbackPath = path6.join(functionsDir, "github-oauth-callback.ts");
|
|
1015
|
-
hasServerless = await fileExists(loginPath) && await fileExists(callbackPath);
|
|
1016
|
-
}
|
|
1017
|
-
if (!hasServerless && config.platform !== "manual") {
|
|
1018
|
-
spinner.warn("Serverless functions not found");
|
|
1019
|
-
} else {
|
|
1020
|
-
spinner.succeed("Serverless functions found");
|
|
1021
|
-
}
|
|
1022
|
-
spinner.start("Validating GitHub connection...");
|
|
1023
|
-
const isValid = await validateGitHubConnection(config.github);
|
|
1024
|
-
if (!isValid) {
|
|
1025
|
-
spinner.fail("GitHub connection failed");
|
|
1026
|
-
console.log(chalk11.red("\n\u2717 Could not connect to GitHub API\n"));
|
|
1027
|
-
process.exit(1);
|
|
1028
|
-
}
|
|
1029
|
-
spinner.succeed("GitHub connection validated");
|
|
1030
|
-
spinner.start("Validating repository access...");
|
|
1031
|
-
const hasAccess = await validateRepoAccess(config.github);
|
|
1032
|
-
if (!hasAccess) {
|
|
1033
|
-
spinner.warn("Repository access could not be confirmed");
|
|
1034
|
-
} else {
|
|
1035
|
-
spinner.succeed("Repository access confirmed");
|
|
304
|
+
function App() {
|
|
305
|
+
const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<Router>
|
|
309
|
+
<CommentProvider>
|
|
310
|
+
<CommentDrawer
|
|
311
|
+
selectedThreadId={selectedThreadId}
|
|
312
|
+
onThreadSelect={setSelectedThreadId}
|
|
313
|
+
>
|
|
314
|
+
<CommentOverlay
|
|
315
|
+
selectedThreadId={selectedThreadId}
|
|
316
|
+
onThreadSelect={setSelectedThreadId}
|
|
317
|
+
/>
|
|
318
|
+
{/* Your app content */}
|
|
319
|
+
</CommentDrawer>
|
|
320
|
+
</CommentProvider>
|
|
321
|
+
</Router>
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
`));
|
|
1036
325
|
}
|
|
1037
|
-
console.log(chalk11.green.bold("\n\u2713 Validation complete!\n"));
|
|
1038
|
-
console.log(chalk11.white("Your Apollo Commenting System setup looks good.\n"));
|
|
1039
326
|
}
|
|
1040
327
|
|
|
1041
328
|
// src/index.ts
|
|
1042
329
|
var program = new Command();
|
|
1043
|
-
program.name("hale-commenting-system").description("Hale Commenting System CLI -
|
|
1044
|
-
program.command("init").description("Initialize
|
|
330
|
+
program.name("hale-commenting-system").description("Hale Commenting System CLI - Local setup wizard").version("2.0.0");
|
|
331
|
+
program.command("init").description("Initialize Hale Commenting System in your project").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
|
|
1045
332
|
try {
|
|
1046
333
|
await initCommand(options);
|
|
1047
334
|
} catch (error) {
|
|
1048
|
-
console.error(
|
|
335
|
+
console.error(chalk4.red("\n\u2717 Error:"), error.message);
|
|
1049
336
|
process.exit(1);
|
|
1050
337
|
}
|
|
1051
338
|
});
|
|
1052
|
-
program.command("validate").description("Validate your commenting system setup").action(async () => {
|
|
1053
|
-
try {
|
|
1054
|
-
await validateCommand();
|
|
1055
|
-
} catch (error) {
|
|
1056
|
-
console.error(chalk12.red("\n\u2717 Error:"), error.message);
|
|
1057
|
-
process.exit(1);
|
|
1058
|
-
}
|
|
1059
|
-
});
|
|
1060
|
-
program.command("update").description("Update existing configuration").action(() => {
|
|
1061
|
-
console.log(chalk12.yellow("\n\u26A0\uFE0F Coming soon: Update configuration\n"));
|
|
1062
|
-
console.log(chalk12.dim("For now, you can manually edit apollo-comments.config.json\n"));
|
|
1063
|
-
});
|
|
1064
339
|
if (!process.argv.slice(2).length) {
|
|
1065
340
|
program.help();
|
|
1066
341
|
}
|