Skip to content

Commit be860bd

Browse files
Planeshifterkgrytestdlib-bot
authored
build: add remark plugin to validate expected HTML sections
PR-URL: #6156 Ref: stdlib-js/metr-issue-tracker#49 Co-authored-by: Athan Reines <kgryte@gmail.com> Reviewed-by: Athan Reines <kgryte@gmail.com> Co-authored-by: stdlib-bot <noreply@stdlib.io>
1 parent f23d66e commit be860bd

File tree

16 files changed

+1997
-0
lines changed

16 files changed

+1997
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<!--
2+
3+
@license Apache-2.0
4+
5+
Copyright (c) 2025 The Stdlib Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
-->
20+
21+
# remark-lint-expected-html-sections
22+
23+
> [remark][remark] plugin to lint expected HTML sections in README files according to stdlib conventions.
24+
25+
<section class="intro">
26+
27+
This plugin checks for the presence of required HTML sections in README.md files, as defined in stdlib's documentation conventions. It ensures that each README contains the essential sections needed for comprehensive documentation, and that sections with special requirements (such as the C API section) contain their own required subsections.
28+
29+
</section>
30+
31+
<!-- /.intro -->
32+
33+
<section class="usage">
34+
35+
## Usage
36+
37+
```javascript
38+
var expectedSections = require( '@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections' );
39+
```
40+
41+
### expectedSections( \[options] )
42+
43+
Validates the presence of expected HTML sections in README files.
44+
45+
```javascript
46+
var remark = require( 'remark' );
47+
48+
remark().use( expectedSections ).process( '# README', done );
49+
50+
function done( error, file ) {
51+
if ( error ) {
52+
console.error( error );
53+
}
54+
}
55+
```
56+
57+
The plugin accepts the following `options`:
58+
59+
- **schema**: schema for expected HTML sections.
60+
61+
The default schema requires `usage`, `examples`, and `links` at the root level, and if a `c` section exists, it requires `usage` and `examples` subsections.
62+
63+
To specify a custom schema, set the `schema` option.
64+
65+
```javascript
66+
var remark = require( 'remark' );
67+
68+
var customSchema = {
69+
'root': {
70+
'required': [ 'usage', 'examples', 'links', 'related' ],
71+
'optional': [ 'intro', 'notes', 'c', 'references' ]
72+
}
73+
};
74+
var opts = {
75+
'schema': customSchema
76+
};
77+
remark().use( expectedSections, opts ).process( '# README', done );
78+
79+
function done( error, file ) {
80+
if ( error ) {
81+
console.error( error );
82+
}
83+
}
84+
```
85+
86+
</section>
87+
88+
<!-- /.usage -->
89+
90+
<section class="notes">
91+
92+
## Notes
93+
94+
- The plugin detects HTML sections using pattern matching for `<section class="name">` tags.
95+
- The plugin builds a hierarchical model of the document structure to validate both root and nested sections.
96+
- Missing required sections will generate lint errors with specific details about which sections are missing.
97+
98+
</section>
99+
100+
<!-- /.notes -->
101+
102+
<section class="examples">
103+
104+
## Examples
105+
106+
```javascript
107+
var remark = require( 'remark' );
108+
var expectedSections = require( '@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections' );
109+
110+
var lines = [
111+
'# Example Package',
112+
'',
113+
'<section class="intro">',
114+
'',
115+
'## Introduction',
116+
'',
117+
'This is an example package.',
118+
'',
119+
'</section>',
120+
'',
121+
'<!-- /.intro -->',
122+
'',
123+
'<section class="examples">',
124+
'',
125+
'## Examples',
126+
'',
127+
'```javascript',
128+
'var example = require( \'example\' );',
129+
'example();',
130+
'```',
131+
'',
132+
'</section>',
133+
'',
134+
'<!-- /.examples -->',
135+
'',
136+
'<!-- Missing usage section! -->',
137+
'',
138+
'<!-- Missing links section! -->'
139+
];
140+
141+
var source = lines.join( '\n' );
142+
143+
// Define default options:
144+
var opts = {
145+
'schema': {
146+
'root': {
147+
'required': [ 'usage', 'examples', 'links' ],
148+
'optional': [ 'intro', 'notes', 'c', 'references', 'related' ]
149+
}
150+
}
151+
};
152+
153+
// Process source with linter:
154+
remark().use( expectedSections, opts ).process( source, done );
155+
156+
function done( error, file ) {
157+
var i;
158+
if ( error ) {
159+
console.error( error );
160+
return;
161+
}
162+
if ( file.messages.length === 0 ) {
163+
console.log( 'No lint errors found.' );
164+
} else {
165+
for ( i = 0; i < file.messages.length; i++ ) {
166+
console.log( '%d. %s', i+1, file.messages[ i ].reason );
167+
}
168+
}
169+
}
170+
```
171+
172+
</section>
173+
174+
<!-- /.examples -->
175+
176+
<section class="links">
177+
178+
[remark]: https://github.com/remarkjs/remark
179+
180+
</section>
181+
182+
<!-- /.links -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 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+
var remark = require( 'remark' );
22+
var expectedSections = require( './../lib' );
23+
24+
var lines = [
25+
'# Example Package',
26+
'',
27+
'<section class="intro">',
28+
'',
29+
'## Introduction',
30+
'',
31+
'This is an example package.',
32+
'',
33+
'</section>',
34+
'',
35+
'<!-- /.intro -->',
36+
'',
37+
'<section class="examples">',
38+
'',
39+
'## Examples',
40+
'',
41+
'```javascript',
42+
'var example = require( \'example\' );',
43+
'example();',
44+
'```',
45+
'',
46+
'</section>',
47+
'',
48+
'<!-- /.examples -->',
49+
'',
50+
'<!-- Missing usage section! -->',
51+
'',
52+
'<!-- Missing links section! -->'
53+
];
54+
55+
var source = lines.join( '\n' );
56+
57+
// Define default options:
58+
var opts = {
59+
'schema': {
60+
'root': {
61+
'required': [ 'usage', 'examples', 'links' ],
62+
'optional': [ 'intro', 'notes', 'c', 'references', 'related' ]
63+
}
64+
}
65+
};
66+
67+
// Process source with linter:
68+
remark().use( expectedSections, opts ).process( source, done );
69+
70+
function done( error, file ) {
71+
var i;
72+
if ( error ) {
73+
console.error( error );
74+
return;
75+
}
76+
if ( file.messages.length === 0 ) {
77+
console.log( 'No lint errors found.' );
78+
} else {
79+
for ( i = 0; i < file.messages.length; i++ ) {
80+
console.log( '%d. %s', i+1, file.messages[ i ].reason );
81+
}
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 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+
// MODULES //
22+
23+
var copy = require( '@stdlib/utils/copy' );
24+
var defaults = require( './defaults.json' );
25+
var validate = require( './validate.js' );
26+
var linter = require( './linter.js' );
27+
28+
29+
// MAIN //
30+
31+
/**
32+
* Attaches a plugin to a remark processor to lint expected HTML sections.
33+
*
34+
* @param {Options} [options] - plugin options
35+
* @param {Object} [options.schema] - schema for expected HTML sections
36+
* @throws {TypeError} options argument must be an object
37+
* @throws {TypeError} must provide valid options
38+
* @returns {Function} transform function
39+
*
40+
* @example
41+
* var remark = require( 'remark' );
42+
*
43+
* var str = [
44+
* '<section class="usage">',
45+
* '',
46+
* '## Usage',
47+
* '',
48+
* '</section>',
49+
* '',
50+
* '<!-- /.usage -->',
51+
* '',
52+
* '<section class="examples">',
53+
* '',
54+
* '## Examples',
55+
* '',
56+
* '</section>',
57+
* '',
58+
* '<!-- /.examples -->',
59+
* ''
60+
* ].join( '\n' );
61+
*
62+
* remark().use( lint ).process( str, done );
63+
*
64+
* function done( error ) {
65+
* if ( error ) {
66+
* throw error;
67+
* }
68+
* }
69+
*/
70+
function attacher( options ) {
71+
var opts;
72+
var err;
73+
74+
// Set default options:
75+
opts = copy( defaults );
76+
77+
// NOTE: cannot use `arguments.length` check, as `options` may be explicitly passed as `undefined`
78+
if ( options !== void 0 ) {
79+
err = validate( opts, options );
80+
if ( err ) {
81+
throw err;
82+
}
83+
}
84+
return linter( opts );
85+
}
86+
87+
88+
// EXPORTS //
89+
90+
module.exports = attacher;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"schema": {
3+
"root": {
4+
"required": [
5+
"usage",
6+
"examples",
7+
"links"
8+
],
9+
"optional": [
10+
"intro",
11+
"notes",
12+
"c",
13+
"references",
14+
"related"
15+
]
16+
},
17+
"c": {
18+
"required": [
19+
"usage",
20+
"examples"
21+
],
22+
"optional": [
23+
"intro",
24+
"notes"
25+
]
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)