hale-commenting-system 2.2.92 → 2.2.95
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/package.json +1 -1
- package/scripts/integrate.js +252 -85
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hale-commenting-system",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.95",
|
|
4
4
|
"description": "A commenting system for PatternFly React applications that allows designers and developers to add comments directly on design pages, sync with GitHub Issues, and link Jira tickets.",
|
|
5
5
|
"repository": "https://github.com/patternfly/patternfly-react-seed.git",
|
|
6
6
|
"homepage": "https://www.npmjs.com/package/hale-commenting-system",
|
package/scripts/integrate.js
CHANGED
|
@@ -1170,115 +1170,282 @@ function modifyRoutesTsx(filePath) {
|
|
|
1170
1170
|
}
|
|
1171
1171
|
|
|
1172
1172
|
function modifyAppLayoutTsx(filePath) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
// Check if already integrated
|
|
1176
|
-
if (content.includes('
|
|
1177
|
-
|
|
1173
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
1174
|
+
|
|
1175
|
+
// Check if already integrated - look for the comprehensive integration
|
|
1176
|
+
if (content.includes('useComments') && content.includes('useGitHubAuth') &&
|
|
1177
|
+
content.includes('setCommentsEnabled') && content.includes('setFloatingWidgetMode')) {
|
|
1178
|
+
console.log(' ⚠️ Already integrated (full commenting system controls found)');
|
|
1178
1179
|
return false;
|
|
1179
1180
|
}
|
|
1180
1181
|
|
|
1181
1182
|
try {
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
if (
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
s.imported && (s.imported.name === 'CommentPanel' || s.local.name === 'CommentPanel')
|
|
1200
|
-
);
|
|
1201
|
-
const hasCommentOverlay = specifiers.some(s =>
|
|
1202
|
-
s.imported && (s.imported.name === 'CommentOverlay' || s.local.name === 'CommentOverlay')
|
|
1183
|
+
// Step 1: Add imports using string manipulation (more reliable for complex imports)
|
|
1184
|
+
|
|
1185
|
+
// Check and add PatternFly imports
|
|
1186
|
+
const patternflyImportRegex = /from\s+['"]@patternfly\/react-core['"]/;
|
|
1187
|
+
const patternflyMatch = content.match(patternflyImportRegex);
|
|
1188
|
+
|
|
1189
|
+
if (patternflyMatch) {
|
|
1190
|
+
// Find the import statement
|
|
1191
|
+
const importMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-core['"]/);
|
|
1192
|
+
if (importMatch) {
|
|
1193
|
+
const imports = importMatch[1];
|
|
1194
|
+
// Add Switch if not present
|
|
1195
|
+
if (!imports.includes('Switch')) {
|
|
1196
|
+
const newImports = imports.trim() + ',\n Switch';
|
|
1197
|
+
content = content.replace(
|
|
1198
|
+
/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-core['"]/,
|
|
1199
|
+
`import {${newImports}\n} from '@patternfly/react-core'`
|
|
1203
1200
|
);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
1204
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1205
|
+
// Check and add PatternFly icons imports
|
|
1206
|
+
const iconsImportRegex = /from\s+['"]@patternfly\/react-icons['"]/;
|
|
1207
|
+
const iconsMatch = content.match(iconsImportRegex);
|
|
1208
|
+
|
|
1209
|
+
if (iconsMatch) {
|
|
1210
|
+
const importMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-icons['"]/);
|
|
1211
|
+
if (importMatch) {
|
|
1212
|
+
const imports = importMatch[1];
|
|
1213
|
+
// Add icons if not present
|
|
1214
|
+
let newImports = imports.trim();
|
|
1215
|
+
if (!imports.includes('ExternalLinkAltIcon')) {
|
|
1216
|
+
newImports += ', ExternalLinkAltIcon';
|
|
1212
1217
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1218
|
+
if (!imports.includes('GithubIcon')) {
|
|
1219
|
+
newImports += ', GithubIcon';
|
|
1220
|
+
}
|
|
1221
|
+
if (newImports !== imports.trim()) {
|
|
1222
|
+
content = content.replace(
|
|
1223
|
+
/import\s+\{([^}]+)\}\s+from\s+['"]@patternfly\/react-icons['"]/,
|
|
1224
|
+
`import { ${newImports} } from '@patternfly/react-icons'`
|
|
1225
|
+
);
|
|
1217
1226
|
}
|
|
1218
1227
|
}
|
|
1219
|
-
}
|
|
1228
|
+
}
|
|
1220
1229
|
|
|
1221
|
-
// Add
|
|
1222
|
-
if (!
|
|
1223
|
-
// Find
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
+
// Add commenting system imports
|
|
1231
|
+
if (!content.includes('hale-commenting-system')) {
|
|
1232
|
+
// Find where to insert (after other imports)
|
|
1233
|
+
const lastImportMatch = content.match(/import[^;]*;(?=\s*(?:interface|const|export|function))/g);
|
|
1234
|
+
if (lastImportMatch) {
|
|
1235
|
+
const lastImport = lastImportMatch[lastImportMatch.length - 1];
|
|
1236
|
+
const insertPos = content.indexOf(lastImport) + lastImport.length;
|
|
1237
|
+
const commentingImport = `\nimport { CommentOverlay, CommentPanel, useComments, useGitHubAuth } from "hale-commenting-system";`;
|
|
1238
|
+
content = content.slice(0, insertPos) + commentingImport + content.slice(insertPos);
|
|
1239
|
+
}
|
|
1240
|
+
} else {
|
|
1241
|
+
// Update existing import to include all needed items
|
|
1242
|
+
const commentingImportMatch = content.match(/import\s+\{([^}]+)\}\s+from\s+["']hale-commenting-system["']/);
|
|
1243
|
+
if (commentingImportMatch) {
|
|
1244
|
+
const imports = commentingImportMatch[1];
|
|
1245
|
+
let newImports = imports.split(',').map(i => i.trim());
|
|
1246
|
+
|
|
1247
|
+
const needed = ['CommentOverlay', 'CommentPanel', 'useComments', 'useGitHubAuth'];
|
|
1248
|
+
needed.forEach(item => {
|
|
1249
|
+
if (!newImports.includes(item)) {
|
|
1250
|
+
newImports.push(item);
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
content = content.replace(
|
|
1255
|
+
/import\s+\{[^}]+\}\s+from\s+["']hale-commenting-system["']/,
|
|
1256
|
+
`import { ${newImports.join(', ')} } from "hale-commenting-system"`
|
|
1257
|
+
);
|
|
1230
1258
|
}
|
|
1231
|
-
const importIndex = lastImportIndex >= 0 ? lastImportIndex + 1 : 0;
|
|
1232
|
-
|
|
1233
|
-
const componentImports = types.importDeclaration(
|
|
1234
|
-
[
|
|
1235
|
-
types.importSpecifier(types.identifier('CommentPanel'), types.identifier('CommentPanel')),
|
|
1236
|
-
types.importSpecifier(types.identifier('CommentOverlay'), types.identifier('CommentOverlay'))
|
|
1237
|
-
],
|
|
1238
|
-
types.stringLiteral('hale-commenting-system')
|
|
1239
|
-
);
|
|
1240
|
-
|
|
1241
|
-
ast.program.body.splice(importIndex, 0, componentImports);
|
|
1242
1259
|
}
|
|
1243
1260
|
|
|
1244
|
-
//
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1261
|
+
// Step 2: Add hooks to the component
|
|
1262
|
+
// Find the AppLayout function/component
|
|
1263
|
+
const componentMatch = content.match(/(const\s+AppLayout[^=]+=\s*\([^)]*\)\s*=>\s*\{)/);
|
|
1264
|
+
if (componentMatch) {
|
|
1265
|
+
const componentStart = content.indexOf(componentMatch[0]);
|
|
1266
|
+
const afterComponentStart = componentStart + componentMatch[0].length;
|
|
1267
|
+
|
|
1268
|
+
// Check if hooks are already added
|
|
1269
|
+
if (!content.includes('const { commentsEnabled, setCommentsEnabled')) {
|
|
1270
|
+
// Find where to insert hooks (after existing useState declarations)
|
|
1271
|
+
const stateMatch = content.slice(afterComponentStart).match(/const\s+\[[^\]]+\]\s*=\s*React\.useState/);
|
|
1272
|
+
let hookInsertPos;
|
|
1273
|
+
|
|
1274
|
+
if (stateMatch) {
|
|
1275
|
+
const statePos = content.indexOf(stateMatch[0], afterComponentStart);
|
|
1276
|
+
const semicolonPos = content.indexOf(';', statePos);
|
|
1277
|
+
hookInsertPos = semicolonPos + 1;
|
|
1278
|
+
} else {
|
|
1279
|
+
hookInsertPos = afterComponentStart;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
const hooks = `
|
|
1283
|
+
const { commentsEnabled, setCommentsEnabled, drawerPinnedOpen, setDrawerPinnedOpen, floatingWidgetMode, setFloatingWidgetMode } = useComments();
|
|
1284
|
+
const { isAuthenticated, user, login, logout } = useGitHubAuth();
|
|
1285
|
+
`;
|
|
1286
|
+
content = content.slice(0, hookInsertPos) + hooks + content.slice(hookInsertPos);
|
|
1254
1287
|
}
|
|
1288
|
+
}
|
|
1255
1289
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1290
|
+
// Step 3: Add the special renderNavGroup logic
|
|
1291
|
+
// Replace the simple arrow function with a block function that has special Comments handling
|
|
1292
|
+
if (!content.includes("group.label === 'Comments'")) {
|
|
1293
|
+
// Find the renderNavGroup function - handle both arrow expression () => (...) and block () => {...}
|
|
1294
|
+
const arrowFuncMatch = content.match(/const\s+renderNavGroup\s*=\s*\(([^)]+)\)\s*=>\s*\(/);
|
|
1295
|
+
|
|
1296
|
+
if (arrowFuncMatch) {
|
|
1297
|
+
const params = arrowFuncMatch[1];
|
|
1298
|
+
|
|
1299
|
+
// Find the entire function including the closing parenthesis and semicolon
|
|
1300
|
+
const funcStart = content.indexOf(arrowFuncMatch[0]);
|
|
1301
|
+
const afterArrow = funcStart + arrowFuncMatch[0].length;
|
|
1302
|
+
|
|
1303
|
+
// Find matching closing paren and semicolon
|
|
1304
|
+
let depth = 1;
|
|
1305
|
+
let endPos = afterArrow;
|
|
1306
|
+
for (let i = afterArrow; i < content.length; i++) {
|
|
1307
|
+
if (content.charAt(i) === '(') depth++;
|
|
1308
|
+
if (content.charAt(i) === ')') {
|
|
1309
|
+
depth--;
|
|
1310
|
+
if (depth === 0) {
|
|
1311
|
+
// Found the closing paren, now find the semicolon
|
|
1312
|
+
endPos = i + 1;
|
|
1313
|
+
while (endPos < content.length && content.charAt(endPos).trim() === '') endPos++;
|
|
1314
|
+
if (content.charAt(endPos) === ';') endPos++;
|
|
1315
|
+
break;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1262
1319
|
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1320
|
+
// Extract the original NavExpandable JSX (we'll use it as the default case)
|
|
1321
|
+
const originalBody = content.slice(funcStart + arrowFuncMatch[0].length - 1, endPos - 1); // Remove opening ( and closing );
|
|
1322
|
+
|
|
1323
|
+
// Create the new block function
|
|
1324
|
+
const newFunction = `const renderNavGroup = (${params}) => {
|
|
1325
|
+
// Special handling for Comments group
|
|
1326
|
+
if (group.label === 'Comments') {
|
|
1327
|
+
return (
|
|
1328
|
+
<NavExpandable
|
|
1329
|
+
key={\`\${group.label}-\${groupIndex}\`}
|
|
1330
|
+
id={\`\${group.label}-\${groupIndex}\`}
|
|
1331
|
+
title="Hale Commenting System"
|
|
1332
|
+
isActive={group.routes.some((route) => route.path === location.pathname)}
|
|
1333
|
+
>
|
|
1334
|
+
<NavItem
|
|
1335
|
+
onClick={(e) => {
|
|
1336
|
+
e.stopPropagation();
|
|
1337
|
+
setFloatingWidgetMode(!floatingWidgetMode);
|
|
1338
|
+
if (!floatingWidgetMode) {
|
|
1339
|
+
setDrawerPinnedOpen(false);
|
|
1340
|
+
}
|
|
1341
|
+
}}
|
|
1342
|
+
style={{ cursor: 'pointer' }}
|
|
1343
|
+
>
|
|
1344
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
|
1345
|
+
<ExternalLinkAltIcon />
|
|
1346
|
+
<span>{floatingWidgetMode ? 'Close widget' : 'Pop out'}</span>
|
|
1347
|
+
</div>
|
|
1348
|
+
</NavItem>
|
|
1349
|
+
<NavItem>
|
|
1350
|
+
<div
|
|
1351
|
+
data-comment-controls
|
|
1352
|
+
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
|
|
1353
|
+
>
|
|
1354
|
+
<span>Enable Comments</span>
|
|
1355
|
+
<Switch
|
|
1356
|
+
id="comments-enabled-switch"
|
|
1357
|
+
isChecked={commentsEnabled}
|
|
1358
|
+
onChange={(_event, checked) => {
|
|
1359
|
+
setCommentsEnabled(checked);
|
|
1360
|
+
if (checked) {
|
|
1361
|
+
setDrawerPinnedOpen(true);
|
|
1362
|
+
}
|
|
1363
|
+
}}
|
|
1364
|
+
aria-label="Enable or disable comments"
|
|
1365
|
+
/>
|
|
1366
|
+
</div>
|
|
1367
|
+
</NavItem>
|
|
1368
|
+
<NavItem>
|
|
1369
|
+
<div
|
|
1370
|
+
data-comment-controls
|
|
1371
|
+
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
|
|
1372
|
+
>
|
|
1373
|
+
<span>Page info drawer</span>
|
|
1374
|
+
<Switch
|
|
1375
|
+
id="page-info-drawer-switch"
|
|
1376
|
+
isChecked={drawerPinnedOpen}
|
|
1377
|
+
onChange={(_event, checked) => setDrawerPinnedOpen(checked)}
|
|
1378
|
+
aria-label="Pin page info drawer open"
|
|
1379
|
+
/>
|
|
1380
|
+
</div>
|
|
1381
|
+
</NavItem>
|
|
1382
|
+
<NavItem>
|
|
1383
|
+
<div
|
|
1384
|
+
data-comment-controls
|
|
1385
|
+
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: '1rem' }}
|
|
1386
|
+
>
|
|
1387
|
+
{isAuthenticated ? (
|
|
1388
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
1389
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '6px' }}>
|
|
1390
|
+
<GithubIcon />
|
|
1391
|
+
{user?.login ? \`@\${user.login}\` : 'Signed in'}
|
|
1392
|
+
</span>
|
|
1393
|
+
<Button variant="link" isInline onClick={logout}>
|
|
1394
|
+
Sign out
|
|
1395
|
+
</Button>
|
|
1396
|
+
</div>
|
|
1397
|
+
) : (
|
|
1398
|
+
<Button variant="link" isInline icon={<GithubIcon />} onClick={login}>
|
|
1399
|
+
Sign in with GitHub
|
|
1400
|
+
</Button>
|
|
1401
|
+
)}
|
|
1402
|
+
</div>
|
|
1403
|
+
</NavItem>
|
|
1404
|
+
{group.routes.map((route, idx) => route.label && renderNavItem(route, idx))}
|
|
1405
|
+
</NavExpandable>
|
|
1268
1406
|
);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// Default handling for other groups
|
|
1410
|
+
return ${originalBody};
|
|
1411
|
+
};`;
|
|
1269
1412
|
|
|
1270
|
-
|
|
1413
|
+
// Replace the old function with the new one
|
|
1414
|
+
content = content.slice(0, funcStart) + newFunction + content.slice(endPos);
|
|
1415
|
+
}
|
|
1271
1416
|
}
|
|
1272
1417
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1418
|
+
// Step 4: Wrap Page children if not already done
|
|
1419
|
+
if (!content.includes('<CommentPanel>')) {
|
|
1420
|
+
// Find the return statement with Page
|
|
1421
|
+
const pageMatch = content.match(/<Page[^>]*>/);
|
|
1422
|
+
if (pageMatch) {
|
|
1423
|
+
const pageStart = content.indexOf(pageMatch[0]);
|
|
1424
|
+
const pageEnd = content.indexOf('</Page>', pageStart);
|
|
1425
|
+
|
|
1426
|
+
if (pageEnd > pageStart) {
|
|
1427
|
+
// Extract children between Page tags
|
|
1428
|
+
const pageOpenTagEnd = content.indexOf('>', pageStart) + 1;
|
|
1429
|
+
const children = content.slice(pageOpenTagEnd, pageEnd);
|
|
1430
|
+
|
|
1431
|
+
// Wrap with CommentPanel and add CommentOverlay
|
|
1432
|
+
const wrappedChildren = `
|
|
1433
|
+
<CommentPanel>
|
|
1434
|
+
<CommentOverlay />
|
|
1435
|
+
${children.trim()}
|
|
1436
|
+
</CommentPanel>
|
|
1437
|
+
`;
|
|
1438
|
+
|
|
1439
|
+
content = content.slice(0, pageOpenTagEnd) + wrappedChildren + content.slice(pageEnd);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1277
1443
|
|
|
1278
|
-
fs.writeFileSync(filePath,
|
|
1444
|
+
fs.writeFileSync(filePath, content);
|
|
1279
1445
|
return true;
|
|
1280
1446
|
} catch (error) {
|
|
1281
1447
|
console.error(` ❌ Error modifying ${filePath}:`, error.message);
|
|
1448
|
+
console.error(error.stack);
|
|
1282
1449
|
return false;
|
|
1283
1450
|
}
|
|
1284
1451
|
}
|