From ea1cf3f1ed2a74ffdaf08d1886729471e6352f4d Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Mon, 17 Mar 2025 21:51:57 -0400 Subject: [PATCH 01/15] build: add remark plugin to validate expected HTML sections --- 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: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../README.md | 176 +++++++++++++++ .../examples/index.js | 83 +++++++ .../lib/index.js | 40 ++++ .../lib/main.js | 192 ++++++++++++++++ .../package.json | 54 +++++ .../test/fixtures/incomplete_c.md.txt | 67 ++++++ .../test/fixtures/invalid_asin.md.txt | 194 ++++++++++++++++ .../fixtures/missing_required_root.md.txt | 35 +++ .../test/fixtures/valid_asin.md.txt | 200 +++++++++++++++++ .../test/fixtures/valid_complete.md.txt | 130 +++++++++++ .../test/fixtures/valid_memoize.md.txt | 212 ++++++++++++++++++ .../test/test.js | 199 ++++++++++++++++ 12 files changed, 1582 insertions(+) create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/examples/index.js create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/package.json create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/incomplete_c.md.txt create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/missing_required_root.md.txt create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_complete.md.txt create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md new file mode 100644 index 000000000000..6e63d5e92f8f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md @@ -0,0 +1,176 @@ + + +# remark-lint-expected-html-sections + +> [remark][remark] plugin to lint expected HTML sections in README files according to stdlib conventions. + +
+ +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. + +
+ + + +
+ +## 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 ); + } +} +``` + +#### options.schema + +The plugin can be configured with a custom schema that defines required and optional sections. + +```javascript +var opts = { + 'schema': { + 'root': { + 'required': [ 'usage', 'examples', 'links' ], + 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] + }, + 'c': { + 'required': [ 'usage', 'examples' ], + 'optional': [ 'intro', 'notes' ] + } + } +}; + +remark().use( expectedSections, opts ).process( src, done ); +``` + +The default schema requires `usage`, `examples`, and `links` at the root level, and if a `c` section exists, it requires `usage` and `examples` subsections. + +
+ + + +
+ +## Notes + +- The plugin detects HTML sections using pattern matching for `
` 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. + +
+ + + +
+ +## Examples + +```javascript +var remark = require( 'remark' ); +var expectedSections = require( '@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections' ); + +var lines = [ + '# Example Package', + '', + '
', + '', + '## Introduction', + '', + 'This is an example package.', + '', + '
', + '', + '', + '', + '
', + '', + '## Examples', + '', + '```javascript', + 'var example = require( \'example\' );', + 'example();', + '```', + '', + '
', + '', + '', + '', + '', + '', + '' +]; + +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 ); + } + } +} +``` + +
+ + + + + + diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/examples/index.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/examples/index.js new file mode 100644 index 000000000000..ffb80e1abece --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/examples/index.js @@ -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', + '', + '
', + '', + '## Introduction', + '', + 'This is an example package.', + '', + '
', + '', + '', + '', + '
', + '', + '## Examples', + '', + '```javascript', + 'var example = require( \'example\' );', + 'example();', + '```', + '', + '
', + '', + '', + '', + '', + '', + '' +]; + +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 ); + } + } +} diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js new file mode 100644 index 000000000000..54942d814094 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js @@ -0,0 +1,40 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2022 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'; + +/** +* remark lint plugin for validating HTML section hierarchy in README files. +* +* @module @stdlib/_tools/remark/plugins/remark-lint-expected-html-sections +* +* @example +* var remark = require( 'remark' ); +* var expectedHtmlSections = require( '@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections' ); +* +* var linter = remark().use( expectedHtmlSections ); +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js new file mode 100644 index 000000000000..93943e9a0eee --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js @@ -0,0 +1,192 @@ +/** +* @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 logger = require( 'debug' ); +var rule = require( 'unified-lint-rule' ); +var visit = require( 'unist-util-visit' ); +var keys = require( '@stdlib/utils/keys' ); + + +// VARIABLES // + +var debug = logger( 'remark-lint-expected-html-sections' ); +var SECTION_START = //; +var DEFAULT_SCHEMA = { + 'root': { + 'required': [ 'usage', 'examples', 'links' ], + 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] + }, + 'c': { + 'required': [ 'usage', 'examples' ], + 'optional': [ 'intro', 'notes' ] + } +}; +var expectedHtmlSectionsRule; + + +// MAIN // + +/** +* Validates HTML section hierarchy in README files according to stdlib conventions. +* +* @private +* @param {Node} tree - abstract syntax tree (AST) +* @param {File} file - virtual file +* @param {Object} options - options +* @param {Object} [options.schema] - section schema defining required and optional sections +* @param {Callback} clbk - callback to invoke upon completion +* @returns {void} +*/ +function linter( tree, file, options, clbk ) { + var requiredRootSections; + var requiredCSections; + var sectionStructure; + var currentSection; + var sectionsFound; + var sectionStack; + var sectionMatch; + var missingRoot; + var className; + var missingC; + var schema; + var msg; + var i; + + debug( 'Linting file: %s', file.path || '' ); + + // Use provided schema or default schema: + schema = options.schema || DEFAULT_SCHEMA; + + // Initialize section tracking: + sectionStack = []; + sectionStructure = []; + + // Keep track of sections at different levels: + sectionsFound = { + 'root': {}, + 'c': {} + }; + + // Visit all HTML nodes to build section structure: + visit( tree, 'html', visitor ); + + // Log all sections found for debugging: + debug( 'Root sections found: %j', keys( sectionsFound.root ) ); + if ( sectionsFound.root.c ) { + debug( 'C sections found: %j', keys( sectionsFound.c ) ); + } + + // After visiting all nodes, validate against schema: + requiredRootSections = schema.root.required || []; + + // Check for missing required root sections: + missingRoot = []; + for ( i = 0; i < requiredRootSections.length; i++ ) { + if ( !sectionsFound.root[ requiredRootSections[ i ] ] ) { + missingRoot.push( requiredRootSections[ i ] ); + } + } + + if ( missingRoot.length > 0 ) { + msg = 'Missing required root-level sections: ' + missingRoot.join( ', ' ) + '. Required sections are: ' + requiredRootSections.join(', ') + '. missing-required-sections'; + debug( msg ); + file.message( msg, tree ); + } + + // If 'c' section exists, check its requirements: + if ( sectionsFound.root.c ) { + requiredCSections = (schema.c) ? (schema.c.required || []) : []; + + // Check for missing required C sections: + missingC = []; + for ( i = 0; i < requiredCSections.length; i++ ) { + if ( !sectionsFound.c[ requiredCSections[ i ] ] ) { + missingC.push( requiredCSections[ i ] ); + } + } + + if ( missingC.length > 0 ) { + msg = 'Missing required sections in "c" section: ' + missingC.join(', ') + '. Required C sections are: ' + requiredCSections.join(', ') + '. missing-required-c-sections'; + debug( msg ); + file.message( msg, sectionsFound.root.c.node ); + } + } + + debug( 'Finished linting: %s', file.path || '' ); + return clbk(); + + /** + * Callback invoked upon finding a matching node. + * + * @private + * @param {Object} node - AST node + * @returns {void} + */ + function visitor( node ) { + // Check if this is a section start tag: + sectionMatch = SECTION_START.exec( node.value ); + if ( sectionMatch ) { + className = sectionMatch[1] || ''; + + debug( 'Found section with class: %s', className ); + + // Create section data object: + currentSection = { + 'node': node, + 'name': className, + 'children': [], + 'parent': null + }; + + // Add to parent if there is one: + if ( sectionStack.length > 0 ) { + currentSection.parent = sectionStack[ sectionStack.length - 1 ]; + currentSection.parent.children.push( currentSection ); + + // Record C-level section: + if ( currentSection.parent.name === 'c' ) { + sectionsFound.c[ className ] = currentSection; + } + } else { + // This is a root section: + sectionStructure.push( currentSection ); + sectionsFound.root[ className ] = currentSection; + } + + // Push to stack: + sectionStack.push( currentSection ); + } + // Check if this is a section end tag: + else if ( /<\/section>/.test( node.value ) ) { + if ( sectionStack.length > 0 ) { + sectionStack.pop(); + } + } + } +} + +expectedHtmlSectionsRule = rule( 'remark-lint:expected-html-sections', linter ); + + +// EXPORTS // + +module.exports = expectedHtmlSectionsRule; diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/package.json b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/package.json new file mode 100644 index 000000000000..bac6cb9faa50 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/package.json @@ -0,0 +1,54 @@ +{ + "name": "@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections", + "version": "0.0.0", + "description": "remark lint plugin for validating expected HTML sections in README files according to stdlib conventions.", + "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" + } + ], + "main": "./lib", + "directories": { + "example": "./examples", + "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": ">=6.0.0", + "npm": ">2.7.0" + }, + "keywords": [ + "stdlib", + "tools", + "lint", + "linter", + "markdown", + "md", + "remark", + "remark-plugin", + "plugin", + "html", + "section", + "structure", + "hierarchy", + "expected", + "validate" + ] +} diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/incomplete_c.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/incomplete_c.md.txt new file mode 100644 index 000000000000..e3e0e13299fe --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/incomplete_c.md.txt @@ -0,0 +1,67 @@ +# Example + +
+ +## Introduction + +This is an introduction. + +
+ + + +
+ +## Usage + +```javascript +var foo = require( 'foo' ); +``` + +
+ + + +
+ +## Examples + +```javascript +var foo = require( 'foo' ); +foo(); +``` + +
+ + + +
+ +## C APIs + +
+ +### Introduction + +This section contains C API documentation. + +
+ + + + + + + +
+ + + + + + diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt new file mode 100644 index 000000000000..5f5f6ea628c9 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt @@ -0,0 +1,194 @@ + + +# asin + +> Compute the [arcsine][arcsine] of a double-precision floating-point number. + +
+ +## Usage + +```javascript +var asin = require( '@stdlib/math/base/special/asin' ); +``` + +#### asin( x ) + +Computes the [arcsine][arcsine] of a double-precision floating-point number (in radians). + +```javascript +var v = asin( 0.0 ); +// returns 0.0 + +v = asin( -3.141592653589793/6.0 ); +// returns ~-0.551 +``` + +The domain of `x` is restricted to `[-1,1]`. If `|x| > 1`, the function returns `NaN`. + +```javascript +var v = asin( -3.14 ); +// returns NaN +``` + +
+ + + +
+ +## Examples + + + +```javascript +var linspace = require( '@stdlib/array/base/linspace' ); +var asin = require( '@stdlib/math/base/special/asin' ); + +var x = linspace( -1.0, 1.0, 100 ); + +var i; +for ( i = 0; i < x.length; i++ ) { + console.log( asin( x[ i ] ) ); +} +``` + +
+ + + + + +* * * + +
+ +## C APIs + + + +
+ +
+ + + + + +### Usage + +```c +#include "stdlib/math/base/special/asin.h" +``` + +#### stdlib_base_asin( x ) + +Computes the [arcsine][arcsine] of a double-precision floating-point number (in radians). + +```c +double out = stdlib_base_asin( 0.0 ); +// returns 0.0 + +out = stdlib_base_asin( -3.141592653589793/6.0 ); +// returns ~-0.551 +``` + +The function accepts the following arguments: + +- **x**: `[in] double` input value (in radians). + +```c +double stdlib_base_asin( const double x ); +``` + + + +
+ +
+ + + + + +
+ +### Examples + +```c +#include "stdlib/math/base/special/asin.h" +#include + +int main( void ) { + const double x[] = { -1.0, -0.78, -0.56, -0.33, -0.11, 0.11, 0.33, 0.56, 0.78, 1.0 }; + + double v; + int i; + for ( i = 0; i < 10; i++ ) { + v = stdlib_base_asin( x[ i ] ); + printf( "asin(%lf) = %lf\n", x[ i ], v ); + } +} +``` + +
+ + + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/missing_required_root.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/missing_required_root.md.txt new file mode 100644 index 000000000000..ad1d31c5a8a6 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/missing_required_root.md.txt @@ -0,0 +1,35 @@ +# Example + +
+ +## Introduction + +This is an introduction. + +
+ + + + + +
+ +## Examples + +```javascript +var foo = require( 'foo' ); +foo(); +``` + +
+ + + + + + diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt new file mode 100644 index 000000000000..357094ff23af --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt @@ -0,0 +1,200 @@ + + +# asin + +> Compute the [arcsine][arcsine] of a double-precision floating-point number. + +
+ +## Usage + +```javascript +var asin = require( '@stdlib/math/base/special/asin' ); +``` + +#### asin( x ) + +Computes the [arcsine][arcsine] of a double-precision floating-point number (in radians). + +```javascript +var v = asin( 0.0 ); +// returns 0.0 + +v = asin( -3.141592653589793/6.0 ); +// returns ~-0.551 +``` + +The domain of `x` is restricted to `[-1,1]`. If `|x| > 1`, the function returns `NaN`. + +```javascript +var v = asin( -3.14 ); +// returns NaN +``` + +
+ + + +
+ +## Examples + + + +```javascript +var linspace = require( '@stdlib/array/base/linspace' ); +var asin = require( '@stdlib/math/base/special/asin' ); + +var x = linspace( -1.0, 1.0, 100 ); + +var i; +for ( i = 0; i < x.length; i++ ) { + console.log( asin( x[ i ] ) ); +} +``` + +
+ + + + + +* * * + +
+ +## C APIs + + + +
+ +
+ + + + + +
+ +### Usage + +```c +#include "stdlib/math/base/special/asin.h" +``` + +#### stdlib_base_asin( x ) + +Computes the [arcsine][arcsine] of a double-precision floating-point number (in radians). + +```c +double out = stdlib_base_asin( 0.0 ); +// returns 0.0 + +out = stdlib_base_asin( -3.141592653589793/6.0 ); +// returns ~-0.551 +``` + +The function accepts the following arguments: + +- **x**: `[in] double` input value (in radians). + +```c +double stdlib_base_asin( const double x ); +``` + +
+ + + + + +
+ +
+ + + + + +
+ +### Examples + +```c +#include "stdlib/math/base/special/asin.h" +#include + +int main( void ) { + const double x[] = { -1.0, -0.78, -0.56, -0.33, -0.11, 0.11, 0.33, 0.56, 0.78, 1.0 }; + + double v; + int i; + for ( i = 0; i < 10; i++ ) { + v = stdlib_base_asin( x[ i ] ); + printf( "asin(%lf) = %lf\n", x[ i ], v ); + } +} +``` + +
+ + + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_complete.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_complete.md.txt new file mode 100644 index 000000000000..06301206be7a --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_complete.md.txt @@ -0,0 +1,130 @@ +# Example + +
+ +## Introduction + +This is an introduction. + +
+ + + +
+ +## Usage + +```javascript +var foo = require( 'foo' ); +``` + +
+ + + +
+ +## Notes + +Some notes here. + +
+ + + +
+ +## Examples + +```javascript +var foo = require( 'foo' ); +foo(); +``` + +
+ + + +
+ +## C APIs + +
+ +### Introduction + +This section contains C API documentation. + +
+ + + +
+ +### Usage + +```c +#include "foo.h" +``` + +
+ + + +
+ +### Notes + +Some implementation notes... + +
+ + + +
+ +### Examples + +```c +// C example +#include "foo.h" +``` + +
+ + + +
+ + + +
+ +## References + +* Reference 1 +* Reference 2 + +
+ + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt new file mode 100644 index 000000000000..6cf2726f3838 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt @@ -0,0 +1,212 @@ + + +# memoize + +> Memoize a function. + + + +
+ +
+ + + + + +
+ +## Usage + +```javascript +var memoize = require( '@stdlib/utils/memoize' ); +``` + +#### memoize( fcn\[, hashFunction] ) + +Memoizes a function. + +```javascript +var randu = require( '@stdlib/random/base/randu' ); + +function rand( n ) { + return n * randu(); +} + +var memoized = memoize( rand ); + +var v1 = memoized( 5 ); +var v2 = memoized( 5 ); + +var bool = ( v1 === v2 ); +// returns true +``` + +By default, the implementation serializes provided arguments as a `string` and stores results using the `string` as an identifier. To use a custom hash function, provide a hash function argument. + +```javascript +function add( obj ) { + return obj.x + obj.y + obj.z; +} + +obj = { + 'x': 3, + 'y': 4, + 'z': 5 +}; + +// Default behavior... + +var memoized = memoize( add ); + +var v1 = memoized( obj ); +// returns 12 + +var str = obj.toString(); +// returns '[object Object]' + +var v2 = memoized.cache[ str ]; +// returns 12 + +obj.x = 1000; + +var v3 = memoized( obj ); +// returns 12 + +// Custom hash function... + +function hashFunction( args ) { + return JSON.stringify( args ); +} + +memoized = memoize( add, hashFunction ); + +v1 = memoized( obj ); +// returns 1009 + +str = hashFunction( [ obj ] ); +// returns '[{"x":1000,"y":4,"z":5}]' + +v2 = memoized.cache[ str ]; +// returns 1009 + +obj.x = 6; + +v3 = memoized( obj ); +// returns 15 +``` + +#### memoized.cache + +Results cache. Note that, while the property is **read-only**, cache contents may be modified independently of the memoized function. + +```javascript +function beep( x ) { + throw new Error( 'boop' ); +} + +var memoized = memoize( beep ); + +var cache = memoized.cache; +// returns {} + +// Modify the cache: +cache[ 'bop' ] = 'bip'; + +var str = memoized( 'bop' ); +// returns 'bip' +``` + +
+ + + + + +
+ +## Notes + +- The implementation does **not** set the `length` of the returned function. Accordingly, the returned function `length` is **always** `0`. +- The evaluation context is **always** `null`. + +
+ + + + + +
+ +## Examples + + + +```javascript +var randu = require( '@stdlib/random/base/randu' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var memoize = require( '@stdlib/utils/memoize' ); + +var fcn; +var n; +var v; +var i; + +function rand( n ) { + return n * randu(); +} + +fcn = memoize( rand ); + +for ( i = 0; i < 100; i++ ) { + n = floor( randu() * 5 ); + v = fcn( n ); + console.log( 'rand(%d) = %d', n, v ); +} +``` + +
+ + + + + +
+ +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js new file mode 100644 index 000000000000..5c8733d5fd3f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js @@ -0,0 +1,199 @@ +/** +* @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 path = require( 'path' ); +var tape = require( 'tape' ); +var remark = require( 'remark' ); +var readFileSync = require( '@stdlib/fs/read-file' ).sync; +var expectedSections = require( './../lib' ); + + +// VARIABLES // + +var TEST_FIXTURES = path.join( __dirname, 'fixtures' ); +var DEFAULT_SCHEMA = { + 'root': { + 'required': [ 'usage', 'examples', 'links' ], + 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] + }, + 'c': { + 'required': [ 'usage', 'examples' ], + 'optional': [ 'intro', 'notes' ] + } +}; +var CUSTOM_SCHEMA = { + 'root': { + 'required': [ 'usage', 'examples', 'links', 'related' ], + 'optional': [ 'intro', 'notes', 'c', 'references' ] + } +}; + + +// FUNCTIONS // + +function fixture( name ) { + var text = readFileSync( path.join( TEST_FIXTURES, name ), 'utf8' ); + return text.toString(); +} + +function lint( text, options, clbk ) { + remark().use( expectedSections, options ).process( text, clbk ); +} + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof expectedSections, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'validates valid_complete.md.txt with default schema', function test( t ) { + var opts = { + 'schema': DEFAULT_SCHEMA + }; + + lint( fixture( 'valid_complete.md.txt' ), opts, done ); + + function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 0, 'has no errors for valid fixture with all sections' ); + t.end(); + } +}); + +tape( 'validates valid_memoize.md.txt', function test( t ) { + var opts = { + 'schema': DEFAULT_SCHEMA + }; + + lint( fixture( 'valid_memoize.md.txt' ), opts, done ); + + function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 0, 'has no errors for memoize README fixture' ); + t.end(); + } +}); + +tape( 'validates valid_asin.md.txt with C section', function test( t ) { + var opts = { + 'schema': DEFAULT_SCHEMA + }; + + lint( fixture( 'valid_asin.md.txt' ), opts, done ); + + function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 0, 'has no errors for asin README fixture with C section' ); + t.end(); + } +}); + +tape( 'detects missing C usage section in invalid_asin.md.txt', function test( t ) { + var opts = { + 'schema': DEFAULT_SCHEMA + }; + + lint( fixture( 'invalid_asin.md.txt' ), opts, done ); + + function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 1, 'has one error for invalid asin fixture' ); + t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: usage'), true, 'error identifies the missing C usage section' ); + t.end(); + } +}); + +tape( 'reports error for missing required root sections', function test( t ) { + var opts = { + 'schema': DEFAULT_SCHEMA + }; + + lint( fixture( 'missing_required_root.md.txt' ), opts, function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 1, 'has one error for fixture missing a required root section' ); + t.strictEqual( file.messages[0].reason.includes('Missing required root-level sections: usage'), true, 'error identifies the missing section' ); + t.end(); + }); +}); + +tape( 'reports error for incomplete C section', function test( t ) { + var opts = { + 'schema': DEFAULT_SCHEMA + }; + + lint( fixture( 'incomplete_c.md.txt' ), opts, done ); + + function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 1, 'has one error for fixture with an incomplete C section' ); + t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: usage, examples'), true, 'error identifies the missing C sections' ); + t.end(); + } +}); + +tape( 'validates valid_complete.md.txt with custom schema', function test( t ) { + var opts = { + 'schema': CUSTOM_SCHEMA + }; + + lint( fixture( 'valid_complete.md.txt' ), opts, done ); + + function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 0, 'has no errors for valid fixture with custom schema' ); + t.end(); + } +}); + +tape( 'reports correct errors with custom schema', function test( t ) { + var opts = { + 'schema': CUSTOM_SCHEMA + }; + + lint( fixture( 'missing_required_root.md.txt' ), opts, done ); + + function done( err, file ) { + if ( err ) { + t.fail( err.message ); + } + t.strictEqual( file.messages.length, 1, 'has correct number of errors with custom schema' ); + t.strictEqual( file.messages[0].reason.includes('usage') && file.messages[0].reason.includes('related'), true, 'error identifies both missing sections' ); + t.end(); + } +}); From f913da89906606e2831dd29be96d65a74eb65521 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 18 Mar 2025 08:52:11 -0400 Subject: [PATCH 02/15] docs: add missing require to example code --- 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: na - task: lint_repl_help status: na - task: lint_javascript_src status: na - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - 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: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../remark/plugins/remark-lint-expected-html-sections/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md index 6e63d5e92f8f..3c5903f745e6 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md @@ -59,6 +59,8 @@ function done( error, file ) { The plugin can be configured with a custom schema that defines required and optional sections. ```javascript +var remark = require( 'remark' ); + var opts = { 'schema': { 'root': { From 859d6086b0f4d59b88fbfa7fa622aae7c6728956 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Tue, 18 Mar 2025 09:25:48 -0400 Subject: [PATCH 03/15] build: add options validation --- 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: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../README.md | 34 ++-- .../lib/attacher.js | 90 ++++++++ .../lib/defaults.json | 28 +++ .../lib/index.js | 6 +- .../lib/linter.js | 184 +++++++++++++++++ .../lib/main.js | 192 ------------------ .../lib/validate.js | 70 +++++++ .../test/test.js | 78 ++----- .../test/test.validate.js | 125 ++++++++++++ 9 files changed, 541 insertions(+), 266 deletions(-) create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/attacher.js create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/defaults.json create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js delete mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js create mode 100644 lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md index 3c5903f745e6..6299fa2d4901 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/README.md @@ -54,31 +54,35 @@ function done( error, file ) { } ``` -#### options.schema +The plugin accepts the following `options`: -The plugin can be configured with a custom schema that defines required and optional sections. +- **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 opts = { - 'schema': { - 'root': { - 'required': [ 'usage', 'examples', 'links' ], - 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] - }, - 'c': { - 'required': [ 'usage', 'examples' ], - 'optional': [ 'intro', 'notes' ] - } +var customSchema = { + 'root': { + 'required': [ 'usage', 'examples', 'links', 'related' ], + 'optional': [ 'intro', 'notes', 'c', 'references' ] } }; +var opts = { + 'schema': customSchema +}; +remark().use( expectedSections, opts ).process( '# README', done ); -remark().use( expectedSections, opts ).process( src, done ); +function done( error, file ) { + if ( error ) { + console.error( error ); + } +} ``` -The default schema requires `usage`, `examples`, and `links` at the root level, and if a `c` section exists, it requires `usage` and `examples` subsections. -
diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/attacher.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/attacher.js new file mode 100644 index 000000000000..0c37b96f3f45 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/attacher.js @@ -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 = [ +* '
', +* '', +* '## Usage', +* '', +* '
', +* '', +* '', +* '', +* '
', +* '', +* '## 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; diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/defaults.json b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/defaults.json new file mode 100644 index 000000000000..25ff7aabaf29 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/defaults.json @@ -0,0 +1,28 @@ +{ + "schema": { + "root": { + "required": [ + "usage", + "examples", + "links" + ], + "optional": [ + "intro", + "notes", + "c", + "references", + "related" + ] + }, + "c": { + "required": [ + "usage", + "examples" + ], + "optional": [ + "intro", + "notes" + ] + } + } +} diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js index 54942d814094..95c7e880236e 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/index.js @@ -1,7 +1,7 @@ /** * @license Apache-2.0 * -* Copyright (c) 2022 The Stdlib Authors. +* 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. @@ -32,9 +32,9 @@ // MODULES // -var main = require( './main.js' ); +var attacher = require( './attacher.js' ); // EXPORTS // -module.exports = main; +module.exports = attacher; diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js new file mode 100644 index 000000000000..5f728119f85f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js @@ -0,0 +1,184 @@ +/** +* @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 logger = require( 'debug' ); +var visit = require( 'unist-util-visit' ); +var keys = require( '@stdlib/utils/keys' ); + + +// VARIABLES // + +var debug = logger( 'remark-lint-expected-html-sections' ); +var SECTION_START = //; + + +// MAIN // + +/** +* Returns a linter function. +* +* @private +* @param {Object} options - linter options +* @param {Object} [options.schema] - section schema defining required and optional sections +* @returns {Function} linter function +*/ +function factory( options ) { + return linter; + + /** + * Validates HTML section hierarchy in README files according to stdlib conventions. + * + * @private + * @param {Node} tree - abstract syntax tree (AST) + * @param {File} file - virtual file + * @returns {void} + */ + function linter( tree, file ) { + var requiredRootSections; + var requiredCSections; + var sectionStructure; + var currentSection; + var sectionsFound; + var sectionStack; + var sectionMatch; + var missingRoot; + var className; + var missingC; + var schema; + var msg; + var i; + + debug( 'Linting file: %s', file.path || '' ); + schema = options.schema; + + // Initialize section tracking: + sectionStack = []; + sectionStructure = []; + + // Keep track of sections at different levels: + sectionsFound = { + 'root': {}, + 'c': {} + }; + + // Visit all HTML nodes to build section structure: + visit( tree, 'html', visitor ); + + // Log all sections found for debugging: + debug( 'Root sections found: %j', keys( sectionsFound.root ) ); + if ( sectionsFound.root.c ) { + debug( 'C sections found: %j', keys( sectionsFound.c ) ); + } + + // After visiting all nodes, validate against schema: + requiredRootSections = schema.root.required || []; + + // Check for missing required root sections: + missingRoot = []; + for ( i = 0; i < requiredRootSections.length; i++ ) { + if ( !sectionsFound.root[ requiredRootSections[ i ] ] ) { + missingRoot.push( requiredRootSections[ i ] ); + } + } + + if ( missingRoot.length > 0 ) { + msg = 'Missing required root-level sections: `' + missingRoot.join( '`, `' ) + '`. Required sections are: `' + requiredRootSections.join('`, `') + '`. missing-required-sections'; + debug( msg ); + file.message( msg, tree ); + } + + // If 'c' section exists, check its requirements: + if ( sectionsFound.root.c ) { + requiredCSections = (schema.c) ? (schema.c.required || []) : []; + + // Check for missing required C sections: + missingC = []; + for ( i = 0; i < requiredCSections.length; i++ ) { + if ( !sectionsFound.c[ requiredCSections[ i ] ] ) { + missingC.push( requiredCSections[ i ] ); + } + } + + if ( missingC.length > 0 ) { + msg = 'Missing required sections in "c" section: `' + missingC.join('`, `') + '`. Required C sections are: `' + requiredCSections.join('`, `') + '`. missing-required-c-sections'; + debug( msg ); + file.message( msg, sectionsFound.root.c.node ); + } + } + + debug( 'Finished linting: %s', file.path || '' ); + + /** + * Callback invoked upon finding a matching node. + * + * @private + * @param {Object} node - AST node + * @returns {void} + */ + function visitor( node ) { + // Check if this is a section start tag: + sectionMatch = SECTION_START.exec( node.value ); + if ( sectionMatch ) { + className = sectionMatch[1] || ''; + + debug( 'Found section with class: %s', className ); + + // Create section data object: + currentSection = { + 'node': node, + 'name': className, + 'children': [], + 'parent': null + }; + + // Add to parent if there is one: + if ( sectionStack.length > 0 ) { + currentSection.parent = sectionStack[ sectionStack.length - 1 ]; + currentSection.parent.children.push( currentSection ); + + // Record C-level section: + if ( currentSection.parent.name === 'c' ) { + sectionsFound.c[ className ] = currentSection; + } + } else { + // This is a root section: + sectionStructure.push( currentSection ); + sectionsFound.root[ className ] = currentSection; + } + + // Push to stack: + sectionStack.push( currentSection ); + } + // Check if this is a section end tag: + else if ( /<\/section>/.test( node.value ) ) { + if ( sectionStack.length > 0 ) { + sectionStack.pop(); + } + } + } + } +} + + +// EXPORTS // + +module.exports = factory; diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js deleted file mode 100644 index 93943e9a0eee..000000000000 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/main.js +++ /dev/null @@ -1,192 +0,0 @@ -/** -* @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 logger = require( 'debug' ); -var rule = require( 'unified-lint-rule' ); -var visit = require( 'unist-util-visit' ); -var keys = require( '@stdlib/utils/keys' ); - - -// VARIABLES // - -var debug = logger( 'remark-lint-expected-html-sections' ); -var SECTION_START = //; -var DEFAULT_SCHEMA = { - 'root': { - 'required': [ 'usage', 'examples', 'links' ], - 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] - }, - 'c': { - 'required': [ 'usage', 'examples' ], - 'optional': [ 'intro', 'notes' ] - } -}; -var expectedHtmlSectionsRule; - - -// MAIN // - -/** -* Validates HTML section hierarchy in README files according to stdlib conventions. -* -* @private -* @param {Node} tree - abstract syntax tree (AST) -* @param {File} file - virtual file -* @param {Object} options - options -* @param {Object} [options.schema] - section schema defining required and optional sections -* @param {Callback} clbk - callback to invoke upon completion -* @returns {void} -*/ -function linter( tree, file, options, clbk ) { - var requiredRootSections; - var requiredCSections; - var sectionStructure; - var currentSection; - var sectionsFound; - var sectionStack; - var sectionMatch; - var missingRoot; - var className; - var missingC; - var schema; - var msg; - var i; - - debug( 'Linting file: %s', file.path || '' ); - - // Use provided schema or default schema: - schema = options.schema || DEFAULT_SCHEMA; - - // Initialize section tracking: - sectionStack = []; - sectionStructure = []; - - // Keep track of sections at different levels: - sectionsFound = { - 'root': {}, - 'c': {} - }; - - // Visit all HTML nodes to build section structure: - visit( tree, 'html', visitor ); - - // Log all sections found for debugging: - debug( 'Root sections found: %j', keys( sectionsFound.root ) ); - if ( sectionsFound.root.c ) { - debug( 'C sections found: %j', keys( sectionsFound.c ) ); - } - - // After visiting all nodes, validate against schema: - requiredRootSections = schema.root.required || []; - - // Check for missing required root sections: - missingRoot = []; - for ( i = 0; i < requiredRootSections.length; i++ ) { - if ( !sectionsFound.root[ requiredRootSections[ i ] ] ) { - missingRoot.push( requiredRootSections[ i ] ); - } - } - - if ( missingRoot.length > 0 ) { - msg = 'Missing required root-level sections: ' + missingRoot.join( ', ' ) + '. Required sections are: ' + requiredRootSections.join(', ') + '. missing-required-sections'; - debug( msg ); - file.message( msg, tree ); - } - - // If 'c' section exists, check its requirements: - if ( sectionsFound.root.c ) { - requiredCSections = (schema.c) ? (schema.c.required || []) : []; - - // Check for missing required C sections: - missingC = []; - for ( i = 0; i < requiredCSections.length; i++ ) { - if ( !sectionsFound.c[ requiredCSections[ i ] ] ) { - missingC.push( requiredCSections[ i ] ); - } - } - - if ( missingC.length > 0 ) { - msg = 'Missing required sections in "c" section: ' + missingC.join(', ') + '. Required C sections are: ' + requiredCSections.join(', ') + '. missing-required-c-sections'; - debug( msg ); - file.message( msg, sectionsFound.root.c.node ); - } - } - - debug( 'Finished linting: %s', file.path || '' ); - return clbk(); - - /** - * Callback invoked upon finding a matching node. - * - * @private - * @param {Object} node - AST node - * @returns {void} - */ - function visitor( node ) { - // Check if this is a section start tag: - sectionMatch = SECTION_START.exec( node.value ); - if ( sectionMatch ) { - className = sectionMatch[1] || ''; - - debug( 'Found section with class: %s', className ); - - // Create section data object: - currentSection = { - 'node': node, - 'name': className, - 'children': [], - 'parent': null - }; - - // Add to parent if there is one: - if ( sectionStack.length > 0 ) { - currentSection.parent = sectionStack[ sectionStack.length - 1 ]; - currentSection.parent.children.push( currentSection ); - - // Record C-level section: - if ( currentSection.parent.name === 'c' ) { - sectionsFound.c[ className ] = currentSection; - } - } else { - // This is a root section: - sectionStructure.push( currentSection ); - sectionsFound.root[ className ] = currentSection; - } - - // Push to stack: - sectionStack.push( currentSection ); - } - // Check if this is a section end tag: - else if ( /<\/section>/.test( node.value ) ) { - if ( sectionStack.length > 0 ) { - sectionStack.pop(); - } - } - } -} - -expectedHtmlSectionsRule = rule( 'remark-lint:expected-html-sections', linter ); - - -// EXPORTS // - -module.exports = expectedHtmlSectionsRule; diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js new file mode 100644 index 000000000000..a6b650f72c8b --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js @@ -0,0 +1,70 @@ +/** +* @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 isObject = require( '@stdlib/assert/is-plain-object' ); +var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var format = require( '@stdlib/string/format' ); + + +// MAIN // + +/** +* Validates function options. +* +* @private +* @param {Object} opts - destination object +* @param {Options} options - function options +* @param {Object} [options.schema] - schema for expected HTML sections +* @returns {(Error|null)} null or an error object +* +* @example +* var opts = {}; +* var options = { +* 'schema': { +* 'root': { +* 'required': [ 'usage', 'examples', 'links' ], +* 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] +* } +* } +* }; +* var err = validate( opts, options ); +* if ( err ) { +* throw err; +* } +*/ +function validate( opts, options ) { + if ( !isObject( options ) ) { + return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); + } + if ( hasOwnProp( options, 'schema' ) ) { + if ( !isObject( options.schema ) ) { + return new TypeError( format( 'invalid option. `%s` option must be an object. Option: `%s`.', 'schema', options.schema ) ); + } + opts.schema = options.schema; + } + return null; +} + + +// EXPORTS // + +module.exports = validate; diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js index 5c8733d5fd3f..8828afa28771 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js @@ -20,7 +20,7 @@ // MODULES // -var path = require( 'path' ); +var join = require( 'path' ).join; var tape = require( 'tape' ); var remark = require( 'remark' ); var readFileSync = require( '@stdlib/fs/read-file' ).sync; @@ -29,32 +29,23 @@ var expectedSections = require( './../lib' ); // VARIABLES // -var TEST_FIXTURES = path.join( __dirname, 'fixtures' ); -var DEFAULT_SCHEMA = { - 'root': { - 'required': [ 'usage', 'examples', 'links' ], - 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] - }, - 'c': { - 'required': [ 'usage', 'examples' ], - 'optional': [ 'intro', 'notes' ] - } -}; +var TEST_FIXTURES = join( __dirname, 'fixtures' ); var CUSTOM_SCHEMA = { 'root': { 'required': [ 'usage', 'examples', 'links', 'related' ], 'optional': [ 'intro', 'notes', 'c', 'references' ] } }; +var FIXTURE_VALID_COMPLETE = readFileSync( join( TEST_FIXTURES, 'valid_complete.md.txt' ), 'utf8' ).toString(); +var FIXTURE_VALID_MEMOIZE = readFileSync( join( TEST_FIXTURES, 'valid_memoize.md.txt' ), 'utf8' ).toString(); +var FIXTURE_VALID_ASIN = readFileSync( join( TEST_FIXTURES, 'valid_asin.md.txt' ), 'utf8' ).toString(); +var FIXTURE_INVALID_ASIN = readFileSync( join( TEST_FIXTURES, 'invalid_asin.md.txt' ), 'utf8' ).toString(); +var FIXTURE_MISSING_ROOT = readFileSync( join( TEST_FIXTURES, 'missing_required_root.md.txt' ), 'utf8' ).toString(); +var FIXTURE_INCOMPLETE_C = readFileSync( join( TEST_FIXTURES, 'incomplete_c.md.txt' ), 'utf8' ).toString(); // FUNCTIONS // -function fixture( name ) { - var text = readFileSync( path.join( TEST_FIXTURES, name ), 'utf8' ); - return text.toString(); -} - function lint( text, options, clbk ) { remark().use( expectedSections, options ).process( text, clbk ); } @@ -69,11 +60,7 @@ tape( 'main export is a function', function test( t ) { }); tape( 'validates valid_complete.md.txt with default schema', function test( t ) { - var opts = { - 'schema': DEFAULT_SCHEMA - }; - - lint( fixture( 'valid_complete.md.txt' ), opts, done ); + lint( FIXTURE_VALID_COMPLETE, {}, done ); function done( err, file ) { if ( err ) { @@ -85,11 +72,7 @@ tape( 'validates valid_complete.md.txt with default schema', function test( t ) }); tape( 'validates valid_memoize.md.txt', function test( t ) { - var opts = { - 'schema': DEFAULT_SCHEMA - }; - - lint( fixture( 'valid_memoize.md.txt' ), opts, done ); + lint( FIXTURE_VALID_MEMOIZE, {}, done ); function done( err, file ) { if ( err ) { @@ -101,11 +84,7 @@ tape( 'validates valid_memoize.md.txt', function test( t ) { }); tape( 'validates valid_asin.md.txt with C section', function test( t ) { - var opts = { - 'schema': DEFAULT_SCHEMA - }; - - lint( fixture( 'valid_asin.md.txt' ), opts, done ); + lint( FIXTURE_VALID_ASIN, {}, done ); function done( err, file ) { if ( err ) { @@ -117,60 +96,48 @@ tape( 'validates valid_asin.md.txt with C section', function test( t ) { }); tape( 'detects missing C usage section in invalid_asin.md.txt', function test( t ) { - var opts = { - 'schema': DEFAULT_SCHEMA - }; - - lint( fixture( 'invalid_asin.md.txt' ), opts, done ); + lint( FIXTURE_INVALID_ASIN, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } t.strictEqual( file.messages.length, 1, 'has one error for invalid asin fixture' ); - t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: usage'), true, 'error identifies the missing C usage section' ); + t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: `usage`'), true, 'error identifies the missing C usage section' ); t.end(); } }); tape( 'reports error for missing required root sections', function test( t ) { - var opts = { - 'schema': DEFAULT_SCHEMA - }; + lint( FIXTURE_MISSING_ROOT, {}, done ); - lint( fixture( 'missing_required_root.md.txt' ), opts, function done( err, file ) { + function done( err, file ) { if ( err ) { t.fail( err.message ); } t.strictEqual( file.messages.length, 1, 'has one error for fixture missing a required root section' ); - t.strictEqual( file.messages[0].reason.includes('Missing required root-level sections: usage'), true, 'error identifies the missing section' ); + t.strictEqual( file.messages[0].reason.includes('Missing required root-level sections: `usage`'), true, 'error identifies the missing section' ); t.end(); - }); + } }); tape( 'reports error for incomplete C section', function test( t ) { - var opts = { - 'schema': DEFAULT_SCHEMA - }; - - lint( fixture( 'incomplete_c.md.txt' ), opts, done ); + lint( FIXTURE_INCOMPLETE_C, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } t.strictEqual( file.messages.length, 1, 'has one error for fixture with an incomplete C section' ); - t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: usage, examples'), true, 'error identifies the missing C sections' ); + t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: `usage`, `examples`'), true, 'error identifies the missing C sections' ); t.end(); } }); tape( 'validates valid_complete.md.txt with custom schema', function test( t ) { - var opts = { + lint( FIXTURE_VALID_COMPLETE, { 'schema': CUSTOM_SCHEMA - }; - - lint( fixture( 'valid_complete.md.txt' ), opts, done ); + }, done ); function done( err, file ) { if ( err ) { @@ -185,8 +152,7 @@ tape( 'reports correct errors with custom schema', function test( t ) { var opts = { 'schema': CUSTOM_SCHEMA }; - - lint( fixture( 'missing_required_root.md.txt' ), opts, done ); + lint( FIXTURE_MISSING_ROOT, opts, done ); function done( err, file ) { if ( err ) { diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js new file mode 100644 index 000000000000..a9eaa564b28f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js @@ -0,0 +1,125 @@ +/** +* @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 tape = require( 'tape' ); +var validate = require( './../lib/validate.js' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof validate, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function returns an error if provided an `options` argument which is not an object', function test( t ) { + var values; + var err; + var i; + + values = [ + '5', + 5, + true, + false, + void 0, + null, + NaN, + [], + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + err = validate( {}, values[ i ] ); + t.strictEqual( err instanceof TypeError, true, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns an error if provided a `schema` option which is not an object', function test( t ) { + var values; + var opts; + var err; + var i; + + values = [ + '5', + 5, + true, + false, + void 0, + null, + NaN, + [], + function noop() {} + ]; + + for ( i = 0; i < values.length; i++ ) { + opts = { + 'schema': values[ i ] + }; + err = validate( {}, opts ); + t.strictEqual( err instanceof TypeError, true, 'returns a type error when provided '+values[i] ); + } + t.end(); +}); + +tape( 'the function returns `null` if all options are valid', function test( t ) { + var options; + var opts; + var err; + + opts = {}; + options = { + 'schema': { + 'root': { + 'required': [ 'usage', 'examples', 'links' ], + 'optional': [ 'intro', 'notes', 'c', 'references', 'related' ] + } + } + }; + + err = validate( opts, options ); + t.strictEqual( err, null, 'returns null' ); + t.deepEqual( opts, options, 'sets options' ); + + t.end(); +}); + +tape( 'the function will ignore unrecognized options', function test( t ) { + var options; + var opts; + var err; + + opts = {}; + options = { + 'beep': true, + 'boop': 'bop' + }; + + err = validate( opts, options ); + t.strictEqual( err, null, 'returns null' ); + t.deepEqual( opts, {}, 'ignores unrecognized options' ); + + t.end(); +}); From 69736740fc742dc3803d5882254d75bc101f9c0c Mon Sep 17 00:00:00 2001 From: stdlib-bot <82920195+stdlib-bot@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:43:47 +0000 Subject: [PATCH 04/15] chore: update copyright years --- .../test/fixtures/invalid_asin.md.txt | 2 +- .../test/fixtures/valid_asin.md.txt | 2 +- .../test/fixtures/valid_memoize.md.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt index 5f5f6ea628c9..f18826250dda 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/invalid_asin.md.txt @@ -2,7 +2,7 @@ @license Apache-2.0 -Copyright (c) 2022 The Stdlib Authors. +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. diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt index 357094ff23af..4c73653603f0 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_asin.md.txt @@ -2,7 +2,7 @@ @license Apache-2.0 -Copyright (c) 2022 The Stdlib Authors. +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. diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt index 6cf2726f3838..93357d7864a0 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/fixtures/valid_memoize.md.txt @@ -2,7 +2,7 @@ @license Apache-2.0 -Copyright (c) 2018 The Stdlib Authors. +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. From 0f608753b38cfb6776216b852efa2832be3a4871 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Sun, 20 Apr 2025 17:15:02 -0400 Subject: [PATCH 05/15] chore: move declarations to visitor function and other clean-up --- 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: na - 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: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../lib/linter.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js index 5f728119f85f..982ef61ddfda 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js @@ -28,7 +28,8 @@ var keys = require( '@stdlib/utils/keys' ); // VARIABLES // var debug = logger( 'remark-lint-expected-html-sections' ); -var SECTION_START = //; +var RE_SECTION_START = //; +var RE_SECTION_END = /<\/section>/; // MAIN // @@ -56,12 +57,9 @@ function factory( options ) { var requiredRootSections; var requiredCSections; var sectionStructure; - var currentSection; var sectionsFound; var sectionStack; - var sectionMatch; var missingRoot; - var className; var missingC; var schema; var msg; @@ -135,10 +133,14 @@ function factory( options ) { * @returns {void} */ function visitor( node ) { + var currentSection; + var sectionMatch; + var className; + // Check if this is a section start tag: - sectionMatch = SECTION_START.exec( node.value ); + sectionMatch = RE_SECTION_START.exec( node.value ); if ( sectionMatch ) { - className = sectionMatch[1] || ''; + className = sectionMatch[ 1 ] || ''; debug( 'Found section with class: %s', className ); @@ -169,7 +171,7 @@ function factory( options ) { sectionStack.push( currentSection ); } // Check if this is a section end tag: - else if ( /<\/section>/.test( node.value ) ) { + else if ( RE_SECTION_END.test( node.value ) ) { if ( sectionStack.length > 0 ) { sectionStack.pop(); } From 5e72ab344c79cedc0c7730cc70a6b297b71c09f5 Mon Sep 17 00:00:00 2001 From: Athan Date: Tue, 22 Apr 2025 20:58:15 -0700 Subject: [PATCH 06/15] Apply suggestions from code review Signed-off-by: Athan --- .../remark-lint-expected-html-sections/lib/linter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js index 982ef61ddfda..d79768b87da0 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js @@ -99,14 +99,14 @@ function factory( options ) { } if ( missingRoot.length > 0 ) { - msg = 'Missing required root-level sections: `' + missingRoot.join( '`, `' ) + '`. Required sections are: `' + requiredRootSections.join('`, `') + '`. missing-required-sections'; + msg = 'Missing required root-level sections: `' + missingRoot.join( '`, `' ) + '`. Required sections are: `' + requiredRootSections.join( '`, `' ) + '`. missing-required-sections'; debug( msg ); file.message( msg, tree ); } // If 'c' section exists, check its requirements: if ( sectionsFound.root.c ) { - requiredCSections = (schema.c) ? (schema.c.required || []) : []; + requiredCSections = ( schema.c ) ? ( schema.c.required || [] ) : []; // Check for missing required C sections: missingC = []; @@ -117,7 +117,7 @@ function factory( options ) { } if ( missingC.length > 0 ) { - msg = 'Missing required sections in "c" section: `' + missingC.join('`, `') + '`. Required C sections are: `' + requiredCSections.join('`, `') + '`. missing-required-c-sections'; + msg = 'Missing required sections in "c" section: `' + missingC.join( '`, `' ) + '`. Required C sections are: `' + requiredCSections.join( '`, `' ) + '`. missing-required-c-sections'; debug( msg ); file.message( msg, sectionsFound.root.c.node ); } From d9b5da1eea11c95cecdc2780faf6eec6d18d695c Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 24 Apr 2025 18:17:30 -0400 Subject: [PATCH 07/15] chore: apply suggestions from code review Co-authored-by: Athan Signed-off-by: Philipp Burckhardt --- .../remark-lint-expected-html-sections/lib/linter.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js index d79768b87da0..14f247773256 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/linter.js @@ -171,10 +171,8 @@ function factory( options ) { sectionStack.push( currentSection ); } // Check if this is a section end tag: - else if ( RE_SECTION_END.test( node.value ) ) { - if ( sectionStack.length > 0 ) { - sectionStack.pop(); - } + else if ( RE_SECTION_END.test( node.value ) && sectionStack.length > 0 ) { + sectionStack.pop(); } } } From 15f47541440f2621284e8647139dd6d5d367cbcf Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Thu, 24 Apr 2025 18:31:50 -0400 Subject: [PATCH 08/15] chore: add validation of schema --- 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: na - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../lib/validate.js | 72 ++++++++++++++++ .../test/test.js | 9 +- .../test/test.validate.js | 82 +++++++++++++++++++ 3 files changed, 159 insertions(+), 4 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js index a6b650f72c8b..269976c2c8b3 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js @@ -20,11 +20,66 @@ // MODULES // +var objectKeys = require( '@stdlib/utils/keys' ); var isObject = require( '@stdlib/assert/is-plain-object' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); +var isArray = require( '@stdlib/assert/is-array' ); +var isString = require( '@stdlib/assert/is-string' ); var format = require( '@stdlib/string/format' ); +// FUNCTIONS // + +/** +* Validates a section schema configuration. +* +* @private +* @param {Object} schema - schema section to validate +* @param {string} section - section name +* @returns {(Error|null)} null or an error object +*/ +function validateSection( schema, section ) { + var i; + var j; + + if ( !isObject( schema ) ) { + return new TypeError( format( 'invalid option. `%s` schema section must be an object. Value: `%s`.', section, schema ) ); + } + + // Check if 'required' exists and is an array of strings + if ( hasOwnProp( schema, 'required' ) ) { + if ( !isArray( schema.required ) ) { + return new TypeError( format( 'invalid option. `%s.required` must be an array. Value: `%s`.', section, schema.required ) ); + } + // Validate each element in the 'required' array is a string + for ( i = 0; i < schema.required.length; i++ ) { + if ( !isString( schema.required[ i ] ) ) { + return new TypeError( format( 'invalid option. `%s.required` must only contain strings. Found: `%s`.', section, schema.required[ i ] ) ); + } + } + } else { + return new TypeError( format( 'invalid option. `%s` schema section must have a `required` property.', section ) ); + } + + // Check if 'optional' exists and is an array of strings + if ( hasOwnProp( schema, 'optional' ) ) { + if ( !isArray( schema.optional ) ) { + return new TypeError( format( 'invalid option. `%s.optional` must be an array. Value: `%s`.', section, schema.optional ) ); + } + // Validate each element in the 'optional' array is a string + for ( j = 0; j < schema.optional.length; j++ ) { + if ( !isString( schema.optional[ j ] ) ) { + return new TypeError( format( 'invalid option. `%s.optional` must only contain strings. Found: `%s`.', section, schema.optional[ j ] ) ); + } + } + } else { + return new TypeError( format( 'invalid option. `%s` schema section must have an `optional` property.', section ) ); + } + + return null; +} + + // MAIN // /** @@ -52,6 +107,11 @@ var format = require( '@stdlib/string/format' ); * } */ function validate( opts, options ) { + var section; + var keys; + var err; + var i; + if ( !isObject( options ) ) { return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); } @@ -59,8 +119,20 @@ function validate( opts, options ) { if ( !isObject( options.schema ) ) { return new TypeError( format( 'invalid option. `%s` option must be an object. Option: `%s`.', 'schema', options.schema ) ); } + if ( !hasOwnProp( options.schema, 'root' ) ) { + return new TypeError( format( 'invalid option. Schema must contain a `root` section.' ) ); + } + keys = objectKeys( options.schema ); + for ( i = 0; i < keys.length; i++ ) { + section = keys[ i ]; + err = validateSection( options.schema[ section ], section ); + if ( err ) { + return err; + } + } opts.schema = options.schema; } + return null; } diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js index 8828afa28771..e5a0481bb6b5 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js @@ -24,6 +24,7 @@ var join = require( 'path' ).join; var tape = require( 'tape' ); var remark = require( 'remark' ); var readFileSync = require( '@stdlib/fs/read-file' ).sync; +var contains = require( '@stdlib/assert/contains' ); var expectedSections = require( './../lib' ); @@ -103,7 +104,7 @@ tape( 'detects missing C usage section in invalid_asin.md.txt', function test( t t.fail( err.message ); } t.strictEqual( file.messages.length, 1, 'has one error for invalid asin fixture' ); - t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: `usage`'), true, 'error identifies the missing C usage section' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required sections in "c" section: `usage`' ), true, 'error identifies the missing C usage section' ); t.end(); } }); @@ -116,7 +117,7 @@ tape( 'reports error for missing required root sections', function test( t ) { t.fail( err.message ); } t.strictEqual( file.messages.length, 1, 'has one error for fixture missing a required root section' ); - t.strictEqual( file.messages[0].reason.includes('Missing required root-level sections: `usage`'), true, 'error identifies the missing section' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required root-level sections: `usage`' ), true, 'error identifies the missing section' ); t.end(); } }); @@ -129,7 +130,7 @@ tape( 'reports error for incomplete C section', function test( t ) { t.fail( err.message ); } t.strictEqual( file.messages.length, 1, 'has one error for fixture with an incomplete C section' ); - t.strictEqual( file.messages[0].reason.includes('Missing required sections in "c" section: `usage`, `examples`'), true, 'error identifies the missing C sections' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required sections in "c" section: `usage`, `examples`' ), true, 'error identifies the missing C sections' ); t.end(); } }); @@ -159,7 +160,7 @@ tape( 'reports correct errors with custom schema', function test( t ) { t.fail( err.message ); } t.strictEqual( file.messages.length, 1, 'has correct number of errors with custom schema' ); - t.strictEqual( file.messages[0].reason.includes('usage') && file.messages[0].reason.includes('related'), true, 'error identifies both missing sections' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'usage' ) && contains( file.messages[ 0 ].reason, 'related' ), true, 'error identifies both missing sections' ); t.end(); } }); diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js index a9eaa564b28f..86c8b4a1ec87 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.validate.js @@ -84,6 +84,88 @@ tape( 'the function returns an error if provided a `schema` option which is not t.end(); }); +tape( 'the function returns an error if provided a schema without a root section', function test( t ) { + var options; + var opts; + var err; + + opts = {}; + options = { + 'schema': { + 'section': { + 'required': [ 'usage' ], + 'optional': [ 'notes' ] + } + } + }; + + err = validate( opts, options ); + t.strictEqual( err instanceof TypeError, true, 'returns a type error' ); + + t.end(); +}); + +tape( 'the function returns an error if provided a schema with incorrect structure', function test( t ) { + var options; + var opts; + var err; + + opts = {}; + options = { + 'schema': { + 'root': { + 'required': 'usage', // Should be an array + 'optional': [ 'notes' ] + } + } + }; + + err = validate( opts, options ); + t.strictEqual( err instanceof TypeError, true, 'returns a type error' ); + + t.end(); +}); + +tape( 'the function returns an error if a schema section is missing the required property', function test( t ) { + var options; + var opts; + var err; + + opts = {}; + options = { + 'schema': { + 'root': { + 'optional': [ 'notes' ] + } + } + }; + + err = validate( opts, options ); + t.strictEqual( err instanceof TypeError, true, 'returns a type error' ); + + t.end(); +}); + +tape( 'the function returns an error if a schema section is missing the optional property', function test( t ) { + var options; + var opts; + var err; + + opts = {}; + options = { + 'schema': { + 'root': { + 'required': [ 'usage' ] + } + } + }; + + err = validate( opts, options ); + t.strictEqual( err instanceof TypeError, true, 'returns a type error' ); + + t.end(); +}); + tape( 'the function returns `null` if all options are valid', function test( t ) { var options; var opts; From 8ca09412b9d04fc932bef09f432e74b6ae9161d8 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 24 Apr 2025 16:16:12 -0700 Subject: [PATCH 09/15] Apply suggestions from code review Signed-off-by: Athan --- .../remark-lint-expected-html-sections/lib/validate.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js index 269976c2c8b3..609689c170c1 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js @@ -45,13 +45,10 @@ function validateSection( schema, section ) { if ( !isObject( schema ) ) { return new TypeError( format( 'invalid option. `%s` schema section must be an object. Value: `%s`.', section, schema ) ); } - - // Check if 'required' exists and is an array of strings if ( hasOwnProp( schema, 'required' ) ) { if ( !isArray( schema.required ) ) { return new TypeError( format( 'invalid option. `%s.required` must be an array. Value: `%s`.', section, schema.required ) ); } - // Validate each element in the 'required' array is a string for ( i = 0; i < schema.required.length; i++ ) { if ( !isString( schema.required[ i ] ) ) { return new TypeError( format( 'invalid option. `%s.required` must only contain strings. Found: `%s`.', section, schema.required[ i ] ) ); @@ -60,13 +57,10 @@ function validateSection( schema, section ) { } else { return new TypeError( format( 'invalid option. `%s` schema section must have a `required` property.', section ) ); } - - // Check if 'optional' exists and is an array of strings if ( hasOwnProp( schema, 'optional' ) ) { if ( !isArray( schema.optional ) ) { return new TypeError( format( 'invalid option. `%s.optional` must be an array. Value: `%s`.', section, schema.optional ) ); } - // Validate each element in the 'optional' array is a string for ( j = 0; j < schema.optional.length; j++ ) { if ( !isString( schema.optional[ j ] ) ) { return new TypeError( format( 'invalid option. `%s.optional` must only contain strings. Found: `%s`.', section, schema.optional[ j ] ) ); @@ -132,7 +126,6 @@ function validate( opts, options ) { } opts.schema = options.schema; } - return null; } From 8a6abb976a256a0504c8eea2655f4ac2d2effb00 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 24 Apr 2025 16:16:39 -0700 Subject: [PATCH 10/15] docs: remove empty line Signed-off-by: Athan --- .../plugins/remark-lint-expected-html-sections/lib/validate.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js index 609689c170c1..a48ec293901e 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js @@ -69,7 +69,6 @@ function validateSection( schema, section ) { } else { return new TypeError( format( 'invalid option. `%s` schema section must have an `optional` property.', section ) ); } - return null; } From 8ac5b4b03c3b716ac364ed67cd67549846bdeaea Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 24 Apr 2025 16:17:14 -0700 Subject: [PATCH 11/15] refactor: restrict allowed values to primitives Signed-off-by: Athan --- .../plugins/remark-lint-expected-html-sections/lib/validate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js index a48ec293901e..061c9d556eff 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js @@ -24,7 +24,7 @@ var objectKeys = require( '@stdlib/utils/keys' ); var isObject = require( '@stdlib/assert/is-plain-object' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var isArray = require( '@stdlib/assert/is-array' ); -var isString = require( '@stdlib/assert/is-string' ); +var isString = require( '@stdlib/assert/is-string' ).isPrimitive; var format = require( '@stdlib/string/format' ); From 08367319a1e894ee8cea0a1924c14f178cbd5c0e Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 24 Apr 2025 16:27:52 -0700 Subject: [PATCH 12/15] chore: clean-up Signed-off-by: Athan --- .../lib/validate.js | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js index 061c9d556eff..087ca47ec565 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js @@ -25,6 +25,7 @@ var isObject = require( '@stdlib/assert/is-plain-object' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var isArray = require( '@stdlib/assert/is-array' ); var isString = require( '@stdlib/assert/is-string' ).isPrimitive; +var join = require( '@stdlib/array/base/join' ); var format = require( '@stdlib/string/format' ); @@ -40,34 +41,32 @@ var format = require( '@stdlib/string/format' ); */ function validateSection( schema, section ) { var i; - var j; - if ( !isObject( schema ) ) { return new TypeError( format( 'invalid option. `%s` schema section must be an object. Value: `%s`.', section, schema ) ); } if ( hasOwnProp( schema, 'required' ) ) { if ( !isArray( schema.required ) ) { - return new TypeError( format( 'invalid option. `%s.required` must be an array. Value: `%s`.', section, schema.required ) ); + return new TypeError( format( 'invalid option. `%s` must be an array. Value: `%s`.', section+'.required', schema.required ) ); } for ( i = 0; i < schema.required.length; i++ ) { if ( !isString( schema.required[ i ] ) ) { - return new TypeError( format( 'invalid option. `%s.required` must only contain strings. Found: `%s`.', section, schema.required[ i ] ) ); + return new TypeError( format( 'invalid option. `%s` must only contain strings. Index: `%u`. Value: `%s`.', section+'.required', i, schema.required[ i ] ) ); } } } else { - return new TypeError( format( 'invalid option. `%s` schema section must have a `required` property.', section ) ); + return new TypeError( format( 'invalid option. `%s` schema section must have %s `%s` property.', section, 'a', 'required' ) ); } if ( hasOwnProp( schema, 'optional' ) ) { if ( !isArray( schema.optional ) ) { - return new TypeError( format( 'invalid option. `%s.optional` must be an array. Value: `%s`.', section, schema.optional ) ); + return new TypeError( format( 'invalid option. `%s` must be an array. Value: `%s`.', section+'.optional', schema.optional ) ); } - for ( j = 0; j < schema.optional.length; j++ ) { - if ( !isString( schema.optional[ j ] ) ) { - return new TypeError( format( 'invalid option. `%s.optional` must only contain strings. Found: `%s`.', section, schema.optional[ j ] ) ); + for ( i = 0; i < schema.optional.length; i++ ) { + if ( !isString( schema.optional[ i ] ) ) { + return new TypeError( format( 'invalid option. `%s` must only contain strings. Index: `%u`. Value: `%s`.', section+'.optional', i, schema.optional[ i ] ) ); } } } else { - return new TypeError( format( 'invalid option. `%s` schema section must have an `optional` property.', section ) ); + return new TypeError( format( 'invalid option. `%s` schema section must have %s `%s` property.', section, 'an', 'optional' ) ); } return null; } @@ -113,7 +112,7 @@ function validate( opts, options ) { return new TypeError( format( 'invalid option. `%s` option must be an object. Option: `%s`.', 'schema', options.schema ) ); } if ( !hasOwnProp( options.schema, 'root' ) ) { - return new TypeError( format( 'invalid option. Schema must contain a `root` section.' ) ); + return new TypeError( format( 'invalid option. `%s` option must have %s `%s` property.', 'schema', 'a', 'root' ) ); } keys = objectKeys( options.schema ); for ( i = 0; i < keys.length; i++ ) { From 06b8d7076de2e28ecc469643037ea4d8672dc81e Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 24 Apr 2025 16:36:52 -0700 Subject: [PATCH 13/15] chore: clean-up Signed-off-by: Athan --- .../test/test.js | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js index e5a0481bb6b5..c007a0163152 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js @@ -60,82 +60,82 @@ tape( 'main export is a function', function test( t ) { t.end(); }); -tape( 'validates valid_complete.md.txt with default schema', function test( t ) { +tape( 'the function validates a valid Markdown file containing all sections (default schema)', function test( t ) { lint( FIXTURE_VALID_COMPLETE, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 0, 'has no errors for valid fixture with all sections' ); + t.strictEqual( file.messages.length, 0, 'returns expected value' ); t.end(); } }); -tape( 'validates valid_memoize.md.txt', function test( t ) { +tape( 'the function validates a valid Markdown file which does not contain C sections (default schema)', function test( t ) { lint( FIXTURE_VALID_MEMOIZE, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 0, 'has no errors for memoize README fixture' ); + t.strictEqual( file.messages.length, 0, 'returns expected value' ); t.end(); } }); -tape( 'validates valid_asin.md.txt with C section', function test( t ) { +tape( 'the function validates a valid Markdown file containing C sections (default schema)', function test( t ) { lint( FIXTURE_VALID_ASIN, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 0, 'has no errors for asin README fixture with C section' ); + t.strictEqual( file.messages.length, 0, 'returns expected value' ); t.end(); } }); -tape( 'detects missing C usage section in invalid_asin.md.txt', function test( t ) { +tape( 'the function validates an invalid Markdown file missing a C section (default schema)', function test( t ) { lint( FIXTURE_INVALID_ASIN, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 1, 'has one error for invalid asin fixture' ); - t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required sections in "c" section: `usage`' ), true, 'error identifies the missing C usage section' ); + t.strictEqual( file.messages.length, 1, 'returns expected value' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required sections in "c" section: `usage`' ), true, 'returns expected value' ); t.end(); } }); -tape( 'reports error for missing required root sections', function test( t ) { +tape( 'the function validates an invalid Markdown file missing required root sections (default schema)', function test( t ) { lint( FIXTURE_MISSING_ROOT, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 1, 'has one error for fixture missing a required root section' ); - t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required root-level sections: `usage`' ), true, 'error identifies the missing section' ); + t.strictEqual( file.messages.length, 1, 'returns expected value' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required root-level sections: `usage`' ), true, 'returns expected value' ); t.end(); } }); -tape( 'reports error for incomplete C section', function test( t ) { +tape( 'the function validates an invalid Markdown file having an incomplete C section (default schema)', function test( t ) { lint( FIXTURE_INCOMPLETE_C, {}, done ); function done( err, file ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 1, 'has one error for fixture with an incomplete C section' ); - t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required sections in "c" section: `usage`, `examples`' ), true, 'error identifies the missing C sections' ); + t.strictEqual( file.messages.length, 1, 'returns expected value' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'Missing required sections in "c" section: `usage`, `examples`' ), true, 'returns expected value' ); t.end(); } }); -tape( 'validates valid_complete.md.txt with custom schema', function test( t ) { +tape( 'the function validates a valid Markdown file containing all sections using a custom schema', function test( t ) { lint( FIXTURE_VALID_COMPLETE, { 'schema': CUSTOM_SCHEMA }, done ); @@ -144,12 +144,12 @@ tape( 'validates valid_complete.md.txt with custom schema', function test( t ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 0, 'has no errors for valid fixture with custom schema' ); + t.strictEqual( file.messages.length, 0, 'returns expected value' ); t.end(); } }); -tape( 'reports correct errors with custom schema', function test( t ) { +tape( 'the function validates an invalid Markdown file containing a missing root section using a custom schema', function test( t ) { var opts = { 'schema': CUSTOM_SCHEMA }; @@ -159,8 +159,8 @@ tape( 'reports correct errors with custom schema', function test( t ) { if ( err ) { t.fail( err.message ); } - t.strictEqual( file.messages.length, 1, 'has correct number of errors with custom schema' ); - t.strictEqual( contains( file.messages[ 0 ].reason, 'usage' ) && contains( file.messages[ 0 ].reason, 'related' ), true, 'error identifies both missing sections' ); + t.strictEqual( file.messages.length, 1, 'returns expected value' ); + t.strictEqual( contains( file.messages[ 0 ].reason, 'usage' ) && contains( file.messages[ 0 ].reason, 'related' ), true, 'returns expected value' ); t.end(); } }); From 7ab18e64369039714f48b6a85d9b54ebc839fb6a Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 24 Apr 2025 16:38:33 -0700 Subject: [PATCH 14/15] chore: clean-up Signed-off-by: Athan --- .../test/test.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js index c007a0163152..8b68056d25e3 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/test/test.js @@ -37,12 +37,15 @@ var CUSTOM_SCHEMA = { 'optional': [ 'intro', 'notes', 'c', 'references' ] } }; -var FIXTURE_VALID_COMPLETE = readFileSync( join( TEST_FIXTURES, 'valid_complete.md.txt' ), 'utf8' ).toString(); -var FIXTURE_VALID_MEMOIZE = readFileSync( join( TEST_FIXTURES, 'valid_memoize.md.txt' ), 'utf8' ).toString(); -var FIXTURE_VALID_ASIN = readFileSync( join( TEST_FIXTURES, 'valid_asin.md.txt' ), 'utf8' ).toString(); -var FIXTURE_INVALID_ASIN = readFileSync( join( TEST_FIXTURES, 'invalid_asin.md.txt' ), 'utf8' ).toString(); -var FIXTURE_MISSING_ROOT = readFileSync( join( TEST_FIXTURES, 'missing_required_root.md.txt' ), 'utf8' ).toString(); -var FIXTURE_INCOMPLETE_C = readFileSync( join( TEST_FIXTURES, 'incomplete_c.md.txt' ), 'utf8' ).toString(); +var opts = { + 'encoding': 'utf8' +}; +var FIXTURE_VALID_COMPLETE = readFileSync( join( TEST_FIXTURES, 'valid_complete.md.txt' ), opts ); +var FIXTURE_VALID_MEMOIZE = readFileSync( join( TEST_FIXTURES, 'valid_memoize.md.txt' ), opts ); +var FIXTURE_VALID_ASIN = readFileSync( join( TEST_FIXTURES, 'valid_asin.md.txt' ), opts ); +var FIXTURE_INVALID_ASIN = readFileSync( join( TEST_FIXTURES, 'invalid_asin.md.txt' ), opts ); +var FIXTURE_MISSING_ROOT = readFileSync( join( TEST_FIXTURES, 'missing_required_root.md.txt' ), opts ); +var FIXTURE_INCOMPLETE_C = readFileSync( join( TEST_FIXTURES, 'incomplete_c.md.txt' ), opts ); // FUNCTIONS // From 7cbbfebabd1eb862590cd801b1a260b2298d045a Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 24 Apr 2025 17:03:53 -0700 Subject: [PATCH 15/15] refactor: consolidate error messages Signed-off-by: Athan --- .../lib/validate.js | 75 ++++++++----------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js index 087ca47ec565..e00e808c013e 100644 --- a/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js +++ b/lib/node_modules/@stdlib/_tools/remark/plugins/remark-lint-expected-html-sections/lib/validate.js @@ -21,52 +21,49 @@ // MODULES // var objectKeys = require( '@stdlib/utils/keys' ); -var isObject = require( '@stdlib/assert/is-plain-object' ); +var isPlainObject = require( '@stdlib/assert/is-plain-object' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); -var isArray = require( '@stdlib/assert/is-array' ); -var isString = require( '@stdlib/assert/is-string' ).isPrimitive; -var join = require( '@stdlib/array/base/join' ); +var isStringArray = require( '@stdlib/assert/is-string-array' ).primitives; +var isEmptyArray = require( '@stdlib/assert/is-empty-array' ); var format = require( '@stdlib/string/format' ); // FUNCTIONS // /** -* Validates a section schema configuration. +* Validates a schema object. * * @private -* @param {Object} schema - schema section to validate -* @param {string} section - section name +* @param {Object} schema - schema object * @returns {(Error|null)} null or an error object */ -function validateSection( schema, section ) { +function validateSchema( schema ) { + var section; + var keys; + var obj; var i; - if ( !isObject( schema ) ) { - return new TypeError( format( 'invalid option. `%s` schema section must be an object. Value: `%s`.', section, schema ) ); - } - if ( hasOwnProp( schema, 'required' ) ) { - if ( !isArray( schema.required ) ) { - return new TypeError( format( 'invalid option. `%s` must be an array. Value: `%s`.', section+'.required', schema.required ) ); + + keys = objectKeys( schema ); + for ( i = 0; i < keys.length; i++ ) { + section = keys[ i ]; + obj = schema[ section ]; + if ( !isPlainObject( obj ) ) { + return new TypeError( format( 'invalid option. `%s` option must be an object containing properties having values which are objects. Option: `%s`.', 'schema', JSON.stringify( obj ) ) ); } - for ( i = 0; i < schema.required.length; i++ ) { - if ( !isString( schema.required[ i ] ) ) { - return new TypeError( format( 'invalid option. `%s` must only contain strings. Index: `%u`. Value: `%s`.', section+'.required', i, schema.required[ i ] ) ); + if ( hasOwnProp( obj, 'required' ) ) { + if ( !isStringArray( obj.required ) && !isEmptyArray( obj.required ) ) { + return new TypeError( format( 'invalid option. `%s` option must be an object having %s `%s` property which is an array of strings. Option: `%s`.', 'schema', 'a', section+'.required', JSON.stringify( obj ) ) ); } + } else { + return new TypeError( format( 'invalid option. `%s` option must be an object having %s `%s` property which is an array of strings. Option: `%s`.', 'schema', 'a', section+'.required', JSON.stringify( obj ) ) ); } - } else { - return new TypeError( format( 'invalid option. `%s` schema section must have %s `%s` property.', section, 'a', 'required' ) ); - } - if ( hasOwnProp( schema, 'optional' ) ) { - if ( !isArray( schema.optional ) ) { - return new TypeError( format( 'invalid option. `%s` must be an array. Value: `%s`.', section+'.optional', schema.optional ) ); - } - for ( i = 0; i < schema.optional.length; i++ ) { - if ( !isString( schema.optional[ i ] ) ) { - return new TypeError( format( 'invalid option. `%s` must only contain strings. Index: `%u`. Value: `%s`.', section+'.optional', i, schema.optional[ i ] ) ); + if ( hasOwnProp( obj, 'optional' ) ) { + if ( !isStringArray( obj.optional ) && !isEmptyArray( obj.optional ) ) { + return new TypeError( format( 'invalid option. `%s` option must be an object having %s `%s` property which is an array of strings. Option: `%s`.', 'schema', 'a', section+'.optional', JSON.stringify( obj ) ) ); } + } else { + return new TypeError( format( 'invalid option. `%s` option must be an object having %s `%s` property which is an array of strings. Option: `%s`.', 'schema', 'a', section+'.optional', JSON.stringify( obj ) ) ); } - } else { - return new TypeError( format( 'invalid option. `%s` schema section must have %s `%s` property.', section, 'an', 'optional' ) ); } return null; } @@ -99,29 +96,21 @@ function validateSection( schema, section ) { * } */ function validate( opts, options ) { - var section; - var keys; var err; - var i; - - if ( !isObject( options ) ) { + if ( !isPlainObject( options ) ) { return new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); } if ( hasOwnProp( options, 'schema' ) ) { - if ( !isObject( options.schema ) ) { + if ( !isPlainObject( options.schema ) ) { return new TypeError( format( 'invalid option. `%s` option must be an object. Option: `%s`.', 'schema', options.schema ) ); } if ( !hasOwnProp( options.schema, 'root' ) ) { return new TypeError( format( 'invalid option. `%s` option must have %s `%s` property.', 'schema', 'a', 'root' ) ); } - keys = objectKeys( options.schema ); - for ( i = 0; i < keys.length; i++ ) { - section = keys[ i ]; - err = validateSection( options.schema[ section ], section ); - if ( err ) { - return err; - } - } + err = validateSchema( options.schema ); + if ( err ) { + return err; + } opts.schema = options.schema; } return null;