From d3fe5edbadf86b1fef10322dc47013100a017790 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 15 Mar 2026 13:59:59 -0500 Subject: [PATCH] build: add JSON and YAML linting via ESLint Ref: https://github.com/stdlib-js/metr-issue-tracker/issues/55 - Add `eslint-plugin-jsonc` and `eslint-plugin-yml` as devDependencies - Create `etc/eslint/rules/json.js` with 10 rules enforcing JSON spec compliance (indent, no-dupe-keys, comma-dangle, etc.) - Create `etc/eslint/rules/yaml.js` with 12 rules for YAML formatting and best practices (indent, block-mapping, quotes as warn, etc.) - Add 5 config blocks to `eslint.config.cjs`: base JSON, cli_opts.json tab override, package.json key ordering (canonical stdlib order), base YAML, and GitHub Actions workflow key ordering - Un-ignore `.github/` and `.codecov.yml` in global ESLint ignores - Create custom `stdlib/yaml-license-header` rule enforcing Apache-2.0 license header at the start of YAML files - Register the new rule in the stdlib ESLint plugin - Add `.cjs` to the filename linter's supported extensions allowlist - Add `lint-json` and `lint-yaml` Make targets with `--cache` - Wire new targets into the top-level `lint` prerequisite --- 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: passed - 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.config.cjs | 104 ++++++++++++ etc/eslint/rules/json.js | 121 ++++++++++++++ etc/eslint/rules/yaml.js | 144 +++++++++++++++++ .../@stdlib/_tools/eslint/rules/lib/index.js | 9 ++ .../rules/yaml-license-header/lib/index.js | 39 +++++ .../rules/yaml-license-header/lib/main.js | 152 ++++++++++++++++++ .../rules/yaml-license-header/package.json | 62 +++++++ .../test/fixtures/invalid.yml.txt | 2 + .../test/fixtures/valid.yml | 19 +++ .../rules/yaml-license-header/test/test.js | 43 +++++ .../_tools/lint/filenames/lib/extensions.json | 1 + package.json | 2 + tools/make/lib/lint/Makefile | 4 +- tools/make/lib/lint/json/eslint.mk | 64 ++++++++ tools/make/lib/lint/yaml/eslint.mk | 64 ++++++++ 15 files changed, 829 insertions(+), 1 deletion(-) create mode 100644 etc/eslint/rules/json.js create mode 100644 etc/eslint/rules/yaml.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/index.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/package.json create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/invalid.yml.txt create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/valid.yml create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/test.js create mode 100644 tools/make/lib/lint/json/eslint.mk create mode 100644 tools/make/lib/lint/yaml/eslint.mk diff --git a/eslint.config.cjs b/eslint.config.cjs index ba7ee5345b12..dd50e744536f 100644 --- a/eslint.config.cjs +++ b/eslint.config.cjs @@ -30,9 +30,15 @@ var pluginCspell = require( '@cspell/eslint-plugin' ); var pluginJsdoc = require( 'eslint-plugin-jsdoc' ); var pluginImport = require( 'eslint-plugin-import' ); var pluginExpectType = require( 'eslint-plugin-expect-type' ); +var pluginJsonc = require( 'eslint-plugin-jsonc' ); +var jsoncParser = require( 'jsonc-eslint-parser' ); +var pluginYml = require( 'eslint-plugin-yml' ); +var yamlParser = require( 'yaml-eslint-parser' ); var stdlibPlugin = require( './lib/node_modules/@stdlib/_tools/eslint/rules/scripts/plugin.js' ); var allRules = require( './etc/eslint/rules' ); var tsRules = require( './etc/eslint/rules/typescript.js' ); +var jsonRules = require( './etc/eslint/rules/json.js' ); +var yamlRules = require( './etc/eslint/rules/yaml.js' ); // VARIABLES // @@ -101,6 +107,8 @@ module.exports = [ '**/reports/', 'dist/', '.git*', + '!.github/', + '!.codecov.yml', // ESLint ignores **/node_modules/ by default. stdlib source // lives in lib/node_modules/, so un-ignore it: @@ -217,5 +225,101 @@ module.exports = [ 'rules': { 'jsdoc/require-jsdoc': 'off' } + }, + + // Base JSON: + { + 'files': [ '**/*.json' ], + 'languageOptions': { + 'parser': jsoncParser + }, + 'plugins': { + 'jsonc': pluginJsonc + }, + 'rules': jsonRules + }, + + // cli_opts.json override (tab-indented per .editorconfig): + { + 'files': [ '**/cli_opts.json' ], + 'rules': { + 'jsonc/indent': [ 'error', 'tab' ] + } + }, + + // package.json key ordering: + { + 'files': [ '**/package.json' ], + 'rules': { + 'jsonc/sort-keys': [ 'error', { + 'pathPattern': '^$', + 'order': [ + 'name', + 'private', + 'version', + 'description', + 'license', + 'licenses', + 'author', + 'maintainers', + 'contributors', + 'funding', + 'bin', + 'main', + 'exports', + 'browser', + 'unpkg', + 'gypfile', + 'directories', + 'types', + 'scripts', + 'homepage', + 'repository', + 'repositories', + 'bugs', + 'dependencies', + 'optionalDependencies', + 'devDependencies', + 'engines', + 'os', + 'keywords', + '__stdlib__' + ] + }] + } + }, + + // Base YAML: + { + 'files': [ '**/*.yml' ], + 'languageOptions': { + 'parser': yamlParser + }, + 'plugins': { + 'yml': pluginYml, + 'stdlib': stdlibPlugin + }, + 'rules': { + ...yamlRules, + 'stdlib/yaml-license-header': 'error' + } + }, + + // GitHub Actions workflow key ordering: + { + 'files': [ '.github/workflows/*.yml' ], + 'rules': { + 'yml/sort-keys': [ 'error', { + 'pathPattern': '^$', + 'order': [ + 'name', + 'on', + 'concurrency', + 'permissions', + 'env', + 'jobs' + ] + }] + } } ]; diff --git a/etc/eslint/rules/json.js b/etc/eslint/rules/json.js new file mode 100644 index 000000000000..a6bf9e3c2d92 --- /dev/null +++ b/etc/eslint/rules/json.js @@ -0,0 +1,121 @@ +/** +* @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 rules for JSON files. +* +* @namespace rules +*/ +var rules = {}; + +/** +* Enforce 2-space indentation per `.editorconfig`. +* +* @name jsonc/indent +* @memberof rules +* @type {Array} +*/ +rules[ 'jsonc/indent' ] = [ 'error', 2 ]; + +/** +* Disallow comments in JSON files. +* +* @name jsonc/no-comments +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/no-comments' ] = 'error'; + +/** +* Disallow trailing commas. +* +* @name jsonc/comma-dangle +* @memberof rules +* @type {Array} +*/ +rules[ 'jsonc/comma-dangle' ] = [ 'error', 'never' ]; + +/** +* Disallow duplicate keys. +* +* @name jsonc/no-dupe-keys +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/no-dupe-keys' ] = 'error'; + +/** +* Disallow floating decimals (e.g., `.5` or `1.`). +* +* @name jsonc/no-floating-decimal +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/no-floating-decimal' ] = 'error'; + +/** +* Disallow irregular whitespace characters. +* +* @name jsonc/no-irregular-whitespace +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/no-irregular-whitespace' ] = 'error'; + +/** +* Disallow multi-line strings. +* +* @name jsonc/no-multi-str +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/no-multi-str' ] = 'error'; + +/** +* Disallow octal literals. +* +* @name jsonc/no-octal +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/no-octal' ] = 'error'; + +/** +* Disallow useless escape characters. +* +* @name jsonc/no-useless-escape +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/no-useless-escape' ] = 'error'; + +/** +* Enforce valid JSON number literals. +* +* @name jsonc/valid-json-number +* @memberof rules +* @type {string} +*/ +rules[ 'jsonc/valid-json-number' ] = 'error'; + + +// EXPORTS // + +module.exports = rules; diff --git a/etc/eslint/rules/yaml.js b/etc/eslint/rules/yaml.js new file mode 100644 index 000000000000..e4aa6dd0e219 --- /dev/null +++ b/etc/eslint/rules/yaml.js @@ -0,0 +1,144 @@ +/** +* @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 rules for YAML files. +* +* @namespace rules +*/ +var rules = {}; + +/** +* Enforce 2-space indentation per `.editorconfig`. +* +* @name yml/indent +* @memberof rules +* @type {Array} +*/ +rules[ 'yml/indent' ] = [ 'error', 2 ]; + +/** +* Disallow empty YAML documents. +* +* @name yml/no-empty-document +* @memberof rules +* @type {string} +*/ +rules[ 'yml/no-empty-document' ] = 'error'; + +/** +* Disallow empty keys. +* +* @name yml/no-empty-key +* @memberof rules +* @type {string} +*/ +rules[ 'yml/no-empty-key' ] = 'error'; + +/** +* Disallow empty mapping values. +* +* @name yml/no-empty-mapping-value +* @memberof rules +* @type {string} +*/ +rules[ 'yml/no-empty-mapping-value' ] = 'error'; + +/** +* Disallow empty sequence entries. +* +* @name yml/no-empty-sequence-entry +* @memberof rules +* @type {string} +*/ +rules[ 'yml/no-empty-sequence-entry' ] = 'error'; + +/** +* Disallow irregular whitespace characters. +* +* @name yml/no-irregular-whitespace +* @memberof rules +* @type {string} +*/ +rules[ 'yml/no-irregular-whitespace' ] = 'error'; + +/** +* Disallow tab indentation. +* +* @name yml/no-tab-indent +* @memberof rules +* @type {string} +*/ +rules[ 'yml/no-tab-indent' ] = 'error'; + +/** +* Require string keys. +* +* @name yml/require-string-key +* @memberof rules +* @type {string} +*/ +rules[ 'yml/require-string-key' ] = 'error'; + +/** +* Enforce block-style mappings. +* +* @name yml/block-mapping +* @memberof rules +* @type {string} +*/ +rules[ 'yml/block-mapping' ] = 'error'; + +/** +* Enforce block-style sequences. +* +* @name yml/block-sequence +* @memberof rules +* @type {string} +*/ +rules[ 'yml/block-sequence' ] = 'error'; + +/** +* Disallow multiple consecutive empty lines. +* +* @name yml/no-multiple-empty-lines +* @memberof rules +* @type {Array} +*/ +rules[ 'yml/no-multiple-empty-lines' ] = [ 'error', { + 'max': 1 +}]; + +/** +* Prefer single quotes (warn first to assess impact on `${{ }}` expressions). +* +* @name yml/quotes +* @memberof rules +* @type {Array} +*/ +rules[ 'yml/quotes' ] = [ 'warn', { + 'prefer': 'single', + 'avoidEscape': true +}]; + + +// EXPORTS // + +module.exports = rules; 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..caff26ece0b2 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js @@ -1134,6 +1134,15 @@ setReadOnly( rules, 'uppercase-required-constants', require( '@stdlib/_tools/esl */ setReadOnly( rules, 'vars-order', require( '@stdlib/_tools/eslint/rules/vars-order' ) ); +/** +* @name yaml-license-header +* @memberof rules +* @readonly +* @type {Function} +* @see {@link module:@stdlib/_tools/eslint/rules/yaml-license-header} +*/ +setReadOnly( rules, 'yaml-license-header', require( '@stdlib/_tools/eslint/rules/yaml-license-header' ) ); + // EXPORTS // diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/index.js new file mode 100644 index 000000000000..71e35dbae03f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/index.js @@ -0,0 +1,39 @@ +/** +* @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 enforce Apache-2.0 license header at the start of YAML files. +* +* @module @stdlib/_tools/eslint/rules/yaml-license-header +* +* @example +* var rule = require( '@stdlib/_tools/eslint/rules/yaml-license-header' ); +* +* console.log( rule ); +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/main.js b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/main.js new file mode 100644 index 000000000000..a6a6931f53e0 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/lib/main.js @@ -0,0 +1,152 @@ +/** +* @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 EXPECTED_LINES = [ + '#/', + '# @license Apache-2.0', + '#', + '# Copyright (c) ', + '#', + '# 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.', + '#/' +]; + +var rule; + + +// FUNCTIONS // + +/** +* Rule for validating that YAML files start with the Apache-2.0 license header. +* +* @param {Object} context - ESLint context +* @returns {Object} validators +*/ +function main( context ) { + /** + * Validates the license header at the start of the YAML file. + * + * @private + * @param {ASTNode} node - program node + */ + function validate( node ) { + var lines; + var text; + var line; + var i; + + text = context.sourceCode.getText( node ); + lines = text.split( '\n' ); + + // Must have at least as many lines as the expected header: + if ( lines.length < EXPECTED_LINES.length ) { + context.report({ + 'node': node, + 'message': 'YAML files must start with the Apache-2.0 license header.', + 'loc': { + 'start': { + 'line': 1, + 'column': 0 + }, + 'end': { + 'line': 1, + 'column': 0 + } + } + }); + return; + } + for ( i = 0; i < EXPECTED_LINES.length; i++ ) { + line = lines[ i ]; + + // Line 3 (index 3) contains the copyright year which varies: + if ( i === 3 ) { + if ( line.indexOf( EXPECTED_LINES[ i ] ) !== 0 ) { + context.report({ + 'node': node, + 'message': 'Missing or malformed copyright line in license header.', + 'loc': { + 'start': { + 'line': i + 1, + 'column': 0 + }, + 'end': { + 'line': i + 1, + 'column': line.length + } + } + }); + return; + } + } else if ( line !== EXPECTED_LINES[ i ] ) { + context.report({ + 'node': node, + 'message': 'YAML files must start with the Apache-2.0 license header. Expected "' + EXPECTED_LINES[ i ] + '" on line ' + ( i + 1 ) + '.', + 'loc': { + 'start': { + 'line': i + 1, + 'column': 0 + }, + 'end': { + 'line': i + 1, + 'column': line.length + } + } + }); + return; + } + } + } + + return { + 'Program': validate + }; +} + + +// MAIN // + +rule = { + 'meta': { + 'docs': { + 'description': 'enforce Apache-2.0 license header at the start of YAML files' + }, + 'schema': [], + 'type': 'suggestion' + }, + 'create': main +}; + + +// EXPORTS // + +module.exports = rule; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/package.json b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/package.json new file mode 100644 index 000000000000..bf827f88e5f2 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/package.json @@ -0,0 +1,62 @@ +{ + "name": "@stdlib/_tools/eslint/rules/yaml-license-header", + "version": "0.0.0", + "description": "ESLint rule to enforce Apache-2.0 license header at the start of YAML files.", + "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": { + "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", + "yaml", + "license", + "header" + ] +} diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/invalid.yml.txt b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/invalid.yml.txt new file mode 100644 index 000000000000..632cbb496193 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/invalid.yml.txt @@ -0,0 +1,2 @@ +# Missing license header +name: test diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/valid.yml b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/valid.yml new file mode 100644 index 000000000000..220a0fd872df --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/fixtures/valid.yml @@ -0,0 +1,19 @@ +#/ +# @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. +#/ + +name: test diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/test.js b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/test.js new file mode 100644 index 000000000000..673acd701970 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/yaml-license-header/test/test.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'; + +// MODULES // + +var tape = require( 'tape' ); +var rule = require( './../lib' ); + + +// 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 export has a `create` method', function test( t ) { + t.strictEqual( typeof rule.create, 'function', 'has a create method' ); + t.end(); +}); + +tape( 'the export has a `meta` property', function test( t ) { + t.strictEqual( typeof rule.meta, 'object', 'has a meta property' ); + t.end(); +}); diff --git a/lib/node_modules/@stdlib/_tools/lint/filenames/lib/extensions.json b/lib/node_modules/@stdlib/_tools/lint/filenames/lib/extensions.json index 67bf146fa27f..3e4fa9c32f93 100644 --- a/lib/node_modules/@stdlib/_tools/lint/filenames/lib/extensions.json +++ b/lib/node_modules/@stdlib/_tools/lint/filenames/lib/extensions.json @@ -4,6 +4,7 @@ ".c", ".cff", ".cfg", + ".cjs", ".cpp", ".csl", ".css", diff --git a/package.json b/package.json index 01e775d72202..6e9e4cbf4206 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,8 @@ "eslint-plugin-expect-type": "^0.6.2", "eslint-plugin-import": "^2.29.0", "eslint-plugin-jsdoc": "^46.8.2", + "eslint-plugin-jsonc": "^2.18.0", + "eslint-plugin-yml": "^1.16.0", "globals": "^16.1.0", "exorcist": "^2.0.0", "factor-bundle": "^2.5.0", diff --git a/tools/make/lib/lint/Makefile b/tools/make/lib/lint/Makefile index b5cb39596f6e..8e45f02b1e1d 100644 --- a/tools/make/lib/lint/Makefile +++ b/tools/make/lib/lint/Makefile @@ -25,6 +25,7 @@ include $(TOOLS_MAKE_LIB_DIR)/lint/editorconfig.mk include $(TOOLS_MAKE_LIB_DIR)/lint/filenames.mk include $(TOOLS_MAKE_LIB_DIR)/lint/git/Makefile include $(TOOLS_MAKE_LIB_DIR)/lint/javascript/Makefile +include $(TOOLS_MAKE_LIB_DIR)/lint/json/eslint.mk include $(TOOLS_MAKE_LIB_DIR)/lint/julia/Makefile include $(TOOLS_MAKE_LIB_DIR)/lint/license-headers/Makefile include $(TOOLS_MAKE_LIB_DIR)/lint/markdown/Makefile @@ -36,6 +37,7 @@ include $(TOOLS_MAKE_LIB_DIR)/lint/repl_help.mk include $(TOOLS_MAKE_LIB_DIR)/lint/shell/Makefile include $(TOOLS_MAKE_LIB_DIR)/lint/toposort.mk include $(TOOLS_MAKE_LIB_DIR)/lint/typescript/Makefile +include $(TOOLS_MAKE_LIB_DIR)/lint/yaml/eslint.mk # RULES # @@ -51,7 +53,7 @@ include $(TOOLS_MAKE_LIB_DIR)/lint/typescript/Makefile # @example # make lint #/ -lint: lint-filenames lint-javascript lint-markdown lint-c lint-python lint-julia lint-r lint-shell lint-typescript lint-conf lint-pkg-json lint-repl-help lint-license-headers lint-editorconfig +lint: lint-filenames lint-javascript lint-json lint-markdown lint-c lint-python lint-julia lint-r lint-shell lint-typescript lint-yaml lint-conf lint-pkg-json lint-repl-help lint-license-headers lint-editorconfig .PHONY: lint diff --git a/tools/make/lib/lint/json/eslint.mk b/tools/make/lib/lint/json/eslint.mk new file mode 100644 index 000000000000..42ca649a409e --- /dev/null +++ b/tools/make/lib/lint/json/eslint.mk @@ -0,0 +1,64 @@ +#/ +# @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. +#/ + +# VARIABLES # + +# Define the path to the [ESLint][1] executable. +# +# To install ESLint: +# +# ```bash +# $ npm install eslint +# ``` +# +# [1]: https://eslint.org/ +ESLINT ?= $(BIN_DIR)/eslint + +# Define the command-line options to use when invoking the ESLint executable: +eslint_json_flags := \ + --cache + +# Define user-supplied command-line options: +ESLINT_JSON_FLAGS ?= + +ifeq ($(AUTOFIX),true) + eslint_json_flags += --fix +endif + +# Append user-supplied command-line options: +eslint_json_flags += $(ESLINT_JSON_FLAGS) + + +# RULES # + +#/ +# Lints JSON files using [ESLint][1]. +# +# ## Notes +# +# - Uses `--cache` for performance with large numbers of JSON files. +# +# [1]: https://eslint.org/ +# +# @example +# make lint-json +#/ +lint-json: $(NODE_MODULES) + $(QUIET) $(ESLINT) $(eslint_json_flags) "lib/node_modules/@stdlib/**/package.json" "./*.json" + +.PHONY: lint-json diff --git a/tools/make/lib/lint/yaml/eslint.mk b/tools/make/lib/lint/yaml/eslint.mk new file mode 100644 index 000000000000..e855d28a7f3f --- /dev/null +++ b/tools/make/lib/lint/yaml/eslint.mk @@ -0,0 +1,64 @@ +#/ +# @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. +#/ + +# VARIABLES # + +# Define the path to the [ESLint][1] executable. +# +# To install ESLint: +# +# ```bash +# $ npm install eslint +# ``` +# +# [1]: https://eslint.org/ +ESLINT ?= $(BIN_DIR)/eslint + +# Define the command-line options to use when invoking the ESLint executable: +eslint_yaml_flags := \ + --cache + +# Define user-supplied command-line options: +ESLINT_YAML_FLAGS ?= + +ifeq ($(AUTOFIX),true) + eslint_yaml_flags += --fix +endif + +# Append user-supplied command-line options: +eslint_yaml_flags += $(ESLINT_YAML_FLAGS) + + +# RULES # + +#/ +# Lints YAML files using [ESLint][1]. +# +# ## Notes +# +# - Uses `--cache` for performance. +# +# [1]: https://eslint.org/ +# +# @example +# make lint-yaml +#/ +lint-yaml: $(NODE_MODULES) + $(QUIET) $(ESLINT) $(eslint_yaml_flags) ".github/**/*.yml" ".codecov.yml" ".github/*.yml" + +.PHONY: lint-yaml