diff --git a/etc/eslint/.eslintrc.examples.js b/etc/eslint/.eslintrc.examples.js index 2b79eaaf2ef0..afa9a472ffd5 100644 --- a/etc/eslint/.eslintrc.examples.js +++ b/etc/eslint/.eslintrc.examples.js @@ -92,6 +92,13 @@ eslint.rules[ 'stdlib/doctest' ] = 'error'; */ eslint.rules[ 'stdlib/vars-order' ] = 'off'; +/** +* Enforce that last `require` is a relative path. +* +* @private +*/ +eslint.rules[ 'stdlib/require-last-path-relative' ] = 'error'; + // EXPORTS // diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js index e4d567fa12f6..7611a92391a2 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js @@ -981,6 +981,15 @@ setReadOnly( rules, 'require-file-extensions', require( '@stdlib/_tools/eslint/r */ setReadOnly( rules, 'require-globals', require( '@stdlib/_tools/eslint/rules/require-globals' ) ); +/** +* @name require-last-path-relative +* @memberof rules +* @readonly +* @type {Function} +* @see {@link module:@stdlib/_tools/eslint/rules/require-last-path-relative} +*/ +setReadOnly( rules, 'require-last-path-relative', require( '@stdlib/_tools/eslint/rules/require-last-path-relative' ) ); + /** * @name require-leading-slash * @memberof rules diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/README.md new file mode 100644 index 000000000000..caccc6d06081 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/README.md @@ -0,0 +1,126 @@ + + +# require-last-path-relative + +> [ESLint rule][eslint-rules] enforcing that the last `require` statement is a relative path. + +
+ +
+ + + +
+ +## Usage + +```javascript +var rule = require( '@stdlib/_tools/eslint/rules/require-last-path-relative' ); +``` + +#### rule + +[ESLint rule][eslint-rules] enforcing that the last `require` statement is a relative path. + +**Bad**: + +```javascript +var zip = require( '@stdlib/utils/zip' ); +var unzip = require( '@stdlib/utils/unzip' ); +``` + +**Good**: + + + +```javascript +var zip = require( '@stdlib/utils/zip' ); +var unzip = require( './../lib' ); +``` + +The rule is mainly for `examples` files to help make explicit that the API being demonstrated belongs to the respective package. + +
+ + + +
+ +## Examples + + + +```javascript +var Linter = require( 'eslint' ).Linter; +var rule = require( '@stdlib/_tools/eslint/rules/require-last-path-relative' ); + +var linter = new Linter(); +var result; + +var code = [ + 'var beep = require( \'@stdlib/boop/beep\' );', + 'var foo = require( \'@stdlib/bar/foo\' );' +].join( '\n' ); + +linter.defineRule( 'require-last-path-relative', rule ); + +result = linter.verify( code, { + 'rules': { + 'require-last-path-relative-relative': 'error' + } +}); +/* returns + [ + { + 'ruleId': 'require-last-path-relative', + 'severity': 2, + 'message': 'Last `require` statement must be a relative path', + 'line': 2, + 'column': 11, + 'nodeType': 'CallExpression', + 'endLine': 2, + 'endColumn': 39 + } + ] +*/ +``` + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/examples/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/examples/index.js new file mode 100644 index 000000000000..1d1ef9a2bb6c --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/examples/index.js @@ -0,0 +1,52 @@ +/** +* @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 Linter = require( 'eslint' ).Linter; +var rule = require( './../lib' ); + +var linter = new Linter(); + +var code = [ + 'var beep = require( \'@stdlib/boop/beep\' );', + 'var foo = require( \'@stdlib/bar/foo\' );' +].join( '\n' ); + +linter.defineRule( 'require-last-path-relative', rule ); + +var result = linter.verify( code, { + 'rules': { + 'require-last-path-relative': 'error' + } +}); +console.log( result ); +/* => + [ + { + 'ruleId': 'require-last-path-relative', + 'severity': 2, + 'message': 'Last `require` statement must be a relative path', + 'line': 2, + 'column': 11, + 'nodeType': 'CallExpression', + 'endLine': 2, + 'endColumn': 39 + } + ] +*/ diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/lib/index.js new file mode 100644 index 000000000000..1a2368134575 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/lib/index.js @@ -0,0 +1,39 @@ +/** +* @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'; + +/** +* ESLint rule to enforce that the last `require` statement is a relative path. +* +* @module @stdlib/_tools/eslint/rules/require-last-path-relative +* +* @example +* var rule = require( '@stdlib/_tools/eslint/rules/require-last-path-relative' ); +* +* console.log( rule ); +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/lib/main.js b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/lib/main.js new file mode 100644 index 000000000000..138d64fa6e1d --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/lib/main.js @@ -0,0 +1,123 @@ +/** +* @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 startsWith = require( '@stdlib/string/starts-with' ); + + +// FUNCTIONS // + +/** +* Checks if a `require` path is relative. +* +* @private +* @param {string} path - `require` path +* @returns {boolean} boolean indicating if path is relative +*/ +function isRelativePath( path ) { + return ( + startsWith( path, './' ) || + startsWith( path, '../' ) + ); +} + + +// MAIN // + +/** +* ESLint rule to enforce that the last `require` statement is a relative path. +* +* @param {Object} context - ESLint context +* @returns {Object} validators +*/ +function main( context ) { + var requires = []; + + /** + * Validates `require` statements. + * + * @private + * @param {ASTNode} node - node to examine + */ + function validate( node ) { + var requirePath; + if ( + node.callee.name === 'require' && + node.arguments[ 0 ] && + node.arguments[ 0 ].type === 'Literal' + ) { + requirePath = node.arguments[ 0 ].value; + requires.push({ + 'node': node, + 'path': requirePath, + 'isRelative': isRelativePath( requirePath ) + }); + } + } + + /** + * Reports the error message. + * + * @private + * @param {Object} lastRequire - last `require` statement + */ + function report( lastRequire ) { + context.report({ + 'node': lastRequire.node, + 'message': 'Last `require` statement in example files must be a relative path' + }); + } + + /** + * Callback invoked upon program exit. + * + * @private + */ + function finish() { + var lastRequire; + if ( requires.length > 0 ) { + lastRequire = requires[ requires.length - 1 ]; + if ( !lastRequire.isRelative ) { + report( lastRequire ); + } + requires.length = 0; // reset to ensure that the array is not used across multiple files + } + } + + return { + 'CallExpression': validate, + 'Program:exit': finish + }; +} + + +// EXPORTS // + +module.exports = { + 'meta': { + 'type': 'suggestion', + 'docs': { + 'description': 'enforce that the last `require` statement is a relative path' + }, + 'schema': [] + }, + 'create': main +}; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/package.json b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/package.json new file mode 100644 index 000000000000..5a29148a35aa --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/package.json @@ -0,0 +1,63 @@ +{ + "name": "@stdlib/_tools/eslint/rules/require-last-path-relative", + "version": "0.0.0", + "description": "ESLint rule enforcing that the last `require` statement is a relative path.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "bin": {}, + "main": "./lib", + "directories": { + "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": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "tools", + "tool", + "eslint", + "lint", + "custom", + "rules", + "rule", + "plugin", + "require", + "path", + "relative" + ] +} diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/fixtures/invalid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/fixtures/invalid.js new file mode 100644 index 000000000000..277982d18130 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/fixtures/invalid.js @@ -0,0 +1,55 @@ +/** +* @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'; + +// VARIABLES // + +var invalid = []; +var test; + +test = { + 'code': [ + 'var foo = require( \'./../lib\' );', + 'var beep = require( \'@stdlib/boop/beep\' );' + ].join( '\n' ), + 'errors': [ + { + 'message': 'Last require statement in example files must be a relative path' + } + ] +}; +invalid.push( test ); + +test = { + 'code': [ + 'var foo = require( \'@stdlib/foo\' );', + 'var beep = require( \'@stdlib/boop/beep\' );' + ].join( '\n' ), + 'errors': [ + { + 'message': 'Last require statement in example files must be a relative path' + } + ] +}; +invalid.push( test ); + + +// EXPORTS // + +module.exports = invalid; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/fixtures/valid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/fixtures/valid.js new file mode 100644 index 000000000000..ed62e9b37150 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/fixtures/valid.js @@ -0,0 +1,58 @@ +/** +* @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'; + +// VARIABLES // + +var valid = []; +var test; + +test = { + 'code': [ + 'var beep = require( \'@stdlib/boop/beep\' );', + 'var foo = require( \'./../lib\' );' + ].join( '\n' ) +}; +valid.push( test ); + +test = { + 'code': [ + 'var beep = require( \'@stdlib/boop/beep\' );', + 'var foo = require( \'./lib\' );' + ].join( '\n' ) +}; +valid.push( test ); + +test = { + 'code': [ + 'var beep = require( \'@stdlib/boop/beep\' );', + 'var foo = require( \'../lib\' );' + ].join( '\n' ) +}; +valid.push( test ); + +test = { + 'code': 'var foo = require( \'./../lib\' );' +}; +valid.push( test ); + + +// EXPORTS // + +module.exports = valid; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/test.js b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/test.js new file mode 100644 index 000000000000..0238b74ac225 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/require-last-path-relative/test/test.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 tape = require( 'tape' ); +var RuleTester = require( 'eslint' ).RuleTester; +var rule = require( './../lib' ); + + +// FIXTURES // + +var valid = require( './fixtures/valid.js' ); +var invalid = require( './fixtures/invalid.js' ); + + +// TESTS // + +tape( 'main export is an object', function test( t ) { + t.ok( true, __filename ); + t.equal( typeof rule, 'object', 'main export is an object' ); + t.end(); +}); + +tape( 'the function positively validates code where the last require statement is a relative path', function test( t ) { + var tester = new RuleTester(); + + try { + tester.run( 'require-last-path', rule, { + 'valid': valid, + 'invalid': [] + }); + t.pass( 'passed without errors' ); + } catch ( err ) { + t.fail( 'encountered an error: ' + err.message ); + } + t.end(); +}); + +tape( 'the function negatively validates code where the last require statement is not a relative path', function test( t ) { + var tester = new RuleTester(); + + try { + tester.run( 'require-last-path', rule, { + 'valid': [], + 'invalid': invalid + }); + t.pass( 'passed without errors' ); + } catch ( err ) { + t.fail( 'encountered an error: ' + err.message ); + } + t.end(); +});