Skip to content

Commit 730d749

Browse files
Copilotfregante
andcommitted
Extend existing JSDoc blocks and add validations to add-examples-to-dts.ts
- Script now detects and extends existing JSDoc comments (both single-line and multi-line) - Added marker to prevent running script twice on the same file - Added validation that at least some examples were added - Added TypeScript validation after modification to ensure file is still valid - Exits with error if any validation fails - Updated distribution files with properly extended JSDoc blocks Co-authored-by: fregante <1402241+fregante@users.noreply.github.com>
1 parent 6bc42f0 commit 730d749

File tree

2 files changed

+116
-11
lines changed

2 files changed

+116
-11
lines changed

add-examples-to-dts.ts

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/env node --experimental-strip-types
2+
/* eslint-disable n/prefer-global/process */
23
import {readFileSync, writeFileSync} from 'node:fs';
4+
import {execSync} from 'node:child_process';
35
// Import index.ts to populate the test data via side effect
46
// eslint-disable-next-line import/no-unassigned-import, n/file-extension-in-import
57
import './index.ts';
@@ -10,9 +12,17 @@ import {getTests} from './collector.ts';
1012
const dtsPath = './distribution/index.d.ts';
1113
const dtsContent = readFileSync(dtsPath, 'utf8');
1214

15+
// Check if script has already been run
16+
const marker = '/* Examples added by add-examples-to-dts.ts */';
17+
if (dtsContent.includes(marker)) {
18+
console.error('❌ Error: Examples have already been added to this file');
19+
process.exit(1);
20+
}
21+
1322
// Process each exported function
1423
const lines = dtsContent.split('\n');
1524
const outputLines: string[] = [];
25+
let examplesAdded = 0;
1626

1727
for (const line of lines) {
1828
// Check if this is a function declaration
@@ -24,28 +34,122 @@ for (const line of lines) {
2434
const examples = getTests(functionName);
2535

2636
// Only add examples if they exist and aren't the special 'combinedTestOnly' marker
27-
// 'combinedTestOnly' is used to skip tests for combined functions (e.g., isPageA() || isPageB())
2837
if (examples && examples.length > 0 && examples[0] !== 'combinedTestOnly') {
2938
// Filter to only include actual URLs (not references to other functions)
30-
// getTests() recursively expands function references, so we just need to filter the final list
3139
const urlExamples = examples.filter((url: string) => url.startsWith('http'));
3240

3341
if (urlExamples.length > 0) {
34-
// Add JSDoc comment with examples before the declaration
35-
outputLines.push('/**');
36-
for (const url of urlExamples) {
37-
outputLines.push(` * @example ${url}`);
42+
// Check if there's an existing JSDoc block immediately before this line
43+
let jsDocumentEndIndex = -1;
44+
let jsDocumentStartIndex = -1;
45+
let isSingleLineJsDocument = false;
46+
47+
// Look backwards from outputLines to find JSDoc
48+
for (let index = outputLines.length - 1; index >= 0; index--) {
49+
const previousLine = outputLines[index];
50+
const trimmed = previousLine.trim();
51+
52+
if (trimmed === '') {
53+
continue; // Skip empty lines
54+
}
55+
56+
// Check for single-line JSDoc: /** ... */
57+
if (trimmed.startsWith('/**') && trimmed.endsWith('*/') && trimmed.length > 5) {
58+
jsDocumentStartIndex = index;
59+
jsDocumentEndIndex = index;
60+
isSingleLineJsDocument = true;
61+
break;
62+
}
63+
64+
// Check for multi-line JSDoc ending
65+
if (trimmed === '*/') {
66+
jsDocumentEndIndex = index;
67+
// Now find the start of this JSDoc
68+
for (let k = index - 1; k >= 0; k--) {
69+
if (outputLines[k].trim().startsWith('/**')) {
70+
jsDocumentStartIndex = k;
71+
break;
72+
}
73+
}
74+
75+
break;
76+
}
77+
78+
// If we hit a non-JSDoc line, there's no JSDoc block
79+
break;
3880
}
3981

40-
outputLines.push(' */');
82+
if (jsDocumentStartIndex >= 0 && jsDocumentEndIndex >= 0) {
83+
// Extend existing JSDoc block
84+
if (isSingleLineJsDocument) {
85+
// Convert single-line to multi-line and add examples
86+
const singleLineContent = outputLines[jsDocumentStartIndex];
87+
// Extract the comment text without /** and */
88+
const commentText = singleLineContent.trim().slice(3, -2).trim();
89+
90+
// Replace the single line with multi-line format
91+
outputLines[jsDocumentStartIndex] = '/**';
92+
if (commentText) {
93+
outputLines.splice(jsDocumentStartIndex + 1, 0, ` * ${commentText}`);
94+
}
95+
96+
// Add examples after the existing content
97+
const insertIndex = jsDocumentStartIndex + (commentText ? 2 : 1);
98+
for (const url of urlExamples) {
99+
outputLines.splice(insertIndex + urlExamples.indexOf(url), 0, ` * @example ${url}`);
100+
}
101+
102+
outputLines.splice(insertIndex + urlExamples.length, 0, ' */');
103+
examplesAdded += urlExamples.length;
104+
} else {
105+
// Insert @example lines before the closing */
106+
for (const url of urlExamples) {
107+
outputLines.splice(jsDocumentEndIndex, 0, ` * @example ${url}`);
108+
}
109+
110+
examplesAdded += urlExamples.length;
111+
}
112+
} else {
113+
// Add new JSDoc comment with examples before the declaration
114+
outputLines.push('/**');
115+
for (const url of urlExamples) {
116+
outputLines.push(` * @example ${url}`);
117+
}
118+
119+
outputLines.push(' */');
120+
examplesAdded += urlExamples.length;
121+
}
41122
}
42123
}
43124
}
44125

45126
outputLines.push(line);
46127
}
47128

129+
// Add marker at the beginning
130+
const finalContent = `${marker}\n${outputLines.join('\n')}`;
131+
132+
// Validate that we added some examples
133+
if (examplesAdded === 0) {
134+
console.error('❌ Error: No examples were added. This likely indicates a problem with the script.');
135+
process.exit(1);
136+
}
137+
48138
// Write the modified content back
49-
writeFileSync(dtsPath, outputLines.join('\n'), 'utf8');
139+
writeFileSync(dtsPath, finalContent, 'utf8');
50140

51-
console.log('✓ Added example URLs to index.d.ts');
141+
console.log(`✓ Added ${examplesAdded} example URLs to index.d.ts`);
142+
143+
// Validate with TypeScript
144+
try {
145+
execSync('npx tsc --noEmit distribution/index.d.ts', {
146+
cwd: process.cwd(),
147+
stdio: 'pipe',
148+
});
149+
console.log('✓ TypeScript validation passed');
150+
} catch (error: unknown) {
151+
console.error('❌ TypeScript validation failed:');
152+
const execError = error as {stdout?: Uint8Array; stderr?: Uint8Array; message?: string};
153+
console.error(execError.stdout?.toString() ?? execError.stderr?.toString() ?? execError.message);
154+
process.exit(1);
155+
}

distribution/index.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* Examples added by add-examples-to-dts.ts */
12
export declare const is404: () => boolean;
23
export declare const is500: () => boolean;
34
export declare const isPasswordConfirmation: () => boolean;
@@ -195,8 +196,8 @@ export declare const isPR: (url?: URL | HTMLAnchorElement | Location) => boolean
195196
* @example https://github.com/sindresorhus/refined-github/pull/148/conflicts
196197
*/
197198
export declare const isPRConflicts: (url?: URL | HTMLAnchorElement | Location) => boolean;
198-
/** Any `isIssueOrPRList` can display both issues and PRs, prefer that detection. `isPRList` only exists because this page has PR-specific filters like the "Reviews" dropdown */
199199
/**
200+
* Any `isIssueOrPRList` can display both issues and PRs, prefer that detection. `isPRList` only exists because this page has PR-specific filters like the "Reviews" dropdown
200201
* @example https://github.com/pulls
201202
* @example https://github.com/pulls?q=issues
202203
* @example https://github.com/sindresorhus/refined-github/pulls
@@ -532,8 +533,8 @@ export declare const hasComments: (url?: URL | HTMLAnchorElement | Location) =>
532533
export declare const hasRichTextEditor: (url?: URL | HTMLAnchorElement | Location) => boolean;
533534
/** Static code, not the code editor */
534535
export declare const hasCode: (url?: URL | HTMLAnchorElement | Location) => boolean;
535-
/** Covers blob, trees and blame pages */
536536
/**
537+
* Covers blob, trees and blame pages
537538
* @example https://github.com/sindresorhus/refined-github
538539
* @example https://github.com/sindresorhus/refined-github/
539540
* @example https://github.com/sindresorhus/notifications/

0 commit comments

Comments
 (0)