hale-commenting-system 2.0.3 → 2.0.4
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 +16 -207
- package/bin/detect.d.ts +1 -0
- package/bin/detect.js +62 -0
- package/bin/generators.d.ts +18 -0
- package/bin/generators.js +193 -0
- package/bin/hale-commenting.js +4 -0
- package/bin/index.d.ts +2 -0
- package/bin/index.js +61 -0
- package/bin/onboarding.d.ts +1 -0
- package/bin/onboarding.js +170 -0
- package/bin/postinstall.d.ts +2 -0
- package/bin/postinstall.js +65 -0
- package/bin/validators.d.ts +2 -0
- package/bin/validators.js +66 -0
- package/dist/cli/detect.d.ts +1 -0
- package/dist/cli/detect.js +62 -0
- package/dist/cli/generators.d.ts +18 -0
- package/dist/cli/generators.js +193 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +61 -0
- package/dist/cli/onboarding.d.ts +1 -0
- package/dist/cli/onboarding.js +170 -0
- package/dist/cli/postinstall.d.ts +2 -0
- package/dist/cli/postinstall.js +65 -0
- package/dist/cli/validators.d.ts +2 -0
- package/dist/cli/validators.js +66 -0
- package/dist/components/CommentOverlay.d.ts +2 -0
- package/dist/components/CommentOverlay.js +101 -0
- package/dist/components/CommentPanel.d.ts +6 -0
- package/dist/components/CommentPanel.js +334 -0
- package/dist/components/CommentPin.d.ts +11 -0
- package/dist/components/CommentPin.js +64 -0
- package/dist/components/DetailsTab.d.ts +2 -0
- package/dist/components/DetailsTab.js +380 -0
- package/dist/components/FloatingWidget.d.ts +8 -0
- package/dist/components/FloatingWidget.js +128 -0
- package/dist/components/JiraTab.d.ts +2 -0
- package/dist/components/JiraTab.js +507 -0
- package/dist/contexts/CommentContext.d.ts +30 -0
- package/dist/contexts/CommentContext.js +891 -0
- package/dist/contexts/GitHubAuthContext.d.ts +13 -0
- package/dist/contexts/GitHubAuthContext.js +96 -0
- package/dist/index.d.ts +10 -97
- package/dist/index.js +26 -786
- package/dist/services/githubAdapter.d.ts +56 -0
- package/dist/services/githubAdapter.js +321 -0
- package/dist/types/index.d.ts +25 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/version.d.ts +1 -0
- package/dist/utils/version.js +23 -0
- package/package.json +39 -38
- package/templates/webpack-middleware.js +226 -0
- package/cli/dist/index.js +0 -370
- package/cli/dist/index.js.map +0 -1
- package/dist/index.d.mts +0 -97
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -759
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// Hale Commenting System - Webpack Dev Server Middleware
|
|
2
|
+
// Add this code to your webpack.dev.js inside setupMiddlewares function
|
|
3
|
+
|
|
4
|
+
// Load env vars for local OAuth/token exchange without bundling secrets into the client.
|
|
5
|
+
try {
|
|
6
|
+
const dotenv = require('dotenv');
|
|
7
|
+
dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|
8
|
+
dotenv.config({ path: path.resolve(__dirname, '.env.server'), override: true });
|
|
9
|
+
} catch (e) {
|
|
10
|
+
// no-op
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const express = require('express');
|
|
14
|
+
devServer.app.use(express.json());
|
|
15
|
+
|
|
16
|
+
// GitHub OAuth Callback
|
|
17
|
+
devServer.app.get('/api/github-oauth-callback', async (req, res) => {
|
|
18
|
+
try {
|
|
19
|
+
const code = req.query.code;
|
|
20
|
+
if (!code) {
|
|
21
|
+
return res.status(400).send('Missing ?code from GitHub OAuth callback.');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const clientId = process.env.VITE_GITHUB_CLIENT_ID;
|
|
25
|
+
const clientSecret = process.env.GITHUB_CLIENT_SECRET;
|
|
26
|
+
|
|
27
|
+
if (!clientId || !clientSecret) {
|
|
28
|
+
return res.status(500).send('Missing GitHub OAuth credentials.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const tokenResp = await fetch('https://github.com/login/oauth/access_token', {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Accept': 'application/json',
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify({
|
|
38
|
+
client_id: clientId,
|
|
39
|
+
client_secret: clientSecret,
|
|
40
|
+
code,
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const tokenData = await tokenResp.json();
|
|
45
|
+
if (!tokenResp.ok || tokenData.error) {
|
|
46
|
+
return res.status(500).send(`OAuth token exchange failed: ${tokenData.error || tokenResp.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const accessToken = tokenData.access_token;
|
|
50
|
+
if (!accessToken) {
|
|
51
|
+
return res.status(500).send('OAuth token exchange did not return an access_token.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const userResp = await fetch('https://api.github.com/user', {
|
|
55
|
+
headers: {
|
|
56
|
+
'Accept': 'application/vnd.github+json',
|
|
57
|
+
'Authorization': `token ${accessToken}`,
|
|
58
|
+
'User-Agent': 'hale-commenting-system',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
const user = await userResp.json();
|
|
62
|
+
if (!userResp.ok) {
|
|
63
|
+
return res.status(500).send(`Failed to fetch GitHub user: ${user.message || userResp.statusText}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const login = encodeURIComponent(user.login || '');
|
|
67
|
+
const avatar = encodeURIComponent(user.avatar_url || '');
|
|
68
|
+
const token = encodeURIComponent(accessToken);
|
|
69
|
+
|
|
70
|
+
return res.redirect(`/#/auth-callback?token=${token}&login=${login}&avatar=${avatar}`);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error(err);
|
|
73
|
+
return res.status(500).send('Unhandled OAuth callback error. See dev server logs.');
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// GitHub API Proxy
|
|
78
|
+
devServer.app.post('/api/github-api', async (req, res) => {
|
|
79
|
+
try {
|
|
80
|
+
const { token, method, endpoint, data } = req.body || {};
|
|
81
|
+
if (!token) return res.status(401).json({ message: 'Missing token' });
|
|
82
|
+
if (!method || !endpoint) return res.status(400).json({ message: 'Missing method or endpoint' });
|
|
83
|
+
|
|
84
|
+
const url = `https://api.github.com${endpoint}`;
|
|
85
|
+
const resp = await fetch(url, {
|
|
86
|
+
method,
|
|
87
|
+
headers: {
|
|
88
|
+
'Accept': 'application/vnd.github+json',
|
|
89
|
+
'Authorization': `token ${token}`,
|
|
90
|
+
'User-Agent': 'hale-commenting-system',
|
|
91
|
+
...(data ? { 'Content-Type': 'application/json' } : {}),
|
|
92
|
+
},
|
|
93
|
+
body: data ? JSON.stringify(data) : undefined,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const text = await resp.text();
|
|
97
|
+
const maybeJson = (() => {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(text);
|
|
100
|
+
} catch {
|
|
101
|
+
return text;
|
|
102
|
+
}
|
|
103
|
+
})();
|
|
104
|
+
|
|
105
|
+
return res.status(resp.status).json(maybeJson);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(err);
|
|
108
|
+
return res.status(500).json({ message: 'Unhandled github-api proxy error. See dev server logs.' });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Jira Issue Proxy
|
|
113
|
+
devServer.app.get('/api/jira-issue', async (req, res) => {
|
|
114
|
+
try {
|
|
115
|
+
const key = String(req.query.key || '').trim();
|
|
116
|
+
if (!key) return res.status(400).json({ message: 'Missing ?key (e.g. ABC-123)' });
|
|
117
|
+
|
|
118
|
+
const baseUrl = (process.env.VITE_JIRA_BASE_URL || 'https://issues.redhat.com').replace(/\/+$/, '');
|
|
119
|
+
const email = process.env.JIRA_EMAIL;
|
|
120
|
+
const token = process.env.JIRA_API_TOKEN;
|
|
121
|
+
|
|
122
|
+
if (!token) {
|
|
123
|
+
return res.status(500).json({
|
|
124
|
+
message: 'Missing JIRA_API_TOKEN. For local dev, put it in .env.server (gitignored).',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const authHeader = email
|
|
129
|
+
? `Basic ${Buffer.from(`${email}:${token}`).toString('base64')}`
|
|
130
|
+
: `Bearer ${token}`;
|
|
131
|
+
|
|
132
|
+
const buildUrl = (apiVersion) =>
|
|
133
|
+
`${baseUrl}/rest/api/${apiVersion}/issue/${encodeURIComponent(key)}?fields=summary,status,assignee,issuetype,priority,created,updated,description&expand=renderedFields`;
|
|
134
|
+
|
|
135
|
+
const commonHeaders = {
|
|
136
|
+
'Accept': 'application/json',
|
|
137
|
+
'Authorization': authHeader,
|
|
138
|
+
'User-Agent': 'hale-commenting-system',
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const fetchOnce = async (apiVersion) => {
|
|
142
|
+
const r = await fetch(buildUrl(apiVersion), { headers: commonHeaders, redirect: 'manual' });
|
|
143
|
+
const text = await r.text();
|
|
144
|
+
const contentType = String(r.headers.get('content-type') || '');
|
|
145
|
+
const looksLikeHtml =
|
|
146
|
+
contentType.includes('text/html') ||
|
|
147
|
+
String(text || '').trim().startsWith('<');
|
|
148
|
+
return { r, text, contentType, looksLikeHtml };
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const preferV2 = baseUrl.includes('issues.redhat.com');
|
|
152
|
+
const firstVersion = preferV2 ? '2' : '3';
|
|
153
|
+
const secondVersion = preferV2 ? '3' : '2';
|
|
154
|
+
|
|
155
|
+
let attempt = await fetchOnce(firstVersion);
|
|
156
|
+
if (
|
|
157
|
+
attempt.r.status === 404 ||
|
|
158
|
+
attempt.r.status === 302 ||
|
|
159
|
+
attempt.looksLikeHtml ||
|
|
160
|
+
attempt.r.status === 401 ||
|
|
161
|
+
attempt.r.status === 403
|
|
162
|
+
) {
|
|
163
|
+
const fallback = await fetchOnce(secondVersion);
|
|
164
|
+
if (fallback.r.ok || attempt.looksLikeHtml || attempt.r.status === 302) {
|
|
165
|
+
attempt = fallback;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const resp = attempt.r;
|
|
170
|
+
const payloadText = attempt.text;
|
|
171
|
+
const contentType = attempt.contentType;
|
|
172
|
+
|
|
173
|
+
const payload = (() => {
|
|
174
|
+
try {
|
|
175
|
+
return JSON.parse(payloadText);
|
|
176
|
+
} catch {
|
|
177
|
+
return { message: payloadText };
|
|
178
|
+
}
|
|
179
|
+
})();
|
|
180
|
+
|
|
181
|
+
if (!resp.ok) {
|
|
182
|
+
const looksLikeHtml =
|
|
183
|
+
contentType.includes('text/html') ||
|
|
184
|
+
String(payloadText || '').trim().startsWith('<');
|
|
185
|
+
|
|
186
|
+
if (looksLikeHtml) {
|
|
187
|
+
return res.status(resp.status).json({
|
|
188
|
+
message:
|
|
189
|
+
resp.status === 401 || resp.status === 403
|
|
190
|
+
? 'Unauthorized to Jira. Your token/auth scheme may be incorrect for this Jira instance.'
|
|
191
|
+
: `Jira request failed (${resp.status}).`,
|
|
192
|
+
hint: email
|
|
193
|
+
? 'You are using Basic auth (JIRA_EMAIL + JIRA_API_TOKEN). If this Jira uses PAT/Bearer tokens, remove JIRA_EMAIL and set only JIRA_API_TOKEN.'
|
|
194
|
+
: baseUrl.includes('issues.redhat.com')
|
|
195
|
+
? 'You are using Bearer auth (JIRA_API_TOKEN). For issues.redhat.com, ensure you are using a PAT that works with REST API v2 and that JIRA_EMAIL is NOT set.'
|
|
196
|
+
: 'You are using Bearer auth (JIRA_API_TOKEN). If this Jira uses Jira Cloud API tokens, set JIRA_EMAIL as well.',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return res.status(resp.status).json({
|
|
201
|
+
message: payload?.message || `Jira request failed (${resp.status}).`,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const issue = payload;
|
|
206
|
+
const fields = issue.fields || {};
|
|
207
|
+
const renderedFields = issue.renderedFields || {};
|
|
208
|
+
|
|
209
|
+
return res.json({
|
|
210
|
+
key: issue.key,
|
|
211
|
+
url: `${baseUrl}/browse/${issue.key}`,
|
|
212
|
+
summary: fields.summary || '',
|
|
213
|
+
status: fields.status?.name || '',
|
|
214
|
+
assignee: fields.assignee?.displayName || '',
|
|
215
|
+
issueType: fields.issuetype?.name || '',
|
|
216
|
+
priority: fields.priority?.name || '',
|
|
217
|
+
created: fields.created || '',
|
|
218
|
+
updated: fields.updated || '',
|
|
219
|
+
description: renderedFields.description || fields.description || '',
|
|
220
|
+
});
|
|
221
|
+
} catch (err) {
|
|
222
|
+
console.error(err);
|
|
223
|
+
return res.status(500).json({ message: 'Unhandled jira-issue proxy error. See dev server logs.' });
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
package/cli/dist/index.js
DELETED
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/index.ts
|
|
4
|
-
import { Command } from "commander";
|
|
5
|
-
import chalk4 from "chalk";
|
|
6
|
-
|
|
7
|
-
// src/commands/init.ts
|
|
8
|
-
import chalk3 from "chalk";
|
|
9
|
-
import ora from "ora";
|
|
10
|
-
import inquirer from "inquirer";
|
|
11
|
-
|
|
12
|
-
// src/utils/detect.ts
|
|
13
|
-
import fs from "fs/promises";
|
|
14
|
-
import path from "path";
|
|
15
|
-
async function detectProject(cwd) {
|
|
16
|
-
const packageJsonPath = path.join(cwd, "package.json");
|
|
17
|
-
let packageJson = {};
|
|
18
|
-
try {
|
|
19
|
-
const content = await fs.readFile(packageJsonPath, "utf-8");
|
|
20
|
-
packageJson = JSON.parse(content);
|
|
21
|
-
} catch (error) {
|
|
22
|
-
throw new Error("No package.json found. Are you in a Node.js project?");
|
|
23
|
-
}
|
|
24
|
-
const deps = {
|
|
25
|
-
...packageJson.dependencies,
|
|
26
|
-
...packageJson.devDependencies
|
|
27
|
-
};
|
|
28
|
-
const buildTool = await detectBuildTool(cwd, deps);
|
|
29
|
-
return {
|
|
30
|
-
root: cwd,
|
|
31
|
-
framework: detectFramework(deps),
|
|
32
|
-
buildTool,
|
|
33
|
-
isReact: !!deps["react"],
|
|
34
|
-
hasPatternFly: !!deps["@patternfly/react-core"],
|
|
35
|
-
hasTypeScript: !!deps["typescript"],
|
|
36
|
-
packageJson
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function detectFramework(deps) {
|
|
40
|
-
if (deps["react"]) return "React";
|
|
41
|
-
if (deps["vue"]) return "Vue";
|
|
42
|
-
if (deps["@angular/core"]) return "Angular";
|
|
43
|
-
return "Unknown";
|
|
44
|
-
}
|
|
45
|
-
async function detectBuildTool(cwd, deps) {
|
|
46
|
-
if (deps["vite"]) return "Vite";
|
|
47
|
-
if (deps["webpack"]) return "Webpack";
|
|
48
|
-
try {
|
|
49
|
-
await fs.access(path.join(cwd, "next.config.js"));
|
|
50
|
-
return "Next.js";
|
|
51
|
-
} catch {
|
|
52
|
-
}
|
|
53
|
-
return "Unknown";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// src/utils/logger.ts
|
|
57
|
-
import chalk from "chalk";
|
|
58
|
-
function printWelcome() {
|
|
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"));
|
|
61
|
-
}
|
|
62
|
-
function printWarning(message) {
|
|
63
|
-
console.log(chalk.yellow(`\u26A0\uFE0F ${message}`));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// src/generators/code.ts
|
|
67
|
-
import path2 from "path";
|
|
68
|
-
import chalk2 from "chalk";
|
|
69
|
-
|
|
70
|
-
// src/utils/fs.ts
|
|
71
|
-
import fs2 from "fs/promises";
|
|
72
|
-
async function fileExists(filePath) {
|
|
73
|
-
try {
|
|
74
|
-
await fs2.access(filePath);
|
|
75
|
-
return true;
|
|
76
|
-
} catch {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
async function readFile(filePath) {
|
|
81
|
-
return fs2.readFile(filePath, "utf-8");
|
|
82
|
-
}
|
|
83
|
-
async function writeFile(filePath, content) {
|
|
84
|
-
await fs2.writeFile(filePath, content, "utf-8");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// src/generators/code.ts
|
|
88
|
-
async function integrateProviders(projectRoot) {
|
|
89
|
-
const possiblePaths = [
|
|
90
|
-
"src/app/index.tsx",
|
|
91
|
-
"src/app/App.tsx",
|
|
92
|
-
"src/App.tsx",
|
|
93
|
-
"src/index.tsx"
|
|
94
|
-
];
|
|
95
|
-
let appFilePath = null;
|
|
96
|
-
for (const p of possiblePaths) {
|
|
97
|
-
const fullPath = path2.join(projectRoot, p);
|
|
98
|
-
if (await fileExists(fullPath)) {
|
|
99
|
-
appFilePath = fullPath;
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (!appFilePath) {
|
|
104
|
-
return {
|
|
105
|
-
success: false,
|
|
106
|
-
filePath: "",
|
|
107
|
-
message: "Could not find App entry point. Please integrate manually."
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
const content = await readFile(appFilePath);
|
|
112
|
-
if (content.includes("hale-commenting-system") || content.includes("CommentProvider")) {
|
|
113
|
-
return {
|
|
114
|
-
success: false,
|
|
115
|
-
filePath: appFilePath,
|
|
116
|
-
message: "Commenting system already integrated."
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
const modifiedContent = injectProviders(content);
|
|
120
|
-
if (modifiedContent === content) {
|
|
121
|
-
return {
|
|
122
|
-
success: false,
|
|
123
|
-
filePath: appFilePath,
|
|
124
|
-
message: "Could not automatically integrate. File structure not recognized."
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
await writeFile(appFilePath, modifiedContent);
|
|
128
|
-
return {
|
|
129
|
-
success: true,
|
|
130
|
-
filePath: appFilePath,
|
|
131
|
-
message: `Successfully integrated providers into ${path2.relative(projectRoot, appFilePath)}`
|
|
132
|
-
};
|
|
133
|
-
} catch (error) {
|
|
134
|
-
return {
|
|
135
|
-
success: false,
|
|
136
|
-
filePath: appFilePath,
|
|
137
|
-
message: `Error: ${error.message}`
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function injectProviders(content) {
|
|
142
|
-
const imports = `import {
|
|
143
|
-
CommentProvider,
|
|
144
|
-
CommentOverlay,
|
|
145
|
-
CommentDrawer,
|
|
146
|
-
useComments,
|
|
147
|
-
VersionProvider
|
|
148
|
-
} from 'hale-commenting-system';`;
|
|
149
|
-
const importRegex = /import\s+.*?from\s+['"].*?['"];?/g;
|
|
150
|
-
const matches = content.match(importRegex);
|
|
151
|
-
if (!matches || matches.length === 0) {
|
|
152
|
-
content = imports + "\n\n" + content;
|
|
153
|
-
} else {
|
|
154
|
-
const lastImport = matches[matches.length - 1];
|
|
155
|
-
const lastImportIndex = content.lastIndexOf(lastImport);
|
|
156
|
-
const insertPosition = lastImportIndex + lastImport.length;
|
|
157
|
-
content = content.slice(0, insertPosition) + "\n\n" + imports + content.slice(insertPosition);
|
|
158
|
-
}
|
|
159
|
-
let returnMatch = content.match(/return\s*\(?[\s\n]*<(\w+)/);
|
|
160
|
-
let isImplicitReturn = false;
|
|
161
|
-
if (!returnMatch) {
|
|
162
|
-
returnMatch = content.match(/=>\s*\(?[\s\n]*<(\w+)/);
|
|
163
|
-
isImplicitReturn = true;
|
|
164
|
-
}
|
|
165
|
-
if (!returnMatch) {
|
|
166
|
-
return content;
|
|
167
|
-
}
|
|
168
|
-
const componentName = returnMatch[1];
|
|
169
|
-
const closingTag = `</${componentName}>`;
|
|
170
|
-
const closingIndex = content.indexOf(closingTag);
|
|
171
|
-
if (closingIndex === -1) {
|
|
172
|
-
return content;
|
|
173
|
-
}
|
|
174
|
-
if (isImplicitReturn) {
|
|
175
|
-
const arrowMatch = content.match(/(const\s+\w+.*?=.*?\(\).*?=>\s*)\(/);
|
|
176
|
-
if (arrowMatch) {
|
|
177
|
-
const beforeParen = arrowMatch[1];
|
|
178
|
-
const arrowEndPos = arrowMatch.index + arrowMatch[0].length - 1;
|
|
179
|
-
const closingParenPos = content.indexOf(");", closingIndex);
|
|
180
|
-
if (closingParenPos !== -1) {
|
|
181
|
-
const jsxContent = content.slice(arrowEndPos + 1, closingParenPos);
|
|
182
|
-
content = content.slice(0, arrowEndPos) + " {\n return" + jsxContent + "\n}" + content.slice(closingParenPos + 2);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const stateHook = ` const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);
|
|
187
|
-
|
|
188
|
-
`;
|
|
189
|
-
const functionMatch = content.match(/const\s+\w+.*?=.*?\(\).*?=>\s*\{/);
|
|
190
|
-
if (functionMatch) {
|
|
191
|
-
const insertPos = functionMatch.index + functionMatch[0].length;
|
|
192
|
-
content = content.slice(0, insertPos) + "\n" + stateHook + content.slice(insertPos);
|
|
193
|
-
}
|
|
194
|
-
const returnStartMatch = content.match(/return\s*\(?[\s\n]*</);
|
|
195
|
-
if (returnStartMatch) {
|
|
196
|
-
const returnStart = returnStartMatch.index + returnStartMatch[0].length - 1;
|
|
197
|
-
const returnEnd = content.indexOf(closingTag, returnStart) + closingTag.length;
|
|
198
|
-
const originalReturn = content.slice(returnStart, returnEnd).trim();
|
|
199
|
-
const isRouter = originalReturn.match(/^\s*<(Router|BrowserRouter)/);
|
|
200
|
-
let wrappedReturn;
|
|
201
|
-
if (isRouter) {
|
|
202
|
-
const routerMatch = originalReturn.match(/<(Router|BrowserRouter)[^>]*>([\s\S]*)<\/\1>/);
|
|
203
|
-
if (routerMatch) {
|
|
204
|
-
const routerType = routerMatch[1];
|
|
205
|
-
const innerContent = routerMatch[2];
|
|
206
|
-
wrappedReturn = `
|
|
207
|
-
<${routerType}>
|
|
208
|
-
<VersionProvider>
|
|
209
|
-
<CommentProvider>
|
|
210
|
-
<CommentDrawer
|
|
211
|
-
selectedThreadId={selectedThreadId}
|
|
212
|
-
onThreadSelect={setSelectedThreadId}
|
|
213
|
-
>
|
|
214
|
-
<CommentOverlay
|
|
215
|
-
selectedThreadId={selectedThreadId}
|
|
216
|
-
onThreadSelect={setSelectedThreadId}
|
|
217
|
-
/>
|
|
218
|
-
${innerContent.trim()}
|
|
219
|
-
</CommentDrawer>
|
|
220
|
-
</CommentProvider>
|
|
221
|
-
</VersionProvider>
|
|
222
|
-
</${routerType}>
|
|
223
|
-
`;
|
|
224
|
-
} else {
|
|
225
|
-
wrappedReturn = `
|
|
226
|
-
<VersionProvider>
|
|
227
|
-
<CommentProvider>
|
|
228
|
-
<CommentDrawer
|
|
229
|
-
selectedThreadId={selectedThreadId}
|
|
230
|
-
onThreadSelect={setSelectedThreadId}
|
|
231
|
-
>
|
|
232
|
-
<CommentOverlay
|
|
233
|
-
selectedThreadId={selectedThreadId}
|
|
234
|
-
onThreadSelect={setSelectedThreadId}
|
|
235
|
-
/>
|
|
236
|
-
${originalReturn}
|
|
237
|
-
</CommentDrawer>
|
|
238
|
-
</CommentProvider>
|
|
239
|
-
</VersionProvider>
|
|
240
|
-
`;
|
|
241
|
-
}
|
|
242
|
-
} else {
|
|
243
|
-
wrappedReturn = `
|
|
244
|
-
<VersionProvider>
|
|
245
|
-
<CommentProvider>
|
|
246
|
-
<CommentDrawer
|
|
247
|
-
selectedThreadId={selectedThreadId}
|
|
248
|
-
onThreadSelect={setSelectedThreadId}
|
|
249
|
-
>
|
|
250
|
-
<CommentOverlay
|
|
251
|
-
selectedThreadId={selectedThreadId}
|
|
252
|
-
onThreadSelect={setSelectedThreadId}
|
|
253
|
-
/>
|
|
254
|
-
${originalReturn}
|
|
255
|
-
</CommentDrawer>
|
|
256
|
-
</CommentProvider>
|
|
257
|
-
</VersionProvider>
|
|
258
|
-
`;
|
|
259
|
-
}
|
|
260
|
-
content = content.slice(0, returnStart) + wrappedReturn + content.slice(returnEnd);
|
|
261
|
-
}
|
|
262
|
-
return content;
|
|
263
|
-
}
|
|
264
|
-
function showIntegrationDiff(filePath) {
|
|
265
|
-
console.log(chalk2.cyan("\nChanges made to your App file:"));
|
|
266
|
-
console.log(chalk2.white("\u2022 Added commenting system imports"));
|
|
267
|
-
console.log(chalk2.white("\u2022 Added state hook for comment thread selection"));
|
|
268
|
-
console.log(chalk2.white("\u2022 Wrapped app with CommentProvider"));
|
|
269
|
-
console.log(chalk2.white("\u2022 Added CommentDrawer and CommentOverlay components\n"));
|
|
270
|
-
console.log(chalk2.dim(`Modified: ${filePath}
|
|
271
|
-
`));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// src/commands/init.ts
|
|
275
|
-
async function initCommand(options) {
|
|
276
|
-
printWelcome();
|
|
277
|
-
const spinner = ora("Detecting project...").start();
|
|
278
|
-
let project;
|
|
279
|
-
try {
|
|
280
|
-
project = await detectProject(process.cwd());
|
|
281
|
-
spinner.succeed(`Detected: ${project.framework} with ${project.buildTool}`);
|
|
282
|
-
} catch (error) {
|
|
283
|
-
spinner.fail(error.message);
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
if (!project.isReact) {
|
|
287
|
-
spinner.fail("This package requires a React project");
|
|
288
|
-
console.log(chalk3.red("\n\u2717 Hale Commenting System requires React.\n"));
|
|
289
|
-
process.exit(1);
|
|
290
|
-
}
|
|
291
|
-
if (!project.hasPatternFly) {
|
|
292
|
-
printWarning("PatternFly not detected. This package is optimized for PatternFly projects.");
|
|
293
|
-
if (!options.yes) {
|
|
294
|
-
const { continueAnyway } = await inquirer.prompt([
|
|
295
|
-
{
|
|
296
|
-
type: "confirm",
|
|
297
|
-
name: "continueAnyway",
|
|
298
|
-
message: "Continue anyway?",
|
|
299
|
-
default: false
|
|
300
|
-
}
|
|
301
|
-
]);
|
|
302
|
-
if (!continueAnyway) {
|
|
303
|
-
console.log(chalk3.dim("\nSetup cancelled.\n"));
|
|
304
|
-
process.exit(0);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
console.log(chalk3.cyan("\n\u{1F4E6} Setting up local commenting system...\n"));
|
|
309
|
-
spinner.start("Integrating commenting system into your app...");
|
|
310
|
-
const result = await integrateProviders(project.root);
|
|
311
|
-
if (result.success) {
|
|
312
|
-
spinner.succeed("Commenting system integrated!");
|
|
313
|
-
showIntegrationDiff(result.filePath);
|
|
314
|
-
console.log(chalk3.green("\n\u2713 Setup complete! Your app is ready to use the commenting system.\n"));
|
|
315
|
-
console.log(chalk3.cyan("Next steps:"));
|
|
316
|
-
console.log(chalk3.white("1. Review the changes in your App file"));
|
|
317
|
-
console.log(chalk3.white("2. Start your dev server (e.g., npm run start:dev)"));
|
|
318
|
-
console.log(chalk3.white("3. Open your app in the browser"));
|
|
319
|
-
console.log(chalk3.white("4. Click anywhere to add comments (stored in localStorage)\n"));
|
|
320
|
-
console.log(chalk3.dim("\u{1F4A1} Tip: Comments persist across page refreshes and are stored locally in your browser.\n"));
|
|
321
|
-
} else {
|
|
322
|
-
spinner.warn(result.message);
|
|
323
|
-
console.log(chalk3.yellow("\n\u26A0\uFE0F Could not automatically integrate the commenting system.\n"));
|
|
324
|
-
console.log(chalk3.white("Please manually add the following to your App component:\n"));
|
|
325
|
-
console.log(chalk3.dim(`import { CommentProvider, CommentOverlay, CommentDrawer, VersionProvider } from 'hale-commenting-system';
|
|
326
|
-
import { BrowserRouter as Router } from 'react-router-dom';
|
|
327
|
-
import React from 'react';
|
|
328
|
-
|
|
329
|
-
function App() {
|
|
330
|
-
const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);
|
|
331
|
-
|
|
332
|
-
return (
|
|
333
|
-
<Router>
|
|
334
|
-
<VersionProvider>
|
|
335
|
-
<CommentProvider>
|
|
336
|
-
<CommentDrawer
|
|
337
|
-
selectedThreadId={selectedThreadId}
|
|
338
|
-
onThreadSelect={setSelectedThreadId}
|
|
339
|
-
>
|
|
340
|
-
<CommentOverlay
|
|
341
|
-
selectedThreadId={selectedThreadId}
|
|
342
|
-
onThreadSelect={setSelectedThreadId}
|
|
343
|
-
/>
|
|
344
|
-
{/* Your app content */}
|
|
345
|
-
</CommentDrawer>
|
|
346
|
-
</CommentProvider>
|
|
347
|
-
</VersionProvider>
|
|
348
|
-
</Router>
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
`));
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// src/index.ts
|
|
356
|
-
var program = new Command();
|
|
357
|
-
program.name("hale-commenting-system").description("Hale Commenting System CLI - Local setup wizard").version("2.0.0");
|
|
358
|
-
program.command("init").description("Initialize Hale Commenting System in your project").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
|
|
359
|
-
try {
|
|
360
|
-
await initCommand(options);
|
|
361
|
-
} catch (error) {
|
|
362
|
-
console.error(chalk4.red("\n\u2717 Error:"), error.message);
|
|
363
|
-
process.exit(1);
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
if (!process.argv.slice(2).length) {
|
|
367
|
-
program.help();
|
|
368
|
-
}
|
|
369
|
-
program.parse();
|
|
370
|
-
//# sourceMappingURL=index.js.map
|
package/cli/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/detect.ts","../src/utils/logger.ts","../src/generators/code.ts","../src/utils/fs.ts"],"sourcesContent":["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { initCommand } from './commands/init.js';\n\nconst program = new Command();\n\nprogram\n .name('hale-commenting-system')\n .description('Hale Commenting System CLI - Local setup wizard')\n .version('2.0.0');\n\nprogram\n .command('init')\n .description('Initialize Hale Commenting System in your project')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .action(async (options) => {\n try {\n await initCommand(options);\n } catch (error: any) {\n console.error(chalk.red('\\n✗ Error:'), error.message);\n process.exit(1);\n }\n });\n\n// Show help if no command provided\nif (!process.argv.slice(2).length) {\n program.help();\n}\n\nprogram.parse();\n","import chalk from 'chalk';\nimport ora from 'ora';\nimport inquirer from 'inquirer';\nimport { detectProject } from '../utils/detect.js';\nimport { printWelcome, printWarning } from '../utils/logger.js';\nimport { integrateProviders, showIntegrationDiff } from '../generators/code.js';\n\ninterface InitOptions {\n yes?: boolean;\n}\n\nexport async function initCommand(options: InitOptions) {\n printWelcome();\n\n const spinner = ora('Detecting project...').start();\n\n // Step 1: Detect project type\n let project;\n try {\n project = await detectProject(process.cwd());\n spinner.succeed(`Detected: ${project.framework} with ${project.buildTool}`);\n } catch (error: any) {\n spinner.fail(error.message);\n process.exit(1);\n }\n\n if (!project.isReact) {\n spinner.fail('This package requires a React project');\n console.log(chalk.red('\\n✗ Hale Commenting System requires React.\\n'));\n process.exit(1);\n }\n\n if (!project.hasPatternFly) {\n printWarning('PatternFly not detected. This package is optimized for PatternFly projects.');\n if (!options.yes) {\n const { continueAnyway } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'continueAnyway',\n message: 'Continue anyway?',\n default: false\n }\n ]);\n if (!continueAnyway) {\n console.log(chalk.dim('\\nSetup cancelled.\\n'));\n process.exit(0);\n }\n }\n }\n\n // Step 2: Auto-integrate providers\n console.log(chalk.cyan('\\n📦 Setting up local commenting system...\\n'));\n \n spinner.start('Integrating commenting system into your app...');\n const result = await integrateProviders(project.root);\n \n if (result.success) {\n spinner.succeed('Commenting system integrated!');\n showIntegrationDiff(result.filePath);\n console.log(chalk.green('\\n✓ Setup complete! Your app is ready to use the commenting system.\\n'));\n console.log(chalk.cyan('Next steps:'));\n console.log(chalk.white('1. Review the changes in your App file'));\n console.log(chalk.white('2. Start your dev server (e.g., npm run start:dev)'));\n console.log(chalk.white('3. Open your app in the browser'));\n console.log(chalk.white('4. Click anywhere to add comments (stored in localStorage)\\n'));\n console.log(chalk.dim('💡 Tip: Comments persist across page refreshes and are stored locally in your browser.\\n'));\n } else {\n spinner.warn(result.message);\n console.log(chalk.yellow('\\n⚠️ Could not automatically integrate the commenting system.\\n'));\n console.log(chalk.white('Please manually add the following to your App component:\\n'));\n console.log(chalk.dim(`import { CommentProvider, CommentOverlay, CommentDrawer, VersionProvider } from 'hale-commenting-system';\nimport { BrowserRouter as Router } from 'react-router-dom';\nimport React from 'react';\n\nfunction App() {\n const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);\n\n return (\n <Router>\n <VersionProvider>\n <CommentProvider>\n <CommentDrawer \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n >\n <CommentOverlay \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n />\n {/* Your app content */}\n </CommentDrawer>\n </CommentProvider>\n </VersionProvider>\n </Router>\n );\n}\\n`));\n }\n}\n","import fs from 'fs/promises';\nimport path from 'path';\n\nexport interface ProjectInfo {\n root: string;\n framework: string;\n buildTool: string;\n isReact: boolean;\n hasPatternFly: boolean;\n hasTypeScript: boolean;\n packageJson: any;\n}\n\nexport async function detectProject(cwd: string): Promise<ProjectInfo> {\n const packageJsonPath = path.join(cwd, 'package.json');\n \n let packageJson: any = {};\n try {\n const content = await fs.readFile(packageJsonPath, 'utf-8');\n packageJson = JSON.parse(content);\n } catch (error) {\n throw new Error('No package.json found. Are you in a Node.js project?');\n }\n\n const deps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies\n };\n\n const buildTool = await detectBuildTool(cwd, deps);\n\n return {\n root: cwd,\n framework: detectFramework(deps),\n buildTool,\n isReact: !!deps['react'],\n hasPatternFly: !!deps['@patternfly/react-core'],\n hasTypeScript: !!deps['typescript'],\n packageJson\n };\n}\n\nfunction detectFramework(deps: any): string {\n if (deps['react']) return 'React';\n if (deps['vue']) return 'Vue';\n if (deps['@angular/core']) return 'Angular';\n return 'Unknown';\n}\n\nasync function detectBuildTool(cwd: string, deps: any): Promise<string> {\n if (deps['vite']) return 'Vite';\n if (deps['webpack']) return 'Webpack';\n \n try {\n await fs.access(path.join(cwd, 'next.config.js'));\n return 'Next.js';\n } catch {\n // Next.js config not found\n }\n\n return 'Unknown';\n}\n\nexport async function detectPlatform(cwd: string): Promise<string | null> {\n try {\n await fs.access(path.join(cwd, 'vercel.json'));\n return 'vercel';\n } catch {\n // Not Vercel\n }\n\n try {\n await fs.access(path.join(cwd, 'netlify.toml'));\n return 'netlify';\n } catch {\n // Not Netlify\n }\n\n return null;\n}\n\n","import chalk from 'chalk';\n\nexport function printWelcome() {\n console.log(chalk.bold.cyan('\\n🚀 Hale Commenting System - Local Setup\\n'));\n console.log(chalk.dim('Setting up a localStorage-based commenting system for your app.\\n'));\n}\n\nexport function printWarning(message: string) {\n console.log(chalk.yellow(`⚠️ ${message}`));\n}\n","import path from 'path';\nimport chalk from 'chalk';\nimport { readFile, writeFile, fileExists } from '../utils/fs.js';\n\ninterface IntegrationResult {\n success: boolean;\n filePath: string;\n message: string;\n}\n\nexport async function integrateProviders(projectRoot: string): Promise<IntegrationResult> {\n // Find the App entry point - try common locations\n const possiblePaths = [\n 'src/app/index.tsx',\n 'src/app/App.tsx',\n 'src/App.tsx',\n 'src/index.tsx'\n ];\n\n let appFilePath: string | null = null;\n \n for (const p of possiblePaths) {\n const fullPath = path.join(projectRoot, p);\n if (await fileExists(fullPath)) {\n appFilePath = fullPath;\n break;\n }\n }\n\n if (!appFilePath) {\n return {\n success: false,\n filePath: '',\n message: 'Could not find App entry point. Please integrate manually.'\n };\n }\n\n try {\n const content = await readFile(appFilePath);\n \n // Check if already integrated\n if (content.includes('hale-commenting-system') || content.includes('CommentProvider')) {\n return {\n success: false,\n filePath: appFilePath,\n message: 'Commenting system already integrated.'\n };\n }\n\n const modifiedContent = injectProviders(content);\n \n if (modifiedContent === content) {\n return {\n success: false,\n filePath: appFilePath,\n message: 'Could not automatically integrate. File structure not recognized.'\n };\n }\n\n await writeFile(appFilePath, modifiedContent);\n\n return {\n success: true,\n filePath: appFilePath,\n message: `Successfully integrated providers into ${path.relative(projectRoot, appFilePath)}`\n };\n } catch (error: any) {\n return {\n success: false,\n filePath: appFilePath,\n message: `Error: ${error.message}`\n };\n }\n}\n\nfunction injectProviders(content: string): string {\n // Add imports at the top (after existing imports)\n const imports = `import {\n CommentProvider,\n CommentOverlay,\n CommentDrawer,\n useComments,\n VersionProvider\n} from 'hale-commenting-system';`;\n\n // Find the last import statement\n const importRegex = /import\\s+.*?from\\s+['\"].*?['\"];?/g;\n const matches = content.match(importRegex);\n \n if (!matches || matches.length === 0) {\n // No imports found, add at the beginning\n content = imports + '\\n\\n' + content;\n } else {\n const lastImport = matches[matches.length - 1];\n const lastImportIndex = content.lastIndexOf(lastImport);\n const insertPosition = lastImportIndex + lastImport.length;\n content = content.slice(0, insertPosition) + '\\n\\n' + imports + content.slice(insertPosition);\n }\n\n // Find the JSX in the component - handle both explicit return and implicit arrow return\n // Pattern 1: return ( <Router> or return <Router>\n // Pattern 2: => ( <Router> (implicit return)\n let returnMatch = content.match(/return\\s*\\(?[\\s\\n]*<(\\w+)/);\n let isImplicitReturn = false;\n \n if (!returnMatch) {\n // Try to match implicit arrow function return: => ( <Component>\n returnMatch = content.match(/=>\\s*\\(?[\\s\\n]*<(\\w+)/);\n isImplicitReturn = true;\n }\n \n if (!returnMatch) {\n return content; // Can't find return statement or JSX\n }\n\n const componentName = returnMatch[1]; // Router, Fragment, etc.\n \n // Find the closing tag\n const closingTag = `</${componentName}>`;\n const closingIndex = content.indexOf(closingTag);\n \n if (closingIndex === -1) {\n return content; // Can't find closing tag\n }\n\n // Convert implicit return to explicit return if needed\n if (isImplicitReturn) {\n // Find the arrow function: const App = () => (\n const arrowMatch = content.match(/(const\\s+\\w+.*?=.*?\\(\\).*?=>\\s*)\\(/);\n if (arrowMatch) {\n const beforeParen = arrowMatch[1];\n const arrowEndPos = arrowMatch.index! + arrowMatch[0].length - 1; // Position before the (\n \n // Find the closing paren + semicolon\n const closingParenPos = content.indexOf(');', closingIndex);\n if (closingParenPos !== -1) {\n // Convert: const App = () => (...) to const App = () => { return (...) }\n const jsxContent = content.slice(arrowEndPos + 1, closingParenPos);\n content = content.slice(0, arrowEndPos) + ' {\\n return' + jsxContent + '\\n}' + content.slice(closingParenPos + 2);\n }\n }\n }\n\n // Add state hook before return\n const stateHook = ` const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);\\n\\n`;\n \n // Find the function body (after function declaration) - now guaranteed to have braces\n const functionMatch = content.match(/const\\s+\\w+.*?=.*?\\(\\).*?=>\\s*\\{/);\n if (functionMatch) {\n const insertPos = functionMatch.index! + functionMatch[0].length;\n content = content.slice(0, insertPos) + '\\n' + stateHook + content.slice(insertPos);\n }\n\n // Wrap the return content with providers\n const returnStartMatch = content.match(/return\\s*\\(?[\\s\\n]*</);\n if (returnStartMatch) {\n const returnStart = returnStartMatch.index! + returnStartMatch[0].length - 1; // Position before <\n const returnEnd = content.indexOf(closingTag, returnStart) + closingTag.length;\n \n const originalReturn = content.slice(returnStart, returnEnd).trim();\n \n // Check if the original return starts with <Router> or <BrowserRouter>\n const isRouter = originalReturn.match(/^\\s*<(Router|BrowserRouter)/);\n \n let wrappedReturn;\n if (isRouter) {\n // Extract Router content - need to find the inner content\n const routerMatch = originalReturn.match(/<(Router|BrowserRouter)[^>]*>([\\s\\S]*)<\\/\\1>/);\n if (routerMatch) {\n const routerType = routerMatch[1];\n const innerContent = routerMatch[2];\n \n // Put Router on outside, VersionProvider and CommentProvider inside\n wrappedReturn = `\n <${routerType}>\n <VersionProvider>\n <CommentProvider>\n <CommentDrawer \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n >\n <CommentOverlay \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n />\n ${innerContent.trim()}\n </CommentDrawer>\n </CommentProvider>\n </VersionProvider>\n </${routerType}>\n `;\n } else {\n // Fallback to wrapping everything\n wrappedReturn = `\n <VersionProvider>\n <CommentProvider>\n <CommentDrawer \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n >\n <CommentOverlay \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n />\n ${originalReturn}\n </CommentDrawer>\n </CommentProvider>\n </VersionProvider>\n `;\n }\n } else {\n // No router, wrap everything normally\n wrappedReturn = `\n <VersionProvider>\n <CommentProvider>\n <CommentDrawer \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n >\n <CommentOverlay \n selectedThreadId={selectedThreadId} \n onThreadSelect={setSelectedThreadId}\n />\n ${originalReturn}\n </CommentDrawer>\n </CommentProvider>\n </VersionProvider>\n `;\n }\n \n content = content.slice(0, returnStart) + wrappedReturn + content.slice(returnEnd);\n }\n\n return content;\n}\n\nexport function showIntegrationDiff(filePath: string) {\n console.log(chalk.cyan('\\nChanges made to your App file:'));\n console.log(chalk.white('• Added commenting system imports'));\n console.log(chalk.white('• Added state hook for comment thread selection'));\n console.log(chalk.white('• Wrapped app with CommentProvider'));\n console.log(chalk.white('• Added CommentDrawer and CommentOverlay components\\n'));\n console.log(chalk.dim(`Modified: ${filePath}\\n`));\n}\n","import fs from 'fs/promises';\nimport path from 'path';\n\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(dirPath: string): Promise<void> {\n await fs.mkdir(dirPath, { recursive: true });\n}\n\nexport async function writeFileIfNotExists(filePath: string, content: string): Promise<boolean> {\n const exists = await fileExists(filePath);\n if (exists) {\n return false;\n }\n \n await fs.writeFile(filePath, content, 'utf-8');\n return true;\n}\n\nexport async function appendToFile(filePath: string, content: string): Promise<void> {\n await fs.appendFile(filePath, content, 'utf-8');\n}\n\nexport async function readFile(filePath: string): Promise<string> {\n return fs.readFile(filePath, 'utf-8');\n}\n\nexport async function writeFile(filePath: string, content: string): Promise<void> {\n await fs.writeFile(filePath, content, 'utf-8');\n}\n\nexport function getRelativePath(from: string, to: string): string {\n return path.relative(from, to);\n}\n\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,OAAOA,YAAW;;;ACDlB,OAAOC,YAAW;AAClB,OAAO,SAAS;AAChB,OAAO,cAAc;;;ACFrB,OAAO,QAAQ;AACf,OAAO,UAAU;AAYjB,eAAsB,cAAc,KAAmC;AACrE,QAAM,kBAAkB,KAAK,KAAK,KAAK,cAAc;AAErD,MAAI,cAAmB,CAAC;AACxB,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,iBAAiB,OAAO;AAC1D,kBAAc,KAAK,MAAM,OAAO;AAAA,EAClC,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AAEA,QAAM,OAAO;AAAA,IACX,GAAG,YAAY;AAAA,IACf,GAAG,YAAY;AAAA,EACjB;AAEA,QAAM,YAAY,MAAM,gBAAgB,KAAK,IAAI;AAEjD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW,gBAAgB,IAAI;AAAA,IAC/B;AAAA,IACA,SAAS,CAAC,CAAC,KAAK,OAAO;AAAA,IACvB,eAAe,CAAC,CAAC,KAAK,wBAAwB;AAAA,IAC9C,eAAe,CAAC,CAAC,KAAK,YAAY;AAAA,IAClC;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,MAAmB;AAC1C,MAAI,KAAK,OAAO,EAAG,QAAO;AAC1B,MAAI,KAAK,KAAK,EAAG,QAAO;AACxB,MAAI,KAAK,eAAe,EAAG,QAAO;AAClC,SAAO;AACT;AAEA,eAAe,gBAAgB,KAAa,MAA4B;AACtE,MAAI,KAAK,MAAM,EAAG,QAAO;AACzB,MAAI,KAAK,SAAS,EAAG,QAAO;AAE5B,MAAI;AACF,UAAM,GAAG,OAAO,KAAK,KAAK,KAAK,gBAAgB,CAAC;AAChD,WAAO;AAAA,EACT,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;;;AC7DA,OAAO,WAAW;AAEX,SAAS,eAAe;AAC7B,UAAQ,IAAI,MAAM,KAAK,KAAK,oDAA6C,CAAC;AAC1E,UAAQ,IAAI,MAAM,IAAI,mEAAmE,CAAC;AAC5F;AAEO,SAAS,aAAa,SAAiB;AAC5C,UAAQ,IAAI,MAAM,OAAO,iBAAO,OAAO,EAAE,CAAC;AAC5C;;;ACTA,OAAOC,WAAU;AACjB,OAAOC,YAAW;;;ACDlB,OAAOC,SAAQ;AAGf,eAAsB,WAAW,UAAoC;AACnE,MAAI;AACF,UAAMC,IAAG,OAAO,QAAQ;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAoBA,eAAsB,SAAS,UAAmC;AAChE,SAAOC,IAAG,SAAS,UAAU,OAAO;AACtC;AAEA,eAAsB,UAAU,UAAkB,SAAgC;AAChF,QAAMA,IAAG,UAAU,UAAU,SAAS,OAAO;AAC/C;;;AD1BA,eAAsB,mBAAmB,aAAiD;AAExF,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,cAA6B;AAEjC,aAAW,KAAK,eAAe;AAC7B,UAAM,WAAWC,MAAK,KAAK,aAAa,CAAC;AACzC,QAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,oBAAc;AACd;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,WAAW;AAG1C,QAAI,QAAQ,SAAS,wBAAwB,KAAK,QAAQ,SAAS,iBAAiB,GAAG;AACrF,aAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,kBAAkB,gBAAgB,OAAO;AAE/C,QAAI,oBAAoB,SAAS;AAC/B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,UAAU,aAAa,eAAe;AAE5C,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,SAAS,0CAA0CA,MAAK,SAAS,aAAa,WAAW,CAAC;AAAA,IAC5F;AAAA,EACF,SAAS,OAAY;AACnB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,SAAS,UAAU,MAAM,OAAO;AAAA,IAClC;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,SAAyB;AAEhD,QAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAShB,QAAM,cAAc;AACpB,QAAM,UAAU,QAAQ,MAAM,WAAW;AAEzC,MAAI,CAAC,WAAW,QAAQ,WAAW,GAAG;AAEpC,cAAU,UAAU,SAAS;AAAA,EAC/B,OAAO;AACL,UAAM,aAAa,QAAQ,QAAQ,SAAS,CAAC;AAC7C,UAAM,kBAAkB,QAAQ,YAAY,UAAU;AACtD,UAAM,iBAAiB,kBAAkB,WAAW;AACpD,cAAU,QAAQ,MAAM,GAAG,cAAc,IAAI,SAAS,UAAU,QAAQ,MAAM,cAAc;AAAA,EAC9F;AAKA,MAAI,cAAc,QAAQ,MAAM,2BAA2B;AAC3D,MAAI,mBAAmB;AAEvB,MAAI,CAAC,aAAa;AAEhB,kBAAc,QAAQ,MAAM,uBAAuB;AACnD,uBAAmB;AAAA,EACrB;AAEA,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,YAAY,CAAC;AAGnC,QAAM,aAAa,KAAK,aAAa;AACrC,QAAM,eAAe,QAAQ,QAAQ,UAAU;AAE/C,MAAI,iBAAiB,IAAI;AACvB,WAAO;AAAA,EACT;AAGA,MAAI,kBAAkB;AAEpB,UAAM,aAAa,QAAQ,MAAM,oCAAoC;AACrE,QAAI,YAAY;AACd,YAAM,cAAc,WAAW,CAAC;AAChC,YAAM,cAAc,WAAW,QAAS,WAAW,CAAC,EAAE,SAAS;AAG/D,YAAM,kBAAkB,QAAQ,QAAQ,MAAM,YAAY;AAC1D,UAAI,oBAAoB,IAAI;AAE1B,cAAM,aAAa,QAAQ,MAAM,cAAc,GAAG,eAAe;AACjE,kBAAU,QAAQ,MAAM,GAAG,WAAW,IAAI,iBAAiB,aAAa,QAAQ,QAAQ,MAAM,kBAAkB,CAAC;AAAA,MACnH;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY;AAAA;AAAA;AAGlB,QAAM,gBAAgB,QAAQ,MAAM,kCAAkC;AACtE,MAAI,eAAe;AACjB,UAAM,YAAY,cAAc,QAAS,cAAc,CAAC,EAAE;AAC1D,cAAU,QAAQ,MAAM,GAAG,SAAS,IAAI,OAAO,YAAY,QAAQ,MAAM,SAAS;AAAA,EACpF;AAGA,QAAM,mBAAmB,QAAQ,MAAM,sBAAsB;AAC7D,MAAI,kBAAkB;AACpB,UAAM,cAAc,iBAAiB,QAAS,iBAAiB,CAAC,EAAE,SAAS;AAC3E,UAAM,YAAY,QAAQ,QAAQ,YAAY,WAAW,IAAI,WAAW;AAExE,UAAM,iBAAiB,QAAQ,MAAM,aAAa,SAAS,EAAE,KAAK;AAGlE,UAAM,WAAW,eAAe,MAAM,6BAA6B;AAEnE,QAAI;AACJ,QAAI,UAAU;AAEZ,YAAM,cAAc,eAAe,MAAM,8CAA8C;AACvF,UAAI,aAAa;AACf,cAAM,aAAa,YAAY,CAAC;AAChC,cAAM,eAAe,YAAY,CAAC;AAGlC,wBAAgB;AAAA,OACjB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAWH,aAAa,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,QAIzB,UAAU;AAAA;AAAA,MAEZ,OAAO;AAEL,wBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAWZ,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,MAKpB;AAAA,IACF,OAAO;AAEL,sBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAWV,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,IAKtB;AAEA,cAAU,QAAQ,MAAM,GAAG,WAAW,IAAI,gBAAgB,QAAQ,MAAM,SAAS;AAAA,EACnF;AAEA,SAAO;AACT;AAEO,SAAS,oBAAoB,UAAkB;AACpD,UAAQ,IAAIC,OAAM,KAAK,kCAAkC,CAAC;AAC1D,UAAQ,IAAIA,OAAM,MAAM,wCAAmC,CAAC;AAC5D,UAAQ,IAAIA,OAAM,MAAM,sDAAiD,CAAC;AAC1E,UAAQ,IAAIA,OAAM,MAAM,yCAAoC,CAAC;AAC7D,UAAQ,IAAIA,OAAM,MAAM,4DAAuD,CAAC;AAChF,UAAQ,IAAIA,OAAM,IAAI,aAAa,QAAQ;AAAA,CAAI,CAAC;AAClD;;;AHxOA,eAAsB,YAAY,SAAsB;AACtD,eAAa;AAEb,QAAM,UAAU,IAAI,sBAAsB,EAAE,MAAM;AAGlD,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,cAAc,QAAQ,IAAI,CAAC;AAC3C,YAAQ,QAAQ,aAAa,QAAQ,SAAS,SAAS,QAAQ,SAAS,EAAE;AAAA,EAC5E,SAAS,OAAY;AACnB,YAAQ,KAAK,MAAM,OAAO;AAC1B,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,QAAQ,SAAS;AACpB,YAAQ,KAAK,uCAAuC;AACpD,YAAQ,IAAIC,OAAM,IAAI,mDAA8C,CAAC;AACrE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,CAAC,QAAQ,eAAe;AAC1B,iBAAa,6EAA6E;AAC1F,QAAI,CAAC,QAAQ,KAAK;AAChB,YAAM,EAAE,eAAe,IAAI,MAAM,SAAS,OAAO;AAAA,QAC/C;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AACD,UAAI,CAAC,gBAAgB;AACnB,gBAAQ,IAAIA,OAAM,IAAI,sBAAsB,CAAC;AAC7C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAGA,UAAQ,IAAIA,OAAM,KAAK,qDAA8C,CAAC;AAEtE,UAAQ,MAAM,gDAAgD;AAC9D,QAAM,SAAS,MAAM,mBAAmB,QAAQ,IAAI;AAEpD,MAAI,OAAO,SAAS;AAClB,YAAQ,QAAQ,+BAA+B;AAC/C,wBAAoB,OAAO,QAAQ;AACnC,YAAQ,IAAIA,OAAM,MAAM,4EAAuE,CAAC;AAChG,YAAQ,IAAIA,OAAM,KAAK,aAAa,CAAC;AACrC,YAAQ,IAAIA,OAAM,MAAM,wCAAwC,CAAC;AACjE,YAAQ,IAAIA,OAAM,MAAM,oDAAoD,CAAC;AAC7E,YAAQ,IAAIA,OAAM,MAAM,iCAAiC,CAAC;AAC1D,YAAQ,IAAIA,OAAM,MAAM,8DAA8D,CAAC;AACvF,YAAQ,IAAIA,OAAM,IAAI,iGAA0F,CAAC;AAAA,EACnH,OAAO;AACL,YAAQ,KAAK,OAAO,OAAO;AAC3B,YAAQ,IAAIA,OAAM,OAAO,4EAAkE,CAAC;AAC5F,YAAQ,IAAIA,OAAM,MAAM,4DAA4D,CAAC;AACrF,YAAQ,IAAIA,OAAM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAyBtB,CAAC;AAAA,EACH;AACF;;;AD7FA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,wBAAwB,EAC7B,YAAY,iDAAiD,EAC7D,QAAQ,OAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,mDAAmD,EAC/D,OAAO,aAAa,+BAA+B,EACnD,OAAO,OAAO,YAAY;AACzB,MAAI;AACF,UAAM,YAAY,OAAO;AAAA,EAC3B,SAAS,OAAY;AACnB,YAAQ,MAAMC,OAAM,IAAI,iBAAY,GAAG,MAAM,OAAO;AACpD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAGH,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE,QAAQ;AACjC,UAAQ,KAAK;AACf;AAEA,QAAQ,MAAM;","names":["chalk","chalk","path","chalk","fs","fs","fs","path","chalk","chalk","chalk"]}
|