Skip to content

Commit d3fe5ed

Browse files
committed
build: add JSON and YAML linting via ESLint
Ref: stdlib-js/metr-issue-tracker#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 ---
1 parent d037e92 commit d3fe5ed

15 files changed

Lines changed: 829 additions & 1 deletion

File tree

eslint.config.cjs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@ var pluginCspell = require( '@cspell/eslint-plugin' );
3030
var pluginJsdoc = require( 'eslint-plugin-jsdoc' );
3131
var pluginImport = require( 'eslint-plugin-import' );
3232
var pluginExpectType = require( 'eslint-plugin-expect-type' );
33+
var pluginJsonc = require( 'eslint-plugin-jsonc' );
34+
var jsoncParser = require( 'jsonc-eslint-parser' );
35+
var pluginYml = require( 'eslint-plugin-yml' );
36+
var yamlParser = require( 'yaml-eslint-parser' );
3337
var stdlibPlugin = require( './lib/node_modules/@stdlib/_tools/eslint/rules/scripts/plugin.js' );
3438
var allRules = require( './etc/eslint/rules' );
3539
var tsRules = require( './etc/eslint/rules/typescript.js' );
40+
var jsonRules = require( './etc/eslint/rules/json.js' );
41+
var yamlRules = require( './etc/eslint/rules/yaml.js' );
3642

3743

3844
// VARIABLES //
@@ -101,6 +107,8 @@ module.exports = [
101107
'**/reports/',
102108
'dist/',
103109
'.git*',
110+
'!.github/',
111+
'!.codecov.yml',
104112

105113
// ESLint ignores **/node_modules/ by default. stdlib source
106114
// lives in lib/node_modules/, so un-ignore it:
@@ -217,5 +225,101 @@ module.exports = [
217225
'rules': {
218226
'jsdoc/require-jsdoc': 'off'
219227
}
228+
},
229+
230+
// Base JSON:
231+
{
232+
'files': [ '**/*.json' ],
233+
'languageOptions': {
234+
'parser': jsoncParser
235+
},
236+
'plugins': {
237+
'jsonc': pluginJsonc
238+
},
239+
'rules': jsonRules
240+
},
241+
242+
// cli_opts.json override (tab-indented per .editorconfig):
243+
{
244+
'files': [ '**/cli_opts.json' ],
245+
'rules': {
246+
'jsonc/indent': [ 'error', 'tab' ]
247+
}
248+
},
249+
250+
// package.json key ordering:
251+
{
252+
'files': [ '**/package.json' ],
253+
'rules': {
254+
'jsonc/sort-keys': [ 'error', {
255+
'pathPattern': '^$',
256+
'order': [
257+
'name',
258+
'private',
259+
'version',
260+
'description',
261+
'license',
262+
'licenses',
263+
'author',
264+
'maintainers',
265+
'contributors',
266+
'funding',
267+
'bin',
268+
'main',
269+
'exports',
270+
'browser',
271+
'unpkg',
272+
'gypfile',
273+
'directories',
274+
'types',
275+
'scripts',
276+
'homepage',
277+
'repository',
278+
'repositories',
279+
'bugs',
280+
'dependencies',
281+
'optionalDependencies',
282+
'devDependencies',
283+
'engines',
284+
'os',
285+
'keywords',
286+
'__stdlib__'
287+
]
288+
}]
289+
}
290+
},
291+
292+
// Base YAML:
293+
{
294+
'files': [ '**/*.yml' ],
295+
'languageOptions': {
296+
'parser': yamlParser
297+
},
298+
'plugins': {
299+
'yml': pluginYml,
300+
'stdlib': stdlibPlugin
301+
},
302+
'rules': {
303+
...yamlRules,
304+
'stdlib/yaml-license-header': 'error'
305+
}
306+
},
307+
308+
// GitHub Actions workflow key ordering:
309+
{
310+
'files': [ '.github/workflows/*.yml' ],
311+
'rules': {
312+
'yml/sort-keys': [ 'error', {
313+
'pathPattern': '^$',
314+
'order': [
315+
'name',
316+
'on',
317+
'concurrency',
318+
'permissions',
319+
'env',
320+
'jobs'
321+
]
322+
}]
323+
}
220324
}
221325
];

etc/eslint/rules/json.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2026 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
/**
22+
* ESLint rules for JSON files.
23+
*
24+
* @namespace rules
25+
*/
26+
var rules = {};
27+
28+
/**
29+
* Enforce 2-space indentation per `.editorconfig`.
30+
*
31+
* @name jsonc/indent
32+
* @memberof rules
33+
* @type {Array}
34+
*/
35+
rules[ 'jsonc/indent' ] = [ 'error', 2 ];
36+
37+
/**
38+
* Disallow comments in JSON files.
39+
*
40+
* @name jsonc/no-comments
41+
* @memberof rules
42+
* @type {string}
43+
*/
44+
rules[ 'jsonc/no-comments' ] = 'error';
45+
46+
/**
47+
* Disallow trailing commas.
48+
*
49+
* @name jsonc/comma-dangle
50+
* @memberof rules
51+
* @type {Array}
52+
*/
53+
rules[ 'jsonc/comma-dangle' ] = [ 'error', 'never' ];
54+
55+
/**
56+
* Disallow duplicate keys.
57+
*
58+
* @name jsonc/no-dupe-keys
59+
* @memberof rules
60+
* @type {string}
61+
*/
62+
rules[ 'jsonc/no-dupe-keys' ] = 'error';
63+
64+
/**
65+
* Disallow floating decimals (e.g., `.5` or `1.`).
66+
*
67+
* @name jsonc/no-floating-decimal
68+
* @memberof rules
69+
* @type {string}
70+
*/
71+
rules[ 'jsonc/no-floating-decimal' ] = 'error';
72+
73+
/**
74+
* Disallow irregular whitespace characters.
75+
*
76+
* @name jsonc/no-irregular-whitespace
77+
* @memberof rules
78+
* @type {string}
79+
*/
80+
rules[ 'jsonc/no-irregular-whitespace' ] = 'error';
81+
82+
/**
83+
* Disallow multi-line strings.
84+
*
85+
* @name jsonc/no-multi-str
86+
* @memberof rules
87+
* @type {string}
88+
*/
89+
rules[ 'jsonc/no-multi-str' ] = 'error';
90+
91+
/**
92+
* Disallow octal literals.
93+
*
94+
* @name jsonc/no-octal
95+
* @memberof rules
96+
* @type {string}
97+
*/
98+
rules[ 'jsonc/no-octal' ] = 'error';
99+
100+
/**
101+
* Disallow useless escape characters.
102+
*
103+
* @name jsonc/no-useless-escape
104+
* @memberof rules
105+
* @type {string}
106+
*/
107+
rules[ 'jsonc/no-useless-escape' ] = 'error';
108+
109+
/**
110+
* Enforce valid JSON number literals.
111+
*
112+
* @name jsonc/valid-json-number
113+
* @memberof rules
114+
* @type {string}
115+
*/
116+
rules[ 'jsonc/valid-json-number' ] = 'error';
117+
118+
119+
// EXPORTS //
120+
121+
module.exports = rules;

etc/eslint/rules/yaml.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2026 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
/**
22+
* ESLint rules for YAML files.
23+
*
24+
* @namespace rules
25+
*/
26+
var rules = {};
27+
28+
/**
29+
* Enforce 2-space indentation per `.editorconfig`.
30+
*
31+
* @name yml/indent
32+
* @memberof rules
33+
* @type {Array}
34+
*/
35+
rules[ 'yml/indent' ] = [ 'error', 2 ];
36+
37+
/**
38+
* Disallow empty YAML documents.
39+
*
40+
* @name yml/no-empty-document
41+
* @memberof rules
42+
* @type {string}
43+
*/
44+
rules[ 'yml/no-empty-document' ] = 'error';
45+
46+
/**
47+
* Disallow empty keys.
48+
*
49+
* @name yml/no-empty-key
50+
* @memberof rules
51+
* @type {string}
52+
*/
53+
rules[ 'yml/no-empty-key' ] = 'error';
54+
55+
/**
56+
* Disallow empty mapping values.
57+
*
58+
* @name yml/no-empty-mapping-value
59+
* @memberof rules
60+
* @type {string}
61+
*/
62+
rules[ 'yml/no-empty-mapping-value' ] = 'error';
63+
64+
/**
65+
* Disallow empty sequence entries.
66+
*
67+
* @name yml/no-empty-sequence-entry
68+
* @memberof rules
69+
* @type {string}
70+
*/
71+
rules[ 'yml/no-empty-sequence-entry' ] = 'error';
72+
73+
/**
74+
* Disallow irregular whitespace characters.
75+
*
76+
* @name yml/no-irregular-whitespace
77+
* @memberof rules
78+
* @type {string}
79+
*/
80+
rules[ 'yml/no-irregular-whitespace' ] = 'error';
81+
82+
/**
83+
* Disallow tab indentation.
84+
*
85+
* @name yml/no-tab-indent
86+
* @memberof rules
87+
* @type {string}
88+
*/
89+
rules[ 'yml/no-tab-indent' ] = 'error';
90+
91+
/**
92+
* Require string keys.
93+
*
94+
* @name yml/require-string-key
95+
* @memberof rules
96+
* @type {string}
97+
*/
98+
rules[ 'yml/require-string-key' ] = 'error';
99+
100+
/**
101+
* Enforce block-style mappings.
102+
*
103+
* @name yml/block-mapping
104+
* @memberof rules
105+
* @type {string}
106+
*/
107+
rules[ 'yml/block-mapping' ] = 'error';
108+
109+
/**
110+
* Enforce block-style sequences.
111+
*
112+
* @name yml/block-sequence
113+
* @memberof rules
114+
* @type {string}
115+
*/
116+
rules[ 'yml/block-sequence' ] = 'error';
117+
118+
/**
119+
* Disallow multiple consecutive empty lines.
120+
*
121+
* @name yml/no-multiple-empty-lines
122+
* @memberof rules
123+
* @type {Array}
124+
*/
125+
rules[ 'yml/no-multiple-empty-lines' ] = [ 'error', {
126+
'max': 1
127+
}];
128+
129+
/**
130+
* Prefer single quotes (warn first to assess impact on `${{ }}` expressions).
131+
*
132+
* @name yml/quotes
133+
* @memberof rules
134+
* @type {Array}
135+
*/
136+
rules[ 'yml/quotes' ] = [ 'warn', {
137+
'prefer': 'single',
138+
'avoidEscape': true
139+
}];
140+
141+
142+
// EXPORTS //
143+
144+
module.exports = rules;

0 commit comments

Comments
 (0)