From 57605a2d2d3ebf40d207dc2e779f0d9a43ba26d1 Mon Sep 17 00:00:00 2001 From: Bryan Mishkin <698306+bmish@users.noreply.github.com> Date: Sat, 20 Aug 2022 10:57:27 -0400 Subject: [PATCH] feat: add TypeScript type-checking to JavaScript files and export type declarations --- .eslintrc.js | 8 + .gitignore | 3 + lib/rules/no-assert-ok-find.js | 23 +- lib/rules/no-handlebar-interpolation.js | 4 + lib/rules/no-missing-tests.js | 4 +- lib/rules/no-restricted-files.js | 5 +- lib/rules/no-test-return-value.js | 10 +- lib/rules/no-translation-key-interpolation.js | 37 ++- lib/rules/require-await-function.js | 20 +- lib/rules/use-call-count-test-assert.js | 41 ++- lib/rules/use-ember-find.js | 6 +- lib/utils/get-parent-function-node.js | 4 + lib/utils/import.js | 46 +-- lib/utils/is-identifier.js | 22 -- lib/utils/is-literal.js | 32 -- lib/utils/is-member-expression.js | 30 -- lib/utils/is-string-literal.js | 7 - lib/utils/is-test-file.js | 4 + lib/utils/is-test-hook.js | 5 + package.json | 13 +- tests/helpers/babel-eslint-parser.js | 8 + tests/helpers/faux-context.js | 6 + tests/lib/rules/use-call-count-test-assert.js | 3 + tests/lib/utils/imports.js | 31 +- tests/lib/utils/is-literal.js | 48 --- tests/rule-setup.js | 14 +- tsconfig.json | 17 + types/global.d.ts | 12 + yarn.lock | 302 +++++++++++++++--- 29 files changed, 500 insertions(+), 265 deletions(-) delete mode 100644 lib/utils/is-identifier.js delete mode 100644 lib/utils/is-literal.js delete mode 100644 lib/utils/is-member-expression.js delete mode 100644 lib/utils/is-string-literal.js delete mode 100644 tests/lib/utils/is-literal.js create mode 100644 tsconfig.json create mode 100644 types/global.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 51ed3f4d..c3f6254e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -44,5 +44,13 @@ module.exports = { 'unicorn/filename-case': 'off', }, }, + { + parser: '@typescript-eslint/parser', + files: ['*.ts'], + extends: ['plugin:@typescript-eslint/recommended'], + rules: { + 'node/no-unsupported-features/es-syntax': 'off', + }, + }, ], }; diff --git a/.gitignore b/.gitignore index cf356fda..05231e10 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ node_modules # eslint .eslintcache + +# TypeScript output +dist/ diff --git a/lib/rules/no-assert-ok-find.js b/lib/rules/no-assert-ok-find.js index c12f32dd..7e9f5df8 100644 --- a/lib/rules/no-assert-ok-find.js +++ b/lib/rules/no-assert-ok-find.js @@ -1,6 +1,5 @@ 'use strict'; -const isMemberExpression = require('../utils/is-member-expression'); const isTestFile = require('../utils/is-test-file'); const { ReferenceTracker } = require('eslint-utils'); @@ -36,7 +35,17 @@ module.exports = { for (const { node } of tracker.iterateGlobalReferences(traceMap)) { const parentNode = node.parent; - if (isAssertOk(parentNode)) { + if ( + // Check for assert.ok(...) + parentNode.type === 'CallExpression' && + parentNode.callee.type === 'MemberExpression' && + parentNode.callee.object.type === 'Identifier' && + parentNode.callee.object.name === 'assert' && + parentNode.callee.property.type === 'Identifier' && + parentNode.callee.property.name === 'ok' && + (parentNode.arguments.length === 1 || + parentNode.arguments.length === 2) + ) { context.report({ node: parentNode, messageId: 'error', @@ -68,13 +77,3 @@ module.exports = { }; }, }; - -function isAssertOk(node) { - return ( - node.type === 'CallExpression' && - isMemberExpression(node.callee) && - node.callee.object.name === 'assert' && - node.callee.property.name === 'ok' && - (node.arguments.length === 1 || node.arguments.length === 2) - ); -} diff --git a/lib/rules/no-handlebar-interpolation.js b/lib/rules/no-handlebar-interpolation.js index 9a2193da..d8629098 100644 --- a/lib/rules/no-handlebar-interpolation.js +++ b/lib/rules/no-handlebar-interpolation.js @@ -37,6 +37,7 @@ module.exports = { // Defaults to anything `*.js` const defaultFilePattern = /\.js$/; // Only run on files that match filePatterns. + /** @type {{filePatterns: RegExp[]}} */ const { filePatterns = [defaultFilePattern] } = context.options[0] || {}; const matchesFilename = filePatterns.some((pattern) => { return pattern.test(context.getFilename()); @@ -46,6 +47,9 @@ module.exports = { return {}; } + /** + * @param {import('estree').Literal} node + */ function checkNode(node) { const { value } = node; if (typeof value === 'string' && tripleStashRegex.test(value)) { diff --git a/lib/rules/no-missing-tests.js b/lib/rules/no-missing-tests.js index b04ac4b6..317e2548 100644 --- a/lib/rules/no-missing-tests.js +++ b/lib/rules/no-missing-tests.js @@ -44,7 +44,9 @@ module.exports = { }, }, create(context) { - const matchingLocation = context.options[0].find((location) => + /** @type {{filePath:string,testPaths:string[],hasTestSuffix?:boolean}[]} */ + const config = context.options[0]; + const matchingLocation = config.find((location) => context.getFilename().includes(location.filePath) ); diff --git a/lib/rules/no-restricted-files.js b/lib/rules/no-restricted-files.js index 2ab9660b..4e9997b6 100644 --- a/lib/rules/no-restricted-files.js +++ b/lib/rules/no-restricted-files.js @@ -40,12 +40,15 @@ module.exports = { create(context) { return { Program(node) { - const match = context.options.find((option) => + /** @type {{message?:string,paths:string[]}[]} */ + const config = context.options; + const match = config.find((option) => option.paths.some((path) => context.getFilename().match(path)) ); if (match) { context.report({ node, + // @ts-ignore -- `messageId` is optional. messageId: match.message ? undefined : 'defaultError', message: match.message, // eslint-disable-line eslint-plugin/prefer-message-ids // Uncomment this autofixer to grandfather in existing files. diff --git a/lib/rules/no-test-return-value.js b/lib/rules/no-test-return-value.js index 2c72ada6..1d5226a6 100644 --- a/lib/rules/no-test-return-value.js +++ b/lib/rules/no-test-return-value.js @@ -22,7 +22,7 @@ const DEFAULT_TEST_HOOKS = [ 'todo', ]; -/** @type {import('eslint').Rule.RuleModule} */ +/** @type {import('eslint').Rule.RuleModule & { DEFAULT_TEST_HOOKS: string[] }} */ module.exports = { DEFAULT_TEST_HOOKS, meta: { @@ -80,6 +80,11 @@ module.exports = { { messageId: 'suggest', fix(fixer) { + if (!node.range || !node.argument || !node.argument.range) { + throw new Error( + 'This is just to make TypeScript happy. Every node should have a range and we already checked that the node has an argument.' + ); + } return fixer.removeRange([ node.range[0], node.argument.range[0], @@ -94,6 +99,9 @@ module.exports = { }, }; +/** + * @param {import('eslint').Rule.RuleContext} context + */ function getTestHooks(context) { return context.options && context.options[0] && context.options[0].testHooks; } diff --git a/lib/rules/no-translation-key-interpolation.js b/lib/rules/no-translation-key-interpolation.js index 9861390e..33c21b06 100644 --- a/lib/rules/no-translation-key-interpolation.js +++ b/lib/rules/no-translation-key-interpolation.js @@ -1,7 +1,5 @@ 'use strict'; -const isIdentifier = require('../utils/is-identifier'); -const isMemberExpression = require('../utils/is-member-expression'); const { getImportIdentifier } = require('../utils/import'); const DEFAULT_SERVICE_NAME = 'intl'; @@ -46,6 +44,7 @@ module.exports = { context.options && context.options.length > 0 && context.options[0].enforceStringLiteralKeys; + /** @type {string|undefined} */ let importedTranslationMethodName; return { @@ -59,7 +58,8 @@ module.exports = { CallExpression(node) { if ( - (isT(node, importedTranslationMethodName) || + ((importedTranslationMethodName && + isT(node, importedTranslationMethodName)) || isIntlT(node, serviceName) || isThisIntlT(node, serviceName)) && (node.arguments.length === 1 || node.arguments.length === 2) @@ -80,37 +80,52 @@ module.exports = { }, }; +/** + * @param {import('eslint').Rule.Node} node + * @param {string} importedTranslationMethodName + * @returns {boolean} + */ function isT(node, importedTranslationMethodName) { // Example: import { t } from 'intl'; t(...); return ( node.type === 'CallExpression' && - isIdentifier(node.callee) && + node.callee.type === 'Identifier' && node.callee.name === importedTranslationMethodName ); } +/** + * @param {import('eslint').Rule.Node} node + * @param {string} serviceName + * @returns {boolean} + */ function isIntlT(node, serviceName) { // Example: intl.t(...); return ( node.type === 'CallExpression' && - isMemberExpression(node.callee) && - isIdentifier(node.callee.object) && + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'Identifier' && node.callee.object.name === serviceName && - isIdentifier(node.callee.property) && + node.callee.property.type === 'Identifier' && node.callee.property.name === DEFAULT_METHOD_NAME ); } +/** + * @param {import('eslint').Rule.Node} node + * @param {string} serviceName + * @returns {boolean} + */ function isThisIntlT(node, serviceName) { // Example: this.intl.t(...); return ( node.type === 'CallExpression' && - isMemberExpression(node.callee) && - isMemberExpression(node.callee.object) && + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'MemberExpression' && node.callee.object.object.type === 'ThisExpression' && - isIdentifier(node.callee.object.property) && + node.callee.object.property.type === 'Identifier' && node.callee.object.property.name === serviceName && - isIdentifier(node.callee.property) && + node.callee.property.type === 'Identifier' && node.callee.property.name === DEFAULT_METHOD_NAME ); } diff --git a/lib/rules/require-await-function.js b/lib/rules/require-await-function.js index bbc77652..8800abb8 100644 --- a/lib/rules/require-await-function.js +++ b/lib/rules/require-await-function.js @@ -1,12 +1,9 @@ 'use strict'; -const isIdentifier = require('../utils/is-identifier'); -const isMemberExpression = require('../utils/is-member-expression'); - /** * Checks if the given node is part of a call with the `await` keyword. * - * @param {ASTNode} node - the node to check. + * @param {import('eslint').Rule.Node} node - the node to check. * @returns {boolean} `true` if the node is part of a call with the `await` keyword. */ function isAwaitCall(node) { @@ -16,7 +13,7 @@ function isAwaitCall(node) { return true; } - if (parent.type === 'CallExpression' || isMemberExpression(parent)) { + if (parent.type === 'CallExpression' || parent.type === 'MemberExpression') { // Check to see if the AwaitExpression is still another level above. return isAwaitCall(parent); } @@ -27,7 +24,7 @@ function isAwaitCall(node) { /** * Checks if the given node is part of a call with the `return` keyword or direct arrow return. * - * @param {ASTNode} node - the node to check. + * @param {import('eslint').Rule.Node} node - the node to check. * @returns {boolean} `true` if the node is part of a returned call */ function isReturnCall(node) { @@ -40,7 +37,7 @@ function isReturnCall(node) { return true; } - if (parent.type === 'CallExpression' || isMemberExpression(parent)) { + if (parent.type === 'CallExpression' || parent.type === 'MemberExpression') { return isReturnCall(parent); } @@ -50,7 +47,7 @@ function isReturnCall(node) { /** * Checks if the given node is a nested call * - * @param {ASTNode} node - the node to check. + * @param {import('eslint').Rule.Node} node - the node to check. * @returns {boolean} `true` if the node is nested within another function */ function isNestedCall(node, isPromiseChain = false) { @@ -64,7 +61,7 @@ function isNestedCall(node, isPromiseChain = false) { } } - if (isMemberExpression(parent)) { + if (parent.type === 'MemberExpression') { return isNestedCall(parent, true); } @@ -102,10 +99,9 @@ module.exports = { create(context) { return { CallExpression(node) { - const callee = node.callee; if ( - !isIdentifier(callee) || - !context.options[0].functions.includes(callee.name) + node.callee.type !== 'Identifier' || + !context.options[0].functions.includes(node.callee.name) ) { // Not one of the specified async functions. return; diff --git a/lib/rules/use-call-count-test-assert.js b/lib/rules/use-call-count-test-assert.js index 230086a1..48c0122d 100644 --- a/lib/rules/use-call-count-test-assert.js +++ b/lib/rules/use-call-count-test-assert.js @@ -12,7 +12,7 @@ const STUB_PROPERTY_NAMES = [ 'called', ]; -/** @type {import('eslint').Rule.RuleModule} */ +/** @type {import('eslint').Rule.RuleModule & { ASSERT_PROPERTY_NAMES: string[], STUB_PROPERTY_NAMES: string[] }} */ module.exports = { ASSERT_PROPERTY_NAMES, STUB_PROPERTY_NAMES, @@ -39,17 +39,22 @@ module.exports = { return { CallExpression(node) { if ( - !node.callee || - !node.callee.object || + node.callee.type !== 'MemberExpression' || + node.callee.object.type !== 'Identifier' || node.callee.object.name !== 'assert' ) { return; } - const assertPropertyName = node.callee.property - ? node.callee.property.name - : null; - if (!ASSERT_PROPERTY_NAMES.includes(assertPropertyName)) { + const assertPropertyName = + node.callee.type === 'MemberExpression' && + node.callee.property.type === 'Identifier' + ? node.callee.property.name + : null; + if ( + !assertPropertyName || + !ASSERT_PROPERTY_NAMES.includes(assertPropertyName) + ) { return; } @@ -57,10 +62,15 @@ module.exports = { return; } - const stubPropertyName = node.arguments[0].property - ? node.arguments[0].property.name - : null; - if (!STUB_PROPERTY_NAMES.includes(stubPropertyName)) { + const stubPropertyName = + node.arguments[0].type === 'MemberExpression' && + node.arguments[0].property.type === 'Identifier' + ? node.arguments[0].property.name + : null; + if ( + !stubPropertyName || + !STUB_PROPERTY_NAMES.includes(stubPropertyName) + ) { return; } @@ -81,6 +91,13 @@ module.exports = { const text = sourceCode.getText(node); // Get `this.myStub` out of the `this.myStub.calledOnce` parameter. + if ( + node.arguments[0].type !== 'MemberExpression' || + !node.arguments[0].object.range || + !node.range + ) { + return null; + } const stub = text.slice( node.arguments[0].object.range[0] - node.range[0], node.arguments[0].object.range[1] - node.range[0] @@ -93,7 +110,7 @@ module.exports = { // Retrieve the optional message parameter. const optionalMessageParameter = - node.arguments.length === 2 + node.arguments.length === 2 && node.arguments[1].range ? text.slice( node.arguments[1].range[0] - node.range[0], node.arguments[1].range[1] - node.range[0] diff --git a/lib/rules/use-ember-find.js b/lib/rules/use-ember-find.js index b3355b40..2e7296ff 100644 --- a/lib/rules/use-ember-find.js +++ b/lib/rules/use-ember-find.js @@ -1,6 +1,5 @@ 'use strict'; -const isStringLiteral = require('../utils/is-string-literal'); const isTestFile = require('../utils/is-test-file'); /** @type {import('eslint').Rule.RuleModule} */ @@ -89,7 +88,7 @@ module.exports = { /** * Check that a node represents a call to find elements using jQuery. * - * @param {ASTNode} node + * @param {import('eslint').Rule.Node} node * @return {boolean} */ function isJQueryCallWithSelector(node) { @@ -99,6 +98,7 @@ function isJQueryCallWithSelector(node) { (node.callee.name === '$' || node.callee.name === 'jQuery') && node.arguments.length === 1 && (node.arguments[0].type === 'TemplateLiteral' || - isStringLiteral(node.arguments[0])) + (node.arguments[0].type === 'Literal' && + typeof node.arguments[0].value === 'string')) ); } diff --git a/lib/utils/get-parent-function-node.js b/lib/utils/get-parent-function-node.js index a6c94945..3d56a2ee 100644 --- a/lib/utils/get-parent-function-node.js +++ b/lib/utils/get-parent-function-node.js @@ -6,6 +6,10 @@ const FUNCTION_NODE_TYPES = new Set([ 'FunctionDeclaration', ]); +/** + * @param {import('eslint').Rule.Node} node + * @returns {import('eslint').Rule.Node|undefined} + */ function getParentFunctionNode(node) { let explorer = node.parent; diff --git a/lib/utils/import.js b/lib/utils/import.js index cd3b151f..622c1d33 100644 --- a/lib/utils/import.js +++ b/lib/utils/import.js @@ -6,8 +6,6 @@ */ const assert = require('node:assert'); -const isIdentifier = require('./is-identifier'); -const isMemberExpression = require('./is-member-expression'); module.exports = { getSourceModuleNameForIdentifier, @@ -19,30 +17,42 @@ module.exports = { * Gets the name of the module that an identifier was imported from, * if it was imported * - * @param {Object} context the context of the ESLint rule - * @param {node} node the Identifier to find an import for + * @param {import('eslint').Rule.RuleContext | import('../../tests/helpers/faux-context').FauxContext} context the context of the ESLint rule + * @param {import('estree').Node} node the node to find an import for * @returns {string | undefined} The name of the module the identifier was imported from, if it was imported */ function getSourceModuleNameForIdentifier(context, node) { const sourceModuleName = getSourceModuleName(node); - const [program] = context.getAncestors(node); - const importDeclaration = program.body - .filter((node) => node && node.type === 'ImportDeclaration') - .find((importDeclaration) => + const [program] = context.getAncestors(); + if (program.type !== 'Program') { + return undefined; + } + const importDeclaration = program.body.find( + (importDeclaration) => + importDeclaration.type === 'ImportDeclaration' && importDeclaration.specifiers.some( (specifier) => specifier.local.name === sourceModuleName ) - ); + ); - return importDeclaration ? importDeclaration.source.value : undefined; + return importDeclaration && + importDeclaration.type === 'ImportDeclaration' && + importDeclaration.source.type === 'Literal' && + typeof importDeclaration.source.value === 'string' + ? importDeclaration.source.value + : undefined; } +/** + * @param {import('estree').Node|undefined} node + * @returns {string|undefined} + */ function getSourceModuleName(node) { - if (node && node.type === 'CallExpression' && node.callee) { + if (node?.type === 'CallExpression' && node.callee) { return getSourceModuleName(node.callee); - } else if (isMemberExpression(node) && node.object) { + } else if (node?.type === 'MemberExpression' && node.object) { return getSourceModuleName(node.object); - } else if (isIdentifier(node)) { + } else if (node?.type === 'Identifier') { return node.name; } else { assert( @@ -56,19 +66,19 @@ function getSourceModuleName(node) { /** * Gets an import identifier (either imported or local name) from the specified ImportDeclaration. * - * @param {node} node the ImportDeclaration to find the import identifier for + * @param {import('estree').Node} node the ImportDeclaration to find the import identifier for * @param {string} source the source, or module name string, of the import - * @param {string} [namedImportIdentifier=null] the named import identifier to find (will return the alias of the import, of found) - * @returns {null} if no import is found with that name + * @param {string=} [namedImportIdentifier=null] the named import identifier to find (will return the alias of the import, of found) + * @returns {string|undefined} if no import is found with that name */ -function getImportIdentifier(node, source, namedImportIdentifier = null) { +function getImportIdentifier(node, source, namedImportIdentifier = undefined) { assert( node && node.type === 'ImportDeclaration', `getImportIdentifier should be called with a node that's type is 'ImportDeclaration'. You passed '${node.type}'` ); if (node.source.value !== source) { - return null; + return undefined; } return node.specifiers diff --git a/lib/utils/is-identifier.js b/lib/utils/is-identifier.js deleted file mode 100644 index fff8a094..00000000 --- a/lib/utils/is-identifier.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -/** - * Checks whether the node is an identifier and optionally, its name. - * - * @param {ASTNode} node - * @param {string=} name - * @returns {boolean} - */ -function isIdentifier(node, name) { - if (!node) { - return false; - } - - if (node.type === 'Identifier') { - return name ? node.name === name : true; - } - - return false; -} - -module.exports = isIdentifier; diff --git a/lib/utils/is-literal.js b/lib/utils/is-literal.js deleted file mode 100644 index d3c8b7d0..00000000 --- a/lib/utils/is-literal.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -/** - * Determines whether this AST node is composed entirely of literal values. - * - * @param {ASTNode} node - * @returns {boolean} - */ -function isLiteral(node) { - switch (node.type) { - case 'Literal': - return true; - - case 'ArrayExpression': - return node.elements.every((element) => isLiteral(element)); - - case 'ObjectExpression': - return node.properties.every((property) => - property.computed - ? isLiteral(property.key) && isLiteral(property.value) - : isLiteral(property.value) - ); - - case 'Identifier': - return node.name === 'NaN' || node.name === 'undefined'; - - default: - return false; - } -} - -module.exports = isLiteral; diff --git a/lib/utils/is-member-expression.js b/lib/utils/is-member-expression.js deleted file mode 100644 index 0b6d11f0..00000000 --- a/lib/utils/is-member-expression.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -const isIdentifier = require('./is-identifier'); - -/** - * Determines whether a node is a simple member expression with the given object - * and property. - * - * @param {ASTNode} node - * @param {string} objectName - * @param {string} propertyName - * @returns {boolean} - */ -function isMemberExpression(node, objectName, propertyName) { - if (!objectName && !propertyName) { - return node && node.type === 'MemberExpression'; - } - - return ( - node && - node.type === 'MemberExpression' && - !node.computed && - (objectName === 'this' - ? node.object.type === 'ThisExpression' - : isIdentifier(node.object, objectName)) && - isIdentifier(node.property, propertyName) - ); -} - -module.exports = isMemberExpression; diff --git a/lib/utils/is-string-literal.js b/lib/utils/is-string-literal.js deleted file mode 100644 index 79931a25..00000000 --- a/lib/utils/is-string-literal.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -function isStringLiteral(node) { - return node.type === 'Literal' && typeof node.value === 'string'; -} - -module.exports = isStringLiteral; diff --git a/lib/utils/is-test-file.js b/lib/utils/is-test-file.js index c1cd7533..acf79de6 100644 --- a/lib/utils/is-test-file.js +++ b/lib/utils/is-test-file.js @@ -1,5 +1,9 @@ 'use strict'; +/** + * @param {string} fileName + * @returns {boolean} + */ function isTestFile(fileName) { return fileName.endsWith('-test.js') || fileName.endsWith('-test.ts'); } diff --git a/lib/utils/is-test-hook.js b/lib/utils/is-test-hook.js index a2656b50..9a5f2153 100644 --- a/lib/utils/is-test-hook.js +++ b/lib/utils/is-test-hook.js @@ -1,5 +1,10 @@ 'use strict'; +/** + * @param {import('eslint').Rule.Node} node + * @param {string[]} testHooks + * @returns {boolean} + */ function isTestHook(node, testHooks) { if (node.type !== 'CallExpression') { return false; diff --git a/package.json b/package.json index a4ac6d22..c9685d09 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "license": "Apache-2.0", "exports": "./lib/index.js", "main": "./lib/index.js", + "types": "./dist/lib/index.d.ts", "files": [ + "dist/lib/index.d.ts", "lib" ], "scripts": { @@ -29,6 +31,8 @@ "lint:package-json": "npmPkgJsonLint .", "lint:package-json-sorting": "sort-package-json --check", "lint:package-json-sorting:fix": "sort-package-json package.json", + "lint:types": "tsc", + "prepublishOnly": "tsc", "preversion": "yarn test && yarn lint", "release": "release-it", "test": "nyc --all --check-coverage mocha --recursive tests" @@ -63,6 +67,12 @@ "@babel/plugin-proposal-decorators": "^7.18.10", "@release-it-plugins/lerna-changelog": "^5.0.0", "@square/prettier-config": "^2.0.0", + "@types/eslint": "^8.4.6", + "@types/eslint-utils": "^3.0.2", + "@types/estree": "^1.0.0", + "@types/jest": "^28.1.7", + "@types/requireindex": "^1.2.0", + "@typescript-eslint/parser": "^5.33.1", "eslint": "^8.22.0", "eslint-plugin-eslint-plugin": "^5.0.4", "eslint-plugin-markdown": "^3.0.0", @@ -74,7 +84,8 @@ "npm-run-all": "^4.1.5", "nyc": "^15.1.0", "release-it": "^15.3.0", - "sort-package-json": "^1.57.0" + "sort-package-json": "^1.57.0", + "typescript": "^4.7.4" }, "peerDependencies": { "eslint": ">= 8.18.0" diff --git a/tests/helpers/babel-eslint-parser.js b/tests/helpers/babel-eslint-parser.js index 383fb970..f7963887 100644 --- a/tests/helpers/babel-eslint-parser.js +++ b/tests/helpers/babel-eslint-parser.js @@ -1,5 +1,9 @@ const babelESLint = require('@babel/eslint-parser'); +/** + * @param {string} code + * @returns {import('eslint').AST.Program} + */ function parse(code) { return babelESLint.parse(code, { babelOptions: { @@ -8,6 +12,10 @@ function parse(code) { }); } +/** + * @param {string} code + * @returns {import('eslint').SourceCode} + */ function parseForESLint(code) { return babelESLint.parseForESLint(code, { babelOptions: { diff --git a/tests/helpers/faux-context.js b/tests/helpers/faux-context.js index 7b4a949c..f7384793 100644 --- a/tests/helpers/faux-context.js +++ b/tests/helpers/faux-context.js @@ -12,6 +12,11 @@ const { parseForESLint } = require('../helpers/babel-eslint-parser'); * expected by `getSourceModuleNameForIdentifier` */ class FauxContext { + /** + * @param {string} code + * @param {string} filename + * @param {Function} report + */ constructor(code, filename = '', report = () => {}) { const { ast } = parseForESLint(code); @@ -23,6 +28,7 @@ class FauxContext { /** * Does not build the full tree of "ancestors" for the identifier, but * we only care about the first one; the Program node + * @returns {[import('estree').Program]} */ getAncestors() { return [this.ast]; diff --git a/tests/lib/rules/use-call-count-test-assert.js b/tests/lib/rules/use-call-count-test-assert.js index 969f7ad6..b387d718 100644 --- a/tests/lib/rules/use-call-count-test-assert.js +++ b/tests/lib/rules/use-call-count-test-assert.js @@ -76,6 +76,7 @@ const INVALID_HELPER_USAGES = ASSERT_PROPERTY_NAMES.flatMap( // Test case: basic. const ex1 = { code: `assert.${assertPropertyName}(myStub.${stubPropertyName});`, + /** @type {null|string} */ output: null, errors: [{ messageId: 'error', type: 'CallExpression' }], filename: TEST_FILE_NAME, @@ -84,6 +85,7 @@ const INVALID_HELPER_USAGES = ASSERT_PROPERTY_NAMES.flatMap( // Test case: with more complicated stub path. const ex2 = { code: `assert.${assertPropertyName}(this.prop.myStub.${stubPropertyName});`, + /** @type {null|string} */ output: null, errors: [{ messageId: 'error', type: 'CallExpression' }], filename: TEST_FILE_NAME, @@ -92,6 +94,7 @@ const INVALID_HELPER_USAGES = ASSERT_PROPERTY_NAMES.flatMap( // Test case: passing the optional message parameter. const ex3 = { code: `assert.${assertPropertyName}(myStub.${stubPropertyName}, 'is called the right number of times');`, + /** @type {null|string} */ output: null, errors: [{ messageId: 'error', type: 'CallExpression' }], filename: TEST_FILE_NAME, diff --git a/tests/lib/utils/imports.js b/tests/lib/utils/imports.js index 7b60f9b9..a9fed4b2 100644 --- a/tests/lib/utils/imports.js +++ b/tests/lib/utils/imports.js @@ -5,8 +5,21 @@ const { FauxContext } = require('../../helpers/faux-context'); const importUtils = require('../../../lib/utils/import'); const assert = require('node:assert'); -function parse(code) { - return babelESLintParse(code).body[0].expression; +/** + * @param {string} code + * @returns {import('estree').CallExpression} + */ +function parseCallExpression(code) { + const parsed = babelESLintParse(code); + if ( + parsed.body[0].type === 'ExpressionStatement' && + parsed.body[0].expression.type === 'CallExpression' + ) { + return parsed.body[0].expression; + } + throw new Error( + 'Expected first statement in body to be an ExpressionStatement of a CallExpression.' + ); } describe('getSourceModuleNameForIdentifier', () => { @@ -14,7 +27,7 @@ describe('getSourceModuleNameForIdentifier', () => { const context = new FauxContext( "import { someMethod } from 'some-service';" ); - const node = parse('someMethod()').callee; + const node = parseCallExpression('someMethod()').callee; assert.strictEqual( importUtils.getSourceModuleNameForIdentifier(context, node), 'some-service' @@ -25,7 +38,7 @@ describe('getSourceModuleNameForIdentifier', () => { const context = new FauxContext( "import { someMethod as aliasMethod } from 'some-service';" ); - const node = parse('aliasMethod()').callee; + const node = parseCallExpression('aliasMethod()').callee; assert.strictEqual( importUtils.getSourceModuleNameForIdentifier(context, node), 'some-service' @@ -35,17 +48,17 @@ describe('getSourceModuleNameForIdentifier', () => { describe('getSourceModuleName', () => { it('gets the correct module name with MemberExpression', () => { - const node = parse('DS.Model.extend()').callee; + const node = parseCallExpression('DS.Model.extend()').callee; assert.strictEqual(importUtils.getSourceModuleName(node), 'DS'); }); it('gets the correct module name with Identifier', () => { - const node = parse('Model.extend()').callee; + const node = parseCallExpression('Model.extend()').callee; assert.strictEqual(importUtils.getSourceModuleName(node), 'Model'); }); it('gets the correct module name with CallExpression', () => { - const node = parse('Model.extend()'); + const node = parseCallExpression('Model.extend()'); assert.strictEqual(importUtils.getSourceModuleName(node), 'Model'); }); @@ -53,7 +66,7 @@ describe('getSourceModuleName', () => { try { const node = undefined; importUtils.getSourceModuleName(node); - } catch (err) { + } catch (/** @type {any} */ err) { assert.strictEqual( err.message, '`getSourceModuleName` should only be called on a `CallExpression`, `MemberExpression` or `Identifier`' @@ -69,7 +82,7 @@ describe('getImportIdentifier', () => { .body[0]; assert.strictEqual( importUtils.getImportIdentifier(node, '@ember/object', 'action'), - null + undefined ); }); diff --git a/tests/lib/utils/is-literal.js b/tests/lib/utils/is-literal.js deleted file mode 100644 index 1452d1dd..00000000 --- a/tests/lib/utils/is-literal.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -const assert = require('node:assert'); -const parse = require('espree').parse; -const isLiteral = require('../../../lib/utils/is-literal'); - -describe('isLiteral', function () { - it('treats numbers as literals', function () { - assert.ok(isLiteral(p('0')), '0 is literal'); - assert.ok(isLiteral(p('NaN')), 'NaN is literal'); - }); - - it('treats booleans as literals', function () { - assert.ok(isLiteral(p('true')), 'true is literal'); - assert.ok(isLiteral(p('false')), 'false is literal'); - }); - - it('treats arrays with literal elements as literals', function () { - assert.ok(isLiteral(p('[]')), 'empty array is literal'); - assert.ok(isLiteral(p('[0]')), 'array with number is literal'); - assert.ok( - !isLiteral(p('[function() {}]')), - 'array with function is not literal' - ); - }); - - it('treats objects with literal values as literals', function () { - assert.ok(isLiteral(p('({})')), 'empty object is literal'); - assert.ok(isLiteral(p('({a: 0})')), 'object with number value is literal'); - assert.ok( - isLiteral(p('({[0]: "foo"})')), - 'object with computed literal key is literal' - ); - assert.ok( - !isLiteral(p('({create() {}})')), - 'object with method is not literal' - ); - }); - - it('treats null and undefined as literals', function () { - assert.ok(isLiteral(p('null')), 'null is literal'); - assert.ok(isLiteral(p('undefined')), 'undefined is literal'); - }); -}); - -function p(code) { - return parse(code, { ecmaVersion: 2021 }).body[0].expression; -} diff --git a/tests/rule-setup.js b/tests/rule-setup.js index a9da9dde..2a9e9735 100644 --- a/tests/rule-setup.js +++ b/tests/rule-setup.js @@ -11,6 +11,10 @@ const RULE_NAMES = Object.keys(rules); const RULE_NAMES_EMBER = new Set(Object.keys(configEmber.rules)); RULE_NAMES_EMBER.add('square/require-await-function'); // This rule is enabled in an override. +/** + * @param {import('json-schema').JSONSchema4} jsonSchema + * @returns {string[]} + */ function getAllNamedOptions(jsonSchema) { if (!jsonSchema) { return []; @@ -69,6 +73,11 @@ describe('rules setup is correct', function () { }); describe('rule files', function () { + const TYPE_ANNOTATION = "/** @type {import('eslint').Rule.RuleModule} */"; + const TYPE_ANNOTATION_UNCLOSED = TYPE_ANNOTATION.slice( + 0, + Math.max(0, TYPE_ANNOTATION.lastIndexOf('}')) + ); // Allow for & in the type annotation. for (const ruleName of RULE_NAMES) { const filePath = path.join( __dirname, @@ -82,8 +91,9 @@ describe('rules setup is correct', function () { describe(ruleName, function () { it('should have the right contents', function () { assert.ok( - file.includes("/** @type {import('eslint').Rule.RuleModule} */"), - 'includes jsdoc comment for rule type' + file.includes(TYPE_ANNOTATION) || + file.includes(TYPE_ANNOTATION_UNCLOSED), + `includes jsdoc comment for rule type: ${TYPE_ANNOTATION}` ); }); }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..7aa8f1af --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "module": "commonjs", + "outDir": "./dist", + "strict": true, + "target": "ES2022" + }, + "include": [ + "lib", + "tests", + "types" + ] +} diff --git a/types/global.d.ts b/types/global.d.ts new file mode 100644 index 00000000..b842e0c6 --- /dev/null +++ b/types/global.d.ts @@ -0,0 +1,12 @@ +// Couldn't find types for this package. +declare module '@babel/eslint-parser' { + export function parse( + code: string, + options?: { babelOptions?: { configFile: string } } + ): import('eslint').AST.Program; + + export function parseForESLint( + code: string, + options?: { babelOptions?: { configFile: string } } + ): import('eslint').SourceCode; +} diff --git a/yarn.lock b/yarn.lock index 65f073a2..4564981b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,7 +10,7 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== @@ -333,6 +333,32 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jest/expect-utils@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" + integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== + dependencies: + jest-get-type "^28.0.2" + +"@jest/schemas@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" + integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" + integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== + dependencies: + "@jest/schemas" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -539,6 +565,11 @@ validate-peer-dependencies "^2.0.0" which "^2.0.2" +"@sinclair/typebox@^0.24.1": + version "0.24.28" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.28.tgz#15aa0b416f82c268b1573ab653e4413c965fe794" + integrity sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow== + "@sindresorhus/is@^5.2.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.3.0.tgz#0ec9264cf54a527671d990eb874e030b55b70dcc" @@ -578,6 +609,27 @@ dependencies: "@types/ms" "*" +"@types/eslint-utils@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/eslint-utils/-/eslint-utils-3.0.2.tgz#d5a9eeef2b7c62f396c9034b232386afb5f1abc7" + integrity sha512-NgxYBOYjO5+s+vAKTWgWyBMITF3sxNOmfbRtr3yXA4CZpmfwnOBbqxYO4TGITdhOOf4fSEETEmGpqcoGxdZzjg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*", "@types/eslint@^8.4.6": + version "8.4.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.6.tgz#7976f054c1bccfcf514bff0564c0c41df5c08207" + integrity sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + "@types/glob@^7.1.1": version "7.2.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" @@ -591,12 +643,39 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^28.1.7": + version "28.1.7" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.7.tgz#a680c5d05b69634c2d54a63cb106d7fb1adaba16" + integrity sha512-acDN4VHD40V24tgu0iC44jchXavRNVFXQ/E6Z5XNsswgoSO/4NgsXoEYmPUGookKldlZQyIpmrEXsHI9cA3ZTA== + dependencies: + expect "^28.0.0" + pretty-format "^28.0.0" + "@types/json-buffer@~3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== -"@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== @@ -650,6 +729,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/requireindex@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/requireindex/-/requireindex-1.2.0.tgz#b31ed3719fe0a5824fef9a3165f7743560d26bd5" + integrity sha512-XgAXuN2bI/w2kAN6jwhb5jrrSznOsded+Fz5YC+3Vs3f8UyE3d8FnTvIWe6shZdpz9NMHY/wtEWk17yL0hCSmg== + "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -657,19 +741,36 @@ dependencies: "@types/node" "*" +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^17.0.8": + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.11.tgz#5e10ca33e219807c0eee0f08b5efcba9b6a42c06" + integrity sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@^5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.32.0.tgz#e27e38cffa4a61226327c874a7be965e9a861624" - integrity sha512-CHLuz5Uz7bHP2WgVlvoZGhf0BvFakBJKAD/43Ty0emn4wXWv5k01ND0C0fHcl/Im8Td2y/7h44E9pca9qAu2ew== + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.1.tgz#c0a480d05211660221eda963cc844732fe9b1714" + integrity sha512-S1iZIxrTvKkU3+m63YUOxYPKaP+yWDQrdhxTglVDVEVBf+aCSw85+BmJnyUaQQsk5TXFG/LpBu9fa+LrAQ91fQ== dependencies: - "@typescript-eslint/scope-manager" "5.32.0" - "@typescript-eslint/type-utils" "5.32.0" - "@typescript-eslint/utils" "5.32.0" + "@typescript-eslint/scope-manager" "5.33.1" + "@typescript-eslint/type-utils" "5.33.1" + "@typescript-eslint/utils" "5.33.1" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -677,59 +778,69 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/scope-manager@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.32.0.tgz#763386e963a8def470580cc36cf9228864190b95" - integrity sha512-KyAE+tUON0D7tNz92p1uetRqVJiiAkeluvwvZOqBmW9z2XApmk5WSMV9FrzOroAcVxJZB3GfUwVKr98Dr/OjOg== +"@typescript-eslint/parser@^5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.1.tgz#e4b253105b4d2a4362cfaa4e184e2d226c440ff3" + integrity sha512-IgLLtW7FOzoDlmaMoXdxG8HOCByTBXrB1V2ZQYSEV1ggMmJfAkMWTwUjjzagS6OkfpySyhKFkBw7A9jYmcHpZA== dependencies: - "@typescript-eslint/types" "5.32.0" - "@typescript-eslint/visitor-keys" "5.32.0" + "@typescript-eslint/scope-manager" "5.33.1" + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/typescript-estree" "5.33.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.1.tgz#8d31553e1b874210018ca069b3d192c6d23bc493" + integrity sha512-8ibcZSqy4c5m69QpzJn8XQq9NnqAToC8OdH/W6IXPXv83vRyEDPYLdjAlUx8h/rbusq6MkW4YdQzURGOqsn3CA== + dependencies: + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/visitor-keys" "5.33.1" -"@typescript-eslint/type-utils@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.32.0.tgz#45a14506fe3fb908600b4cef2f70778f7b5cdc79" - integrity sha512-0gSsIhFDduBz3QcHJIp3qRCvVYbqzHg8D6bHFsDMrm0rURYDj+skBK2zmYebdCp+4nrd9VWd13egvhYFJj/wZg== +"@typescript-eslint/type-utils@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.1.tgz#1a14e94650a0ae39f6e3b77478baff002cec4367" + integrity sha512-X3pGsJsD8OiqhNa5fim41YtlnyiWMF/eKsEZGsHID2HcDqeSC5yr/uLOeph8rNF2/utwuI0IQoAK3fpoxcLl2g== dependencies: - "@typescript-eslint/utils" "5.32.0" + "@typescript-eslint/utils" "5.33.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.32.0.tgz#484273021eeeae87ddb288f39586ef5efeb6dcd8" - integrity sha512-EBUKs68DOcT/EjGfzywp+f8wG9Zw6gj6BjWu7KV/IYllqKJFPlZlLSYw/PTvVyiRw50t6wVbgv4p9uE2h6sZrQ== +"@typescript-eslint/types@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.1.tgz#3faef41793d527a519e19ab2747c12d6f3741ff7" + integrity sha512-7K6MoQPQh6WVEkMrMW5QOA5FO+BOwzHSNd0j3+BlBwd6vtzfZceJ8xJ7Um2XDi/O3umS8/qDX6jdy2i7CijkwQ== -"@typescript-eslint/typescript-estree@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.32.0.tgz#282943f34babf07a4afa7b0ff347a8e7b6030d12" - integrity sha512-ZVAUkvPk3ITGtCLU5J4atCw9RTxK+SRc6hXqLtllC2sGSeMFWN+YwbiJR9CFrSFJ3w4SJfcWtDwNb/DmUIHdhg== +"@typescript-eslint/typescript-estree@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.1.tgz#a573bd360790afdcba80844e962d8b2031984f34" + integrity sha512-JOAzJ4pJ+tHzA2pgsWQi4804XisPHOtbvwUyqsuuq8+y5B5GMZs7lI1xDWs6V2d7gE/Ez5bTGojSK12+IIPtXA== dependencies: - "@typescript-eslint/types" "5.32.0" - "@typescript-eslint/visitor-keys" "5.32.0" + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/visitor-keys" "5.33.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.32.0.tgz#eccb6b672b94516f1afc6508d05173c45924840c" - integrity sha512-W7lYIAI5Zlc5K082dGR27Fczjb3Q57ECcXefKU/f0ajM5ToM0P+N9NmJWip8GmGu/g6QISNT+K6KYB+iSHjXCQ== +"@typescript-eslint/utils@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.1.tgz#171725f924fe1fe82bb776522bb85bc034e88575" + integrity sha512-uphZjkMaZ4fE8CR4dU7BquOV6u0doeQAr8n6cQenl/poMaIyJtBu8eys5uk6u5HiDH01Mj5lzbJ5SfeDz7oqMQ== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.32.0" - "@typescript-eslint/types" "5.32.0" - "@typescript-eslint/typescript-estree" "5.32.0" + "@typescript-eslint/scope-manager" "5.33.1" + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/typescript-estree" "5.33.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.32.0": - version "5.32.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.32.0.tgz#b9715d0b11fdb5dd10fd0c42ff13987470525394" - integrity sha512-S54xOHZgfThiZ38/ZGTgB2rqx51CMJ5MCfVT2IplK4Q7hgzGfe0nLzLCcenDnc/cSjP568hdeKfeDcBgqNHD/g== +"@typescript-eslint/visitor-keys@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.1.tgz#0155c7571c8cd08956580b880aea327d5c34a18b" + integrity sha512-nwIxOK8Z2MPWltLKMLOEZwmfBZReqUdbEoHQXeCpa+sRVARe5twpJGHCB4dk9903Yaf0nMAlGbQfaAH92F60eg== dependencies: - "@typescript-eslint/types" "5.32.0" + "@typescript-eslint/types" "5.33.1" eslint-visitor-keys "^3.3.0" "@ungap/promise-all-settled@1.1.2": @@ -834,6 +945,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + ansi-styles@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" @@ -1573,6 +1689,11 @@ detect-newline@3.1.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +diff-sequences@^28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" + integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== + diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -1764,6 +1885,11 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + escape-string-regexp@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" @@ -2133,6 +2259,17 @@ execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +expect@^28.0.0: + version "28.1.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" + integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== + dependencies: + "@jest/expect-utils" "^28.1.3" + jest-get-type "^28.0.2" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -2563,7 +2700,7 @@ got@12.3.1, got@^12.1.0: p-cancelable "^3.0.0" responselike "^2.0.0" -graceful-fs@4.2.10, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: +graceful-fs@4.2.10, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -3213,6 +3350,58 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" +jest-diff@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" + integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== + dependencies: + chalk "^4.0.0" + diff-sequences "^28.1.1" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-get-type@^28.0.2: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" + integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== + +jest-matcher-utils@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" + integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== + dependencies: + chalk "^4.0.0" + jest-diff "^28.1.3" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" + integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" + integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4652,7 +4841,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -4708,6 +4897,16 @@ prettier@^2.7.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== +pretty-format@^28.0.0, pretty-format@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" + integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== + dependencies: + "@jest/schemas" "^28.1.3" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-on-spawn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" @@ -4850,6 +5049,11 @@ react-is@^16.13.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -5361,6 +5565,13 @@ ssri@^8.0.0, ssri@^8.0.1: dependencies: minipass "^3.1.1" +stack-utils@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" + integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + dependencies: + escape-string-regexp "^2.0.0" + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -5678,6 +5889,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typescript@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"