feat: add jsdoc-example-eslint ESLint rule#11101
feat: add jsdoc-example-eslint ESLint rule#11101Planeshifter wants to merge 3 commits intodevelopfrom
jsdoc-example-eslint ESLint rule#11101Conversation
|
No dependency changes detected. Learn more about Socket for GitHub. 👍 No dependency changes detected in pull request |
8fce0dc to
33e0f6e
Compare
33e0f6e to
9dbe8eb
Compare
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
---
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
---
There was a problem hiding this comment.
I'm at a loss for explaining why the overrides file is being modified in this PR.
There was a problem hiding this comment.
I believe that the actual approach which should be taken is to 1) create an .eslintrc.jsdoc.js file which uses the .eslintrc.examples.js file has the source of the defaults and 2) enables the rule in the stdlib.js file by loading in the file created in step (1).
As it is, this PR seems confused and is just creating a configuration mess.
There was a problem hiding this comment.
It's workaround to avoid a circular dependency as was detailed in the PR description:
The rule is configured in .eslintrc.overrides.js (rather than stdlib.js) to avoid a circular dependency: stdlib.js → .eslintrc.examples.js → .eslintrc.js → rules/index.js → stdlib.js. The overrides file loads after the base config resolves, so it can safely require the examples config.
Per Claude,
What actually happens at runtime under that cycle: when stdlib.js is being evaluated as part of resolving .eslintrc.js,
requiring .eslintrc.jsdoc.js reaches back to .eslintrc.js mid-evaluation. Node returns the partial module.exports (empty at
that point). .eslintrc.examples.js does copy(defaults) against {}, sets a handful of its own rules, returns a near-empty
config. .eslintrc.jsdoc.js filters that near-empty rules map and the jsdoc-example-eslint rule ends up with effectively no
rules to apply. Silent breakage — the linter loads, but the JSDoc snippet rule does nothing.
I ran into this problem during development of this rule. To address your desire, Claude suggests
split config into .eslintrc.jsdoc.js, but keep wiring in .eslintrc.overrides.js (one-liner:
eslint.rules['stdlib/jsdoc-example-eslint'] = ['warn', require('./.eslintrc.jsdoc.js')];). Cycle stays broken because the
overrides file resolves AFTER the base. Addresses his "config mess" complaint cleanly: the heavy lifting lives in a
dedicated, named file consistent with .eslintrc.examples.js/.eslintrc.tests.js/.eslintrc.benchmarks.js; the overrides file
just hooks it up.
Thoughts?
| 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: |
There was a problem hiding this comment.
I don't understand this comment.
| * @private | ||
| * @type {Array} | ||
| */ | ||
| var JSDOC_SNIPPET_EXCLUDE = [ |
There was a problem hiding this comment.
Refer to other files such as .eslintrc.examples for how rules are overridden. In short, be consistent with what we already have.
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
---
f807776 to
6b854ee
Compare
Resolves stdlib-js/metr-issue-tracker#57.
Description
This pull request:
jsdoc-example-eslintthat extracts JavaScript code from JSDoc@exampleblocks and lints it using ESLint's synchronousLinterAPI.eslintrc.overrides.jsusing the existing examples ESLint config, keeping JSDoc example linting rules in sync with standalone example file linting--external eslintto the Browserify build for the ESLint plugin to avoid bundling ESLint inside itselfRelated Issues
Questions
No.
Other
The rule is a pure mechanism: it accepts a
rulesobject and lints example code with those rules, reporting violations mapped back to the correct source file lines. It has no built-in opinions about which rules to apply.The rule is configured in
.eslintrc.overrides.js(rather thanstdlib.js) to avoid a circular dependency:stdlib.js→.eslintrc.examples.js→.eslintrc.js→rules/index.js→stdlib.js. The overrides file loads after the base config resolves, so it can safely require the examples config.Rules from the examples config are filtered to exclude plugin rules (which the internal
Linterinstance doesn't have registered) and rules incompatible with code snippets (no-undef,no-unused-vars,strict,no-var,eol-last,indent,no-restricted-syntax).Checklist
AI Assistance
If you answered "yes" above, how did you use AI assistance?
Disclosure
This PR was written primarily by Claude Code following my instructions.
@stdlib-js/reviewers