From e2f14b15681b5540acb66240f11e64b60d8b77b0 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 7 Aug 2026 01:29:34 -0500 Subject: [PATCH 1/5] feat: add `no-single-property-require` ESLint rule --- 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_pkg_readmes status: passed - task: lint_markdown_docs status: na - task: lint_markdown status: na - task: lint_package_json status: passed - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: 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: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- etc/eslint/.eslintrc.tests.js | 7 + etc/eslint/overrides/index.js | 2 + etc/eslint/rules/stdlib.js | 24 ++ .../@stdlib/_tools/eslint/rules/README.md | 3 + .../@stdlib/_tools/eslint/rules/lib/index.js | 9 + .../no-single-property-require/README.md | 176 +++++++++++++++ .../examples/index.js | 59 +++++ .../no-single-property-require/lib/index.js | 39 ++++ .../no-single-property-require/lib/main.js | 205 ++++++++++++++++++ .../no-single-property-require/package.json | 65 ++++++ .../test/fixtures/invalid.js | 112 ++++++++++ .../test/fixtures/unvalidated.js | 60 +++++ .../test/fixtures/valid.js | 131 +++++++++++ .../no-single-property-require/test/test.js | 86 ++++++++ 14 files changed, 978 insertions(+) create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/invalid.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/unvalidated.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/valid.js create mode 100644 lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/test.js diff --git a/etc/eslint/.eslintrc.tests.js b/etc/eslint/.eslintrc.tests.js index d845bbc7da83..a4b217899c0e 100644 --- a/etc/eslint/.eslintrc.tests.js +++ b/etc/eslint/.eslintrc.tests.js @@ -128,6 +128,13 @@ eslint.rules[ 'stdlib/jsdoc-private-annotation' ] = 'off'; */ eslint.rules[ 'stdlib/jsdoc-doctest' ] = 'off'; +/** +* Allow requiring the package under test as a whole when only a single property is used. +* +* @private +*/ +eslint.rules[ 'stdlib/no-single-property-require' ] = 'off'; + /** * Do not enforce nested function elevation. * diff --git a/etc/eslint/overrides/index.js b/etc/eslint/overrides/index.js index f0eb50d0aee0..889232a71b7a 100644 --- a/etc/eslint/overrides/index.js +++ b/etc/eslint/overrides/index.js @@ -122,6 +122,7 @@ var overrides = [ 'require-jsdoc': 'off', 'stdlib/jsdoc-private-annotation': 'off', 'stdlib/jsdoc-doctest': 'off', + 'stdlib/no-single-property-require': 'off', 'stdlib/no-unnecessary-nested-functions': 'off', 'no-undefined': 'off' } @@ -158,6 +159,7 @@ var overrides = [ 'require-jsdoc': 'off', 'stdlib/jsdoc-private-annotation': 'off', 'stdlib/jsdoc-return-annotations-values': 'off', + 'stdlib/no-single-property-require': 'off', 'stdlib/no-unnecessary-nested-functions': 'off', 'stdlib/return-annotations-values': 'off', 'strict': 'off', diff --git a/etc/eslint/rules/stdlib.js b/etc/eslint/rules/stdlib.js index cfc51e8e65b8..c4f3222f12d0 100644 --- a/etc/eslint/rules/stdlib.js +++ b/etc/eslint/rules/stdlib.js @@ -4589,6 +4589,30 @@ rules[ 'stdlib/no-require-index' ] = 'error'; */ rules[ 'stdlib/no-self-require' ] = 'error'; +/** +* Enforce that a property is required directly when only a single property of a required module is used. Requiring a property directly reduces bundle sizes during ESM tree-shaking via named imports. +* +* @name no-single-property-require +* @memberof rules +* @type {string} +* @default 'warn' +* +* @example +* // Bad... +* var dcopy = require( '@stdlib/blas/base/dcopy' ); +* +* dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 ); +* dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 ); +* +* @example +* // Good... +* var dcopy = require( '@stdlib/blas/base/dcopy' ).ndarray; +* +* dcopy( x.length, x, 1, 0, y, 1, 0 ); +* dcopy( y.length, y, 1, 0, z, 1, 0 ); +*/ +rules[ 'stdlib/no-single-property-require' ] = 'warn'; + /** * Never allow unassigned `require()` calls. * diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/README.md index 64f53a1c648d..1ec7374187a9 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/README.md +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/README.md @@ -83,6 +83,7 @@ var eslint = rules; - [`no-require-absolute-path`][@stdlib/_tools/eslint/rules/no-require-absolute-path]: ESLint rule disallowing `require()` statements containing absolute file paths. - [`no-require-index`][@stdlib/_tools/eslint/rules/no-require-index]: ESLint rule disallowing `require()` calls which explicitly require `index.` files. - [`no-self-require`][@stdlib/_tools/eslint/rules/no-self-require]: ESLint rule enforcing that modules do not require themselves. +- [`no-single-property-require`][@stdlib/_tools/eslint/rules/no-single-property-require]: ESLint rule enforcing that a property is required directly when only a single property of a required module is used. - [`no-unassigned-require`][@stdlib/_tools/eslint/rules/no-unassigned-require]: ESLint rule forbidding unassigned require statements. - [`no-unnecessary-nested-functions`][@stdlib/_tools/eslint/rules/no-unnecessary-nested-functions]: ESLint rule to prevent unnecessary function nesting when inner functions don't depend on outer scope variables. - [`repl-namespace-order`][@stdlib/_tools/eslint/rules/repl-namespace-order]: ESLint rule to enforce that packages are added to a REPL namespace object in alphabetical order according to alias (namespace key). @@ -440,6 +441,8 @@ console.log( getKeys( rules ) ); [@stdlib/_tools/eslint/rules/no-self-require]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-self-require +[@stdlib/_tools/eslint/rules/no-single-property-require]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-single-property-require + [@stdlib/_tools/eslint/rules/no-unassigned-require]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-unassigned-require [@stdlib/_tools/eslint/rules/no-unnecessary-nested-functions]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-unnecessary-nested-functions 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 fcf6e671c4c9..c90cab4a4289 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js @@ -999,6 +999,15 @@ setReadOnly( rules, 'no-require-index', require( '@stdlib/_tools/eslint/rules/no */ setReadOnly( rules, 'no-self-require', require( '@stdlib/_tools/eslint/rules/no-self-require' ) ); +/** +* @name no-single-property-require +* @memberof rules +* @readonly +* @type {Function} +* @see {@link module:@stdlib/_tools/eslint/rules/no-single-property-require} +*/ +setReadOnly( rules, 'no-single-property-require', require( '@stdlib/_tools/eslint/rules/no-single-property-require' ) ); + /** * @name no-unassigned-require * @memberof rules diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md new file mode 100644 index 000000000000..f00ca3d90324 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md @@ -0,0 +1,176 @@ + + +# no-single-property-require + +> [ESLint rule][eslint-rules] to enforce requiring a property directly when only a single property of a required module is used. + +
+ +This rule enforces that, when only a single property of a required module is ever used within a module, the property is required directly (e.g., `var dcopy = require( '@stdlib/blas/base/dcopy' ).ndarray;`). Requiring a property directly reduces bundle sizes during ESM tree-shaking via named imports. + +
+ + + +
+ +## Usage + +```javascript +var rule = require( '@stdlib/_tools/eslint/rules/no-single-property-require' ); +``` + +#### rule + +[ESLint rule][eslint-rules] to enforce requiring a property directly when only a single property of a required module is used. + +**Bad**: + + + +```javascript +var dcopy = require( '@stdlib/blas/base/dcopy' ); + +// ... + +dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 ); + +// ... + +dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 ); +``` + +**Good**: + +```javascript +var dcopy = require( '@stdlib/blas/base/dcopy' ).ndarray; + +// ... + +dcopy( x.length, x, 1, 0, y, 1, 0 ); + +// ... + +dcopy( y.length, y, 1, 0, z, 1, 0 ); +``` + +**Good** (multiple properties are used): + +```javascript +var dcopy = require( '@stdlib/blas/base/dcopy' ); + +// ... + +dcopy( x.length, x, 1, y, 1 ); + +// ... + +dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 ); +``` + +
+ + + +
+ +## Notes + +- The rule only checks module-scope (top-level) variable declarations, as `stdlib` convention is to place `require` statements at the top of a module. +- Bindings which are used "bare" anywhere (e.g., called directly, passed as an argument, returned, exported, or interrogated via `typeof`) are not flagged. +- Computed property access (e.g., `x[ key ]`) is not flagged, as the accessed property cannot be statically determined. +- Bindings which are re-assigned or whose properties are mutated (e.g., assigned, deleted, or updated) are not flagged. +- The rule does not resolve whether `require` refers to the global CommonJS function or a shadowing local binding. +- When converting a method-style call (e.g., `x.ndarray( ... )`) to a direct call, the `this` context within the called function changes; for `stdlib` packages, exported methods do not rely on `this` binding, and, hence, the transformation is safe. + +
+ + + +
+ +## Examples + + + +```javascript +var Linter = require( 'eslint' ).Linter; +var rule = require( '@stdlib/_tools/eslint/rules/no-single-property-require' ); + +var linter = new Linter(); + +// Generate source code in which only a single property of a required module is used: +var code = [ + 'var dcopy = require( \'@stdlib/blas/base/dcopy\' );', + 'dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 );', + 'dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 );' +].join( '\n' ); + +// Define the ESLint configuration: +var config = { + 'rules': { + 'no-single-property-require': 'error' + } +}; + +// Register the rule: +linter.defineRule( 'no-single-property-require', rule ); + +// Lint the code: +var out = linter.verify( code, config ); +console.log( out ); +/* => + [ + { + 'ruleId': 'no-single-property-require', + 'severity': 2, + 'message': 'only the `ndarray` property of the required module is used; require the property directly', + 'line': 1, + 'column': 5, + 'nodeType': 'VariableDeclarator', + 'endLine': 1, + 'endColumn': 50 + } + ] +*/ +``` + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js new file mode 100644 index 000000000000..d85584a3c67a --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js @@ -0,0 +1,59 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 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(); + +// Generate source code in which only a single property of a required module is used: +var code = [ + 'var dcopy = require( \'@stdlib/blas/base/dcopy\' );', + 'dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 );', + 'dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 );' +].join( '\n' ); + +// Define the ESLint configuration: +var config = { + 'rules': { + 'no-single-property-require': 'error' + } +}; + +// Register the rule: +linter.defineRule( 'no-single-property-require', rule ); + +// Lint the code: +var out = linter.verify( code, config ); +console.log( out ); +/* => + [ + { + 'ruleId': 'no-single-property-require', + 'severity': 2, + 'message': 'only the `ndarray` property of the required module is used; require the property directly', + 'line': 1, + 'column': 5, + 'nodeType': 'VariableDeclarator', + 'endLine': 1, + 'endColumn': 50 + } + ] +*/ diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js new file mode 100644 index 000000000000..a8688bcecbf3 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js @@ -0,0 +1,39 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 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 enforcing that a property is required directly when only a single property of a required module is used. +* +* @module @stdlib/_tools/eslint/rules/no-single-property-require +* +* @example +* var rule = require( '@stdlib/_tools/eslint/rules/no-single-property-require' ); +* +* console.log( rule ); +*/ + +// MODULES // + +var main = require( './main.js' ); + + +// EXPORTS // + +module.exports = main; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js new file mode 100644 index 000000000000..5baecd1a6574 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js @@ -0,0 +1,205 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 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 isString = require( '@stdlib/assert/is-string' ).isPrimitive; + + +// VARIABLES // + +var rule; + + +// FUNCTIONS // + +/** +* Returns the property name accessed by a reference, or `null` if the reference is not a simple non-computed single-property read. +* +* @private +* @param {Object} ref - ESLint reference +* @returns {(string|null)} property name or null +*/ +function propertyName( ref ) { + var parent; + var id; + var p2; + + if ( ref.isWrite() ) { + // Re-assignment (e.g., `x = other`): binding is mutated... + return null; + } + id = ref.identifier; + parent = id.parent; + if ( + !parent || + parent.type !== 'MemberExpression' || + parent.object !== id || + parent.computed || + parent.optional + ) { + // Bare use (e.g., `x()`, `f( x )`, `typeof x`, `module.exports = x`) or computed access (e.g., `x[ key ]`)... + return null; + } + p2 = parent.parent; + if ( p2 ) { + if ( p2.type === 'AssignmentExpression' && p2.left === parent ) { + // Write target (e.g., `x.foo = bar`): mutating the module object... + return null; + } + if ( p2.type === 'UpdateExpression' ) { + // Update expression (e.g., `x.count++`)... + return null; + } + if ( p2.type === 'UnaryExpression' && p2.operator === 'delete' ) { + // Delete expression (e.g., `delete x.foo`)... + return null; + } + } + return parent.property.name; +} + +/** +* Returns the single property name used across all references of a variable, or `null` if references are absent, mixed, or not simple property reads. +* +* @private +* @param {Object} variable - ESLint scope variable +* @returns {(string|null)} property name or null +*/ +function singleProperty( variable ) { + var count; + var refs; + var prop; + var name; + var i; + + prop = null; + count = 0; + refs = variable.references; + for ( i = 0; i < refs.length; i++ ) { + if ( refs[ i ].init ) { + // Skip the reference created by the declarator's own initialization... + continue; + } + count += 1; + name = propertyName( refs[ i ] ); + if ( name === null ) { + return null; + } + if ( prop === null ) { + prop = name; + } else if ( prop !== name ) { + // More than one property is used... + return null; + } + } + if ( count === 0 ) { + // Unused binding: out of scope for this rule... + return null; + } + return prop; +} + +/** +* Rule for flagging required modules of which only a single property is ever used. +* +* @param {Object} context - ESLint context +* @returns {Object} validators +*/ +function main( context ) { + /** + * Checks a variable declaration for required modules of which only a single property is used. + * + * @private + * @param {Node} node - variable declaration node + */ + function validate( node ) { + var variables; + var variable; + var decl; + var init; + var prop; + var i; + var j; + + if ( !node.parent || node.parent.type !== 'Program' ) { + // Only consider module-scope declarations: + return; + } + variables = null; + for ( i = 0; i < node.declarations.length; i++ ) { + decl = node.declarations[ i ]; + if ( decl.type !== 'VariableDeclarator' || decl.id.type !== 'Identifier' ) { + continue; + } + init = decl.init; + if ( + !init || + init.type !== 'CallExpression' || + !init.callee || + init.callee.type !== 'Identifier' || + init.callee.name !== 'require' || + init.arguments.length !== 1 || + !isString( init.arguments[ 0 ].value ) + ) { + continue; + } + if ( variables === null ) { + variables = context.sourceCode.getDeclaredVariables( node ); + } + for ( j = 0; j < variables.length; j++ ) { + variable = variables[ j ]; + if ( variable.name !== decl.id.name ) { + continue; + } + prop = singleProperty( variable ); + if ( prop !== null ) { + context.report({ + 'node': decl, + 'message': 'only the `' + prop + '` property of the required module is used; require the property directly' + }); + } + break; + } + } + } + return { + 'VariableDeclaration': validate + }; +} + + +// MAIN // + +rule = { + 'meta': { + 'type': 'suggestion', + 'docs': { + 'description': 'enforce requiring a property directly when only a single property of a required module is used' + }, + 'schema': [] + }, + 'create': main +}; + + +// EXPORTS // + +module.exports = rule; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json new file mode 100644 index 000000000000..7fc15f78b4ff --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json @@ -0,0 +1,65 @@ +{ + "name": "@stdlib/_tools/eslint/rules/no-single-property-require", + "version": "0.0.0", + "description": "ESLint rule enforcing that a property is required directly when only a single property of a required module is used.", + "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", + "import", + "property", + "treeshaking", + "style" + ] +} diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/invalid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/invalid.js new file mode 100644 index 000000000000..a3e6fd267c4d --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/invalid.js @@ -0,0 +1,112 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 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'; + +// MAIN // + +var invalid = []; + +invalid.push({ + 'code': [ + 'var dcopy = require( \'@stdlib/blas/base/dcopy\' );', + 'dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 );', + 'dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 );' + ].join( '\n' ), + 'errors': [ + { + 'message': 'only the `ndarray` property of the required module is used; require the property directly', + 'type': 'VariableDeclarator' + } + ] +}); + +invalid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'var y = x.bar;' + ].join( '\n' ), + 'errors': [ + { + 'message': 'only the `bar` property of the required module is used; require the property directly', + 'type': 'VariableDeclarator' + } + ] +}); + +invalid.push({ + 'code': [ + 'var dcopy = require( \'@stdlib/blas/base/dcopy\' );', + 'function copy( x, y ) {', + '\treturn dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 );', + '}', + 'dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 );' + ].join( '\n' ), + 'errors': [ + { + 'message': 'only the `ndarray` property of the required module is used; require the property directly', + 'type': 'VariableDeclarator' + } + ] +}); + +invalid.push({ + 'code': [ + 'var dcopy = require( \'@stdlib/blas/base/dcopy\' );', + 'var len = dcopy.ndarray.length;', + 'dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 );' + ].join( '\n' ), + 'errors': [ + { + 'message': 'only the `ndarray` property of the required module is used; require the property directly', + 'type': 'VariableDeclarator' + } + ] +}); + +invalid.push({ + 'code': [ + 'var a = require( \'a\' ), b = require( \'b\' );', + 'a();', + 'b.only();' + ].join( '\n' ), + 'errors': [ + { + 'message': 'only the `only` property of the required module is used; require the property directly', + 'type': 'VariableDeclarator' + } + ] +}); + +invalid.push({ + 'code': [ + 'var x = require( \'./../lib\' );', + 'x.method();' + ].join( '\n' ), + 'errors': [ + { + 'message': 'only the `method` property of the required module is used; require the property directly', + 'type': 'VariableDeclarator' + } + ] +}); + + +// EXPORTS // + +module.exports = invalid; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/unvalidated.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/unvalidated.js new file mode 100644 index 000000000000..e8bbfb33a134 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/unvalidated.js @@ -0,0 +1,60 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 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'; + +// MAIN // + +var unvalidated = []; + +unvalidated.push({ + 'code': [ + 'var x = foo();', + 'x.bar();', + 'x.bar();' + ].join( '\n' ) +}); + +unvalidated.push({ + 'code': [ + 'var x = require( name );', + 'x.bar();' + ].join( '\n' ) +}); + +unvalidated.push({ + 'code': [ + 'var { a } = require( \'foo\' );', + 'a();' + ].join( '\n' ), + 'parserOptions': { + 'ecmaVersion': 6 + } +}); + +unvalidated.push({ + 'code': [ + 'var x = require( \'foo\', \'bar\' );', + 'x.baz();' + ].join( '\n' ) +}); + + +// EXPORTS // + +module.exports = unvalidated; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/valid.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/valid.js new file mode 100644 index 000000000000..3dc19e3e4740 --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/fixtures/valid.js @@ -0,0 +1,131 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 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'; + +// MAIN // + +var valid = []; + +valid.push({ + 'code': [ + 'var floor = require( \'@stdlib/math/base/special/floor\' );', + 'var y = floor( 3.14 );' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var dcopy = require( \'@stdlib/blas/base/dcopy\' );', + 'var type = typeof dcopy;', + 'dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 );' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'x.a();', + 'x.b();' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'x[ k ]();' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'f( x );' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'module.exports = x;' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var dcopy = require( \'@stdlib/blas/base/dcopy\' ).ndarray;', + 'dcopy( x.length, x, 1, 0, y, 1, 0 );' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'x.foo = 1;', + 'x.foo();' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'x = other;', + 'x.foo();' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'var str = \'\' + x;' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'delete x.foo;', + 'x.foo();' + ].join( '\n' ) +}); + +valid.push({ + 'code': [ + 'var x = require( \'foo\' );', + 'x.count += 1;', + 'var v = x.count;' + ].join( '\n' ) +}); + +valid.push({ + 'code': 'var x = require( \'foo\' );' +}); + +valid.push({ + 'code': [ + 'function f() {', + '\tvar x = require( \'foo\' );', + '\treturn x.bar();', + '}' + ].join( '\n' ) +}); + + +// EXPORTS // + +module.exports = valid; diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/test.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/test.js new file mode 100644 index 000000000000..16f75fcc253f --- /dev/null +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/test/test.js @@ -0,0 +1,86 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 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' ); +var unvalidated = require( './fixtures/unvalidated.js' ); + + +// TESTS // + +tape( 'main export is an object', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof rule, 'object', 'main export is an object' ); + t.end(); +}); + +tape( 'the function positively validates code in which required modules are not used via only a single property', function test( t ) { + var tester = new RuleTester(); + + try { + tester.run( 'no-single-property-require', 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 in which only a single property of a required module is used', function test( t ) { + var tester = new RuleTester(); + + try { + tester.run( 'no-single-property-require', rule, { + 'valid': [], + 'invalid': invalid + }); + t.pass( 'passed without errors' ); + } catch ( err ) { + t.fail( 'encountered an error: ' + err.message ); + } + t.end(); +}); + +tape( 'the function does not validate other `require` expressions', function test( t ) { + var tester = new RuleTester(); + + try { + tester.run( 'no-single-property-require', rule, { + 'valid': unvalidated, + 'invalid': [] + }); + t.pass( 'passed without errors' ); + } catch ( err ) { + t.fail( 'encountered an error: ' + err.message ); + } + t.end(); +}); From d4c11de11de5b84161b46397b0e83d74cddcdf35 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 7 Aug 2026 01:37:33 -0500 Subject: [PATCH 2/5] chore: remove README namespace update --- 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_pkg_readmes status: passed - task: lint_markdown_docs status: na - task: lint_markdown status: na - 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: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- lib/node_modules/@stdlib/_tools/eslint/rules/README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/README.md index 1ec7374187a9..64f53a1c648d 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/README.md +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/README.md @@ -83,7 +83,6 @@ var eslint = rules; - [`no-require-absolute-path`][@stdlib/_tools/eslint/rules/no-require-absolute-path]: ESLint rule disallowing `require()` statements containing absolute file paths. - [`no-require-index`][@stdlib/_tools/eslint/rules/no-require-index]: ESLint rule disallowing `require()` calls which explicitly require `index.` files. - [`no-self-require`][@stdlib/_tools/eslint/rules/no-self-require]: ESLint rule enforcing that modules do not require themselves. -- [`no-single-property-require`][@stdlib/_tools/eslint/rules/no-single-property-require]: ESLint rule enforcing that a property is required directly when only a single property of a required module is used. - [`no-unassigned-require`][@stdlib/_tools/eslint/rules/no-unassigned-require]: ESLint rule forbidding unassigned require statements. - [`no-unnecessary-nested-functions`][@stdlib/_tools/eslint/rules/no-unnecessary-nested-functions]: ESLint rule to prevent unnecessary function nesting when inner functions don't depend on outer scope variables. - [`repl-namespace-order`][@stdlib/_tools/eslint/rules/repl-namespace-order]: ESLint rule to enforce that packages are added to a REPL namespace object in alphabetical order according to alias (namespace key). @@ -441,8 +440,6 @@ console.log( getKeys( rules ) ); [@stdlib/_tools/eslint/rules/no-self-require]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-self-require -[@stdlib/_tools/eslint/rules/no-single-property-require]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-single-property-require - [@stdlib/_tools/eslint/rules/no-unassigned-require]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-unassigned-require [@stdlib/_tools/eslint/rules/no-unnecessary-nested-functions]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/_tools/eslint/rules/no-unnecessary-nested-functions From 4a5565bef6303446af5cf5cec574fa7fea740380 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 7 Aug 2026 01:43:38 -0500 Subject: [PATCH 3/5] docs: fix undefined variables in examples and update descriptions --- 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_pkg_readmes status: passed - task: lint_markdown_docs status: na - task: lint_markdown status: na - task: lint_package_json status: passed - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: 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: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../rules/no-single-property-require/README.md | 16 +++++++++++----- .../no-single-property-require/lib/index.js | 2 +- .../rules/no-single-property-require/lib/main.js | 2 +- .../no-single-property-require/package.json | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md index f00ca3d90324..1f752b621582 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md @@ -20,7 +20,7 @@ limitations under the License. # no-single-property-require -> [ESLint rule][eslint-rules] to enforce requiring a property directly when only a single property of a required module is used. +> [ESLint rule][eslint-rules] disallowing requiring an entire module when only a single property of the module is used.
@@ -40,7 +40,7 @@ var rule = require( '@stdlib/_tools/eslint/rules/no-single-property-require' ); #### rule -[ESLint rule][eslint-rules] to enforce requiring a property directly when only a single property of a required module is used. +[ESLint rule][eslint-rules] disallowing requiring an entire module when only a single property of the module is used. **Bad**: @@ -49,7 +49,9 @@ var rule = require( '@stdlib/_tools/eslint/rules/no-single-property-require' ); ```javascript var dcopy = require( '@stdlib/blas/base/dcopy' ); -// ... +var x = [ 1.0, 2.0, 3.0 ]; +var y = [ 0.0, 0.0, 0.0 ]; +var z = [ 0.0, 0.0, 0.0 ]; dcopy.ndarray( x.length, x, 1, 0, y, 1, 0 ); @@ -63,7 +65,9 @@ dcopy.ndarray( y.length, y, 1, 0, z, 1, 0 ); ```javascript var dcopy = require( '@stdlib/blas/base/dcopy' ).ndarray; -// ... +var x = [ 1.0, 2.0, 3.0 ]; +var y = [ 0.0, 0.0, 0.0 ]; +var z = [ 0.0, 0.0, 0.0 ]; dcopy( x.length, x, 1, 0, y, 1, 0 ); @@ -77,7 +81,9 @@ dcopy( y.length, y, 1, 0, z, 1, 0 ); ```javascript var dcopy = require( '@stdlib/blas/base/dcopy' ); -// ... +var x = [ 1.0, 2.0, 3.0 ]; +var y = [ 0.0, 0.0, 0.0 ]; +var z = [ 0.0, 0.0, 0.0 ]; dcopy( x.length, x, 1, y, 1 ); diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js index a8688bcecbf3..f9bb7bf38b8e 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/index.js @@ -19,7 +19,7 @@ 'use strict'; /** -* ESLint rule enforcing that a property is required directly when only a single property of a required module is used. +* ESLint rule disallowing requiring an entire module when only a single property of the module is used. * * @module @stdlib/_tools/eslint/rules/no-single-property-require * diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js index 5baecd1a6574..010888542eed 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/lib/main.js @@ -192,7 +192,7 @@ rule = { 'meta': { 'type': 'suggestion', 'docs': { - 'description': 'enforce requiring a property directly when only a single property of a required module is used' + 'description': 'disallow requiring an entire module when only a single property is used' }, 'schema': [] }, diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json index 7fc15f78b4ff..deed2dfeb27b 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/package.json @@ -1,7 +1,7 @@ { "name": "@stdlib/_tools/eslint/rules/no-single-property-require", "version": "0.0.0", - "description": "ESLint rule enforcing that a property is required directly when only a single property of a required module is used.", + "description": "ESLint rule disallowing requiring an entire module when only a single property of the module is used.", "license": "Apache-2.0", "author": { "name": "The Stdlib Authors", From fcdf60a6ac6ec8833a1537f7f292d2c7e18981d5 Mon Sep 17 00:00:00 2001 From: Philipp Burckhardt Date: Fri, 7 Aug 2026 01:51:13 -0500 Subject: [PATCH 4/5] docs: fix expected output in examples --- 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_pkg_readmes status: passed - task: lint_markdown_docs status: na - task: lint_markdown status: na - 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: passed - 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: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../_tools/eslint/rules/no-single-property-require/README.md | 2 +- .../eslint/rules/no-single-property-require/examples/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md index 1f752b621582..ded0b8f09329 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/README.md @@ -153,7 +153,7 @@ console.log( out ); 'column': 5, 'nodeType': 'VariableDeclarator', 'endLine': 1, - 'endColumn': 50 + 'endColumn': 49 } ] */ diff --git a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js index d85584a3c67a..85bbcc4da7ec 100644 --- a/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js +++ b/lib/node_modules/@stdlib/_tools/eslint/rules/no-single-property-require/examples/index.js @@ -53,7 +53,7 @@ console.log( out ); 'column': 5, 'nodeType': 'VariableDeclarator', 'endLine': 1, - 'endColumn': 50 + 'endColumn': 49 } ] */ From 4ebe08061b7c43a09ebc84f468bc41134b7b3a5a Mon Sep 17 00:00:00 2001 From: Athan Date: Fri, 7 Aug 2026 16:09:06 -0700 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Athan Signed-off-by: Athan --- etc/eslint/.eslintrc.tests.js | 2 +- etc/eslint/rules/stdlib.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/etc/eslint/.eslintrc.tests.js b/etc/eslint/.eslintrc.tests.js index a4b217899c0e..5e9e1b45efd8 100644 --- a/etc/eslint/.eslintrc.tests.js +++ b/etc/eslint/.eslintrc.tests.js @@ -129,7 +129,7 @@ eslint.rules[ 'stdlib/jsdoc-private-annotation' ] = 'off'; eslint.rules[ 'stdlib/jsdoc-doctest' ] = 'off'; /** -* Allow requiring the package under test as a whole when only a single property is used. +* When testing, allow requiring the package as a whole even though only a single property is used. * * @private */ diff --git a/etc/eslint/rules/stdlib.js b/etc/eslint/rules/stdlib.js index c4f3222f12d0..1c84b79de46c 100644 --- a/etc/eslint/rules/stdlib.js +++ b/etc/eslint/rules/stdlib.js @@ -4590,7 +4590,11 @@ rules[ 'stdlib/no-require-index' ] = 'error'; rules[ 'stdlib/no-self-require' ] = 'error'; /** -* Enforce that a property is required directly when only a single property of a required module is used. Requiring a property directly reduces bundle sizes during ESM tree-shaking via named imports. +* Enforce that a property is required directly when only a single property of a required module is used. +* +* ## Notes +* +* - Requiring a property directly reduces bundle sizes during ESM tree-shaking via named imports. * * @name no-single-property-require * @memberof rules