From 160b526c0beb23a29882630dfff4b2cc40e9dacb Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Mon, 23 Mar 2026 00:41:03 -0500 Subject: [PATCH 1/3] feat: add `jsdoc-example-eslint` ESLint rule Add a new custom ESLint rule that extracts JavaScript code from JSDoc `@example` blocks and lints it using ESLint's synchronous `Linter` API. The rule is a pure mechanism: it accepts a `rules` object and lints example code with those rules, reporting violations mapped back to the correct source file lines. The rule is configured in `.eslintrc.overrides.js`, where the examples ESLint config is loaded (avoiding a circular dependency with `stdlib.js`), filtered to exclude plugin rules and rules incompatible with code snippets, and passed to the rule. --- 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: passed - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: passed - 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: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- etc/eslint/.eslintrc.overrides.js | 66 +++++- etc/eslint/rules/stdlib.js | 32 +++ .../rules/jsdoc-example-eslint/README.md | 152 ++++++++++++++ .../jsdoc-example-eslint/examples/index.js | 62 ++++++ .../rules/jsdoc-example-eslint/lib/index.js | 43 ++++ .../rules/jsdoc-example-eslint/lib/main.js | 178 ++++++++++++++++ .../rules/jsdoc-example-eslint/package.json | 66 ++++++ .../test/fixtures/invalid.js | 85 ++++++++ .../test/fixtures/valid.js | 191 ++++++++++++++++++ .../rules/jsdoc-example-eslint/test/test.js | 70 +++++++ .../@stdlib/_tools/eslint/rules/lib/index.js | 9 + 11 files changed, 953 insertions(+), 1 deletion(-) create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/package.json create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/test.js diff --git a/etc/eslint/.eslintrc.overrides.js b/etc/eslint/.eslintrc.overrides.js index 8b603bf87967..2ac35dfe0dff 100644 --- a/etc/eslint/.eslintrc.overrides.js +++ b/etc/eslint/.eslintrc.overrides.js @@ -16,15 +16,72 @@ * limitations under the License. */ +/* eslint-disable vars-on-top, stdlib/empty-line-before-comment */ + 'use strict'; // MODULES // // FIXME: remove the next line and uncomment the subsequent line once all remark JSDoc ESLint rules are completed var copy = require( './../../lib/node_modules/@stdlib/utils/copy' ); - // var copy = require( './utils/copy.js' ); +var objectKeys = require( './../../lib/node_modules/@stdlib/utils/keys' ); var defaults = require( './.eslintrc.js' ); +var examplesConfig = require( './.eslintrc.examples.js' ); + + +// VARIABLES // + +/** +* List of rules to exclude when linting JSDoc code snippets because they are incompatible with code fragments extracted from JSDoc comments. +* +* @private +* @type {Array} +*/ +var JSDOC_SNIPPET_EXCLUDE = [ + 'no-undef', + 'no-unused-vars', + 'strict', + 'no-var', + 'eol-last', + 'indent', + 'no-restricted-syntax' +]; + + +// FUNCTIONS // + +/** +* Extracts built-in ESLint rules from a configuration object, filtering out plugin rules and rules listed in an exclusion list. +* +* @private +* @param {Object} config - ESLint configuration object +* @param {Array} exclude - rule names to exclude +* @returns {Object} filtered rules object +*/ +function extractSnippetRules( config, exclude ) { + var rules; + var keys; + var key; + var i; + + rules = {}; + keys = objectKeys( config.rules ); + for ( i = 0; i < keys.length; i++ ) { + key = keys[ i ]; + + // Skip plugin rules (e.g., "stdlib/foo", "node/bar") since the Linter instance does not have them registered: + if ( key.indexOf( '/' ) !== -1 ) { + continue; + } + // Skip rules explicitly excluded as incompatible with code snippets: + if ( exclude.indexOf( key ) !== -1 ) { + continue; + } + rules[ key ] = config.rules[ key ]; + } + return rules; +} // MAIN // @@ -45,6 +102,13 @@ var eslint = copy( defaults ); */ eslint.overrides = require( './overrides' ); +/** +* Configure JSDoc example linting using the examples ESLint config, with rules filtered for use on JSDoc code snippets. +*/ +eslint.rules[ 'stdlib/jsdoc-example-eslint' ] = [ 'warn', { + 'rules': extractSnippetRules( examplesConfig, JSDOC_SNIPPET_EXCLUDE ) +}]; + // EXPORTS // diff --git a/etc/eslint/rules/stdlib.js b/etc/eslint/rules/stdlib.js index cfc51e8e65b8..05caeda6bf91 100644 --- a/etc/eslint/rules/stdlib.js +++ b/etc/eslint/rules/stdlib.js @@ -867,6 +867,38 @@ rules[ 'stdlib/jsdoc-emphasis-marker' ] = [ 'error', '_' ]; */ rules[ 'stdlib/jsdoc-empty-line-before-example' ] = 'error'; +/** +* Lint JavaScript code in JSDoc example blocks using ESLint. +* +* @name jsdoc-example-eslint +* @memberof rules +* @type {string} +* @default 'warn' +* +* @example +* // Bad... +* +* /** +* * Squares a number. +* * +* * @example +* * var y = square( 3.0 ) +* *\/ +* function square( x ) {} +* +* @example +* // Good... +* +* /** +* * Squares a number. +* * +* * @example +* * var y = square( 3.0 ); +* *\/ +* function square( x ) {} +*/ +rules[ 'stdlib/jsdoc-example-eslint' ] = 'off'; + /** * Enforce empty lines between requires and code in JSDoc examples. * diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md new file mode 100644 index 000000000000..8e0caed7c3c7 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md @@ -0,0 +1,152 @@ + + +# jsdoc-example-eslint + +> [ESLint rule][eslint-rules] to lint JavaScript code in JSDoc example blocks. + +
+ +
+ + + +
+ +## Usage + +```javascript +var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-example-eslint' ); +``` + +#### rule + +[ESLint rule][eslint-rules] to lint JavaScript code in JSDoc example blocks. The rule extracts code from `@example` tags in JSDoc comments, lints it using a provided set of ESLint rules, and reports violations mapped back to the original source lines. + +The rule accepts an options object with a `rules` property specifying which ESLint rules to apply to the extracted example code. + +**Bad** (missing semicolon): + + + +```javascript +/** +* Squares a number. +* +* @example +* var y = square( 3.0 ) +*/ +function square( x ) { + return x * x; +} +``` + +**Good**: + + + +```javascript +/** +* Squares a number. +* +* @example +* var y = square( 3.0 ); +*/ +function square( x ) { + return x * x; +} +``` + +
+ + + +
+ +## Examples + + + +```javascript +var Linter = require( 'eslint' ).Linter; +var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-example-eslint' ); + +var linter = new Linter(); +var result; +var code; + +code = [ + '/**', + '* Squares a number.', + '*', + '* @example', + '* var y = square( 3.0 )', + '* // returns 9.0', + '*/', + 'function square( x ) {', + '\treturn x * x;', + '}' +].join( '\n' ); + +linter.defineRule( 'jsdoc-example-eslint', rule ); + +result = linter.verify( code, { + 'rules': { + 'jsdoc-example-eslint': [ 'error', { + 'rules': { + 'semi': 'error' + } + }] + } +}); +console.log( result ); +/* => + [ + { + 'ruleId': 'jsdoc-example-eslint', + 'severity': 2, + 'message': 'Missing semicolon. (semi)', + ... + } + ] +*/ +``` + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js new file mode 100644 index 000000000000..3cd7996bd321 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js @@ -0,0 +1,62 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var Linter = require( 'eslint' ).Linter; +var rule = require( './../lib' ); + +var linter = new Linter(); +var result; +var code; + +code = [ + '/**', + '* Squares a number.', + '*', + '* @example', + '* var y = square( 3.0 )', + '* // returns 9.0', + '*/', + 'function square( x ) {', + '\treturn x * x;', + '}' +].join( '\n' ); + +linter.defineRule( 'jsdoc-example-eslint', rule ); + +result = linter.verify( code, { + 'rules': { + 'jsdoc-example-eslint': [ 'error', { + 'rules': { + 'semi': 'error' + } + }] + } +}); +console.log( result ); +/* => + [ + { + 'ruleId': 'jsdoc-example-eslint', + 'severity': 2, + 'message': 'Missing semicolon. (semi)', + ... + } + ] +*/ diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js new file mode 100644 index 000000000000..0446247c07af --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js @@ -0,0 +1,43 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* ESLint rule to lint JavaScript code in JSDoc example blocks. +* +* @module @stdlib/_tools/eslint/rules/jsdoc-example-eslint +* +* @example +* var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-example-eslint' ); +* +* var config = { +* 'rules': { +* 'stdlib/jsdoc-example-eslint': 'warn' +* } +* }; +*/ + +// MODULES // + +var rule = require( './main.js' ); + + +// EXPORTS // + +module.exports = rule; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/main.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/main.js new file mode 100644 index 000000000000..de0b06f67a97 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/main.js @@ -0,0 +1,178 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var Linter = require( 'eslint' ).Linter; +var parseJSDoc = require( 'doctrine' ).parse; +var isObject = require( '@stdlib/assert/is-object' ); +var objectKeys = require( '@stdlib/utils/keys' ); +var contains = require( '@stdlib/assert/contains' ); +var replace = require( '@stdlib/string/replace' ); +var format = require( '@stdlib/string/format' ); +var findJSDoc = require( '@stdlib/_tools/eslint/utils/find-jsdoc' ); + + +// VARIABLES // + +var RE_MODULE_TAG = /\* @module[^\n]*/g; +var DOPTS = { + 'sloppy': true, + 'unwrap': true, + 'tags': [ 'example' ], + 'lineNumbers': true +}; +var linter = new Linter(); +var rule; + + +// FUNCTIONS // + +/** +* Rule for linting JavaScript code in JSDoc example blocks using ESLint. +* +* @param {Object} context - ESLint context +* @returns {Object} validators +*/ +function main( context ) { + var source; + var config; + var opts; + + source = context.sourceCode; + + // Build linter configuration from the provided rules: + opts = context.options[ 0 ]; + if ( !isObject( opts ) || !isObject( opts.rules ) || objectKeys( opts.rules ).length === 0 ) { + return {}; + } + config = { + 'rules': opts.rules + }; + + /** + * Checks JavaScript code in JSDoc example blocks for lint violations. + * + * @private + * @param {ASTNode} node - AST node + */ + function validate( node ) { + var wrapped; + var jsdoc; + var tags; + var tag; + var ast; + var j; + + if ( node.parent.type !== 'Program' ) { + return; + } + jsdoc = findJSDoc( source, node ); + if ( isObject( jsdoc ) ) { + if ( contains( jsdoc.value, '@module' ) ) { + wrapped = '/**' + jsdoc.value + '*/'; + wrapped = replace( wrapped, RE_MODULE_TAG, '*' ); + ast = parseJSDoc( wrapped, DOPTS ); + } else { + ast = parseJSDoc( jsdoc.value, DOPTS ); + } + tags = ast.tags; + for ( j = 0; j < tags.length; j++ ) { + tag = tags[ j ]; + if ( tag.title === 'example' && tag.description ) { + lintExample( jsdoc, tag ); + } + } + } + } + + /** + * Lints an example code block and reports violations. + * + * @private + * @param {Object} jsdoc - JSDoc comment token + * @param {Object} tag - doctrine tag object with description and lineNumber + */ + function lintExample( jsdoc, tag ) { + var violations; + var sourceLine; + var msg; + var v; + var i; + + violations = linter.verify( tag.description, config ); + for ( i = 0; i < violations.length; i++ ) { + v = violations[ i ]; + + // Skip fatal parse errors (syntax errors in example code): + if ( v.fatal ) { + continue; + } + sourceLine = jsdoc.loc.start.line + tag.lineNumber + v.line; + msg = format( '%s (%s)', v.message, v.ruleId ); + context.report({ + 'node': null, + 'loc': { + 'start': { + 'line': sourceLine, + 'column': 0 + } + }, + 'message': msg + }); + } + } + + return { + 'FunctionDeclaration': validate, + 'FunctionExpression:exit': validate, + 'VariableDeclaration': validate, + 'ExpressionStatement': validate + }; +} + + +// MAIN // + +rule = { + 'meta': { + 'type': 'suggestion', + 'docs': { + 'description': 'lint JavaScript code in JSDoc example blocks using ESLint' + }, + 'schema': [ + { + 'type': 'object', + 'properties': { + 'rules': { + 'type': 'object' + } + }, + 'additionalProperties': false + } + ] + }, + 'create': main +}; + + +// EXPORTS // + +module.exports = rule; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/package.json b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/package.json new file mode 100644 index 000000000000..5a1dd26a1f90 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/package.json @@ -0,0 +1,66 @@ +{ + "name": "@stdlib/_tools/eslint/rules/jsdoc-example-eslint", + "version": "0.0.0", + "description": "ESLint rule to lint JavaScript code in JSDoc example blocks.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "bin": {}, + "main": "./lib", + "directories": { + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "tools", + "tool", + "eslint", + "lint", + "custom", + "rules", + "rule", + "plugin", + "jsdoc", + "examples", + "example", + "style", + "code", + "quality" + ] +} diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js new file mode 100644 index 000000000000..d65f2ba3b42f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js @@ -0,0 +1,85 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +var invalid = []; +var test; + +// Missing semicolon: +test = { + 'code': [ + '/**', + '* Squares a number.', + '*', + '* @example', + '* var y = square( 3.0 )', + '* // returns 9.0', + '*/', + 'function square( x ) {', + '\treturn x * x;', + '}' + ].join( '\n' ), + 'options': [ + { + 'rules': { + 'semi': 'error' + } + } + ], + 'errors': [ + { + 'message': 'Missing semicolon. (semi)' + } + ] +}; +invalid.push( test ); + +// Use of == instead of ===: +test = { + 'code': [ + '/**', + '* Checks equality.', + '*', + '* @example', + '* var bool = ( 1 == \'1\' );', + '* // returns true', + '*/', + 'function isEqual( a, b ) {', + '\treturn a === b;', + '}' + ].join( '\n' ), + 'options': [ + { + 'rules': { + 'eqeqeq': 'error' + } + } + ], + 'errors': [ + { + 'message': 'Expected \'===\' and instead saw \'==\'. (eqeqeq)' + } + ] +}; +invalid.push( test ); + + +// EXPORTS // + +module.exports = invalid; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js new file mode 100644 index 000000000000..a8d0621109dc --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js @@ -0,0 +1,191 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// VARIABLES // + +var OPTS = [ + { + 'rules': { + 'semi': 'error', + 'eqeqeq': 'error' + } + } +]; + +var valid = []; +var test; + +// Clean example with proper style: +test = { + 'code': [ + '/**', + '* Squares a number.', + '*', + '* @example', + '* var y = square( 3.0 );', + '* // returns 9.0', + '*/', + 'function square( x ) {', + '\treturn x * x;', + '}' + ].join( '\n' ), + 'options': OPTS +}; +valid.push( test ); + +// JSDoc without @example tag: +test = { + 'code': [ + '/**', + '* Squares a number.', + '*', + '* @param {number} x - input value', + '* @returns {number} squared value', + '*/', + 'function square( x ) {', + '\treturn x * x;', + '}' + ].join( '\n' ), + 'options': OPTS +}; +valid.push( test ); + +// Multiple clean examples: +test = { + 'code': [ + '/**', + '* Computes an absolute value.', + '*', + '* @example', + '* var v = abs( -1.0 );', + '* // returns 1.0', + '*', + '* @example', + '* var v = abs( 2.0 );', + '* // returns 2.0', + '*/', + 'function abs( x ) {', + '\tif ( x < 0 ) {', + '\t\treturn -x;', + '\t}', + '\treturn x;', + '}' + ].join( '\n' ), + 'options': OPTS +}; +valid.push( test ); + +// Example with require statement: +test = { + 'code': [ + '/**', + '* Computes an absolute value.', + '*', + '* @module @stdlib/math/base/special/abs', + '*', + '* @example', + '* var abs = require( \'@stdlib/math/base/special/abs\' );', + '*', + '* var v = abs( -1.0 );', + '* // returns 1.0', + '*/', + 'var main = require( \'./main.js\' );', + '', + 'module.exports = main;' + ].join( '\n' ), + 'options': OPTS +}; +valid.push( test ); + +// Example with console.log: +test = { + 'code': [ + '/**', + '* Squares a number.', + '*', + '* @example', + '* var y = square( 3.0 );', + '* console.log( y );', + '*/', + 'function square( x ) {', + '\treturn x * x;', + '}' + ].join( '\n' ), + 'options': OPTS +}; +valid.push( test ); + +// Variable declaration with example: +test = { + 'code': [ + '/**', + '* Maximum value.', + '*', + '* @example', + '* var v = MAX;', + '* // returns 100', + '*/', + 'var MAX = 100;' + ].join( '\n' ), + 'options': OPTS +}; +valid.push( test ); + +// Example with if statement and curly braces: +test = { + 'code': [ + '/**', + '* Checks if positive.', + '*', + '* @example', + '* var bool = isPositive( 3.0 );', + '* // returns true', + '*', + '* bool = isPositive( -1.0 );', + '* // returns false', + '*/', + 'function isPositive( x ) {', + '\treturn x > 0;', + '}' + ].join( '\n' ), + 'options': OPTS +}; +valid.push( test ); + +// No options provided (rule does nothing): +test = { + 'code': [ + '/**', + '* Squares a number.', + '*', + '* @example', + '* var y = square( 3.0 )', + '*/', + 'function square( x ) {', + '\treturn x * x;', + '}' + ].join( '\n' ) +}; +valid.push( test ); + + +// EXPORTS // + +module.exports = valid; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/test.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/test.js new file mode 100644 index 000000000000..db2ad3748540 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/test.js @@ -0,0 +1,70 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var RuleTester = require( 'eslint' ).RuleTester; +var rule = require( './../lib' ); + + +// FIXTURES // + +var valid = require( './fixtures/valid.js' ); +var invalid = require( './fixtures/invalid.js' ); + + +// TESTS // + +tape( 'main export is an object', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof rule, 'object', 'main export is an object' ); + t.end(); +}); + +tape( 'the rule positively validates JSDoc examples with proper style', function test( t ) { + var tester = new RuleTester(); + + try { + tester.run( 'jsdoc-example-eslint', rule, { + 'valid': valid, + 'invalid': [] + }); + t.pass( 'passed without errors' ); + } catch ( err ) { + t.fail( 'encountered an error: ' + err.message ); + } + t.end(); +}); + +tape( 'the rule negatively validates JSDoc examples with style violations', function test( t ) { + var tester = new RuleTester(); + + try { + tester.run( 'jsdoc-example-eslint', rule, { + 'valid': [], + 'invalid': invalid + }); + t.pass( 'passed without errors' ); + } catch ( err ) { + t.fail( 'encountered an error: ' + err.message ); + } + t.end(); +}); diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js index fcf6e671c4c9..1375e30b2ffa 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js @@ -216,6 +216,15 @@ setReadOnly( rules, 'jsdoc-emphasis-marker', require( '@stdlib/_tools/eslint/rul */ setReadOnly( rules, 'jsdoc-empty-line-before-example', require( '@stdlib/_tools/eslint/rules/jsdoc-empty-line-before-example' ) ); +/** +* @name jsdoc-example-eslint +* @memberof rules +* @readonly +* @type {Function} +* @see {@link module:@stdlib/_tools/eslint/rules/jsdoc-example-eslint} +*/ +setReadOnly( rules, 'jsdoc-example-eslint', require( '@stdlib/_tools/eslint/rules/jsdoc-example-eslint' ) ); + /** * @name jsdoc-example-require-spacing * @memberof rules From f65408ea4b39424dd2f055fe356ecd79600aee9d Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 26 Apr 2026 23:27:36 -0500 Subject: [PATCH 2/3] style: align `jsdoc-example-eslint` package with peer rule conventions Rename the local require alias in `lib/index.js` from `rule` to `main` to match the convention used across all peer ESLint rule packages (e.g., `jsdoc-empty-line-before-example`, `jsdoc-doctest-decimal-point`), and replace the dangling `var config` block in the `@example` JSDoc with a `console.log( main );` per the peer pattern. Drop the spurious `// VARIABLES //` section marker from the `valid.js` test fixture; peer fixtures and this package's own `invalid.js` do not use section markers in fixture files. --- 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: na - 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: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../eslint/rules/jsdoc-example-eslint/lib/index.js | 12 ++++-------- .../jsdoc-example-eslint/test/fixtures/valid.js | 3 --- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js index 0446247c07af..89d3ec70c2f1 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/lib/index.js @@ -24,20 +24,16 @@ * @module @stdlib/_tools/eslint/rules/jsdoc-example-eslint * * @example -* var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-example-eslint' ); +* var main = require( '@stdlib/_tools/eslint/rules/jsdoc-example-eslint' ); * -* var config = { -* 'rules': { -* 'stdlib/jsdoc-example-eslint': 'warn' -* } -* }; +* console.log( main ); */ // MODULES // -var rule = require( './main.js' ); +var main = require( './main.js' ); // EXPORTS // -module.exports = rule; +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js index a8d0621109dc..94d9634cb9c2 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js @@ -18,8 +18,6 @@ 'use strict'; -// VARIABLES // - var OPTS = [ { 'rules': { @@ -28,7 +26,6 @@ var OPTS = [ } } ]; - var valid = []; var test; From 6b854eebc4c02c25398e020b96f5c5648416c793 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 28 Apr 2026 00:06:39 -0500 Subject: [PATCH 3/3] style: address review feedback on `jsdoc-example-eslint` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline `var test = {…}` for the first test in `invalid.js` / `valid.js` and drop the upfront `var test;` declaration. Move the `var code` / `var result` declarations to the top of `examples/index.js` and the README example, removing the empty up-front declarations. Rename the inaccurate `// Example with if statement and curly braces:` test comment in `valid.js` to reflect what the JSDoc actually exercises. --- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: skipped - task: lint_editorconfig status: skipped - task: lint_markdown status: skipped - task: lint_package_json status: skipped - task: lint_repl_help status: skipped - task: lint_javascript_src status: skipped - task: lint_javascript_cli status: skipped - task: lint_javascript_examples status: skipped - task: lint_javascript_tests status: skipped - task: lint_javascript_benchmarks status: skipped - 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: skipped - task: lint_typescript_tests status: skipped - task: lint_license_headers status: skipped --- --- .../_tools/eslint/rules/jsdoc-example-eslint/README.md | 7 ++----- .../eslint/rules/jsdoc-example-eslint/examples/index.js | 7 ++----- .../rules/jsdoc-example-eslint/test/fixtures/invalid.js | 3 +-- .../rules/jsdoc-example-eslint/test/fixtures/valid.js | 5 ++--- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md index 8e0caed7c3c7..98483d6676f6 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/README.md @@ -89,10 +89,7 @@ var Linter = require( 'eslint' ).Linter; var rule = require( '@stdlib/_tools/eslint/rules/jsdoc-example-eslint' ); var linter = new Linter(); -var result; -var code; - -code = [ +var code = [ '/**', '* Squares a number.', '*', @@ -107,7 +104,7 @@ code = [ linter.defineRule( 'jsdoc-example-eslint', rule ); -result = linter.verify( code, { +var result = linter.verify( code, { 'rules': { 'jsdoc-example-eslint': [ 'error', { 'rules': { diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js index 3cd7996bd321..1642a86413a7 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/examples/index.js @@ -22,10 +22,7 @@ var Linter = require( 'eslint' ).Linter; var rule = require( './../lib' ); var linter = new Linter(); -var result; -var code; - -code = [ +var code = [ '/**', '* Squares a number.', '*', @@ -40,7 +37,7 @@ code = [ linter.defineRule( 'jsdoc-example-eslint', rule ); -result = linter.verify( code, { +var result = linter.verify( code, { 'rules': { 'jsdoc-example-eslint': [ 'error', { 'rules': { diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js index d65f2ba3b42f..8f08263051dd 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/invalid.js @@ -19,10 +19,9 @@ 'use strict'; var invalid = []; -var test; // Missing semicolon: -test = { +var test = { 'code': [ '/**', '* Squares a number.', diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js index 94d9634cb9c2..16b12598bf73 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/jsdoc-example-eslint/test/fixtures/valid.js @@ -27,10 +27,9 @@ var OPTS = [ } ]; var valid = []; -var test; // Clean example with proper style: -test = { +var test = { 'code': [ '/**', '* Squares a number.', @@ -145,7 +144,7 @@ test = { }; valid.push( test ); -// Example with if statement and curly braces: +// Example with multiple statements across lines: test = { 'code': [ '/**',