Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: add remark plugin to validate expected HTML sections #6156

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<!--

@license Apache-2.0

Copyright (c) 2025 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.

-->

# remark-lint-expected-html-sections

> [remark][remark] plugin to lint expected HTML sections in README files according to stdlib conventions.

<section class="intro">

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.

</section>

<!-- /.intro -->

<section class="usage">

## Usage

```javascript
var expectedSections = require( '@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections' );
```

### expectedSections( \[options] )

Validates the presence of expected HTML sections in README files.

```javascript
var remark = require( 'remark' );

remark().use( expectedSections ).process( '# README', done );

function done( error, file ) {
if ( error ) {
console.error( error );
}
}
```

The plugin accepts the following `options`:

- **schema**: schema for expected HTML sections.

The default schema requires `usage`, `examples`, and `links` at the root level, and if a `c` section exists, it requires `usage` and `examples` subsections.

To specify a custom schema, set the `schema` option.

```javascript
var remark = require( 'remark' );

var customSchema = {
'root': {
'required': [ 'usage', 'examples', 'links', 'related' ],
'optional': [ 'intro', 'notes', 'c', 'references' ]
}
};
var opts = {
'schema': customSchema
};
remark().use( expectedSections, opts ).process( '# README', done );

function done( error, file ) {
if ( error ) {
console.error( error );
}
}
```

</section>

<!-- /.usage -->

<section class="notes">

## Notes

- The plugin detects HTML sections using pattern matching for `<section class="name">` tags.
- The plugin builds a hierarchical model of the document structure to validate both root and nested sections.
- Missing required sections will generate lint errors with specific details about which sections are missing.

</section>

<!-- /.notes -->

<section class="examples">

## Examples

```javascript
var remark = require( 'remark' );
var expectedSections = require( '@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections' );

var lines = [
'# Example Package',
'',
'<section class="intro">',
'',
'## Introduction',
'',
'This is an example package.',
'',
'</section>',
'',
'<!-- /.intro -->',
'',
'<section class="examples">',
'',
'## Examples',
'',
'```javascript',
'var example = require( \'example\' );',
'example();',
'```',
'',
'</section>',
'',
'<!-- /.examples -->',
'',
'<!-- Missing usage section! -->',
'',
'<!-- Missing links section! -->'
];

var source = lines.join( '\n' );

// Define default options:
var opts = {
'schema': {
'root': {
'required': [ 'usage', 'examples', 'links' ],
'optional': [ 'intro', 'notes', 'c', 'references', 'related' ]
}
}
};

// Process source with linter:
remark().use( expectedSections, opts ).process( source, done );

function done( error, file ) {
var i;
if ( error ) {
console.error( error );
return;
}
if ( file.messages.length === 0 ) {
console.log( 'No lint errors found.' );
} else {
for ( i = 0; i < file.messages.length; i++ ) {
console.log( '%d. %s', i+1, file.messages[ i ].reason );
}
}
}
```

</section>

<!-- /.examples -->

<section class="links">

[remark]: https://github.com/remarkjs/remark

</section>

<!-- /.links -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* @license Apache-2.0
*
* Copyright (c) 2025 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 remark = require( 'remark' );
var expectedSections = require( './../lib' );

var lines = [
'# Example Package',
'',
'<section class="intro">',
'',
'## Introduction',
'',
'This is an example package.',
'',
'</section>',
'',
'<!-- /.intro -->',
'',
'<section class="examples">',
'',
'## Examples',
'',
'```javascript',
'var example = require( \'example\' );',
'example();',
'```',
'',
'</section>',
'',
'<!-- /.examples -->',
'',
'<!-- Missing usage section! -->',
'',
'<!-- Missing links section! -->'
];

var source = lines.join( '\n' );

// Define default options:
var opts = {
'schema': {
'root': {
'required': [ 'usage', 'examples', 'links' ],
'optional': [ 'intro', 'notes', 'c', 'references', 'related' ]
}
}
};

// Process source with linter:
remark().use( expectedSections, opts ).process( source, done );

function done( error, file ) {
var i;
if ( error ) {
console.error( error );
return;
}
if ( file.messages.length === 0 ) {
console.log( 'No lint errors found.' );
} else {
for ( i = 0; i < file.messages.length; i++ ) {
console.log( '%d. %s', i+1, file.messages[ i ].reason );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* @license Apache-2.0
*
* Copyright (c) 2025 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 copy = require( '@stdlib/utils/copy' );
var defaults = require( './defaults.json' );
var validate = require( './validate.js' );
var linter = require( './linter.js' );


// MAIN //

/**
* Attaches a plugin to a remark processor to lint expected HTML sections.
*
* @param {Options} [options] - plugin options
* @param {Object} [options.schema] - schema for expected HTML sections
* @throws {TypeError} options argument must be an object
* @throws {TypeError} must provide valid options
* @returns {Function} transform function
*
* @example
* var remark = require( 'remark' );
*
* var str = [
* '<section class="usage">',
* '',
* '## Usage',
* '',
* '</section>',
* '',
* '<!-- /.usage -->',
* '',
* '<section class="examples">',
* '',
* '## Examples',
* '',
* '</section>',
* '',
* '<!-- /.examples -->',
* ''
* ].join( '\n' );
*
* remark().use( lint ).process( str, done );
*
* function done( error ) {
* if ( error ) {
* throw error;
* }
* }
*/
function attacher( options ) {
var opts;
var err;

// Set default options:
opts = copy( defaults );

// NOTE: cannot use `arguments.length` check, as `options` may be explicitly passed as `undefined`
if ( options !== void 0 ) {
err = validate( opts, options );
if ( err ) {
throw err;
}
}
return linter( opts );
}


// EXPORTS //

module.exports = attacher;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"schema": {
"root": {
"required": [
"usage",
"examples",
"links"
],
"optional": [
"intro",
"notes",
"c",
"references",
"related"
]
},
"c": {
"required": [
"usage",
"examples"
],
"optional": [
"intro",
"notes"
]
}
}
}
Loading