Skip to content

Commit 9dd2b66

Browse files
committed
refactor: clean-up and handle interface case
--- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: passed - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: passed - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed ---
1 parent f723627 commit 9dd2b66

File tree

5 files changed

+97
-38
lines changed

5 files changed

+97
-38
lines changed

lib/node_modules/@stdlib/_tools/eslint/rules/tsdoc-doctest/README.md

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,6 @@ var rule = require( '@stdlib/_tools/eslint/rules/tsdoc-doctest' );
4040

4141
[ESLint rule][eslint-rules] to ensure that return annotations in TSDoc examples match the actual output. The rule validates `@example` blocks in TSDoc comments within `.d.ts` files.
4242

43-
The rule:
44-
45-
- Extracts `@example` blocks from TSDoc comments in TypeScript declaration files
46-
- Loads the actual JavaScript package corresponding to the declaration file
47-
- Executes the example code in a sandboxed environment
48-
- Compares the actual output with the expected return annotations
49-
- Supports functions, constants, classes, and namespace objects
50-
- Handles `// returns`, `// throws`, and `// =>` annotations
51-
5243
**Bad**:
5344

5445
<!-- eslint-disable stdlib/tsdoc-doctest -->
@@ -140,9 +131,7 @@ var result = linter.verify( code, {
140131
}, {
141132
'filename': 'lib/node_modules/@stdlib/math/base/special/abs/docs/types/index.d.ts'
142133
});
143-
144-
console.log( result );
145-
/* =>
134+
/* returns
146135
[
147136
{
148137
'ruleId': 'tsdoc-doctest',
@@ -166,10 +155,11 @@ console.log( result );
166155

167156
## Notes
168157

169-
- The rule requires that the TypeScript declaration file path follows the stdlib convention: `lib/node_modules/@stdlib/<package>/docs/types/index.d.ts`
170158
- The corresponding JavaScript package must be loadable via `require('@stdlib/<package>')`
171-
- The rule skips validation if the package cannot be loaded
172-
- Examples are executed in a sandboxed VM context with limited globals
159+
- The rule skips validation if the package cannot be loaded.
160+
- Examples are executed in a sandboxed VM context with limited globals.
161+
- The rule validates assignment-form examples (e.g., `var x = fn(...);` followed by an annotation). It does not validate console output or expression-only forms using `// =>`.
162+
- The implementation path the rule uses to load the JavaScript implementation can be overridden via the `implementationPath` rule option (default: `../../lib` relative to the declaration file).
173163

174164
</section>
175165

lib/node_modules/@stdlib/_tools/eslint/rules/tsdoc-doctest/lib/add_package_to_scope.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ var RE_DECLARE_VAR = /declare\s+var\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/;
2626
var RE_DECLARE_CLASS = /declare\s+class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s/;
2727
var RE_DECLARE_VAR_NAMESPACE = /declare\s+var\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:\s*[A-Z][a-zA-Z0-9_$]*/;
2828
var RE_DECLARE_CONST = /declare\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/;
29+
var RE_DECLARE_VAR_INTERFACE = /declare\s+var\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:\s*([A-Z][a-zA-Z0-9_$]*)/;
2930

3031

3132
// MAIN //
@@ -38,6 +39,7 @@ var RE_DECLARE_CONST = /declare\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/;
3839
* @param {string} sourceText - TypeScript declaration source text to parse for identifier names
3940
*/
4041
function addPackageToScope( scope, pkg, sourceText ) {
42+
var interfaceMatch;
4143
var namespaceMatch;
4244
var funcMatch;
4345

@@ -55,6 +57,13 @@ function addPackageToScope( scope, pkg, sourceText ) {
5557
if ( funcMatch ) {
5658
scope[ funcMatch[1] ] = pkg;
5759
}
60+
// Also check for declare var with interface pattern (e.g., declare var ctor: Int32Vector):
61+
interfaceMatch = sourceText.match( RE_DECLARE_VAR_INTERFACE );
62+
if ( interfaceMatch ) {
63+
// Make the function available under both the variable name and the interface name:
64+
scope[ interfaceMatch[1] ] = pkg; // e.g., ctor
65+
scope[ interfaceMatch[2] ] = pkg; // e.g., Int32Vector
66+
}
5867
} else if ( typeof pkg === 'object' && pkg !== null ) {
5968
// Handle namespace objects and other object interfaces:
6069
namespaceMatch = sourceText.match( RE_DECLARE_VAR_NAMESPACE );

lib/node_modules/@stdlib/_tools/eslint/rules/tsdoc-doctest/lib/main.js

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ var RE_TSDOC = /\/\*\*[\s\S]+?\*\//g;
4646
var RE_EXAMPLE = /@example\s*([\s\S]*?)(?=\n\s*@\w|\*\/|$)/g;
4747
var RE_NEWLINE = /\r?\n/g;
4848
var RE_ANNOTATION = /(?:\n|^)(?:var|let|const)? ?([a-zA-Z0-9._]+) ?=[^;]*;\n\/\/ ?(returns|([A-Za-z][A-Za-z_0-9]*)? ?=>|throws) ?([\s\S]*?)(\n|$)/g;
49-
var RE_CONSOLE = /console\.(?:dir|error|log)/;
5049
var RE_COMMENT_PREFIX = /^\s*\*\s?/gm;
5150
var RE_SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g;
5251
var regexCache = {};
@@ -201,27 +200,23 @@ function processExampleCode( code, commentIdx, comments, scope, report, opts, so
201200
var type;
202201
var loc;
203202
var msg;
204-
var out;
205203
var arr;
206204

207-
// VM context already created in validate() function
208-
209205
last = 0;
210206
RE_ANNOTATION.lastIndex = 0;
211207
try {
212208
arr = RE_ANNOTATION.exec( code );
213209
while ( !isNull( arr ) ) {
214-
// Run intermediary code
215210
intermediary = code.substring( last, arr.index );
216211
last = arr.index + arr[ 0 ].length;
217212
if ( intermediary ) {
218213
vm.runInContext( intermediary, scope );
219214
}
220215

221-
// Calculate line of current code chunk within the comment
216+
// Calculate line of current code chunk within the comment:
222217
commentStartIdx = sourceCode.text.indexOf( comments[ commentIdx ] );
223218

224-
// Find the annotation in the original comment
219+
// Find the annotation in the original comment:
225220
returnAnnotationPattern = getAnnotationRegex( arr[ 2 ], arr[ 4 ] );
226221
annotationMatch = comments[ commentIdx ].match( returnAnnotationPattern );
227222

@@ -242,14 +237,12 @@ function processExampleCode( code, commentIdx, comments, scope, report, opts, so
242237
}
243238
};
244239

245-
// Run code preceding return annotation
240+
// Run code preceding return annotation:
246241
try {
247-
out = vm.runInContext( arr[ 0 ], scope );
248-
if ( RE_CONSOLE.test( arr[ 1 ] ) ) {
249-
actual = out;
250-
} else {
251-
actual = scope[ arr[ 1 ] ];
252-
}
242+
vm.runInContext( arr[ 0 ], scope );
243+
244+
// For assignment-form examples, read the assigned variable from scope:
245+
actual = scope[ arr[ 1 ] ];
253246
if ( arr[ 3 ] ) {
254247
actual = vm.runInContext( arr[ 3 ], scope );
255248
}
@@ -259,13 +252,13 @@ function processExampleCode( code, commentIdx, comments, scope, report, opts, so
259252
opts.includeDecimal = isNumber( actual ) && contains( expected, '.' );
260253
replacement = createAnnotationValue( actual, opts );
261254

262-
// Find the position of the return value in the annotation
255+
// Find the position of the return value in the annotation:
263256
valueStartInAnnotation = annotationMatch[ 0 ].indexOf( arr[ 4 ] );
264257
loc.range = [
265-
// Position of arr[4] start in source text
258+
// Position of arr[4] start in source text:
266259
commentStartIdx + codeIdx + valueStartInAnnotation,
267260

268-
// Position of arr[4] end in source text
261+
// Position of arr[4] end in source text:
269262
commentStartIdx + codeIdx + valueStartInAnnotation + arr[ 4 ].length
270263
];
271264
report( loc, msg, replacement );
@@ -286,7 +279,7 @@ function processExampleCode( code, commentIdx, comments, scope, report, opts, so
286279
if ( msg ) {
287280
replacement = ( arr[ 3 ] ) ? findName( scope, arr[ 4 ] ) + ' => ' + arr[ 4 ] : 'throws '+type;
288281

289-
// Find annotation part in the match (after "// ")
282+
// Find annotation part in the match (after "// "):
290283
annotationStart = arr[ 0 ].indexOf( '// ' ) + 3;
291284
annotationEnd = arr[ 0 ].length - arr[ 5 ].length;
292285
loc.range = [
@@ -299,13 +292,12 @@ function processExampleCode( code, commentIdx, comments, scope, report, opts, so
299292
arr = RE_ANNOTATION.exec( code );
300293
}
301294

302-
// Run any remaining code
303295
remaining = code.substring( last );
304296
if ( remaining ) {
305297
vm.runInContext( remaining, scope );
306298
}
307299
} catch ( err ) {
308-
// Calculate the line number for the example that caused the error
300+
// Calculate the line number for the example that caused the error...
309301
exampleStartIdx = sourceCode.text.indexOf( comments[ commentIdx ] );
310302
exampleIdx = comments[ commentIdx ].indexOf( code );
311303
line = countLines( sourceCode.text.substring( 0, exampleStartIdx + exampleIdx ) ) + 1;
@@ -320,7 +312,7 @@ function processExampleCode( code, commentIdx, comments, scope, report, opts, so
320312
}
321313
};
322314

323-
// Do not report errors in TypeScript declaration files due to modules failing to load
315+
// Do not report errors in TypeScript declaration files due to modules failing to load:
324316
if ( contains( err.message, 'Cannot find module' ) ) {
325317
return;
326318
}
@@ -344,7 +336,7 @@ function main( context ) {
344336
filename = context.getFilename();
345337
options = context.options[ 0 ] || {};
346338

347-
// Only process TypeScript declaration files
339+
// Only process TypeScript declaration files:
348340
if ( !filename.endsWith( '.d.ts' ) ) {
349341
return {};
350342
}
@@ -401,7 +393,7 @@ function main( context ) {
401393
var i;
402394
var j;
403395

404-
// Clear caches to prevent memory leaks
396+
// Clear caches to prevent memory leaks:
405397
regexCache = {};
406398
lineCountCache = {};
407399

@@ -468,6 +460,7 @@ function main( context ) {
468460

469461
rule = {
470462
'meta': {
463+
'type': 'problem',
471464
'docs': {
472465
'description': 'ensure return annotations in TSDoc examples match the actual output'
473466
},

lib/node_modules/@stdlib/_tools/eslint/rules/tsdoc-doctest/test/fixtures/invalid.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,51 @@ test = {
7474
};
7575
invalid.push( test );
7676

77+
test = {
78+
'code': [
79+
'/**',
80+
'* Abbreviated Int32Vector example (invalid).',
81+
'*',
82+
'* @example',
83+
"* var numel = require( '@stdlib/ndarray/numel' );",
84+
'*',
85+
'* var arr = new Int32Vector();',
86+
'* // returns <ndarray>',
87+
'*',
88+
'* var len = numel( arr );',
89+
'* // returns 1',
90+
'*/',
91+
'declare var ctor: Int32Vector;',
92+
'',
93+
'export = ctor;'
94+
].join( '\n' ),
95+
'filename': join( ROOT_DIR, 'lib/node_modules/@stdlib/ndarray/vector/int32/docs/types/index.d.ts' ),
96+
'output': [
97+
'/**',
98+
'* Abbreviated Int32Vector example (invalid).',
99+
'*',
100+
'* @example',
101+
"* var numel = require( '@stdlib/ndarray/numel' );",
102+
'*',
103+
'* var arr = new Int32Vector();',
104+
'* // returns <ndarray>',
105+
'*',
106+
'* var len = numel( arr );',
107+
'* // returns 0',
108+
'*/',
109+
'declare var ctor: Int32Vector;',
110+
'',
111+
'export = ctor;'
112+
].join( '\n' ),
113+
'errors': [
114+
{
115+
'message': 'Displayed return value is `1`, but expected `0` instead',
116+
'type': null
117+
}
118+
]
119+
};
120+
invalid.push( test );
121+
77122
test = {
78123
'code': [
79124
'/**',

lib/node_modules/@stdlib/_tools/eslint/rules/tsdoc-doctest/test/fixtures/valid.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,28 @@ test = {
156156
};
157157
valid.push( test );
158158

159+
test = {
160+
'code': [
161+
'/**',
162+
'* Abbreviated Int32Vector example.',
163+
'*',
164+
'* @example',
165+
"* var numel = require( '@stdlib/ndarray/numel' );",
166+
'*',
167+
'* var arr = new Int32Vector();',
168+
'* // returns <ndarray>',
169+
'*',
170+
'* var len = numel( arr );',
171+
'* // returns 0',
172+
'*/',
173+
'declare var ctor: Int32Vector;',
174+
'',
175+
'export = ctor;'
176+
].join( '\n' ),
177+
'filename': join( ROOT_DIR, 'lib/node_modules/@stdlib/ndarray/vector/int32/docs/types/index.d.ts' )
178+
};
179+
valid.push( test );
180+
159181

160182
// EXPORTS //
161183

0 commit comments

Comments
 (0)