From b8329a3ffcbdc8d897d2b6fd9dab6f1e53edd043 Mon Sep 17 00:00:00 2001 From: Matt Bierner <12821956+mjbvz@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:38:15 -0800 Subject: [PATCH] Run TS eslint rules directly with strip-types Wth node 20.18, we can now run these typescript files directly instead of having to use ts-node --- .eslint-plugin-local/code-amd-node-module.ts | 8 +++--- .../code-declare-service-brand.ts | 4 +-- ...code-ensure-no-disposables-leak-in-test.ts | 8 +++--- .eslint-plugin-local/code-import-patterns.ts | 12 ++++----- .eslint-plugin-local/code-layering.ts | 9 +++---- .../code-limited-top-functions.ts | 8 +++--- .eslint-plugin-local/code-must-use-result.ts | 6 ++--- .../code-must-use-super-dispose.ts | 6 ++--- .eslint-plugin-local/code-no-any-casts.ts | 2 +- .../code-no-dangerous-type-assertions.ts | 4 +-- .../code-no-deep-import-of-internal.ts | 4 +-- .../code-no-global-document-listener.ts | 2 +- .eslint-plugin-local/code-no-in-operator.ts | 4 +-- .../code-no-native-private.ts | 4 +-- .../code-no-nls-in-standalone-editor.ts | 4 +-- ...e-no-observable-get-in-reactive-context.ts | 4 +-- .../code-no-potentially-unsafe-disposables.ts | 4 +-- .../code-no-reader-after-await.ts | 4 +-- .../code-no-runtime-import.ts | 8 +++--- .../code-no-standalone-editor.ts | 4 +-- .../code-no-static-self-ref.ts | 4 +-- .../code-no-test-async-suite.ts | 4 +-- .eslint-plugin-local/code-no-test-only.ts | 4 +-- .../code-no-unexternalized-strings.ts | 8 +++--- .../code-no-unused-expressions.ts | 8 +++--- ...erties-must-have-explicit-accessibility.ts | 2 +- .../code-policy-localization-key-match.ts | 4 +-- .../code-translation-remind.ts | 4 +-- .eslint-plugin-local/index.js | 25 ------------------- .eslint-plugin-local/index.ts | 20 +++++++++++++++ .eslint-plugin-local/package.json | 2 +- .eslint-plugin-local/tsconfig.json | 12 ++++----- .eslint-plugin-local/utils.ts | 2 +- .../vscode-dts-cancellation.ts | 4 +-- .../vscode-dts-create-func.ts | 6 ++--- .../vscode-dts-event-naming.ts | 8 +++--- .../vscode-dts-interface-naming.ts | 6 ++--- .../vscode-dts-literal-or-types.ts | 2 +- .../vscode-dts-provider-naming.ts | 8 +++--- .../vscode-dts-string-type-literals.ts | 4 +-- .eslint-plugin-local/vscode-dts-use-export.ts | 4 +-- .../vscode-dts-use-thenable.ts | 4 +-- .../vscode-dts-vscode-in-comments.ts | 4 +-- eslint.config.js | 5 ++-- 44 files changed, 127 insertions(+), 136 deletions(-) delete mode 100644 .eslint-plugin-local/index.js create mode 100644 .eslint-plugin-local/index.ts diff --git a/.eslint-plugin-local/code-amd-node-module.ts b/.eslint-plugin-local/code-amd-node-module.ts index ea427658612..eb6a40c5e30 100644 --- a/.eslint-plugin-local/code-amd-node-module.ts +++ b/.eslint-plugin-local/code-amd-node-module.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; +import { readFileSync } from 'fs'; import { join } from 'path'; -export = new class ApiProviderNaming implements eslint.Rule.RuleModule { +export default new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { @@ -22,7 +23,8 @@ export = new class ApiProviderNaming implements eslint.Rule.RuleModule { const modules = new Set(); try { - const { dependencies, optionalDependencies } = require(join(__dirname, '../package.json')); + const packageJson = JSON.parse(readFileSync(join(import.meta.dirname, '../package.json'), 'utf-8')); + const { dependencies, optionalDependencies } = packageJson; const all = Object.keys(dependencies).concat(Object.keys(optionalDependencies)); for (const key of all) { modules.add(key); diff --git a/.eslint-plugin-local/code-declare-service-brand.ts b/.eslint-plugin-local/code-declare-service-brand.ts index 0aa0dab2a6d..a077e7b38c6 100644 --- a/.eslint-plugin-local/code-declare-service-brand.ts +++ b/.eslint-plugin-local/code-declare-service-brand.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class DeclareServiceBrand implements eslint.Rule.RuleModule { +export default new class DeclareServiceBrand implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { fixable: 'code', diff --git a/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts b/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts index c657df9bd30..7f1d20482b8 100644 --- a/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts +++ b/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { Node } from 'estree'; +import type * as estree from 'estree'; -export = new class EnsureNoDisposablesAreLeakedInTestSuite implements eslint.Rule.RuleModule { +export default new class EnsureNoDisposablesAreLeakedInTestSuite implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { type: 'problem', @@ -18,7 +18,7 @@ export = new class EnsureNoDisposablesAreLeakedInTestSuite implements eslint.Rul }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - const config = <{ exclude: string[] }>context.options[0]; + const config = context.options[0] as { exclude: string[] }; const needle = context.getFilename().replace(/\\/g, '/'); if (config.exclude.some((e) => needle.endsWith(e))) { @@ -26,7 +26,7 @@ export = new class EnsureNoDisposablesAreLeakedInTestSuite implements eslint.Rul } return { - [`Program > ExpressionStatement > CallExpression[callee.name='suite']`]: (node: Node) => { + [`Program > ExpressionStatement > CallExpression[callee.name='suite']`]: (node: estree.Node) => { const src = context.getSourceCode().getText(node); if (!src.includes('ensureNoDisposablesAreLeakedInTestSuite(')) { context.report({ diff --git a/.eslint-plugin-local/code-import-patterns.ts b/.eslint-plugin-local/code-import-patterns.ts index e4beb9a4738..419e26d41ac 100644 --- a/.eslint-plugin-local/code-import-patterns.ts +++ b/.eslint-plugin-local/code-import-patterns.ts @@ -7,9 +7,9 @@ import * as eslint from 'eslint'; import { TSESTree } from '@typescript-eslint/utils'; import * as path from 'path'; import minimatch from 'minimatch'; -import { createImportRuleListener } from './utils'; +import { createImportRuleListener } from './utils.ts'; -const REPO_ROOT = path.normalize(path.join(__dirname, '../')); +const REPO_ROOT = path.normalize(path.join(import.meta.dirname, '../')); interface ConditionalPattern { when?: 'hasBrowser' | 'hasNode' | 'hasElectron' | 'test'; @@ -31,7 +31,7 @@ interface LayerAllowRule { type RawOption = RawImportPatternsConfig | LayerAllowRule; function isLayerAllowRule(option: RawOption): option is LayerAllowRule { - return !!((option).when && (option).allow); + return !!((option as LayerAllowRule).when && (option as LayerAllowRule).allow); } interface ImportPatternsConfig { @@ -39,7 +39,7 @@ interface ImportPatternsConfig { restrictions: string[]; } -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { @@ -55,7 +55,7 @@ export = new class implements eslint.Rule.RuleModule { }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - const options = context.options; + const options = context.options as RawOption[]; const configs = this._processOptions(options); const relativeFilename = getRelativeFilename(context); @@ -217,7 +217,7 @@ export = new class implements eslint.Rule.RuleModule { configs.push(testConfig); } } else { - configs.push({ target, restrictions: restrictions.filter(r => typeof r === 'string') }); + configs.push({ target, restrictions: restrictions.filter(r => typeof r === 'string') as string[] }); } } this._optionsCache.set(options, configs); diff --git a/.eslint-plugin-local/code-layering.ts b/.eslint-plugin-local/code-layering.ts index f8b769a1bf6..10872ae4b4e 100644 --- a/.eslint-plugin-local/code-layering.ts +++ b/.eslint-plugin-local/code-layering.ts @@ -5,14 +5,14 @@ import * as eslint from 'eslint'; import { join, dirname } from 'path'; -import { createImportRuleListener } from './utils'; +import { createImportRuleListener } from './utils.ts'; type Config = { allowed: Set; disallowed: Set; }; -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { @@ -38,9 +38,7 @@ export = new class implements eslint.Rule.RuleModule { const fileDirname = dirname(context.getFilename()); const parts = fileDirname.split(/\\|\//); - const ruleArgs = >context.options[0]; - - let config: Config | undefined; + const ruleArgs = context.options[0] as Record; let config: Config | undefined; for (let i = parts.length - 1; i >= 0; i--) { if (ruleArgs[parts[i]]) { config = { @@ -91,4 +89,3 @@ export = new class implements eslint.Rule.RuleModule { }); } }; - diff --git a/.eslint-plugin-local/code-limited-top-functions.ts b/.eslint-plugin-local/code-limited-top-functions.ts index 5b8bbc7da90..8c6abacc9d8 100644 --- a/.eslint-plugin-local/code-limited-top-functions.ts +++ b/.eslint-plugin-local/code-limited-top-functions.ts @@ -6,9 +6,9 @@ import * as eslint from 'eslint'; import { dirname, relative } from 'path'; import minimatch from 'minimatch'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { @@ -29,11 +29,11 @@ export = new class implements eslint.Rule.RuleModule { }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - let fileRelativePath = relative(dirname(__dirname), context.getFilename()); + let fileRelativePath = relative(dirname(import.meta.dirname), context.getFilename()); if (!fileRelativePath.endsWith('/')) { fileRelativePath += '/'; } - const ruleArgs = >context.options[0]; + const ruleArgs = context.options[0] as Record; const matchingKey = Object.keys(ruleArgs).find(key => fileRelativePath.startsWith(key) || minimatch(fileRelativePath, key)); if (!matchingKey) { diff --git a/.eslint-plugin-local/code-must-use-result.ts b/.eslint-plugin-local/code-must-use-result.ts index 5f43b87ff12..b97396c7e52 100644 --- a/.eslint-plugin-local/code-must-use-result.ts +++ b/.eslint-plugin-local/code-must-use-result.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; const VALID_USES = new Set([ @@ -12,14 +12,14 @@ const VALID_USES = new Set([ TSESTree.AST_NODE_TYPES.VariableDeclarator, ]); -export = new class MustUseResults implements eslint.Rule.RuleModule { +export default new class MustUseResults implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { schema: false }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - const config = <{ message: string; functions: string[] }[]>context.options[0]; + const config = context.options[0] as { message: string; functions: string[] }[]; const listener: eslint.Rule.RuleListener = {}; for (const { message, functions } of config) { diff --git a/.eslint-plugin-local/code-must-use-super-dispose.ts b/.eslint-plugin-local/code-must-use-super-dispose.ts index ec23445611c..0213d200957 100644 --- a/.eslint-plugin-local/code-must-use-super-dispose.ts +++ b/.eslint-plugin-local/code-must-use-super-dispose.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as eslint from 'eslint'; -import * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; +import * as eslint from 'eslint'; +import type * as ESTree from 'estree'; -export = new class NoAsyncSuite implements eslint.Rule.RuleModule { +export default new class NoAsyncSuite implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { function doesCallSuperDispose(node: TSESTree.MethodDefinition) { diff --git a/.eslint-plugin-local/code-no-any-casts.ts b/.eslint-plugin-local/code-no-any-casts.ts index ec1ea1ec3d5..87c3c9466cd 100644 --- a/.eslint-plugin-local/code-no-any-casts.ts +++ b/.eslint-plugin-local/code-no-any-casts.ts @@ -6,7 +6,7 @@ import * as eslint from 'eslint'; import { TSESTree } from '@typescript-eslint/utils'; -export = new class NoAnyCasts implements eslint.Rule.RuleModule { +export default new class NoAnyCasts implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { return { diff --git a/.eslint-plugin-local/code-no-dangerous-type-assertions.ts b/.eslint-plugin-local/code-no-dangerous-type-assertions.ts index f49c8d2eea3..b2e97943670 100644 --- a/.eslint-plugin-local/code-no-dangerous-type-assertions.ts +++ b/.eslint-plugin-local/code-no-dangerous-type-assertions.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; -export = new class NoDangerousTypeAssertions implements eslint.Rule.RuleModule { +export default new class NoDangerousTypeAssertions implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { return { diff --git a/.eslint-plugin-local/code-no-deep-import-of-internal.ts b/.eslint-plugin-local/code-no-deep-import-of-internal.ts index 4aa5cf4b9c7..cb2d450d2ee 100644 --- a/.eslint-plugin-local/code-no-deep-import-of-internal.ts +++ b/.eslint-plugin-local/code-no-deep-import-of-internal.ts @@ -5,9 +5,9 @@ import * as eslint from 'eslint'; import { join, dirname } from 'path'; -import { createImportRuleListener } from './utils'; +import { createImportRuleListener } from './utils.ts'; -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/code-no-global-document-listener.ts b/.eslint-plugin-local/code-no-global-document-listener.ts index 049426a5a03..ad4ec0da820 100644 --- a/.eslint-plugin-local/code-no-global-document-listener.ts +++ b/.eslint-plugin-local/code-no-global-document-listener.ts @@ -5,7 +5,7 @@ import * as eslint from 'eslint'; -export = new class NoGlobalDocumentListener implements eslint.Rule.RuleModule { +export default new class NoGlobalDocumentListener implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { return { diff --git a/.eslint-plugin-local/code-no-in-operator.ts b/.eslint-plugin-local/code-no-in-operator.ts index 9f2fb11ae80..026a8f5fe7a 100644 --- a/.eslint-plugin-local/code-no-in-operator.ts +++ b/.eslint-plugin-local/code-no-in-operator.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; /** @@ -17,7 +17,7 @@ import { TSESTree } from '@typescript-eslint/utils'; * Exception: Type predicate functions are allowed to use the `in` operator * since they are the standard way to perform runtime type checking. */ -export = new class NoInOperator implements eslint.Rule.RuleModule { +export default new class NoInOperator implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/code-no-native-private.ts b/.eslint-plugin-local/code-no-native-private.ts index 96c326ec84c..5d945ec34f7 100644 --- a/.eslint-plugin-local/code-no-native-private.ts +++ b/.eslint-plugin-local/code-no-native-private.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class ApiProviderNaming implements eslint.Rule.RuleModule { +export default new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts b/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts index c0d60985604..2b3896795a8 100644 --- a/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts +++ b/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts @@ -5,9 +5,9 @@ import * as eslint from 'eslint'; import { join } from 'path'; -import { createImportRuleListener } from './utils'; +import { createImportRuleListener } from './utils.ts'; -export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule { +export default new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/code-no-observable-get-in-reactive-context.ts b/.eslint-plugin-local/code-no-observable-get-in-reactive-context.ts index d96cdd4a31b..94d3a1b4ead 100644 --- a/.eslint-plugin-local/code-no-observable-get-in-reactive-context.ts +++ b/.eslint-plugin-local/code-no-observable-get-in-reactive-context.ts @@ -6,9 +6,9 @@ import { TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; import * as visitorKeys from 'eslint-visitor-keys'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class NoObservableGetInReactiveContext implements eslint.Rule.RuleModule { +export default new class NoObservableGetInReactiveContext implements eslint.Rule.RuleModule { meta: eslint.Rule.RuleMetaData = { type: 'problem', docs: { diff --git a/.eslint-plugin-local/code-no-potentially-unsafe-disposables.ts b/.eslint-plugin-local/code-no-potentially-unsafe-disposables.ts index 95b66aee4ac..bc250df1182 100644 --- a/.eslint-plugin-local/code-no-potentially-unsafe-disposables.ts +++ b/.eslint-plugin-local/code-no-potentially-unsafe-disposables.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; /** * Checks for potentially unsafe usage of `DisposableStore` / `MutableDisposable`. * * These have been the source of leaks in the past. */ -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { function checkVariableDeclaration(inNode: ESTree.Node) { diff --git a/.eslint-plugin-local/code-no-reader-after-await.ts b/.eslint-plugin-local/code-no-reader-after-await.ts index 8e95ee712f8..6d0e8d39b06 100644 --- a/.eslint-plugin-local/code-no-reader-after-await.ts +++ b/.eslint-plugin-local/code-no-reader-after-await.ts @@ -5,9 +5,9 @@ import { TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class NoReaderAfterAwait implements eslint.Rule.RuleModule { +export default new class NoReaderAfterAwait implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { return { 'CallExpression': (node: ESTree.CallExpression) => { diff --git a/.eslint-plugin-local/code-no-runtime-import.ts b/.eslint-plugin-local/code-no-runtime-import.ts index afebe0b0d68..2c53d84f973 100644 --- a/.eslint-plugin-local/code-no-runtime-import.ts +++ b/.eslint-plugin-local/code-no-runtime-import.ts @@ -7,9 +7,9 @@ import { TSESTree } from '@typescript-eslint/typescript-estree'; import * as eslint from 'eslint'; import { dirname, join, relative } from 'path'; import minimatch from 'minimatch'; -import { createImportRuleListener } from './utils'; +import { createImportRuleListener } from './utils.ts'; -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { @@ -30,11 +30,11 @@ export = new class implements eslint.Rule.RuleModule { }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - let fileRelativePath = relative(dirname(__dirname), context.getFilename()); + let fileRelativePath = relative(dirname(import.meta.dirname), context.getFilename()); if (!fileRelativePath.endsWith('/')) { fileRelativePath += '/'; } - const ruleArgs = >context.options[0]; + const ruleArgs = context.options[0] as Record; const matchingKey = Object.keys(ruleArgs).find(key => fileRelativePath.startsWith(key) || minimatch(fileRelativePath, key)); if (!matchingKey) { diff --git a/.eslint-plugin-local/code-no-standalone-editor.ts b/.eslint-plugin-local/code-no-standalone-editor.ts index 36bf48b1417..dca4e22bfb0 100644 --- a/.eslint-plugin-local/code-no-standalone-editor.ts +++ b/.eslint-plugin-local/code-no-standalone-editor.ts @@ -5,9 +5,9 @@ import * as eslint from 'eslint'; import { join } from 'path'; -import { createImportRuleListener } from './utils'; +import { createImportRuleListener } from './utils.ts'; -export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule { +export default new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/code-no-static-self-ref.ts b/.eslint-plugin-local/code-no-static-self-ref.ts index 2ba0bdd7845..9a47f87b9c1 100644 --- a/.eslint-plugin-local/code-no-static-self-ref.ts +++ b/.eslint-plugin-local/code-no-static-self-ref.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; /** * WORKAROUND for https://github.com/evanw/esbuild/issues/3823 */ -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslint-plugin-local/code-no-test-async-suite.ts b/.eslint-plugin-local/code-no-test-async-suite.ts index 763d4d20c78..b53747390b0 100644 --- a/.eslint-plugin-local/code-no-test-async-suite.ts +++ b/.eslint-plugin-local/code-no-test-async-suite.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; function isCallExpression(node: TSESTree.Node): node is TSESTree.CallExpression { @@ -15,7 +15,7 @@ function isFunctionExpression(node: TSESTree.Node): node is TSESTree.FunctionExp return node.type.includes('FunctionExpression'); } -export = new class NoAsyncSuite implements eslint.Rule.RuleModule { +export default new class NoAsyncSuite implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { function hasAsyncSuite(node: ESTree.Node) { diff --git a/.eslint-plugin-local/code-no-test-only.ts b/.eslint-plugin-local/code-no-test-only.ts index a10ad9d5ba8..389d32fe13b 100644 --- a/.eslint-plugin-local/code-no-test-only.ts +++ b/.eslint-plugin-local/code-no-test-only.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class NoTestOnly implements eslint.Rule.RuleModule { +export default new class NoTestOnly implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { return { diff --git a/.eslint-plugin-local/code-no-unexternalized-strings.ts b/.eslint-plugin-local/code-no-unexternalized-strings.ts index 7cf7b2f38ee..a7065cb2a0d 100644 --- a/.eslint-plugin-local/code-no-unexternalized-strings.ts +++ b/.eslint-plugin-local/code-no-unexternalized-strings.ts @@ -5,7 +5,7 @@ import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; function isStringLiteral(node: TSESTree.Node | ESTree.Node | null | undefined): node is TSESTree.StringLiteral { return !!node && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string'; @@ -24,7 +24,7 @@ function isDoubleQuoted(node: TSESTree.StringLiteral): boolean { const enableDoubleToSingleQuoteFixes = false; -export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { +export default new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { private static _rNlsKeys = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/; @@ -100,9 +100,7 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { function visitL10NCall(node: TSESTree.CallExpression) { // localize(key, message) - const [messageNode] = (node).arguments; - - // remove message-argument from doubleQuoted list and make + const [messageNode] = (node as TSESTree.CallExpression).arguments; // remove message-argument from doubleQuoted list and make // sure it is a string-literal if (isStringLiteral(messageNode)) { doubleQuotedStringLiterals.delete(messageNode); diff --git a/.eslint-plugin-local/code-no-unused-expressions.ts b/.eslint-plugin-local/code-no-unused-expressions.ts index 160db2ab21c..c481313a9a2 100644 --- a/.eslint-plugin-local/code-no-unused-expressions.ts +++ b/.eslint-plugin-local/code-no-unused-expressions.ts @@ -13,13 +13,13 @@ import { TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ -module.exports = { +export default { meta: { type: 'suggestion', @@ -141,8 +141,8 @@ module.exports = { return { ExpressionStatement(node: TSESTree.ExpressionStatement) { - if (!isValidExpression(node.expression) && !isDirective(node, context.sourceCode.getAncestors(node as ESTree.Node))) { - context.report({ node: node, message: `Expected an assignment or function call and instead saw an expression. ${node.expression}` }); + if (!isValidExpression(node.expression) && !isDirective(node, context.sourceCode.getAncestors(node as ESTree.Node) as TSESTree.Node[])) { + context.report({ node: node as ESTree.Node, message: `Expected an assignment or function call and instead saw an expression. ${node.expression}` }); } } }; diff --git a/.eslint-plugin-local/code-parameter-properties-must-have-explicit-accessibility.ts b/.eslint-plugin-local/code-parameter-properties-must-have-explicit-accessibility.ts index 36a26557346..f00d3e1435c 100644 --- a/.eslint-plugin-local/code-parameter-properties-must-have-explicit-accessibility.ts +++ b/.eslint-plugin-local/code-parameter-properties-must-have-explicit-accessibility.ts @@ -11,7 +11,7 @@ import * as eslint from 'eslint'; * * This catches a common bug where a service is accidentally made public by simply writing: `readonly prop: Foo` */ -export = new class implements eslint.Rule.RuleModule { +export default new class implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { function check(node: TSESTree.TSParameterProperty) { diff --git a/.eslint-plugin-local/code-policy-localization-key-match.ts b/.eslint-plugin-local/code-policy-localization-key-match.ts index c646f768a3a..10749d5bb00 100644 --- a/.eslint-plugin-local/code-policy-localization-key-match.ts +++ b/.eslint-plugin-local/code-policy-localization-key-match.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; /** * Ensures that localization keys in policy blocks match the keys used in nls.localize() calls. @@ -22,7 +22,7 @@ import * as ESTree from 'estree'; * The key property ('autoApprove2.description') must match the first argument * to nls.localize() ('autoApprove2.description'). */ -export = new class PolicyLocalizationKeyMatch implements eslint.Rule.RuleModule { +export default new class PolicyLocalizationKeyMatch implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/code-translation-remind.ts b/.eslint-plugin-local/code-translation-remind.ts index cceaba4c419..42032321167 100644 --- a/.eslint-plugin-local/code-translation-remind.ts +++ b/.eslint-plugin-local/code-translation-remind.ts @@ -6,10 +6,10 @@ import * as eslint from 'eslint'; import { TSESTree } from '@typescript-eslint/utils'; import { readFileSync } from 'fs'; -import { createImportRuleListener } from './utils'; +import { createImportRuleListener } from './utils.ts'; -export = new class TranslationRemind implements eslint.Rule.RuleModule { +export default new class TranslationRemind implements eslint.Rule.RuleModule { private static NLS_MODULE = 'vs/nls'; diff --git a/.eslint-plugin-local/index.js b/.eslint-plugin-local/index.js deleted file mode 100644 index bc6d9d3c3dc..00000000000 --- a/.eslint-plugin-local/index.js +++ /dev/null @@ -1,25 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -// @ts-check -const glob = require('glob'); -const path = require('path'); - -require('ts-node').register({ - experimentalResolver: true, - transpileOnly: true, - compilerOptions: { - module: 'nodenext', - moduleResolution: 'nodenext', - } -}); - -// Re-export all .ts files as rules -/** @type {Record} */ -const rules = {}; -glob.sync(`${__dirname}/*.ts`).forEach((file) => { - rules[path.basename(file, '.ts')] = require(file); -}); - -exports.rules = rules; diff --git a/.eslint-plugin-local/index.ts b/.eslint-plugin-local/index.ts new file mode 100644 index 00000000000..101733773f0 --- /dev/null +++ b/.eslint-plugin-local/index.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import type { LooseRuleDefinition } from '@typescript-eslint/utils/ts-eslint'; +import glob from 'glob'; +import { createRequire } from 'module'; +import path from 'path'; + +const require = createRequire(import.meta.url); + +// Re-export all .ts files as rules +const rules: Record = {}; +glob.sync(`${import.meta.dirname}/*.ts`) + .filter(file => !file.endsWith('index.ts') && !file.endsWith('utils.ts')) + .map(file => { + rules[path.basename(file, '.ts')] = require(file).default; + }); + +export { rules }; diff --git a/.eslint-plugin-local/package.json b/.eslint-plugin-local/package.json index a03f28ed036..7c3579a1f79 100644 --- a/.eslint-plugin-local/package.json +++ b/.eslint-plugin-local/package.json @@ -1,5 +1,5 @@ { - "type": "commonjs", + "type": "module", "scripts": { "typecheck": "tsgo -p tsconfig.json --noEmit" } diff --git a/.eslint-plugin-local/tsconfig.json b/.eslint-plugin-local/tsconfig.json index a087877ecd4..4f199170a11 100644 --- a/.eslint-plugin-local/tsconfig.json +++ b/.eslint-plugin-local/tsconfig.json @@ -5,27 +5,25 @@ "ES2024" ], "rootDir": ".", - "module": "commonjs", - "esModuleInterop": true, - "alwaysStrict": true, - "allowJs": true, + "module": "esnext", + "allowImportingTsExtensions": true, + "erasableSyntaxOnly": true, + "noEmit": true, "strict": true, "exactOptionalPropertyTypes": false, "useUnknownInCatchVariables": false, "noUnusedLocals": true, "noUnusedParameters": true, "newLine": "lf", - "noEmit": true, "typeRoots": [ "." ] }, "include": [ "./**/*.ts", - "./**/*.js" ], "exclude": [ "node_modules/**", - "tests/**" + "./tests/**" ] } diff --git a/.eslint-plugin-local/utils.ts b/.eslint-plugin-local/utils.ts index 63a5f58917b..e956e679148 100644 --- a/.eslint-plugin-local/utils.ts +++ b/.eslint-plugin-local/utils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; export function createImportRuleListener(validateImport: (node: TSESTree.Literal, value: string) => any): eslint.Rule.RuleListener { diff --git a/.eslint-plugin-local/vscode-dts-cancellation.ts b/.eslint-plugin-local/vscode-dts-cancellation.ts index aabdfcfd05b..dd5ca293727 100644 --- a/.eslint-plugin-local/vscode-dts-cancellation.ts +++ b/.eslint-plugin-local/vscode-dts-cancellation.ts @@ -6,7 +6,7 @@ import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; -export = new class ApiProviderNaming implements eslint.Rule.RuleModule { +export default new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { @@ -21,7 +21,7 @@ export = new class ApiProviderNaming implements eslint.Rule.RuleModule { ['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature[key.name=/^(provide|resolve).+/]']: (node: TSESTree.Node) => { let found = false; - for (const param of (node).params) { + for (const param of (node as TSESTree.TSMethodSignature).params) { if (param.type === AST_NODE_TYPES.Identifier) { found = found || param.name === 'token'; } diff --git a/.eslint-plugin-local/vscode-dts-create-func.ts b/.eslint-plugin-local/vscode-dts-create-func.ts index 3ce5ec07e8c..91589f91584 100644 --- a/.eslint-plugin-local/vscode-dts-create-func.ts +++ b/.eslint-plugin-local/vscode-dts-create-func.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; -export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { +export default new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#creating-objects' }, @@ -20,7 +20,7 @@ export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { return { ['TSDeclareFunction Identifier[name=/create.*/]']: (node: ESTree.Node) => { - const decl = (node).parent; + const decl = (node as TSESTree.Identifier).parent as TSESTree.FunctionDeclaration; if (decl.returnType?.typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) { return; diff --git a/.eslint-plugin-local/vscode-dts-event-naming.ts b/.eslint-plugin-local/vscode-dts-event-naming.ts index 230fdc60332..6f75c50ca12 100644 --- a/.eslint-plugin-local/vscode-dts-event-naming.ts +++ b/.eslint-plugin-local/vscode-dts-event-naming.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; -export = new class ApiEventNaming implements eslint.Rule.RuleModule { +export default new class ApiEventNaming implements eslint.Rule.RuleModule { private static _nameRegExp = /on(Did|Will)([A-Z][a-z]+)([A-Z][a-z]+)?/; @@ -26,14 +26,14 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - const config = <{ allowed: string[]; verbs: string[] }>context.options[0]; + const config = context.options[0] as { allowed: string[]; verbs: string[] }; const allowed = new Set(config.allowed); const verbs = new Set(config.verbs); return { ['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node: ESTree.Identifier) => { - const def = (node).parent?.parent?.parent; + const def = (node as TSESTree.Identifier).parent?.parent?.parent; const ident = this.getIdent(def); if (!ident) { diff --git a/.eslint-plugin-local/vscode-dts-interface-naming.ts b/.eslint-plugin-local/vscode-dts-interface-naming.ts index 85f81720663..d6591b97d8d 100644 --- a/.eslint-plugin-local/vscode-dts-interface-naming.ts +++ b/.eslint-plugin-local/vscode-dts-interface-naming.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; -export = new class ApiInterfaceNaming implements eslint.Rule.RuleModule { +export default new class ApiInterfaceNaming implements eslint.Rule.RuleModule { private static _nameRegExp = /^I[A-Z]/; @@ -23,7 +23,7 @@ export = new class ApiInterfaceNaming implements eslint.Rule.RuleModule { return { ['TSInterfaceDeclaration Identifier']: (node: ESTree.Identifier) => { - const name = (node).name; + const name = (node as TSESTree.Identifier).name; if (ApiInterfaceNaming._nameRegExp.test(name)) { context.report({ node, diff --git a/.eslint-plugin-local/vscode-dts-literal-or-types.ts b/.eslint-plugin-local/vscode-dts-literal-or-types.ts index 2d1dac279df..0815720cf92 100644 --- a/.eslint-plugin-local/vscode-dts-literal-or-types.ts +++ b/.eslint-plugin-local/vscode-dts-literal-or-types.ts @@ -6,7 +6,7 @@ import { TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; -export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { +export default new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#enums' }, diff --git a/.eslint-plugin-local/vscode-dts-provider-naming.ts b/.eslint-plugin-local/vscode-dts-provider-naming.ts index 19338a65ab4..64e0101a71e 100644 --- a/.eslint-plugin-local/vscode-dts-provider-naming.ts +++ b/.eslint-plugin-local/vscode-dts-provider-naming.ts @@ -6,7 +6,7 @@ import { TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; -export = new class ApiProviderNaming implements eslint.Rule.RuleModule { +export default new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { @@ -19,18 +19,18 @@ export = new class ApiProviderNaming implements eslint.Rule.RuleModule { create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - const config = <{ allowed: string[] }>context.options[0]; + const config = context.options[0] as { allowed: string[] }; const allowed = new Set(config.allowed); return { ['TSInterfaceDeclaration[id.name=/.+Provider/] TSMethodSignature']: (node: TSESTree.Node) => { - const interfaceName = ((node).parent?.parent).id.name; + const interfaceName = ((node as TSESTree.Identifier).parent?.parent as TSESTree.TSInterfaceDeclaration).id.name; if (allowed.has(interfaceName)) { // allowed return; } - const methodName = ((node).key as TSESTree.Identifier).name; + const methodName = ((node as TSESTree.TSMethodSignatureNonComputedName).key as TSESTree.Identifier).name; if (!ApiProviderNaming._providerFunctionNames.test(methodName)) { context.report({ diff --git a/.eslint-plugin-local/vscode-dts-string-type-literals.ts b/.eslint-plugin-local/vscode-dts-string-type-literals.ts index 1f8bc96ca17..ee70d663281 100644 --- a/.eslint-plugin-local/vscode-dts-string-type-literals.ts +++ b/.eslint-plugin-local/vscode-dts-string-type-literals.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; import { TSESTree } from '@typescript-eslint/utils'; -export = new class ApiTypeDiscrimination implements eslint.Rule.RuleModule { +export default new class ApiTypeDiscrimination implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines' }, diff --git a/.eslint-plugin-local/vscode-dts-use-export.ts b/.eslint-plugin-local/vscode-dts-use-export.ts index ab19e9eedce..798572d4f21 100644 --- a/.eslint-plugin-local/vscode-dts-use-export.ts +++ b/.eslint-plugin-local/vscode-dts-use-export.ts @@ -5,9 +5,9 @@ import { TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class VscodeDtsUseExport implements eslint.Rule.RuleModule { +export default new class VscodeDtsUseExport implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/vscode-dts-use-thenable.ts b/.eslint-plugin-local/vscode-dts-use-thenable.ts index 40e7d10a45b..2c1ff4c9296 100644 --- a/.eslint-plugin-local/vscode-dts-use-thenable.ts +++ b/.eslint-plugin-local/vscode-dts-use-thenable.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class ApiEventNaming implements eslint.Rule.RuleModule { +export default new class ApiEventNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/.eslint-plugin-local/vscode-dts-vscode-in-comments.ts b/.eslint-plugin-local/vscode-dts-vscode-in-comments.ts index 63c59bf03ae..ab3c338096c 100644 --- a/.eslint-plugin-local/vscode-dts-vscode-in-comments.ts +++ b/.eslint-plugin-local/vscode-dts-vscode-in-comments.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import * as ESTree from 'estree'; +import type * as ESTree from 'estree'; -export = new class ApiVsCodeInComments implements eslint.Rule.RuleModule { +export default new class ApiVsCodeInComments implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { diff --git a/eslint.config.js b/eslint.config.js index eb2bc839305..1e7dfbc33c6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -8,7 +8,7 @@ import path from 'path'; import tseslint from 'typescript-eslint'; import stylisticTs from '@stylistic/eslint-plugin-ts'; -import * as pluginLocal from './.eslint-plugin-local/index.js'; +import * as pluginLocal from './.eslint-plugin-local/index.ts'; import pluginJsdoc from 'eslint-plugin-jsdoc'; import pluginHeader from 'eslint-plugin-header'; @@ -183,7 +183,8 @@ export default tseslint.config( // Disallow 'in' operator except in type predicates { files: [ - '**/*.ts' + '**/*.ts', + '.eslint-plugin-local/**/*.ts', // Explicitly include files under dot directories ], ignores: [ 'src/bootstrap-node.ts',