From f4b69d80950596d4fb6e018289d92d25c9a82d87 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Sun, 11 Aug 2024 08:49:48 -0400 Subject: [PATCH 1/2] chore: ignore Aider files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7fd209929a..233fef9ea1 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,4 @@ dist/ /packages-graph* api-docs +.aider* From 70e4e55963c911222cf8243a5e126e9ce8319942 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Sun, 11 Aug 2024 08:54:06 -0400 Subject: [PATCH 2/2] feat: destructured exports in harden-exports --- packages/eslint-plugin/lib/index.js | 2 +- .../eslint-plugin/lib/rules/harden-exports.js | 58 ++++++++++++++----- .../eslint-plugin/test/harden-exports.test.js | 45 ++++++++++++++ 3 files changed, 91 insertions(+), 14 deletions(-) diff --git a/packages/eslint-plugin/lib/index.js b/packages/eslint-plugin/lib/index.js index 961aa15644..42212a159a 100644 --- a/packages/eslint-plugin/lib/index.js +++ b/packages/eslint-plugin/lib/index.js @@ -8,7 +8,7 @@ // Requirements //------------------------------------------------------------------------------ -var requireIndex = require('requireindex'); +const requireIndex = require('requireindex'); //------------------------------------------------------------------------------ // Plugin Definition diff --git a/packages/eslint-plugin/lib/rules/harden-exports.js b/packages/eslint-plugin/lib/rules/harden-exports.js index 87a87930b0..0c2663dd60 100644 --- a/packages/eslint-plugin/lib/rules/harden-exports.js +++ b/packages/eslint-plugin/lib/rules/harden-exports.js @@ -46,23 +46,39 @@ module.exports = { /** @type {string[]} */ let exportNames = []; if (exportNode.declaration) { - // @ts-expect-error xxx typedef - if (exportNode.declaration.declarations) { - // @ts-expect-error xxx typedef + if (exportNode.declaration.type === 'VariableDeclaration') { for (const declaration of exportNode.declaration.declarations) { if (declaration.id.type === 'ObjectPattern') { for (const prop of declaration.id.properties) { - exportNames.push(prop.key.name); + if (prop.type === 'RestElement') { + console.warn('Rest elements are not supported'); + continue; + } + if (prop.value.type === 'Identifier') { + exportNames.push(prop.value.name); + } else if ( + prop.value.type === 'AssignmentPattern' && + prop.value.left.type === 'Identifier' + ) { + exportNames.push(prop.value.left.name); + } } - } else { + } else if (declaration.id.type === 'ArrayPattern') { + for (const element of declaration.id.elements) { + if (element && element.type === 'Identifier') { + exportNames.push(element.name); + } + } + } else if (declaration.id.type === 'Identifier') { exportNames.push(declaration.id.name); } } } else if (exportNode.declaration.type === 'FunctionDeclaration') { + const nodeName = exportNode.declaration.id?.name ?? ''; context.report({ node: exportNode, // The 'function' keyword hoisting makes the valuable mutable before it can be hardened. - message: `Export '${exportNode.declaration.id.name}' should be a const declaration with an arrow function.`, + message: `Export '${nodeName}' should be a const declaration with an arrow function.`, }); } } else if (exportNode.specifiers) { @@ -73,17 +89,33 @@ module.exports = { const missingHardenCalls = []; for (const exportName of exportNames) { - const hasHardenCall = sourceCode.ast.body.some(statement => { - return ( + const hasHardenCall = sourceCode.ast.body.some( + statement => statement.type === 'ExpressionStatement' && statement.expression.type === 'CallExpression' && - // @ts-expect-error xxx typedef + statement.expression.callee.type === 'Identifier' && statement.expression.callee.name === 'harden' && statement.expression.arguments.length === 1 && - // @ts-expect-error xxx typedef - statement.expression.arguments[0].name === exportName - ); - }); + ((statement.expression.arguments[0].type === 'Identifier' && + statement.expression.arguments[0].name === exportName) || + // @ts-expect-error XXX non-overlapping + (statement.expression.arguments[0].type === 'ObjectPattern' && + // @ts-expect-error XXX non-overlapping + statement.expression.arguments[0].properties.some( + prop => + prop.value.type === 'Identifier' && + prop.value.name === exportName, + )) || + // @ts-expect-error XXX non-overlapping + (statement.expression.arguments[0].type === 'ArrayPattern' && + // @ts-expect-error XXX non-overlapping + statement.expression.arguments[0].elements.some( + element => + element && + element.type === 'Identifier' && + element.name === exportName, + ))), + ); if (!hasHardenCall) { missingHardenCalls.push(exportName); diff --git a/packages/eslint-plugin/test/harden-exports.test.js b/packages/eslint-plugin/test/harden-exports.test.js index bd00f69dca..2a97f62c2a 100644 --- a/packages/eslint-plugin/test/harden-exports.test.js +++ b/packages/eslint-plugin/test/harden-exports.test.js @@ -30,6 +30,19 @@ harden(getEnvironmentOptionsList); harden(environmentOptionsListHas); `, }, + { + code: ` +export const { propName: exportName } = objWithPropName; +harden(exportName); + `, + }, + { + code: ` +export const [ item1, item2 ] = [fn1, fn2]; +harden(item1); +harden(item2); + `, + }, ]; const invalid = [ @@ -180,6 +193,38 @@ harden(getEnvironmentOptionsList); harden(environmentOptionsListHas); `, }, + { + code: ` +export const { propName: exportName } = objWithPropName; + `, + errors: [ + { + message: + "Named export 'exportName' should be followed by a call to 'harden'.", + }, + ], + output: ` +export const { propName: exportName } = objWithPropName; +harden(exportName); + `, + }, + { + code: ` +export const [ item1, item2 ] = [fn1, fn2]; +harden(item1); + `, + errors: [ + { + message: + "Named export 'item2' should be followed by a call to 'harden'.", + }, + ], + output: ` +export const [ item1, item2 ] = [fn1, fn2]; +harden(item2); +harden(item1); + `, + }, ]; const jsTester = new RuleTester({