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