From f66db57652c9c86c1dcd895f929afd7c5ae7c643 Mon Sep 17 00:00:00 2001 From: tyankatsu <frips.ryilsufupe+dev@gmail.com> Date: Tue, 4 May 2021 01:33:31 +0900 Subject: [PATCH 1/4] feat: add eslint-plugins directory --- src/eslint-plugins/README.md | 1 + src/eslint-plugins/index.ts | 1 + src/eslint-plugins/utils.ts | 0 src/index.ts | 2 ++ 4 files changed, 4 insertions(+) create mode 100644 src/eslint-plugins/README.md create mode 100644 src/eslint-plugins/index.ts create mode 100644 src/eslint-plugins/utils.ts diff --git a/src/eslint-plugins/README.md b/src/eslint-plugins/README.md new file mode 100644 index 00000000..ac2637bd --- /dev/null +++ b/src/eslint-plugins/README.md @@ -0,0 +1 @@ +The code in this directory is designed to be used in ESLint plugins, such as `eslint-plugin-vue`. \ No newline at end of file diff --git a/src/eslint-plugins/index.ts b/src/eslint-plugins/index.ts new file mode 100644 index 00000000..cfa334d6 --- /dev/null +++ b/src/eslint-plugins/index.ts @@ -0,0 +1 @@ +export * from './utils' \ No newline at end of file diff --git a/src/eslint-plugins/utils.ts b/src/eslint-plugins/utils.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/index.ts b/src/index.ts index 92ff7408..398bef8a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ */ import * as path from "path" import * as AST from "./ast" +import * as ESLintPlugins from "./eslint-plugins" import { LocationCalculator } from "./common/location-calculator" import { HTMLParser, HTMLTokenizer } from "./html" import { parseScript, parseScriptElement } from "./script" @@ -149,3 +150,4 @@ export function parse(code: string, options: any): AST.ESLintProgram { } export { AST } +export { ESLintPlugins } \ No newline at end of file From b3bf25df9770f71475ba75fcd6a9ad665605d408 Mon Sep 17 00:00:00 2001 From: tyankatsu <frips.ryilsufupe+dev@gmail.com> Date: Tue, 4 May 2021 16:32:50 +0900 Subject: [PATCH 2/4] feat: move type and util from eslint-plugin-vue --- src/eslint-plugins/global.d.ts | 178 ++ src/eslint-plugins/index.ts | 1 - src/eslint-plugins/types/ast/ast.ts | 343 +++ src/eslint-plugins/types/ast/es-ast.ts | 543 +++++ src/eslint-plugins/types/ast/index.ts | 5 + src/eslint-plugins/types/ast/jsx-ast.ts | 106 + src/eslint-plugins/types/ast/ts-ast.ts | 11 + src/eslint-plugins/types/ast/v-ast.ts | 174 ++ src/eslint-plugins/types/errors.ts | 43 + src/eslint-plugins/types/node/index.ts | 3 + src/eslint-plugins/types/node/locations.ts | 13 + src/eslint-plugins/types/node/node.ts | 11 + src/eslint-plugins/types/node/tokens.ts | 17 + src/eslint-plugins/types/parser-services.ts | 122 + src/eslint-plugins/types/utils.ts | 29 + src/eslint-plugins/utils.ts | 0 src/eslint-plugins/utils/casing.js | 203 ++ .../utils/deprecated-html-elements.json | 1 + src/eslint-plugins/utils/html-comments.js | 259 ++ src/eslint-plugins/utils/html-elements.json | 1 + src/eslint-plugins/utils/indent-common.ts | 2085 +++++++++++++++++ src/eslint-plugins/utils/index.ts | 2068 ++++++++++++++++ .../utils/inline-non-void-elements.json | 40 + src/eslint-plugins/utils/js-reserved.json | 18 + src/eslint-plugins/utils/key-aliases.json | 68 + src/eslint-plugins/utils/keycode-to-key.ts | 98 + src/eslint-plugins/utils/regexp.ts | 35 + src/eslint-plugins/utils/svg-elements.json | 1 + src/eslint-plugins/utils/void-elements.json | 1 + .../utils/vue-component-options.json | 54 + src/eslint-plugins/utils/vue-reserved.json | 4 + 31 files changed, 6534 insertions(+), 1 deletion(-) create mode 100644 src/eslint-plugins/global.d.ts delete mode 100644 src/eslint-plugins/index.ts create mode 100644 src/eslint-plugins/types/ast/ast.ts create mode 100644 src/eslint-plugins/types/ast/es-ast.ts create mode 100644 src/eslint-plugins/types/ast/index.ts create mode 100644 src/eslint-plugins/types/ast/jsx-ast.ts create mode 100644 src/eslint-plugins/types/ast/ts-ast.ts create mode 100644 src/eslint-plugins/types/ast/v-ast.ts create mode 100644 src/eslint-plugins/types/errors.ts create mode 100644 src/eslint-plugins/types/node/index.ts create mode 100644 src/eslint-plugins/types/node/locations.ts create mode 100644 src/eslint-plugins/types/node/node.ts create mode 100644 src/eslint-plugins/types/node/tokens.ts create mode 100644 src/eslint-plugins/types/parser-services.ts create mode 100644 src/eslint-plugins/types/utils.ts delete mode 100644 src/eslint-plugins/utils.ts create mode 100644 src/eslint-plugins/utils/casing.js create mode 100644 src/eslint-plugins/utils/deprecated-html-elements.json create mode 100644 src/eslint-plugins/utils/html-comments.js create mode 100644 src/eslint-plugins/utils/html-elements.json create mode 100644 src/eslint-plugins/utils/indent-common.ts create mode 100644 src/eslint-plugins/utils/index.ts create mode 100644 src/eslint-plugins/utils/inline-non-void-elements.json create mode 100644 src/eslint-plugins/utils/js-reserved.json create mode 100644 src/eslint-plugins/utils/key-aliases.json create mode 100644 src/eslint-plugins/utils/keycode-to-key.ts create mode 100644 src/eslint-plugins/utils/regexp.ts create mode 100644 src/eslint-plugins/utils/svg-elements.json create mode 100644 src/eslint-plugins/utils/void-elements.json create mode 100644 src/eslint-plugins/utils/vue-component-options.json create mode 100644 src/eslint-plugins/utils/vue-reserved.json diff --git a/src/eslint-plugins/global.d.ts b/src/eslint-plugins/global.d.ts new file mode 100644 index 00000000..285b3f83 --- /dev/null +++ b/src/eslint-plugins/global.d.ts @@ -0,0 +1,178 @@ +import * as VAST from './types/ast' +import * as VNODE from './types/node' +import * as parserServices from './types/parser-services' +import * as eslint from 'eslint' + +declare global { + // **** Rule Helpers **** + type RuleModule = eslint.Rule.RuleModule + type RuleContext = eslint.Rule.RuleContext + namespace Rule { + type ReportDescriptor = eslint.Rule.ReportDescriptor + type SuggestionReportDescriptor = eslint.Rule.SuggestionReportDescriptor + } + type SourceCode = eslint.SourceCode + namespace SourceCode { + type CursorWithSkipOptions = eslint.SourceCode.CursorWithSkipOptions + type CursorWithCountOptions = eslint.SourceCode.CursorWithCountOptions + } + type RuleFixer = eslint.Rule.RuleFixer + type Fix = eslint.Rule.Fix + + type NodeListener = eslint.Rule.NodeListener + type RuleListener = eslint.Rule.RuleListener + type TemplateListener = parserServices.TemplateListener + type ParserServices = parserServices.ParserServices + namespace ParserServices { + type TokenStore = parserServices.ParserServices.TokenStore + } + + // **** Node data **** + + type Range = VNODE.Range + type Position = VNODE.Position + type SourceLocation = VNODE.SourceLocation + type Token = VNODE.Token + type Comment = VNODE.Comment + type HTMLComment = VNODE.HTMLComment + type HTMLBogusComment = VNODE.HTMLBogusComment + + type NodeListenerMap = VAST.NodeListenerMap + type VNodeListenerMap = VAST.VNodeListenerMap + + // **** AST nodes **** + + type ASTNode = VAST.ASTNode + type ESNode = VAST.ESNode + type VNode = VAST.VNode + type TSNode = VAST.TSNode + type JSXNode = VAST.JSXNode + + // ---- Vue Template Nodes ---- + + type VAttribute = VAST.VAttribute + type VDirective = VAST.VDirective + type VDirectiveKey = VAST.VDirectiveKey + type VDocumentFragment = VAST.VDocumentFragment + type VElement = VAST.VElement + type VRootElement = VAST.VRootElement + type VEndTag = VAST.VEndTag + type VExpressionContainer = VAST.VExpressionContainer + type VIdentifier = VAST.VIdentifier + type VLiteral = VAST.VLiteral + type VStartTag = VAST.VStartTag + type VText = VAST.VText + type VForExpression = VAST.VForExpression + type VOnExpression = VAST.VOnExpression + type VSlotScopeExpression = VAST.VSlotScopeExpression + type VFilterSequenceExpression = VAST.VFilterSequenceExpression + type VFilter = VAST.VFilter + + // ---- ES Nodes ---- + + type Identifier = VAST.Identifier + type Literal = VAST.Literal + type Program = VAST.Program + type SwitchCase = VAST.SwitchCase + type CatchClause = VAST.CatchClause + type VariableDeclarator = VAST.VariableDeclarator + type Statement = VAST.Statement + type ExpressionStatement = VAST.ExpressionStatement + type BlockStatement = VAST.BlockStatement + type EmptyStatement = VAST.EmptyStatement + type DebuggerStatement = VAST.DebuggerStatement + type WithStatement = VAST.WithStatement + type ReturnStatement = VAST.ReturnStatement + type LabeledStatement = VAST.LabeledStatement + type BreakStatement = VAST.BreakStatement + type ContinueStatement = VAST.ContinueStatement + type IfStatement = VAST.IfStatement + type SwitchStatement = VAST.SwitchStatement + type ThrowStatement = VAST.ThrowStatement + type TryStatement = VAST.TryStatement + type WhileStatement = VAST.WhileStatement + type DoWhileStatement = VAST.DoWhileStatement + type ForStatement = VAST.ForStatement + type ForInStatement = VAST.ForInStatement + type ForOfStatement = VAST.ForOfStatement + type Declaration = VAST.Declaration + type FunctionDeclaration = VAST.FunctionDeclaration + type VariableDeclaration = VAST.VariableDeclaration + type ClassDeclaration = VAST.ClassDeclaration + type Expression = VAST.Expression + type ThisExpression = VAST.ThisExpression + type ArrayExpression = VAST.ArrayExpression + type ObjectExpression = VAST.ObjectExpression + type FunctionExpression = VAST.FunctionExpression + type ArrowFunctionExpression = VAST.ArrowFunctionExpression + type YieldExpression = VAST.YieldExpression + type UnaryExpression = VAST.UnaryExpression + type UpdateExpression = VAST.UpdateExpression + type BinaryExpression = VAST.BinaryExpression + type AssignmentExpression = VAST.AssignmentExpression + type LogicalExpression = VAST.LogicalExpression + type MemberExpression = VAST.MemberExpression + type ConditionalExpression = VAST.ConditionalExpression + type CallExpression = VAST.CallExpression + type NewExpression = VAST.NewExpression + type SequenceExpression = VAST.SequenceExpression + type TemplateLiteral = VAST.TemplateLiteral + type TaggedTemplateExpression = VAST.TaggedTemplateExpression + type ClassExpression = VAST.ClassExpression + type MetaProperty = VAST.MetaProperty + type AwaitExpression = VAST.AwaitExpression + type ChainExpression = VAST.ChainExpression + type ChainElement = VAST.ChainElement + type Property = VAST.Property + type AssignmentProperty = VAST.AssignmentProperty + type Super = VAST.Super + type TemplateElement = VAST.TemplateElement + type SpreadElement = VAST.SpreadElement + type Pattern = VAST.Pattern + type ObjectPattern = VAST.ObjectPattern + type ArrayPattern = VAST.ArrayPattern + type RestElement = VAST.RestElement + type AssignmentPattern = VAST.AssignmentPattern + type ClassBody = VAST.ClassBody + type MethodDefinition = VAST.MethodDefinition + type ModuleDeclaration = VAST.ModuleDeclaration + type ImportDeclaration = VAST.ImportDeclaration + type ExportNamedDeclaration = VAST.ExportNamedDeclaration + type ExportDefaultDeclaration = VAST.ExportDefaultDeclaration + type ExportAllDeclaration = VAST.ExportAllDeclaration + type ModuleSpecifier = VAST.ModuleSpecifier + type ImportSpecifier = VAST.ImportSpecifier + type ImportDefaultSpecifier = VAST.ImportDefaultSpecifier + type ImportNamespaceSpecifier = VAST.ImportNamespaceSpecifier + type ExportSpecifier = VAST.ExportSpecifier + type ImportExpression = VAST.ImportExpression + + // ---- TS Nodes ---- + + type TSAsExpression = VAST.TSAsExpression + + // ---- JSX Nodes ---- + + type JSXAttribute = VAST.JSXAttribute + type JSXClosingElement = VAST.JSXClosingElement + type JSXClosingFragment = VAST.JSXClosingFragment + type JSXElement = VAST.JSXElement + type JSXEmptyExpression = VAST.JSXEmptyExpression + type JSXExpressionContainer = VAST.JSXExpressionContainer + type JSXFragment = VAST.JSXFragment + type JSXIdentifier = VAST.JSXIdentifier + type JSXOpeningElement = VAST.JSXOpeningElement + type JSXOpeningFragment = VAST.JSXOpeningFragment + type JSXSpreadAttribute = VAST.JSXSpreadAttribute + type JSXSpreadChild = VAST.JSXSpreadChild + type JSXMemberExpression = VAST.JSXMemberExpression + type JSXText = VAST.JSXText + + // **** Variables **** + + type VVariable = VAST.VVariable + type VReference = VAST.VReference + + type Variable = eslint.Scope.Variable + type Reference = eslint.Scope.Reference +} diff --git a/src/eslint-plugins/index.ts b/src/eslint-plugins/index.ts deleted file mode 100644 index cfa334d6..00000000 --- a/src/eslint-plugins/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './utils' \ No newline at end of file diff --git a/src/eslint-plugins/types/ast/ast.ts b/src/eslint-plugins/types/ast/ast.ts new file mode 100644 index 00000000..d8f21315 --- /dev/null +++ b/src/eslint-plugins/types/ast/ast.ts @@ -0,0 +1,343 @@ +import * as ES from './es-ast' +import * as V from './v-ast' +import * as TS from './ts-ast' +import * as JSX from './jsx-ast' + +export type ASTNode = ES.ESNode | V.VNode | TS.TSNode | JSX.JSXNode + +export type ParamNode = never // You specify the node type in JSDoc. + +export type VNodeListenerMap = { + VAttribute: V.VAttribute | V.VDirective + 'VAttribute:exit': V.VAttribute | V.VDirective + 'VAttribute[directive=false]': V.VAttribute + 'VAttribute[directive=false]:exit': V.VAttribute + "VAttribute[directive=true][key.name.name='bind']": V.VDirective & { + value: + | (V.VExpressionContainer & { + expression: ES.Expression | V.VFilterSequenceExpression | null + }) + | null + } + "VAttribute[directive=true][key.name.name='bind']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { + expression: ES.Expression | V.VFilterSequenceExpression | null + }) + | null + } + "VAttribute[directive=true][key.name.name='cloak']": V.VDirective + "VAttribute[directive=true][key.name.name='cloak']:exit": V.VDirective + "VAttribute[directive=true][key.name.name='else-if']": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='else-if']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='else']": V.VDirective + "VAttribute[directive=true][key.name.name='else']:exit": V.VDirective + "VAttribute[directive=true][key.name.name='for']": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: V.VForExpression | null }) + | null + } + "VAttribute[directive=true][key.name.name='for']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: V.VForExpression | null }) + | null + } + "VAttribute[directive=true][key.name.name='html']": V.VDirective + "VAttribute[directive=true][key.name.name='html']:exit": V.VDirective + "VAttribute[directive=true][key.name.name='if']": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='if']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='is']": V.VDirective + "VAttribute[directive=true][key.name.name='is']:exit": V.VDirective + "VAttribute[directive=true][key.name.name='model']": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='model']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='on']": V.VDirective & { + value: + | (V.VExpressionContainer & { + expression: ES.Expression | V.VOnExpression | null + }) + | null + } + "VAttribute[directive=true][key.name.name='on']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { + expression: ES.Expression | V.VOnExpression | null + }) + | null + } + "VAttribute[directive=true][key.name.name='once']": V.VDirective + "VAttribute[directive=true][key.name.name='once']:exit": V.VDirective + "VAttribute[directive=true][key.name.name='pre']": V.VDirective + "VAttribute[directive=true][key.name.name='pre']:exit": V.VDirective + "VAttribute[directive=true][key.name.name='show']": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='show']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: ES.Expression | null }) + | null + } + "VAttribute[directive=true][key.name.name='slot']": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: V.VSlotScopeExpression | null }) + | null + } + "VAttribute[directive=true][key.name.name='slot']:exit": V.VDirective & { + value: + | (V.VExpressionContainer & { expression: V.VSlotScopeExpression | null }) + | null + } + "VAttribute[directive=true][key.name.name='text']": V.VDirective + "VAttribute[directive=true][key.name.name='text']:exit": V.VDirective + 'VAttribute[value!=null]': + | (V.VAttribute & { value: VLiteral }) + | (V.VDirective & { value: VExpressionContainer }) + // VDirective: V.VDirective + // 'VDirective:exit': V.VDirective + VDirectiveKey: V.VDirectiveKey + 'VDirectiveKey:exit': V.VDirectiveKey + VElement: V.VElement + 'VElement:exit': V.VElement + VEndTag: V.VEndTag + 'VEndTag:exit': V.VEndTag + VExpressionContainer: V.VExpressionContainer + 'VExpressionContainer:exit': V.VExpressionContainer + VIdentifier: V.VIdentifier + 'VIdentifier:exit': V.VIdentifier + VLiteral: V.VLiteral + 'VLiteral:exit': V.VLiteral + VStartTag: V.VStartTag + 'VStartTag:exit': V.VStartTag + VText: V.VText + 'VText:exit': V.VText + VForExpression: V.VForExpression + 'VForExpression:exit': V.VForExpression + VOnExpression: V.VOnExpression + 'VOnExpression:exit': V.VOnExpression + VSlotScopeExpression: V.VSlotScopeExpression + 'VSlotScopeExpression:exit': V.VSlotScopeExpression + VFilterSequenceExpression: V.VFilterSequenceExpression + 'VFilterSequenceExpression:exit': V.VFilterSequenceExpression + VFilter: V.VFilter + 'VFilter:exit': V.VFilter +} & ESNodeListenerMap +export type NodeListenerMap = { + JSXAttribute: JSX.JSXAttribute + 'JSXAttribute:exit': JSX.JSXAttribute + JSXClosingElement: JSX.JSXClosingElement + 'JSXClosingElement:exit': JSX.JSXClosingElement + JSXClosingFragment: JSX.JSXClosingFragment + 'JSXClosingFragment:exit': JSX.JSXClosingFragment + JSXElement: JSX.JSXElement + 'JSXElement:exit': JSX.JSXElement + JSXEmptyExpression: JSX.JSXEmptyExpression + 'JSXEmptyExpression:exit': JSX.JSXEmptyExpression + JSXExpressionContainer: JSX.JSXExpressionContainer + 'JSXExpressionContainer:exit': JSX.JSXExpressionContainer + JSXFragment: JSX.JSXFragment + 'JSXFragment:exit': JSX.JSXFragment + JSXIdentifier: JSX.JSXIdentifier + 'JSXIdentifier:exit': JSX.JSXIdentifier + JSXOpeningElement: JSX.JSXOpeningElement + 'JSXOpeningElement:exit': JSX.JSXOpeningElement + JSXOpeningFragment: JSX.JSXOpeningFragment + 'JSXOpeningFragment:exit': JSX.JSXOpeningFragment + JSXSpreadAttribute: JSX.JSXSpreadAttribute + 'JSXSpreadAttribute:exit': JSX.JSXSpreadAttribute + JSXSpreadChild: JSX.JSXSpreadChild + 'JSXSpreadChild:exit': JSX.JSXSpreadChild + JSXMemberExpression: JSX.JSXMemberExpression + 'JSXMemberExpression:exit': JSX.JSXMemberExpression + JSXText: JSX.JSXText + 'JSXText:exit': JSX.JSXText +} & ESNodeListenerMap +export type ESNodeListenerMap = { + Identifier: ES.Identifier + 'Identifier:exit': ES.Identifier + Literal: ES.Literal + 'Literal:exit': ES.Literal + Program: ES.Program + 'Program:exit': ES.Program + SwitchCase: ES.SwitchCase + 'SwitchCase:exit': ES.SwitchCase + CatchClause: ES.CatchClause + 'CatchClause:exit': ES.CatchClause + VariableDeclarator: ES.VariableDeclarator + 'VariableDeclarator:exit': ES.VariableDeclarator + ':statement': ES.Statement + ':statement:exit': ES.Statement + ExpressionStatement: ES.ExpressionStatement + 'ExpressionStatement:exit': ES.ExpressionStatement + BlockStatement: ES.BlockStatement + 'BlockStatement:exit': ES.BlockStatement + EmptyStatement: ES.EmptyStatement + 'EmptyStatement:exit': ES.EmptyStatement + DebuggerStatement: ES.DebuggerStatement + 'DebuggerStatement:exit': ES.DebuggerStatement + WithStatement: ES.WithStatement + 'WithStatement:exit': ES.WithStatement + ReturnStatement: ES.ReturnStatement + 'ReturnStatement:exit': ES.ReturnStatement + LabeledStatement: ES.LabeledStatement + 'LabeledStatement:exit': ES.LabeledStatement + BreakStatement: ES.BreakStatement + 'BreakStatement:exit': ES.BreakStatement + ContinueStatement: ES.ContinueStatement + 'ContinueStatement:exit': ES.ContinueStatement + IfStatement: ES.IfStatement + 'IfStatement:exit': ES.IfStatement + SwitchStatement: ES.SwitchStatement + 'SwitchStatement:exit': ES.SwitchStatement + ThrowStatement: ES.ThrowStatement + 'ThrowStatement:exit': ES.ThrowStatement + TryStatement: ES.TryStatement + 'TryStatement:exit': ES.TryStatement + WhileStatement: ES.WhileStatement + 'WhileStatement:exit': ES.WhileStatement + DoWhileStatement: ES.DoWhileStatement + 'DoWhileStatement:exit': ES.DoWhileStatement + ForStatement: ES.ForStatement + 'ForStatement:exit': ES.ForStatement + ForInStatement: ES.ForInStatement + 'ForInStatement:exit': ES.ForInStatement + ForOfStatement: ES.ForOfStatement + 'ForOfStatement:exit': ES.ForOfStatement + ':declaration': ES.Declaration + ':declaration:exit': ES.Declaration + FunctionDeclaration: ES.FunctionDeclaration + 'FunctionDeclaration:exit': ES.FunctionDeclaration + VariableDeclaration: ES.VariableDeclaration + 'VariableDeclaration:exit': ES.VariableDeclaration + ClassDeclaration: ES.ClassDeclaration + 'ClassDeclaration:exit': ES.ClassDeclaration + ':expression': ES.Expression + ':expression:exit': ES.Expression + ThisExpression: ES.ThisExpression + 'ThisExpression:exit': ES.ThisExpression + ArrayExpression: ES.ArrayExpression + 'ArrayExpression:exit': ES.ArrayExpression + ObjectExpression: ES.ObjectExpression + 'ObjectExpression:exit': ES.ObjectExpression + ':function': + | ES.FunctionExpression + | ES.ArrowFunctionExpression + | ES.FunctionDeclaration + ':function:exit': + | ES.FunctionExpression + | ES.ArrowFunctionExpression + | ES.FunctionDeclaration + FunctionExpression: ES.FunctionExpression + 'FunctionExpression:exit': ES.FunctionExpression + ArrowFunctionExpression: ES.ArrowFunctionExpression + 'ArrowFunctionExpression:exit': ES.ArrowFunctionExpression + YieldExpression: ES.YieldExpression + 'YieldExpression:exit': ES.YieldExpression + UnaryExpression: ES.UnaryExpression + 'UnaryExpression:exit': ES.UnaryExpression + UpdateExpression: ES.UpdateExpression + 'UpdateExpression:exit': ES.UpdateExpression + BinaryExpression: ES.BinaryExpression + 'BinaryExpression:exit': ES.BinaryExpression + AssignmentExpression: ES.AssignmentExpression + 'AssignmentExpression:exit': ES.AssignmentExpression + LogicalExpression: ES.LogicalExpression + 'LogicalExpression:exit': ES.LogicalExpression + MemberExpression: ES.MemberExpression + 'MemberExpression:exit': ES.MemberExpression + ConditionalExpression: ES.ConditionalExpression + 'ConditionalExpression:exit': ES.ConditionalExpression + CallExpression: ES.CallExpression + 'CallExpression:exit': ES.CallExpression + NewExpression: ES.NewExpression + 'NewExpression:exit': ES.NewExpression + SequenceExpression: ES.SequenceExpression + 'SequenceExpression:exit': ES.SequenceExpression + TemplateLiteral: ES.TemplateLiteral + 'TemplateLiteral:exit': ES.TemplateLiteral + TaggedTemplateExpression: ES.TaggedTemplateExpression + 'TaggedTemplateExpression:exit': ES.TaggedTemplateExpression + ClassExpression: ES.ClassExpression + 'ClassExpression:exit': ES.ClassExpression + MetaProperty: ES.MetaProperty + 'MetaProperty:exit': ES.MetaProperty + AwaitExpression: ES.AwaitExpression + 'AwaitExpression:exit': ES.AwaitExpression + Property: ES.Property | ES.AssignmentProperty + 'Property:exit': ES.Property | ES.AssignmentProperty + 'ObjectExpression>Property': ES.Property + 'ObjectExpression>Property:exit': ES.Property + 'ObjectExpression > Property': ES.Property + 'ObjectExpression > Property:exit': ES.Property + 'ObjectPattern>Property': ES.AssignmentProperty + 'ObjectPattern>Property:exit': ES.AssignmentProperty + 'ObjectPattern > Property': ES.AssignmentProperty + 'ObjectPattern > Property:exit': ES.AssignmentProperty + Super: ES.Super + 'Super:exit': ES.Super + TemplateElement: ES.TemplateElement + 'TemplateElement:exit': ES.TemplateElement + SpreadElement: ES.SpreadElement + 'SpreadElement:exit': ES.SpreadElement + ':pattern': ES.Pattern + ':pattern:exit': ES.Pattern + ObjectPattern: ES.ObjectPattern + 'ObjectPattern:exit': ES.ObjectPattern + ArrayPattern: ES.ArrayPattern + 'ArrayPattern:exit': ES.ArrayPattern + RestElement: ES.RestElement + 'RestElement:exit': ES.RestElement + AssignmentPattern: ES.AssignmentPattern + 'AssignmentPattern:exit': ES.AssignmentPattern + ClassBody: ES.ClassBody + 'ClassBody:exit': ES.ClassBody + MethodDefinition: ES.MethodDefinition + 'MethodDefinition:exit': ES.MethodDefinition + ImportDeclaration: ES.ImportDeclaration + 'ImportDeclaration:exit': ES.ImportDeclaration + ExportNamedDeclaration: ES.ExportNamedDeclaration + 'ExportNamedDeclaration:exit': ES.ExportNamedDeclaration + ExportDefaultDeclaration: ES.ExportDefaultDeclaration + 'ExportDefaultDeclaration:exit': ES.ExportDefaultDeclaration + ExportAllDeclaration: ES.ExportAllDeclaration + 'ExportAllDeclaration:exit': ES.ExportAllDeclaration + ImportSpecifier: ES.ImportSpecifier + 'ImportSpecifier:exit': ES.ImportSpecifier + ImportDefaultSpecifier: ES.ImportDefaultSpecifier + 'ImportDefaultSpecifier:exit': ES.ImportDefaultSpecifier + ImportNamespaceSpecifier: ES.ImportNamespaceSpecifier + 'ImportNamespaceSpecifier:exit': ES.ImportNamespaceSpecifier + ExportSpecifier: ES.ExportSpecifier + 'ExportSpecifier:exit': ES.ExportSpecifier + ImportExpression: ES.ImportExpression + 'ImportExpression:exit': ES.ImportExpression + ChainExpression: ES.ChainExpression + 'ChainExpression:exit': ES.ChainExpression + + TSAsExpression: TS.TSAsExpression + 'TSAsExpression:exit': TS.TSAsExpression +} diff --git a/src/eslint-plugins/types/ast/es-ast.ts b/src/eslint-plugins/types/ast/es-ast.ts new file mode 100644 index 00000000..9af1e7d0 --- /dev/null +++ b/src/eslint-plugins/types/ast/es-ast.ts @@ -0,0 +1,543 @@ +/** + * @see https://github.com/estree/estree + */ +import { BaseNode, HasParentNode } from '../node' +import { Token } from '../node' +import { ParseError } from '../errors' +import * as V from './v-ast' +import * as TS from './ts-ast' +import * as JSX from './jsx-ast' + +export type ESNode = + | Identifier + | Literal + | Program + | SwitchCase + | CatchClause + | VariableDeclarator + | Statement + | Expression + | Property + | AssignmentProperty + | Super + | TemplateElement + | SpreadElement + | Pattern + | ClassBody + | MethodDefinition + | ModuleDeclaration + | ModuleSpecifier + +export interface Program extends BaseNode { + type: 'Program' + sourceType: 'script' | 'module' + body: (Statement | ModuleDeclaration)[] + templateBody?: V.VRootElement + tokens: Token[] + comments: Token[] + errors: ParseError[] + parent: null +} +export type Statement = + | ExpressionStatement + | BlockStatement + | EmptyStatement + | DebuggerStatement + | WithStatement + | ReturnStatement + | LabeledStatement + | BreakStatement + | ContinueStatement + | IfStatement + | SwitchStatement + | ThrowStatement + | TryStatement + | WhileStatement + | DoWhileStatement + | ForStatement + | ForInStatement + | ForOfStatement + | Declaration +export interface EmptyStatement extends HasParentNode { + type: 'EmptyStatement' +} +export interface BlockStatement extends HasParentNode { + type: 'BlockStatement' + body: Statement[] +} +export interface ExpressionStatement extends HasParentNode { + type: 'ExpressionStatement' + expression: Expression +} +export interface IfStatement extends HasParentNode { + type: 'IfStatement' + test: Expression + consequent: Statement + alternate: Statement | null +} +export interface SwitchStatement extends HasParentNode { + type: 'SwitchStatement' + discriminant: Expression + cases: SwitchCase[] +} +export interface SwitchCase extends HasParentNode { + type: 'SwitchCase' + test: Expression | null + consequent: Statement[] +} +export interface WhileStatement extends HasParentNode { + type: 'WhileStatement' + test: Expression + body: Statement +} +export interface DoWhileStatement extends HasParentNode { + type: 'DoWhileStatement' + body: Statement + test: Expression +} +export interface ForStatement extends HasParentNode { + type: 'ForStatement' + init: VariableDeclaration | Expression | null + test: Expression | null + update: Expression | null + body: Statement +} +export interface ForInStatement extends HasParentNode { + type: 'ForInStatement' + left: VariableDeclaration | Pattern + right: Expression + body: Statement +} +export interface ForOfStatement extends HasParentNode { + type: 'ForOfStatement' + left: VariableDeclaration | Pattern + right: Expression + body: Statement + await: boolean +} +export interface LabeledStatement extends HasParentNode { + type: 'LabeledStatement' + label: Identifier + body: Statement +} +export interface BreakStatement extends HasParentNode { + type: 'BreakStatement' + label: Identifier | null +} +export interface ContinueStatement extends HasParentNode { + type: 'ContinueStatement' + label: Identifier | null +} +export interface ReturnStatement extends HasParentNode { + type: 'ReturnStatement' + argument: Expression | null +} +export interface ThrowStatement extends HasParentNode { + type: 'ThrowStatement' + argument: Expression +} +export interface TryStatement extends HasParentNode { + type: 'TryStatement' + block: BlockStatement + handler: CatchClause | null + finalizer: BlockStatement | null +} +export interface CatchClause extends HasParentNode { + type: 'CatchClause' + param: Pattern | null + body: BlockStatement +} +export interface WithStatement extends HasParentNode { + type: 'WithStatement' + object: Expression + body: Statement +} +export interface DebuggerStatement extends HasParentNode { + type: 'DebuggerStatement' +} +export type Declaration = + | FunctionDeclaration + | VariableDeclaration + | ClassDeclaration +export interface FunctionDeclaration extends HasParentNode { + type: 'FunctionDeclaration' + async: boolean + generator: boolean + id: Identifier | null + params: _FunctionParameter[] + body: BlockStatement +} +export interface VariableDeclaration extends HasParentNode { + type: 'VariableDeclaration' + kind: 'var' | 'let' | 'const' + declarations: VariableDeclarator[] +} +export interface VariableDeclarator extends HasParentNode { + type: 'VariableDeclarator' + id: Pattern + init: Expression | null +} +export interface ClassDeclaration extends HasParentNode { + type: 'ClassDeclaration' + id: Identifier | null + superClass: Expression | null + body: ClassBody +} +export interface ClassBody extends HasParentNode { + type: 'ClassBody' + body: MethodDefinition[] +} +export interface MethodDefinition extends HasParentNode { + type: 'MethodDefinition' + kind: 'constructor' | 'method' | 'get' | 'set' + computed: boolean + static: boolean + key: Expression + value: FunctionExpression +} +export type ModuleDeclaration = + | ImportDeclaration + | ExportNamedDeclaration + | ExportDefaultDeclaration + | ExportAllDeclaration +export type ModuleSpecifier = + | ImportSpecifier + | ImportDefaultSpecifier + | ImportNamespaceSpecifier + | ExportSpecifier +export interface ImportDeclaration extends HasParentNode { + type: 'ImportDeclaration' + specifiers: ( + | ImportSpecifier + | ImportDefaultSpecifier + | ImportNamespaceSpecifier + )[] + source: Literal +} +export interface ImportSpecifier extends HasParentNode { + type: 'ImportSpecifier' + imported: Identifier + local: Identifier +} +export interface ImportDefaultSpecifier extends HasParentNode { + type: 'ImportDefaultSpecifier' + local: Identifier +} +export interface ImportNamespaceSpecifier extends HasParentNode { + type: 'ImportNamespaceSpecifier' + local: Identifier +} +export interface ExportNamedDeclaration extends HasParentNode { + type: 'ExportNamedDeclaration' + declaration?: Declaration | null + specifiers: ExportSpecifier[] + source?: Literal | null +} +export interface ExportSpecifier extends HasParentNode { + type: 'ExportSpecifier' + exported: Identifier +} +export interface ExportDefaultDeclaration extends HasParentNode { + type: 'ExportDefaultDeclaration' + declaration: Declaration | Expression +} +export interface ExportAllDeclaration extends HasParentNode { + type: 'ExportAllDeclaration' + source: Literal + exported: Identifier | null +} +export interface ImportExpression extends HasParentNode { + type: 'ImportExpression' + source: Expression +} +export type Expression = + | ThisExpression + | ArrayExpression + | ObjectExpression + | FunctionExpression + | ArrowFunctionExpression + | YieldExpression + | Literal + | UnaryExpression + | UpdateExpression + | BinaryExpression + | AssignmentExpression + | LogicalExpression + | MemberExpression + | ConditionalExpression + | CallExpression + | NewExpression + | SequenceExpression + | TemplateLiteral + | TaggedTemplateExpression + | ClassExpression + | MetaProperty + | Identifier + | AwaitExpression + | ImportExpression + | ChainExpression + | JSX.JSXElement + | JSX.JSXFragment + | TS.TSAsExpression + +export interface Identifier extends HasParentNode { + type: 'Identifier' + name: string +} +export interface Literal extends HasParentNode { + type: 'Literal' + value: string | boolean | null | number | RegExp | BigInt + regex?: { + pattern: string + flags: string + } + bigint?: string +} +export interface ThisExpression extends HasParentNode { + type: 'ThisExpression' +} +export interface ArrayExpression extends HasParentNode { + type: 'ArrayExpression' + elements: (Expression | SpreadElement | null)[] +} +export interface ObjectExpression extends HasParentNode { + type: 'ObjectExpression' + properties: (Property | SpreadElement)[] +} +export interface Property extends HasParentNode { + type: 'Property' + kind: 'init' | 'get' | 'set' + method: boolean + shorthand: boolean + computed: boolean + key: Expression + value: Expression + parent: ObjectExpression +} +export interface FunctionExpression extends HasParentNode { + type: 'FunctionExpression' + async: boolean + generator: boolean + id: Identifier | null + params: _FunctionParameter[] + body: BlockStatement +} + +interface ArrowFunctionExpressionHasBlock extends HasParentNode { + type: 'ArrowFunctionExpression' + async: boolean + generator: boolean + id: Identifier | null + params: _FunctionParameter[] + body: BlockStatement + expression: false +} + +interface ArrowFunctionExpressionNoBlock extends HasParentNode { + type: 'ArrowFunctionExpression' + async: boolean + generator: boolean + id: Identifier | null + params: _FunctionParameter[] + body: Expression + expression: true +} + +export type ArrowFunctionExpression = + | ArrowFunctionExpressionNoBlock + | ArrowFunctionExpressionHasBlock + +export interface SequenceExpression extends HasParentNode { + type: 'SequenceExpression' + expressions: Expression[] +} +export type UnaryOperator = '-' | '+' | '!' | '~' | 'typeof' | 'void' | 'delete' +export interface UnaryExpression extends HasParentNode { + type: 'UnaryExpression' + operator: UnaryOperator + prefix: boolean + argument: Expression +} +export type BinaryOperator = + | '==' + | '!=' + | '===' + | '!==' + | '<' + | '<=' + | '>' + | '>=' + | '<<' + | '>>' + | '>>>' + | '+' + | '-' + | '*' + | '/' + | '%' + | '|' + | '^' + | '&' + | 'in' + | 'instanceof' + | '**' +export interface BinaryExpression extends HasParentNode { + type: 'BinaryExpression' + operator: BinaryOperator + left: Expression + right: Expression +} +export type AssignmentOperator = + | '=' + | '+=' + | '-=' + | '*=' + | '/=' + | '%=' + | '<<=' + | '>>=' + | '>>>=' + | '|=' + | '^=' + | '&=' + | '**=' + | '||=' + | '&&=' + | '??=' +export interface AssignmentExpression extends HasParentNode { + type: 'AssignmentExpression' + operator: AssignmentOperator + left: Pattern + right: Expression +} +export type UpdateOperator = '++' | '--' +export interface UpdateExpression extends HasParentNode { + type: 'UpdateExpression' + operator: UpdateOperator + argument: Expression + prefix: boolean +} +export type LogicalOperator = '||' | '&&' | '??' +export interface LogicalExpression extends HasParentNode { + type: 'LogicalExpression' + operator: LogicalOperator + left: Expression + right: Expression +} +export interface ConditionalExpression extends HasParentNode { + type: 'ConditionalExpression' + test: Expression + alternate: Expression + consequent: Expression +} +export interface CallExpression extends HasParentNode { + type: 'CallExpression' + callee: Expression | Super + arguments: (Expression | SpreadElement)[] + optional: boolean +} +export interface Super extends HasParentNode { + type: 'Super' +} +export interface NewExpression extends HasParentNode { + type: 'NewExpression' + callee: Expression + arguments: (Expression | SpreadElement)[] +} +export interface MemberExpression extends HasParentNode { + type: 'MemberExpression' + computed: boolean + object: Expression | Super + property: Expression + optional: boolean +} +export interface ChainExpression extends HasParentNode { + type: 'ChainExpression' + expression: ChainElement +} +export type ChainElement = CallExpression | MemberExpression +export interface YieldExpression extends HasParentNode { + type: 'YieldExpression' + delegate: boolean + argument: Expression | null +} +export interface AwaitExpression extends HasParentNode { + type: 'AwaitExpression' + argument: Expression +} +export interface TemplateLiteral extends HasParentNode { + type: 'TemplateLiteral' + quasis: TemplateElement[] + expressions: Expression[] +} +export interface TaggedTemplateExpression extends HasParentNode { + type: 'TaggedTemplateExpression' + tag: Expression + quasi: TemplateLiteral +} +export interface TemplateElement extends HasParentNode { + type: 'TemplateElement' + tail: boolean + value: { + cooked: string + // cooked: string | null // If the template literal is tagged and the text has an invalid escape, `cooked` will be `null`, e.g., `` tag`\unicode and \u{55}` `` + raw: string + } +} +export interface ClassExpression extends HasParentNode { + type: 'ClassExpression' + id: Identifier | null + superClass: Expression | null + body: ClassBody +} +export interface MetaProperty extends HasParentNode { + type: 'MetaProperty' + meta: Identifier + property: Identifier +} +export type Pattern = + | Identifier + | ObjectPattern + | ArrayPattern + | RestElement + | AssignmentPattern + | MemberExpression +export interface ObjectPattern extends HasParentNode { + type: 'ObjectPattern' + properties: (AssignmentProperty | RestElement)[] +} +export interface AssignmentProperty extends HasParentNode { + type: 'Property' + kind: 'init' + method: false + shorthand: boolean + computed: boolean + key: Expression + value: Pattern + parent: ObjectPattern +} +export interface ArrayPattern extends HasParentNode { + type: 'ArrayPattern' + elements: Pattern[] +} +export interface RestElement extends HasParentNode { + type: 'RestElement' + argument: Pattern +} +export interface SpreadElement extends HasParentNode { + type: 'SpreadElement' + argument: Expression +} +export interface AssignmentPattern extends HasParentNode { + type: 'AssignmentPattern' + left: Pattern + right: Expression +} + +export type _FunctionParameter = + | AssignmentPattern + | RestElement + | ArrayPattern + | ObjectPattern + | Identifier +// | TSParameterProperty; diff --git a/src/eslint-plugins/types/ast/index.ts b/src/eslint-plugins/types/ast/index.ts new file mode 100644 index 00000000..5372ff3e --- /dev/null +++ b/src/eslint-plugins/types/ast/index.ts @@ -0,0 +1,5 @@ +export * from './ast' +export * from './es-ast' +export * from './v-ast' +export * from './ts-ast' +export * from './jsx-ast' diff --git a/src/eslint-plugins/types/ast/jsx-ast.ts b/src/eslint-plugins/types/ast/jsx-ast.ts new file mode 100644 index 00000000..4f1b04af --- /dev/null +++ b/src/eslint-plugins/types/ast/jsx-ast.ts @@ -0,0 +1,106 @@ +/** + * @see https://github.com/facebook/jsx/blob/master/AST.md + * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/typescript-estree/src/ts-estree/ts-estree.ts + */ +import { HasParentNode } from '../node' +import * as ES from './es-ast' + +export type JSXNode = + | JSXAttribute + | JSXClosingElement + | JSXClosingFragment + | JSXElement + | JSXEmptyExpression + | JSXExpressionContainer + | JSXFragment + | JSXIdentifier + | JSXOpeningElement + | JSXOpeningFragment + | JSXSpreadAttribute + | JSXSpreadChild + | JSXMemberExpression + | JSXText + +export type JSXChild = JSXElement | JSXExpression | JSXFragment | JSXText +export type JSXExpression = + | JSXEmptyExpression + | JSXSpreadChild + | JSXExpressionContainer +export type JSXTagNameExpression = JSXIdentifier | JSXMemberExpression + +export interface JSXAttribute extends HasParentNode { + type: 'JSXAttribute' + name: JSXIdentifier + value: ES.Literal | JSXExpression | null +} + +export interface JSXClosingElement extends HasParentNode { + type: 'JSXClosingElement' + name: JSXTagNameExpression +} + +export interface JSXClosingFragment extends HasParentNode { + type: 'JSXClosingFragment' +} + +export interface JSXElement extends HasParentNode { + type: 'JSXElement' + openingElement: JSXOpeningElement + closingElement: JSXClosingElement | null + children: JSXChild[] +} + +export interface JSXEmptyExpression extends HasParentNode { + type: 'JSXEmptyExpression' +} + +export interface JSXExpressionContainer extends HasParentNode { + type: 'JSXExpressionContainer' + expression: ES.Expression | JSXEmptyExpression +} + +export interface JSXFragment extends HasParentNode { + type: 'JSXFragment' + openingFragment: JSXOpeningFragment + closingFragment: JSXClosingFragment + children: JSXChild[] +} + +export interface JSXIdentifier extends HasParentNode { + type: 'JSXIdentifier' + name: string +} + +export interface JSXMemberExpression extends HasParentNode { + type: 'JSXMemberExpression' + object: JSXTagNameExpression + property: JSXIdentifier +} + +export interface JSXOpeningElement extends HasParentNode { + type: 'JSXOpeningElement' + // typeParameters?: TSTypeParameterInstantiation; + selfClosing: boolean + name: JSXTagNameExpression + attributes: JSXAttribute[] +} + +export interface JSXOpeningFragment extends HasParentNode { + type: 'JSXOpeningFragment' +} + +export interface JSXSpreadAttribute extends HasParentNode { + type: 'JSXSpreadAttribute' + argument: ES.Expression +} + +export interface JSXSpreadChild extends HasParentNode { + type: 'JSXSpreadChild' + expression: ES.Expression | JSXEmptyExpression +} + +export interface JSXText extends HasParentNode { + type: 'JSXText' + value: string + raw: string +} diff --git a/src/eslint-plugins/types/ast/ts-ast.ts b/src/eslint-plugins/types/ast/ts-ast.ts new file mode 100644 index 00000000..28dc29fe --- /dev/null +++ b/src/eslint-plugins/types/ast/ts-ast.ts @@ -0,0 +1,11 @@ +/** + * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/typescript-estree/src/ts-estree/ts-estree.ts + */ +import { HasParentNode } from '../node' +import * as ES from './es-ast' +export type TSNode = TSAsExpression + +export interface TSAsExpression extends HasParentNode { + type: 'TSAsExpression' + expression: ES.Expression +} diff --git a/src/eslint-plugins/types/ast/v-ast.ts b/src/eslint-plugins/types/ast/v-ast.ts new file mode 100644 index 00000000..fe0ac637 --- /dev/null +++ b/src/eslint-plugins/types/ast/v-ast.ts @@ -0,0 +1,174 @@ +/** + * @see https://github.com/vuejs/vue-eslint-parser/blob/master/docs/ast.md + */ +import { HasParentNode, BaseNode } from '../node' +import { Token, HTMLComment, HTMLBogusComment, Comment } from '../node' +import { ParseError } from '../errors' +import * as ES from './es-ast' + +export type NS = { + HTML: 'http://www.w3.org/1999/xhtml' + MathML: 'http://www.w3.org/1998/Math/MathML' + SVG: 'http://www.w3.org/2000/svg' + XLink: 'http://www.w3.org/1999/xlink' + XML: 'http://www.w3.org/XML/1998/namespace' + XMLNS: 'http://www.w3.org/2000/xmlns/' +} +export type Namespace = + | NS['HTML'] + | NS['MathML'] + | NS['SVG'] + | NS['XLink'] + | NS['XML'] + | NS['XMLNS'] +export interface VVariable { + id: ES.Identifier + kind: 'v-for' | 'scope' + references: VReference[] +} +export interface VReference { + id: ES.Identifier + mode: 'rw' | 'r' | 'w' + variable: VVariable | null +} +export interface VForExpression extends HasParentNode { + type: 'VForExpression' + parent: VExpressionContainer + left: ES.Pattern[] + right: ES.Expression +} +export interface VOnExpression extends HasParentNode { + type: 'VOnExpression' + parent: VExpressionContainer + body: ES.Statement[] +} +export interface VSlotScopeExpression extends HasParentNode { + type: 'VSlotScopeExpression' + parent: VExpressionContainer + params: ES._FunctionParameter[] +} +export interface VFilterSequenceExpression extends HasParentNode { + type: 'VFilterSequenceExpression' + parent: VExpressionContainer + expression: ES.Expression + filters: VFilter[] +} +export interface VFilter extends HasParentNode { + type: 'VFilter' + parent: VFilterSequenceExpression + callee: ES.Identifier + arguments: (ES.Expression | ES.SpreadElement)[] +} +export type VNode = + | VAttribute + | VDirective + | VDirectiveKey + | VElement + | VEndTag + | VExpressionContainer + | VIdentifier + | VLiteral + | VStartTag + | VText + | VDocumentFragment + | VForExpression + | VOnExpression + | VSlotScopeExpression + | VFilterSequenceExpression + | VFilter + +export interface VText extends HasParentNode { + type: 'VText' + parent: VDocumentFragment | VElement + value: string +} +export interface VExpressionContainer extends HasParentNode { + type: 'VExpressionContainer' + parent: VDocumentFragment | VElement | VDirective | VDirectiveKey + expression: + | ES.Expression + | VFilterSequenceExpression + | VForExpression + | VOnExpression + | VSlotScopeExpression + | null + references: VReference[] +} +export interface VIdentifier extends HasParentNode { + type: 'VIdentifier' + parent: VAttribute | VDirectiveKey + name: string + rawName: string +} +export interface VDirectiveKey extends HasParentNode { + type: 'VDirectiveKey' + parent: VDirective + name: VIdentifier + argument: VExpressionContainer | VIdentifier | null + modifiers: VIdentifier[] +} +export interface VLiteral extends HasParentNode { + type: 'VLiteral' + parent: VAttribute + value: string +} +export interface VAttribute extends HasParentNode { + type: 'VAttribute' + parent: VStartTag + directive: false + key: VIdentifier + value: VLiteral | null +} +export interface VDirective extends HasParentNode { + type: 'VAttribute' + parent: VStartTag + directive: true + key: VDirectiveKey + value: VExpressionContainer | null +} +export interface VStartTag extends HasParentNode { + type: 'VStartTag' + parent: VElement + selfClosing: boolean + attributes: (VAttribute | VDirective)[] +} +export interface VEndTag extends HasParentNode { + type: 'VEndTag' + parent: VElement +} +interface HasConcreteInfo { + tokens: Token[] + comments: (HTMLComment | HTMLBogusComment | Comment)[] + errors: ParseError[] +} +export interface VRootElement extends HasParentNode, HasConcreteInfo { + type: 'VElement' + parent: VDocumentFragment + namespace: Namespace + name: string + rawName: string + startTag: VStartTag + children: (VElement | VText | VExpressionContainer)[] + endTag: VEndTag | null + variables: VVariable[] +} + +interface VChildElement extends HasParentNode { + type: 'VElement' + parent: VRootElement | VElement + namespace: Namespace + name: string + rawName: string + startTag: VStartTag + children: (VElement | VText | VExpressionContainer)[] + endTag: VEndTag | null + variables: VVariable[] +} + +export type VElement = VChildElement | VRootElement + +export interface VDocumentFragment extends BaseNode, HasConcreteInfo { + type: 'VDocumentFragment' + parent: null + children: (VElement | VText | VExpressionContainer)[] +} diff --git a/src/eslint-plugins/types/errors.ts b/src/eslint-plugins/types/errors.ts new file mode 100644 index 00000000..213e8038 --- /dev/null +++ b/src/eslint-plugins/types/errors.ts @@ -0,0 +1,43 @@ +export interface ParseError extends SyntaxError { + code?: VParseErrorCode + index: number + lineNumber: number + column: number +} +type VParseErrorCode = + | 'abrupt-closing-of-empty-comment' + | 'absence-of-digits-in-numeric-character-reference' + | 'cdata-in-html-content' + | 'character-reference-outside-unicode-range' + | 'control-character-in-input-stream' + | 'control-character-reference' + | 'eof-before-tag-name' + | 'eof-in-cdata' + | 'eof-in-comment' + | 'eof-in-tag' + | 'incorrectly-closed-comment' + | 'incorrectly-opened-comment' + | 'invalid-first-character-of-tag-name' + | 'missing-attribute-value' + | 'missing-end-tag-name' + | 'missing-semicolon-after-character-reference' + | 'missing-whitespace-between-attributes' + | 'nested-comment' + | 'noncharacter-character-reference' + | 'noncharacter-in-input-stream' + | 'null-character-reference' + | 'surrogate-character-reference' + | 'surrogate-in-input-stream' + | 'unexpected-character-in-attribute-name' + | 'unexpected-character-in-unquoted-attribute-value' + | 'unexpected-equals-sign-before-attribute-name' + | 'unexpected-null-character' + | 'unexpected-question-mark-instead-of-tag-name' + | 'unexpected-solidus-in-tag' + | 'unknown-named-character-reference' + | 'end-tag-with-attributes' + | 'duplicate-attribute' + | 'end-tag-with-trailing-solidus' + | 'non-void-html-element-start-tag-with-trailing-solidus' + | 'x-invalid-end-tag' + | 'x-invalid-namespace' diff --git a/src/eslint-plugins/types/node/index.ts b/src/eslint-plugins/types/node/index.ts new file mode 100644 index 00000000..b51b688c --- /dev/null +++ b/src/eslint-plugins/types/node/index.ts @@ -0,0 +1,3 @@ +export * from './locations' +export * from './tokens' +export * from './node' diff --git a/src/eslint-plugins/types/node/locations.ts b/src/eslint-plugins/types/node/locations.ts new file mode 100644 index 00000000..78a4e641 --- /dev/null +++ b/src/eslint-plugins/types/node/locations.ts @@ -0,0 +1,13 @@ +export interface Position { + line: number + column: number +} +export interface SourceLocation { + start: Position + end: Position +} +export type Range = [number, number] +export interface HasLocation { + range: Range + loc: SourceLocation +} diff --git a/src/eslint-plugins/types/node/node.ts b/src/eslint-plugins/types/node/node.ts new file mode 100644 index 00000000..93dee902 --- /dev/null +++ b/src/eslint-plugins/types/node/node.ts @@ -0,0 +1,11 @@ +import { HasLocation } from './locations' +import * as VAST from '../ast' + +export interface BaseNode extends HasLocation { + type: string + parent: VAST.ASTNode | null +} + +export interface HasParentNode extends BaseNode { + parent: VAST.ASTNode +} diff --git a/src/eslint-plugins/types/node/tokens.ts b/src/eslint-plugins/types/node/tokens.ts new file mode 100644 index 00000000..fd765281 --- /dev/null +++ b/src/eslint-plugins/types/node/tokens.ts @@ -0,0 +1,17 @@ +import { HasLocation } from './locations' +export interface Token extends HasLocation { + type: string + value: string +} +export interface Comment extends Token { + type: 'Line' | 'Block' + value: string +} +export interface HTMLComment extends Token { + type: 'HTMLComment' + value: string +} +export interface HTMLBogusComment extends Token { + type: 'HTMLBogusComment' + value: string +} diff --git a/src/eslint-plugins/types/parser-services.ts b/src/eslint-plugins/types/parser-services.ts new file mode 100644 index 00000000..8f6e2a62 --- /dev/null +++ b/src/eslint-plugins/types/parser-services.ts @@ -0,0 +1,122 @@ +import * as VNODE from './node' +import * as VAST from './ast' +import * as eslint from 'eslint' + +type TemplateListenerBase = { + [T in keyof VAST.VNodeListenerMap]?: (node: VAST.VNodeListenerMap[T]) => void +} +export interface TemplateListener + extends TemplateListenerBase, + eslint.Rule.NodeListener { + [key: string]: ((node: VAST.ParamNode) => void) | undefined +} + +export interface ParserServices { + getTemplateBodyTokenStore: () => ParserServices.TokenStore + defineTemplateBodyVisitor?: ( + templateBodyVisitor: TemplateListener, + scriptVisitor?: eslint.Rule.RuleListener + ) => eslint.Rule.RuleListener + getDocumentFragment?: () => VAST.VDocumentFragment | null +} +export namespace ParserServices { + export interface TokenStore { + getTokenByRangeStart( + offset: number, + options?: { includeComments: boolean } + ): VNODE.Token | null + getFirstToken(node: VNODE.HasLocation): VNODE.Token + getFirstToken(node: VNODE.HasLocation, options: number): VNODE.Token + getFirstToken( + node: VNODE.HasLocation, + options: eslint.SourceCode.CursorWithSkipOptions + ): VNODE.Token | null + getLastToken(node: VNODE.HasLocation): VNODE.Token + getLastToken(node: VNODE.HasLocation, options: number): VNODE.Token + getLastToken( + node: VNODE.HasLocation, + options: eslint.SourceCode.CursorWithSkipOptions + ): VNODE.Token | null + getTokenBefore(node: VNODE.HasLocation): VNODE.Token + getTokenBefore(node: VNODE.HasLocation, options: number): VNODE.Token + getTokenBefore( + node: VNODE.HasLocation, + options: { includeComments: boolean } + ): VNODE.Token + getTokenBefore( + node: VNODE.HasLocation, + options: eslint.SourceCode.CursorWithSkipOptions + ): VNODE.Token | null + getTokenAfter(node: VNODE.HasLocation): VNODE.Token + getTokenAfter(node: VNODE.HasLocation, options: number): VNODE.Token + getTokenAfter( + node: VNODE.HasLocation, + options: { includeComments: boolean } + ): VNODE.Token + getTokenAfter( + node: VNODE.HasLocation, + options: eslint.SourceCode.CursorWithSkipOptions + ): VNODE.Token | null + getFirstTokenBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithSkipOptions + ): VNODE.Token | null + getLastTokenBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithSkipOptions + ): VNODE.Token | null + getTokenOrCommentBefore( + node: VNODE.HasLocation, + skip?: number + ): VNODE.Token | null + getTokenOrCommentAfter( + node: VNODE.HasLocation, + skip?: number + ): VNODE.Token | null + getFirstTokens( + node: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithCountOptions + ): VNODE.Token[] + getLastTokens( + node: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithCountOptions + ): VNODE.Token[] + getTokensBefore( + node: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithCountOptions + ): VNODE.Token[] + getTokensAfter( + node: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithCountOptions + ): VNODE.Token[] + getFirstTokensBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithCountOptions + ): VNODE.Token[] + getLastTokensBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: eslint.SourceCode.CursorWithCountOptions + ): VNODE.Token[] + getTokens( + node: VNODE.HasLocation, + beforeCount?: eslint.SourceCode.CursorWithCountOptions, + afterCount?: number + ): VNODE.Token[] + getTokensBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + padding?: eslint.SourceCode.CursorWithCountOptions + ): VNODE.Token[] + commentsExistBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation + ): boolean + getCommentsBefore(nodeOrToken: VNODE.HasLocation): VNODE.Token[] + getCommentsAfter(nodeOrToken: VNODE.HasLocation): VNODE.Token[] + getCommentsInside(node: VNODE.HasLocation): VNODE.Token[] + } +} diff --git a/src/eslint-plugins/types/utils.ts b/src/eslint-plugins/types/utils.ts new file mode 100644 index 00000000..6888e9a4 --- /dev/null +++ b/src/eslint-plugins/types/utils.ts @@ -0,0 +1,29 @@ +import * as VAST from './ast' +export type VueObjectType = 'mark' | 'export' | 'definition' | 'instance' +export type VueObjectData = { + node: ObjectExpression + type: VueObjectType + parent: VueObjectData | null + functional: boolean +} +type VueVisitorBase = { + [T in keyof NodeListenerMap]?: ( + node: NodeListenerMap[T], + obj: VueObjectData + ) => void +} +export interface VueVisitor extends VueVisitorBase { + onVueObjectEnter?(node: ObjectExpression, obj: VueObjectData): void + onVueObjectExit?(node: ObjectExpression, obj: VueObjectData): void + onSetupFunctionEnter?( + node: (FunctionExpression | ArrowFunctionExpression) & { parent: Property }, + obj: VueObjectData + ): void + onRenderFunctionEnter?( + node: (FunctionExpression | ArrowFunctionExpression) & { parent: Property }, + obj: VueObjectData + ): void + [query: string]: + | ((node: VAST.ParamNode, obj: VueObjectData) => void) + | undefined +} diff --git a/src/eslint-plugins/utils.ts b/src/eslint-plugins/utils.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/eslint-plugins/utils/casing.js b/src/eslint-plugins/utils/casing.js new file mode 100644 index 00000000..b2f2e89b --- /dev/null +++ b/src/eslint-plugins/utils/casing.js @@ -0,0 +1,203 @@ +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +/** + * Capitalize a string. + * @param {string} str + */ +function capitalize(str) { + return str.charAt(0).toUpperCase() + str.slice(1) +} +/** + * Checks whether the given string has symbols. + * @param {string} str + */ +function hasSymbols(str) { + return /[!"#%&'()*+,./:;<=>?@[\\\]^`{|}]/u.exec(str) // without " ", "$", "-" and "_" +} +/** + * Checks whether the given string has upper. + * @param {string} str + */ +function hasUpper(str) { + return /[A-Z]/u.exec(str) +} + +/** + * Convert text to kebab-case + * @param {string} str Text to be converted + * @return {string} + */ +function kebabCase(str) { + return str + .replace(/_/gu, '-') + .replace(/\B([A-Z])/gu, '-$1') + .toLowerCase() +} + +/** + * Checks whether the given string is kebab-case. + * @param {string} str + */ +function isKebabCase(str) { + if ( + hasUpper(str) || + hasSymbols(str) || + /^-/u.exec(str) || // starts with hyphen is not kebab-case + /_|--|\s/u.exec(str) + ) { + return false + } + return true +} + +/** + * Convert text to snake_case + * @param {string} str Text to be converted + * @return {string} + */ +function snakeCase(str) { + return str + .replace(/\B([A-Z])/gu, '_$1') + .replace(/-/gu, '_') + .toLowerCase() +} + +/** + * Checks whether the given string is snake_case. + * @param {string} str + */ +function isSnakeCase(str) { + if (hasUpper(str) || hasSymbols(str) || /-|__|\s/u.exec(str)) { + return false + } + return true +} + +/** + * Convert text to camelCase + * @param {string} str Text to be converted + * @return {string} Converted string + */ +function camelCase(str) { + if (isPascalCase(str)) { + return str.charAt(0).toLowerCase() + str.slice(1) + } + return str.replace(/[-_](\w)/gu, (_, c) => (c ? c.toUpperCase() : '')) +} + +/** + * Checks whether the given string is camelCase. + * @param {string} str + */ +function isCamelCase(str) { + if ( + hasSymbols(str) || + /^[A-Z]/u.exec(str) || + /-|_|\s/u.exec(str) // kebab or snake or space + ) { + return false + } + return true +} + +/** + * Convert text to PascalCase + * @param {string} str Text to be converted + * @return {string} Converted string + */ +function pascalCase(str) { + return capitalize(camelCase(str)) +} + +/** + * Checks whether the given string is PascalCase. + * @param {string} str + */ +function isPascalCase(str) { + if ( + hasSymbols(str) || + /^[a-z]/u.exec(str) || + /-|_|\s/u.exec(str) // kebab or snake or space + ) { + return false + } + return true +} + +const convertersMap = { + 'kebab-case': kebabCase, + snake_case: snakeCase, + camelCase, + PascalCase: pascalCase +} + +const checkersMap = { + 'kebab-case': isKebabCase, + snake_case: isSnakeCase, + camelCase: isCamelCase, + PascalCase: isPascalCase +} +/** + * Return case checker + * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of checker to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase} + */ +function getChecker(name) { + return checkersMap[name] || isPascalCase +} + +/** + * Return case converter + * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {kebabCase|camelCase|pascalCase|snakeCase} + */ +function getConverter(name) { + return convertersMap[name] || pascalCase +} + +module.exports = { + allowedCaseOptions: ['camelCase', 'kebab-case', 'PascalCase'], + + /** + * Return case converter + * @param {string} name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {kebabCase|camelCase|pascalCase|snakeCase} + */ + getConverter, + + /** + * Return case checker + * @param {string} name type of checker to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase} + */ + getChecker, + + /** + * Return case exact converter. + * If the converted result is not the correct case, the original value is returned. + * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') + * @return {kebabCase|camelCase|pascalCase|snakeCase} + */ + getExactConverter(name) { + const converter = getConverter(name) + const checker = getChecker(name) + return (str) => { + const result = converter(str) + return checker(result) ? result : str /* cannot convert */ + } + }, + + camelCase, + pascalCase, + kebabCase, + snakeCase, + + isCamelCase, + isPascalCase, + isKebabCase, + isSnakeCase, + + capitalize +} diff --git a/src/eslint-plugins/utils/deprecated-html-elements.json b/src/eslint-plugins/utils/deprecated-html-elements.json new file mode 100644 index 00000000..daf23f51 --- /dev/null +++ b/src/eslint-plugins/utils/deprecated-html-elements.json @@ -0,0 +1 @@ +["acronym","applet","basefont","bgsound","big","blink","center","command","content","dir","element","font","frame","frameset","image","isindex","keygen","listing","marquee","menuitem","multicol","nextid","nobr","noembed","noframes","plaintext","shadow","spacer","strike","tt","xmp"] \ No newline at end of file diff --git a/src/eslint-plugins/utils/html-comments.js b/src/eslint-plugins/utils/html-comments.js new file mode 100644 index 00000000..aced46e7 --- /dev/null +++ b/src/eslint-plugins/utils/html-comments.js @@ -0,0 +1,259 @@ +/** + * @typedef { { exceptions?: string[] } } CommentParserConfig + * @typedef { (comment: ParsedHTMLComment) => void } HTMLCommentVisitor + * @typedef { { includeDirectives?: boolean } } CommentVisitorOption + * + * @typedef { Token & { type: 'HTMLCommentOpen' } } HTMLCommentOpen + * @typedef { Token & { type: 'HTMLCommentOpenDecoration' } } HTMLCommentOpenDecoration + * @typedef { Token & { type: 'HTMLCommentValue' } } HTMLCommentValue + * @typedef { Token & { type: 'HTMLCommentClose' } } HTMLCommentClose + * @typedef { Token & { type: 'HTMLCommentCloseDecoration' } } HTMLCommentCloseDecoration + * @typedef { { open: HTMLCommentOpen, openDecoration: HTMLCommentOpenDecoration | null, value: HTMLCommentValue | null, closeDecoration: HTMLCommentCloseDecoration | null, close: HTMLCommentClose } } ParsedHTMLComment + */ +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- + +const utils = require('./') + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +const COMMENT_DIRECTIVE = /^\s*eslint-(?:en|dis)able/ +const IE_CONDITIONAL_IF = /^\[if\s+/ +const IE_CONDITIONAL_ENDIF = /\[endif\]$/ + +/** @type { 'HTMLCommentOpen' } */ +const TYPE_HTML_COMMENT_OPEN = 'HTMLCommentOpen' +/** @type { 'HTMLCommentOpenDecoration' } */ +const TYPE_HTML_COMMENT_OPEN_DECORATION = 'HTMLCommentOpenDecoration' +/** @type { 'HTMLCommentValue' } */ +const TYPE_HTML_COMMENT_VALUE = 'HTMLCommentValue' +/** @type { 'HTMLCommentClose' } */ +const TYPE_HTML_COMMENT_CLOSE = 'HTMLCommentClose' +/** @type { 'HTMLCommentCloseDecoration' } */ +const TYPE_HTML_COMMENT_CLOSE_DECORATION = 'HTMLCommentCloseDecoration' + +/** + * @param {HTMLComment} comment + * @returns {boolean} + */ +function isCommentDirective(comment) { + return COMMENT_DIRECTIVE.test(comment.value) +} + +/** + * @param {HTMLComment} comment + * @returns {boolean} + */ +function isIEConditionalComment(comment) { + return ( + IE_CONDITIONAL_IF.test(comment.value) || + IE_CONDITIONAL_ENDIF.test(comment.value) + ) +} + +/** + * Define HTML comment parser + * + * @param {SourceCode} sourceCode The source code instance. + * @param {CommentParserConfig | null} config The config. + * @returns { (node: Token) => (ParsedHTMLComment | null) } HTML comment parser. + */ +function defineParser(sourceCode, config) { + config = config || {} + + const exceptions = config.exceptions || [] + + /** + * Get a open decoration string from comment contents. + * @param {string} contents comment contents + * @returns {string} decoration string + */ + function getOpenDecoration(contents) { + let decoration = '' + for (const exception of exceptions) { + const length = exception.length + let index = 0 + while (contents.startsWith(exception, index)) { + index += length + } + const exceptionLength = index + if (decoration.length < exceptionLength) { + decoration = contents.slice(0, exceptionLength) + } + } + return decoration + } + + /** + * Get a close decoration string from comment contents. + * @param {string} contents comment contents + * @returns {string} decoration string + */ + function getCloseDecoration(contents) { + let decoration = '' + for (const exception of exceptions) { + const length = exception.length + let index = contents.length + while (contents.endsWith(exception, index)) { + index -= length + } + const exceptionLength = contents.length - index + if (decoration.length < exceptionLength) { + decoration = contents.slice(index) + } + } + return decoration + } + + /** + * Parse HTMLComment. + * @param {ASTToken} node a comment token + * @returns {HTMLComment | null} the result of HTMLComment tokens. + */ + return function parseHTMLComment(node) { + if (node.type !== 'HTMLComment') { + // Is not HTMLComment + return null + } + + const htmlCommentText = sourceCode.getText(node) + + if ( + !htmlCommentText.startsWith('<!--') || + !htmlCommentText.endsWith('-->') + ) { + // Is not normal HTML Comment + // e.g. Error Code: "abrupt-closing-of-empty-comment", "incorrectly-closed-comment" + return null + } + + let valueText = htmlCommentText.slice(4, -3) + const openDecorationText = getOpenDecoration(valueText) + valueText = valueText.slice(openDecorationText.length) + const firstCharIndex = valueText.search(/\S/) + const beforeSpace = + firstCharIndex >= 0 ? valueText.slice(0, firstCharIndex) : valueText + valueText = valueText.slice(beforeSpace.length) + + const closeDecorationText = getCloseDecoration(valueText) + if (closeDecorationText) { + valueText = valueText.slice(0, -closeDecorationText.length) + } + const lastCharIndex = valueText.search(/\S\s*$/) + const afterSpace = + lastCharIndex >= 0 ? valueText.slice(lastCharIndex + 1) : valueText + if (afterSpace) { + valueText = valueText.slice(0, -afterSpace.length) + } + + let tokenIndex = node.range[0] + /** + * @param {string} type + * @param {string} value + * @returns {any} + */ + const createToken = (type, value) => { + /** @type {Range} */ + const range = [tokenIndex, tokenIndex + value.length] + tokenIndex = range[1] + /** @type {SourceLocation} */ + let loc + return { + type, + value, + range, + get loc() { + if (loc) { + return loc + } + return (loc = { + start: sourceCode.getLocFromIndex(range[0]), + end: sourceCode.getLocFromIndex(range[1]) + }) + } + } + } + + /** @type {HTMLCommentOpen} */ + const open = createToken(TYPE_HTML_COMMENT_OPEN, '<!--') + /** @type {HTMLCommentOpenDecoration | null} */ + const openDecoration = openDecorationText + ? createToken(TYPE_HTML_COMMENT_OPEN_DECORATION, openDecorationText) + : null + tokenIndex += beforeSpace.length + /** @type {HTMLCommentValue | null} */ + const value = valueText + ? createToken(TYPE_HTML_COMMENT_VALUE, valueText) + : null + tokenIndex += afterSpace.length + /** @type {HTMLCommentCloseDecoration | null} */ + const closeDecoration = closeDecorationText + ? createToken(TYPE_HTML_COMMENT_CLOSE_DECORATION, closeDecorationText) + : null + /** @type {HTMLCommentClose} */ + const close = createToken(TYPE_HTML_COMMENT_CLOSE, '-->') + + return { + /** HTML comment open (`<!--`) */ + open, + /** decoration of the start of HTML comments. (`*****` when `<!--*****`) */ + openDecoration, + /** value of HTML comment. whitespaces and other tokens are not included. */ + value, + /** decoration of the end of HTML comments. (`*****` when `*****-->`) */ + closeDecoration, + /** HTML comment close (`-->`) */ + close + } + } +} + +/** + * Define HTML comment visitor + * + * @param {RuleContext} context The rule context. + * @param {CommentParserConfig | null} config The config. + * @param {HTMLCommentVisitor} visitHTMLComment The HTML comment visitor. + * @param {CommentVisitorOption} [visitorOption] The option for visitor. + * @returns {RuleListener} HTML comment visitor. + */ +function defineVisitor(context, config, visitHTMLComment, visitorOption) { + return { + Program(node) { + visitorOption = visitorOption || {} + if (utils.hasInvalidEOF(node)) { + return + } + if (!node.templateBody) { + return + } + const parse = defineParser(context.getSourceCode(), config) + + for (const comment of node.templateBody.comments) { + if (comment.type !== 'HTMLComment') { + continue + } + if (!visitorOption.includeDirectives && isCommentDirective(comment)) { + // ignore directives + continue + } + if (isIEConditionalComment(comment)) { + // ignore IE conditional + continue + } + + const tokens = parse(comment) + if (tokens) { + visitHTMLComment(tokens) + } + } + } + } +} + +module.exports = { + defineVisitor +} diff --git a/src/eslint-plugins/utils/html-elements.json b/src/eslint-plugins/utils/html-elements.json new file mode 100644 index 00000000..721f7876 --- /dev/null +++ b/src/eslint-plugins/utils/html-elements.json @@ -0,0 +1 @@ +["html","body","base","head","link","meta","style","title","address","article","aside","footer","header","h1","h2","h3","h4","h5","h6","hgroup","nav","section","div","dd","dl","dt","figcaption","figure","hr","img","li","main","ol","p","pre","ul","a","b","abbr","bdi","bdo","br","cite","code","data","dfn","em","i","kbd","mark","q","rp","rt","rtc","ruby","s","samp","small","span","strong","sub","sup","time","u","var","wbr","area","audio","map","track","video","embed","object","param","source","canvas","script","noscript","del","ins","caption","col","colgroup","table","thead","tbody","tfoot","td","th","tr","button","datalist","fieldset","form","input","label","legend","meter","optgroup","option","output","progress","select","textarea","details","dialog","menu","menuitem","summary","content","element","shadow","template","slot","blockquote","iframe","noframes","picture"] diff --git a/src/eslint-plugins/utils/indent-common.ts b/src/eslint-plugins/utils/indent-common.ts new file mode 100644 index 00000000..8ef9fc61 --- /dev/null +++ b/src/eslint-plugins/utils/indent-common.ts @@ -0,0 +1,2085 @@ +/** + * @author Toru Nagashima <https://github.com/mysticatea> + * See LICENSE file in root directory for full license. + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + + +const KNOWN_NODES = new Set([ + 'ArrayExpression', + 'ArrayPattern', + 'ArrowFunctionExpression', + 'AssignmentExpression', + 'AssignmentPattern', + 'AwaitExpression', + 'BinaryExpression', + 'BlockStatement', + 'BreakStatement', + 'CallExpression', + 'CatchClause', + 'ChainExpression', + 'ClassBody', + 'ClassDeclaration', + 'ClassExpression', + 'ConditionalExpression', + 'ContinueStatement', + 'DebuggerStatement', + 'DoWhileStatement', + 'EmptyStatement', + 'ExportAllDeclaration', + 'ExportDefaultDeclaration', + 'ExportNamedDeclaration', + 'ExportSpecifier', + 'ExpressionStatement', + 'ForInStatement', + 'ForOfStatement', + 'ForStatement', + 'FunctionDeclaration', + 'FunctionExpression', + 'Identifier', + 'IfStatement', + 'ImportDeclaration', + 'ImportDefaultSpecifier', + 'ImportExpression', + 'ImportNamespaceSpecifier', + 'ImportSpecifier', + 'LabeledStatement', + 'Literal', + 'LogicalExpression', + 'MemberExpression', + 'MetaProperty', + 'MethodDefinition', + 'NewExpression', + 'ObjectExpression', + 'ObjectPattern', + 'Program', + 'Property', + 'RestElement', + 'ReturnStatement', + 'SequenceExpression', + 'SpreadElement', + 'Super', + 'SwitchCase', + 'SwitchStatement', + 'TaggedTemplateExpression', + 'TemplateElement', + 'TemplateLiteral', + 'ThisExpression', + 'ThrowStatement', + 'TryStatement', + 'UnaryExpression', + 'UpdateExpression', + 'VariableDeclaration', + 'VariableDeclarator', + 'WhileStatement', + 'WithStatement', + 'YieldExpression', + 'VAttribute', + 'VDirectiveKey', + 'VDocumentFragment', + 'VElement', + 'VEndTag', + 'VExpressionContainer', + 'VFilter', + 'VFilterSequenceExpression', + 'VForExpression', + 'VIdentifier', + 'VLiteral', + 'VOnExpression', + 'VSlotScopeExpression', + 'VStartTag', + 'VText' +]) +const NON_STANDARD_KNOWN_NODES = new Set([ + 'ExperimentalRestProperty', + 'ExperimentalSpreadProperty' +]) +const LT_CHAR = /[\r\n\u2028\u2029]/ +const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g +const BLOCK_COMMENT_PREFIX = /^\s*\*/ +const ITERATION_OPTS = Object.freeze({ + includeComments: true, + filter: isNotWhitespace +}) +const PREFORMATTED_ELEMENT_NAMES = ['pre', 'textarea'] + +/** + * @typedef {object} IndentOptions + * @property { " " | "\t" } IndentOptions.indentChar + * @property {number} IndentOptions.indentSize + * @property {number} IndentOptions.baseIndent + * @property {number} IndentOptions.attribute + * @property {object} IndentOptions.closeBracket + * @property {number} IndentOptions.closeBracket.startTag + * @property {number} IndentOptions.closeBracket.endTag + * @property {number} IndentOptions.closeBracket.selfClosingTag + * @property {number} IndentOptions.switchCase + * @property {boolean} IndentOptions.alignAttributesVertically + * @property {string[]} IndentOptions.ignores + */ +/** + * @typedef {object} IndentUserOptions + * @property { " " | "\t" } [IndentUserOptions.indentChar] + * @property {number} [IndentUserOptions.indentSize] + * @property {number} [IndentUserOptions.baseIndent] + * @property {number} [IndentUserOptions.attribute] + * @property {IndentOptions['closeBracket'] | number} [IndentUserOptions.closeBracket] + * @property {number} [IndentUserOptions.switchCase] + * @property {boolean} [IndentUserOptions.alignAttributesVertically] + * @property {string[]} [IndentUserOptions.ignores] + */ +/** + * Normalize options. + * @param {number|"tab"|undefined} type The type of indentation. + * @param {IndentUserOptions} options Other options. + * @param {Partial<IndentOptions>} defaultOptions The default value of options. + * @returns {IndentOptions} Normalized options. + */ +function parseOptions(type, options, defaultOptions) { + /** @type {IndentOptions} */ + const ret = Object.assign( + { + indentChar: ' ', + indentSize: 2, + baseIndent: 0, + attribute: 1, + closeBracket: { + startTag: 0, + endTag: 0, + selfClosingTag: 0 + }, + switchCase: 0, + alignAttributesVertically: true, + ignores: [] + }, + defaultOptions + ) + + if (Number.isSafeInteger(type)) { + ret.indentSize = Number(type) + } else if (type === 'tab') { + ret.indentChar = '\t' + ret.indentSize = 1 + } + + if (Number.isSafeInteger(options.baseIndent)) { + ret.baseIndent = options.baseIndent + } + if (Number.isSafeInteger(options.attribute)) { + ret.attribute = options.attribute + } + if (Number.isSafeInteger(options.closeBracket)) { + const num = Number(options.closeBracket) + ret.closeBracket = { + startTag: num, + endTag: num, + selfClosingTag: num + } + } else if (options.closeBracket) { + ret.closeBracket = Object.assign( + { + startTag: 0, + endTag: 0, + selfClosingTag: 0 + }, + options.closeBracket + ) + } + if (Number.isSafeInteger(options.switchCase)) { + ret.switchCase = options.switchCase + } + + if (options.alignAttributesVertically != null) { + ret.alignAttributesVertically = options.alignAttributesVertically + } + if (options.ignores != null) { + ret.ignores = options.ignores + } + + return ret +} + +/** + * Check whether the given token is an arrow. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is an arrow. + */ +function isArrow(token) { + return token != null && token.type === 'Punctuator' && token.value === '=>' +} + +/** + * Check whether the given token is a left parenthesis. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a left parenthesis. + */ +function isLeftParen(token) { + return token != null && token.type === 'Punctuator' && token.value === '(' +} + +/** + * Check whether the given token is a left parenthesis. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `false` if the token is a left parenthesis. + */ +function isNotLeftParen(token) { + return token != null && (token.type !== 'Punctuator' || token.value !== '(') +} + +/** + * Check whether the given token is a right parenthesis. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a right parenthesis. + */ +function isRightParen(token) { + return token != null && token.type === 'Punctuator' && token.value === ')' +} + +/** + * Check whether the given token is a right parenthesis. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `false` if the token is a right parenthesis. + */ +function isNotRightParen(token) { + return token != null && (token.type !== 'Punctuator' || token.value !== ')') +} + +/** + * Check whether the given token is a left brace. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a left brace. + */ +function isLeftBrace(token) { + return token != null && token.type === 'Punctuator' && token.value === '{' +} + +/** + * Check whether the given token is a right brace. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a right brace. + */ +function isRightBrace(token) { + return token != null && token.type === 'Punctuator' && token.value === '}' +} + +/** + * Check whether the given token is a left bracket. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a left bracket. + */ +function isLeftBracket(token) { + return token != null && token.type === 'Punctuator' && token.value === '[' +} + +/** + * Check whether the given token is a right bracket. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a right bracket. + */ +function isRightBracket(token) { + return token != null && token.type === 'Punctuator' && token.value === ']' +} + +/** + * Check whether the given token is a semicolon. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a semicolon. + */ +function isSemicolon(token) { + return token != null && token.type === 'Punctuator' && token.value === ';' +} + +/** + * Check whether the given token is a comma. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a comma. + */ +function isComma(token) { + return token != null && token.type === 'Punctuator' && token.value === ',' +} +/** + * Check whether the given token is a wildcard. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a wildcard. + */ +function isWildcard(token) { + return token != null && token.type === 'Punctuator' && token.value === '*' +} + +/** + * Check whether the given token is a whitespace. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a whitespace. + */ +function isNotWhitespace(token) { + return token != null && token.type !== 'HTMLWhitespace' +} + +/** + * Check whether the given token is a comment. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a comment. + */ +function isComment(token) { + return ( + token != null && + (token.type === 'Block' || + token.type === 'Line' || + token.type === 'Shebang' || + (typeof token.type === + 'string' /* Although acorn supports new tokens, espree may not yet support new tokens.*/ && + token.type.endsWith('Comment'))) + ) +} + +/** + * Check whether the given token is a comment. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `false` if the token is a comment. + */ +function isNotComment(token) { + return ( + token != null && + token.type !== 'Block' && + token.type !== 'Line' && + token.type !== 'Shebang' && + !( + typeof token.type === + 'string' /* Although acorn supports new tokens, espree may not yet support new tokens.*/ && + token.type.endsWith('Comment') + ) + ) +} + +/** + * Check whether the given node is not an empty text node. + * @param {ASTNode} node The node to check. + * @returns {boolean} `false` if the token is empty text node. + */ +function isNotEmptyTextNode(node) { + return !(node.type === 'VText' && node.value.trim() === '') +} + +/** + * Check whether the given token is a pipe operator. + * @param {Token|undefined|null} token The token to check. + * @returns {boolean} `true` if the token is a pipe operator. + */ +function isPipeOperator(token) { + return token != null && token.type === 'Punctuator' && token.value === '|' +} + +/** + * Get the last element. + * @template T + * @param {T[]} xs The array to get the last element. + * @returns {T | undefined} The last element or undefined. + */ +function last(xs) { + return xs.length === 0 ? undefined : xs[xs.length - 1] +} + +/** + * Check whether the node is at the beginning of line. + * @param {ASTNode|null} node The node to check. + * @param {number} index The index of the node in the nodes. + * @param {(ASTNode|null)[]} nodes The array of nodes. + * @returns {boolean} `true` if the node is at the beginning of line. + */ +function isBeginningOfLine(node, index, nodes) { + if (node != null) { + for (let i = index - 1; i >= 0; --i) { + const prevNode = nodes[i] + if (prevNode == null) { + continue + } + + return node.loc.start.line !== prevNode.loc.end.line + } + } + return false +} + +/** + * Check whether a given token is a closing token which triggers unindent. + * @param {Token} token The token to check. + * @returns {boolean} `true` if the token is a closing token. + */ +function isClosingToken(token) { + return ( + token != null && + (token.type === 'HTMLEndTagOpen' || + token.type === 'VExpressionEnd' || + (token.type === 'Punctuator' && + (token.value === ')' || token.value === '}' || token.value === ']'))) + ) +} + +/** + * Creates AST event handlers for html-indent. + * + * @param {RuleContext} context The rule context. + * @param {ParserServices.TokenStore | SourceCode} tokenStore The token store object to get tokens. + * @param {Partial<IndentOptions>} defaultOptions The default value of options. + * @returns {NodeListener} AST event handlers. + */ +module.exports.defineVisitor = function create( + context, + tokenStore, + defaultOptions +) { + if (!context.getFilename().endsWith('.vue')) return {} + + const options = parseOptions( + context.options[0], + context.options[1] || {}, + defaultOptions + ) + const sourceCode = context.getSourceCode() + const offsets = new Map() + const ignoreTokens = new Set() + + /** + * Set offset to the given tokens. + * @param {Token|Token[]|null|(Token|null)[]} token The token to set. + * @param {number} offset The offset of the tokens. + * @param {Token} baseToken The token of the base offset. + * @returns {void} + */ + function setOffset(token, offset, baseToken) { + if (!token) { + return + } + if (Array.isArray(token)) { + for (const t of token) { + offsets.set(t, { + baseToken, + offset, + baseline: false, + expectedIndent: undefined + }) + } + } else { + offsets.set(token, { + baseToken, + offset, + baseline: false, + expectedIndent: undefined + }) + } + } + + /** + * Set baseline flag to the given token. + * @param {Token} token The token to set. + * @returns {void} + */ + function setBaseline(token) { + const offsetInfo = offsets.get(token) + if (offsetInfo != null) { + offsetInfo.baseline = true + } + } + + /** + * Sets preformatted tokens to the given element node. + * @param {VElement} node The node to set. + * @returns {void} + */ + function setPreformattedTokens(node) { + const endToken = + (node.endTag && tokenStore.getFirstToken(node.endTag)) || + tokenStore.getTokenAfter(node) + + /** @type {SourceCode.CursorWithSkipOptions} */ + const option = { + includeComments: true, + filter: (token) => + token != null && + (token.type === 'HTMLText' || + token.type === 'HTMLRCDataText' || + token.type === 'HTMLTagOpen' || + token.type === 'HTMLEndTagOpen' || + token.type === 'HTMLComment') + } + for (const token of tokenStore.getTokensBetween( + node.startTag, + endToken, + option + )) { + ignoreTokens.add(token) + } + ignoreTokens.add(endToken) + } + + /** + * Get the first and last tokens of the given node. + * If the node is parenthesized, this gets the outermost parentheses. + * @param {ASTNode} node The node to get. + * @param {number} [borderOffset] The least offset of the first token. Defailt is 0. This value is used to prevent false positive in the following case: `(a) => {}` The parentheses are enclosing the whole parameter part rather than the first parameter, but this offset parameter is needed to distinguish. + * @returns {{firstToken:Token,lastToken:Token}} The gotten tokens. + */ + function getFirstAndLastTokens(node, borderOffset = 0) { + borderOffset |= 0 + + let firstToken = tokenStore.getFirstToken(node) + let lastToken = tokenStore.getLastToken(node) + + // Get the outermost left parenthesis if it's parenthesized. + let t, u + while ( + (t = tokenStore.getTokenBefore(firstToken)) != null && + (u = tokenStore.getTokenAfter(lastToken)) != null && + isLeftParen(t) && + isRightParen(u) && + t.range[0] >= borderOffset + ) { + firstToken = t + lastToken = u + } + + return { firstToken, lastToken } + } + + /** + * Process the given node list. + * The first node is offsetted from the given left token. + * Rest nodes are adjusted to the first node. + * @param {(ASTNode|null)[]} nodeList The node to process. + * @param {ASTNode|Token|null} left The left parenthesis token. + * @param {ASTNode|Token|null} right The right parenthesis token. + * @param {number} offset The offset to set. + * @param {boolean} [alignVertically=true] The flag to align vertically. If `false`, this doesn't align vertically even if the first node is not at beginning of line. + * @returns {void} + */ + function processNodeList(nodeList, left, right, offset, alignVertically) { + let t + const leftToken = left && tokenStore.getFirstToken(left) + const rightToken = right && tokenStore.getFirstToken(right) + + if (nodeList.length >= 1) { + let baseToken = null + let lastToken = left + const alignTokensBeforeBaseToken = [] + const alignTokens = [] + + for (let i = 0; i < nodeList.length; ++i) { + const node = nodeList[i] + if (node == null) { + // Holes of an array. + continue + } + const elementTokens = getFirstAndLastTokens( + node, + lastToken != null ? lastToken.range[1] : 0 + ) + + // Collect comma/comment tokens between the last token of the previous node and the first token of this node. + if (lastToken != null) { + t = lastToken + while ( + (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null && + t.range[1] <= elementTokens.firstToken.range[0] + ) { + if (baseToken == null) { + alignTokensBeforeBaseToken.push(t) + } else { + alignTokens.push(t) + } + } + } + + if (baseToken == null) { + baseToken = elementTokens.firstToken + } else { + alignTokens.push(elementTokens.firstToken) + } + + // Save the last token to find tokens between this node and the next node. + lastToken = elementTokens.lastToken + } + + // Check trailing commas and comments. + if (rightToken != null && lastToken != null) { + t = lastToken + while ( + (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null && + t.range[1] <= rightToken.range[0] + ) { + if (baseToken == null) { + alignTokensBeforeBaseToken.push(t) + } else { + alignTokens.push(t) + } + } + } + + // Set offsets. + if (leftToken != null) { + setOffset(alignTokensBeforeBaseToken, offset, leftToken) + } + if (baseToken != null) { + // Set offset to the first token. + if (leftToken != null) { + setOffset(baseToken, offset, leftToken) + } + + // Set baseline. + if (nodeList.some(isBeginningOfLine)) { + setBaseline(baseToken) + } + + if (alignVertically === false && leftToken != null) { + // Align tokens relatively to the left token. + setOffset(alignTokens, offset, leftToken) + } else { + // Align the rest tokens to the first token. + setOffset(alignTokens, 0, baseToken) + } + } + } + + if (rightToken != null && leftToken != null) { + setOffset(rightToken, 0, leftToken) + } + } + + /** + * Process the given node as body. + * The body node maybe a block statement or an expression node. + * @param {ASTNode} node The body node to process. + * @param {Token} baseToken The base token. + * @returns {void} + */ + function processMaybeBlock(node, baseToken) { + const firstToken = getFirstAndLastTokens(node).firstToken + setOffset(firstToken, isLeftBrace(firstToken) ? 0 : 1, baseToken) + } + + /** + * Collect prefix tokens of the given property. + * The prefix includes `async`, `get`, `set`, `static`, and `*`. + * @param {Property|MethodDefinition} node The property node to collect prefix tokens. + */ + function getPrefixTokens(node) { + const prefixes = [] + + /** @type {Token|null} */ + let token = tokenStore.getFirstToken(node) + while (token != null && token.range[1] <= node.key.range[0]) { + prefixes.push(token) + token = tokenStore.getTokenAfter(token) + } + while (isLeftParen(last(prefixes)) || isLeftBracket(last(prefixes))) { + prefixes.pop() + } + + return prefixes + } + + /** + * Find the head of chaining nodes. + * @param {ASTNode} node The start node to find the head. + * @returns {Token} The head token of the chain. + */ + function getChainHeadToken(node) { + const type = node.type + while (node.parent && node.parent.type === type) { + const prevToken = tokenStore.getTokenBefore(node) + if (isLeftParen(prevToken)) { + // The chaining is broken by parentheses. + break + } + node = node.parent + } + return tokenStore.getFirstToken(node) + } + + /** + * Check whether a given token is the first token of: + * + * - ExpressionStatement + * - VExpressionContainer + * - A parameter of CallExpression/NewExpression + * - An element of ArrayExpression + * - An expression of SequenceExpression + * + * @param {Token} token The token to check. + * @param {ASTNode} belongingNode The node that the token is belonging to. + * @returns {boolean} `true` if the token is the first token of an element. + */ + function isBeginningOfElement(token, belongingNode) { + let node = belongingNode + + while (node != null && node.parent != null) { + const parent = node.parent + if ( + parent.type.endsWith('Statement') || + parent.type.endsWith('Declaration') + ) { + return parent.range[0] === token.range[0] + } + if (parent.type === 'VExpressionContainer') { + if (node.range[0] !== token.range[0]) { + return false + } + const prevToken = tokenStore.getTokenBefore(belongingNode) + if (isLeftParen(prevToken)) { + // It is not the first token because it is enclosed in parentheses. + return false + } + return true + } + if (parent.type === 'CallExpression' || parent.type === 'NewExpression') { + const openParen = /** @type {Token} */ (tokenStore.getTokenAfter( + parent.callee, + isNotRightParen + )) + return parent.arguments.some( + (param) => + getFirstAndLastTokens(param, openParen.range[1]).firstToken + .range[0] === token.range[0] + ) + } + if (parent.type === 'ArrayExpression') { + return parent.elements.some( + (element) => + element != null && + getFirstAndLastTokens(element).firstToken.range[0] === + token.range[0] + ) + } + if (parent.type === 'SequenceExpression') { + return parent.expressions.some( + (expr) => + getFirstAndLastTokens(expr).firstToken.range[0] === token.range[0] + ) + } + + node = parent + } + + return false + } + + /** + * Set the base indentation to a given top-level AST node. + * @param {ASTNode} node The node to set. + * @param {number} expectedIndent The number of expected indent. + * @returns {void} + */ + function processTopLevelNode(node, expectedIndent) { + const token = tokenStore.getFirstToken(node) + const offsetInfo = offsets.get(token) + if (offsetInfo != null) { + offsetInfo.expectedIndent = expectedIndent + } else { + offsets.set(token, { + baseToken: null, + offset: 0, + baseline: false, + expectedIndent + }) + } + } + + /** + * Ignore all tokens of the given node. + * @param {ASTNode} node The node to ignore. + * @returns {void} + */ + function ignore(node) { + for (const token of tokenStore.getTokens(node)) { + offsets.delete(token) + ignoreTokens.add(token) + } + } + + /** + * Define functions to ignore nodes into the given visitor. + * @param {NodeListener} visitor The visitor to define functions to ignore nodes. + * @returns {NodeListener} The visitor. + */ + function processIgnores(visitor) { + for (const ignorePattern of options.ignores) { + const key = `${ignorePattern}:exit` + + if (visitor.hasOwnProperty(key)) { + const handler = visitor[key] + visitor[key] = function (node, ...args) { + // @ts-expect-error + const ret = handler.call(this, node, ...args) + ignore(node) + return ret + } + } else { + visitor[key] = ignore + } + } + + return visitor + } + + /** + * Calculate correct indentation of the line of the given tokens. + * @param {Token[]} tokens Tokens which are on the same line. + * @returns { { expectedIndent: number, expectedBaseIndent: number } |null } Correct indentation. If it failed to calculate then `null`. + */ + function getExpectedIndents(tokens) { + const expectedIndents = [] + + for (let i = 0; i < tokens.length; ++i) { + const token = tokens[i] + const offsetInfo = offsets.get(token) + + if (offsetInfo != null) { + if (offsetInfo.expectedIndent != null) { + expectedIndents.push(offsetInfo.expectedIndent) + } else { + const baseOffsetInfo = offsets.get(offsetInfo.baseToken) + if ( + baseOffsetInfo != null && + baseOffsetInfo.expectedIndent != null && + (i === 0 || !baseOffsetInfo.baseline) + ) { + expectedIndents.push( + baseOffsetInfo.expectedIndent + + offsetInfo.offset * options.indentSize + ) + if (baseOffsetInfo.baseline) { + break + } + } + } + } + } + if (!expectedIndents.length) { + return null + } + + return { + expectedIndent: expectedIndents[0], + expectedBaseIndent: expectedIndents.reduce((a, b) => Math.min(a, b)) + } + } + + /** + * Get the text of the indentation part of the line which the given token is on. + * @param {Token} firstToken The first token on a line. + * @returns {string} The text of indentation part. + */ + function getIndentText(firstToken) { + const text = sourceCode.text + let i = firstToken.range[0] - 1 + + while (i >= 0 && !LT_CHAR.test(text[i])) { + i -= 1 + } + + return text.slice(i + 1, firstToken.range[0]) + } + + /** + * Define the function which fixes the problem. + * @param {Token} token The token to fix. + * @param {number} actualIndent The number of actual indentation. + * @param {number} expectedIndent The number of expected indentation. + * @returns { (fixer: RuleFixer) => Fix } The defined function. + */ + function defineFix(token, actualIndent, expectedIndent) { + if (token.type === 'Block' && token.loc.start.line !== token.loc.end.line) { + // Fix indentation in multiline block comments. + const lines = sourceCode.getText(token).match(LINES) || [] + const firstLine = lines.shift() + if (lines.every((l) => BLOCK_COMMENT_PREFIX.test(l))) { + return (fixer) => { + /** @type {Range} */ + const range = [token.range[0] - actualIndent, token.range[1]] + const indent = options.indentChar.repeat(expectedIndent) + + return fixer.replaceTextRange( + range, + `${indent}${firstLine}${lines + .map((l) => l.replace(BLOCK_COMMENT_PREFIX, `${indent} *`)) + .join('')}` + ) + } + } + } + + return (fixer) => { + /** @type {Range} */ + const range = [token.range[0] - actualIndent, token.range[0]] + const indent = options.indentChar.repeat(expectedIndent) + return fixer.replaceTextRange(range, indent) + } + } + + /** + * Validate the given token with the pre-calculated expected indentation. + * @param {Token} token The token to validate. + * @param {number} expectedIndent The expected indentation. + * @param {number[]} [optionalExpectedIndents] The optional expected indentation. + * @returns {void} + */ + function validateCore(token, expectedIndent, optionalExpectedIndents) { + const line = token.loc.start.line + const indentText = getIndentText(token) + + // If there is no line terminator after the `<script>` start tag, + // `indentText` contains non-whitespace characters. + // In that case, do nothing in order to prevent removing the `<script>` tag. + if (indentText.trim() !== '') { + return + } + + const actualIndent = token.loc.start.column + const unit = options.indentChar === '\t' ? 'tab' : 'space' + + for (let i = 0; i < indentText.length; ++i) { + if (indentText[i] !== options.indentChar) { + context.report({ + loc: { + start: { line, column: i }, + end: { line, column: i + 1 } + }, + message: + 'Expected {{expected}} character, but found {{actual}} character.', + data: { + expected: JSON.stringify(options.indentChar), + actual: JSON.stringify(indentText[i]) + }, + fix: defineFix(token, actualIndent, expectedIndent) + }) + return + } + } + + if ( + actualIndent !== expectedIndent && + (optionalExpectedIndents == null || + !optionalExpectedIndents.includes(actualIndent)) + ) { + context.report({ + loc: { + start: { line, column: 0 }, + end: { line, column: actualIndent } + }, + message: + 'Expected indentation of {{expectedIndent}} {{unit}}{{expectedIndentPlural}} but found {{actualIndent}} {{unit}}{{actualIndentPlural}}.', + data: { + expectedIndent, + actualIndent, + unit, + expectedIndentPlural: expectedIndent === 1 ? '' : 's', + actualIndentPlural: actualIndent === 1 ? '' : 's' + }, + fix: defineFix(token, actualIndent, expectedIndent) + }) + } + } + + /** + * Get the expected indent of comments. + * @param {Token} nextToken The next token of comments. + * @param {number} nextExpectedIndent The expected indent of the next token. + * @param {number} lastExpectedIndent The expected indent of the last token. + * @returns {number[]} + */ + function getCommentExpectedIndents( + nextToken, + nextExpectedIndent, + lastExpectedIndent + ) { + if (typeof lastExpectedIndent === 'number' && isClosingToken(nextToken)) { + if (nextExpectedIndent === lastExpectedIndent) { + // For solo comment. E.g., + // <div> + // <!-- comment --> + // </div> + return [nextExpectedIndent + options.indentSize, nextExpectedIndent] + } + + // For last comment. E.g., + // <div> + // <div></div> + // <!-- comment --> + // </div> + return [lastExpectedIndent, nextExpectedIndent] + } + + // Adjust to next normally. E.g., + // <div> + // <!-- comment --> + // <div></div> + // </div> + return [nextExpectedIndent] + } + + /** + * Validate indentation of the line that the given tokens are on. + * @param {Token[]} tokens The tokens on the same line to validate. + * @param {Token[]} comments The comments which are on the immediately previous lines of the tokens. + * @param {Token|null} lastToken The last validated token. Comments can adjust to the token. + * @returns {void} + */ + function validate(tokens, comments, lastToken) { + // Calculate and save expected indentation. + const firstToken = tokens[0] + const actualIndent = firstToken.loc.start.column + const expectedIndents = getExpectedIndents(tokens) + if (!expectedIndents) { + return + } + + const expectedBaseIndent = expectedIndents.expectedBaseIndent + const expectedIndent = expectedIndents.expectedIndent + + // Debug log + // console.log('line', firstToken.loc.start.line, '=', { actualIndent, expectedIndent }, 'from:') + // for (const token of tokens) { + // const offsetInfo = offsets.get(token) + // if (offsetInfo == null) { + // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is unknown.') + // } else if (offsetInfo.expectedIndent != null) { + // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is fixed at', offsetInfo.expectedIndent, '.') + // } else { + // const baseOffsetInfo = offsets.get(offsetInfo.baseToken) + // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is', offsetInfo.offset, 'offset from ', JSON.stringify(sourceCode.getText(offsetInfo.baseToken)), '( line:', offsetInfo.baseToken && offsetInfo.baseToken.loc.start.line, ', indent:', baseOffsetInfo && baseOffsetInfo.expectedIndent, ', baseline:', baseOffsetInfo && baseOffsetInfo.baseline, ')') + // } + // } + + // Save. + const baseline = new Set() + for (const token of tokens) { + const offsetInfo = offsets.get(token) + if (offsetInfo != null) { + if (offsetInfo.baseline) { + // This is a baseline token, so the expected indent is the column of this token. + if (options.indentChar === ' ') { + offsetInfo.expectedIndent = Math.max( + 0, + token.loc.start.column + expectedBaseIndent - actualIndent + ) + } else { + // In hard-tabs mode, it cannot align tokens strictly, so use one additional offset. + // But the additional offset isn't needed if it's at the beginning of the line. + offsetInfo.expectedIndent = + expectedBaseIndent + (token === tokens[0] ? 0 : 1) + } + baseline.add(token) + } else if (baseline.has(offsetInfo.baseToken)) { + // The base token is a baseline token on this line, so inherit it. + offsetInfo.expectedIndent = offsets.get( + offsetInfo.baseToken + ).expectedIndent + baseline.add(token) + } else { + // Otherwise, set the expected indent of this line. + offsetInfo.expectedIndent = expectedBaseIndent + } + } + } + + // It does not validate ignore tokens. + if (ignoreTokens.has(firstToken)) { + return + } + + // Calculate the expected indents for comments. + // It allows the same indent level with the previous line. + const lastOffsetInfo = offsets.get(lastToken) + const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo.expectedIndent + const commentOptionalExpectedIndents = getCommentExpectedIndents( + firstToken, + expectedIndent, + lastExpectedIndent + ) + + // Validate. + for (const comment of comments) { + const commentExpectedIndents = getExpectedIndents([comment]) + const commentExpectedIndent = commentExpectedIndents + ? commentExpectedIndents.expectedIndent + : commentOptionalExpectedIndents[0] + + validateCore( + comment, + commentExpectedIndent, + commentOptionalExpectedIndents + ) + } + validateCore(firstToken, expectedIndent) + } + + // ------------------------------------------------------------------------------ + // Main + // ------------------------------------------------------------------------------ + + return processIgnores({ + /** @param {VAttribute} node */ + VAttribute(node) { + const keyToken = tokenStore.getFirstToken(node) + const eqToken = tokenStore.getTokenAfter(node.key) + + if (eqToken != null && eqToken.range[1] <= node.range[1]) { + setOffset(eqToken, 1, keyToken) + + const valueToken = tokenStore.getTokenAfter(eqToken) + if (valueToken != null && valueToken.range[1] <= node.range[1]) { + setOffset(valueToken, 1, keyToken) + } + } + }, + /** @param {VElement} node */ + VElement(node) { + if (!PREFORMATTED_ELEMENT_NAMES.includes(node.name)) { + const isTopLevel = node.parent.type !== 'VElement' + const offset = isTopLevel ? options.baseIndent : 1 + processNodeList( + node.children.filter(isNotEmptyTextNode), + node.startTag, + node.endTag, + offset, + false + ) + } else { + const startTagToken = tokenStore.getFirstToken(node) + const endTagToken = node.endTag && tokenStore.getFirstToken(node.endTag) + setOffset(endTagToken, 0, startTagToken) + setPreformattedTokens(node) + } + }, + /** @param {VEndTag} node */ + VEndTag(node) { + const element = node.parent + const startTagOpenToken = tokenStore.getFirstToken(element.startTag) + const closeToken = tokenStore.getLastToken(node) + + if (closeToken.type.endsWith('TagClose')) { + setOffset(closeToken, options.closeBracket.endTag, startTagOpenToken) + } + }, + /** @param {VExpressionContainer} node */ + VExpressionContainer(node) { + if ( + node.expression != null && + node.range[0] !== node.expression.range[0] + ) { + const startQuoteToken = tokenStore.getFirstToken(node) + const endQuoteToken = tokenStore.getLastToken(node) + const childToken = tokenStore.getTokenAfter(startQuoteToken) + + setOffset(childToken, 1, startQuoteToken) + setOffset(endQuoteToken, 0, startQuoteToken) + } + }, + /** @param {VFilter} node */ + VFilter(node) { + const idToken = tokenStore.getFirstToken(node) + const lastToken = tokenStore.getLastToken(node) + if (isRightParen(lastToken)) { + const leftParenToken = tokenStore.getTokenAfter(node.callee) + setOffset(leftParenToken, 1, idToken) + processNodeList(node.arguments, leftParenToken, lastToken, 1) + } + }, + /** @param {VFilterSequenceExpression} node */ + VFilterSequenceExpression(node) { + if (node.filters.length === 0) { + return + } + + const firstToken = tokenStore.getFirstToken(node) + /** @type {(Token|null)[]} */ + const tokens = [] + + for (const filter of node.filters) { + tokens.push( + tokenStore.getTokenBefore(filter, isPipeOperator), + tokenStore.getFirstToken(filter) + ) + } + + setOffset(tokens, 1, firstToken) + }, + /** @param {VForExpression} node */ + VForExpression(node) { + const firstToken = tokenStore.getFirstToken(node) + const lastOfLeft = last(node.left) || firstToken + const inToken = /** @type {Token} */ (tokenStore.getTokenAfter( + lastOfLeft, + isNotRightParen + )) + const rightToken = tokenStore.getFirstToken(node.right) + + if (isLeftParen(firstToken)) { + const rightToken = tokenStore.getTokenAfter(lastOfLeft, isRightParen) + processNodeList(node.left, firstToken, rightToken, 1) + } + setOffset(inToken, 1, firstToken) + setOffset(rightToken, 1, inToken) + }, + /** @param {VOnExpression} node */ + VOnExpression(node) { + processNodeList(node.body, null, null, 0) + }, + /** @param {VStartTag} node */ + VStartTag(node) { + const openToken = tokenStore.getFirstToken(node) + const closeToken = tokenStore.getLastToken(node) + + processNodeList( + node.attributes, + openToken, + null, + options.attribute, + options.alignAttributesVertically + ) + if (closeToken != null && closeToken.type.endsWith('TagClose')) { + const offset = + closeToken.type !== 'HTMLSelfClosingTagClose' + ? options.closeBracket.startTag + : options.closeBracket.selfClosingTag + setOffset(closeToken, offset, openToken) + } + }, + /** @param {VText} node */ + VText(node) { + const tokens = tokenStore.getTokens(node, isNotWhitespace) + const firstTokenInfo = offsets.get(tokenStore.getFirstToken(node)) + + for (const token of tokens) { + offsets.set(token, Object.assign({}, firstTokenInfo)) + } + }, + /** @param {ArrayExpression | ArrayPattern} node */ + 'ArrayExpression, ArrayPattern'(node) { + processNodeList( + node.elements, + tokenStore.getFirstToken(node), + tokenStore.getLastToken(node), + 1 + ) + }, + /** @param {ArrowFunctionExpression} node */ + ArrowFunctionExpression(node) { + const firstToken = tokenStore.getFirstToken(node) + const secondToken = tokenStore.getTokenAfter(firstToken) + const leftToken = node.async ? secondToken : firstToken + const arrowToken = tokenStore.getTokenBefore(node.body, isArrow) + + if (node.async) { + setOffset(secondToken, 1, firstToken) + } + if (isLeftParen(leftToken)) { + const rightToken = tokenStore.getTokenAfter( + last(node.params) || leftToken, + isRightParen + ) + processNodeList(node.params, leftToken, rightToken, 1) + } + + setOffset(arrowToken, 1, firstToken) + processMaybeBlock(node.body, firstToken) + }, + /** @param {AssignmentExpression | AssignmentPattern | BinaryExpression | LogicalExpression} node */ + 'AssignmentExpression, AssignmentPattern, BinaryExpression, LogicalExpression'( + node + ) { + const leftToken = getChainHeadToken(node) + const opToken = /** @type {Token} */ (tokenStore.getTokenAfter( + node.left, + isNotRightParen + )) + const rightToken = tokenStore.getTokenAfter(opToken) + const prevToken = tokenStore.getTokenBefore(leftToken) + const shouldIndent = + prevToken == null || + prevToken.loc.end.line === leftToken.loc.start.line || + isBeginningOfElement(leftToken, node) + + setOffset([opToken, rightToken], shouldIndent ? 1 : 0, leftToken) + }, + /** @param {AwaitExpression | RestElement | SpreadElement | UnaryExpression} node */ + 'AwaitExpression, RestElement, SpreadElement, UnaryExpression'(node) { + const firstToken = tokenStore.getFirstToken(node) + const nextToken = tokenStore.getTokenAfter(firstToken) + + setOffset(nextToken, 1, firstToken) + }, + /** @param {BlockStatement | ClassBody} node */ + 'BlockStatement, ClassBody'(node) { + processNodeList( + node.body, + tokenStore.getFirstToken(node), + tokenStore.getLastToken(node), + 1 + ) + }, + /** @param {BreakStatement | ContinueStatement | ReturnStatement | ThrowStatement} node */ + 'BreakStatement, ContinueStatement, ReturnStatement, ThrowStatement'(node) { + if ( + ((node.type === 'ReturnStatement' || node.type === 'ThrowStatement') && + node.argument != null) || + ((node.type === 'BreakStatement' || + node.type === 'ContinueStatement') && + node.label != null) + ) { + const firstToken = tokenStore.getFirstToken(node) + const nextToken = tokenStore.getTokenAfter(firstToken) + + setOffset(nextToken, 1, firstToken) + } + }, + /** @param {CallExpression} node */ + CallExpression(node) { + const firstToken = tokenStore.getFirstToken(node) + const rightToken = tokenStore.getLastToken(node) + const leftToken = tokenStore.getTokenAfter(node.callee, isLeftParen) + + setOffset(leftToken, 1, firstToken) + processNodeList(node.arguments, leftToken, rightToken, 1) + }, + /** @param {ImportExpression} node */ + ImportExpression(node) { + const firstToken = tokenStore.getFirstToken(node) + const rightToken = tokenStore.getLastToken(node) + const leftToken = tokenStore.getTokenAfter(firstToken, isLeftParen) + + setOffset(leftToken, 1, firstToken) + processNodeList([node.source], leftToken, rightToken, 1) + }, + /** @param {CatchClause} node */ + CatchClause(node) { + const firstToken = tokenStore.getFirstToken(node) + const bodyToken = tokenStore.getFirstToken(node.body) + + if (node.param != null) { + const leftToken = tokenStore.getTokenAfter(firstToken) + const rightToken = tokenStore.getTokenAfter(node.param) + + setOffset(leftToken, 1, firstToken) + processNodeList([node.param], leftToken, rightToken, 1) + } + setOffset(bodyToken, 0, firstToken) + }, + /** @param {ClassDeclaration | ClassExpression} node */ + 'ClassDeclaration, ClassExpression'(node) { + const firstToken = tokenStore.getFirstToken(node) + const bodyToken = tokenStore.getFirstToken(node.body) + + if (node.id != null) { + setOffset(tokenStore.getFirstToken(node.id), 1, firstToken) + } + if (node.superClass != null) { + const extendsToken = tokenStore.getTokenAfter(node.id || firstToken) + const superClassToken = tokenStore.getTokenAfter(extendsToken) + setOffset(extendsToken, 1, firstToken) + setOffset(superClassToken, 1, extendsToken) + } + setOffset(bodyToken, 0, firstToken) + }, + /** @param {ConditionalExpression} node */ + ConditionalExpression(node) { + const prevToken = tokenStore.getTokenBefore(node) + const firstToken = tokenStore.getFirstToken(node) + const questionToken = /** @type {Token} */ (tokenStore.getTokenAfter( + node.test, + isNotRightParen + )) + const consequentToken = tokenStore.getTokenAfter(questionToken) + const colonToken = /** @type {Token} */ (tokenStore.getTokenAfter( + node.consequent, + isNotRightParen + )) + const alternateToken = tokenStore.getTokenAfter(colonToken) + const isFlat = + prevToken && + prevToken.loc.end.line !== node.loc.start.line && + node.test.loc.end.line === node.consequent.loc.start.line + + if (isFlat) { + setOffset( + [questionToken, consequentToken, colonToken, alternateToken], + 0, + firstToken + ) + } else { + setOffset([questionToken, colonToken], 1, firstToken) + setOffset([consequentToken, alternateToken], 1, questionToken) + } + }, + /** @param {DoWhileStatement} node */ + DoWhileStatement(node) { + const doToken = tokenStore.getFirstToken(node) + const whileToken = /** @type {Token} */ (tokenStore.getTokenAfter( + node.body, + isNotRightParen + )) + const leftToken = tokenStore.getTokenAfter(whileToken) + const testToken = tokenStore.getTokenAfter(leftToken) + const lastToken = tokenStore.getLastToken(node) + const rightToken = isSemicolon(lastToken) + ? tokenStore.getTokenBefore(lastToken) + : lastToken + + processMaybeBlock(node.body, doToken) + setOffset(whileToken, 0, doToken) + setOffset(leftToken, 1, whileToken) + setOffset(testToken, 1, leftToken) + setOffset(rightToken, 0, leftToken) + }, + /** @param {ExportAllDeclaration} node */ + ExportAllDeclaration(node) { + const tokens = tokenStore.getTokens(node) + const firstToken = /** @type {Token} */ (tokens.shift()) + if (isSemicolon(last(tokens))) { + tokens.pop() + } + if (!node.exported) { + setOffset(tokens, 1, firstToken) + } else { + // export * as foo from "mod" + const starToken = /** @type {Token} */ (tokens.find(isWildcard)) + const asToken = tokenStore.getTokenAfter(starToken) + const exportedToken = tokenStore.getTokenAfter(asToken) + const afterTokens = tokens.slice(tokens.indexOf(exportedToken) + 1) + + setOffset(starToken, 1, firstToken) + setOffset(asToken, 1, starToken) + setOffset(exportedToken, 1, starToken) + setOffset(afterTokens, 1, firstToken) + } + }, + /** @param {ExportDefaultDeclaration} node */ + ExportDefaultDeclaration(node) { + const exportToken = tokenStore.getFirstToken(node) + const defaultToken = tokenStore.getFirstToken(node, 1) + const declarationToken = getFirstAndLastTokens(node.declaration) + .firstToken + setOffset([defaultToken, declarationToken], 1, exportToken) + }, + /** @param {ExportNamedDeclaration} node */ + ExportNamedDeclaration(node) { + const exportToken = tokenStore.getFirstToken(node) + if (node.declaration) { + // export var foo = 1; + const declarationToken = tokenStore.getFirstToken(node, 1) + setOffset(declarationToken, 1, exportToken) + } else { + const firstSpecifier = node.specifiers[0] + if (!firstSpecifier || firstSpecifier.type === 'ExportSpecifier') { + // export {foo, bar}; or export {foo, bar} from "mod"; + const leftParenToken = tokenStore.getFirstToken(node, 1) + const rightParenToken = /** @type {Token} */ (tokenStore.getLastToken( + node, + isRightBrace + )) + setOffset(leftParenToken, 0, exportToken) + processNodeList(node.specifiers, leftParenToken, rightParenToken, 1) + + const maybeFromToken = tokenStore.getTokenAfter(rightParenToken) + if ( + maybeFromToken != null && + sourceCode.getText(maybeFromToken) === 'from' + ) { + const fromToken = maybeFromToken + const nameToken = tokenStore.getTokenAfter(fromToken) + setOffset([fromToken, nameToken], 1, exportToken) + } + } else { + // maybe babel-eslint + } + } + }, + /** @param {ExportSpecifier} node */ + ExportSpecifier(node) { + const tokens = tokenStore.getTokens(node) + const firstToken = /** @type {Token} */ (tokens.shift()) + setOffset(tokens, 1, firstToken) + }, + /** @param {ForInStatement | ForOfStatement} node */ + 'ForInStatement, ForOfStatement'(node) { + const forToken = tokenStore.getFirstToken(node) + const awaitToken = + (node.type === 'ForOfStatement' && + node.await && + tokenStore.getTokenAfter(forToken)) || + null + const leftParenToken = tokenStore.getTokenAfter(awaitToken || forToken) + const leftToken = tokenStore.getTokenAfter(leftParenToken) + const inToken = /** @type {Token} */ (tokenStore.getTokenAfter( + leftToken, + isNotRightParen + )) + const rightToken = tokenStore.getTokenAfter(inToken) + const rightParenToken = tokenStore.getTokenBefore( + node.body, + isNotLeftParen + ) + + if (awaitToken != null) { + setOffset(awaitToken, 0, forToken) + } + setOffset(leftParenToken, 1, forToken) + setOffset(leftToken, 1, leftParenToken) + setOffset(inToken, 1, leftToken) + setOffset(rightToken, 1, leftToken) + setOffset(rightParenToken, 0, leftParenToken) + processMaybeBlock(node.body, forToken) + }, + /** @param {ForStatement} node */ + ForStatement(node) { + const forToken = tokenStore.getFirstToken(node) + const leftParenToken = tokenStore.getTokenAfter(forToken) + const rightParenToken = tokenStore.getTokenBefore( + node.body, + isNotLeftParen + ) + + setOffset(leftParenToken, 1, forToken) + processNodeList( + [node.init, node.test, node.update], + leftParenToken, + rightParenToken, + 1 + ) + processMaybeBlock(node.body, forToken) + }, + /** @param {FunctionDeclaration | FunctionExpression} node */ + 'FunctionDeclaration, FunctionExpression'(node) { + const firstToken = tokenStore.getFirstToken(node) + if (isLeftParen(firstToken)) { + // Methods. + const leftToken = firstToken + const rightToken = tokenStore.getTokenAfter( + last(node.params) || leftToken, + isRightParen + ) + const bodyToken = tokenStore.getFirstToken(node.body) + + processNodeList(node.params, leftToken, rightToken, 1) + setOffset(bodyToken, 0, tokenStore.getFirstToken(node.parent)) + } else { + // Normal functions. + const functionToken = node.async + ? tokenStore.getTokenAfter(firstToken) + : firstToken + const starToken = node.generator + ? tokenStore.getTokenAfter(functionToken) + : null + const idToken = node.id && tokenStore.getFirstToken(node.id) + const leftToken = tokenStore.getTokenAfter( + idToken || starToken || functionToken + ) + const rightToken = tokenStore.getTokenAfter( + last(node.params) || leftToken, + isRightParen + ) + const bodyToken = tokenStore.getFirstToken(node.body) + + if (node.async) { + setOffset(functionToken, 0, firstToken) + } + if (node.generator) { + setOffset(starToken, 1, firstToken) + } + if (node.id != null) { + setOffset(idToken, 1, firstToken) + } + setOffset(leftToken, 1, firstToken) + processNodeList(node.params, leftToken, rightToken, 1) + setOffset(bodyToken, 0, firstToken) + } + }, + /** @param {IfStatement} node */ + IfStatement(node) { + const ifToken = tokenStore.getFirstToken(node) + const ifLeftParenToken = tokenStore.getTokenAfter(ifToken) + const ifRightParenToken = tokenStore.getTokenBefore( + node.consequent, + isRightParen + ) + + setOffset(ifLeftParenToken, 1, ifToken) + setOffset(ifRightParenToken, 0, ifLeftParenToken) + processMaybeBlock(node.consequent, ifToken) + + if (node.alternate != null) { + const elseToken = /** @type {Token} */ (tokenStore.getTokenAfter( + node.consequent, + isNotRightParen + )) + + setOffset(elseToken, 0, ifToken) + processMaybeBlock(node.alternate, elseToken) + } + }, + /** @param {ImportDeclaration} node */ + ImportDeclaration(node) { + const firstSpecifier = node.specifiers[0] + const secondSpecifier = node.specifiers[1] + const importToken = tokenStore.getFirstToken(node) + const hasSemi = tokenStore.getLastToken(node).value === ';' + /** @type {Token[]} */ + const tokens = [] // tokens to one indent + + if (!firstSpecifier) { + // There are 2 patterns: + // import "foo" + // import {} from "foo" + const secondToken = tokenStore.getFirstToken(node, 1) + if (isLeftBrace(secondToken)) { + setOffset( + [secondToken, tokenStore.getTokenAfter(secondToken)], + 0, + importToken + ) + tokens.push( + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" + ) + } else { + tokens.push(tokenStore.getLastToken(node, hasSemi ? 1 : 0)) + } + } else if (firstSpecifier.type === 'ImportDefaultSpecifier') { + if ( + secondSpecifier && + secondSpecifier.type === 'ImportNamespaceSpecifier' + ) { + // There is a pattern: + // import Foo, * as foo from "foo" + tokens.push( + tokenStore.getFirstToken(firstSpecifier), // Foo + tokenStore.getTokenAfter(firstSpecifier), // comma + tokenStore.getFirstToken(secondSpecifier), // * + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" + ) + } else { + // There are 3 patterns: + // import Foo from "foo" + // import Foo, {} from "foo" + // import Foo, {a} from "foo" + const idToken = tokenStore.getFirstToken(firstSpecifier) + const nextToken = tokenStore.getTokenAfter(firstSpecifier) + if (isComma(nextToken)) { + const leftBrace = tokenStore.getTokenAfter(nextToken) + const rightBrace = tokenStore.getLastToken(node, hasSemi ? 3 : 2) + setOffset([idToken, nextToken], 1, importToken) + setOffset(leftBrace, 0, idToken) + processNodeList(node.specifiers.slice(1), leftBrace, rightBrace, 1) + tokens.push( + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" + ) + } else { + tokens.push( + idToken, + nextToken, // from + tokenStore.getTokenAfter(nextToken) // "foo" + ) + } + } + } else if (firstSpecifier.type === 'ImportNamespaceSpecifier') { + // There is a pattern: + // import * as foo from "foo" + tokens.push( + tokenStore.getFirstToken(firstSpecifier), // * + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" + ) + } else { + // There is a pattern: + // import {a} from "foo" + const leftBrace = tokenStore.getFirstToken(node, 1) + const rightBrace = tokenStore.getLastToken(node, hasSemi ? 3 : 2) + setOffset(leftBrace, 0, importToken) + processNodeList(node.specifiers, leftBrace, rightBrace, 1) + tokens.push( + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" + ) + } + + setOffset(tokens, 1, importToken) + }, + /** @param {ImportSpecifier} node */ + ImportSpecifier(node) { + if (node.local.range[0] !== node.imported.range[0]) { + const tokens = tokenStore.getTokens(node) + const firstToken = /** @type {Token} */ (tokens.shift()) + setOffset(tokens, 1, firstToken) + } + }, + /** @param {ImportNamespaceSpecifier} node */ + ImportNamespaceSpecifier(node) { + const tokens = tokenStore.getTokens(node) + const firstToken = /** @type {Token} */ (tokens.shift()) + setOffset(tokens, 1, firstToken) + }, + /** @param {LabeledStatement} node */ + LabeledStatement(node) { + const labelToken = tokenStore.getFirstToken(node) + const colonToken = tokenStore.getTokenAfter(labelToken) + const bodyToken = tokenStore.getTokenAfter(colonToken) + + setOffset([colonToken, bodyToken], 1, labelToken) + }, + /** @param {MemberExpression | MetaProperty} node */ + 'MemberExpression, MetaProperty'(node) { + const objectToken = tokenStore.getFirstToken(node) + if (node.type === 'MemberExpression' && node.computed) { + const leftBracketToken = /** @type {Token} */ (tokenStore.getTokenBefore( + node.property, + isLeftBracket + )) + const propertyToken = tokenStore.getTokenAfter(leftBracketToken) + const rightBracketToken = tokenStore.getTokenAfter( + node.property, + isRightBracket + ) + + setOffset(leftBracketToken, 1, objectToken) + setOffset(propertyToken, 1, leftBracketToken) + setOffset(rightBracketToken, 0, leftBracketToken) + } else { + const dotToken = tokenStore.getTokenBefore(node.property) + const propertyToken = tokenStore.getTokenAfter(dotToken) + + setOffset([dotToken, propertyToken], 1, objectToken) + } + }, + /** @param {MethodDefinition | Property} node */ + 'MethodDefinition, Property'(node) { + const isMethod = node.type === 'MethodDefinition' || node.method === true + const prefixTokens = getPrefixTokens(node) + const hasPrefix = prefixTokens.length >= 1 + + for (let i = 1; i < prefixTokens.length; ++i) { + setOffset(prefixTokens[i], 0, prefixTokens[i - 1]) + } + + /** @type {Token} */ + let lastKeyToken + if (node.computed) { + const keyLeftToken = /** @type {Token} */ (tokenStore.getFirstToken( + node, + isLeftBracket + )) + const keyToken = tokenStore.getTokenAfter(keyLeftToken) + const keyRightToken = (lastKeyToken = /** @type {Token} */ (tokenStore.getTokenAfter( + node.key, + isRightBracket + ))) + + if (hasPrefix) { + setOffset(keyLeftToken, 0, /** @type {Token} */ (last(prefixTokens))) + } + setOffset(keyToken, 1, keyLeftToken) + setOffset(keyRightToken, 0, keyLeftToken) + } else { + const idToken = (lastKeyToken = tokenStore.getFirstToken(node.key)) + + if (hasPrefix) { + setOffset(idToken, 0, /** @type {Token} */ (last(prefixTokens))) + } + } + + if (isMethod) { + const leftParenToken = tokenStore.getTokenAfter(lastKeyToken) + + setOffset(leftParenToken, 1, lastKeyToken) + } else if (node.type === 'Property' && !node.shorthand) { + const colonToken = tokenStore.getTokenAfter(lastKeyToken) + const valueToken = tokenStore.getTokenAfter(colonToken) + + setOffset([colonToken, valueToken], 1, lastKeyToken) + } + }, + /** @param {NewExpression} node */ + NewExpression(node) { + const newToken = tokenStore.getFirstToken(node) + const calleeToken = tokenStore.getTokenAfter(newToken) + const rightToken = tokenStore.getLastToken(node) + const leftToken = isRightParen(rightToken) + ? tokenStore.getFirstTokenBetween(node.callee, rightToken, isLeftParen) + : null + + setOffset(calleeToken, 1, newToken) + if (leftToken != null) { + setOffset(leftToken, 1, calleeToken) + processNodeList(node.arguments, leftToken, rightToken, 1) + } + }, + /** @param {ObjectExpression | ObjectPattern} node */ + 'ObjectExpression, ObjectPattern'(node) { + processNodeList( + node.properties, + tokenStore.getFirstToken(node), + tokenStore.getLastToken(node), + 1 + ) + }, + /** @param {SequenceExpression} node */ + SequenceExpression(node) { + processNodeList(node.expressions, null, null, 0) + }, + /** @param {SwitchCase} node */ + SwitchCase(node) { + const caseToken = tokenStore.getFirstToken(node) + + if (node.test != null) { + const testToken = tokenStore.getTokenAfter(caseToken) + const colonToken = tokenStore.getTokenAfter(node.test, isNotRightParen) + + setOffset([testToken, colonToken], 1, caseToken) + } else { + const colonToken = tokenStore.getTokenAfter(caseToken) + + setOffset(colonToken, 1, caseToken) + } + + if ( + node.consequent.length === 1 && + node.consequent[0].type === 'BlockStatement' + ) { + setOffset(tokenStore.getFirstToken(node.consequent[0]), 0, caseToken) + } else if (node.consequent.length >= 1) { + setOffset(tokenStore.getFirstToken(node.consequent[0]), 1, caseToken) + processNodeList(node.consequent, null, null, 0) + } + }, + /** @param {SwitchStatement} node */ + SwitchStatement(node) { + const switchToken = tokenStore.getFirstToken(node) + const leftParenToken = tokenStore.getTokenAfter(switchToken) + const discriminantToken = tokenStore.getTokenAfter(leftParenToken) + const leftBraceToken = /** @type {Token} */ (tokenStore.getTokenAfter( + node.discriminant, + isLeftBrace + )) + const rightParenToken = tokenStore.getTokenBefore(leftBraceToken) + const rightBraceToken = tokenStore.getLastToken(node) + + setOffset(leftParenToken, 1, switchToken) + setOffset(discriminantToken, 1, leftParenToken) + setOffset(rightParenToken, 0, leftParenToken) + setOffset(leftBraceToken, 0, switchToken) + processNodeList( + node.cases, + leftBraceToken, + rightBraceToken, + options.switchCase + ) + }, + /** @param {TaggedTemplateExpression} node */ + TaggedTemplateExpression(node) { + const tagTokens = getFirstAndLastTokens(node.tag, node.range[0]) + const quasiToken = tokenStore.getTokenAfter(tagTokens.lastToken) + + setOffset(quasiToken, 1, tagTokens.firstToken) + }, + /** @param {TemplateLiteral} node */ + TemplateLiteral(node) { + const firstToken = tokenStore.getFirstToken(node) + const quasiTokens = node.quasis + .slice(1) + .map((n) => tokenStore.getFirstToken(n)) + const expressionToken = node.quasis + .slice(0, -1) + .map((n) => tokenStore.getTokenAfter(n)) + + setOffset(quasiTokens, 0, firstToken) + setOffset(expressionToken, 1, firstToken) + }, + /** @param {TryStatement} node */ + TryStatement(node) { + const tryToken = tokenStore.getFirstToken(node) + const tryBlockToken = tokenStore.getFirstToken(node.block) + + setOffset(tryBlockToken, 0, tryToken) + + if (node.handler != null) { + const catchToken = tokenStore.getFirstToken(node.handler) + + setOffset(catchToken, 0, tryToken) + } + + if (node.finalizer != null) { + const finallyToken = tokenStore.getTokenBefore(node.finalizer) + const finallyBlockToken = tokenStore.getFirstToken(node.finalizer) + + setOffset([finallyToken, finallyBlockToken], 0, tryToken) + } + }, + /** @param {UpdateExpression} node */ + UpdateExpression(node) { + const firstToken = tokenStore.getFirstToken(node) + const nextToken = tokenStore.getTokenAfter(firstToken) + + setOffset(nextToken, 1, firstToken) + }, + /** @param {VariableDeclaration} node */ + VariableDeclaration(node) { + processNodeList( + node.declarations, + tokenStore.getFirstToken(node), + null, + 1 + ) + }, + /** @param {VariableDeclarator} node */ + VariableDeclarator(node) { + if (node.init != null) { + const idToken = tokenStore.getFirstToken(node) + const eqToken = tokenStore.getTokenAfter(node.id) + const initToken = tokenStore.getTokenAfter(eqToken) + + setOffset([eqToken, initToken], 1, idToken) + } + }, + /** @param {WhileStatement | WithStatement} node */ + 'WhileStatement, WithStatement'(node) { + const firstToken = tokenStore.getFirstToken(node) + const leftParenToken = tokenStore.getTokenAfter(firstToken) + const rightParenToken = tokenStore.getTokenBefore(node.body, isRightParen) + + setOffset(leftParenToken, 1, firstToken) + setOffset(rightParenToken, 0, leftParenToken) + processMaybeBlock(node.body, firstToken) + }, + /** @param {YieldExpression} node */ + YieldExpression(node) { + if (node.argument != null) { + const yieldToken = tokenStore.getFirstToken(node) + + setOffset(tokenStore.getTokenAfter(yieldToken), 1, yieldToken) + if (node.delegate) { + setOffset(tokenStore.getTokenAfter(yieldToken, 1), 1, yieldToken) + } + } + }, + /** @param {Statement} node */ + // Process semicolons. + ':statement'(node) { + const firstToken = tokenStore.getFirstToken(node) + const lastToken = tokenStore.getLastToken(node) + if (isSemicolon(lastToken) && firstToken !== lastToken) { + setOffset(lastToken, 0, firstToken) + } + + // Set to the semicolon of the previous token for semicolon-free style. + // E.g., + // foo + // ;[1,2,3].forEach(f) + const info = offsets.get(firstToken) + const prevToken = tokenStore.getTokenBefore(firstToken) + if ( + info != null && + isSemicolon(prevToken) && + prevToken.loc.end.line === firstToken.loc.start.line + ) { + offsets.set(prevToken, info) + } + }, + /** @param {Expression | MetaProperty | TemplateLiteral} node */ + // Process parentheses. + // `:expression` does not match with MetaProperty and TemplateLiteral as a bug: https://github.com/estools/esquery/pull/59 + ':expression, MetaProperty, TemplateLiteral'(node) { + let leftToken = tokenStore.getTokenBefore(node) + let rightToken = tokenStore.getTokenAfter(node) + let firstToken = tokenStore.getFirstToken(node) + + while (isLeftParen(leftToken) && isRightParen(rightToken)) { + setOffset(firstToken, 1, leftToken) + setOffset(rightToken, 0, leftToken) + + firstToken = leftToken + leftToken = tokenStore.getTokenBefore(leftToken) + rightToken = tokenStore.getTokenAfter(rightToken) + } + }, + /** @param {ASTNode} node */ + // Ignore tokens of unknown nodes. + '*:exit'(node) { + if ( + !KNOWN_NODES.has(node.type) && + !NON_STANDARD_KNOWN_NODES.has(node.type) + ) { + ignore(node) + } + }, + /** @param {Program} node */ + // Top-level process. + Program(node) { + const firstToken = node.tokens[0] + const isScriptTag = + firstToken != null && + firstToken.type === 'Punctuator' && + firstToken.value === '<script>' + const baseIndent = isScriptTag + ? options.indentSize * options.baseIndent + : 0 + + for (const statement of node.body) { + processTopLevelNode(statement, baseIndent) + } + }, + /** @param {VElement} node */ + "VElement[parent.type!='VElement']"(node) { + processTopLevelNode(node, 0) + }, + /** @param {Program | VElement} node */ + // Do validation. + ":matches(Program, VElement[parent.type!='VElement']):exit"(node) { + let comments = [] + /** @type {Token[]} */ + let tokensOnSameLine = [] + let isBesideMultilineToken = false + let lastValidatedToken = null + + // Validate indentation of tokens. + for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) { + if ( + tokensOnSameLine.length === 0 || + tokensOnSameLine[0].loc.start.line === token.loc.start.line + ) { + // This is on the same line (or the first token). + tokensOnSameLine.push(token) + } else if (tokensOnSameLine.every(isComment)) { + // New line is detected, but the all tokens of the previous line are comment. + // Comment lines are adjusted to the next code line. + comments.push(tokensOnSameLine[0]) + isBesideMultilineToken = + /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line === + token.loc.start.line + tokensOnSameLine = [token] + } else { + // New line is detected, so validate the tokens. + if (!isBesideMultilineToken) { + validate(tokensOnSameLine, comments, lastValidatedToken) + lastValidatedToken = tokensOnSameLine[0] + } + isBesideMultilineToken = + /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line === + token.loc.start.line + tokensOnSameLine = [token] + comments = [] + } + } + if (tokensOnSameLine.length >= 1 && tokensOnSameLine.some(isNotComment)) { + validate(tokensOnSameLine, comments, lastValidatedToken) + } + } + }) +} diff --git a/src/eslint-plugins/utils/index.ts b/src/eslint-plugins/utils/index.ts new file mode 100644 index 00000000..6f77ab61 --- /dev/null +++ b/src/eslint-plugins/utils/index.ts @@ -0,0 +1,2068 @@ +/** + * @author Toru Nagashima <https://github.com/mysticatea> + * @copyright 2017 Toru Nagashima. All rights reserved. + * See LICENSE file in root directory for full license. + */ + 'use strict' + + import * as ESLint from "eslint"; + import * as ESTree from "estree"; + + /** + * @typedef {import('eslint').Rule.RuleModule} RuleModule + * @typedef {import('estree').Position} Position + * @typedef {import('eslint').Rule.CodePath} CodePath + * @typedef {import('eslint').Rule.CodePathSegment} CodePathSegment + */ + /** + * @typedef {object} ComponentArrayPropDetectName + * @property {'array'} type + * @property {Literal | TemplateLiteral} key + * @property {string} propName + * @property {null} value + * @property {Expression | SpreadElement} node + * + * @typedef {object} ComponentArrayPropUnknownName + * @property {'array'} type + * @property {null} key + * @property {null} propName + * @property {null} value + * @property {Expression | SpreadElement} node + * + * @typedef {ComponentArrayPropDetectName | ComponentArrayPropUnknownName} ComponentArrayProp + * + * @typedef {object} ComponentObjectPropDetectName + * @property {'object'} type + * @property {Expression} key + * @property {string} propName + * @property {Expression} value + * @property {Property} node + * + * @typedef {object} ComponentObjectPropUnknownName + * @property {'object'} type + * @property {null} key + * @property {null} propName + * @property {Expression} value + * @property {Property} node + * + * @typedef {ComponentObjectPropDetectName | ComponentObjectPropUnknownName} ComponentObjectProp + */ + /** + * @typedef {object} ComponentArrayEmitDetectName + * @property {'array'} type + * @property {Literal | TemplateLiteral} key + * @property {string} emitName + * @property {null} value + * @property {Expression | SpreadElement} node + * + * @typedef {object} ComponentArrayEmitUnknownName + * @property {'array'} type + * @property {null} key + * @property {null} emitName + * @property {null} value + * @property {Expression | SpreadElement} node + * + * @typedef {ComponentArrayEmitDetectName | ComponentArrayEmitUnknownName} ComponentArrayEmit + * + * @typedef {object} ComponentObjectEmitDetectName + * @property {'object'} type + * @property {Expression} key + * @property {string} emitName + * @property {Expression} value + * @property {Property} node + * + * @typedef {object} ComponentObjectEmitUnknownName + * @property {'object'} type + * @property {null} key + * @property {null} emitName + * @property {Expression} value + * @property {Property} node + * + * @typedef {ComponentObjectEmitDetectName | ComponentObjectEmitUnknownName} ComponentObjectEmit + */ + /** + * @typedef { {key: string | null, value: BlockStatement | null} } ComponentComputedProperty + */ + /** + * @typedef { 'props' | 'data' | 'computed' | 'setup' | 'watch' | 'methods' } GroupName + * @typedef { { type: 'array', name: string, groupName: GroupName, node: Literal | TemplateLiteral } } ComponentArrayPropertyData + * @typedef { { type: 'object', name: string, groupName: GroupName, node: Identifier | Literal | TemplateLiteral, property: Property } } ComponentObjectPropertyData + * @typedef { ComponentArrayPropertyData | ComponentObjectPropertyData } ComponentPropertyData + */ + /** + * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').VueObjectType} VueObjectType + * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').VueObjectData} VueObjectData + * @typedef {import('../../typings/eslint-plugin-vue/util-types/utils').VueVisitor} VueVisitor + */ + + // ------------------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------------------ + + const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json')) + const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json')) + const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json')) + const path = require('path') + const vueEslintParser = require('vue-eslint-parser') + const { findVariable } = require('eslint-utils') + + /** + * @type { WeakMap<RuleContext, Token[]> } + */ + const componentComments = new WeakMap() + + + let ruleMap: Map<string, RuleModule> | null = null + /** + * Get the core rule implementation from the rule name + */ + function getCoreRule(name: string): RuleModule { + const map = ruleMap || (ruleMap = new (require('eslint').Linter)().getRules()) + return map.get(name) || require(`eslint/lib/rules/${name}`) + } + + /** + * Wrap the rule context object to override methods which access to tokens (such as getTokenAfter). + * @param {RuleContext} context The rule context object. + * @param {ParserServices.TokenStore} tokenStore The token store object for template. + * @returns {RuleContext} + */ + function wrapContextToOverrideTokenMethods(context, tokenStore) { + const eslintSourceCode = context.getSourceCode() + /** @type {Token[] | null} */ + let tokensAndComments = null + function getTokensAndComments() { + if (tokensAndComments) { + return tokensAndComments + } + const templateBody = eslintSourceCode.ast.templateBody + tokensAndComments = templateBody + ? tokenStore.getTokens(templateBody, { + includeComments: true + }) + : [] + return tokensAndComments + } + const sourceCode = new Proxy(Object.assign({}, eslintSourceCode), { + get(_object, key) { + if (key === 'tokensAndComments') { + return getTokensAndComments() + } + // @ts-expect-error + return key in tokenStore ? tokenStore[key] : eslintSourceCode[key] + } + }) + + return { + // @ts-expect-error + __proto__: context, + getSourceCode() { + return sourceCode + } + } + } + + /** + * Wrap the rule context object to override report method to skip the dynamic argument. + * @param {RuleContext} context The rule context object. + * @returns {RuleContext} + */ + function wrapContextToOverrideReportMethodToSkipDynamicArgument(context) { + const sourceCode = context.getSourceCode() + const templateBody = sourceCode.ast.templateBody + if (!templateBody) { + return context + } + /** @type {Range[]} */ + const directiveKeyRanges = [] + const traverseNodes = vueEslintParser.AST.traverseNodes + traverseNodes(templateBody, { + enterNode(node, parent) { + if ( + parent && + parent.type === 'VDirectiveKey' && + node.type === 'VExpressionContainer' + ) { + directiveKeyRanges.push(node.range) + } + }, + leaveNode() {} + }) + + return { + // @ts-expect-error + __proto__: context, + report(descriptor, ...args) { + let range = null + if (descriptor.loc) { + const startLoc = descriptor.loc.start || descriptor.loc + const endLoc = descriptor.loc.end || startLoc + range = [ + sourceCode.getIndexFromLoc(startLoc), + sourceCode.getIndexFromLoc(endLoc) + ] + } else if (descriptor.node) { + range = descriptor.node.range + } + if (range) { + for (const directiveKeyRange of directiveKeyRanges) { + if ( + range[0] < directiveKeyRange[1] && + directiveKeyRange[0] < range[1] + ) { + return + } + } + } + context.report(descriptor, ...args) + } + } + } + + // ------------------------------------------------------------------------------ + // Exports + // ------------------------------------------------------------------------------ + + module.exports = { + /** + * Register the given visitor to parser services. + * If the parser service of `vue-eslint-parser` was not found, + * this generates a warning. + * + * @param {RuleContext} context The rule context to use parser services. + * @param {TemplateListener} templateBodyVisitor The visitor to traverse the template body. + * @param {RuleListener} [scriptVisitor] The visitor to traverse the script. + * @returns {RuleListener} The merged visitor. + */ + defineTemplateBodyVisitor, + + /** + * Wrap a given core rule to apply it to Vue.js template. + * @param {string} coreRuleName The name of the core rule implementation to wrap. + * @param {Object} [options] The option of this rule. + * @param {string[]} [options.categories] The categories of this rule. + * @param {boolean} [options.skipDynamicArguments] If `true`, skip validation within dynamic arguments. + * @param {boolean} [options.skipDynamicArgumentsReport] If `true`, skip report within dynamic arguments. + * @param { (context: RuleContext, options: { coreHandlers: RuleListener }) => TemplateListener } [options.create] If define, extend core rule. + */ + wrapCoreRule(coreRuleName: string, options: { + categories: string[] + skipDynamicArguments: boolean + skipDynamicArgumentsReport: boolean + create: (context: ESLint.Rule.RuleContext, + options: { + coreHandlers: ESLint.Rule.RuleListener + }) => TemplateListener + }): RuleModule { + const coreRule = getCoreRule(coreRuleName) + const { + categories, + skipDynamicArguments, + skipDynamicArgumentsReport, + create + } = options || {} + return { + create(context) { + const tokenStore = + context.parserServices.getTemplateBodyTokenStore && + context.parserServices.getTemplateBodyTokenStore() + + // The `context.getSourceCode()` cannot access the tokens of templates. + // So override the methods which access to tokens by the `tokenStore`. + if (tokenStore) { + context = wrapContextToOverrideTokenMethods(context, tokenStore) + } + + if (skipDynamicArgumentsReport) { + context = wrapContextToOverrideReportMethodToSkipDynamicArgument( + context + ) + } + + // Move `Program` handlers to `VElement[parent.type!='VElement']` + const coreHandlers = coreRule.create(context) + + const handlers = /** @type {TemplateListener} */ (Object.assign( + {}, + coreHandlers + )) + if (handlers.Program) { + handlers["VElement[parent.type!='VElement']"] = handlers.Program + delete handlers.Program + } + if (handlers['Program:exit']) { + handlers["VElement[parent.type!='VElement']:exit"] = + handlers['Program:exit'] + delete handlers['Program:exit'] + } + + if (skipDynamicArguments) { + let withinDynamicArguments = false + for (const name of Object.keys(handlers)) { + const original = handlers[name] + /** @param {any[]} args */ + handlers[name] = (...args) => { + if (withinDynamicArguments) return + // @ts-expect-error + original(...args) + } + } + handlers['VDirectiveKey > VExpressionContainer'] = () => { + withinDynamicArguments = true + } + handlers['VDirectiveKey > VExpressionContainer:exit'] = () => { + withinDynamicArguments = false + } + } + + if (create) { + compositingVisitors(handlers, create(context, { coreHandlers })) + } + + // Apply the handlers to templates. + return defineTemplateBodyVisitor(context, handlers) + }, + + meta: Object.assign({}, coreRule.meta, { + docs: Object.assign({}, coreRule.meta.docs, { + category: null, + categories, + url: `https://eslint.vuejs.org/rules/${path.basename( + coreRule.meta.docs.url || '' + )}.html`, + extensionRule: true, + coreRuleUrl: coreRule.meta.docs.url + }) + }) + } + }, + /** + * Checks whether the given value is defined. + * @template T + * @param {T | null | undefined} v + * @returns {v is T} + */ + isDef, + /** + * Get the previous sibling element of the given element. + * @param {VElement} node The element node to get the previous sibling element. + * @returns {VElement|null} The previous sibling element. + */ + prevSibling(node) { + let prevElement = null + + for (const siblingNode of (node.parent && node.parent.children) || []) { + if (siblingNode === node) { + return prevElement + } + if (siblingNode.type === 'VElement') { + prevElement = siblingNode + } + } + + return null + }, + + /** + * Check whether the given start tag has specific directive. + * @param {VElement} node The start tag node to check. + * @param {string} name The attribute name to check. + * @param {string} [value] The attribute value to check. + * @returns {boolean} `true` if the start tag has the attribute. + */ + hasAttribute(node, name, value) { + return Boolean(this.getAttribute(node, name, value)) + }, + + /** + * Check whether the given start tag has specific directive. + * @param {VElement} node The start tag node to check. + * @param {string} name The directive name to check. + * @param {string} [argument] The directive argument to check. + * @returns {boolean} `true` if the start tag has the directive. + */ + hasDirective(node, name, argument) { + return Boolean(this.getDirective(node, name, argument)) + }, + + /** + * Check whether the given directive attribute has their empty value (`=""`). + * @param {VDirective} node The directive attribute node to check. + * @param {RuleContext} context The rule context to use parser services. + * @returns {boolean} `true` if the directive attribute has their empty value (`=""`). + */ + isEmptyValueDirective(node, context) { + if (node.value == null) { + return false + } + if (node.value.expression != null) { + return false + } + + let valueText = context.getSourceCode().getText(node.value) + if ( + (valueText[0] === '"' || valueText[0] === "'") && + valueText[0] === valueText[valueText.length - 1] + ) { + // quoted + valueText = valueText.slice(1, -1) + } + if (!valueText) { + // empty + return true + } + return false + }, + + /** + * Check whether the given directive attribute has their empty expression value (e.g. `=" "`, `="/* */"`). + * @param {VDirective} node The directive attribute node to check. + * @param {RuleContext} context The rule context to use parser services. + * @returns {boolean} `true` if the directive attribute has their empty expression value. + */ + isEmptyExpressionValueDirective(node, context) { + if (node.value == null) { + return false + } + if (node.value.expression != null) { + return false + } + + const valueNode = node.value + const tokenStore = context.parserServices.getTemplateBodyTokenStore() + let quote1 = null + let quote2 = null + // `node.value` may be only comments, so cannot get the correct tokens with `tokenStore.getTokens(node.value)`. + for (const token of tokenStore.getTokens(node)) { + if (token.range[1] <= valueNode.range[0]) { + continue + } + if (valueNode.range[1] <= token.range[0]) { + // empty + return true + } + if ( + !quote1 && + token.type === 'Punctuator' && + (token.value === '"' || token.value === "'") + ) { + quote1 = token + continue + } + if ( + !quote2 && + quote1 && + token.type === 'Punctuator' && + token.value === quote1.value + ) { + quote2 = token + continue + } + // not empty + return false + } + // empty + return true + }, + + /** + * Get the attribute which has the given name. + * @param {VElement} node The start tag node to check. + * @param {string} name The attribute name to check. + * @param {string} [value] The attribute value to check. + * @returns {VAttribute | null} The found attribute. + */ + getAttribute(node, name, value) { + return ( + node.startTag.attributes.find( + /** + * @param {VAttribute | VDirective} node + * @returns {node is VAttribute} + */ + (node) => { + return ( + !node.directive && + node.key.name === name && + (value === undefined || + (node.value != null && node.value.value === value)) + ) + } + ) || null + ) + }, + + /** + * Get the directive list which has the given name. + * @param {VElement | VStartTag} node The start tag node to check. + * @param {string} name The directive name to check. + * @returns {VDirective[]} The array of `v-slot` directives. + */ + getDirectives(node, name) { + const attributes = + node.type === 'VElement' ? node.startTag.attributes : node.attributes + return attributes.filter( + /** + * @param {VAttribute | VDirective} node + * @returns {node is VDirective} + */ + (node) => { + return node.directive && node.key.name.name === name + } + ) + }, + /** + * Get the directive which has the given name. + * @param {VElement} node The start tag node to check. + * @param {string} name The directive name to check. + * @param {string} [argument] The directive argument to check. + * @returns {VDirective | null} The found directive. + */ + getDirective(node, name, argument) { + return ( + node.startTag.attributes.find( + /** + * @param {VAttribute | VDirective} node + * @returns {node is VDirective} + */ + (node) => { + return ( + node.directive && + node.key.name.name === name && + (argument === undefined || + (node.key.argument && + node.key.argument.type === 'VIdentifier' && + node.key.argument.name) === argument) + ) + } + ) || null + ) + }, + + /** + * Returns the list of all registered components + * @param {ObjectExpression} componentObject + * @returns { { node: Property, name: string }[] } Array of ASTNodes + */ + getRegisteredComponents(componentObject) { + const componentsNode = componentObject.properties.find( + /** + * @param {ESNode} p + * @returns {p is (Property & { key: Identifier & {name: 'components'}, value: ObjectExpression })} + */ + (p) => { + return ( + p.type === 'Property' && + getStaticPropertyName(p) === 'components' && + p.value.type === 'ObjectExpression' + ) + } + ) + + if (!componentsNode) { + return [] + } + + return componentsNode.value.properties + .filter(isProperty) + .map((node) => { + const name = getStaticPropertyName(node) + return name ? { node, name } : null + }) + .filter(isDef) + }, + + /** + * Check whether the previous sibling element has `if` or `else-if` directive. + * @param {VElement} node The element node to check. + * @returns {boolean} `true` if the previous sibling element has `if` or `else-if` directive. + */ + prevElementHasIf(node) { + const prev = this.prevSibling(node) + return ( + prev != null && + prev.startTag.attributes.some( + (a) => + a.directive && + (a.key.name.name === 'if' || a.key.name.name === 'else-if') + ) + ) + }, + + /** + * Check whether the given node is a custom component or not. + * @param {VElement} node The start tag node to check. + * @returns {boolean} `true` if the node is a custom component. + */ + isCustomComponent(node) { + return ( + (this.isHtmlElementNode(node) && + !this.isHtmlWellKnownElementName(node.rawName)) || + (this.isSvgElementNode(node) && + !this.isSvgWellKnownElementName(node.rawName)) || + this.hasAttribute(node, 'is') || + this.hasDirective(node, 'bind', 'is') || + this.hasDirective(node, 'is') + ) + }, + + /** + * Check whether the given node is a HTML element or not. + * @param {VElement} node The node to check. + * @returns {boolean} `true` if the node is a HTML element. + */ + isHtmlElementNode(node) { + return node.namespace === vueEslintParser.AST.NS.HTML + }, + + /** + * Check whether the given node is a SVG element or not. + * @param {VElement} node The node to check. + * @returns {boolean} `true` if the name is a SVG element. + */ + isSvgElementNode(node) { + return node.namespace === vueEslintParser.AST.NS.SVG + }, + + /** + * Check whether the given name is a MathML element or not. + * @param {VElement} node The node to check. + * @returns {boolean} `true` if the node is a MathML element. + */ + isMathMLElementNode(node) { + return node.namespace === vueEslintParser.AST.NS.MathML + }, + + /** + * Check whether the given name is an well-known element or not. + * @param {string} name The name to check. + * @returns {boolean} `true` if the name is an well-known element name. + */ + isHtmlWellKnownElementName(name) { + return HTML_ELEMENT_NAMES.has(name) + }, + + /** + * Check whether the given name is an well-known SVG element or not. + * @param {string} name The name to check. + * @returns {boolean} `true` if the name is an well-known SVG element name. + */ + isSvgWellKnownElementName(name) { + return SVG_ELEMENT_NAMES.has(name) + }, + + /** + * Check whether the given name is a void element name or not. + * @param {string} name The name to check. + * @returns {boolean} `true` if the name is a void element name. + */ + isHtmlVoidElementName(name) { + return VOID_ELEMENT_NAMES.has(name) + }, + /** + * Gets the property name of a given node. + * @param {Property|AssignmentProperty|MethodDefinition|MemberExpression} node - The node to get. + * @return {string|null} The property name if static. Otherwise, null. + */ + getStaticPropertyName, + /** + * Gets the string of a given node. + * @param {Literal|TemplateLiteral} node - The node to get. + * @return {string|null} The string if static. Otherwise, null. + */ + getStringLiteralValue, + /** + * Get all props by looking at all component's properties + * @param {ObjectExpression} componentObject Object with component definition + * @return {(ComponentArrayProp | ComponentObjectProp)[]} Array of component props in format: [{key?: String, value?: ASTNode, node: ASTNod}] + */ + getComponentProps(componentObject) { + const propsNode = componentObject.properties.find( + /** + * @param {ESNode} p + * @returns {p is (Property & { key: Identifier & {name: 'props'}, value: ObjectExpression | ArrayExpression })} + */ + (p) => { + return ( + p.type === 'Property' && + getStaticPropertyName(p) === 'props' && + (p.value.type === 'ObjectExpression' || + p.value.type === 'ArrayExpression') + ) + } + ) + + if (!propsNode) { + return [] + } + + if (propsNode.value.type === 'ObjectExpression') { + return propsNode.value.properties.filter(isProperty).map((prop) => { + const propName = getStaticPropertyName(prop) + if (propName != null) { + return { + type: 'object', + key: prop.key, + propName, + value: skipTSAsExpression(prop.value), + node: prop + } + } + return { + type: 'object', + key: null, + propName: null, + value: skipTSAsExpression(prop.value), + node: prop + } + }) + } else { + return propsNode.value.elements.filter(isDef).map((prop) => { + if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') { + const propName = getStringLiteralValue(prop) + if (propName != null) { + return { + type: 'array', + key: prop, + propName, + value: null, + node: prop + } + } + } + return { + type: 'array', + key: null, + propName: null, + value: null, + node: prop + } + }) + } + }, + + /** + * Get all emits by looking at all component's properties + * @param {ObjectExpression} componentObject Object with component definition + * @return {(ComponentArrayEmit | ComponentObjectEmit)[]} Array of component emits in format: [{key?: String, value?: ASTNode, node: ASTNod}] + */ + getComponentEmits(componentObject) { + const emitsNode = componentObject.properties.find( + /** + * @param {ESNode} p + * @returns {p is (Property & { key: Identifier & {name: 'emits'}, value: ObjectExpression | ArrayExpression })} + */ + (p) => { + return ( + p.type === 'Property' && + getStaticPropertyName(p) === 'emits' && + (p.value.type === 'ObjectExpression' || + p.value.type === 'ArrayExpression') + ) + } + ) + + if (!emitsNode) { + return [] + } + + if (emitsNode.value.type === 'ObjectExpression') { + return emitsNode.value.properties.filter(isProperty).map((prop) => { + const emitName = getStaticPropertyName(prop) + if (emitName != null) { + return { + type: 'object', + key: prop.key, + emitName, + value: skipTSAsExpression(prop.value), + node: prop + } + } + return { + type: 'object', + key: null, + emitName: null, + value: skipTSAsExpression(prop.value), + node: prop + } + }) + } else { + return emitsNode.value.elements.filter(isDef).map((prop) => { + if (prop.type === 'Literal' || prop.type === 'TemplateLiteral') { + const emitName = getStringLiteralValue(prop) + if (emitName != null) { + return { + type: 'array', + key: prop, + emitName, + value: null, + node: prop + } + } + } + return { + type: 'array', + key: null, + emitName: null, + value: null, + node: prop + } + }) + } + }, + + /** + * Get all computed properties by looking at all component's properties + * @param {ObjectExpression} componentObject Object with component definition + * @return {ComponentComputedProperty[]} Array of computed properties in format: [{key: String, value: ASTNode}] + */ + getComputedProperties(componentObject) { + const computedPropertiesNode = componentObject.properties.find( + /** + * @param {ESNode} p + * @returns {p is (Property & { key: Identifier & {name: 'computed'}, value: ObjectExpression })} + */ + (p) => { + return ( + p.type === 'Property' && + getStaticPropertyName(p) === 'computed' && + p.value.type === 'ObjectExpression' + ) + } + ) + + if (!computedPropertiesNode) { + return [] + } + + return computedPropertiesNode.value.properties + .filter(isProperty) + .map((cp) => { + const key = getStaticPropertyName(cp) + /** @type {Expression} */ + const propValue = skipTSAsExpression(cp.value) + /** @type {BlockStatement | null} */ + let value = null + + if (propValue.type === 'FunctionExpression') { + value = propValue.body + } else if (propValue.type === 'ObjectExpression') { + const get = /** @type {(Property & { value: FunctionExpression }) | null} */ (findProperty( + propValue, + 'get', + (p) => p.value.type === 'FunctionExpression' + )) + value = get ? get.value.body : null + } + + return { key, value } + }) + }, + + /** + * Get getter body from computed function + * @param {CallExpression} callExpression call of computed function + * @return {FunctionExpression | ArrowFunctionExpression | null} getter function + */ + getGetterBodyFromComputedFunction(callExpression) { + if (callExpression.arguments.length <= 0) { + return null + } + + const arg = callExpression.arguments[0] + + if ( + arg.type === 'FunctionExpression' || + arg.type === 'ArrowFunctionExpression' + ) { + return arg + } + + if (arg.type === 'ObjectExpression') { + const getProperty = /** @type {(Property & { value: FunctionExpression | ArrowFunctionExpression }) | null} */ (findProperty( + arg, + 'get', + (p) => + p.value.type === 'FunctionExpression' || + p.value.type === 'ArrowFunctionExpression' + )) + return getProperty ? getProperty.value : null + } + + return null + }, + + isVueFile, + + /** + * Check if current file is a Vue instance or component and call callback + * @param {RuleContext} context The ESLint rule context object. + * @param { (node: ObjectExpression, type: VueObjectType) => void } cb Callback function + */ + executeOnVue(context, cb) { + return compositingVisitors( + this.executeOnVueComponent(context, cb), + this.executeOnVueInstance(context, cb) + ) + }, + + /** + * Define handlers to traverse the Vue Objects. + * Some special events are available to visitor. + * + * - `onVueObjectEnter` ... Event when Vue Object is found. + * - `onVueObjectExit` ... Event when Vue Object visit ends. + * - `onSetupFunctionEnter` ... Event when setup function found. + * - `onRenderFunctionEnter` ... Event when render function found. + * + * @param {RuleContext} context The ESLint rule context object. + * @param {VueVisitor} visitor The visitor to traverse the Vue Objects. + */ + defineVueVisitor(context, visitor) { + /** @type {VueObjectData | null} */ + let vueStack = null + + /** + * @param {string} key + * @param {ESNode} node + */ + function callVisitor(key, node) { + if (visitor[key] && vueStack) { + // @ts-expect-error + visitor[key](node, vueStack) + } + } + + /** @type {NodeListener} */ + const vueVisitor = {} + for (const key in visitor) { + vueVisitor[key] = (node) => callVisitor(key, node) + } + + /** + * @param {ObjectExpression} node + */ + vueVisitor.ObjectExpression = (node) => { + const type = getVueObjectType(context, node) + if (type) { + vueStack = { + node, + type, + parent: vueStack, + get functional() { + const functional = node.properties.find( + /** + * @param {Property | SpreadElement} p + * @returns {p is Property} + */ + (p) => + p.type === 'Property' && + getStaticPropertyName(p) === 'functional' + ) + if (!functional) { + return false + } + if ( + functional.value.type === 'Literal' && + functional.value.value === false + ) { + return false + } + return true + } + } + callVisitor('onVueObjectEnter', node) + } + callVisitor('ObjectExpression', node) + } + vueVisitor['ObjectExpression:exit'] = (node) => { + callVisitor('ObjectExpression:exit', node) + if (vueStack && vueStack.node === node) { + callVisitor('onVueObjectExit', node) + vueStack = vueStack.parent + } + } + if (visitor.onSetupFunctionEnter || visitor.onRenderFunctionEnter) { + /** @param { (FunctionExpression | ArrowFunctionExpression) & { parent: Property } } node */ + vueVisitor[ + 'Property[value.type=/^(Arrow)?FunctionExpression$/] > :function' + ] = (node) => { + /** @type {Property} */ + const prop = node.parent + if (vueStack && prop.parent === vueStack.node && prop.value === node) { + const name = getStaticPropertyName(prop) + if (name === 'setup') { + callVisitor('onSetupFunctionEnter', node) + } else if (name === 'render') { + callVisitor('onRenderFunctionEnter', node) + } + } + callVisitor( + 'Property[value.type=/^(Arrow)?FunctionExpression$/] > :function', + node + ) + } + } + + return vueVisitor + }, + + getVueObjectType, + /** + * Get the Vue component definition type from given node + * Vue.component('xxx', {}) || component('xxx', {}) + * @param {ObjectExpression} node Node to check + * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | null} + */ + getVueComponentDefinitionType, + compositingVisitors, + + /** + * Check if current file is a Vue instance (new Vue) and call callback + * @param {RuleContext} context The ESLint rule context object. + * @param { (node: ObjectExpression, type: VueObjectType) => void } cb Callback function + */ + executeOnVueInstance(context, cb) { + return { + /** @param {ObjectExpression} node */ + 'ObjectExpression:exit'(node) { + const type = getVueObjectType(context, node) + if (!type || type !== 'instance') return + cb(node, type) + } + } + }, + + /** + * Check if current file is a Vue component and call callback + * @param {RuleContext} context The ESLint rule context object. + * @param { (node: ObjectExpression, type: VueObjectType) => void } cb Callback function + */ + executeOnVueComponent(context, cb) { + return { + /** @param {ObjectExpression} node */ + 'ObjectExpression:exit'(node) { + const type = getVueObjectType(context, node) + if ( + !type || + (type !== 'mark' && type !== 'export' && type !== 'definition') + ) + return + cb(node, type) + } + } + }, + + /** + * Check call `Vue.component` and call callback. + * @param {RuleContext} _context The ESLint rule context object. + * @param { (node: CallExpression) => void } cb Callback function + */ + executeOnCallVueComponent(_context, cb) { + return { + /** @param {Identifier & { parent: MemberExpression & { parent: CallExpression } } } node */ + "CallExpression > MemberExpression > Identifier[name='component']": ( + node + ) => { + const callExpr = node.parent.parent + const callee = callExpr.callee + + if (callee.type === 'MemberExpression') { + const calleeObject = skipTSAsExpression(callee.object) + + if ( + calleeObject.type === 'Identifier' && + // calleeObject.name === 'Vue' && // Any names can be used in Vue.js 3.x. e.g. app.component() + callee.property === node && + callExpr.arguments.length >= 1 + ) { + cb(callExpr) + } + } + } + } + }, + /** + * Return generator with all properties + * @param {ObjectExpression} node Node to check + * @param {Set<GroupName>} groups Name of parent group + * @returns {IterableIterator<ComponentPropertyData>} + */ + *iterateProperties(node, groups) { + for (const item of node.properties) { + if (item.type !== 'Property') { + continue + } + + const name = /** @type {GroupName | null} */ (getStaticPropertyName(item)) + if (!name || !groups.has(name)) continue + + if (item.value.type === 'ArrayExpression') { + yield* this.iterateArrayExpression(item.value, name) + } else if (item.value.type === 'ObjectExpression') { + yield* this.iterateObjectExpression(item.value, name) + } else if (item.value.type === 'FunctionExpression') { + yield* this.iterateFunctionExpression(item.value, name) + } else if (item.value.type === 'ArrowFunctionExpression') { + yield* this.iterateArrowFunctionExpression(item.value, name) + } + } + }, + + /** + * Return generator with all elements inside ArrayExpression + * @param {ArrayExpression} node Node to check + * @param {GroupName} groupName Name of parent group + * @returns {IterableIterator<ComponentArrayPropertyData>} + */ + *iterateArrayExpression(node, groupName) { + for (const item of node.elements) { + if ( + item && + (item.type === 'Literal' || item.type === 'TemplateLiteral') + ) { + const name = getStringLiteralValue(item) + if (name) { + yield { type: 'array', name, groupName, node: item } + } + } + } + }, + + /** + * Return generator with all elements inside ObjectExpression + * @param {ObjectExpression} node Node to check + * @param {GroupName} groupName Name of parent group + * @returns {IterableIterator<ComponentObjectPropertyData>} + */ + *iterateObjectExpression(node, groupName) { + /** @type {Set<Property> | undefined} */ + let usedGetter + for (const item of node.properties) { + if (item.type === 'Property') { + const key = item.key + if ( + key.type === 'Identifier' || + key.type === 'Literal' || + key.type === 'TemplateLiteral' + ) { + const name = getStaticPropertyName(item) + if (name) { + if (item.kind === 'set') { + // find getter pair + if ( + node.properties.some((item2) => { + if (item2.type === 'Property' && item2.kind === 'get') { + if (!usedGetter) { + usedGetter = new Set() + } + if (usedGetter.has(item2)) { + return false + } + const getterName = getStaticPropertyName(item2) + if (getterName === name) { + usedGetter.add(item2) + return true + } + } + return false + }) + ) { + // has getter pair + continue + } + } + yield { + type: 'object', + name, + groupName, + node: key, + property: item + } + } + } + } + } + }, + + /** + * Return generator with all elements inside FunctionExpression + * @param {FunctionExpression} node Node to check + * @param {GroupName} groupName Name of parent group + * @returns {IterableIterator<ComponentObjectPropertyData>} + */ + *iterateFunctionExpression(node, groupName) { + if (node.body.type === 'BlockStatement') { + for (const item of node.body.body) { + if ( + item.type === 'ReturnStatement' && + item.argument && + item.argument.type === 'ObjectExpression' + ) { + yield* this.iterateObjectExpression(item.argument, groupName) + } + } + } + }, + + /** + * Return generator with all elements inside ArrowFunctionExpression + * @param {ArrowFunctionExpression} node Node to check + * @param {GroupName} groupName Name of parent group + * @returns {IterableIterator<ComponentObjectPropertyData>} + */ + *iterateArrowFunctionExpression(node, groupName) { + const body = node.body + if (body.type === 'BlockStatement') { + for (const item of body.body) { + if ( + item.type === 'ReturnStatement' && + item.argument && + item.argument.type === 'ObjectExpression' + ) { + yield* this.iterateObjectExpression(item.argument, groupName) + } + } + } else if (body.type === 'ObjectExpression') { + yield* this.iterateObjectExpression(body, groupName) + } + }, + + /** + * Find all functions which do not always return values + * @param {boolean} treatUndefinedAsUnspecified + * @param { (node: ESNode) => void } cb Callback function + * @returns {RuleListener} + */ + executeOnFunctionsWithoutReturn(treatUndefinedAsUnspecified, cb) { + /** + * @typedef {object} FuncInfo + * @property {FuncInfo} funcInfo + * @property {CodePath} codePath + * @property {boolean} hasReturn + * @property {boolean} hasReturnValue + * @property {ESNode} node + */ + + /** @type {FuncInfo} */ + let funcInfo + + /** @param {CodePathSegment} segment */ + function isReachable(segment) { + return segment.reachable + } + + function isValidReturn() { + if ( + funcInfo.codePath && + funcInfo.codePath.currentSegments.some(isReachable) + ) { + return false + } + return !treatUndefinedAsUnspecified || funcInfo.hasReturnValue + } + + return { + /** + * @param {CodePath} codePath + * @param {ESNode} node + */ + onCodePathStart(codePath, node) { + funcInfo = { + codePath, + funcInfo, + hasReturn: false, + hasReturnValue: false, + node + } + }, + onCodePathEnd() { + funcInfo = funcInfo.funcInfo + }, + /** @param {ReturnStatement} node */ + ReturnStatement(node) { + funcInfo.hasReturn = true + funcInfo.hasReturnValue = Boolean(node.argument) + }, + /** @param {ArrowFunctionExpression} node */ + 'ArrowFunctionExpression:exit'(node) { + if (!isValidReturn() && !node.expression) { + cb(funcInfo.node) + } + }, + 'FunctionExpression:exit'() { + if (!isValidReturn()) { + cb(funcInfo.node) + } + } + } + }, + + /** + * Check whether the component is declared in a single line or not. + * @param {ASTNode} node + * @returns {boolean} + */ + isSingleLine(node) { + return node.loc.start.line === node.loc.end.line + }, + + /** + * Check whether the templateBody of the program has invalid EOF or not. + * @param {Program} node The program node to check. + * @returns {boolean} `true` if it has invalid EOF. + */ + hasInvalidEOF(node) { + const body = node.templateBody + if (body == null || body.errors == null) { + return false + } + return body.errors.some( + (error) => typeof error.code === 'string' && error.code.startsWith('eof-') + ) + }, + + /** + * Get the chaining nodes of MemberExpression. + * + * @param {ESNode} node The node to parse + * @return {[ESNode, ...MemberExpression[]]} The chaining nodes + */ + getMemberChaining(node) { + /** @type {MemberExpression[]} */ + const nodes = [] + let n = skipChainExpression(node) + + while (n.type === 'MemberExpression') { + nodes.push(n) + n = skipChainExpression(n.object) + } + + return [n, ...nodes.reverse()] + }, + /** + * return two string editdistance + * @param {string} a string a to compare + * @param {string} b string b to compare + * @returns {number} + */ + editDistance(a, b) { + if (a === b) { + return 0 + } + const alen = a.length + const blen = b.length + const dp = Array.from({ length: alen + 1 }).map((_) => + Array.from({ length: blen + 1 }).fill(0) + ) + for (let i = 0; i <= alen; i++) { + dp[i][0] = i + } + for (let j = 0; j <= blen; j++) { + dp[0][j] = j + } + for (let i = 1; i <= alen; i++) { + for (let j = 1; j <= blen; j++) { + if (a[i - 1] === b[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + } else { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1 + } + } + } + return dp[alen][blen] + }, + /** + * Checks whether the given node is Property. + */ + isProperty, + /** + * Checks whether the given node is AssignmentProperty. + */ + isAssignmentProperty, + /** + * Checks whether the given node is VElement. + */ + isVElement, + /** + * Finds the property with the given name from the given ObjectExpression node. + */ + findProperty, + /** + * Finds the assignment property with the given name from the given ObjectPattern node. + */ + findAssignmentProperty, + /** + * Checks if the given node is a property value. + * @param {Property} prop + * @param {Expression} node + */ + isPropertyChain, + /** + * Retrieve `TSAsExpression#expression` value if the given node a `TSAsExpression` node. Otherwise, pass through it. + */ + skipTSAsExpression, + /** + * Retrieve `AssignmentPattern#left` value if the given node a `AssignmentPattern` node. Otherwise, pass through it. + */ + skipDefaultParamValue, + /** + * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it. + */ + skipChainExpression, + + /** + * Check whether the given node is `this` or variable that stores `this`. + * @param {ESNode} node The node to check + * @param {RuleContext} context The rule context to use parser services. + * @returns {boolean} `true` if the given node is `this`. + */ + isThis(node, context) { + if (node.type === 'ThisExpression') { + return true + } + if (node.type !== 'Identifier') { + return false + } + const parent = node.parent + if (parent.type === 'MemberExpression') { + if (parent.property === node) { + return false + } + } else if (parent.type === 'Property') { + if (parent.key === node && !parent.computed) { + return false + } + } + + const variable = findVariable(context.getScope(), node) + + if (variable != null && variable.defs.length === 1) { + const def = variable.defs[0] + if ( + def.type === 'Variable' && + def.parent.kind === 'const' && + def.node.id.type === 'Identifier' + ) { + return Boolean( + def.node && def.node.init && def.node.init.type === 'ThisExpression' + ) + } + } + return false + }, + + /** + * @param {MemberExpression|Identifier} props + * @returns { { kind: 'assignment' | 'update' | 'call' , node: ESNode, pathNodes: MemberExpression[] } | null } + */ + findMutating(props) { + /** @type {MemberExpression[]} */ + const pathNodes = [] + /** @type {MemberExpression | Identifier | ChainExpression} */ + let node = props + let target = node.parent + while (true) { + if (target.type === 'AssignmentExpression') { + if (target.left === node) { + // this.xxx <=|+=|-=> + return { + kind: 'assignment', + node: target, + pathNodes + } + } + } else if (target.type === 'UpdateExpression') { + // this.xxx <++|--> + return { + kind: 'update', + node: target, + pathNodes + } + } else if (target.type === 'CallExpression') { + if (pathNodes.length > 0 && target.callee === node) { + const mem = pathNodes[pathNodes.length - 1] + const callName = getStaticPropertyName(mem) + if ( + callName && + /^push|pop|shift|unshift|reverse|splice|sort|copyWithin|fill$/u.exec( + callName + ) + ) { + // this.xxx.push() + pathNodes.pop() + return { + kind: 'call', + node: target, + pathNodes + } + } + } + } else if (target.type === 'MemberExpression') { + if (target.object === node) { + pathNodes.push(target) + node = target + target = target.parent + continue // loop + } + } else if (target.type === 'ChainExpression') { + node = target + target = target.parent + continue // loop + } + + return null + } + }, + + /** + * Return generator with the all handler nodes defined in the given watcher property. + * @param {Property|Expression} property + * @returns {IterableIterator<Expression>} + */ + iterateWatchHandlerValues, + + /** + * Wraps composition API trace map in both 'vue' and '@vue/composition-api' imports + * @param {import('eslint-utils').TYPES.TraceMap} map + */ + createCompositionApiTraceMap: (map) => ({ + vue: map, + '@vue/composition-api': map + }), + + /** + * Checks whether or not the tokens of two given nodes are same. + * @param {ASTNode} left A node 1 to compare. + * @param {ASTNode} right A node 2 to compare. + * @param {ParserServices.TokenStore | SourceCode} sourceCode The ESLint source code object. + * @returns {boolean} the source code for the given node. + */ + equalTokens(left, right, sourceCode) { + const tokensL = sourceCode.getTokens(left) + const tokensR = sourceCode.getTokens(right) + + if (tokensL.length !== tokensR.length) { + return false + } + for (let i = 0; i < tokensL.length; ++i) { + if ( + tokensL[i].type !== tokensR[i].type || + tokensL[i].value !== tokensR[i].value + ) { + return false + } + } + + return true + } + } + + // ------------------------------------------------------------------------------ + // Standard Helpers + // ------------------------------------------------------------------------------ + + /** + * Checks whether the given value is defined. + * @template T + * @param {T | null | undefined} v + * @returns {v is T} + */ + function isDef(v) { + return v != null + } + + // ------------------------------------------------------------------------------ + // Rule Helpers + // ------------------------------------------------------------------------------ + + /** + * Register the given visitor to parser services. + * If the parser service of `vue-eslint-parser` was not found, + * this generates a warning. + * + * @param {RuleContext} context The rule context to use parser services. + * @param {TemplateListener} templateBodyVisitor The visitor to traverse the template body. + * @param {RuleListener} [scriptVisitor] The visitor to traverse the script. + * @returns {RuleListener} The merged visitor. + */ + function defineTemplateBodyVisitor( + context, + templateBodyVisitor, + scriptVisitor + ) { + if (context.parserServices.defineTemplateBodyVisitor == null) { + const filename = context.getFilename() + if (path.extname(filename) === '.vue') { + context.report({ + loc: { line: 1, column: 0 }, + message: + 'Use the latest vue-eslint-parser. See also https://eslint.vuejs.org/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error.' + }) + } + return {} + } + return context.parserServices.defineTemplateBodyVisitor( + templateBodyVisitor, + scriptVisitor + ) + } + + /** + * @template T + * @param {T} visitor + * @param {...(TemplateListener | RuleListener | NodeListener)} visitors + * @returns {T} + */ + function compositingVisitors(visitor, ...visitors) { + for (const v of visitors) { + for (const key in v) { + // @ts-expect-error + if (visitor[key]) { + // @ts-expect-error + const o = visitor[key] + // @ts-expect-error + visitor[key] = (...args) => { + o(...args) + // @ts-expect-error + v[key](...args) + } + } else { + // @ts-expect-error + visitor[key] = v[key] + } + } + } + return visitor + } + + // ------------------------------------------------------------------------------ + // AST Helpers + // ------------------------------------------------------------------------------ + + /** + * Finds the property with the given name from the given ObjectExpression node. + * @param {ObjectExpression} node + * @param {string} name + * @param { (p: Property) => boolean } [filter] + * @returns { (Property) | null} + */ + function findProperty(node, name, filter) { + const predicate = filter + ? /** + * @param {Property | SpreadElement} prop + * @returns {prop is Property} + */ + (prop) => + isProperty(prop) && getStaticPropertyName(prop) === name && filter(prop) + : /** + * @param {Property | SpreadElement} prop + * @returns {prop is Property} + */ + (prop) => isProperty(prop) && getStaticPropertyName(prop) === name + return node.properties.find(predicate) || null + } + + /** + * Finds the assignment property with the given name from the given ObjectPattern node. + * @param {ObjectPattern} node + * @param {string} name + * @param { (p: AssignmentProperty) => boolean } [filter] + * @returns { (AssignmentProperty) | null} + */ + function findAssignmentProperty(node, name, filter) { + const predicate = filter + ? /** + * @param {AssignmentProperty | RestElement} prop + * @returns {prop is AssignmentProperty} + */ + (prop) => + isAssignmentProperty(prop) && + getStaticPropertyName(prop) === name && + filter(prop) + : /** + * @param {AssignmentProperty | RestElement} prop + * @returns {prop is AssignmentProperty} + */ + (prop) => + isAssignmentProperty(prop) && getStaticPropertyName(prop) === name + return node.properties.find(predicate) || null + } + + /** + * Checks whether the given node is Property. + * @param {Property | SpreadElement | AssignmentProperty} node + * @returns {node is Property} + */ + function isProperty(node) { + if (node.type !== 'Property') { + return false + } + return !node.parent || node.parent.type === 'ObjectExpression' + } + /** + * Checks whether the given node is AssignmentProperty. + * @param {AssignmentProperty | RestElement} node + * @returns {node is AssignmentProperty} + */ + function isAssignmentProperty(node) { + return node.type === 'Property' + } + /** + * Checks whether the given node is VElement. + * @param {VElement | VExpressionContainer | VText} node + * @returns {node is VElement} + */ + function isVElement(node) { + return node.type === 'VElement' + } + + /** + * Retrieve `TSAsExpression#expression` value if the given node a `TSAsExpression` node. Otherwise, pass through it. + * @template T Node type + * @param {T | TSAsExpression} node The node to address. + * @returns {T} The `TSAsExpression#expression` value if the node is a `TSAsExpression` node. Otherwise, the node. + */ + function skipTSAsExpression(node) { + if (!node) { + return node + } + // @ts-expect-error + if (node.type === 'TSAsExpression') { + // @ts-expect-error + return skipTSAsExpression(node.expression) + } + // @ts-expect-error + return node + } + + /** + * Gets the parent node of the given node. This method returns a value ignoring `X as F`. + * @param {Expression} node + * @returns {ASTNode} + */ + function getParent(node) { + let parent = node.parent + while (parent.type === 'TSAsExpression') { + parent = parent.parent + } + return parent + } + + /** + * Checks if the given node is a property value. + * @param {Property} prop + * @param {Expression} node + */ + function isPropertyChain(prop, node) { + let value = node + while (value.parent.type === 'TSAsExpression') { + value = value.parent + } + return prop === value.parent && prop.value === value + } + + /** + * Retrieve `AssignmentPattern#left` value if the given node a `AssignmentPattern` node. Otherwise, pass through it. + * @template T Node type + * @param {T | AssignmentPattern} node The node to address. + * @return {T} The `AssignmentPattern#left` value if the node is a `AssignmentPattern` node. Otherwise, the node. + */ + function skipDefaultParamValue(node) { + if (!node) { + return node + } + // @ts-expect-error + if (node.type === 'AssignmentPattern') { + // @ts-expect-error + return skipDefaultParamValue(node.left) + } + // @ts-expect-error + return node + } + + /** + * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it. + * @template T Node type + * @param {T | ChainExpression} node The node to address. + * @returns {T} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node. + */ + function skipChainExpression(node) { + if (!node) { + return node + } + // @ts-expect-error + if (node.type === 'ChainExpression') { + // @ts-expect-error + return skipChainExpression(node.expression) + } + // @ts-expect-error + return node + } + + /** + * Gets the property name of a given node. + * @param {Property|AssignmentProperty|MethodDefinition|MemberExpression} node - The node to get. + * @return {string|null} The property name if static. Otherwise, null. + */ + function getStaticPropertyName(node) { + if (node.type === 'Property' || node.type === 'MethodDefinition') { + const key = node.key + + if (!node.computed) { + if (key.type === 'Identifier') { + return key.name + } + } + // @ts-expect-error + return getStringLiteralValue(key) + } else if (node.type === 'MemberExpression') { + const property = node.property + if (!node.computed) { + if (property.type === 'Identifier') { + return property.name + } + return null + } + // @ts-expect-error + return getStringLiteralValue(property) + } + return null + } + + /** + * Gets the string of a given node. + * @param {Literal|TemplateLiteral} node - The node to get. + * @param {boolean} [stringOnly] + * @return {string|null} The string if static. Otherwise, null. + */ + function getStringLiteralValue(node, stringOnly) { + if (node.type === 'Literal') { + if (node.value == null) { + if (!stringOnly && node.bigint != null) { + return node.bigint + } + return null + } + if (typeof node.value === 'string') { + return node.value + } + if (!stringOnly) { + return String(node.value) + } + return null + } + if (node.type === 'TemplateLiteral') { + if (node.expressions.length === 0 && node.quasis.length === 1) { + return node.quasis[0].value.cooked + } + } + return null + } + + // ------------------------------------------------------------------------------ + // Vue Helpers + // ------------------------------------------------------------------------------ + + /** + * @param {string} path + */ + function isVueFile(path) { + return path.endsWith('.vue') || path.endsWith('.jsx') + } + + /** + * Check whether the given node is a Vue component based + * on the filename and default export type + * export default {} in .vue || .jsx + * @param {ESNode} node Node to check + * @param {string} path File name with extension + * @returns {boolean} + */ + function isVueComponentFile(node, path) { + return ( + isVueFile(path) && + node.type === 'ExportDefaultDeclaration' && + node.declaration.type === 'ObjectExpression' + ) + } + + /** + * Get the Vue component definition type from given node + * Vue.component('xxx', {}) || component('xxx', {}) + * @param {ObjectExpression} node Node to check + * @returns {'component' | 'mixin' | 'extend' | 'createApp' | 'defineComponent' | null} + */ + function getVueComponentDefinitionType(node) { + const parent = getParent(node) + if (parent.type === 'CallExpression') { + const callee = parent.callee + + if (callee.type === 'MemberExpression') { + const calleeObject = skipTSAsExpression(callee.object) + + if (calleeObject.type === 'Identifier') { + const propName = getStaticPropertyName(callee) + if (calleeObject.name === 'Vue') { + // for Vue.js 2.x + // Vue.component('xxx', {}) || Vue.mixin({}) || Vue.extend('xxx', {}) + const maybeFullVueComponentForVue2 = + propName && isObjectArgument(parent) + + return maybeFullVueComponentForVue2 && + (propName === 'component' || + propName === 'mixin' || + propName === 'extend') + ? propName + : null + } + + // for Vue.js 3.x + // app.component('xxx', {}) || app.mixin({}) + const maybeFullVueComponent = propName && isObjectArgument(parent) + + return maybeFullVueComponent && + (propName === 'component' || propName === 'mixin') + ? propName + : null + } + } + + if (callee.type === 'Identifier') { + if (callee.name === 'component') { + // for Vue.js 2.x + // component('xxx', {}) + const isDestructedVueComponent = isObjectArgument(parent) + return isDestructedVueComponent ? 'component' : null + } + if (callee.name === 'createApp') { + // for Vue.js 3.x + // createApp({}) + const isAppVueComponent = isObjectArgument(parent) + return isAppVueComponent ? 'createApp' : null + } + if (callee.name === 'defineComponent') { + // for Vue.js 3.x + // defineComponent({}) + const isDestructedVueComponent = isObjectArgument(parent) + return isDestructedVueComponent ? 'defineComponent' : null + } + } + } + + return null + + /** @param {CallExpression} node */ + function isObjectArgument(node) { + return ( + node.arguments.length > 0 && + skipTSAsExpression(node.arguments.slice(-1)[0]).type === + 'ObjectExpression' + ) + } + } + + /** + * Check whether given node is new Vue instance + * new Vue({}) + * @param {NewExpression} node Node to check + * @returns {boolean} + */ + function isVueInstance(node) { + const callee = node.callee + return Boolean( + node.type === 'NewExpression' && + callee.type === 'Identifier' && + callee.name === 'Vue' && + node.arguments.length && + skipTSAsExpression(node.arguments[0]).type === 'ObjectExpression' + ) + } + + /** + * If the given object is a Vue component or instance, returns the Vue definition type. + * @param {RuleContext} context The ESLint rule context object. + * @param {ObjectExpression} node Node to check + * @returns { VueObjectType | null } The Vue definition type. + */ + function getVueObjectType(context, node) { + if (node.type !== 'ObjectExpression') { + return null + } + const parent = getParent(node) + if (parent.type === 'ExportDefaultDeclaration') { + // export default {} in .vue || .jsx + const filePath = context.getFilename() + if ( + isVueComponentFile(parent, filePath) && + skipTSAsExpression(parent.declaration) === node + ) { + return 'export' + } + } else if (parent.type === 'CallExpression') { + // Vue.component('xxx', {}) || component('xxx', {}) + if ( + getVueComponentDefinitionType(node) != null && + skipTSAsExpression(parent.arguments.slice(-1)[0]) === node + ) { + return 'definition' + } + } else if (parent.type === 'NewExpression') { + // new Vue({}) + if ( + isVueInstance(parent) && + skipTSAsExpression(parent.arguments[0]) === node + ) { + return 'instance' + } + } + if ( + getComponentComments(context).some( + (el) => el.loc.end.line === node.loc.start.line - 1 + ) + ) { + return 'mark' + } + return null + } + + /** + * Gets the component comments of a given context. + * @param {RuleContext} context The ESLint rule context object. + * @return {Token[]} The the component comments. + */ + function getComponentComments(context) { + let tokens = componentComments.get(context) + if (tokens) { + return tokens + } + const sourceCode = context.getSourceCode() + tokens = sourceCode + .getAllComments() + .filter((comment) => /@vue\/component/g.test(comment.value)) + componentComments.set(context, tokens) + return tokens + } + + /** + * Return generator with the all handler nodes defined in the given watcher property. + * @param {Property|Expression} property + * @returns {IterableIterator<Expression>} + */ + function* iterateWatchHandlerValues(property) { + const value = property.type === 'Property' ? property.value : property + if (value.type === 'ObjectExpression') { + const handler = findProperty(value, 'handler') + if (handler) { + yield handler.value + } + } else if (value.type === 'ArrayExpression') { + for (const element of value.elements.filter(isDef)) { + if (element.type !== 'SpreadElement') { + yield* iterateWatchHandlerValues(element) + } + } + } else { + yield value + } + } + \ No newline at end of file diff --git a/src/eslint-plugins/utils/inline-non-void-elements.json b/src/eslint-plugins/utils/inline-non-void-elements.json new file mode 100644 index 00000000..b3dda839 --- /dev/null +++ b/src/eslint-plugins/utils/inline-non-void-elements.json @@ -0,0 +1,40 @@ +[ + "a", + "abbr", + "audio", + "b", + "bdi", + "bdo", + "canvas", + "cite", + "code", + "data", + "del", + "dfn", + "em", + "i", + "iframe", + "ins", + "kbd", + "label", + "map", + "mark", + "noscript", + "object", + "output", + "picture", + "q", + "ruby", + "s", + "samp", + "small", + "span", + "strong", + "sub", + "sup", + "svg", + "time", + "u", + "var", + "video" +] diff --git a/src/eslint-plugins/utils/js-reserved.json b/src/eslint-plugins/utils/js-reserved.json new file mode 100644 index 00000000..9b3eb11c --- /dev/null +++ b/src/eslint-plugins/utils/js-reserved.json @@ -0,0 +1,18 @@ +[ + "abstract", "arguments", "await", "boolean", + "break", "byte", "case", "catch", + "char", "class", "const", "continue", + "debugger", "default", "delete", "do", + "double", "else", "enum", "eval", + "export", "extends", "false", "final", + "finally", "float", "for", "function", + "goto", "if", "implements", "import", + "in", "instanceof", "int", "interface", + "let", "long", "native", "new", + "null", "package", "private", "protected", + "public", "return", "short", "static", + "super", "switch", "synchronized", "this", + "throw", "throws", "transient", "true", + "try", "typeof", "var", "void", + "volatile", "while", "with", "yield" +] diff --git a/src/eslint-plugins/utils/key-aliases.json b/src/eslint-plugins/utils/key-aliases.json new file mode 100644 index 00000000..49de3b3d --- /dev/null +++ b/src/eslint-plugins/utils/key-aliases.json @@ -0,0 +1,68 @@ +[ + "unidentified", "alt", "alt-graph", "caps-lock", "control", "fn", "fn-lock", + "meta", "num-lock", "scroll-lock", "shift", "symbol", "symbol-lock", "hyper", + "super", "enter", "tab", "arrow-down", "arrow-left", "arrow-right", + "arrow-up", "end", "home", "page-down", "page-up", "backspace", "clear", + "copy", "cr-sel", "cut", "delete", "erase-eof", "ex-sel", "insert", "paste", + "redo", "undo", "accept", "again", "attn", "cancel", "context-menu", "escape", + "execute", "find", "help", "pause", "select", "zoom-in", "zoom-out", + "brightness-down", "brightness-up", "eject", "log-off", "power", + "print-screen", "hibernate", "standby", "wake-up", "all-candidates", + "alphanumeric", "code-input", "compose", "convert", "dead", "final-mode", + "group-first", "group-last", "group-next", "group-previous", "mode-change", + "next-candidate", "non-convert", "previous-candidate", "process", + "single-candidate", "hangul-mode", "hanja-mode", "junja-mode", "eisu", + "hankaku", "hiragana", "hiragana-katakana", "kana-mode", "kanji-mode", + "katakana", "romaji", "zenkaku", "zenkaku-hankaku", "f1", "f2", "f3", "f4", + "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "soft1", "soft2", "soft3", + "soft4", "channel-down", "channel-up", "close", "mail-forward", "mail-reply", + "mail-send", "media-close", "media-fast-forward", "media-pause", + "media-play-pause", "media-record", "media-rewind", "media-stop", + "media-track-next", "media-track-previous", "new", "open", "print", "save", + "spell-check", "key11", "key12", "audio-balance-left", "audio-balance-right", + "audio-bass-boost-down", "audio-bass-boost-toggle", "audio-bass-boost-up", + "audio-fader-front", "audio-fader-rear", "audio-surround-mode-next", + "audio-treble-down", "audio-treble-up", "audio-volume-down", + "audio-volume-up", "audio-volume-mute", "microphone-toggle", + "microphone-volume-down", "microphone-volume-up", "microphone-volume-mute", + "speech-correction-list", "speech-input-toggle", "launch-application1", + "launch-application2", "launch-calendar", "launch-contacts", "launch-mail", + "launch-media-player", "launch-music-player", "launch-phone", + "launch-screen-saver", "launch-spreadsheet", "launch-web-browser", + "launch-web-cam", "launch-word-processor", "browser-back", + "browser-favorites", "browser-forward", "browser-home", "browser-refresh", + "browser-search", "browser-stop", "app-switch", "call", "camera", + "camera-focus", "end-call", "go-back", "go-home", "headset-hook", + "last-number-redial", "notification", "manner-mode", "voice-dial", "t-v", + "t-v3-d-mode", "t-v-antenna-cable", "t-v-audio-description", + "t-v-audio-description-mix-down", "t-v-audio-description-mix-up", + "t-v-contents-menu", "t-v-data-service", "t-v-input", "t-v-input-component1", + "t-v-input-component2", "t-v-input-composite1", "t-v-input-composite2", + "t-v-input-h-d-m-i1", "t-v-input-h-d-m-i2", "t-v-input-h-d-m-i3", + "t-v-input-h-d-m-i4", "t-v-input-v-g-a1", "t-v-media-context", "t-v-network", + "t-v-number-entry", "t-v-power", "t-v-radio-service", "t-v-satellite", + "t-v-satellite-b-s", "t-v-satellite-c-s", "t-v-satellite-toggle", + "t-v-terrestrial-analog", "t-v-terrestrial-digital", "t-v-timer", + "a-v-r-input", "a-v-r-power", "color-f0-red", "color-f1-green", + "color-f2-yellow", "color-f3-blue", "color-f4-grey", "color-f5-brown", + "closed-caption-toggle", "dimmer", "display-swap", "d-v-r", "exit", + "favorite-clear0", "favorite-clear1", "favorite-clear2", "favorite-clear3", + "favorite-recall0", "favorite-recall1", "favorite-recall2", + "favorite-recall3", "favorite-store0", "favorite-store1", "favorite-store2", + "favorite-store3", "guide", "guide-next-day", "guide-previous-day", "info", + "instant-replay", "link", "list-program", "live-content", "lock", + "media-apps", "media-last", "media-skip-backward", "media-skip-forward", + "media-step-backward", "media-step-forward", "media-top-menu", "navigate-in", + "navigate-next", "navigate-out", "navigate-previous", "next-favorite-channel", + "next-user-profile", "on-demand", "pairing", "pin-p-down", "pin-p-move", + "pin-p-toggle", "pin-p-up", "play-speed-down", "play-speed-reset", + "play-speed-up", "random-toggle", "rc-low-battery", "record-speed-next", + "rf-bypass", "scan-channels-toggle", "screen-mode-next", "settings", + "split-screen-toggle", "s-t-b-input", "s-t-b-power", "subtitle", "teletext", + "video-mode-next", "wink", "zoom-toggle", "audio-volume-down", + "audio-volume-up", "audio-volume-mute", "browser-back", "browser-forward", + "channel-down", "channel-up", "context-menu", "eject", "end", "enter", "home", + "media-fast-forward", "media-play", "media-play-pause", "media-record", + "media-rewind", "media-stop", "media-next-track", "media-pause", + "media-previous-track", "power", "unidentified" +] diff --git a/src/eslint-plugins/utils/keycode-to-key.ts b/src/eslint-plugins/utils/keycode-to-key.ts new file mode 100644 index 00000000..3defe3bf --- /dev/null +++ b/src/eslint-plugins/utils/keycode-to-key.ts @@ -0,0 +1,98 @@ +// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values +export = { + 8: 'backspace', + 9: 'tab', + 13: 'enter', + 16: 'shift', + 17: 'ctrl', + 18: 'alt', + 19: 'pause', // windows + 20: 'caps-lock', + 27: 'escape', + 32: 'space', // Vue.js specially key name. + 33: 'page-up', + 34: 'page-down', + 35: 'end', + 36: 'home', + 37: 'arrow-left', + 38: 'arrow-up', + 39: 'arrow-right', + 40: 'arrow-down', + 45: 'insert', // windows + 46: 'delete', + + // If mistakenly use it in Vue.js 2.x, it will be irreversibly broken. Therefore, it will not be autofix. + // '48': '0', + // '49': '1', + // '50': '2', + // '51': '3', + // '52': '4', + // '53': '5', + // '54': '6', + // '55': '7', + // '56': '8', + // '57': '9', + + 65: 'a', + 66: 'b', + 67: 'c', + 68: 'd', + 69: 'e', + 70: 'f', + 71: 'g', + 72: 'h', + 73: 'i', + 74: 'j', + 75: 'k', + 76: 'l', + 77: 'm', + 78: 'n', + 79: 'o', + 80: 'p', + 81: 'q', + 82: 'r', + 83: 's', + 84: 't', + 85: 'u', + 86: 'v', + 87: 'w', + 88: 'x', + 89: 'y', + 90: 'z', + + // The key value may change depending on the OS. + // '91': 'meta' ,// Win: 'os'? + // '92': 'meta', // Win: 'meta' Mac: ? + // '93': 'meta', // Win: 'context-menu' Mac: 'meta' + + // Cannot determine numpad with key. + // '96': 'numpad-0', + // '97': 'numpad-1', + // '98': 'numpad-2', + // '99': 'numpad-3', + // '100': 'numpad-4', + // '101': 'numpad-5', + // '102': 'numpad-6', + // '103': 'numpad-7', + // '104': 'numpad-8', + // '105': 'numpad-9', + // '106': 'multiply', + // '107': 'add', + // '109': 'subtract', + // '110': 'decimal', + // '111': 'divide', + 112: 'f1', + 113: 'f2', + 114: 'f3', + 115: 'f4', + 116: 'f5', + 117: 'f6', + 118: 'f7', + 119: 'f8', + 120: 'f9', + 121: 'f10', + 122: 'f11', + 123: 'f12', + 144: 'num-lock', + 145: 'scroll-lock' +} diff --git a/src/eslint-plugins/utils/regexp.ts b/src/eslint-plugins/utils/regexp.ts new file mode 100644 index 00000000..2b71bc2f --- /dev/null +++ b/src/eslint-plugins/utils/regexp.ts @@ -0,0 +1,35 @@ +const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu +const RE_HAS_REGEXP_CHAR = new RegExp(RE_REGEXP_CHAR.source) + +const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u + +/** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + */ +export function escape(string: string) { + return string && RE_HAS_REGEXP_CHAR.test(string) + ? string.replace(RE_REGEXP_CHAR, '\\$&') + : string +} + +/** + * Convert a string to the `RegExp`. + * Normal strings (e.g. `"foo"`) is converted to `/^foo$/` of `RegExp`. + * Strings like `"/^foo/i"` are converted to `/^foo/i` of `RegExp`. + */ +export function toRegExp(string: string) { + const parts = RE_REGEXP_STR.exec(string) + if (parts) { + return new RegExp(parts[1], parts[2]) + } + return new RegExp(`^${escape(string)}$`) +} + +/** + * Checks whether given string is regexp string + */ +export function isRegExp(string: string) { + return Boolean(RE_REGEXP_STR.exec(string)) +} + diff --git a/src/eslint-plugins/utils/svg-elements.json b/src/eslint-plugins/utils/svg-elements.json new file mode 100644 index 00000000..aaee2e6f --- /dev/null +++ b/src/eslint-plugins/utils/svg-elements.json @@ -0,0 +1 @@ +["a","animate","animateMotion","animateTransform","audio","canvas","circle","clipPath","defs","desc","discard","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","filter","foreignObject","g","iframe","image","line","linearGradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialGradient","rect","script","set","stop","style","svg","switch","symbol","text","textPath","title","tspan","unknown","use","video","view"] diff --git a/src/eslint-plugins/utils/void-elements.json b/src/eslint-plugins/utils/void-elements.json new file mode 100644 index 00000000..b737cc68 --- /dev/null +++ b/src/eslint-plugins/utils/void-elements.json @@ -0,0 +1 @@ +["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"] diff --git a/src/eslint-plugins/utils/vue-component-options.json b/src/eslint-plugins/utils/vue-component-options.json new file mode 100644 index 00000000..1dcce9f5 --- /dev/null +++ b/src/eslint-plugins/utils/vue-component-options.json @@ -0,0 +1,54 @@ +{ + "nuxt": ["asyncData", "fetch", "head", "key", "layout", "loading", "middleware", "scrollToTop", "transition", "validate", "watchQuery"], + "vue-router": [ + "beforeRouteEnter", + "beforeRouteUpdate", + "beforeRouteLeave" + ], + "vue": [ + "data", + "props", + "propsData", + "computed", + "methods", + "watch", + "el", + "template", + "render", + "renderError", + "staticRenderFns", + "beforeCreate", + "created", + "beforeDestroy", + "destroyed", + "beforeMount", + "mounted", + "beforeUpdate", + "updated", + "activated", + "deactivated", + "errorCaptured", + "serverPrefetch", + "directives", + "components", + "transitions", + "filters", + "provide", + "inject", + "model", + "parent", + "mixins", + "name", + "extends", + "delimiters", + "comments", + "inheritAttrs", + + "setup", + "emits", + "beforeUnmount", + "unmounted", + "renderTracked", + "renderTriggered" + ] +} diff --git a/src/eslint-plugins/utils/vue-reserved.json b/src/eslint-plugins/utils/vue-reserved.json new file mode 100644 index 00000000..03bcdcba --- /dev/null +++ b/src/eslint-plugins/utils/vue-reserved.json @@ -0,0 +1,4 @@ +[ + "$data", "$props", "$el", "$options", "$parent", "$root", "$children", "$slots", "$scopedSlots", "$refs", "$isServer", "$attrs", "$listeners", + "$watch", "$set", "$delete", "$on", "$once", "$off", "$emit", "$mount", "$forceUpdate", "$nextTick", "$destroy" +] From 0c903df4eb1ac04993b981e08d3002e75c86b617 Mon Sep 17 00:00:00 2001 From: tyankatsu <frips.ryilsufupe+dev@gmail.com> Date: Sat, 8 May 2021 01:15:36 +0900 Subject: [PATCH 3/4] feat: change to ts --- src/eslint-plugins/utils/casing.js | 203 - src/eslint-plugins/utils/casing.ts | 165 + src/eslint-plugins/utils/html-comments.js | 259 -- src/eslint-plugins/utils/html-comments.ts | 276 ++ src/eslint-plugins/utils/indent-common.ts | 3867 +++++++++-------- src/eslint-plugins/utils/index.ts | 237 +- src/eslint-plugins/utils/keycode-to-key.ts | 182 +- src/eslint-plugins/utils/regexp.ts | 19 +- typings/eslint-utils/index.d.ts | 81 + typings/eslint/index.d.ts | 431 ++ typings/eslint/lib/rules/accessor-pairs.d.ts | 3 + .../lib/rules/array-bracket-newline.d.ts | 3 + .../lib/rules/array-bracket-spacing.d.ts | 3 + .../lib/rules/array-callback-return.d.ts | 3 + .../lib/rules/array-element-newline.d.ts | 3 + .../eslint/lib/rules/arrow-body-style.d.ts | 3 + typings/eslint/lib/rules/arrow-parens.d.ts | 3 + typings/eslint/lib/rules/arrow-spacing.d.ts | 3 + .../eslint/lib/rules/block-scoped-var.d.ts | 3 + typings/eslint/lib/rules/block-spacing.d.ts | 3 + typings/eslint/lib/rules/brace-style.d.ts | 3 + typings/eslint/lib/rules/callback-return.d.ts | 3 + typings/eslint/lib/rules/camelcase.d.ts | 3 + .../lib/rules/capitalized-comments.d.ts | 3 + .../lib/rules/class-methods-use-this.d.ts | 3 + typings/eslint/lib/rules/comma-dangle.d.ts | 3 + typings/eslint/lib/rules/comma-spacing.d.ts | 3 + typings/eslint/lib/rules/comma-style.d.ts | 3 + typings/eslint/lib/rules/complexity.d.ts | 3 + .../lib/rules/computed-property-spacing.d.ts | 3 + .../eslint/lib/rules/consistent-return.d.ts | 3 + typings/eslint/lib/rules/consistent-this.d.ts | 3 + .../eslint/lib/rules/constructor-super.d.ts | 3 + typings/eslint/lib/rules/curly.d.ts | 3 + .../eslint/lib/rules/default-case-last.d.ts | 3 + typings/eslint/lib/rules/default-case.d.ts | 3 + .../eslint/lib/rules/default-param-last.d.ts | 3 + typings/eslint/lib/rules/dot-location.d.ts | 3 + typings/eslint/lib/rules/dot-notation.d.ts | 3 + typings/eslint/lib/rules/eol-last.d.ts | 3 + typings/eslint/lib/rules/eqeqeq.d.ts | 3 + typings/eslint/lib/rules/for-direction.d.ts | 3 + .../eslint/lib/rules/func-call-spacing.d.ts | 3 + .../eslint/lib/rules/func-name-matching.d.ts | 3 + typings/eslint/lib/rules/func-names.d.ts | 3 + typings/eslint/lib/rules/func-style.d.ts | 3 + .../rules/function-call-argument-newline.d.ts | 3 + .../lib/rules/function-paren-newline.d.ts | 3 + .../lib/rules/generator-star-spacing.d.ts | 3 + typings/eslint/lib/rules/getter-return.d.ts | 3 + typings/eslint/lib/rules/global-require.d.ts | 3 + .../lib/rules/grouped-accessor-pairs.d.ts | 3 + typings/eslint/lib/rules/guard-for-in.d.ts | 3 + .../eslint/lib/rules/handle-callback-err.d.ts | 3 + typings/eslint/lib/rules/id-blacklist.d.ts | 3 + typings/eslint/lib/rules/id-length.d.ts | 3 + typings/eslint/lib/rules/id-match.d.ts | 3 + .../lib/rules/implicit-arrow-linebreak.d.ts | 3 + typings/eslint/lib/rules/indent-legacy.d.ts | 3 + typings/eslint/lib/rules/indent.d.ts | 3 + .../eslint/lib/rules/init-declarations.d.ts | 3 + typings/eslint/lib/rules/jsx-quotes.d.ts | 3 + typings/eslint/lib/rules/key-spacing.d.ts | 3 + typings/eslint/lib/rules/keyword-spacing.d.ts | 3 + .../lib/rules/line-comment-position.d.ts | 3 + typings/eslint/lib/rules/linebreak-style.d.ts | 3 + .../lib/rules/lines-around-comment.d.ts | 3 + .../lib/rules/lines-around-directive.d.ts | 3 + .../rules/lines-between-class-members.d.ts | 3 + .../lib/rules/max-classes-per-file.d.ts | 3 + typings/eslint/lib/rules/max-depth.d.ts | 3 + typings/eslint/lib/rules/max-len.d.ts | 3 + .../lib/rules/max-lines-per-function.d.ts | 3 + typings/eslint/lib/rules/max-lines.d.ts | 3 + .../lib/rules/max-nested-callbacks.d.ts | 3 + typings/eslint/lib/rules/max-params.d.ts | 3 + .../lib/rules/max-statements-per-line.d.ts | 3 + typings/eslint/lib/rules/max-statements.d.ts | 3 + .../lib/rules/multiline-comment-style.d.ts | 3 + .../eslint/lib/rules/multiline-ternary.d.ts | 3 + typings/eslint/lib/rules/new-cap.d.ts | 3 + typings/eslint/lib/rules/new-parens.d.ts | 3 + .../eslint/lib/rules/newline-after-var.d.ts | 3 + .../lib/rules/newline-before-return.d.ts | 3 + .../lib/rules/newline-per-chained-call.d.ts | 3 + typings/eslint/lib/rules/no-alert.d.ts | 3 + .../lib/rules/no-array-constructor.d.ts | 3 + .../lib/rules/no-async-promise-executor.d.ts | 3 + .../eslint/lib/rules/no-await-in-loop.d.ts | 3 + typings/eslint/lib/rules/no-bitwise.d.ts | 3 + .../lib/rules/no-buffer-constructor.d.ts | 3 + typings/eslint/lib/rules/no-caller.d.ts | 3 + .../lib/rules/no-case-declarations.d.ts | 3 + typings/eslint/lib/rules/no-catch-shadow.d.ts | 3 + typings/eslint/lib/rules/no-class-assign.d.ts | 3 + .../eslint/lib/rules/no-compare-neg-zero.d.ts | 3 + typings/eslint/lib/rules/no-cond-assign.d.ts | 3 + .../eslint/lib/rules/no-confusing-arrow.d.ts | 3 + typings/eslint/lib/rules/no-console.d.ts | 3 + typings/eslint/lib/rules/no-const-assign.d.ts | 3 + .../lib/rules/no-constant-condition.d.ts | 3 + .../lib/rules/no-constructor-return.d.ts | 3 + typings/eslint/lib/rules/no-continue.d.ts | 3 + .../eslint/lib/rules/no-control-regex.d.ts | 3 + typings/eslint/lib/rules/no-debugger.d.ts | 3 + typings/eslint/lib/rules/no-delete-var.d.ts | 3 + typings/eslint/lib/rules/no-div-regex.d.ts | 3 + typings/eslint/lib/rules/no-dupe-args.d.ts | 3 + .../lib/rules/no-dupe-class-members.d.ts | 3 + typings/eslint/lib/rules/no-dupe-else-if.d.ts | 3 + typings/eslint/lib/rules/no-dupe-keys.d.ts | 3 + .../eslint/lib/rules/no-duplicate-case.d.ts | 3 + .../lib/rules/no-duplicate-imports.d.ts | 3 + typings/eslint/lib/rules/no-else-return.d.ts | 3 + .../lib/rules/no-empty-character-class.d.ts | 3 + .../eslint/lib/rules/no-empty-function.d.ts | 3 + .../eslint/lib/rules/no-empty-pattern.d.ts | 3 + typings/eslint/lib/rules/no-empty.d.ts | 3 + typings/eslint/lib/rules/no-eq-null.d.ts | 3 + typings/eslint/lib/rules/no-eval.d.ts | 3 + typings/eslint/lib/rules/no-ex-assign.d.ts | 3 + .../eslint/lib/rules/no-extend-native.d.ts | 3 + typings/eslint/lib/rules/no-extra-bind.d.ts | 3 + .../lib/rules/no-extra-boolean-cast.d.ts | 3 + typings/eslint/lib/rules/no-extra-label.d.ts | 3 + typings/eslint/lib/rules/no-extra-parens.d.ts | 3 + typings/eslint/lib/rules/no-extra-semi.d.ts | 3 + typings/eslint/lib/rules/no-fallthrough.d.ts | 3 + .../eslint/lib/rules/no-floating-decimal.d.ts | 3 + typings/eslint/lib/rules/no-func-assign.d.ts | 3 + .../eslint/lib/rules/no-global-assign.d.ts | 3 + .../lib/rules/no-implicit-coercion.d.ts | 3 + .../eslint/lib/rules/no-implicit-globals.d.ts | 3 + typings/eslint/lib/rules/no-implied-eval.d.ts | 3 + .../eslint/lib/rules/no-import-assign.d.ts | 3 + .../eslint/lib/rules/no-inline-comments.d.ts | 3 + .../lib/rules/no-inner-declarations.d.ts | 3 + .../eslint/lib/rules/no-invalid-regexp.d.ts | 3 + typings/eslint/lib/rules/no-invalid-this.d.ts | 3 + .../lib/rules/no-irregular-whitespace.d.ts | 3 + typings/eslint/lib/rules/no-iterator.d.ts | 3 + typings/eslint/lib/rules/no-label-var.d.ts | 3 + typings/eslint/lib/rules/no-labels.d.ts | 3 + typings/eslint/lib/rules/no-lone-blocks.d.ts | 3 + typings/eslint/lib/rules/no-lonely-if.d.ts | 3 + typings/eslint/lib/rules/no-loop-func.d.ts | 3 + .../lib/rules/no-loss-of-precision.d.ts | 3 + .../eslint/lib/rules/no-magic-numbers.d.ts | 3 + .../rules/no-misleading-character-class.d.ts | 3 + .../eslint/lib/rules/no-mixed-operators.d.ts | 3 + .../eslint/lib/rules/no-mixed-requires.d.ts | 3 + .../lib/rules/no-mixed-spaces-and-tabs.d.ts | 3 + typings/eslint/lib/rules/no-multi-assign.d.ts | 3 + typings/eslint/lib/rules/no-multi-spaces.d.ts | 3 + typings/eslint/lib/rules/no-multi-str.d.ts | 3 + .../lib/rules/no-multiple-empty-lines.d.ts | 3 + .../eslint/lib/rules/no-native-reassign.d.ts | 3 + .../lib/rules/no-negated-condition.d.ts | 3 + .../eslint/lib/rules/no-negated-in-lhs.d.ts | 3 + .../eslint/lib/rules/no-nested-ternary.d.ts | 3 + typings/eslint/lib/rules/no-new-func.d.ts | 3 + typings/eslint/lib/rules/no-new-object.d.ts | 3 + typings/eslint/lib/rules/no-new-require.d.ts | 3 + typings/eslint/lib/rules/no-new-symbol.d.ts | 3 + typings/eslint/lib/rules/no-new-wrappers.d.ts | 3 + typings/eslint/lib/rules/no-new.d.ts | 3 + typings/eslint/lib/rules/no-obj-calls.d.ts | 3 + typings/eslint/lib/rules/no-octal-escape.d.ts | 3 + typings/eslint/lib/rules/no-octal.d.ts | 3 + .../eslint/lib/rules/no-param-reassign.d.ts | 3 + typings/eslint/lib/rules/no-path-concat.d.ts | 3 + typings/eslint/lib/rules/no-plusplus.d.ts | 3 + typings/eslint/lib/rules/no-process-env.d.ts | 3 + typings/eslint/lib/rules/no-process-exit.d.ts | 3 + .../lib/rules/no-promise-executor-return.d.ts | 3 + typings/eslint/lib/rules/no-proto.d.ts | 3 + .../lib/rules/no-prototype-builtins.d.ts | 3 + typings/eslint/lib/rules/no-redeclare.d.ts | 3 + typings/eslint/lib/rules/no-regex-spaces.d.ts | 3 + .../lib/rules/no-restricted-exports.d.ts | 3 + .../lib/rules/no-restricted-globals.d.ts | 3 + .../lib/rules/no-restricted-imports.d.ts | 3 + .../lib/rules/no-restricted-modules.d.ts | 3 + .../lib/rules/no-restricted-properties.d.ts | 3 + .../lib/rules/no-restricted-syntax.d.ts | 3 + .../eslint/lib/rules/no-return-assign.d.ts | 3 + typings/eslint/lib/rules/no-return-await.d.ts | 3 + typings/eslint/lib/rules/no-script-url.d.ts | 3 + typings/eslint/lib/rules/no-self-assign.d.ts | 3 + typings/eslint/lib/rules/no-self-compare.d.ts | 3 + typings/eslint/lib/rules/no-sequences.d.ts | 3 + .../eslint/lib/rules/no-setter-return.d.ts | 3 + .../lib/rules/no-shadow-restricted-names.d.ts | 3 + typings/eslint/lib/rules/no-shadow.d.ts | 3 + typings/eslint/lib/rules/no-spaced-func.d.ts | 3 + .../eslint/lib/rules/no-sparse-arrays.d.ts | 3 + typings/eslint/lib/rules/no-sync.d.ts | 3 + typings/eslint/lib/rules/no-tabs.d.ts | 3 + .../rules/no-template-curly-in-string.d.ts | 3 + typings/eslint/lib/rules/no-ternary.d.ts | 3 + .../lib/rules/no-this-before-super.d.ts | 3 + .../eslint/lib/rules/no-throw-literal.d.ts | 3 + .../eslint/lib/rules/no-trailing-spaces.d.ts | 3 + typings/eslint/lib/rules/no-undef-init.d.ts | 3 + typings/eslint/lib/rules/no-undef.d.ts | 3 + typings/eslint/lib/rules/no-undefined.d.ts | 3 + .../lib/rules/no-underscore-dangle.d.ts | 3 + .../lib/rules/no-unexpected-multiline.d.ts | 3 + .../rules/no-unmodified-loop-condition.d.ts | 3 + .../eslint/lib/rules/no-unneeded-ternary.d.ts | 3 + .../eslint/lib/rules/no-unreachable-loop.d.ts | 3 + typings/eslint/lib/rules/no-unreachable.d.ts | 3 + .../eslint/lib/rules/no-unsafe-finally.d.ts | 3 + .../eslint/lib/rules/no-unsafe-negation.d.ts | 3 + .../lib/rules/no-unused-expressions.d.ts | 3 + .../eslint/lib/rules/no-unused-labels.d.ts | 3 + typings/eslint/lib/rules/no-unused-vars.d.ts | 3 + .../lib/rules/no-use-before-define.d.ts | 3 + .../lib/rules/no-useless-backreference.d.ts | 3 + typings/eslint/lib/rules/no-useless-call.d.ts | 3 + .../eslint/lib/rules/no-useless-catch.d.ts | 3 + .../lib/rules/no-useless-computed-key.d.ts | 3 + .../eslint/lib/rules/no-useless-concat.d.ts | 3 + .../lib/rules/no-useless-constructor.d.ts | 3 + .../eslint/lib/rules/no-useless-escape.d.ts | 3 + .../eslint/lib/rules/no-useless-rename.d.ts | 3 + .../eslint/lib/rules/no-useless-return.d.ts | 3 + typings/eslint/lib/rules/no-var.d.ts | 3 + typings/eslint/lib/rules/no-void.d.ts | 3 + .../eslint/lib/rules/no-warning-comments.d.ts | 3 + .../rules/no-whitespace-before-property.d.ts | 3 + typings/eslint/lib/rules/no-with.d.ts | 3 + .../nonblock-statement-body-position.d.ts | 3 + .../lib/rules/object-curly-newline.d.ts | 3 + .../lib/rules/object-curly-spacing.d.ts | 3 + .../lib/rules/object-property-newline.d.ts | 3 + .../eslint/lib/rules/object-shorthand.d.ts | 3 + .../rules/one-var-declaration-per-line.d.ts | 3 + typings/eslint/lib/rules/one-var.d.ts | 3 + .../eslint/lib/rules/operator-assignment.d.ts | 3 + .../eslint/lib/rules/operator-linebreak.d.ts | 3 + typings/eslint/lib/rules/padded-blocks.d.ts | 3 + .../padding-line-between-statements.d.ts | 3 + .../lib/rules/prefer-arrow-callback.d.ts | 3 + typings/eslint/lib/rules/prefer-const.d.ts | 3 + .../lib/rules/prefer-destructuring.d.ts | 3 + .../rules/prefer-exponentiation-operator.d.ts | 3 + .../lib/rules/prefer-named-capture-group.d.ts | 3 + .../lib/rules/prefer-numeric-literals.d.ts | 3 + .../lib/rules/prefer-object-spread.d.ts | 3 + .../rules/prefer-promise-reject-errors.d.ts | 3 + typings/eslint/lib/rules/prefer-reflect.d.ts | 3 + .../lib/rules/prefer-regex-literals.d.ts | 3 + .../eslint/lib/rules/prefer-rest-params.d.ts | 3 + typings/eslint/lib/rules/prefer-spread.d.ts | 3 + typings/eslint/lib/rules/prefer-template.d.ts | 3 + typings/eslint/lib/rules/quote-props.d.ts | 3 + typings/eslint/lib/rules/quotes.d.ts | 3 + typings/eslint/lib/rules/radix.d.ts | 3 + .../lib/rules/require-atomic-updates.d.ts | 3 + typings/eslint/lib/rules/require-await.d.ts | 3 + typings/eslint/lib/rules/require-jsdoc.d.ts | 3 + .../lib/rules/require-unicode-regexp.d.ts | 3 + typings/eslint/lib/rules/require-yield.d.ts | 3 + .../eslint/lib/rules/rest-spread-spacing.d.ts | 3 + typings/eslint/lib/rules/semi-spacing.d.ts | 3 + typings/eslint/lib/rules/semi-style.d.ts | 3 + typings/eslint/lib/rules/semi.d.ts | 3 + typings/eslint/lib/rules/sort-imports.d.ts | 3 + typings/eslint/lib/rules/sort-keys.d.ts | 3 + typings/eslint/lib/rules/sort-vars.d.ts | 3 + .../eslint/lib/rules/space-before-blocks.d.ts | 3 + .../rules/space-before-function-paren.d.ts | 3 + typings/eslint/lib/rules/space-in-parens.d.ts | 3 + typings/eslint/lib/rules/space-infix-ops.d.ts | 3 + typings/eslint/lib/rules/space-unary-ops.d.ts | 3 + typings/eslint/lib/rules/spaced-comment.d.ts | 3 + typings/eslint/lib/rules/strict.d.ts | 3 + .../lib/rules/switch-colon-spacing.d.ts | 3 + .../eslint/lib/rules/symbol-description.d.ts | 3 + .../lib/rules/template-curly-spacing.d.ts | 3 + .../lib/rules/template-tag-spacing.d.ts | 3 + typings/eslint/lib/rules/unicode-bom.d.ts | 3 + typings/eslint/lib/rules/use-isnan.d.ts | 3 + typings/eslint/lib/rules/valid-jsdoc.d.ts | 3 + typings/eslint/lib/rules/valid-typeof.d.ts | 3 + typings/eslint/lib/rules/vars-on-top.d.ts | 3 + typings/eslint/lib/rules/wrap-iife.d.ts | 3 + typings/eslint/lib/rules/wrap-regex.d.ts | 3 + .../eslint/lib/rules/yield-star-spacing.d.ts | 3 + typings/eslint/lib/rules/yoda.d.ts | 3 + typings/vue-eslint-parser/index.d.ts | 5 + 292 files changed, 4061 insertions(+), 2507 deletions(-) delete mode 100644 src/eslint-plugins/utils/casing.js create mode 100644 src/eslint-plugins/utils/casing.ts delete mode 100644 src/eslint-plugins/utils/html-comments.js create mode 100644 src/eslint-plugins/utils/html-comments.ts create mode 100644 typings/eslint-utils/index.d.ts create mode 100644 typings/eslint/index.d.ts create mode 100644 typings/eslint/lib/rules/accessor-pairs.d.ts create mode 100644 typings/eslint/lib/rules/array-bracket-newline.d.ts create mode 100644 typings/eslint/lib/rules/array-bracket-spacing.d.ts create mode 100644 typings/eslint/lib/rules/array-callback-return.d.ts create mode 100644 typings/eslint/lib/rules/array-element-newline.d.ts create mode 100644 typings/eslint/lib/rules/arrow-body-style.d.ts create mode 100644 typings/eslint/lib/rules/arrow-parens.d.ts create mode 100644 typings/eslint/lib/rules/arrow-spacing.d.ts create mode 100644 typings/eslint/lib/rules/block-scoped-var.d.ts create mode 100644 typings/eslint/lib/rules/block-spacing.d.ts create mode 100644 typings/eslint/lib/rules/brace-style.d.ts create mode 100644 typings/eslint/lib/rules/callback-return.d.ts create mode 100644 typings/eslint/lib/rules/camelcase.d.ts create mode 100644 typings/eslint/lib/rules/capitalized-comments.d.ts create mode 100644 typings/eslint/lib/rules/class-methods-use-this.d.ts create mode 100644 typings/eslint/lib/rules/comma-dangle.d.ts create mode 100644 typings/eslint/lib/rules/comma-spacing.d.ts create mode 100644 typings/eslint/lib/rules/comma-style.d.ts create mode 100644 typings/eslint/lib/rules/complexity.d.ts create mode 100644 typings/eslint/lib/rules/computed-property-spacing.d.ts create mode 100644 typings/eslint/lib/rules/consistent-return.d.ts create mode 100644 typings/eslint/lib/rules/consistent-this.d.ts create mode 100644 typings/eslint/lib/rules/constructor-super.d.ts create mode 100644 typings/eslint/lib/rules/curly.d.ts create mode 100644 typings/eslint/lib/rules/default-case-last.d.ts create mode 100644 typings/eslint/lib/rules/default-case.d.ts create mode 100644 typings/eslint/lib/rules/default-param-last.d.ts create mode 100644 typings/eslint/lib/rules/dot-location.d.ts create mode 100644 typings/eslint/lib/rules/dot-notation.d.ts create mode 100644 typings/eslint/lib/rules/eol-last.d.ts create mode 100644 typings/eslint/lib/rules/eqeqeq.d.ts create mode 100644 typings/eslint/lib/rules/for-direction.d.ts create mode 100644 typings/eslint/lib/rules/func-call-spacing.d.ts create mode 100644 typings/eslint/lib/rules/func-name-matching.d.ts create mode 100644 typings/eslint/lib/rules/func-names.d.ts create mode 100644 typings/eslint/lib/rules/func-style.d.ts create mode 100644 typings/eslint/lib/rules/function-call-argument-newline.d.ts create mode 100644 typings/eslint/lib/rules/function-paren-newline.d.ts create mode 100644 typings/eslint/lib/rules/generator-star-spacing.d.ts create mode 100644 typings/eslint/lib/rules/getter-return.d.ts create mode 100644 typings/eslint/lib/rules/global-require.d.ts create mode 100644 typings/eslint/lib/rules/grouped-accessor-pairs.d.ts create mode 100644 typings/eslint/lib/rules/guard-for-in.d.ts create mode 100644 typings/eslint/lib/rules/handle-callback-err.d.ts create mode 100644 typings/eslint/lib/rules/id-blacklist.d.ts create mode 100644 typings/eslint/lib/rules/id-length.d.ts create mode 100644 typings/eslint/lib/rules/id-match.d.ts create mode 100644 typings/eslint/lib/rules/implicit-arrow-linebreak.d.ts create mode 100644 typings/eslint/lib/rules/indent-legacy.d.ts create mode 100644 typings/eslint/lib/rules/indent.d.ts create mode 100644 typings/eslint/lib/rules/init-declarations.d.ts create mode 100644 typings/eslint/lib/rules/jsx-quotes.d.ts create mode 100644 typings/eslint/lib/rules/key-spacing.d.ts create mode 100644 typings/eslint/lib/rules/keyword-spacing.d.ts create mode 100644 typings/eslint/lib/rules/line-comment-position.d.ts create mode 100644 typings/eslint/lib/rules/linebreak-style.d.ts create mode 100644 typings/eslint/lib/rules/lines-around-comment.d.ts create mode 100644 typings/eslint/lib/rules/lines-around-directive.d.ts create mode 100644 typings/eslint/lib/rules/lines-between-class-members.d.ts create mode 100644 typings/eslint/lib/rules/max-classes-per-file.d.ts create mode 100644 typings/eslint/lib/rules/max-depth.d.ts create mode 100644 typings/eslint/lib/rules/max-len.d.ts create mode 100644 typings/eslint/lib/rules/max-lines-per-function.d.ts create mode 100644 typings/eslint/lib/rules/max-lines.d.ts create mode 100644 typings/eslint/lib/rules/max-nested-callbacks.d.ts create mode 100644 typings/eslint/lib/rules/max-params.d.ts create mode 100644 typings/eslint/lib/rules/max-statements-per-line.d.ts create mode 100644 typings/eslint/lib/rules/max-statements.d.ts create mode 100644 typings/eslint/lib/rules/multiline-comment-style.d.ts create mode 100644 typings/eslint/lib/rules/multiline-ternary.d.ts create mode 100644 typings/eslint/lib/rules/new-cap.d.ts create mode 100644 typings/eslint/lib/rules/new-parens.d.ts create mode 100644 typings/eslint/lib/rules/newline-after-var.d.ts create mode 100644 typings/eslint/lib/rules/newline-before-return.d.ts create mode 100644 typings/eslint/lib/rules/newline-per-chained-call.d.ts create mode 100644 typings/eslint/lib/rules/no-alert.d.ts create mode 100644 typings/eslint/lib/rules/no-array-constructor.d.ts create mode 100644 typings/eslint/lib/rules/no-async-promise-executor.d.ts create mode 100644 typings/eslint/lib/rules/no-await-in-loop.d.ts create mode 100644 typings/eslint/lib/rules/no-bitwise.d.ts create mode 100644 typings/eslint/lib/rules/no-buffer-constructor.d.ts create mode 100644 typings/eslint/lib/rules/no-caller.d.ts create mode 100644 typings/eslint/lib/rules/no-case-declarations.d.ts create mode 100644 typings/eslint/lib/rules/no-catch-shadow.d.ts create mode 100644 typings/eslint/lib/rules/no-class-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-compare-neg-zero.d.ts create mode 100644 typings/eslint/lib/rules/no-cond-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-confusing-arrow.d.ts create mode 100644 typings/eslint/lib/rules/no-console.d.ts create mode 100644 typings/eslint/lib/rules/no-const-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-constant-condition.d.ts create mode 100644 typings/eslint/lib/rules/no-constructor-return.d.ts create mode 100644 typings/eslint/lib/rules/no-continue.d.ts create mode 100644 typings/eslint/lib/rules/no-control-regex.d.ts create mode 100644 typings/eslint/lib/rules/no-debugger.d.ts create mode 100644 typings/eslint/lib/rules/no-delete-var.d.ts create mode 100644 typings/eslint/lib/rules/no-div-regex.d.ts create mode 100644 typings/eslint/lib/rules/no-dupe-args.d.ts create mode 100644 typings/eslint/lib/rules/no-dupe-class-members.d.ts create mode 100644 typings/eslint/lib/rules/no-dupe-else-if.d.ts create mode 100644 typings/eslint/lib/rules/no-dupe-keys.d.ts create mode 100644 typings/eslint/lib/rules/no-duplicate-case.d.ts create mode 100644 typings/eslint/lib/rules/no-duplicate-imports.d.ts create mode 100644 typings/eslint/lib/rules/no-else-return.d.ts create mode 100644 typings/eslint/lib/rules/no-empty-character-class.d.ts create mode 100644 typings/eslint/lib/rules/no-empty-function.d.ts create mode 100644 typings/eslint/lib/rules/no-empty-pattern.d.ts create mode 100644 typings/eslint/lib/rules/no-empty.d.ts create mode 100644 typings/eslint/lib/rules/no-eq-null.d.ts create mode 100644 typings/eslint/lib/rules/no-eval.d.ts create mode 100644 typings/eslint/lib/rules/no-ex-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-extend-native.d.ts create mode 100644 typings/eslint/lib/rules/no-extra-bind.d.ts create mode 100644 typings/eslint/lib/rules/no-extra-boolean-cast.d.ts create mode 100644 typings/eslint/lib/rules/no-extra-label.d.ts create mode 100644 typings/eslint/lib/rules/no-extra-parens.d.ts create mode 100644 typings/eslint/lib/rules/no-extra-semi.d.ts create mode 100644 typings/eslint/lib/rules/no-fallthrough.d.ts create mode 100644 typings/eslint/lib/rules/no-floating-decimal.d.ts create mode 100644 typings/eslint/lib/rules/no-func-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-global-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-implicit-coercion.d.ts create mode 100644 typings/eslint/lib/rules/no-implicit-globals.d.ts create mode 100644 typings/eslint/lib/rules/no-implied-eval.d.ts create mode 100644 typings/eslint/lib/rules/no-import-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-inline-comments.d.ts create mode 100644 typings/eslint/lib/rules/no-inner-declarations.d.ts create mode 100644 typings/eslint/lib/rules/no-invalid-regexp.d.ts create mode 100644 typings/eslint/lib/rules/no-invalid-this.d.ts create mode 100644 typings/eslint/lib/rules/no-irregular-whitespace.d.ts create mode 100644 typings/eslint/lib/rules/no-iterator.d.ts create mode 100644 typings/eslint/lib/rules/no-label-var.d.ts create mode 100644 typings/eslint/lib/rules/no-labels.d.ts create mode 100644 typings/eslint/lib/rules/no-lone-blocks.d.ts create mode 100644 typings/eslint/lib/rules/no-lonely-if.d.ts create mode 100644 typings/eslint/lib/rules/no-loop-func.d.ts create mode 100644 typings/eslint/lib/rules/no-loss-of-precision.d.ts create mode 100644 typings/eslint/lib/rules/no-magic-numbers.d.ts create mode 100644 typings/eslint/lib/rules/no-misleading-character-class.d.ts create mode 100644 typings/eslint/lib/rules/no-mixed-operators.d.ts create mode 100644 typings/eslint/lib/rules/no-mixed-requires.d.ts create mode 100644 typings/eslint/lib/rules/no-mixed-spaces-and-tabs.d.ts create mode 100644 typings/eslint/lib/rules/no-multi-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-multi-spaces.d.ts create mode 100644 typings/eslint/lib/rules/no-multi-str.d.ts create mode 100644 typings/eslint/lib/rules/no-multiple-empty-lines.d.ts create mode 100644 typings/eslint/lib/rules/no-native-reassign.d.ts create mode 100644 typings/eslint/lib/rules/no-negated-condition.d.ts create mode 100644 typings/eslint/lib/rules/no-negated-in-lhs.d.ts create mode 100644 typings/eslint/lib/rules/no-nested-ternary.d.ts create mode 100644 typings/eslint/lib/rules/no-new-func.d.ts create mode 100644 typings/eslint/lib/rules/no-new-object.d.ts create mode 100644 typings/eslint/lib/rules/no-new-require.d.ts create mode 100644 typings/eslint/lib/rules/no-new-symbol.d.ts create mode 100644 typings/eslint/lib/rules/no-new-wrappers.d.ts create mode 100644 typings/eslint/lib/rules/no-new.d.ts create mode 100644 typings/eslint/lib/rules/no-obj-calls.d.ts create mode 100644 typings/eslint/lib/rules/no-octal-escape.d.ts create mode 100644 typings/eslint/lib/rules/no-octal.d.ts create mode 100644 typings/eslint/lib/rules/no-param-reassign.d.ts create mode 100644 typings/eslint/lib/rules/no-path-concat.d.ts create mode 100644 typings/eslint/lib/rules/no-plusplus.d.ts create mode 100644 typings/eslint/lib/rules/no-process-env.d.ts create mode 100644 typings/eslint/lib/rules/no-process-exit.d.ts create mode 100644 typings/eslint/lib/rules/no-promise-executor-return.d.ts create mode 100644 typings/eslint/lib/rules/no-proto.d.ts create mode 100644 typings/eslint/lib/rules/no-prototype-builtins.d.ts create mode 100644 typings/eslint/lib/rules/no-redeclare.d.ts create mode 100644 typings/eslint/lib/rules/no-regex-spaces.d.ts create mode 100644 typings/eslint/lib/rules/no-restricted-exports.d.ts create mode 100644 typings/eslint/lib/rules/no-restricted-globals.d.ts create mode 100644 typings/eslint/lib/rules/no-restricted-imports.d.ts create mode 100644 typings/eslint/lib/rules/no-restricted-modules.d.ts create mode 100644 typings/eslint/lib/rules/no-restricted-properties.d.ts create mode 100644 typings/eslint/lib/rules/no-restricted-syntax.d.ts create mode 100644 typings/eslint/lib/rules/no-return-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-return-await.d.ts create mode 100644 typings/eslint/lib/rules/no-script-url.d.ts create mode 100644 typings/eslint/lib/rules/no-self-assign.d.ts create mode 100644 typings/eslint/lib/rules/no-self-compare.d.ts create mode 100644 typings/eslint/lib/rules/no-sequences.d.ts create mode 100644 typings/eslint/lib/rules/no-setter-return.d.ts create mode 100644 typings/eslint/lib/rules/no-shadow-restricted-names.d.ts create mode 100644 typings/eslint/lib/rules/no-shadow.d.ts create mode 100644 typings/eslint/lib/rules/no-spaced-func.d.ts create mode 100644 typings/eslint/lib/rules/no-sparse-arrays.d.ts create mode 100644 typings/eslint/lib/rules/no-sync.d.ts create mode 100644 typings/eslint/lib/rules/no-tabs.d.ts create mode 100644 typings/eslint/lib/rules/no-template-curly-in-string.d.ts create mode 100644 typings/eslint/lib/rules/no-ternary.d.ts create mode 100644 typings/eslint/lib/rules/no-this-before-super.d.ts create mode 100644 typings/eslint/lib/rules/no-throw-literal.d.ts create mode 100644 typings/eslint/lib/rules/no-trailing-spaces.d.ts create mode 100644 typings/eslint/lib/rules/no-undef-init.d.ts create mode 100644 typings/eslint/lib/rules/no-undef.d.ts create mode 100644 typings/eslint/lib/rules/no-undefined.d.ts create mode 100644 typings/eslint/lib/rules/no-underscore-dangle.d.ts create mode 100644 typings/eslint/lib/rules/no-unexpected-multiline.d.ts create mode 100644 typings/eslint/lib/rules/no-unmodified-loop-condition.d.ts create mode 100644 typings/eslint/lib/rules/no-unneeded-ternary.d.ts create mode 100644 typings/eslint/lib/rules/no-unreachable-loop.d.ts create mode 100644 typings/eslint/lib/rules/no-unreachable.d.ts create mode 100644 typings/eslint/lib/rules/no-unsafe-finally.d.ts create mode 100644 typings/eslint/lib/rules/no-unsafe-negation.d.ts create mode 100644 typings/eslint/lib/rules/no-unused-expressions.d.ts create mode 100644 typings/eslint/lib/rules/no-unused-labels.d.ts create mode 100644 typings/eslint/lib/rules/no-unused-vars.d.ts create mode 100644 typings/eslint/lib/rules/no-use-before-define.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-backreference.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-call.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-catch.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-computed-key.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-concat.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-constructor.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-escape.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-rename.d.ts create mode 100644 typings/eslint/lib/rules/no-useless-return.d.ts create mode 100644 typings/eslint/lib/rules/no-var.d.ts create mode 100644 typings/eslint/lib/rules/no-void.d.ts create mode 100644 typings/eslint/lib/rules/no-warning-comments.d.ts create mode 100644 typings/eslint/lib/rules/no-whitespace-before-property.d.ts create mode 100644 typings/eslint/lib/rules/no-with.d.ts create mode 100644 typings/eslint/lib/rules/nonblock-statement-body-position.d.ts create mode 100644 typings/eslint/lib/rules/object-curly-newline.d.ts create mode 100644 typings/eslint/lib/rules/object-curly-spacing.d.ts create mode 100644 typings/eslint/lib/rules/object-property-newline.d.ts create mode 100644 typings/eslint/lib/rules/object-shorthand.d.ts create mode 100644 typings/eslint/lib/rules/one-var-declaration-per-line.d.ts create mode 100644 typings/eslint/lib/rules/one-var.d.ts create mode 100644 typings/eslint/lib/rules/operator-assignment.d.ts create mode 100644 typings/eslint/lib/rules/operator-linebreak.d.ts create mode 100644 typings/eslint/lib/rules/padded-blocks.d.ts create mode 100644 typings/eslint/lib/rules/padding-line-between-statements.d.ts create mode 100644 typings/eslint/lib/rules/prefer-arrow-callback.d.ts create mode 100644 typings/eslint/lib/rules/prefer-const.d.ts create mode 100644 typings/eslint/lib/rules/prefer-destructuring.d.ts create mode 100644 typings/eslint/lib/rules/prefer-exponentiation-operator.d.ts create mode 100644 typings/eslint/lib/rules/prefer-named-capture-group.d.ts create mode 100644 typings/eslint/lib/rules/prefer-numeric-literals.d.ts create mode 100644 typings/eslint/lib/rules/prefer-object-spread.d.ts create mode 100644 typings/eslint/lib/rules/prefer-promise-reject-errors.d.ts create mode 100644 typings/eslint/lib/rules/prefer-reflect.d.ts create mode 100644 typings/eslint/lib/rules/prefer-regex-literals.d.ts create mode 100644 typings/eslint/lib/rules/prefer-rest-params.d.ts create mode 100644 typings/eslint/lib/rules/prefer-spread.d.ts create mode 100644 typings/eslint/lib/rules/prefer-template.d.ts create mode 100644 typings/eslint/lib/rules/quote-props.d.ts create mode 100644 typings/eslint/lib/rules/quotes.d.ts create mode 100644 typings/eslint/lib/rules/radix.d.ts create mode 100644 typings/eslint/lib/rules/require-atomic-updates.d.ts create mode 100644 typings/eslint/lib/rules/require-await.d.ts create mode 100644 typings/eslint/lib/rules/require-jsdoc.d.ts create mode 100644 typings/eslint/lib/rules/require-unicode-regexp.d.ts create mode 100644 typings/eslint/lib/rules/require-yield.d.ts create mode 100644 typings/eslint/lib/rules/rest-spread-spacing.d.ts create mode 100644 typings/eslint/lib/rules/semi-spacing.d.ts create mode 100644 typings/eslint/lib/rules/semi-style.d.ts create mode 100644 typings/eslint/lib/rules/semi.d.ts create mode 100644 typings/eslint/lib/rules/sort-imports.d.ts create mode 100644 typings/eslint/lib/rules/sort-keys.d.ts create mode 100644 typings/eslint/lib/rules/sort-vars.d.ts create mode 100644 typings/eslint/lib/rules/space-before-blocks.d.ts create mode 100644 typings/eslint/lib/rules/space-before-function-paren.d.ts create mode 100644 typings/eslint/lib/rules/space-in-parens.d.ts create mode 100644 typings/eslint/lib/rules/space-infix-ops.d.ts create mode 100644 typings/eslint/lib/rules/space-unary-ops.d.ts create mode 100644 typings/eslint/lib/rules/spaced-comment.d.ts create mode 100644 typings/eslint/lib/rules/strict.d.ts create mode 100644 typings/eslint/lib/rules/switch-colon-spacing.d.ts create mode 100644 typings/eslint/lib/rules/symbol-description.d.ts create mode 100644 typings/eslint/lib/rules/template-curly-spacing.d.ts create mode 100644 typings/eslint/lib/rules/template-tag-spacing.d.ts create mode 100644 typings/eslint/lib/rules/unicode-bom.d.ts create mode 100644 typings/eslint/lib/rules/use-isnan.d.ts create mode 100644 typings/eslint/lib/rules/valid-jsdoc.d.ts create mode 100644 typings/eslint/lib/rules/valid-typeof.d.ts create mode 100644 typings/eslint/lib/rules/vars-on-top.d.ts create mode 100644 typings/eslint/lib/rules/wrap-iife.d.ts create mode 100644 typings/eslint/lib/rules/wrap-regex.d.ts create mode 100644 typings/eslint/lib/rules/yield-star-spacing.d.ts create mode 100644 typings/eslint/lib/rules/yoda.d.ts create mode 100644 typings/vue-eslint-parser/index.d.ts diff --git a/src/eslint-plugins/utils/casing.js b/src/eslint-plugins/utils/casing.js deleted file mode 100644 index b2f2e89b..00000000 --- a/src/eslint-plugins/utils/casing.js +++ /dev/null @@ -1,203 +0,0 @@ -// ------------------------------------------------------------------------------ -// Helpers -// ------------------------------------------------------------------------------ - -/** - * Capitalize a string. - * @param {string} str - */ -function capitalize(str) { - return str.charAt(0).toUpperCase() + str.slice(1) -} -/** - * Checks whether the given string has symbols. - * @param {string} str - */ -function hasSymbols(str) { - return /[!"#%&'()*+,./:;<=>?@[\\\]^`{|}]/u.exec(str) // without " ", "$", "-" and "_" -} -/** - * Checks whether the given string has upper. - * @param {string} str - */ -function hasUpper(str) { - return /[A-Z]/u.exec(str) -} - -/** - * Convert text to kebab-case - * @param {string} str Text to be converted - * @return {string} - */ -function kebabCase(str) { - return str - .replace(/_/gu, '-') - .replace(/\B([A-Z])/gu, '-$1') - .toLowerCase() -} - -/** - * Checks whether the given string is kebab-case. - * @param {string} str - */ -function isKebabCase(str) { - if ( - hasUpper(str) || - hasSymbols(str) || - /^-/u.exec(str) || // starts with hyphen is not kebab-case - /_|--|\s/u.exec(str) - ) { - return false - } - return true -} - -/** - * Convert text to snake_case - * @param {string} str Text to be converted - * @return {string} - */ -function snakeCase(str) { - return str - .replace(/\B([A-Z])/gu, '_$1') - .replace(/-/gu, '_') - .toLowerCase() -} - -/** - * Checks whether the given string is snake_case. - * @param {string} str - */ -function isSnakeCase(str) { - if (hasUpper(str) || hasSymbols(str) || /-|__|\s/u.exec(str)) { - return false - } - return true -} - -/** - * Convert text to camelCase - * @param {string} str Text to be converted - * @return {string} Converted string - */ -function camelCase(str) { - if (isPascalCase(str)) { - return str.charAt(0).toLowerCase() + str.slice(1) - } - return str.replace(/[-_](\w)/gu, (_, c) => (c ? c.toUpperCase() : '')) -} - -/** - * Checks whether the given string is camelCase. - * @param {string} str - */ -function isCamelCase(str) { - if ( - hasSymbols(str) || - /^[A-Z]/u.exec(str) || - /-|_|\s/u.exec(str) // kebab or snake or space - ) { - return false - } - return true -} - -/** - * Convert text to PascalCase - * @param {string} str Text to be converted - * @return {string} Converted string - */ -function pascalCase(str) { - return capitalize(camelCase(str)) -} - -/** - * Checks whether the given string is PascalCase. - * @param {string} str - */ -function isPascalCase(str) { - if ( - hasSymbols(str) || - /^[a-z]/u.exec(str) || - /-|_|\s/u.exec(str) // kebab or snake or space - ) { - return false - } - return true -} - -const convertersMap = { - 'kebab-case': kebabCase, - snake_case: snakeCase, - camelCase, - PascalCase: pascalCase -} - -const checkersMap = { - 'kebab-case': isKebabCase, - snake_case: isSnakeCase, - camelCase: isCamelCase, - PascalCase: isPascalCase -} -/** - * Return case checker - * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of checker to return ('camelCase', 'kebab-case', 'PascalCase') - * @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase} - */ -function getChecker(name) { - return checkersMap[name] || isPascalCase -} - -/** - * Return case converter - * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') - * @return {kebabCase|camelCase|pascalCase|snakeCase} - */ -function getConverter(name) { - return convertersMap[name] || pascalCase -} - -module.exports = { - allowedCaseOptions: ['camelCase', 'kebab-case', 'PascalCase'], - - /** - * Return case converter - * @param {string} name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') - * @return {kebabCase|camelCase|pascalCase|snakeCase} - */ - getConverter, - - /** - * Return case checker - * @param {string} name type of checker to return ('camelCase', 'kebab-case', 'PascalCase') - * @return {isKebabCase|isCamelCase|isPascalCase|isSnakeCase} - */ - getChecker, - - /** - * Return case exact converter. - * If the converted result is not the correct case, the original value is returned. - * @param { 'camelCase' | 'kebab-case' | 'PascalCase' | 'snake_case' } name type of converter to return ('camelCase', 'kebab-case', 'PascalCase') - * @return {kebabCase|camelCase|pascalCase|snakeCase} - */ - getExactConverter(name) { - const converter = getConverter(name) - const checker = getChecker(name) - return (str) => { - const result = converter(str) - return checker(result) ? result : str /* cannot convert */ - } - }, - - camelCase, - pascalCase, - kebabCase, - snakeCase, - - isCamelCase, - isPascalCase, - isKebabCase, - isSnakeCase, - - capitalize -} diff --git a/src/eslint-plugins/utils/casing.ts b/src/eslint-plugins/utils/casing.ts new file mode 100644 index 00000000..bd30fe7a --- /dev/null +++ b/src/eslint-plugins/utils/casing.ts @@ -0,0 +1,165 @@ +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +/** + * Capitalize a string. + */ +export function capitalize(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1) +} + +/** + * Checks whether the given string has symbols. + */ +function hasSymbols(str: string) { + return /[!"#%&'()*+,./:;<=>?@[\\\]^`{|}]/u.exec(str) // without " ", "$", "-" and "_" +} + +/** + * Checks whether the given string has upper. + */ +function hasUpper(str: string) { + return /[A-Z]/u.exec(str) +} + +/** + * Convert text to kebab-case + * @param str Text to be converted + */ +export function kebabCase(str: string) { + return str + .replace(/_/gu, "-") + .replace(/\B([A-Z])/gu, "-$1") + .toLowerCase() +} + +/** + * Checks whether the given string is kebab-case. + */ +export function isKebabCase(str: string) { + if ( + hasUpper(str) || + hasSymbols(str) || + /^-/u.exec(str) || // starts with hyphen is not kebab-case + /_|--|\s/u.exec(str) + ) { + return false + } + return true +} + +/** + * Convert text to snake_case + * @param str Text to be converted + */ +export function snakeCase(str: string) { + return str + .replace(/\B([A-Z])/gu, "_$1") + .replace(/-/gu, "_") + .toLowerCase() +} + +/** + * Checks whether the given string is snake_case. + */ +export function isSnakeCase(str: string) { + if (hasUpper(str) || hasSymbols(str) || /-|__|\s/u.exec(str)) { + return false + } + return true +} + +/** + * Convert text to camelCase + * @param str Text to be converted + * @return Converted string + */ +export function camelCase(str: string) { + if (isPascalCase(str)) { + return str.charAt(0).toLowerCase() + str.slice(1) + } + return str.replace(/[-_](\w)/gu, (_, c) => (c ? c.toUpperCase() : "")) +} + +/** + * Checks whether the given string is camelCase. + */ +export function isCamelCase(str: string) { + if ( + hasSymbols(str) || + /^[A-Z]/u.exec(str) || + /-|_|\s/u.exec(str) // kebab or snake or space + ) { + return false + } + return true +} + +/** + * Convert text to PascalCase + * @param str Text to be converted + * @return Converted string + */ +export function pascalCase(str: string) { + return capitalize(camelCase(str)) +} + +/** + * Checks whether the given string is PascalCase. + */ +export function isPascalCase(str: string) { + if ( + hasSymbols(str) || + /^[a-z]/u.exec(str) || + /-|_|\s/u.exec(str) // kebab or snake or space + ) { + return false + } + return true +} + +const convertersMap = { + "kebab-case": kebabCase, + // eslint-disable-next-line @mysticatea/ts/camelcase + snake_case: snakeCase, + camelCase, + PascalCase: pascalCase, +} + +const checkersMap = { + "kebab-case": isKebabCase, + // eslint-disable-next-line @mysticatea/ts/camelcase + snake_case: isSnakeCase, + camelCase: isCamelCase, + PascalCase: isPascalCase, +} + +/** + * Return case checker + */ +function getChecker(name: keyof typeof checkersMap) { + return checkersMap[name] || isPascalCase +} + +/** + * Return case converter + */ +function getConverter(name: keyof typeof convertersMap) { + return convertersMap[name] || pascalCase +} + +export const allowedCaseOptions = ["camelCase", "kebab-case", "PascalCase"] + +/** + * Return case exact converter. + * If the converted result is not the correct case, the original value is returned. + */ +export function getExactConverter(name: keyof typeof convertersMap) { + const converter = getConverter(name) + const checker = getChecker(name) + return (str: string) => { + const result = converter(str) + return checker(result) ? result : str /* cannot convert */ + } +} diff --git a/src/eslint-plugins/utils/html-comments.js b/src/eslint-plugins/utils/html-comments.js deleted file mode 100644 index aced46e7..00000000 --- a/src/eslint-plugins/utils/html-comments.js +++ /dev/null @@ -1,259 +0,0 @@ -/** - * @typedef { { exceptions?: string[] } } CommentParserConfig - * @typedef { (comment: ParsedHTMLComment) => void } HTMLCommentVisitor - * @typedef { { includeDirectives?: boolean } } CommentVisitorOption - * - * @typedef { Token & { type: 'HTMLCommentOpen' } } HTMLCommentOpen - * @typedef { Token & { type: 'HTMLCommentOpenDecoration' } } HTMLCommentOpenDecoration - * @typedef { Token & { type: 'HTMLCommentValue' } } HTMLCommentValue - * @typedef { Token & { type: 'HTMLCommentClose' } } HTMLCommentClose - * @typedef { Token & { type: 'HTMLCommentCloseDecoration' } } HTMLCommentCloseDecoration - * @typedef { { open: HTMLCommentOpen, openDecoration: HTMLCommentOpenDecoration | null, value: HTMLCommentValue | null, closeDecoration: HTMLCommentCloseDecoration | null, close: HTMLCommentClose } } ParsedHTMLComment - */ -// ----------------------------------------------------------------------------- -// Requirements -// ----------------------------------------------------------------------------- - -const utils = require('./') - -// ------------------------------------------------------------------------------ -// Helpers -// ------------------------------------------------------------------------------ - -const COMMENT_DIRECTIVE = /^\s*eslint-(?:en|dis)able/ -const IE_CONDITIONAL_IF = /^\[if\s+/ -const IE_CONDITIONAL_ENDIF = /\[endif\]$/ - -/** @type { 'HTMLCommentOpen' } */ -const TYPE_HTML_COMMENT_OPEN = 'HTMLCommentOpen' -/** @type { 'HTMLCommentOpenDecoration' } */ -const TYPE_HTML_COMMENT_OPEN_DECORATION = 'HTMLCommentOpenDecoration' -/** @type { 'HTMLCommentValue' } */ -const TYPE_HTML_COMMENT_VALUE = 'HTMLCommentValue' -/** @type { 'HTMLCommentClose' } */ -const TYPE_HTML_COMMENT_CLOSE = 'HTMLCommentClose' -/** @type { 'HTMLCommentCloseDecoration' } */ -const TYPE_HTML_COMMENT_CLOSE_DECORATION = 'HTMLCommentCloseDecoration' - -/** - * @param {HTMLComment} comment - * @returns {boolean} - */ -function isCommentDirective(comment) { - return COMMENT_DIRECTIVE.test(comment.value) -} - -/** - * @param {HTMLComment} comment - * @returns {boolean} - */ -function isIEConditionalComment(comment) { - return ( - IE_CONDITIONAL_IF.test(comment.value) || - IE_CONDITIONAL_ENDIF.test(comment.value) - ) -} - -/** - * Define HTML comment parser - * - * @param {SourceCode} sourceCode The source code instance. - * @param {CommentParserConfig | null} config The config. - * @returns { (node: Token) => (ParsedHTMLComment | null) } HTML comment parser. - */ -function defineParser(sourceCode, config) { - config = config || {} - - const exceptions = config.exceptions || [] - - /** - * Get a open decoration string from comment contents. - * @param {string} contents comment contents - * @returns {string} decoration string - */ - function getOpenDecoration(contents) { - let decoration = '' - for (const exception of exceptions) { - const length = exception.length - let index = 0 - while (contents.startsWith(exception, index)) { - index += length - } - const exceptionLength = index - if (decoration.length < exceptionLength) { - decoration = contents.slice(0, exceptionLength) - } - } - return decoration - } - - /** - * Get a close decoration string from comment contents. - * @param {string} contents comment contents - * @returns {string} decoration string - */ - function getCloseDecoration(contents) { - let decoration = '' - for (const exception of exceptions) { - const length = exception.length - let index = contents.length - while (contents.endsWith(exception, index)) { - index -= length - } - const exceptionLength = contents.length - index - if (decoration.length < exceptionLength) { - decoration = contents.slice(index) - } - } - return decoration - } - - /** - * Parse HTMLComment. - * @param {ASTToken} node a comment token - * @returns {HTMLComment | null} the result of HTMLComment tokens. - */ - return function parseHTMLComment(node) { - if (node.type !== 'HTMLComment') { - // Is not HTMLComment - return null - } - - const htmlCommentText = sourceCode.getText(node) - - if ( - !htmlCommentText.startsWith('<!--') || - !htmlCommentText.endsWith('-->') - ) { - // Is not normal HTML Comment - // e.g. Error Code: "abrupt-closing-of-empty-comment", "incorrectly-closed-comment" - return null - } - - let valueText = htmlCommentText.slice(4, -3) - const openDecorationText = getOpenDecoration(valueText) - valueText = valueText.slice(openDecorationText.length) - const firstCharIndex = valueText.search(/\S/) - const beforeSpace = - firstCharIndex >= 0 ? valueText.slice(0, firstCharIndex) : valueText - valueText = valueText.slice(beforeSpace.length) - - const closeDecorationText = getCloseDecoration(valueText) - if (closeDecorationText) { - valueText = valueText.slice(0, -closeDecorationText.length) - } - const lastCharIndex = valueText.search(/\S\s*$/) - const afterSpace = - lastCharIndex >= 0 ? valueText.slice(lastCharIndex + 1) : valueText - if (afterSpace) { - valueText = valueText.slice(0, -afterSpace.length) - } - - let tokenIndex = node.range[0] - /** - * @param {string} type - * @param {string} value - * @returns {any} - */ - const createToken = (type, value) => { - /** @type {Range} */ - const range = [tokenIndex, tokenIndex + value.length] - tokenIndex = range[1] - /** @type {SourceLocation} */ - let loc - return { - type, - value, - range, - get loc() { - if (loc) { - return loc - } - return (loc = { - start: sourceCode.getLocFromIndex(range[0]), - end: sourceCode.getLocFromIndex(range[1]) - }) - } - } - } - - /** @type {HTMLCommentOpen} */ - const open = createToken(TYPE_HTML_COMMENT_OPEN, '<!--') - /** @type {HTMLCommentOpenDecoration | null} */ - const openDecoration = openDecorationText - ? createToken(TYPE_HTML_COMMENT_OPEN_DECORATION, openDecorationText) - : null - tokenIndex += beforeSpace.length - /** @type {HTMLCommentValue | null} */ - const value = valueText - ? createToken(TYPE_HTML_COMMENT_VALUE, valueText) - : null - tokenIndex += afterSpace.length - /** @type {HTMLCommentCloseDecoration | null} */ - const closeDecoration = closeDecorationText - ? createToken(TYPE_HTML_COMMENT_CLOSE_DECORATION, closeDecorationText) - : null - /** @type {HTMLCommentClose} */ - const close = createToken(TYPE_HTML_COMMENT_CLOSE, '-->') - - return { - /** HTML comment open (`<!--`) */ - open, - /** decoration of the start of HTML comments. (`*****` when `<!--*****`) */ - openDecoration, - /** value of HTML comment. whitespaces and other tokens are not included. */ - value, - /** decoration of the end of HTML comments. (`*****` when `*****-->`) */ - closeDecoration, - /** HTML comment close (`-->`) */ - close - } - } -} - -/** - * Define HTML comment visitor - * - * @param {RuleContext} context The rule context. - * @param {CommentParserConfig | null} config The config. - * @param {HTMLCommentVisitor} visitHTMLComment The HTML comment visitor. - * @param {CommentVisitorOption} [visitorOption] The option for visitor. - * @returns {RuleListener} HTML comment visitor. - */ -function defineVisitor(context, config, visitHTMLComment, visitorOption) { - return { - Program(node) { - visitorOption = visitorOption || {} - if (utils.hasInvalidEOF(node)) { - return - } - if (!node.templateBody) { - return - } - const parse = defineParser(context.getSourceCode(), config) - - for (const comment of node.templateBody.comments) { - if (comment.type !== 'HTMLComment') { - continue - } - if (!visitorOption.includeDirectives && isCommentDirective(comment)) { - // ignore directives - continue - } - if (isIEConditionalComment(comment)) { - // ignore IE conditional - continue - } - - const tokens = parse(comment) - if (tokens) { - visitHTMLComment(tokens) - } - } - } - } -} - -module.exports = { - defineVisitor -} diff --git a/src/eslint-plugins/utils/html-comments.ts b/src/eslint-plugins/utils/html-comments.ts new file mode 100644 index 00000000..7d6cf25d --- /dev/null +++ b/src/eslint-plugins/utils/html-comments.ts @@ -0,0 +1,276 @@ +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- +import * as utils from "./" + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +type CommentParserConfig = { + exceptions?: string[] +} + +type HTMLCommentVisitor = (comment: ParsedHTMLComment) => void + +type CommentVisitorOption = { + includeDirectives?: boolean +} + +type HTMLCommentOpen = Token & { type: "HTMLCommentOpen" } +type HTMLCommentOpenDecoration = Token & { type: "HTMLCommentOpenDecoration" } +type HTMLCommentValue = Token & { type: "HTMLCommentValue" } +type HTMLCommentClose = Token & { type: "HTMLCommentClose" } +type HTMLCommentCloseDecoration = Token & { type: "HTMLCommentCloseDecoration" } +type ParsedHTMLComment = { + open: HTMLCommentOpen + openDecoration: HTMLCommentOpenDecoration | null + value: HTMLCommentValue | null + closeDecoration: HTMLCommentCloseDecoration | null + close: HTMLCommentClose +} + +// eslint-disable-next-line require-unicode-regexp +const COMMENT_DIRECTIVE = /^\s*eslint-(?:en|dis)able/ +// eslint-disable-next-line require-unicode-regexp +const IE_CONDITIONAL_IF = /^\[if\s+/ +// eslint-disable-next-line require-unicode-regexp +const IE_CONDITIONAL_ENDIF = /\[endif\]$/ + +const TYPE_HTML_COMMENT_OPEN: "HTMLCommentOpen" = "HTMLCommentOpen" +const TYPE_HTML_COMMENT_OPEN_DECORATION: "HTMLCommentOpenDecoration" = + "HTMLCommentOpenDecoration" +const TYPE_HTML_COMMENT_VALUE: "HTMLCommentValue" = "HTMLCommentValue" +const TYPE_HTML_COMMENT_CLOSE: "HTMLCommentClose" = "HTMLCommentClose" +const TYPE_HTML_COMMENT_CLOSE_DECORATION: "HTMLCommentCloseDecoration" = + "HTMLCommentCloseDecoration" + +function isCommentDirective(comment: HTMLComment) { + return COMMENT_DIRECTIVE.test(comment.value) +} + +function isIEConditionalComment(comment: HTMLComment) { + return ( + IE_CONDITIONAL_IF.test(comment.value) || + // eslint-disable-next-line @mysticatea/ts/prefer-string-starts-ends-with + IE_CONDITIONAL_ENDIF.test(comment.value) + ) +} + +/** + * Define HTML comment parser + * + * @param sourceCode The source code instance. + * @param config The config. + * @returns HTML comment parser. + */ +function defineParser( + sourceCode: SourceCode, + config: CommentParserConfig | null, +): (node: Token) => ParsedHTMLComment | null { + // eslint-disable-next-line no-param-reassign + config = config || {} + + const exceptions = config.exceptions || [] + + /** + * Get a open decoration string from comment contents. + * @param contents comment contents + * @returns decoration string + */ + function getOpenDecoration(contents: string) { + let decoration = "" + for (const exception of exceptions) { + const length = exception.length + let index = 0 + while (contents.startsWith(exception, index)) { + index += length + } + const exceptionLength = index + if (decoration.length < exceptionLength) { + decoration = contents.slice(0, exceptionLength) + } + } + return decoration + } + + /** + * Get a close decoration string from comment contents. + * @param contents comment contents + * @returns decoration string + */ + function getCloseDecoration(contents: string) { + let decoration = "" + for (const exception of exceptions) { + const length = exception.length + let index = contents.length + while (contents.endsWith(exception, index)) { + index -= length + } + const exceptionLength = contents.length - index + if (decoration.length < exceptionLength) { + decoration = contents.slice(index) + } + } + return decoration + } + + /** + * Parse HTMLComment. + * @param {ASTToken} node a comment token + * @returns {HTMLComment | null} the result of HTMLComment tokens. + */ + return function parseHTMLComment(node: Token): ParsedHTMLComment | null { + if (node.type !== "HTMLComment") { + // Is not HTMLComment + return null + } + + const htmlCommentText = sourceCode.getText(node) + + if ( + !htmlCommentText.startsWith("<!--") || + !htmlCommentText.endsWith("-->") + ) { + // Is not normal HTML Comment + // e.g. Error Code: "abrupt-closing-of-empty-comment", "incorrectly-closed-comment" + return null + } + + let valueText = htmlCommentText.slice(4, -3) + const openDecorationText = getOpenDecoration(valueText) + valueText = valueText.slice(openDecorationText.length) + // eslint-disable-next-line require-unicode-regexp + const firstCharIndex = valueText.search(/\S/) + const beforeSpace = + firstCharIndex >= 0 ? valueText.slice(0, firstCharIndex) : valueText + valueText = valueText.slice(beforeSpace.length) + + const closeDecorationText = getCloseDecoration(valueText) + if (closeDecorationText) { + valueText = valueText.slice(0, -closeDecorationText.length) + } + // eslint-disable-next-line require-unicode-regexp + const lastCharIndex = valueText.search(/\S\s*$/) + const afterSpace = + lastCharIndex >= 0 ? valueText.slice(lastCharIndex + 1) : valueText + if (afterSpace) { + valueText = valueText.slice(0, -afterSpace.length) + } + + let tokenIndex = node.range[0] + /** + * @param type + * @param value + * @returns {any} + */ + const createToken = (type: string, value: string): any => { + const range: Range = [tokenIndex, tokenIndex + value.length] + tokenIndex = range[1] + + let loc: SourceLocation + return { + type, + value, + range, + get loc() { + if (loc) { + return loc + } + return (loc = { + start: sourceCode.getLocFromIndex(range[0]), + end: sourceCode.getLocFromIndex(range[1]), + }) + }, + } + } + + const open: HTMLCommentOpen = createToken( + TYPE_HTML_COMMENT_OPEN, + "<!--", + ) + const openDecoration: HTMLCommentOpenDecoration | null = openDecorationText + ? createToken(TYPE_HTML_COMMENT_OPEN_DECORATION, openDecorationText) + : null + tokenIndex += beforeSpace.length + const value: HTMLCommentValue | null = valueText + ? createToken(TYPE_HTML_COMMENT_VALUE, valueText) + : null + tokenIndex += afterSpace.length + const closeDecoration: HTMLCommentCloseDecoration | null = closeDecorationText + ? createToken( + TYPE_HTML_COMMENT_CLOSE_DECORATION, + closeDecorationText, + ) + : null + const close: HTMLCommentClose = createToken( + TYPE_HTML_COMMENT_CLOSE, + "-->", + ) + + return { + /** HTML comment open (`<!--`) */ + open, + /** decoration of the start of HTML comments. (`*****` when `<!--*****`) */ + openDecoration, + /** value of HTML comment. whitespaces and other tokens are not included. */ + value, + /** decoration of the end of HTML comments. (`*****` when `*****-->`) */ + closeDecoration, + /** HTML comment close (`-->`) */ + close, + } + } +} + +/** + * Define HTML comment visitor + * + * @param context The rule context. + * @param config The config. + * @param visitHTMLComment The HTML comment visitor. + * @param [visitorOption] The option for visitor. + * @returns HTML comment visitor. + */ +export function defineVisitor( + context: RuleContext, + config: CommentParserConfig | null, + visitHTMLComment: HTMLCommentVisitor, + visitorOption: CommentVisitorOption, +): RuleListener { + return { + Program(node) { + // eslint-disable-next-line no-param-reassign + visitorOption = visitorOption || {} + if (utils.hasInvalidEOF(node)) { + return + } + if (!node.templateBody) { + return + } + const parse = defineParser(context.getSourceCode(), config) + + for (const comment of node.templateBody.comments) { + if (comment.type !== "HTMLComment") { + continue + } + if ( + !visitorOption.includeDirectives && + isCommentDirective(comment) + ) { + // ignore directives + continue + } + if (isIEConditionalComment(comment)) { + // ignore IE conditional + continue + } + + const tokens = parse(comment) + if (tokens) { + visitHTMLComment(tokens) + } + } + }, + } +} diff --git a/src/eslint-plugins/utils/indent-common.ts b/src/eslint-plugins/utils/indent-common.ts index 8ef9fc61..e9a0b158 100644 --- a/src/eslint-plugins/utils/indent-common.ts +++ b/src/eslint-plugins/utils/indent-common.ts @@ -2,7 +2,6 @@ * @author Toru Nagashima <https://github.com/mysticatea> * See LICENSE file in root directory for full license. */ -'use strict' // ------------------------------------------------------------------------------ // Requirements @@ -12,2074 +11,2178 @@ // Helpers // ------------------------------------------------------------------------------ - -const KNOWN_NODES = new Set([ - 'ArrayExpression', - 'ArrayPattern', - 'ArrowFunctionExpression', - 'AssignmentExpression', - 'AssignmentPattern', - 'AwaitExpression', - 'BinaryExpression', - 'BlockStatement', - 'BreakStatement', - 'CallExpression', - 'CatchClause', - 'ChainExpression', - 'ClassBody', - 'ClassDeclaration', - 'ClassExpression', - 'ConditionalExpression', - 'ContinueStatement', - 'DebuggerStatement', - 'DoWhileStatement', - 'EmptyStatement', - 'ExportAllDeclaration', - 'ExportDefaultDeclaration', - 'ExportNamedDeclaration', - 'ExportSpecifier', - 'ExpressionStatement', - 'ForInStatement', - 'ForOfStatement', - 'ForStatement', - 'FunctionDeclaration', - 'FunctionExpression', - 'Identifier', - 'IfStatement', - 'ImportDeclaration', - 'ImportDefaultSpecifier', - 'ImportExpression', - 'ImportNamespaceSpecifier', - 'ImportSpecifier', - 'LabeledStatement', - 'Literal', - 'LogicalExpression', - 'MemberExpression', - 'MetaProperty', - 'MethodDefinition', - 'NewExpression', - 'ObjectExpression', - 'ObjectPattern', - 'Program', - 'Property', - 'RestElement', - 'ReturnStatement', - 'SequenceExpression', - 'SpreadElement', - 'Super', - 'SwitchCase', - 'SwitchStatement', - 'TaggedTemplateExpression', - 'TemplateElement', - 'TemplateLiteral', - 'ThisExpression', - 'ThrowStatement', - 'TryStatement', - 'UnaryExpression', - 'UpdateExpression', - 'VariableDeclaration', - 'VariableDeclarator', - 'WhileStatement', - 'WithStatement', - 'YieldExpression', - 'VAttribute', - 'VDirectiveKey', - 'VDocumentFragment', - 'VElement', - 'VEndTag', - 'VExpressionContainer', - 'VFilter', - 'VFilterSequenceExpression', - 'VForExpression', - 'VIdentifier', - 'VLiteral', - 'VOnExpression', - 'VSlotScopeExpression', - 'VStartTag', - 'VText' +const KNOWN_NODES = new Set<ASTNode["type"]>([ + "ArrayExpression", + "ArrayPattern", + "ArrowFunctionExpression", + "AssignmentExpression", + "AssignmentPattern", + "AwaitExpression", + "BinaryExpression", + "BlockStatement", + "BreakStatement", + "CallExpression", + "CatchClause", + "ChainExpression", + "ClassBody", + "ClassDeclaration", + "ClassExpression", + "ConditionalExpression", + "ContinueStatement", + "DebuggerStatement", + "DoWhileStatement", + "EmptyStatement", + "ExportAllDeclaration", + "ExportDefaultDeclaration", + "ExportNamedDeclaration", + "ExportSpecifier", + "ExpressionStatement", + "ForInStatement", + "ForOfStatement", + "ForStatement", + "FunctionDeclaration", + "FunctionExpression", + "Identifier", + "IfStatement", + "ImportDeclaration", + "ImportDefaultSpecifier", + "ImportExpression", + "ImportNamespaceSpecifier", + "ImportSpecifier", + "LabeledStatement", + "Literal", + "LogicalExpression", + "MemberExpression", + "MetaProperty", + "MethodDefinition", + "NewExpression", + "ObjectExpression", + "ObjectPattern", + "Program", + "Property", + "RestElement", + "ReturnStatement", + "SequenceExpression", + "SpreadElement", + "Super", + "SwitchCase", + "SwitchStatement", + "TaggedTemplateExpression", + "TemplateElement", + "TemplateLiteral", + "ThisExpression", + "ThrowStatement", + "TryStatement", + "UnaryExpression", + "UpdateExpression", + "VariableDeclaration", + "VariableDeclarator", + "WhileStatement", + "WithStatement", + "YieldExpression", + "VAttribute", + "VDirectiveKey", + "VDocumentFragment", + "VElement", + "VEndTag", + "VExpressionContainer", + "VFilter", + "VFilterSequenceExpression", + "VForExpression", + "VIdentifier", + "VLiteral", + "VOnExpression", + "VSlotScopeExpression", + "VStartTag", + "VText", ]) const NON_STANDARD_KNOWN_NODES = new Set([ - 'ExperimentalRestProperty', - 'ExperimentalSpreadProperty' + "ExperimentalRestProperty", + "ExperimentalSpreadProperty", ]) +// eslint-disable-next-line require-unicode-regexp const LT_CHAR = /[\r\n\u2028\u2029]/ +// eslint-disable-next-line require-unicode-regexp const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g +// eslint-disable-next-line require-unicode-regexp const BLOCK_COMMENT_PREFIX = /^\s*\*/ const ITERATION_OPTS = Object.freeze({ - includeComments: true, - filter: isNotWhitespace + includeComments: true, + filter: isNotWhitespace, }) -const PREFORMATTED_ELEMENT_NAMES = ['pre', 'textarea'] - -/** - * @typedef {object} IndentOptions - * @property { " " | "\t" } IndentOptions.indentChar - * @property {number} IndentOptions.indentSize - * @property {number} IndentOptions.baseIndent - * @property {number} IndentOptions.attribute - * @property {object} IndentOptions.closeBracket - * @property {number} IndentOptions.closeBracket.startTag - * @property {number} IndentOptions.closeBracket.endTag - * @property {number} IndentOptions.closeBracket.selfClosingTag - * @property {number} IndentOptions.switchCase - * @property {boolean} IndentOptions.alignAttributesVertically - * @property {string[]} IndentOptions.ignores - */ -/** - * @typedef {object} IndentUserOptions - * @property { " " | "\t" } [IndentUserOptions.indentChar] - * @property {number} [IndentUserOptions.indentSize] - * @property {number} [IndentUserOptions.baseIndent] - * @property {number} [IndentUserOptions.attribute] - * @property {IndentOptions['closeBracket'] | number} [IndentUserOptions.closeBracket] - * @property {number} [IndentUserOptions.switchCase] - * @property {boolean} [IndentUserOptions.alignAttributesVertically] - * @property {string[]} [IndentUserOptions.ignores] - */ -/** - * Normalize options. - * @param {number|"tab"|undefined} type The type of indentation. - * @param {IndentUserOptions} options Other options. - * @param {Partial<IndentOptions>} defaultOptions The default value of options. - * @returns {IndentOptions} Normalized options. - */ -function parseOptions(type, options, defaultOptions) { - /** @type {IndentOptions} */ - const ret = Object.assign( - { - indentChar: ' ', - indentSize: 2, - baseIndent: 0, - attribute: 1, - closeBracket: { - startTag: 0, - endTag: 0, - selfClosingTag: 0 - }, - switchCase: 0, - alignAttributesVertically: true, - ignores: [] - }, - defaultOptions - ) - - if (Number.isSafeInteger(type)) { - ret.indentSize = Number(type) - } else if (type === 'tab') { - ret.indentChar = '\t' - ret.indentSize = 1 - } - - if (Number.isSafeInteger(options.baseIndent)) { - ret.baseIndent = options.baseIndent - } - if (Number.isSafeInteger(options.attribute)) { - ret.attribute = options.attribute - } - if (Number.isSafeInteger(options.closeBracket)) { - const num = Number(options.closeBracket) - ret.closeBracket = { - startTag: num, - endTag: num, - selfClosingTag: num +const PREFORMATTED_ELEMENT_NAMES = ["pre", "textarea"] + +type IndentOptions = { + indentChar: " " | "\t" + indentSize: number + baseIndent: number + attribute: number + closeBracket: { + startTag: number + endTag: number + selfClosingTag: number } - } else if (options.closeBracket) { - ret.closeBracket = Object.assign( - { - startTag: 0, - endTag: 0, - selfClosingTag: 0 - }, - options.closeBracket + switchCase: number + alignAttributesVertically: boolean + ignores: string[] +} + +type IndentUserOptions = { + indentChar: " " | "\t" + indentSize: number + baseIndent: number + attribute: number + closeBracket: IndentOptions["closeBracket"] | number + switchCase: number + alignAttributesVertically: boolean + ignores: string[] +} + +function parseOptions( + type: number | "tab" | undefined, + options: IndentUserOptions, + defaultOptions: Partial<IndentOptions>, +) { + const ret: IndentOptions = Object.assign( + { + indentChar: " ", + indentSize: 2, + baseIndent: 0, + attribute: 1, + closeBracket: { + startTag: 0, + endTag: 0, + selfClosingTag: 0, + }, + switchCase: 0, + alignAttributesVertically: true, + ignores: [], + }, + defaultOptions, ) - } - if (Number.isSafeInteger(options.switchCase)) { - ret.switchCase = options.switchCase - } - - if (options.alignAttributesVertically != null) { - ret.alignAttributesVertically = options.alignAttributesVertically - } - if (options.ignores != null) { - ret.ignores = options.ignores - } - - return ret + + if (Number.isSafeInteger(type)) { + ret.indentSize = Number(type) + } else if (type === "tab") { + ret.indentChar = "\t" + ret.indentSize = 1 + } + + if (Number.isSafeInteger(options.baseIndent)) { + ret.baseIndent = options.baseIndent + } + if (Number.isSafeInteger(options.attribute)) { + ret.attribute = options.attribute + } + if (Number.isSafeInteger(options.closeBracket)) { + const num = Number(options.closeBracket) + ret.closeBracket = { + startTag: num, + endTag: num, + selfClosingTag: num, + } + } else if (options.closeBracket) { + ret.closeBracket = Object.assign( + { + startTag: 0, + endTag: 0, + selfClosingTag: 0, + }, + options.closeBracket, + ) + } + if (Number.isSafeInteger(options.switchCase)) { + ret.switchCase = options.switchCase + } + + if (options.alignAttributesVertically != null) { + ret.alignAttributesVertically = options.alignAttributesVertically + } + if (options.ignores != null) { + ret.ignores = options.ignores + } + + return ret } /** * Check whether the given token is an arrow. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is an arrow. */ -function isArrow(token) { - return token != null && token.type === 'Punctuator' && token.value === '=>' +function isArrow(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "=>" } /** * Check whether the given token is a left parenthesis. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a left parenthesis. */ -function isLeftParen(token) { - return token != null && token.type === 'Punctuator' && token.value === '(' +function isLeftParen(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "(" } /** * Check whether the given token is a left parenthesis. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `false` if the token is a left parenthesis. */ -function isNotLeftParen(token) { - return token != null && (token.type !== 'Punctuator' || token.value !== '(') +function isNotLeftParen(token: Token | undefined | null) { + return token != null && (token.type !== "Punctuator" || token.value !== "(") } /** * Check whether the given token is a right parenthesis. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a right parenthesis. */ -function isRightParen(token) { - return token != null && token.type === 'Punctuator' && token.value === ')' +function isRightParen(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === ")" } /** * Check whether the given token is a right parenthesis. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `false` if the token is a right parenthesis. */ -function isNotRightParen(token) { - return token != null && (token.type !== 'Punctuator' || token.value !== ')') +function isNotRightParen(token: Token | undefined | null) { + return token != null && (token.type !== "Punctuator" || token.value !== ")") } /** * Check whether the given token is a left brace. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a left brace. */ -function isLeftBrace(token) { - return token != null && token.type === 'Punctuator' && token.value === '{' +function isLeftBrace(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "{" } /** * Check whether the given token is a right brace. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a right brace. */ -function isRightBrace(token) { - return token != null && token.type === 'Punctuator' && token.value === '}' +function isRightBrace(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "}" } /** * Check whether the given token is a left bracket. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a left bracket. */ -function isLeftBracket(token) { - return token != null && token.type === 'Punctuator' && token.value === '[' +function isLeftBracket(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "[" } /** * Check whether the given token is a right bracket. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a right bracket. */ -function isRightBracket(token) { - return token != null && token.type === 'Punctuator' && token.value === ']' +function isRightBracket(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "]" } /** * Check whether the given token is a semicolon. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a semicolon. */ -function isSemicolon(token) { - return token != null && token.type === 'Punctuator' && token.value === ';' +function isSemicolon(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === ";" } /** * Check whether the given token is a comma. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a comma. */ -function isComma(token) { - return token != null && token.type === 'Punctuator' && token.value === ',' +function isComma(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "," } + /** * Check whether the given token is a wildcard. - * @param {Token} token The token to check. - * @returns {boolean} `true` if the token is a wildcard. */ -function isWildcard(token) { - return token != null && token.type === 'Punctuator' && token.value === '*' +function isWildcard(token: Token) { + return token != null && token.type === "Punctuator" && token.value === "*" } /** * Check whether the given token is a whitespace. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a whitespace. */ -function isNotWhitespace(token) { - return token != null && token.type !== 'HTMLWhitespace' +function isNotWhitespace(token: Token | undefined | null) { + return token != null && token.type !== "HTMLWhitespace" } /** * Check whether the given token is a comment. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a comment. */ -function isComment(token) { - return ( - token != null && - (token.type === 'Block' || - token.type === 'Line' || - token.type === 'Shebang' || - (typeof token.type === - 'string' /* Although acorn supports new tokens, espree may not yet support new tokens.*/ && - token.type.endsWith('Comment'))) - ) +function isComment(token: Token | undefined | null) { + return ( + token != null && + (token.type === "Block" || + token.type === "Line" || + token.type === "Shebang" || + (typeof token.type === + "string" /* Although acorn supports new tokens, espree may not yet support new tokens. */ && + token.type.endsWith("Comment"))) + ) } /** * Check whether the given token is a comment. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `false` if the token is a comment. */ -function isNotComment(token) { - return ( - token != null && - token.type !== 'Block' && - token.type !== 'Line' && - token.type !== 'Shebang' && - !( - typeof token.type === - 'string' /* Although acorn supports new tokens, espree may not yet support new tokens.*/ && - token.type.endsWith('Comment') +function isNotComment(token: Token | undefined | null) { + return ( + token != null && + token.type !== "Block" && + token.type !== "Line" && + token.type !== "Shebang" && + !( + typeof token.type === + "string" /* Although acorn supports new tokens, espree may not yet support new tokens. */ && + token.type.endsWith("Comment") + ) ) - ) } /** * Check whether the given node is not an empty text node. - * @param {ASTNode} node The node to check. - * @returns {boolean} `false` if the token is empty text node. */ -function isNotEmptyTextNode(node) { - return !(node.type === 'VText' && node.value.trim() === '') +function isNotEmptyTextNode(node: ASTNode) { + return !(node.type === "VText" && node.value.trim() === "") } /** * Check whether the given token is a pipe operator. - * @param {Token|undefined|null} token The token to check. - * @returns {boolean} `true` if the token is a pipe operator. */ -function isPipeOperator(token) { - return token != null && token.type === 'Punctuator' && token.value === '|' +function isPipeOperator(token: Token | undefined | null) { + return token != null && token.type === "Punctuator" && token.value === "|" } /** * Get the last element. - * @template T - * @param {T[]} xs The array to get the last element. - * @returns {T | undefined} The last element or undefined. */ -function last(xs) { - return xs.length === 0 ? undefined : xs[xs.length - 1] +function last<T>(xs: T[]) { + return xs.length === 0 ? undefined : xs[xs.length - 1] } /** * Check whether the node is at the beginning of line. - * @param {ASTNode|null} node The node to check. - * @param {number} index The index of the node in the nodes. - * @param {(ASTNode|null)[]} nodes The array of nodes. - * @returns {boolean} `true` if the node is at the beginning of line. */ -function isBeginningOfLine(node, index, nodes) { - if (node != null) { - for (let i = index - 1; i >= 0; --i) { - const prevNode = nodes[i] - if (prevNode == null) { - continue - } - - return node.loc.start.line !== prevNode.loc.end.line +function isBeginningOfLine( + node: ASTNode | null, + index: number, + nodes: (ASTNode | null)[], +) { + if (node != null) { + for (let i = index - 1; i >= 0; --i) { + const prevNode = nodes[i] + if (prevNode == null) { + continue + } + + return node.loc.start.line !== prevNode.loc.end.line + } } - } - return false + return false } /** * Check whether a given token is a closing token which triggers unindent. - * @param {Token} token The token to check. - * @returns {boolean} `true` if the token is a closing token. */ -function isClosingToken(token) { - return ( - token != null && - (token.type === 'HTMLEndTagOpen' || - token.type === 'VExpressionEnd' || - (token.type === 'Punctuator' && - (token.value === ')' || token.value === '}' || token.value === ']'))) - ) +function isClosingToken(token: Token) { + return ( + token != null && + (token.type === "HTMLEndTagOpen" || + token.type === "VExpressionEnd" || + (token.type === "Punctuator" && + (token.value === ")" || + token.value === "}" || + token.value === "]"))) + ) } /** * Creates AST event handlers for html-indent. * - * @param {RuleContext} context The rule context. - * @param {ParserServices.TokenStore | SourceCode} tokenStore The token store object to get tokens. - * @param {Partial<IndentOptions>} defaultOptions The default value of options. + * @param context The rule context. + * @param tokenStore The token store object to get tokens. + * @param defaultOptions The default value of options. * @returns {NodeListener} AST event handlers. */ module.exports.defineVisitor = function create( - context, - tokenStore, - defaultOptions -) { - if (!context.getFilename().endsWith('.vue')) return {} - - const options = parseOptions( - context.options[0], - context.options[1] || {}, - defaultOptions - ) - const sourceCode = context.getSourceCode() - const offsets = new Map() - const ignoreTokens = new Set() - - /** - * Set offset to the given tokens. - * @param {Token|Token[]|null|(Token|null)[]} token The token to set. - * @param {number} offset The offset of the tokens. - * @param {Token} baseToken The token of the base offset. - * @returns {void} - */ - function setOffset(token, offset, baseToken) { - if (!token) { - return - } - if (Array.isArray(token)) { - for (const t of token) { - offsets.set(t, { - baseToken, - offset, - baseline: false, - expectedIndent: undefined - }) - } - } else { - offsets.set(token, { - baseToken, - offset, - baseline: false, - expectedIndent: undefined - }) - } - } - - /** - * Set baseline flag to the given token. - * @param {Token} token The token to set. - * @returns {void} - */ - function setBaseline(token) { - const offsetInfo = offsets.get(token) - if (offsetInfo != null) { - offsetInfo.baseline = true - } - } - - /** - * Sets preformatted tokens to the given element node. - * @param {VElement} node The node to set. - * @returns {void} - */ - function setPreformattedTokens(node) { - const endToken = - (node.endTag && tokenStore.getFirstToken(node.endTag)) || - tokenStore.getTokenAfter(node) - - /** @type {SourceCode.CursorWithSkipOptions} */ - const option = { - includeComments: true, - filter: (token) => - token != null && - (token.type === 'HTMLText' || - token.type === 'HTMLRCDataText' || - token.type === 'HTMLTagOpen' || - token.type === 'HTMLEndTagOpen' || - token.type === 'HTMLComment') - } - for (const token of tokenStore.getTokensBetween( - node.startTag, - endToken, - option - )) { - ignoreTokens.add(token) - } - ignoreTokens.add(endToken) - } - - /** - * Get the first and last tokens of the given node. - * If the node is parenthesized, this gets the outermost parentheses. - * @param {ASTNode} node The node to get. - * @param {number} [borderOffset] The least offset of the first token. Defailt is 0. This value is used to prevent false positive in the following case: `(a) => {}` The parentheses are enclosing the whole parameter part rather than the first parameter, but this offset parameter is needed to distinguish. - * @returns {{firstToken:Token,lastToken:Token}} The gotten tokens. - */ - function getFirstAndLastTokens(node, borderOffset = 0) { - borderOffset |= 0 - - let firstToken = tokenStore.getFirstToken(node) - let lastToken = tokenStore.getLastToken(node) - - // Get the outermost left parenthesis if it's parenthesized. - let t, u - while ( - (t = tokenStore.getTokenBefore(firstToken)) != null && - (u = tokenStore.getTokenAfter(lastToken)) != null && - isLeftParen(t) && - isRightParen(u) && - t.range[0] >= borderOffset - ) { - firstToken = t - lastToken = u + context: RuleContext, + tokenStore: ParserServices.TokenStore | SourceCode, + defaultOptions: Partial<IndentOptions>, +): NodeListener { + if (!context.getFilename().endsWith(".vue")) { + return {} } - return { firstToken, lastToken } - } - - /** - * Process the given node list. - * The first node is offsetted from the given left token. - * Rest nodes are adjusted to the first node. - * @param {(ASTNode|null)[]} nodeList The node to process. - * @param {ASTNode|Token|null} left The left parenthesis token. - * @param {ASTNode|Token|null} right The right parenthesis token. - * @param {number} offset The offset to set. - * @param {boolean} [alignVertically=true] The flag to align vertically. If `false`, this doesn't align vertically even if the first node is not at beginning of line. - * @returns {void} - */ - function processNodeList(nodeList, left, right, offset, alignVertically) { - let t - const leftToken = left && tokenStore.getFirstToken(left) - const rightToken = right && tokenStore.getFirstToken(right) - - if (nodeList.length >= 1) { - let baseToken = null - let lastToken = left - const alignTokensBeforeBaseToken = [] - const alignTokens = [] - - for (let i = 0; i < nodeList.length; ++i) { - const node = nodeList[i] - if (node == null) { - // Holes of an array. - continue + const options = parseOptions( + context.options[0], + context.options[1] || {}, + defaultOptions, + ) + const sourceCode = context.getSourceCode() + const offsets = new Map() + const ignoreTokens = new Set() + + /** + * Set offset to the given tokens. + */ + function setOffset( + token: Token | Token[] | null | (Token | null)[], + offset: number, + baseToken: Token, + ) { + if (!token) { + return } - const elementTokens = getFirstAndLastTokens( - node, - lastToken != null ? lastToken.range[1] : 0 - ) - - // Collect comma/comment tokens between the last token of the previous node and the first token of this node. - if (lastToken != null) { - t = lastToken - while ( - (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null && - t.range[1] <= elementTokens.firstToken.range[0] - ) { - if (baseToken == null) { - alignTokensBeforeBaseToken.push(t) - } else { - alignTokens.push(t) + if (Array.isArray(token)) { + for (const t of token) { + offsets.set(t, { + baseToken, + offset, + baseline: false, + expectedIndent: undefined, + }) } - } + } else { + offsets.set(token, { + baseToken, + offset, + baseline: false, + expectedIndent: undefined, + }) } + } - if (baseToken == null) { - baseToken = elementTokens.firstToken - } else { - alignTokens.push(elementTokens.firstToken) + /** + * Set baseline flag to the given token. + */ + function setBaseline(token: Token) { + const offsetInfo = offsets.get(token) + if (offsetInfo != null) { + offsetInfo.baseline = true } + } - // Save the last token to find tokens between this node and the next node. - lastToken = elementTokens.lastToken - } + /** + * Sets preformatted tokens to the given element node. + */ + function setPreformattedTokens(node: VElement) { + const endToken = + (node.endTag && tokenStore.getFirstToken(node.endTag)) || + tokenStore.getTokenAfter(node) + + const option: SourceCode.CursorWithSkipOptions = { + includeComments: true, + filter: (token) => + token != null && + (token.type === "HTMLText" || + token.type === "HTMLRCDataText" || + token.type === "HTMLTagOpen" || + token.type === "HTMLEndTagOpen" || + token.type === "HTMLComment"), + } + for (const token of tokenStore.getTokensBetween( + node.startTag, + endToken, + option, + )) { + ignoreTokens.add(token) + } + ignoreTokens.add(endToken) + } - // Check trailing commas and comments. - if (rightToken != null && lastToken != null) { - t = lastToken + /** + * Get the first and last tokens of the given node. + * If the node is parenthesized, this gets the outermost parentheses. + * @param node The node to get. + * @param [borderOffset] The least offset of the first token. Defailt is 0. This value is used to prevent false positive in the following case: `(a) => {}` The parentheses are enclosing the whole parameter part rather than the first parameter, but this offset parameter is needed to distinguish. + * @returns The gotten tokens. + */ + function getFirstAndLastTokens(node: ASTNode, borderOffset = 0) { + // eslint-disable-next-line no-param-reassign + borderOffset |= 0 + + let firstToken = tokenStore.getFirstToken(node) + let lastToken = tokenStore.getLastToken(node) + + // Get the outermost left parenthesis if it's parenthesized. + let t, u while ( - (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null && - t.range[1] <= rightToken.range[0] + (t = tokenStore.getTokenBefore(firstToken)) != null && + (u = tokenStore.getTokenAfter(lastToken)) != null && + isLeftParen(t) && + isRightParen(u) && + t.range[0] >= borderOffset ) { - if (baseToken == null) { - alignTokensBeforeBaseToken.push(t) - } else { - alignTokens.push(t) - } - } - } - - // Set offsets. - if (leftToken != null) { - setOffset(alignTokensBeforeBaseToken, offset, leftToken) - } - if (baseToken != null) { - // Set offset to the first token. - if (leftToken != null) { - setOffset(baseToken, offset, leftToken) + firstToken = t + lastToken = u } - // Set baseline. - if (nodeList.some(isBeginningOfLine)) { - setBaseline(baseToken) + return { firstToken, lastToken } + } + + /** + * Process the given node list. + * The first node is offsetted from the given left token. + * Rest nodes are adjusted to the first node. + * @param nodeList The node to process. + * @param left The left parenthesis token. + * @param right The right parenthesis token. + * @param offset The offset to set. + * @param [alignVertically=true] The flag to align vertically. If `false`, this doesn't align vertically even if the first node is not at beginning of line. + * @returns {void} + */ + // eslint-disable-next-line complexity + function processNodeList( + nodeList: (ASTNode | null)[], + left: ASTNode | Token | null, + right: ASTNode | Token | null, + offset: number, + alignVertically = true, + ) { + let t + const leftToken = left && tokenStore.getFirstToken(left) + const rightToken = right && tokenStore.getFirstToken(right) + + if (nodeList.length >= 1) { + let baseToken = null + let lastToken = left + const alignTokensBeforeBaseToken = [] + const alignTokens = [] + + for (const node of nodeList) { + if (node == null) { + // Holes of an array. + continue + } + const elementTokens = getFirstAndLastTokens( + node, + lastToken != null ? lastToken.range[1] : 0, + ) + + // Collect comma/comment tokens between the last token of the previous node and the first token of this node. + if (lastToken != null) { + t = lastToken + while ( + (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != + null && + t.range[1] <= elementTokens.firstToken.range[0] + ) { + if (baseToken == null) { + alignTokensBeforeBaseToken.push(t) + } else { + alignTokens.push(t) + } + } + } + + if (baseToken == null) { + baseToken = elementTokens.firstToken + } else { + alignTokens.push(elementTokens.firstToken) + } + + // Save the last token to find tokens between this node and the next node. + lastToken = elementTokens.lastToken + } + + // Check trailing commas and comments. + if (rightToken != null && lastToken != null) { + t = lastToken + while ( + (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null && + t.range[1] <= rightToken.range[0] + ) { + if (baseToken == null) { + alignTokensBeforeBaseToken.push(t) + } else { + alignTokens.push(t) + } + } + } + + // Set offsets. + if (leftToken != null) { + setOffset(alignTokensBeforeBaseToken, offset, leftToken) + } + if (baseToken != null) { + // Set offset to the first token. + if (leftToken != null) { + setOffset(baseToken, offset, leftToken) + } + + // Set baseline. + if (nodeList.some(isBeginningOfLine)) { + setBaseline(baseToken) + } + + if (alignVertically === false && leftToken != null) { + // Align tokens relatively to the left token. + setOffset(alignTokens, offset, leftToken) + } else { + // Align the rest tokens to the first token. + setOffset(alignTokens, 0, baseToken) + } + } } - if (alignVertically === false && leftToken != null) { - // Align tokens relatively to the left token. - setOffset(alignTokens, offset, leftToken) - } else { - // Align the rest tokens to the first token. - setOffset(alignTokens, 0, baseToken) + if (rightToken != null && leftToken != null) { + setOffset(rightToken, 0, leftToken) } - } } - if (rightToken != null && leftToken != null) { - setOffset(rightToken, 0, leftToken) - } - } - - /** - * Process the given node as body. - * The body node maybe a block statement or an expression node. - * @param {ASTNode} node The body node to process. - * @param {Token} baseToken The base token. - * @returns {void} - */ - function processMaybeBlock(node, baseToken) { - const firstToken = getFirstAndLastTokens(node).firstToken - setOffset(firstToken, isLeftBrace(firstToken) ? 0 : 1, baseToken) - } - - /** - * Collect prefix tokens of the given property. - * The prefix includes `async`, `get`, `set`, `static`, and `*`. - * @param {Property|MethodDefinition} node The property node to collect prefix tokens. - */ - function getPrefixTokens(node) { - const prefixes = [] - - /** @type {Token|null} */ - let token = tokenStore.getFirstToken(node) - while (token != null && token.range[1] <= node.key.range[0]) { - prefixes.push(token) - token = tokenStore.getTokenAfter(token) - } - while (isLeftParen(last(prefixes)) || isLeftBracket(last(prefixes))) { - prefixes.pop() + /** + * Process the given node as body. + * The body node maybe a block statement or an expression node. + * @param node The body node to process. + * @param baseToken The base token. + * @returns {void} + */ + function processMaybeBlock(node: ASTNode, baseToken: Token) { + const firstToken = getFirstAndLastTokens(node).firstToken + setOffset(firstToken, isLeftBrace(firstToken) ? 0 : 1, baseToken) } - return prefixes - } - - /** - * Find the head of chaining nodes. - * @param {ASTNode} node The start node to find the head. - * @returns {Token} The head token of the chain. - */ - function getChainHeadToken(node) { - const type = node.type - while (node.parent && node.parent.type === type) { - const prevToken = tokenStore.getTokenBefore(node) - if (isLeftParen(prevToken)) { - // The chaining is broken by parentheses. - break - } - node = node.parent - } - return tokenStore.getFirstToken(node) - } - - /** - * Check whether a given token is the first token of: - * - * - ExpressionStatement - * - VExpressionContainer - * - A parameter of CallExpression/NewExpression - * - An element of ArrayExpression - * - An expression of SequenceExpression - * - * @param {Token} token The token to check. - * @param {ASTNode} belongingNode The node that the token is belonging to. - * @returns {boolean} `true` if the token is the first token of an element. - */ - function isBeginningOfElement(token, belongingNode) { - let node = belongingNode - - while (node != null && node.parent != null) { - const parent = node.parent - if ( - parent.type.endsWith('Statement') || - parent.type.endsWith('Declaration') - ) { - return parent.range[0] === token.range[0] - } - if (parent.type === 'VExpressionContainer') { - if (node.range[0] !== token.range[0]) { - return false + /** + * Collect prefix tokens of the given property. + * The prefix includes `async`, `get`, `set`, `static`, and `*`. + * @param node The property node to collect prefix tokens. + */ + function getPrefixTokens(node: Property | MethodDefinition) { + const prefixes = [] + + /** @type {Token|null} */ + let token = tokenStore.getFirstToken(node) + while (token != null && token.range[1] <= node.key.range[0]) { + prefixes.push(token) + token = tokenStore.getTokenAfter(token) } - const prevToken = tokenStore.getTokenBefore(belongingNode) - if (isLeftParen(prevToken)) { - // It is not the first token because it is enclosed in parentheses. - return false + while (isLeftParen(last(prefixes)) || isLeftBracket(last(prefixes))) { + prefixes.pop() } - return true - } - if (parent.type === 'CallExpression' || parent.type === 'NewExpression') { - const openParen = /** @type {Token} */ (tokenStore.getTokenAfter( - parent.callee, - isNotRightParen - )) - return parent.arguments.some( - (param) => - getFirstAndLastTokens(param, openParen.range[1]).firstToken - .range[0] === token.range[0] - ) - } - if (parent.type === 'ArrayExpression') { - return parent.elements.some( - (element) => - element != null && - getFirstAndLastTokens(element).firstToken.range[0] === - token.range[0] - ) - } - if (parent.type === 'SequenceExpression') { - return parent.expressions.some( - (expr) => - getFirstAndLastTokens(expr).firstToken.range[0] === token.range[0] - ) - } - node = parent + return prefixes } - return false - } - - /** - * Set the base indentation to a given top-level AST node. - * @param {ASTNode} node The node to set. - * @param {number} expectedIndent The number of expected indent. - * @returns {void} - */ - function processTopLevelNode(node, expectedIndent) { - const token = tokenStore.getFirstToken(node) - const offsetInfo = offsets.get(token) - if (offsetInfo != null) { - offsetInfo.expectedIndent = expectedIndent - } else { - offsets.set(token, { - baseToken: null, - offset: 0, - baseline: false, - expectedIndent - }) - } - } - - /** - * Ignore all tokens of the given node. - * @param {ASTNode} node The node to ignore. - * @returns {void} - */ - function ignore(node) { - for (const token of tokenStore.getTokens(node)) { - offsets.delete(token) - ignoreTokens.add(token) - } - } - - /** - * Define functions to ignore nodes into the given visitor. - * @param {NodeListener} visitor The visitor to define functions to ignore nodes. - * @returns {NodeListener} The visitor. - */ - function processIgnores(visitor) { - for (const ignorePattern of options.ignores) { - const key = `${ignorePattern}:exit` - - if (visitor.hasOwnProperty(key)) { - const handler = visitor[key] - visitor[key] = function (node, ...args) { - // @ts-expect-error - const ret = handler.call(this, node, ...args) - ignore(node) - return ret + /** + * Find the head of chaining nodes. + * @param node The start node to find the head. + * @returns {Token} The head token of the chain. + */ + function getChainHeadToken(node: ASTNode) { + const type = node.type + while (node.parent && node.parent.type === type) { + const prevToken = tokenStore.getTokenBefore(node) + if (isLeftParen(prevToken)) { + // The chaining is broken by parentheses. + break + } + // eslint-disable-next-line no-param-reassign + node = node.parent } - } else { - visitor[key] = ignore - } + return tokenStore.getFirstToken(node) } - return visitor - } + /** + * Check whether a given token is the first token of: + * + * - ExpressionStatement + * - VExpressionContainer + * - A parameter of CallExpression/NewExpression + * - An element of ArrayExpression + * - An expression of SequenceExpression + * + * @param token The token to check. + * @param belongingNode The node that the token is belonging to. + * @returns {boolean} `true` if the token is the first token of an element. + */ + function isBeginningOfElement(token: Token, belongingNode: ASTNode) { + let node = belongingNode + + while (node != null && node.parent != null) { + const parent = node.parent + if ( + parent.type.endsWith("Statement") || + parent.type.endsWith("Declaration") + ) { + return parent.range[0] === token.range[0] + } + if (parent.type === "VExpressionContainer") { + if (node.range[0] !== token.range[0]) { + return false + } + const prevToken = tokenStore.getTokenBefore(belongingNode) + if (isLeftParen(prevToken)) { + // It is not the first token because it is enclosed in parentheses. + return false + } + return true + } + if ( + parent.type === "CallExpression" || + parent.type === "NewExpression" + ) { + const openParen = tokenStore.getTokenAfter( + parent.callee, + isNotRightParen, + ) as Token + return parent.arguments.some( + (param) => + getFirstAndLastTokens(param, openParen.range[1]) + .firstToken.range[0] === token.range[0], + ) + } + if (parent.type === "ArrayExpression") { + return parent.elements.some( + (element) => + element != null && + getFirstAndLastTokens(element).firstToken.range[0] === + token.range[0], + ) + } + if (parent.type === "SequenceExpression") { + return parent.expressions.some( + (expr) => + getFirstAndLastTokens(expr).firstToken.range[0] === + token.range[0], + ) + } - /** - * Calculate correct indentation of the line of the given tokens. - * @param {Token[]} tokens Tokens which are on the same line. - * @returns { { expectedIndent: number, expectedBaseIndent: number } |null } Correct indentation. If it failed to calculate then `null`. - */ - function getExpectedIndents(tokens) { - const expectedIndents = [] + node = parent + } - for (let i = 0; i < tokens.length; ++i) { - const token = tokens[i] - const offsetInfo = offsets.get(token) + return false + } - if (offsetInfo != null) { - if (offsetInfo.expectedIndent != null) { - expectedIndents.push(offsetInfo.expectedIndent) + /** + * Set the base indentation to a given top-level AST node. + * @param node The node to set. + * @param expectedIndent The number of expected indent. + * @returns {void} + */ + function processTopLevelNode(node: ASTNode, expectedIndent: number) { + const token = tokenStore.getFirstToken(node) + const offsetInfo = offsets.get(token) + if (offsetInfo != null) { + offsetInfo.expectedIndent = expectedIndent } else { - const baseOffsetInfo = offsets.get(offsetInfo.baseToken) - if ( - baseOffsetInfo != null && - baseOffsetInfo.expectedIndent != null && - (i === 0 || !baseOffsetInfo.baseline) - ) { - expectedIndents.push( - baseOffsetInfo.expectedIndent + - offsetInfo.offset * options.indentSize - ) - if (baseOffsetInfo.baseline) { - break - } - } + offsets.set(token, { + baseToken: null, + offset: 0, + baseline: false, + expectedIndent, + }) } - } - } - if (!expectedIndents.length) { - return null } - return { - expectedIndent: expectedIndents[0], - expectedBaseIndent: expectedIndents.reduce((a, b) => Math.min(a, b)) - } - } - - /** - * Get the text of the indentation part of the line which the given token is on. - * @param {Token} firstToken The first token on a line. - * @returns {string} The text of indentation part. - */ - function getIndentText(firstToken) { - const text = sourceCode.text - let i = firstToken.range[0] - 1 - - while (i >= 0 && !LT_CHAR.test(text[i])) { - i -= 1 + /** + * Ignore all tokens of the given node. + * @param node The node to ignore. + * @returns {void} + */ + function ignore(node: ASTNode) { + for (const token of tokenStore.getTokens(node)) { + offsets.delete(token) + ignoreTokens.add(token) + } } - return text.slice(i + 1, firstToken.range[0]) - } - - /** - * Define the function which fixes the problem. - * @param {Token} token The token to fix. - * @param {number} actualIndent The number of actual indentation. - * @param {number} expectedIndent The number of expected indentation. - * @returns { (fixer: RuleFixer) => Fix } The defined function. - */ - function defineFix(token, actualIndent, expectedIndent) { - if (token.type === 'Block' && token.loc.start.line !== token.loc.end.line) { - // Fix indentation in multiline block comments. - const lines = sourceCode.getText(token).match(LINES) || [] - const firstLine = lines.shift() - if (lines.every((l) => BLOCK_COMMENT_PREFIX.test(l))) { - return (fixer) => { - /** @type {Range} */ - const range = [token.range[0] - actualIndent, token.range[1]] - const indent = options.indentChar.repeat(expectedIndent) - - return fixer.replaceTextRange( - range, - `${indent}${firstLine}${lines - .map((l) => l.replace(BLOCK_COMMENT_PREFIX, `${indent} *`)) - .join('')}` - ) + /** + * Define functions to ignore nodes into the given visitor. + * @param visitor The visitor to define functions to ignore nodes. + * @returns The visitor. + */ + function processIgnores(visitor: NodeListener) { + for (const ignorePattern of options.ignores) { + const key = `${ignorePattern}:exit` + // eslint-disable-next-line no-prototype-builtins + if (visitor.hasOwnProperty(key)) { + const handler = visitor[key] + visitor[key] = function (node, ...args) { + // @ts-expect-error + const ret = handler.call(this, node, ...args) + ignore(node) + return ret + } + } else { + visitor[key] = ignore + } } - } + + return visitor } - return (fixer) => { - /** @type {Range} */ - const range = [token.range[0] - actualIndent, token.range[0]] - const indent = options.indentChar.repeat(expectedIndent) - return fixer.replaceTextRange(range, indent) + /** + * Calculate correct indentation of the line of the given tokens. + * @param tokens Tokens which are on the same line. + * @returns Correct indentation. If it failed to calculate then `null`. + */ + function getExpectedIndents( + tokens: Token[], + ): { expectedIndent: number; expectedBaseIndent: number } | null { + const expectedIndents = [] + + for (let i = 0; i < tokens.length; ++i) { + const token = tokens[i] + const offsetInfo = offsets.get(token) + + if (offsetInfo != null) { + if (offsetInfo.expectedIndent != null) { + expectedIndents.push(offsetInfo.expectedIndent) + } else { + const baseOffsetInfo = offsets.get(offsetInfo.baseToken) + if ( + baseOffsetInfo != null && + baseOffsetInfo.expectedIndent != null && + (i === 0 || !baseOffsetInfo.baseline) + ) { + expectedIndents.push( + // eslint-disable-next-line @mysticatea/ts/restrict-plus-operands + baseOffsetInfo.expectedIndent + + offsetInfo.offset * options.indentSize, + ) + if (baseOffsetInfo.baseline) { + break + } + } + } + } + } + if (!expectedIndents.length) { + return null + } + + return { + expectedIndent: expectedIndents[0], + expectedBaseIndent: expectedIndents.reduce((a, b) => + Math.min(a, b), + ), + } } - } - - /** - * Validate the given token with the pre-calculated expected indentation. - * @param {Token} token The token to validate. - * @param {number} expectedIndent The expected indentation. - * @param {number[]} [optionalExpectedIndents] The optional expected indentation. - * @returns {void} - */ - function validateCore(token, expectedIndent, optionalExpectedIndents) { - const line = token.loc.start.line - const indentText = getIndentText(token) - - // If there is no line terminator after the `<script>` start tag, - // `indentText` contains non-whitespace characters. - // In that case, do nothing in order to prevent removing the `<script>` tag. - if (indentText.trim() !== '') { - return + + /** + * Get the text of the indentation part of the line which the given token is on. + * @param firstToken The first token on a line. + * @returns The text of indentation part. + */ + function getIndentText(firstToken: Token) { + const text = sourceCode.text + let i = firstToken.range[0] - 1 + + while (i >= 0 && !LT_CHAR.test(text[i])) { + i -= 1 + } + + return text.slice(i + 1, firstToken.range[0]) } - const actualIndent = token.loc.start.column - const unit = options.indentChar === '\t' ? 'tab' : 'space' - - for (let i = 0; i < indentText.length; ++i) { - if (indentText[i] !== options.indentChar) { - context.report({ - loc: { - start: { line, column: i }, - end: { line, column: i + 1 } - }, - message: - 'Expected {{expected}} character, but found {{actual}} character.', - data: { - expected: JSON.stringify(options.indentChar), - actual: JSON.stringify(indentText[i]) - }, - fix: defineFix(token, actualIndent, expectedIndent) - }) - return - } + /** + * Define the function which fixes the problem. + * @param token The token to fix. + * @param actualIndent The number of actual indentation. + * @param expectedIndent The number of expected indentation. + * @returns The defined function. + */ + function defineFix( + token: Token, + actualIndent: number, + expectedIndent: number, + ): (fixer: RuleFixer) => Fix { + if ( + token.type === "Block" && + token.loc.start.line !== token.loc.end.line + ) { + // Fix indentation in multiline block comments. + const lines = sourceCode.getText(token).match(LINES) || [] + const firstLine = lines.shift() + if (lines.every((l) => BLOCK_COMMENT_PREFIX.test(l))) { + return (fixer) => { + const range: Range = [ + token.range[0] - actualIndent, + token.range[1], + ] + const indent = options.indentChar.repeat(expectedIndent) + + return fixer.replaceTextRange( + range, + `${indent}${firstLine}${lines + .map((l) => + l.replace(BLOCK_COMMENT_PREFIX, `${indent} *`), + ) + .join("")}`, + ) + } + } + } + + return (fixer) => { + const range: Range = [token.range[0] - actualIndent, token.range[0]] + const indent = options.indentChar.repeat(expectedIndent) + return fixer.replaceTextRange(range, indent) + } } - if ( - actualIndent !== expectedIndent && - (optionalExpectedIndents == null || - !optionalExpectedIndents.includes(actualIndent)) + /** + * Validate the given token with the pre-calculated expected indentation. + * @param token The token to validate. + * @param expectedIndent The expected indentation. + * @param [optionalExpectedIndents] The optional expected indentation. + */ + function validateCore( + token: Token, + expectedIndent: number, + optionalExpectedIndents?: number[], ) { - context.report({ - loc: { - start: { line, column: 0 }, - end: { line, column: actualIndent } - }, - message: - 'Expected indentation of {{expectedIndent}} {{unit}}{{expectedIndentPlural}} but found {{actualIndent}} {{unit}}{{actualIndentPlural}}.', - data: { - expectedIndent, - actualIndent, - unit, - expectedIndentPlural: expectedIndent === 1 ? '' : 's', - actualIndentPlural: actualIndent === 1 ? '' : 's' - }, - fix: defineFix(token, actualIndent, expectedIndent) - }) + const line = token.loc.start.line + const indentText = getIndentText(token) + + // If there is no line terminator after the `<script>` start tag, + // `indentText` contains non-whitespace characters. + // In that case, do nothing in order to prevent removing the `<script>` tag. + if (indentText.trim() !== "") { + return + } + + const actualIndent = token.loc.start.column + const unit = options.indentChar === "\t" ? "tab" : "space" + + for (let i = 0; i < indentText.length; ++i) { + if (indentText[i] !== options.indentChar) { + context.report({ + loc: { + start: { line, column: i }, + end: { line, column: i + 1 }, + }, + message: + "Expected {{expected}} character, but found {{actual}} character.", + data: { + expected: JSON.stringify(options.indentChar), + actual: JSON.stringify(indentText[i]), + }, + fix: defineFix(token, actualIndent, expectedIndent), + }) + return + } + } + + if ( + actualIndent !== expectedIndent && + (optionalExpectedIndents == null || + !optionalExpectedIndents.includes(actualIndent)) + ) { + context.report({ + loc: { + start: { line, column: 0 }, + end: { line, column: actualIndent }, + }, + message: + "Expected indentation of {{expectedIndent}} {{unit}}{{expectedIndentPlural}} but found {{actualIndent}} {{unit}}{{actualIndentPlural}}.", + data: { + expectedIndent, + actualIndent, + unit, + expectedIndentPlural: expectedIndent === 1 ? "" : "s", + actualIndentPlural: actualIndent === 1 ? "" : "s", + }, + fix: defineFix(token, actualIndent, expectedIndent), + }) + } } - } - - /** - * Get the expected indent of comments. - * @param {Token} nextToken The next token of comments. - * @param {number} nextExpectedIndent The expected indent of the next token. - * @param {number} lastExpectedIndent The expected indent of the last token. - * @returns {number[]} - */ - function getCommentExpectedIndents( - nextToken, - nextExpectedIndent, - lastExpectedIndent - ) { - if (typeof lastExpectedIndent === 'number' && isClosingToken(nextToken)) { - if (nextExpectedIndent === lastExpectedIndent) { - // For solo comment. E.g., + + /** + * Get the expected indent of comments. + * @param nextToken The next token of comments. + * @param nextExpectedIndent The expected indent of the next token. + * @param lastExpectedIndent The expected indent of the last token. + */ + function getCommentExpectedIndents( + nextToken: Token, + nextExpectedIndent: number, + lastExpectedIndent: number, + ) { + if ( + typeof lastExpectedIndent === "number" && + isClosingToken(nextToken) + ) { + if (nextExpectedIndent === lastExpectedIndent) { + // For solo comment. E.g., + // <div> + // <!-- comment --> + // </div> + return [ + nextExpectedIndent + options.indentSize, + nextExpectedIndent, + ] + } + + // For last comment. E.g., + // <div> + // <div></div> + // <!-- comment --> + // </div> + return [lastExpectedIndent, nextExpectedIndent] + } + + // Adjust to next normally. E.g., // <div> // <!-- comment --> + // <div></div> // </div> - return [nextExpectedIndent + options.indentSize, nextExpectedIndent] - } - - // For last comment. E.g., - // <div> - // <div></div> - // <!-- comment --> - // </div> - return [lastExpectedIndent, nextExpectedIndent] + return [nextExpectedIndent] } - // Adjust to next normally. E.g., - // <div> - // <!-- comment --> - // <div></div> - // </div> - return [nextExpectedIndent] - } - - /** - * Validate indentation of the line that the given tokens are on. - * @param {Token[]} tokens The tokens on the same line to validate. - * @param {Token[]} comments The comments which are on the immediately previous lines of the tokens. - * @param {Token|null} lastToken The last validated token. Comments can adjust to the token. - * @returns {void} - */ - function validate(tokens, comments, lastToken) { - // Calculate and save expected indentation. - const firstToken = tokens[0] - const actualIndent = firstToken.loc.start.column - const expectedIndents = getExpectedIndents(tokens) - if (!expectedIndents) { - return - } + /** + * Validate indentation of the line that the given tokens are on. + * @param tokens The tokens on the same line to validate. + * @param comments The comments which are on the immediately previous lines of the tokens. + * @param lastToken The last validated token. Comments can adjust to the token. + */ + function validate( + tokens: Token[], + comments: Token[], + lastToken: Token | null, + ) { + // Calculate and save expected indentation. + const firstToken = tokens[0] + const actualIndent = firstToken.loc.start.column + const expectedIndents = getExpectedIndents(tokens) + if (!expectedIndents) { + return + } + + const expectedBaseIndent = expectedIndents.expectedBaseIndent + const expectedIndent = expectedIndents.expectedIndent + + // Debug log + // console.log('line', firstToken.loc.start.line, '=', { actualIndent, expectedIndent }, 'from:') + // for (const token of tokens) { + // const offsetInfo = offsets.get(token) + // if (offsetInfo == null) { + // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is unknown.') + // } else if (offsetInfo.expectedIndent != null) { + // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is fixed at', offsetInfo.expectedIndent, '.') + // } else { + // const baseOffsetInfo = offsets.get(offsetInfo.baseToken) + // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is', offsetInfo.offset, 'offset from ', JSON.stringify(sourceCode.getText(offsetInfo.baseToken)), '( line:', offsetInfo.baseToken && offsetInfo.baseToken.loc.start.line, ', indent:', baseOffsetInfo && baseOffsetInfo.expectedIndent, ', baseline:', baseOffsetInfo && baseOffsetInfo.baseline, ')') + // } + // } + + // Save. + const baseline = new Set() + for (const token of tokens) { + const offsetInfo = offsets.get(token) + if (offsetInfo != null) { + if (offsetInfo.baseline) { + // This is a baseline token, so the expected indent is the column of this token. + if (options.indentChar === " ") { + offsetInfo.expectedIndent = Math.max( + 0, + token.loc.start.column + + expectedBaseIndent - + actualIndent, + ) + } else { + // In hard-tabs mode, it cannot align tokens strictly, so use one additional offset. + // But the additional offset isn't needed if it's at the beginning of the line. + offsetInfo.expectedIndent = + expectedBaseIndent + (token === tokens[0] ? 0 : 1) + } + baseline.add(token) + } else if (baseline.has(offsetInfo.baseToken)) { + // The base token is a baseline token on this line, so inherit it. + offsetInfo.expectedIndent = offsets.get( + offsetInfo.baseToken, + ).expectedIndent + baseline.add(token) + } else { + // Otherwise, set the expected indent of this line. + offsetInfo.expectedIndent = expectedBaseIndent + } + } + } + + // It does not validate ignore tokens. + if (ignoreTokens.has(firstToken)) { + return + } + + // Calculate the expected indents for comments. + // It allows the same indent level with the previous line. + const lastOffsetInfo = offsets.get(lastToken) + const lastExpectedIndent = + lastOffsetInfo && lastOffsetInfo.expectedIndent + const commentOptionalExpectedIndents = getCommentExpectedIndents( + firstToken, + expectedIndent, + lastExpectedIndent, + ) - const expectedBaseIndent = expectedIndents.expectedBaseIndent - const expectedIndent = expectedIndents.expectedIndent - - // Debug log - // console.log('line', firstToken.loc.start.line, '=', { actualIndent, expectedIndent }, 'from:') - // for (const token of tokens) { - // const offsetInfo = offsets.get(token) - // if (offsetInfo == null) { - // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is unknown.') - // } else if (offsetInfo.expectedIndent != null) { - // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is fixed at', offsetInfo.expectedIndent, '.') - // } else { - // const baseOffsetInfo = offsets.get(offsetInfo.baseToken) - // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is', offsetInfo.offset, 'offset from ', JSON.stringify(sourceCode.getText(offsetInfo.baseToken)), '( line:', offsetInfo.baseToken && offsetInfo.baseToken.loc.start.line, ', indent:', baseOffsetInfo && baseOffsetInfo.expectedIndent, ', baseline:', baseOffsetInfo && baseOffsetInfo.baseline, ')') - // } - // } - - // Save. - const baseline = new Set() - for (const token of tokens) { - const offsetInfo = offsets.get(token) - if (offsetInfo != null) { - if (offsetInfo.baseline) { - // This is a baseline token, so the expected indent is the column of this token. - if (options.indentChar === ' ') { - offsetInfo.expectedIndent = Math.max( - 0, - token.loc.start.column + expectedBaseIndent - actualIndent + // Validate. + for (const comment of comments) { + const commentExpectedIndents = getExpectedIndents([comment]) + const commentExpectedIndent = commentExpectedIndents + ? commentExpectedIndents.expectedIndent + : commentOptionalExpectedIndents[0] + + validateCore( + comment, + commentExpectedIndent, + commentOptionalExpectedIndents, ) - } else { - // In hard-tabs mode, it cannot align tokens strictly, so use one additional offset. - // But the additional offset isn't needed if it's at the beginning of the line. - offsetInfo.expectedIndent = - expectedBaseIndent + (token === tokens[0] ? 0 : 1) - } - baseline.add(token) - } else if (baseline.has(offsetInfo.baseToken)) { - // The base token is a baseline token on this line, so inherit it. - offsetInfo.expectedIndent = offsets.get( - offsetInfo.baseToken - ).expectedIndent - baseline.add(token) - } else { - // Otherwise, set the expected indent of this line. - offsetInfo.expectedIndent = expectedBaseIndent } - } + validateCore(firstToken, expectedIndent) } - // It does not validate ignore tokens. - if (ignoreTokens.has(firstToken)) { - return - } + // ------------------------------------------------------------------------------ + // Main + // ------------------------------------------------------------------------------ + + return processIgnores({ + VAttribute(node: VAttribute) { + const keyToken = tokenStore.getFirstToken(node) + const eqToken = tokenStore.getTokenAfter(node.key) + + if (eqToken != null && eqToken.range[1] <= node.range[1]) { + setOffset(eqToken, 1, keyToken) + + const valueToken = tokenStore.getTokenAfter(eqToken) + if ( + valueToken != null && + valueToken.range[1] <= node.range[1] + ) { + setOffset(valueToken, 1, keyToken) + } + } + }, - // Calculate the expected indents for comments. - // It allows the same indent level with the previous line. - const lastOffsetInfo = offsets.get(lastToken) - const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo.expectedIndent - const commentOptionalExpectedIndents = getCommentExpectedIndents( - firstToken, - expectedIndent, - lastExpectedIndent - ) + VElement(node: VElement) { + if (!PREFORMATTED_ELEMENT_NAMES.includes(node.name)) { + const isTopLevel = node.parent.type !== "VElement" + const offset = isTopLevel ? options.baseIndent : 1 + processNodeList( + node.children.filter(isNotEmptyTextNode), + node.startTag, + node.endTag, + offset, + false, + ) + } else { + const startTagToken = tokenStore.getFirstToken(node) + const endTagToken = + node.endTag && tokenStore.getFirstToken(node.endTag) + setOffset(endTagToken, 0, startTagToken) + setPreformattedTokens(node) + } + }, - // Validate. - for (const comment of comments) { - const commentExpectedIndents = getExpectedIndents([comment]) - const commentExpectedIndent = commentExpectedIndents - ? commentExpectedIndents.expectedIndent - : commentOptionalExpectedIndents[0] - - validateCore( - comment, - commentExpectedIndent, - commentOptionalExpectedIndents - ) - } - validateCore(firstToken, expectedIndent) - } + VEndTag(node: VEndTag) { + const element = node.parent + const startTagOpenToken = tokenStore.getFirstToken(element.startTag) + const closeToken = tokenStore.getLastToken(node) + + if (closeToken.type.endsWith("TagClose")) { + setOffset( + closeToken, + options.closeBracket.endTag, + startTagOpenToken, + ) + } + }, - // ------------------------------------------------------------------------------ - // Main - // ------------------------------------------------------------------------------ + VExpressionContainer(node: VExpressionContainer) { + if ( + node.expression != null && + node.range[0] !== node.expression.range[0] + ) { + const startQuoteToken = tokenStore.getFirstToken(node) + const endQuoteToken = tokenStore.getLastToken(node) + const childToken = tokenStore.getTokenAfter(startQuoteToken) + + setOffset(childToken, 1, startQuoteToken) + setOffset(endQuoteToken, 0, startQuoteToken) + } + }, - return processIgnores({ - /** @param {VAttribute} node */ - VAttribute(node) { - const keyToken = tokenStore.getFirstToken(node) - const eqToken = tokenStore.getTokenAfter(node.key) + VFilter(node: VFilter) { + const idToken = tokenStore.getFirstToken(node) + const lastToken = tokenStore.getLastToken(node) + if (isRightParen(lastToken)) { + const leftParenToken = tokenStore.getTokenAfter(node.callee) + setOffset(leftParenToken, 1, idToken) + processNodeList(node.arguments, leftParenToken, lastToken, 1) + } + }, - if (eqToken != null && eqToken.range[1] <= node.range[1]) { - setOffset(eqToken, 1, keyToken) + VFilterSequenceExpression(node: VFilterSequenceExpression) { + if (node.filters.length === 0) { + return + } - const valueToken = tokenStore.getTokenAfter(eqToken) - if (valueToken != null && valueToken.range[1] <= node.range[1]) { - setOffset(valueToken, 1, keyToken) - } - } - }, - /** @param {VElement} node */ - VElement(node) { - if (!PREFORMATTED_ELEMENT_NAMES.includes(node.name)) { - const isTopLevel = node.parent.type !== 'VElement' - const offset = isTopLevel ? options.baseIndent : 1 - processNodeList( - node.children.filter(isNotEmptyTextNode), - node.startTag, - node.endTag, - offset, - false - ) - } else { - const startTagToken = tokenStore.getFirstToken(node) - const endTagToken = node.endTag && tokenStore.getFirstToken(node.endTag) - setOffset(endTagToken, 0, startTagToken) - setPreformattedTokens(node) - } - }, - /** @param {VEndTag} node */ - VEndTag(node) { - const element = node.parent - const startTagOpenToken = tokenStore.getFirstToken(element.startTag) - const closeToken = tokenStore.getLastToken(node) - - if (closeToken.type.endsWith('TagClose')) { - setOffset(closeToken, options.closeBracket.endTag, startTagOpenToken) - } - }, - /** @param {VExpressionContainer} node */ - VExpressionContainer(node) { - if ( - node.expression != null && - node.range[0] !== node.expression.range[0] - ) { - const startQuoteToken = tokenStore.getFirstToken(node) - const endQuoteToken = tokenStore.getLastToken(node) - const childToken = tokenStore.getTokenAfter(startQuoteToken) - - setOffset(childToken, 1, startQuoteToken) - setOffset(endQuoteToken, 0, startQuoteToken) - } - }, - /** @param {VFilter} node */ - VFilter(node) { - const idToken = tokenStore.getFirstToken(node) - const lastToken = tokenStore.getLastToken(node) - if (isRightParen(lastToken)) { - const leftParenToken = tokenStore.getTokenAfter(node.callee) - setOffset(leftParenToken, 1, idToken) - processNodeList(node.arguments, leftParenToken, lastToken, 1) - } - }, - /** @param {VFilterSequenceExpression} node */ - VFilterSequenceExpression(node) { - if (node.filters.length === 0) { - return - } - - const firstToken = tokenStore.getFirstToken(node) - /** @type {(Token|null)[]} */ - const tokens = [] - - for (const filter of node.filters) { - tokens.push( - tokenStore.getTokenBefore(filter, isPipeOperator), - tokenStore.getFirstToken(filter) - ) - } - - setOffset(tokens, 1, firstToken) - }, - /** @param {VForExpression} node */ - VForExpression(node) { - const firstToken = tokenStore.getFirstToken(node) - const lastOfLeft = last(node.left) || firstToken - const inToken = /** @type {Token} */ (tokenStore.getTokenAfter( - lastOfLeft, - isNotRightParen - )) - const rightToken = tokenStore.getFirstToken(node.right) - - if (isLeftParen(firstToken)) { - const rightToken = tokenStore.getTokenAfter(lastOfLeft, isRightParen) - processNodeList(node.left, firstToken, rightToken, 1) - } - setOffset(inToken, 1, firstToken) - setOffset(rightToken, 1, inToken) - }, - /** @param {VOnExpression} node */ - VOnExpression(node) { - processNodeList(node.body, null, null, 0) - }, - /** @param {VStartTag} node */ - VStartTag(node) { - const openToken = tokenStore.getFirstToken(node) - const closeToken = tokenStore.getLastToken(node) - - processNodeList( - node.attributes, - openToken, - null, - options.attribute, - options.alignAttributesVertically - ) - if (closeToken != null && closeToken.type.endsWith('TagClose')) { - const offset = - closeToken.type !== 'HTMLSelfClosingTagClose' - ? options.closeBracket.startTag - : options.closeBracket.selfClosingTag - setOffset(closeToken, offset, openToken) - } - }, - /** @param {VText} node */ - VText(node) { - const tokens = tokenStore.getTokens(node, isNotWhitespace) - const firstTokenInfo = offsets.get(tokenStore.getFirstToken(node)) - - for (const token of tokens) { - offsets.set(token, Object.assign({}, firstTokenInfo)) - } - }, - /** @param {ArrayExpression | ArrayPattern} node */ - 'ArrayExpression, ArrayPattern'(node) { - processNodeList( - node.elements, - tokenStore.getFirstToken(node), - tokenStore.getLastToken(node), - 1 - ) - }, - /** @param {ArrowFunctionExpression} node */ - ArrowFunctionExpression(node) { - const firstToken = tokenStore.getFirstToken(node) - const secondToken = tokenStore.getTokenAfter(firstToken) - const leftToken = node.async ? secondToken : firstToken - const arrowToken = tokenStore.getTokenBefore(node.body, isArrow) - - if (node.async) { - setOffset(secondToken, 1, firstToken) - } - if (isLeftParen(leftToken)) { - const rightToken = tokenStore.getTokenAfter( - last(node.params) || leftToken, - isRightParen - ) - processNodeList(node.params, leftToken, rightToken, 1) - } - - setOffset(arrowToken, 1, firstToken) - processMaybeBlock(node.body, firstToken) - }, - /** @param {AssignmentExpression | AssignmentPattern | BinaryExpression | LogicalExpression} node */ - 'AssignmentExpression, AssignmentPattern, BinaryExpression, LogicalExpression'( - node - ) { - const leftToken = getChainHeadToken(node) - const opToken = /** @type {Token} */ (tokenStore.getTokenAfter( - node.left, - isNotRightParen - )) - const rightToken = tokenStore.getTokenAfter(opToken) - const prevToken = tokenStore.getTokenBefore(leftToken) - const shouldIndent = - prevToken == null || - prevToken.loc.end.line === leftToken.loc.start.line || - isBeginningOfElement(leftToken, node) - - setOffset([opToken, rightToken], shouldIndent ? 1 : 0, leftToken) - }, - /** @param {AwaitExpression | RestElement | SpreadElement | UnaryExpression} node */ - 'AwaitExpression, RestElement, SpreadElement, UnaryExpression'(node) { - const firstToken = tokenStore.getFirstToken(node) - const nextToken = tokenStore.getTokenAfter(firstToken) - - setOffset(nextToken, 1, firstToken) - }, - /** @param {BlockStatement | ClassBody} node */ - 'BlockStatement, ClassBody'(node) { - processNodeList( - node.body, - tokenStore.getFirstToken(node), - tokenStore.getLastToken(node), - 1 - ) - }, - /** @param {BreakStatement | ContinueStatement | ReturnStatement | ThrowStatement} node */ - 'BreakStatement, ContinueStatement, ReturnStatement, ThrowStatement'(node) { - if ( - ((node.type === 'ReturnStatement' || node.type === 'ThrowStatement') && - node.argument != null) || - ((node.type === 'BreakStatement' || - node.type === 'ContinueStatement') && - node.label != null) - ) { - const firstToken = tokenStore.getFirstToken(node) - const nextToken = tokenStore.getTokenAfter(firstToken) - - setOffset(nextToken, 1, firstToken) - } - }, - /** @param {CallExpression} node */ - CallExpression(node) { - const firstToken = tokenStore.getFirstToken(node) - const rightToken = tokenStore.getLastToken(node) - const leftToken = tokenStore.getTokenAfter(node.callee, isLeftParen) - - setOffset(leftToken, 1, firstToken) - processNodeList(node.arguments, leftToken, rightToken, 1) - }, - /** @param {ImportExpression} node */ - ImportExpression(node) { - const firstToken = tokenStore.getFirstToken(node) - const rightToken = tokenStore.getLastToken(node) - const leftToken = tokenStore.getTokenAfter(firstToken, isLeftParen) - - setOffset(leftToken, 1, firstToken) - processNodeList([node.source], leftToken, rightToken, 1) - }, - /** @param {CatchClause} node */ - CatchClause(node) { - const firstToken = tokenStore.getFirstToken(node) - const bodyToken = tokenStore.getFirstToken(node.body) - - if (node.param != null) { - const leftToken = tokenStore.getTokenAfter(firstToken) - const rightToken = tokenStore.getTokenAfter(node.param) - - setOffset(leftToken, 1, firstToken) - processNodeList([node.param], leftToken, rightToken, 1) - } - setOffset(bodyToken, 0, firstToken) - }, - /** @param {ClassDeclaration | ClassExpression} node */ - 'ClassDeclaration, ClassExpression'(node) { - const firstToken = tokenStore.getFirstToken(node) - const bodyToken = tokenStore.getFirstToken(node.body) - - if (node.id != null) { - setOffset(tokenStore.getFirstToken(node.id), 1, firstToken) - } - if (node.superClass != null) { - const extendsToken = tokenStore.getTokenAfter(node.id || firstToken) - const superClassToken = tokenStore.getTokenAfter(extendsToken) - setOffset(extendsToken, 1, firstToken) - setOffset(superClassToken, 1, extendsToken) - } - setOffset(bodyToken, 0, firstToken) - }, - /** @param {ConditionalExpression} node */ - ConditionalExpression(node) { - const prevToken = tokenStore.getTokenBefore(node) - const firstToken = tokenStore.getFirstToken(node) - const questionToken = /** @type {Token} */ (tokenStore.getTokenAfter( - node.test, - isNotRightParen - )) - const consequentToken = tokenStore.getTokenAfter(questionToken) - const colonToken = /** @type {Token} */ (tokenStore.getTokenAfter( - node.consequent, - isNotRightParen - )) - const alternateToken = tokenStore.getTokenAfter(colonToken) - const isFlat = - prevToken && - prevToken.loc.end.line !== node.loc.start.line && - node.test.loc.end.line === node.consequent.loc.start.line - - if (isFlat) { - setOffset( - [questionToken, consequentToken, colonToken, alternateToken], - 0, - firstToken - ) - } else { - setOffset([questionToken, colonToken], 1, firstToken) - setOffset([consequentToken, alternateToken], 1, questionToken) - } - }, - /** @param {DoWhileStatement} node */ - DoWhileStatement(node) { - const doToken = tokenStore.getFirstToken(node) - const whileToken = /** @type {Token} */ (tokenStore.getTokenAfter( - node.body, - isNotRightParen - )) - const leftToken = tokenStore.getTokenAfter(whileToken) - const testToken = tokenStore.getTokenAfter(leftToken) - const lastToken = tokenStore.getLastToken(node) - const rightToken = isSemicolon(lastToken) - ? tokenStore.getTokenBefore(lastToken) - : lastToken - - processMaybeBlock(node.body, doToken) - setOffset(whileToken, 0, doToken) - setOffset(leftToken, 1, whileToken) - setOffset(testToken, 1, leftToken) - setOffset(rightToken, 0, leftToken) - }, - /** @param {ExportAllDeclaration} node */ - ExportAllDeclaration(node) { - const tokens = tokenStore.getTokens(node) - const firstToken = /** @type {Token} */ (tokens.shift()) - if (isSemicolon(last(tokens))) { - tokens.pop() - } - if (!node.exported) { - setOffset(tokens, 1, firstToken) - } else { - // export * as foo from "mod" - const starToken = /** @type {Token} */ (tokens.find(isWildcard)) - const asToken = tokenStore.getTokenAfter(starToken) - const exportedToken = tokenStore.getTokenAfter(asToken) - const afterTokens = tokens.slice(tokens.indexOf(exportedToken) + 1) - - setOffset(starToken, 1, firstToken) - setOffset(asToken, 1, starToken) - setOffset(exportedToken, 1, starToken) - setOffset(afterTokens, 1, firstToken) - } - }, - /** @param {ExportDefaultDeclaration} node */ - ExportDefaultDeclaration(node) { - const exportToken = tokenStore.getFirstToken(node) - const defaultToken = tokenStore.getFirstToken(node, 1) - const declarationToken = getFirstAndLastTokens(node.declaration) - .firstToken - setOffset([defaultToken, declarationToken], 1, exportToken) - }, - /** @param {ExportNamedDeclaration} node */ - ExportNamedDeclaration(node) { - const exportToken = tokenStore.getFirstToken(node) - if (node.declaration) { - // export var foo = 1; - const declarationToken = tokenStore.getFirstToken(node, 1) - setOffset(declarationToken, 1, exportToken) - } else { - const firstSpecifier = node.specifiers[0] - if (!firstSpecifier || firstSpecifier.type === 'ExportSpecifier') { - // export {foo, bar}; or export {foo, bar} from "mod"; - const leftParenToken = tokenStore.getFirstToken(node, 1) - const rightParenToken = /** @type {Token} */ (tokenStore.getLastToken( - node, - isRightBrace - )) - setOffset(leftParenToken, 0, exportToken) - processNodeList(node.specifiers, leftParenToken, rightParenToken, 1) - - const maybeFromToken = tokenStore.getTokenAfter(rightParenToken) - if ( - maybeFromToken != null && - sourceCode.getText(maybeFromToken) === 'from' - ) { - const fromToken = maybeFromToken - const nameToken = tokenStore.getTokenAfter(fromToken) - setOffset([fromToken, nameToken], 1, exportToken) - } - } else { - // maybe babel-eslint - } - } - }, - /** @param {ExportSpecifier} node */ - ExportSpecifier(node) { - const tokens = tokenStore.getTokens(node) - const firstToken = /** @type {Token} */ (tokens.shift()) - setOffset(tokens, 1, firstToken) - }, - /** @param {ForInStatement | ForOfStatement} node */ - 'ForInStatement, ForOfStatement'(node) { - const forToken = tokenStore.getFirstToken(node) - const awaitToken = - (node.type === 'ForOfStatement' && - node.await && - tokenStore.getTokenAfter(forToken)) || - null - const leftParenToken = tokenStore.getTokenAfter(awaitToken || forToken) - const leftToken = tokenStore.getTokenAfter(leftParenToken) - const inToken = /** @type {Token} */ (tokenStore.getTokenAfter( - leftToken, - isNotRightParen - )) - const rightToken = tokenStore.getTokenAfter(inToken) - const rightParenToken = tokenStore.getTokenBefore( - node.body, - isNotLeftParen - ) - - if (awaitToken != null) { - setOffset(awaitToken, 0, forToken) - } - setOffset(leftParenToken, 1, forToken) - setOffset(leftToken, 1, leftParenToken) - setOffset(inToken, 1, leftToken) - setOffset(rightToken, 1, leftToken) - setOffset(rightParenToken, 0, leftParenToken) - processMaybeBlock(node.body, forToken) - }, - /** @param {ForStatement} node */ - ForStatement(node) { - const forToken = tokenStore.getFirstToken(node) - const leftParenToken = tokenStore.getTokenAfter(forToken) - const rightParenToken = tokenStore.getTokenBefore( - node.body, - isNotLeftParen - ) - - setOffset(leftParenToken, 1, forToken) - processNodeList( - [node.init, node.test, node.update], - leftParenToken, - rightParenToken, - 1 - ) - processMaybeBlock(node.body, forToken) - }, - /** @param {FunctionDeclaration | FunctionExpression} node */ - 'FunctionDeclaration, FunctionExpression'(node) { - const firstToken = tokenStore.getFirstToken(node) - if (isLeftParen(firstToken)) { - // Methods. - const leftToken = firstToken - const rightToken = tokenStore.getTokenAfter( - last(node.params) || leftToken, - isRightParen - ) - const bodyToken = tokenStore.getFirstToken(node.body) - - processNodeList(node.params, leftToken, rightToken, 1) - setOffset(bodyToken, 0, tokenStore.getFirstToken(node.parent)) - } else { - // Normal functions. - const functionToken = node.async - ? tokenStore.getTokenAfter(firstToken) - : firstToken - const starToken = node.generator - ? tokenStore.getTokenAfter(functionToken) - : null - const idToken = node.id && tokenStore.getFirstToken(node.id) - const leftToken = tokenStore.getTokenAfter( - idToken || starToken || functionToken - ) - const rightToken = tokenStore.getTokenAfter( - last(node.params) || leftToken, - isRightParen - ) - const bodyToken = tokenStore.getFirstToken(node.body) + const firstToken = tokenStore.getFirstToken(node) + /** @type {(Token|null)[]} */ + const tokens = [] - if (node.async) { - setOffset(functionToken, 0, firstToken) - } - if (node.generator) { - setOffset(starToken, 1, firstToken) - } - if (node.id != null) { - setOffset(idToken, 1, firstToken) - } - setOffset(leftToken, 1, firstToken) - processNodeList(node.params, leftToken, rightToken, 1) - setOffset(bodyToken, 0, firstToken) - } - }, - /** @param {IfStatement} node */ - IfStatement(node) { - const ifToken = tokenStore.getFirstToken(node) - const ifLeftParenToken = tokenStore.getTokenAfter(ifToken) - const ifRightParenToken = tokenStore.getTokenBefore( - node.consequent, - isRightParen - ) - - setOffset(ifLeftParenToken, 1, ifToken) - setOffset(ifRightParenToken, 0, ifLeftParenToken) - processMaybeBlock(node.consequent, ifToken) - - if (node.alternate != null) { - const elseToken = /** @type {Token} */ (tokenStore.getTokenAfter( - node.consequent, - isNotRightParen - )) - - setOffset(elseToken, 0, ifToken) - processMaybeBlock(node.alternate, elseToken) - } - }, - /** @param {ImportDeclaration} node */ - ImportDeclaration(node) { - const firstSpecifier = node.specifiers[0] - const secondSpecifier = node.specifiers[1] - const importToken = tokenStore.getFirstToken(node) - const hasSemi = tokenStore.getLastToken(node).value === ';' - /** @type {Token[]} */ - const tokens = [] // tokens to one indent - - if (!firstSpecifier) { - // There are 2 patterns: - // import "foo" - // import {} from "foo" - const secondToken = tokenStore.getFirstToken(node, 1) - if (isLeftBrace(secondToken)) { - setOffset( - [secondToken, tokenStore.getTokenAfter(secondToken)], - 0, - importToken - ) - tokens.push( - tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from - tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" - ) - } else { - tokens.push(tokenStore.getLastToken(node, hasSemi ? 1 : 0)) - } - } else if (firstSpecifier.type === 'ImportDefaultSpecifier') { - if ( - secondSpecifier && - secondSpecifier.type === 'ImportNamespaceSpecifier' + for (const filter of node.filters) { + tokens.push( + tokenStore.getTokenBefore(filter, isPipeOperator), + tokenStore.getFirstToken(filter), + ) + } + + setOffset(tokens, 1, firstToken) + }, + + VForExpression(node: VForExpression) { + const firstToken = tokenStore.getFirstToken(node) + const lastOfLeft = last(node.left) || firstToken + const inToken = tokenStore.getTokenAfter( + lastOfLeft, + isNotRightParen, + ) as Token + const rightToken = tokenStore.getFirstToken(node.right) + + if (isLeftParen(firstToken)) { + // eslint-disable-next-line no-shadow + const rightToken = tokenStore.getTokenAfter( + lastOfLeft, + isRightParen, + ) + processNodeList(node.left, firstToken, rightToken, 1) + } + setOffset(inToken, 1, firstToken) + setOffset(rightToken, 1, inToken) + }, + + VOnExpression(node: VOnExpression) { + processNodeList(node.body, null, null, 0) + }, + + VStartTag(node: VStartTag) { + const openToken = tokenStore.getFirstToken(node) + const closeToken = tokenStore.getLastToken(node) + + processNodeList( + node.attributes, + openToken, + null, + options.attribute, + options.alignAttributesVertically, + ) + if (closeToken != null && closeToken.type.endsWith("TagClose")) { + const offset = + closeToken.type !== "HTMLSelfClosingTagClose" + ? options.closeBracket.startTag + : options.closeBracket.selfClosingTag + setOffset(closeToken, offset, openToken) + } + }, + + VText(node: VText) { + const tokens = tokenStore.getTokens(node, isNotWhitespace) + const firstTokenInfo = offsets.get(tokenStore.getFirstToken(node)) + + for (const token of tokens) { + offsets.set(token, Object.assign({}, firstTokenInfo)) + } + }, + + "ArrayExpression, ArrayPattern"(node: ArrayExpression | ArrayPattern) { + processNodeList( + node.elements, + tokenStore.getFirstToken(node), + tokenStore.getLastToken(node), + 1, + ) + }, + + ArrowFunctionExpression(node: ArrowFunctionExpression) { + const firstToken = tokenStore.getFirstToken(node) + const secondToken = tokenStore.getTokenAfter(firstToken) + const leftToken = node.async ? secondToken : firstToken + const arrowToken = tokenStore.getTokenBefore(node.body, isArrow) + + if (node.async) { + setOffset(secondToken, 1, firstToken) + } + if (isLeftParen(leftToken)) { + const rightToken = tokenStore.getTokenAfter( + last(node.params) || leftToken, + isRightParen, + ) + processNodeList(node.params, leftToken, rightToken, 1) + } + + setOffset(arrowToken, 1, firstToken) + processMaybeBlock(node.body, firstToken) + }, + + "AssignmentExpression, AssignmentPattern, BinaryExpression, LogicalExpression"( + node: + | AssignmentExpression + | AssignmentPattern + | BinaryExpression + | LogicalExpression, ) { - // There is a pattern: - // import Foo, * as foo from "foo" - tokens.push( - tokenStore.getFirstToken(firstSpecifier), // Foo - tokenStore.getTokenAfter(firstSpecifier), // comma - tokenStore.getFirstToken(secondSpecifier), // * - tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from - tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" - ) - } else { - // There are 3 patterns: - // import Foo from "foo" - // import Foo, {} from "foo" - // import Foo, {a} from "foo" - const idToken = tokenStore.getFirstToken(firstSpecifier) - const nextToken = tokenStore.getTokenAfter(firstSpecifier) - if (isComma(nextToken)) { - const leftBrace = tokenStore.getTokenAfter(nextToken) - const rightBrace = tokenStore.getLastToken(node, hasSemi ? 3 : 2) - setOffset([idToken, nextToken], 1, importToken) - setOffset(leftBrace, 0, idToken) - processNodeList(node.specifiers.slice(1), leftBrace, rightBrace, 1) - tokens.push( - tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from - tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" + const leftToken = getChainHeadToken(node) + const opToken = tokenStore.getTokenAfter( + node.left, + isNotRightParen, + ) as Token + const rightToken = tokenStore.getTokenAfter(opToken) + const prevToken = tokenStore.getTokenBefore(leftToken) + const shouldIndent = + prevToken == null || + prevToken.loc.end.line === leftToken.loc.start.line || + isBeginningOfElement(leftToken, node) + + setOffset([opToken, rightToken], shouldIndent ? 1 : 0, leftToken) + }, + + "AwaitExpression, RestElement, SpreadElement, UnaryExpression"( + node: + | AwaitExpression + | RestElement + | SpreadElement + | UnaryExpression, + ) { + const firstToken = tokenStore.getFirstToken(node) + const nextToken = tokenStore.getTokenAfter(firstToken) + + setOffset(nextToken, 1, firstToken) + }, + + "BlockStatement, ClassBody"(node: BlockStatement | ClassBody) { + processNodeList( + node.body, + tokenStore.getFirstToken(node), + tokenStore.getLastToken(node), + 1, + ) + }, + + "BreakStatement, ContinueStatement, ReturnStatement, ThrowStatement"( + node: + | BreakStatement + | ContinueStatement + | ReturnStatement + | ThrowStatement, + ) { + if ( + ((node.type === "ReturnStatement" || + node.type === "ThrowStatement") && + node.argument != null) || + ((node.type === "BreakStatement" || + node.type === "ContinueStatement") && + node.label != null) + ) { + const firstToken = tokenStore.getFirstToken(node) + const nextToken = tokenStore.getTokenAfter(firstToken) + + setOffset(nextToken, 1, firstToken) + } + }, + + CallExpression(node: CallExpression) { + const firstToken = tokenStore.getFirstToken(node) + const rightToken = tokenStore.getLastToken(node) + const leftToken = tokenStore.getTokenAfter(node.callee, isLeftParen) + + setOffset(leftToken, 1, firstToken) + processNodeList(node.arguments, leftToken, rightToken, 1) + }, + + ImportExpression(node: ImportExpression) { + const firstToken = tokenStore.getFirstToken(node) + const rightToken = tokenStore.getLastToken(node) + const leftToken = tokenStore.getTokenAfter(firstToken, isLeftParen) + + setOffset(leftToken, 1, firstToken) + processNodeList([node.source], leftToken, rightToken, 1) + }, + + CatchClause(node: CatchClause) { + const firstToken = tokenStore.getFirstToken(node) + const bodyToken = tokenStore.getFirstToken(node.body) + + if (node.param != null) { + const leftToken = tokenStore.getTokenAfter(firstToken) + const rightToken = tokenStore.getTokenAfter(node.param) + + setOffset(leftToken, 1, firstToken) + processNodeList([node.param], leftToken, rightToken, 1) + } + setOffset(bodyToken, 0, firstToken) + }, + + "ClassDeclaration, ClassExpression"( + node: ClassDeclaration | ClassExpression, + ) { + const firstToken = tokenStore.getFirstToken(node) + const bodyToken = tokenStore.getFirstToken(node.body) + + if (node.id != null) { + setOffset(tokenStore.getFirstToken(node.id), 1, firstToken) + } + if (node.superClass != null) { + const extendsToken = tokenStore.getTokenAfter( + node.id || firstToken, + ) + const superClassToken = tokenStore.getTokenAfter(extendsToken) + setOffset(extendsToken, 1, firstToken) + setOffset(superClassToken, 1, extendsToken) + } + setOffset(bodyToken, 0, firstToken) + }, + + ConditionalExpression(node: ConditionalExpression) { + const prevToken = tokenStore.getTokenBefore(node) + const firstToken = tokenStore.getFirstToken(node) + const questionToken = tokenStore.getTokenAfter( + node.test, + isNotRightParen, + ) as Token + const consequentToken = tokenStore.getTokenAfter(questionToken) + const colonToken = tokenStore.getTokenAfter( + node.consequent, + isNotRightParen, + ) as Token + const alternateToken = tokenStore.getTokenAfter(colonToken) + const isFlat = + prevToken && + prevToken.loc.end.line !== node.loc.start.line && + node.test.loc.end.line === node.consequent.loc.start.line + + if (isFlat) { + setOffset( + [ + questionToken, + consequentToken, + colonToken, + alternateToken, + ], + 0, + firstToken, + ) + } else { + setOffset([questionToken, colonToken], 1, firstToken) + setOffset([consequentToken, alternateToken], 1, questionToken) + } + }, + + DoWhileStatement(node: DoWhileStatement) { + const doToken = tokenStore.getFirstToken(node) + const whileToken = tokenStore.getTokenAfter( + node.body, + isNotRightParen, + ) as Token + const leftToken = tokenStore.getTokenAfter(whileToken) + const testToken = tokenStore.getTokenAfter(leftToken) + const lastToken = tokenStore.getLastToken(node) + const rightToken = isSemicolon(lastToken) + ? tokenStore.getTokenBefore(lastToken) + : lastToken + + processMaybeBlock(node.body, doToken) + setOffset(whileToken, 0, doToken) + setOffset(leftToken, 1, whileToken) + setOffset(testToken, 1, leftToken) + setOffset(rightToken, 0, leftToken) + }, + + ExportAllDeclaration(node: ExportAllDeclaration) { + const tokens = tokenStore.getTokens(node) + const firstToken = tokens.shift() as Token + if (isSemicolon(last(tokens))) { + tokens.pop() + } + if (!node.exported) { + setOffset(tokens, 1, firstToken) + } else { + // export * as foo from "mod" + const starToken = tokens.find(isWildcard) as Token + const asToken = tokenStore.getTokenAfter(starToken) + const exportedToken = tokenStore.getTokenAfter(asToken) + const afterTokens = tokens.slice( + tokens.indexOf(exportedToken) + 1, + ) + + setOffset(starToken, 1, firstToken) + setOffset(asToken, 1, starToken) + setOffset(exportedToken, 1, starToken) + setOffset(afterTokens, 1, firstToken) + } + }, + + ExportDefaultDeclaration(node: ExportDefaultDeclaration) { + const exportToken = tokenStore.getFirstToken(node) + const defaultToken = tokenStore.getFirstToken(node, 1) + const declarationToken = getFirstAndLastTokens(node.declaration) + .firstToken + setOffset([defaultToken, declarationToken], 1, exportToken) + }, + + ExportNamedDeclaration(node: ExportNamedDeclaration) { + const exportToken = tokenStore.getFirstToken(node) + if (node.declaration) { + // export var foo = 1; + const declarationToken = tokenStore.getFirstToken(node, 1) + setOffset(declarationToken, 1, exportToken) + } else { + const firstSpecifier = node.specifiers[0] + if ( + !firstSpecifier || + firstSpecifier.type === "ExportSpecifier" + ) { + // export {foo, bar}; or export {foo, bar} from "mod"; + const leftParenToken = tokenStore.getFirstToken(node, 1) + const rightParenToken = tokenStore.getLastToken( + node, + isRightBrace, + ) as Token + setOffset(leftParenToken, 0, exportToken) + processNodeList( + node.specifiers, + leftParenToken, + rightParenToken, + 1, + ) + + const maybeFromToken = tokenStore.getTokenAfter( + rightParenToken, + ) + if ( + maybeFromToken != null && + sourceCode.getText(maybeFromToken) === "from" + ) { + const fromToken = maybeFromToken + const nameToken = tokenStore.getTokenAfter(fromToken) + setOffset([fromToken, nameToken], 1, exportToken) + } + } else { + // maybe babel-eslint + } + } + }, + + ExportSpecifier(node: ExportSpecifier) { + const tokens = tokenStore.getTokens(node) + const firstToken = tokens.shift() as Token + setOffset(tokens, 1, firstToken) + }, + + "ForInStatement, ForOfStatement"( + node: ForInStatement | ForOfStatement, + ) { + const forToken = tokenStore.getFirstToken(node) + const awaitToken = + (node.type === "ForOfStatement" && + node.await && + tokenStore.getTokenAfter(forToken)) || + null + const leftParenToken = tokenStore.getTokenAfter( + awaitToken || forToken, ) - } else { - tokens.push( - idToken, - nextToken, // from - tokenStore.getTokenAfter(nextToken) // "foo" + const leftToken = tokenStore.getTokenAfter(leftParenToken) + const inToken = tokenStore.getTokenAfter( + leftToken, + isNotRightParen, + ) as Token + const rightToken = tokenStore.getTokenAfter(inToken) + const rightParenToken = tokenStore.getTokenBefore( + node.body, + isNotLeftParen, ) - } - } - } else if (firstSpecifier.type === 'ImportNamespaceSpecifier') { - // There is a pattern: - // import * as foo from "foo" - tokens.push( - tokenStore.getFirstToken(firstSpecifier), // * - tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from - tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" - ) - } else { - // There is a pattern: - // import {a} from "foo" - const leftBrace = tokenStore.getFirstToken(node, 1) - const rightBrace = tokenStore.getLastToken(node, hasSemi ? 3 : 2) - setOffset(leftBrace, 0, importToken) - processNodeList(node.specifiers, leftBrace, rightBrace, 1) - tokens.push( - tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from - tokenStore.getLastToken(node, hasSemi ? 1 : 0) // "foo" - ) - } - - setOffset(tokens, 1, importToken) - }, - /** @param {ImportSpecifier} node */ - ImportSpecifier(node) { - if (node.local.range[0] !== node.imported.range[0]) { - const tokens = tokenStore.getTokens(node) - const firstToken = /** @type {Token} */ (tokens.shift()) - setOffset(tokens, 1, firstToken) - } - }, - /** @param {ImportNamespaceSpecifier} node */ - ImportNamespaceSpecifier(node) { - const tokens = tokenStore.getTokens(node) - const firstToken = /** @type {Token} */ (tokens.shift()) - setOffset(tokens, 1, firstToken) - }, - /** @param {LabeledStatement} node */ - LabeledStatement(node) { - const labelToken = tokenStore.getFirstToken(node) - const colonToken = tokenStore.getTokenAfter(labelToken) - const bodyToken = tokenStore.getTokenAfter(colonToken) - - setOffset([colonToken, bodyToken], 1, labelToken) - }, - /** @param {MemberExpression | MetaProperty} node */ - 'MemberExpression, MetaProperty'(node) { - const objectToken = tokenStore.getFirstToken(node) - if (node.type === 'MemberExpression' && node.computed) { - const leftBracketToken = /** @type {Token} */ (tokenStore.getTokenBefore( - node.property, - isLeftBracket - )) - const propertyToken = tokenStore.getTokenAfter(leftBracketToken) - const rightBracketToken = tokenStore.getTokenAfter( - node.property, - isRightBracket - ) - setOffset(leftBracketToken, 1, objectToken) - setOffset(propertyToken, 1, leftBracketToken) - setOffset(rightBracketToken, 0, leftBracketToken) - } else { - const dotToken = tokenStore.getTokenBefore(node.property) - const propertyToken = tokenStore.getTokenAfter(dotToken) - - setOffset([dotToken, propertyToken], 1, objectToken) - } - }, - /** @param {MethodDefinition | Property} node */ - 'MethodDefinition, Property'(node) { - const isMethod = node.type === 'MethodDefinition' || node.method === true - const prefixTokens = getPrefixTokens(node) - const hasPrefix = prefixTokens.length >= 1 - - for (let i = 1; i < prefixTokens.length; ++i) { - setOffset(prefixTokens[i], 0, prefixTokens[i - 1]) - } - - /** @type {Token} */ - let lastKeyToken - if (node.computed) { - const keyLeftToken = /** @type {Token} */ (tokenStore.getFirstToken( - node, - isLeftBracket - )) - const keyToken = tokenStore.getTokenAfter(keyLeftToken) - const keyRightToken = (lastKeyToken = /** @type {Token} */ (tokenStore.getTokenAfter( - node.key, - isRightBracket - ))) - - if (hasPrefix) { - setOffset(keyLeftToken, 0, /** @type {Token} */ (last(prefixTokens))) - } - setOffset(keyToken, 1, keyLeftToken) - setOffset(keyRightToken, 0, keyLeftToken) - } else { - const idToken = (lastKeyToken = tokenStore.getFirstToken(node.key)) + if (awaitToken != null) { + setOffset(awaitToken, 0, forToken) + } + setOffset(leftParenToken, 1, forToken) + setOffset(leftToken, 1, leftParenToken) + setOffset(inToken, 1, leftToken) + setOffset(rightToken, 1, leftToken) + setOffset(rightParenToken, 0, leftParenToken) + processMaybeBlock(node.body, forToken) + }, - if (hasPrefix) { - setOffset(idToken, 0, /** @type {Token} */ (last(prefixTokens))) - } - } - - if (isMethod) { - const leftParenToken = tokenStore.getTokenAfter(lastKeyToken) - - setOffset(leftParenToken, 1, lastKeyToken) - } else if (node.type === 'Property' && !node.shorthand) { - const colonToken = tokenStore.getTokenAfter(lastKeyToken) - const valueToken = tokenStore.getTokenAfter(colonToken) - - setOffset([colonToken, valueToken], 1, lastKeyToken) - } - }, - /** @param {NewExpression} node */ - NewExpression(node) { - const newToken = tokenStore.getFirstToken(node) - const calleeToken = tokenStore.getTokenAfter(newToken) - const rightToken = tokenStore.getLastToken(node) - const leftToken = isRightParen(rightToken) - ? tokenStore.getFirstTokenBetween(node.callee, rightToken, isLeftParen) - : null - - setOffset(calleeToken, 1, newToken) - if (leftToken != null) { - setOffset(leftToken, 1, calleeToken) - processNodeList(node.arguments, leftToken, rightToken, 1) - } - }, - /** @param {ObjectExpression | ObjectPattern} node */ - 'ObjectExpression, ObjectPattern'(node) { - processNodeList( - node.properties, - tokenStore.getFirstToken(node), - tokenStore.getLastToken(node), - 1 - ) - }, - /** @param {SequenceExpression} node */ - SequenceExpression(node) { - processNodeList(node.expressions, null, null, 0) - }, - /** @param {SwitchCase} node */ - SwitchCase(node) { - const caseToken = tokenStore.getFirstToken(node) - - if (node.test != null) { - const testToken = tokenStore.getTokenAfter(caseToken) - const colonToken = tokenStore.getTokenAfter(node.test, isNotRightParen) - - setOffset([testToken, colonToken], 1, caseToken) - } else { - const colonToken = tokenStore.getTokenAfter(caseToken) - - setOffset(colonToken, 1, caseToken) - } - - if ( - node.consequent.length === 1 && - node.consequent[0].type === 'BlockStatement' - ) { - setOffset(tokenStore.getFirstToken(node.consequent[0]), 0, caseToken) - } else if (node.consequent.length >= 1) { - setOffset(tokenStore.getFirstToken(node.consequent[0]), 1, caseToken) - processNodeList(node.consequent, null, null, 0) - } - }, - /** @param {SwitchStatement} node */ - SwitchStatement(node) { - const switchToken = tokenStore.getFirstToken(node) - const leftParenToken = tokenStore.getTokenAfter(switchToken) - const discriminantToken = tokenStore.getTokenAfter(leftParenToken) - const leftBraceToken = /** @type {Token} */ (tokenStore.getTokenAfter( - node.discriminant, - isLeftBrace - )) - const rightParenToken = tokenStore.getTokenBefore(leftBraceToken) - const rightBraceToken = tokenStore.getLastToken(node) - - setOffset(leftParenToken, 1, switchToken) - setOffset(discriminantToken, 1, leftParenToken) - setOffset(rightParenToken, 0, leftParenToken) - setOffset(leftBraceToken, 0, switchToken) - processNodeList( - node.cases, - leftBraceToken, - rightBraceToken, - options.switchCase - ) - }, - /** @param {TaggedTemplateExpression} node */ - TaggedTemplateExpression(node) { - const tagTokens = getFirstAndLastTokens(node.tag, node.range[0]) - const quasiToken = tokenStore.getTokenAfter(tagTokens.lastToken) - - setOffset(quasiToken, 1, tagTokens.firstToken) - }, - /** @param {TemplateLiteral} node */ - TemplateLiteral(node) { - const firstToken = tokenStore.getFirstToken(node) - const quasiTokens = node.quasis - .slice(1) - .map((n) => tokenStore.getFirstToken(n)) - const expressionToken = node.quasis - .slice(0, -1) - .map((n) => tokenStore.getTokenAfter(n)) - - setOffset(quasiTokens, 0, firstToken) - setOffset(expressionToken, 1, firstToken) - }, - /** @param {TryStatement} node */ - TryStatement(node) { - const tryToken = tokenStore.getFirstToken(node) - const tryBlockToken = tokenStore.getFirstToken(node.block) - - setOffset(tryBlockToken, 0, tryToken) - - if (node.handler != null) { - const catchToken = tokenStore.getFirstToken(node.handler) - - setOffset(catchToken, 0, tryToken) - } - - if (node.finalizer != null) { - const finallyToken = tokenStore.getTokenBefore(node.finalizer) - const finallyBlockToken = tokenStore.getFirstToken(node.finalizer) - - setOffset([finallyToken, finallyBlockToken], 0, tryToken) - } - }, - /** @param {UpdateExpression} node */ - UpdateExpression(node) { - const firstToken = tokenStore.getFirstToken(node) - const nextToken = tokenStore.getTokenAfter(firstToken) - - setOffset(nextToken, 1, firstToken) - }, - /** @param {VariableDeclaration} node */ - VariableDeclaration(node) { - processNodeList( - node.declarations, - tokenStore.getFirstToken(node), - null, - 1 - ) - }, - /** @param {VariableDeclarator} node */ - VariableDeclarator(node) { - if (node.init != null) { - const idToken = tokenStore.getFirstToken(node) - const eqToken = tokenStore.getTokenAfter(node.id) - const initToken = tokenStore.getTokenAfter(eqToken) - - setOffset([eqToken, initToken], 1, idToken) - } - }, - /** @param {WhileStatement | WithStatement} node */ - 'WhileStatement, WithStatement'(node) { - const firstToken = tokenStore.getFirstToken(node) - const leftParenToken = tokenStore.getTokenAfter(firstToken) - const rightParenToken = tokenStore.getTokenBefore(node.body, isRightParen) - - setOffset(leftParenToken, 1, firstToken) - setOffset(rightParenToken, 0, leftParenToken) - processMaybeBlock(node.body, firstToken) - }, - /** @param {YieldExpression} node */ - YieldExpression(node) { - if (node.argument != null) { - const yieldToken = tokenStore.getFirstToken(node) - - setOffset(tokenStore.getTokenAfter(yieldToken), 1, yieldToken) - if (node.delegate) { - setOffset(tokenStore.getTokenAfter(yieldToken, 1), 1, yieldToken) - } - } - }, - /** @param {Statement} node */ - // Process semicolons. - ':statement'(node) { - const firstToken = tokenStore.getFirstToken(node) - const lastToken = tokenStore.getLastToken(node) - if (isSemicolon(lastToken) && firstToken !== lastToken) { - setOffset(lastToken, 0, firstToken) - } - - // Set to the semicolon of the previous token for semicolon-free style. - // E.g., - // foo - // ;[1,2,3].forEach(f) - const info = offsets.get(firstToken) - const prevToken = tokenStore.getTokenBefore(firstToken) - if ( - info != null && - isSemicolon(prevToken) && - prevToken.loc.end.line === firstToken.loc.start.line - ) { - offsets.set(prevToken, info) - } - }, - /** @param {Expression | MetaProperty | TemplateLiteral} node */ - // Process parentheses. - // `:expression` does not match with MetaProperty and TemplateLiteral as a bug: https://github.com/estools/esquery/pull/59 - ':expression, MetaProperty, TemplateLiteral'(node) { - let leftToken = tokenStore.getTokenBefore(node) - let rightToken = tokenStore.getTokenAfter(node) - let firstToken = tokenStore.getFirstToken(node) - - while (isLeftParen(leftToken) && isRightParen(rightToken)) { - setOffset(firstToken, 1, leftToken) - setOffset(rightToken, 0, leftToken) - - firstToken = leftToken - leftToken = tokenStore.getTokenBefore(leftToken) - rightToken = tokenStore.getTokenAfter(rightToken) - } - }, - /** @param {ASTNode} node */ - // Ignore tokens of unknown nodes. - '*:exit'(node) { - if ( - !KNOWN_NODES.has(node.type) && - !NON_STANDARD_KNOWN_NODES.has(node.type) - ) { - ignore(node) - } - }, - /** @param {Program} node */ - // Top-level process. - Program(node) { - const firstToken = node.tokens[0] - const isScriptTag = - firstToken != null && - firstToken.type === 'Punctuator' && - firstToken.value === '<script>' - const baseIndent = isScriptTag - ? options.indentSize * options.baseIndent - : 0 - - for (const statement of node.body) { - processTopLevelNode(statement, baseIndent) - } - }, - /** @param {VElement} node */ - "VElement[parent.type!='VElement']"(node) { - processTopLevelNode(node, 0) - }, - /** @param {Program | VElement} node */ - // Do validation. - ":matches(Program, VElement[parent.type!='VElement']):exit"(node) { - let comments = [] - /** @type {Token[]} */ - let tokensOnSameLine = [] - let isBesideMultilineToken = false - let lastValidatedToken = null - - // Validate indentation of tokens. - for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) { - if ( - tokensOnSameLine.length === 0 || - tokensOnSameLine[0].loc.start.line === token.loc.start.line + ForStatement(node: ForStatement) { + const forToken = tokenStore.getFirstToken(node) + const leftParenToken = tokenStore.getTokenAfter(forToken) + const rightParenToken = tokenStore.getTokenBefore( + node.body, + isNotLeftParen, + ) + + setOffset(leftParenToken, 1, forToken) + processNodeList( + [node.init, node.test, node.update], + leftParenToken, + rightParenToken, + 1, + ) + processMaybeBlock(node.body, forToken) + }, + + "FunctionDeclaration, FunctionExpression"( + node: FunctionDeclaration | FunctionExpression, ) { - // This is on the same line (or the first token). - tokensOnSameLine.push(token) - } else if (tokensOnSameLine.every(isComment)) { - // New line is detected, but the all tokens of the previous line are comment. - // Comment lines are adjusted to the next code line. - comments.push(tokensOnSameLine[0]) - isBesideMultilineToken = - /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line === - token.loc.start.line - tokensOnSameLine = [token] - } else { - // New line is detected, so validate the tokens. - if (!isBesideMultilineToken) { - validate(tokensOnSameLine, comments, lastValidatedToken) - lastValidatedToken = tokensOnSameLine[0] - } - isBesideMultilineToken = - /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line === - token.loc.start.line - tokensOnSameLine = [token] - comments = [] - } - } - if (tokensOnSameLine.length >= 1 && tokensOnSameLine.some(isNotComment)) { - validate(tokensOnSameLine, comments, lastValidatedToken) - } - } - }) + const firstToken = tokenStore.getFirstToken(node) + if (isLeftParen(firstToken)) { + // Methods. + const leftToken = firstToken + const rightToken = tokenStore.getTokenAfter( + last(node.params) || leftToken, + isRightParen, + ) + const bodyToken = tokenStore.getFirstToken(node.body) + + processNodeList(node.params, leftToken, rightToken, 1) + setOffset(bodyToken, 0, tokenStore.getFirstToken(node.parent)) + } else { + // Normal functions. + const functionToken = node.async + ? tokenStore.getTokenAfter(firstToken) + : firstToken + const starToken = node.generator + ? tokenStore.getTokenAfter(functionToken) + : null + const idToken = node.id && tokenStore.getFirstToken(node.id) + const leftToken = tokenStore.getTokenAfter( + idToken || starToken || functionToken, + ) + const rightToken = tokenStore.getTokenAfter( + last(node.params) || leftToken, + isRightParen, + ) + const bodyToken = tokenStore.getFirstToken(node.body) + + if (node.async) { + setOffset(functionToken, 0, firstToken) + } + if (node.generator) { + setOffset(starToken, 1, firstToken) + } + if (node.id != null) { + setOffset(idToken, 1, firstToken) + } + setOffset(leftToken, 1, firstToken) + processNodeList(node.params, leftToken, rightToken, 1) + setOffset(bodyToken, 0, firstToken) + } + }, + + IfStatement(node: IfStatement) { + const ifToken = tokenStore.getFirstToken(node) + const ifLeftParenToken = tokenStore.getTokenAfter(ifToken) + const ifRightParenToken = tokenStore.getTokenBefore( + node.consequent, + isRightParen, + ) + + setOffset(ifLeftParenToken, 1, ifToken) + setOffset(ifRightParenToken, 0, ifLeftParenToken) + processMaybeBlock(node.consequent, ifToken) + + if (node.alternate != null) { + const elseToken = tokenStore.getTokenAfter( + node.consequent, + isNotRightParen, + ) as Token + + setOffset(elseToken, 0, ifToken) + processMaybeBlock(node.alternate, elseToken) + } + }, + + ImportDeclaration(node: ImportDeclaration) { + const firstSpecifier = node.specifiers[0] + const secondSpecifier = node.specifiers[1] + const importToken = tokenStore.getFirstToken(node) + const hasSemi = tokenStore.getLastToken(node).value === ";" + + const tokens: Token[] = [] // tokens to one indent + + if (!firstSpecifier) { + // There are 2 patterns: + // import "foo" + // import {} from "foo" + const secondToken = tokenStore.getFirstToken(node, 1) + if (isLeftBrace(secondToken)) { + setOffset( + [secondToken, tokenStore.getTokenAfter(secondToken)], + 0, + importToken, + ) + tokens.push( + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0), // "foo" + ) + } else { + tokens.push(tokenStore.getLastToken(node, hasSemi ? 1 : 0)) + } + } else if (firstSpecifier.type === "ImportDefaultSpecifier") { + if ( + secondSpecifier && + secondSpecifier.type === "ImportNamespaceSpecifier" + ) { + // There is a pattern: + // import Foo, * as foo from "foo" + tokens.push( + tokenStore.getFirstToken(firstSpecifier), // Foo + tokenStore.getTokenAfter(firstSpecifier), // comma + tokenStore.getFirstToken(secondSpecifier), // * + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0), // "foo" + ) + } else { + // There are 3 patterns: + // import Foo from "foo" + // import Foo, {} from "foo" + // import Foo, {a} from "foo" + const idToken = tokenStore.getFirstToken(firstSpecifier) + const nextToken = tokenStore.getTokenAfter(firstSpecifier) + if (isComma(nextToken)) { + const leftBrace = tokenStore.getTokenAfter(nextToken) + const rightBrace = tokenStore.getLastToken( + node, + hasSemi ? 3 : 2, + ) + setOffset([idToken, nextToken], 1, importToken) + setOffset(leftBrace, 0, idToken) + processNodeList( + node.specifiers.slice(1), + leftBrace, + rightBrace, + 1, + ) + tokens.push( + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0), // "foo" + ) + } else { + tokens.push( + idToken, + nextToken, // from + tokenStore.getTokenAfter(nextToken), // "foo" + ) + } + } + } else if (firstSpecifier.type === "ImportNamespaceSpecifier") { + // There is a pattern: + // import * as foo from "foo" + tokens.push( + tokenStore.getFirstToken(firstSpecifier), // * + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0), // "foo" + ) + } else { + // There is a pattern: + // import {a} from "foo" + const leftBrace = tokenStore.getFirstToken(node, 1) + const rightBrace = tokenStore.getLastToken( + node, + hasSemi ? 3 : 2, + ) + setOffset(leftBrace, 0, importToken) + processNodeList(node.specifiers, leftBrace, rightBrace, 1) + tokens.push( + tokenStore.getLastToken(node, hasSemi ? 2 : 1), // from + tokenStore.getLastToken(node, hasSemi ? 1 : 0), // "foo" + ) + } + + setOffset(tokens, 1, importToken) + }, + + ImportSpecifier(node: ImportSpecifier) { + if (node.local.range[0] !== node.imported.range[0]) { + const tokens = tokenStore.getTokens(node) + const firstToken = tokens.shift() as Token + setOffset(tokens, 1, firstToken) + } + }, + + ImportNamespaceSpecifier(node: ImportNamespaceSpecifier) { + const tokens = tokenStore.getTokens(node) + const firstToken = tokens.shift() as Token + setOffset(tokens, 1, firstToken) + }, + + LabeledStatement(node: LabeledStatement) { + const labelToken = tokenStore.getFirstToken(node) + const colonToken = tokenStore.getTokenAfter(labelToken) + const bodyToken = tokenStore.getTokenAfter(colonToken) + + setOffset([colonToken, bodyToken], 1, labelToken) + }, + + "MemberExpression, MetaProperty"( + node: MemberExpression | MetaProperty, + ) { + const objectToken = tokenStore.getFirstToken(node) + if (node.type === "MemberExpression" && node.computed) { + const leftBracketToken = tokenStore.getTokenBefore( + node.property, + isLeftBracket, + ) as Token + const propertyToken = tokenStore.getTokenAfter(leftBracketToken) + const rightBracketToken = tokenStore.getTokenAfter( + node.property, + isRightBracket, + ) + + setOffset(leftBracketToken, 1, objectToken) + setOffset(propertyToken, 1, leftBracketToken) + setOffset(rightBracketToken, 0, leftBracketToken) + } else { + const dotToken = tokenStore.getTokenBefore(node.property) + const propertyToken = tokenStore.getTokenAfter(dotToken) + + setOffset([dotToken, propertyToken], 1, objectToken) + } + }, + + "MethodDefinition, Property"(node: MethodDefinition | Property) { + const isMethod = + node.type === "MethodDefinition" || node.method === true + const prefixTokens = getPrefixTokens(node) + const hasPrefix = prefixTokens.length >= 1 + + for (let i = 1; i < prefixTokens.length; ++i) { + setOffset(prefixTokens[i], 0, prefixTokens[i - 1]) + } + + let lastKeyToken: Token + if (node.computed) { + const keyLeftToken = tokenStore.getFirstToken( + node, + isLeftBracket, + ) as Token + const keyToken = tokenStore.getTokenAfter(keyLeftToken) + const keyRightToken = (lastKeyToken = tokenStore.getTokenAfter( + node.key, + isRightBracket, + ) as Token) + + if (hasPrefix) { + setOffset(keyLeftToken, 0, last(prefixTokens) as Token) + } + setOffset(keyToken, 1, keyLeftToken) + setOffset(keyRightToken, 0, keyLeftToken) + } else { + const idToken = (lastKeyToken = tokenStore.getFirstToken( + node.key, + )) + + if (hasPrefix) { + setOffset(idToken, 0, last(prefixTokens) as Token) + } + } + + if (isMethod) { + const leftParenToken = tokenStore.getTokenAfter(lastKeyToken) + + setOffset(leftParenToken, 1, lastKeyToken) + } else if (node.type === "Property" && !node.shorthand) { + const colonToken = tokenStore.getTokenAfter(lastKeyToken) + const valueToken = tokenStore.getTokenAfter(colonToken) + + setOffset([colonToken, valueToken], 1, lastKeyToken) + } + }, + + NewExpression(node: NewExpression) { + const newToken = tokenStore.getFirstToken(node) + const calleeToken = tokenStore.getTokenAfter(newToken) + const rightToken = tokenStore.getLastToken(node) + const leftToken = isRightParen(rightToken) + ? tokenStore.getFirstTokenBetween( + node.callee, + rightToken, + isLeftParen, + ) + : null + + setOffset(calleeToken, 1, newToken) + if (leftToken != null) { + setOffset(leftToken, 1, calleeToken) + processNodeList(node.arguments, leftToken, rightToken, 1) + } + }, + + "ObjectExpression, ObjectPattern"( + node: ObjectExpression | ObjectPattern, + ) { + processNodeList( + node.properties, + tokenStore.getFirstToken(node), + tokenStore.getLastToken(node), + 1, + ) + }, + + SequenceExpression(node: SequenceExpression) { + processNodeList(node.expressions, null, null, 0) + }, + + SwitchCase(node: SwitchCase) { + const caseToken = tokenStore.getFirstToken(node) + + if (node.test != null) { + const testToken = tokenStore.getTokenAfter(caseToken) + const colonToken = tokenStore.getTokenAfter( + node.test, + isNotRightParen, + ) + + setOffset([testToken, colonToken], 1, caseToken) + } else { + const colonToken = tokenStore.getTokenAfter(caseToken) + + setOffset(colonToken, 1, caseToken) + } + + if ( + node.consequent.length === 1 && + node.consequent[0].type === "BlockStatement" + ) { + setOffset( + tokenStore.getFirstToken(node.consequent[0]), + 0, + caseToken, + ) + } else if (node.consequent.length >= 1) { + setOffset( + tokenStore.getFirstToken(node.consequent[0]), + 1, + caseToken, + ) + processNodeList(node.consequent, null, null, 0) + } + }, + + SwitchStatement(node: SwitchStatement) { + const switchToken = tokenStore.getFirstToken(node) + const leftParenToken = tokenStore.getTokenAfter(switchToken) + const discriminantToken = tokenStore.getTokenAfter(leftParenToken) + const leftBraceToken = tokenStore.getTokenAfter( + node.discriminant, + isLeftBrace, + ) as Token + const rightParenToken = tokenStore.getTokenBefore(leftBraceToken) + const rightBraceToken = tokenStore.getLastToken(node) + + setOffset(leftParenToken, 1, switchToken) + setOffset(discriminantToken, 1, leftParenToken) + setOffset(rightParenToken, 0, leftParenToken) + setOffset(leftBraceToken, 0, switchToken) + processNodeList( + node.cases, + leftBraceToken, + rightBraceToken, + options.switchCase, + ) + }, + + TaggedTemplateExpression(node: TaggedTemplateExpression) { + const tagTokens = getFirstAndLastTokens(node.tag, node.range[0]) + const quasiToken = tokenStore.getTokenAfter(tagTokens.lastToken) + + setOffset(quasiToken, 1, tagTokens.firstToken) + }, + + TemplateLiteral(node: TemplateLiteral) { + const firstToken = tokenStore.getFirstToken(node) + const quasiTokens = node.quasis + .slice(1) + .map((n) => tokenStore.getFirstToken(n)) + const expressionToken = node.quasis + .slice(0, -1) + .map((n) => tokenStore.getTokenAfter(n)) + + setOffset(quasiTokens, 0, firstToken) + setOffset(expressionToken, 1, firstToken) + }, + + TryStatement(node: TryStatement) { + const tryToken = tokenStore.getFirstToken(node) + const tryBlockToken = tokenStore.getFirstToken(node.block) + + setOffset(tryBlockToken, 0, tryToken) + + if (node.handler != null) { + const catchToken = tokenStore.getFirstToken(node.handler) + + setOffset(catchToken, 0, tryToken) + } + + if (node.finalizer != null) { + const finallyToken = tokenStore.getTokenBefore(node.finalizer) + const finallyBlockToken = tokenStore.getFirstToken( + node.finalizer, + ) + + setOffset([finallyToken, finallyBlockToken], 0, tryToken) + } + }, + + UpdateExpression(node: UpdateExpression) { + const firstToken = tokenStore.getFirstToken(node) + const nextToken = tokenStore.getTokenAfter(firstToken) + + setOffset(nextToken, 1, firstToken) + }, + + VariableDeclaration(node: VariableDeclaration) { + processNodeList( + node.declarations, + tokenStore.getFirstToken(node), + null, + 1, + ) + }, + + VariableDeclarator(node: VariableDeclarator) { + if (node.init != null) { + const idToken = tokenStore.getFirstToken(node) + const eqToken = tokenStore.getTokenAfter(node.id) + const initToken = tokenStore.getTokenAfter(eqToken) + + setOffset([eqToken, initToken], 1, idToken) + } + }, + + "WhileStatement, WithStatement"(node: WhileStatement | WithStatement) { + const firstToken = tokenStore.getFirstToken(node) + const leftParenToken = tokenStore.getTokenAfter(firstToken) + const rightParenToken = tokenStore.getTokenBefore( + node.body, + isRightParen, + ) + + setOffset(leftParenToken, 1, firstToken) + setOffset(rightParenToken, 0, leftParenToken) + processMaybeBlock(node.body, firstToken) + }, + + YieldExpression(node: YieldExpression) { + if (node.argument != null) { + const yieldToken = tokenStore.getFirstToken(node) + + setOffset(tokenStore.getTokenAfter(yieldToken), 1, yieldToken) + if (node.delegate) { + setOffset( + tokenStore.getTokenAfter(yieldToken, 1), + 1, + yieldToken, + ) + } + } + }, + + // Process semicolons. + ":statement"(node: Statement) { + const firstToken = tokenStore.getFirstToken(node) + const lastToken = tokenStore.getLastToken(node) + if (isSemicolon(lastToken) && firstToken !== lastToken) { + setOffset(lastToken, 0, firstToken) + } + + // Set to the semicolon of the previous token for semicolon-free style. + // E.g., + // foo + // ;[1,2,3].forEach(f) + const info = offsets.get(firstToken) + const prevToken = tokenStore.getTokenBefore(firstToken) + if ( + info != null && + isSemicolon(prevToken) && + prevToken.loc.end.line === firstToken.loc.start.line + ) { + offsets.set(prevToken, info) + } + }, + + // Process parentheses. + // `:expression` does not match with MetaProperty and TemplateLiteral as a bug: https://github.com/estools/esquery/pull/59 + ":expression, MetaProperty, TemplateLiteral"( + node: Expression | MetaProperty | TemplateLiteral, + ) { + let leftToken = tokenStore.getTokenBefore(node) + let rightToken = tokenStore.getTokenAfter(node) + let firstToken = tokenStore.getFirstToken(node) + + while (isLeftParen(leftToken) && isRightParen(rightToken)) { + setOffset(firstToken, 1, leftToken) + setOffset(rightToken, 0, leftToken) + + firstToken = leftToken + leftToken = tokenStore.getTokenBefore(leftToken) + rightToken = tokenStore.getTokenAfter(rightToken) + } + }, + + // Ignore tokens of unknown nodes. + "*:exit"(node: ASTNode) { + if ( + !KNOWN_NODES.has(node.type) && + !NON_STANDARD_KNOWN_NODES.has(node.type) + ) { + ignore(node) + } + }, + + // Top-level process. + Program(node: Program) { + const firstToken = node.tokens[0] + const isScriptTag = + firstToken != null && + firstToken.type === "Punctuator" && + firstToken.value === "<script>" + const baseIndent = isScriptTag + ? options.indentSize * options.baseIndent + : 0 + + for (const statement of node.body) { + processTopLevelNode(statement, baseIndent) + } + }, + + "VElement[parent.type!='VElement']"(node: VElement) { + processTopLevelNode(node, 0) + }, + + // Do validation. + ":matches(Program, VElement[parent.type!='VElement']):exit"( + node: Program | VElement, + ) { + let comments = [] + let tokensOnSameLine: Token[] = [] + let isBesideMultilineToken = false + let lastValidatedToken = null + + // Validate indentation of tokens. + for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) { + if ( + tokensOnSameLine.length === 0 || + tokensOnSameLine[0].loc.start.line === token.loc.start.line + ) { + // This is on the same line (or the first token). + tokensOnSameLine.push(token) + } else if (tokensOnSameLine.every(isComment)) { + // New line is detected, but the all tokens of the previous line are comment. + // Comment lines are adjusted to the next code line. + comments.push(tokensOnSameLine[0]) + isBesideMultilineToken = + (last(tokensOnSameLine) as Token).loc.end.line === + token.loc.start.line + tokensOnSameLine = [token] + } else { + // New line is detected, so validate the tokens. + if (!isBesideMultilineToken) { + validate(tokensOnSameLine, comments, lastValidatedToken) + lastValidatedToken = tokensOnSameLine[0] + } + isBesideMultilineToken = + (last(tokensOnSameLine) as Token).loc.end.line === + token.loc.start.line + tokensOnSameLine = [token] + comments = [] + } + } + if ( + tokensOnSameLine.length >= 1 && + tokensOnSameLine.some(isNotComment) + ) { + validate(tokensOnSameLine, comments, lastValidatedToken) + } + }, + }) } diff --git a/src/eslint-plugins/utils/index.ts b/src/eslint-plugins/utils/index.ts index 6f77ab61..ed97222d 100644 --- a/src/eslint-plugins/utils/index.ts +++ b/src/eslint-plugins/utils/index.ts @@ -3,80 +3,190 @@ * @copyright 2017 Toru Nagashima. All rights reserved. * See LICENSE file in root directory for full license. */ - 'use strict' +'use strict' + +import * as ESLint from "eslint"; +import * as ESTree from "estree"; +import { VueObjectData, VueObjectType, VueVisitor } from "../types/utils"; + +type RuleModule = ESLint.Rule.RuleModule; +type Position = ESTree.Position; +type CodePath = ESLint.Rule.CodePath; +type CodePathSegment = ESLint.Rule.CodePathSegment; + +type ComponentArrayPropDetectName = { + type: "array"; + key: Literal | TemplateLiteral; + propName: string; + value: null; + node: Expression | SpreadElement; +}; + +type ComponentArrayPropUnknownName = { + type: "array"; + key: null; + propName: null; + value: null; + node: Expression | SpreadElement; +}; + +type ComponentArrayProp = + | ComponentArrayPropDetectName + | ComponentArrayPropUnknownName; + +type ComponentObjectPropDetectName = { + type: "object"; + key: Expression; + propName: string; + value: Expression; + node: Property; +}; + +type ComponentObjectPropUnknownName = { + type: "object"; + key: null; + propName: null; + value: Expression; + node: Property; +}; + +type ComponentObjectProp = + | ComponentObjectPropDetectName + | ComponentObjectPropUnknownName; + +type ComponentArrayEmitDetectName = { + type: "array"; + key: Literal | TemplateLiteral; + emitName: string; + value: null; + node: Expression | SpreadElement; +}; + +type ComponentArrayEmitUnknownName = { + type: "array"; + key: null; + emitName: null; + value: null; + node: Expression | SpreadElement; +}; + +type ComponentArrayEmit = + | ComponentArrayEmitDetectName + | ComponentArrayEmitUnknownName; + +type ComponentObjectEmitDetectName = { + type: "object"; + key: Expression; + emitName: string; + value: Expression; + node: Property; +}; + +type ComponentObjectEmitUnknownName = { + type: "object"; + key: null; + emitName: null; + value: Expression; + node: Property; +}; + +type ComponentObjectEmit = + | ComponentObjectEmitDetectName + | ComponentObjectEmitUnknownName; + +type ComponentComputedProperty = { + key: string | null; + value: BlockStatement | null; +}; + +type GroupName = "props" | "data" | "computed" | "setup" | "watch" | "methods"; +type ComponentArrayPropertyData = { + type: "array"; + name: string; + groupName: GroupName; + node: Literal | TemplateLiteral; +}; +type ComponentObjectPropertyData = { + type: "object"; + name: string; + groupName: GroupName; + node: Identifier | Literal | TemplateLiteral; + property: Property; +}; +type ComponentPropertyData = + | ComponentArrayPropertyData + | ComponentObjectPropertyData; - import * as ESLint from "eslint"; - import * as ESTree from "estree"; /** - * @typedef {import('eslint').Rule.RuleModule} RuleModule - * @typedef {import('estree').Position} Position - * @typedef {import('eslint').Rule.CodePath} CodePath - * @typedef {import('eslint').Rule.CodePathSegment} CodePathSegment + // * @typedef {import('eslint').Rule.RuleModule} RuleModule + // * @typedef {import('estree').Position} Position + // * @typedef {import('eslint').Rule.CodePath} CodePath + // * @typedef {import('eslint').Rule.CodePathSegment} CodePathSegment */ /** - * @typedef {object} ComponentArrayPropDetectName - * @property {'array'} type - * @property {Literal | TemplateLiteral} key - * @property {string} propName - * @property {null} value - * @property {Expression | SpreadElement} node + // * @typedef {object} ComponentArrayPropDetectName + // * @property {'array'} type + // * @property {Literal | TemplateLiteral} key + // * @property {string} propName + // * @property {null} value + // * @property {Expression | SpreadElement} node * - * @typedef {object} ComponentArrayPropUnknownName - * @property {'array'} type - * @property {null} key - * @property {null} propName - * @property {null} value - * @property {Expression | SpreadElement} node + // * @typedef {object} ComponentArrayPropUnknownName + // * @property {'array'} type + // * @property {null} key + // * @property {null} propName + // * @property {null} value + // * @property {Expression | SpreadElement} node * - * @typedef {ComponentArrayPropDetectName | ComponentArrayPropUnknownName} ComponentArrayProp + // * @typedef {ComponentArrayPropDetectName | ComponentArrayPropUnknownName} ComponentArrayProp * - * @typedef {object} ComponentObjectPropDetectName - * @property {'object'} type - * @property {Expression} key - * @property {string} propName - * @property {Expression} value - * @property {Property} node + // * @typedef {object} ComponentObjectPropDetectName + // * @property {'object'} type + // * @property {Expression} key + // * @property {string} propName + // * @property {Expression} value + // * @property {Property} node * - * @typedef {object} ComponentObjectPropUnknownName - * @property {'object'} type - * @property {null} key - * @property {null} propName - * @property {Expression} value - * @property {Property} node + // * @typedef {object} ComponentObjectPropUnknownName + // * @property {'object'} type + // * @property {null} key + // * @property {null} propName + // * @property {Expression} value + // * @property {Property} node * - * @typedef {ComponentObjectPropDetectName | ComponentObjectPropUnknownName} ComponentObjectProp + // * @typedef {ComponentObjectPropDetectName | ComponentObjectPropUnknownName} ComponentObjectProp */ /** - * @typedef {object} ComponentArrayEmitDetectName - * @property {'array'} type - * @property {Literal | TemplateLiteral} key - * @property {string} emitName - * @property {null} value - * @property {Expression | SpreadElement} node + // * @typedef {object} ComponentArrayEmitDetectName + // * @property {'array'} type + // * @property {Literal | TemplateLiteral} key + // * @property {string} emitName + // * @property {null} value + // * @property {Expression | SpreadElement} node * - * @typedef {object} ComponentArrayEmitUnknownName - * @property {'array'} type - * @property {null} key - * @property {null} emitName - * @property {null} value - * @property {Expression | SpreadElement} node + // * @typedef {object} ComponentArrayEmitUnknownName + // * @property {'array'} type + // * @property {null} key + // * @property {null} emitName + // * @property {null} value + // * @property {Expression | SpreadElement} node * - * @typedef {ComponentArrayEmitDetectName | ComponentArrayEmitUnknownName} ComponentArrayEmit + // * @typedef {ComponentArrayEmitDetectName | ComponentArrayEmitUnknownName} ComponentArrayEmit * - * @typedef {object} ComponentObjectEmitDetectName - * @property {'object'} type - * @property {Expression} key - * @property {string} emitName - * @property {Expression} value - * @property {Property} node + // * @typedef {object} ComponentObjectEmitDetectName + // * @property {'object'} type + // * @property {Expression} key + // * @property {string} emitName + // * @property {Expression} value + // * @property {Property} node * - * @typedef {object} ComponentObjectEmitUnknownName - * @property {'object'} type - * @property {null} key - * @property {null} emitName - * @property {Expression} value - * @property {Property} node + // * @typedef {object} ComponentObjectEmitUnknownName + // * @property {'object'} type + // * @property {null} key + // * @property {null} emitName + // * @property {Expression} value + // * @property {Property} node * * @typedef {ComponentObjectEmitDetectName | ComponentObjectEmitUnknownName} ComponentObjectEmit */ @@ -102,10 +212,13 @@ const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json')) const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json')) const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json')) - const path = require('path') - const vueEslintParser = require('vue-eslint-parser') - const { findVariable } = require('eslint-utils') - + import path from "path"; + import vueEslintParser from "vue-eslint-parser"; + import {findVariable} from "eslint-utils"; + + + + /** * @type { WeakMap<RuleContext, Token[]> } */ diff --git a/src/eslint-plugins/utils/keycode-to-key.ts b/src/eslint-plugins/utils/keycode-to-key.ts index 3defe3bf..9adbe049 100644 --- a/src/eslint-plugins/utils/keycode-to-key.ts +++ b/src/eslint-plugins/utils/keycode-to-key.ts @@ -1,98 +1,98 @@ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values export = { - 8: 'backspace', - 9: 'tab', - 13: 'enter', - 16: 'shift', - 17: 'ctrl', - 18: 'alt', - 19: 'pause', // windows - 20: 'caps-lock', - 27: 'escape', - 32: 'space', // Vue.js specially key name. - 33: 'page-up', - 34: 'page-down', - 35: 'end', - 36: 'home', - 37: 'arrow-left', - 38: 'arrow-up', - 39: 'arrow-right', - 40: 'arrow-down', - 45: 'insert', // windows - 46: 'delete', + 8: "backspace", + 9: "tab", + 13: "enter", + 16: "shift", + 17: "ctrl", + 18: "alt", + 19: "pause", // windows + 20: "caps-lock", + 27: "escape", + 32: "space", // Vue.js specially key name. + 33: "page-up", + 34: "page-down", + 35: "end", + 36: "home", + 37: "arrow-left", + 38: "arrow-up", + 39: "arrow-right", + 40: "arrow-down", + 45: "insert", // windows + 46: "delete", - // If mistakenly use it in Vue.js 2.x, it will be irreversibly broken. Therefore, it will not be autofix. - // '48': '0', - // '49': '1', - // '50': '2', - // '51': '3', - // '52': '4', - // '53': '5', - // '54': '6', - // '55': '7', - // '56': '8', - // '57': '9', + // If mistakenly use it in Vue.js 2.x, it will be irreversibly broken. Therefore, it will not be autofix. + // '48': '0', + // '49': '1', + // '50': '2', + // '51': '3', + // '52': '4', + // '53': '5', + // '54': '6', + // '55': '7', + // '56': '8', + // '57': '9', - 65: 'a', - 66: 'b', - 67: 'c', - 68: 'd', - 69: 'e', - 70: 'f', - 71: 'g', - 72: 'h', - 73: 'i', - 74: 'j', - 75: 'k', - 76: 'l', - 77: 'm', - 78: 'n', - 79: 'o', - 80: 'p', - 81: 'q', - 82: 'r', - 83: 's', - 84: 't', - 85: 'u', - 86: 'v', - 87: 'w', - 88: 'x', - 89: 'y', - 90: 'z', + 65: "a", + 66: "b", + 67: "c", + 68: "d", + 69: "e", + 70: "f", + 71: "g", + 72: "h", + 73: "i", + 74: "j", + 75: "k", + 76: "l", + 77: "m", + 78: "n", + 79: "o", + 80: "p", + 81: "q", + 82: "r", + 83: "s", + 84: "t", + 85: "u", + 86: "v", + 87: "w", + 88: "x", + 89: "y", + 90: "z", - // The key value may change depending on the OS. - // '91': 'meta' ,// Win: 'os'? - // '92': 'meta', // Win: 'meta' Mac: ? - // '93': 'meta', // Win: 'context-menu' Mac: 'meta' + // The key value may change depending on the OS. + // '91': 'meta' ,// Win: 'os'? + // '92': 'meta', // Win: 'meta' Mac: ? + // '93': 'meta', // Win: 'context-menu' Mac: 'meta' - // Cannot determine numpad with key. - // '96': 'numpad-0', - // '97': 'numpad-1', - // '98': 'numpad-2', - // '99': 'numpad-3', - // '100': 'numpad-4', - // '101': 'numpad-5', - // '102': 'numpad-6', - // '103': 'numpad-7', - // '104': 'numpad-8', - // '105': 'numpad-9', - // '106': 'multiply', - // '107': 'add', - // '109': 'subtract', - // '110': 'decimal', - // '111': 'divide', - 112: 'f1', - 113: 'f2', - 114: 'f3', - 115: 'f4', - 116: 'f5', - 117: 'f6', - 118: 'f7', - 119: 'f8', - 120: 'f9', - 121: 'f10', - 122: 'f11', - 123: 'f12', - 144: 'num-lock', - 145: 'scroll-lock' + // Cannot determine numpad with key. + // '96': 'numpad-0', + // '97': 'numpad-1', + // '98': 'numpad-2', + // '99': 'numpad-3', + // '100': 'numpad-4', + // '101': 'numpad-5', + // '102': 'numpad-6', + // '103': 'numpad-7', + // '104': 'numpad-8', + // '105': 'numpad-9', + // '106': 'multiply', + // '107': 'add', + // '109': 'subtract', + // '110': 'decimal', + // '111': 'divide', + 112: "f1", + 113: "f2", + 114: "f3", + 115: "f4", + 116: "f5", + 117: "f6", + 118: "f7", + 119: "f8", + 120: "f9", + 121: "f10", + 122: "f11", + 123: "f12", + 144: "num-lock", + 145: "scroll-lock", } diff --git a/src/eslint-plugins/utils/regexp.ts b/src/eslint-plugins/utils/regexp.ts index 2b71bc2f..3cc99aab 100644 --- a/src/eslint-plugins/utils/regexp.ts +++ b/src/eslint-plugins/utils/regexp.ts @@ -8,9 +8,9 @@ const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. */ export function escape(string: string) { - return string && RE_HAS_REGEXP_CHAR.test(string) - ? string.replace(RE_REGEXP_CHAR, '\\$&') - : string + return string && RE_HAS_REGEXP_CHAR.test(string) + ? string.replace(RE_REGEXP_CHAR, "\\$&") + : string } /** @@ -19,17 +19,16 @@ export function escape(string: string) { * Strings like `"/^foo/i"` are converted to `/^foo/i` of `RegExp`. */ export function toRegExp(string: string) { - const parts = RE_REGEXP_STR.exec(string) - if (parts) { - return new RegExp(parts[1], parts[2]) - } - return new RegExp(`^${escape(string)}$`) + const parts = RE_REGEXP_STR.exec(string) + if (parts) { + return new RegExp(parts[1], parts[2]) + } + return new RegExp(`^${escape(string)}$`) } /** * Checks whether given string is regexp string */ export function isRegExp(string: string) { - return Boolean(RE_REGEXP_STR.exec(string)) + return Boolean(RE_REGEXP_STR.exec(string)) } - diff --git a/typings/eslint-utils/index.d.ts b/typings/eslint-utils/index.d.ts new file mode 100644 index 00000000..f79d5a2b --- /dev/null +++ b/typings/eslint-utils/index.d.ts @@ -0,0 +1,81 @@ +import * as VAST from '../../src/eslint-plugins/types/ast' +import { Token, Comment } from '../../src/eslint-plugins/types/node' +import { ParserServices } from '../../src/eslint-plugins/types/parser-services' + +import eslint from 'eslint' + +export function findVariable( + initialScope: eslint.Scope.Scope, + nameOrNode: VAST.Identifier | string +): eslint.Scope.Variable + +export function getStaticValue( + node: VAST.ESNode, + initialScope?: eslint.Scope.Scope +): { value: any } | null + +export function isParenthesized( + num: number, + node: VAST.ESNode, + sourceCode: eslint.SourceCode | ParserServices.TokenStore +): boolean +export function isParenthesized( + node: VAST.ESNode, + sourceCode: eslint.SourceCode | ParserServices.TokenStore +): boolean + +export namespace TYPES { + type TraceKind = { + [ReferenceTracker.READ]?: boolean + [ReferenceTracker.CALL]?: boolean + [ReferenceTracker.CONSTRUCT]?: boolean + [ReferenceTracker.ESM]?: boolean + } + type TraceMap = { + [key: string]: TraceKind & TraceMap + } +} + +export class ReferenceTracker { + constructor( + globalScope: eslint.Scope.Scope, + options?: { + mode?: 'legacy' | 'strict' + globalObjectNames?: ('global' | 'globalThis' | 'self' | 'window')[] + } + ) + + iterateGlobalReferences( + traceMap: TYPES.TraceMap + ): IterableIterator<{ + node: VAST.ESNode + path: string[] + type: symbol + info: any + }> + iterateCjsReferences( + traceMap: TYPES.TraceMap + ): IterableIterator<{ + node: VAST.ESNode + path: string[] + type: symbol + info: any + }> + iterateEsmReferences( + traceMap: TYPES.TraceMap + ): IterableIterator<{ + node: VAST.ESNode + path: string[] + type: symbol + info: any + }> +} + +export namespace ReferenceTracker { + const READ: unique symbol + const CALL: unique symbol + const CONSTRUCT: unique symbol + const ESM: unique symbol +} + +export function isCommentToken(token: Token): token is Comment diff --git a/typings/eslint/index.d.ts b/typings/eslint/index.d.ts new file mode 100644 index 00000000..5d2f87c0 --- /dev/null +++ b/typings/eslint/index.d.ts @@ -0,0 +1,431 @@ +import { + Rule as ESLintRule, + RuleTester as ESLintRuleTester, + Linter as ESLintLinter +} from '../../node_modules/@types/eslint' +import * as VAST from '../../src/eslint-plugins/types/ast' +import * as VNODE from '../../src/eslint-plugins/types/node' +import * as parserServices from '../../src/eslint-plugins/types/parser-services' + +export namespace AST { + type Token = VNODE.Token + type Range = VNODE.Range + type SourceLocation = VNODE.SourceLocation + type Program = VAST.Program +} +export namespace Scope { + interface ScopeManager { + scopes: Scope[] + globalScope: Scope | null + acquire(node: VAST.ESNode | VAST.Program, inner?: boolean): Scope | null + getDeclaredVariables(node: VAST.ESNode): Variable[] + } + interface Scope { + type: + | 'block' + | 'catch' + | 'class' + | 'for' + | 'function' + | 'function-expression-name' + | 'global' + | 'module' + | 'switch' + | 'with' + | 'TDZ' + isStrict: boolean + upper: Scope | null + childScopes: Scope[] + variableScope: Scope + block: VAST.ESNode + variables: Variable[] + set: Map<string, Variable> + references: Reference[] + through: Reference[] + functionExpressionScope: boolean + } + interface Variable { + name: string + identifiers: VAST.Identifier[] + references: Reference[] + defs: Definition[] + + writeable?: boolean | undefined + eslintExplicitGlobal?: boolean | undefined + eslintExplicitGlobalComments?: Comment[] | undefined + eslintImplicitGlobalSetting?: 'readonly' | 'writable' | undefined + } + interface Reference { + identifier: VAST.Identifier + from: Scope + resolved: Variable | null + writeExpr: VAST.ESNode | null + init: boolean + isWrite(): boolean + isRead(): boolean + isWriteOnly(): boolean + isReadOnly(): boolean + isReadWrite(): boolean + } + type DefinitionType = + | { type: 'CatchClause'; node: VAST.CatchClause; parent: null } + | { + type: 'ClassName' + node: VAST.ClassDeclaration | VAST.ClassExpression + parent: null + } + | { + type: 'FunctionName' + node: VAST.FunctionDeclaration | VAST.FunctionExpression + parent: null + } + | { type: 'ImplicitGlobalVariable'; node: VAST.Program; parent: null } + | { + type: 'ImportBinding' + node: + | VAST.ImportSpecifier + | VAST.ImportDefaultSpecifier + | VAST.ImportNamespaceSpecifier + parent: VAST.ImportDeclaration + } + | { + type: 'Parameter' + node: + | VAST.FunctionDeclaration + | VAST.FunctionExpression + | VAST.ArrowFunctionExpression + parent: null + } + | { type: 'TDZ'; node: any; parent: null } + | { + type: 'Variable' + node: VAST.VariableDeclarator + parent: VAST.VariableDeclaration + } + type Definition = DefinitionType & { name: VAST.Identifier } +} + +export class SourceCode /*extends ESLintSourceCode*/ { + text: string + ast: AST.Program + lines: string[] + hasBOM: boolean + parserServices: SourceCode.ParserServices + scopeManager: Scope.ScopeManager + visitorKeys: SourceCode.VisitorKeys + + static splitLines(text: string): string[] + + getText( + node?: VNODE.HasLocation, + beforeCount?: number, + afterCount?: number + ): string + getLines(): string[] + getAllComments(): VNODE.Comment[] + getComments( + node: VAST.ESNode + ): { leading: VNODE.Comment[]; trailing: VNODE.Comment[] } + getJSDocComment(node: VAST.ESNode): AST.Token | null + getNodeByRangeIndex(index: number): VAST.ESNode | VAST.JSXNode + isSpaceBetweenTokens(first: AST.Token, second: AST.Token): boolean + getLocFromIndex(index: number): VNODE.Position + getIndexFromLoc(location: VNODE.Position): number + + getTokenByRangeStart( + offset: number, + options?: { includeComments?: boolean } + ): AST.Token | null + getFirstToken(node: VNODE.HasLocation): AST.Token + getFirstToken(node: VNODE.HasLocation, options: number): AST.Token + getFirstToken( + node: VNODE.HasLocation, + options: SourceCode.CursorWithSkipOptions + ): AST.Token | null + getFirstTokens( + node: VNODE.HasLocation, + options?: SourceCode.CursorWithCountOptions + ): AST.Token[] + getLastToken(node: VNODE.HasLocation): AST.Token + getLastToken(node: VNODE.HasLocation, options: number): AST.Token + getLastToken( + node: VNODE.HasLocation, + options: SourceCode.CursorWithSkipOptions + ): AST.Token | null + getLastTokens( + node: VNODE.HasLocation, + options?: SourceCode.CursorWithCountOptions + ): AST.Token[] + getTokenBefore(node: VNODE.HasLocation): AST.Token + getTokenBefore(node: VNODE.HasLocation, options: number): AST.Token + getTokenBefore( + node: VNODE.HasLocation, + options: { includeComments: boolean } + ): AST.Token + getTokenBefore( + node: VNODE.HasLocation, + options?: SourceCode.CursorWithSkipOptions + ): AST.Token | null + getTokensBefore( + node: VNODE.HasLocation, + options?: SourceCode.CursorWithCountOptions + ): AST.Token[] + getTokenAfter(node: VNODE.HasLocation): AST.Token + getTokenAfter(node: VNODE.HasLocation, options: number): AST.Token + getTokenAfter( + node: VNODE.HasLocation, + options: { includeComments: boolean } + ): AST.Token + getTokenAfter( + node: VNODE.HasLocation, + options: SourceCode.CursorWithSkipOptions + ): AST.Token | null + getTokensAfter( + node: VNODE.HasLocation, + options?: SourceCode.CursorWithCountOptions + ): AST.Token[] + getFirstTokenBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: SourceCode.CursorWithSkipOptions + ): AST.Token | null + getFirstTokensBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: SourceCode.CursorWithCountOptions + ): AST.Token[] + getLastTokenBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: SourceCode.CursorWithSkipOptions + ): AST.Token | null + getLastTokensBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + options?: SourceCode.CursorWithCountOptions + ): AST.Token[] + getTokensBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation, + padding?: + | number + | SourceCode.FilterPredicate + | SourceCode.CursorWithCountOptions + ): AST.Token[] + getTokens( + node: VNODE.HasLocation, + beforeCount?: number, + afterCount?: number + ): AST.Token[] + getTokens( + node: VNODE.HasLocation, + options: SourceCode.FilterPredicate | SourceCode.CursorWithCountOptions + ): AST.Token[] + commentsExistBetween( + left: VNODE.HasLocation, + right: VNODE.HasLocation + ): boolean + getCommentsBefore(nodeOrToken: VNODE.HasLocation): VNODE.Comment[] + getCommentsAfter(nodeOrToken: VNODE.HasLocation): VNODE.Comment[] + getCommentsInside(node: VNODE.HasLocation): VNODE.Comment[] +} +export namespace SourceCode { + interface Config { + text: string + ast: AST.Program + parserServices?: ParserServices + scopeManager?: Scope.ScopeManager + visitorKeys?: VisitorKeys + } + + type ParserServices = parserServices.ParserServices + + interface VisitorKeys { + [nodeType: string]: string[] + } + + type FilterPredicate = (tokenOrComment: AST.Token) => boolean + + type CursorWithSkipOptions = + | number + | FilterPredicate + | { + includeComments?: boolean + filter?: FilterPredicate + skip?: number + } + + type CursorWithCountOptions = + | number + | FilterPredicate + | { + includeComments?: boolean + filter?: FilterPredicate + count?: number + } +} + +export namespace Rule { + interface RuleModule /*extends ESLintRule.RuleModule*/ { + meta: RuleMetaData + create(context: RuleContext): Rule.RuleListener + } + + type NodeTypes = VAST.ESNode['type'] + + type NodeListenerBase = { + [T in keyof VAST.NodeListenerMap]?: (node: VAST.NodeListenerMap[T]) => void + } + interface NodeListener extends NodeListenerBase { + [key: string]: ((node: VAST.ParamNode) => void) | undefined + } + + interface RuleListener extends NodeListenerBase { + onCodePathStart?(codePath: CodePath, node: VAST.ParamNode): void + onCodePathEnd?(codePath: CodePath, node: VAST.ParamNode): void + onCodePathSegmentStart?( + segment: CodePathSegment, + node: VAST.ParamNode + ): void + onCodePathSegmentEnd?(segment: CodePathSegment, node: VAST.ParamNode): void + onCodePathSegmentLoop?( + fromSegment: CodePathSegment, + toSegment: CodePathSegment, + node: VAST.ParamNode + ): void + [key: string]: + | ((codePath: CodePath, node: VAST.ParamNode) => void) + | ((segment: CodePathSegment, node: VAST.ParamNode) => void) + | (( + fromSegment: CodePathSegment, + toSegment: CodePathSegment, + node: VAST.ParamNode + ) => void) + | ((node: VAST.ParamNode) => void) + | undefined + } + interface CodePath extends ESLintRule.CodePath {} + interface CodePathSegment extends ESLintRule.CodePathSegment {} + + interface RuleMetaData extends ESLintRule.RuleMetaData { + docs: Required<ESLintRule.RuleMetaData>['docs'] + } + + interface RuleContext { + id: string + options: ESLintRule.RuleContext['options'] + settings: { [name: string]: any } + parserPath: string + parserOptions: any + parserServices: parserServices.ParserServices + + getAncestors(): VAST.ESNode[] + + getDeclaredVariables(node: VAST.ESNode): Scope.Variable[] + getFilename(): string + getScope(): Scope.Scope + getSourceCode(): SourceCode + markVariableAsUsed(name: string): boolean + report(descriptor: ReportDescriptor): void + + // eslint@6 does not have this method. + getCwd?: () => string + } + + type ReportDescriptor = + | ReportDescriptor1 + | ReportDescriptor2 + | ReportDescriptor3 + | ReportDescriptor4 + + type SuggestionReportDescriptor = + | SuggestionReportDescriptor1 + | SuggestionReportDescriptor2 + + interface RuleFixer { + insertTextAfter(nodeOrToken: VNODE.HasLocation, text: string): Fix + insertTextAfterRange(range: AST.Range, text: string): Fix + insertTextBefore(nodeOrToken: VNODE.HasLocation, text: string): Fix + insertTextBeforeRange(range: AST.Range, text: string): Fix + remove(nodeOrToken: VNODE.HasLocation): Fix + removeRange(range: AST.Range): Fix + replaceText(nodeOrToken: VNODE.HasLocation, text: string): Fix + replaceTextRange(range: AST.Range, text: string): Fix + } + + interface Fix { + range: AST.Range + text: string + } +} + +export class RuleTester extends ESLintRuleTester {} +export class Linter { + getRules(): Map<string, Rule.RuleModule> +} + +export namespace Linter { + type LintMessage = ESLintLinter.LintMessage + type LintOptions = ESLintLinter.LintOptions +} + +interface ReportDescriptorOptionsBase { + data?: { + [key: string]: string | number + } + fix?: + | null + | (( + fixer: Rule.RuleFixer + ) => null | Rule.Fix | IterableIterator<Rule.Fix> | Rule.Fix[]) +} + +interface SuggestionReportDescriptor1 extends ReportDescriptorOptionsBase { + desc: string +} + +interface SuggestionReportDescriptor2 extends ReportDescriptorOptionsBase { + messageId: string +} +interface ReportDescriptorOptions extends ReportDescriptorOptionsBase { + suggest?: Rule.SuggestionReportDescriptor[] | null +} + +interface ReportSourceLocation1 { + start: VNODE.Position + end: VNODE.Position + line?: undefined + column?: undefined +} + +interface ReportSourceLocation2 extends VNODE.Position { + start?: undefined + end?: undefined +} + +type ReportSourceLocation = ReportSourceLocation1 | ReportSourceLocation2 + +interface ReportDescriptor1 extends ReportDescriptorOptions { + message: string + messageId?: string + node: VNODE.HasLocation + loc?: ReportSourceLocation +} +interface ReportDescriptor2 extends ReportDescriptorOptions { + message: string + messageId?: string + node?: VNODE.HasLocation + loc: ReportSourceLocation +} +interface ReportDescriptor3 extends ReportDescriptorOptions { + message?: string + messageId: string + node: VNODE.HasLocation + loc?: ReportSourceLocation +} +interface ReportDescriptor4 extends ReportDescriptorOptions { + message?: string + messageId: string + node?: VNODE.HasLocation + loc: ReportSourceLocation +} diff --git a/typings/eslint/lib/rules/accessor-pairs.d.ts b/typings/eslint/lib/rules/accessor-pairs.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/accessor-pairs.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/array-bracket-newline.d.ts b/typings/eslint/lib/rules/array-bracket-newline.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/array-bracket-newline.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/array-bracket-spacing.d.ts b/typings/eslint/lib/rules/array-bracket-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/array-bracket-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/array-callback-return.d.ts b/typings/eslint/lib/rules/array-callback-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/array-callback-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/array-element-newline.d.ts b/typings/eslint/lib/rules/array-element-newline.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/array-element-newline.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/arrow-body-style.d.ts b/typings/eslint/lib/rules/arrow-body-style.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/arrow-body-style.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/arrow-parens.d.ts b/typings/eslint/lib/rules/arrow-parens.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/arrow-parens.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/arrow-spacing.d.ts b/typings/eslint/lib/rules/arrow-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/arrow-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/block-scoped-var.d.ts b/typings/eslint/lib/rules/block-scoped-var.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/block-scoped-var.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/block-spacing.d.ts b/typings/eslint/lib/rules/block-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/block-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/brace-style.d.ts b/typings/eslint/lib/rules/brace-style.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/brace-style.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/callback-return.d.ts b/typings/eslint/lib/rules/callback-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/callback-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/camelcase.d.ts b/typings/eslint/lib/rules/camelcase.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/camelcase.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/capitalized-comments.d.ts b/typings/eslint/lib/rules/capitalized-comments.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/capitalized-comments.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/class-methods-use-this.d.ts b/typings/eslint/lib/rules/class-methods-use-this.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/class-methods-use-this.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/comma-dangle.d.ts b/typings/eslint/lib/rules/comma-dangle.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/comma-dangle.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/comma-spacing.d.ts b/typings/eslint/lib/rules/comma-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/comma-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/comma-style.d.ts b/typings/eslint/lib/rules/comma-style.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/comma-style.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/complexity.d.ts b/typings/eslint/lib/rules/complexity.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/complexity.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/computed-property-spacing.d.ts b/typings/eslint/lib/rules/computed-property-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/computed-property-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/consistent-return.d.ts b/typings/eslint/lib/rules/consistent-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/consistent-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/consistent-this.d.ts b/typings/eslint/lib/rules/consistent-this.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/consistent-this.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/constructor-super.d.ts b/typings/eslint/lib/rules/constructor-super.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/constructor-super.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/curly.d.ts b/typings/eslint/lib/rules/curly.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/curly.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/default-case-last.d.ts b/typings/eslint/lib/rules/default-case-last.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/default-case-last.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/default-case.d.ts b/typings/eslint/lib/rules/default-case.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/default-case.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/default-param-last.d.ts b/typings/eslint/lib/rules/default-param-last.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/default-param-last.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/dot-location.d.ts b/typings/eslint/lib/rules/dot-location.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/dot-location.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/dot-notation.d.ts b/typings/eslint/lib/rules/dot-notation.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/dot-notation.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/eol-last.d.ts b/typings/eslint/lib/rules/eol-last.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/eol-last.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/eqeqeq.d.ts b/typings/eslint/lib/rules/eqeqeq.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/eqeqeq.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/for-direction.d.ts b/typings/eslint/lib/rules/for-direction.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/for-direction.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/func-call-spacing.d.ts b/typings/eslint/lib/rules/func-call-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/func-call-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/func-name-matching.d.ts b/typings/eslint/lib/rules/func-name-matching.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/func-name-matching.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/func-names.d.ts b/typings/eslint/lib/rules/func-names.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/func-names.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/func-style.d.ts b/typings/eslint/lib/rules/func-style.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/func-style.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/function-call-argument-newline.d.ts b/typings/eslint/lib/rules/function-call-argument-newline.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/function-call-argument-newline.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/function-paren-newline.d.ts b/typings/eslint/lib/rules/function-paren-newline.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/function-paren-newline.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/generator-star-spacing.d.ts b/typings/eslint/lib/rules/generator-star-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/generator-star-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/getter-return.d.ts b/typings/eslint/lib/rules/getter-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/getter-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/global-require.d.ts b/typings/eslint/lib/rules/global-require.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/global-require.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/grouped-accessor-pairs.d.ts b/typings/eslint/lib/rules/grouped-accessor-pairs.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/grouped-accessor-pairs.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/guard-for-in.d.ts b/typings/eslint/lib/rules/guard-for-in.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/guard-for-in.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/handle-callback-err.d.ts b/typings/eslint/lib/rules/handle-callback-err.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/handle-callback-err.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/id-blacklist.d.ts b/typings/eslint/lib/rules/id-blacklist.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/id-blacklist.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/id-length.d.ts b/typings/eslint/lib/rules/id-length.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/id-length.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/id-match.d.ts b/typings/eslint/lib/rules/id-match.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/id-match.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/implicit-arrow-linebreak.d.ts b/typings/eslint/lib/rules/implicit-arrow-linebreak.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/implicit-arrow-linebreak.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/indent-legacy.d.ts b/typings/eslint/lib/rules/indent-legacy.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/indent-legacy.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/indent.d.ts b/typings/eslint/lib/rules/indent.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/indent.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/init-declarations.d.ts b/typings/eslint/lib/rules/init-declarations.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/init-declarations.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/jsx-quotes.d.ts b/typings/eslint/lib/rules/jsx-quotes.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/jsx-quotes.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/key-spacing.d.ts b/typings/eslint/lib/rules/key-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/key-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/keyword-spacing.d.ts b/typings/eslint/lib/rules/keyword-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/keyword-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/line-comment-position.d.ts b/typings/eslint/lib/rules/line-comment-position.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/line-comment-position.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/linebreak-style.d.ts b/typings/eslint/lib/rules/linebreak-style.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/linebreak-style.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/lines-around-comment.d.ts b/typings/eslint/lib/rules/lines-around-comment.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/lines-around-comment.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/lines-around-directive.d.ts b/typings/eslint/lib/rules/lines-around-directive.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/lines-around-directive.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/lines-between-class-members.d.ts b/typings/eslint/lib/rules/lines-between-class-members.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/lines-between-class-members.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-classes-per-file.d.ts b/typings/eslint/lib/rules/max-classes-per-file.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-classes-per-file.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-depth.d.ts b/typings/eslint/lib/rules/max-depth.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-depth.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-len.d.ts b/typings/eslint/lib/rules/max-len.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-len.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-lines-per-function.d.ts b/typings/eslint/lib/rules/max-lines-per-function.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-lines-per-function.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-lines.d.ts b/typings/eslint/lib/rules/max-lines.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-lines.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-nested-callbacks.d.ts b/typings/eslint/lib/rules/max-nested-callbacks.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-nested-callbacks.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-params.d.ts b/typings/eslint/lib/rules/max-params.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-params.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-statements-per-line.d.ts b/typings/eslint/lib/rules/max-statements-per-line.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-statements-per-line.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/max-statements.d.ts b/typings/eslint/lib/rules/max-statements.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/max-statements.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/multiline-comment-style.d.ts b/typings/eslint/lib/rules/multiline-comment-style.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/multiline-comment-style.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/multiline-ternary.d.ts b/typings/eslint/lib/rules/multiline-ternary.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/multiline-ternary.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/new-cap.d.ts b/typings/eslint/lib/rules/new-cap.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/new-cap.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/new-parens.d.ts b/typings/eslint/lib/rules/new-parens.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/new-parens.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/newline-after-var.d.ts b/typings/eslint/lib/rules/newline-after-var.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/newline-after-var.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/newline-before-return.d.ts b/typings/eslint/lib/rules/newline-before-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/newline-before-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/newline-per-chained-call.d.ts b/typings/eslint/lib/rules/newline-per-chained-call.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/newline-per-chained-call.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-alert.d.ts b/typings/eslint/lib/rules/no-alert.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-alert.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-array-constructor.d.ts b/typings/eslint/lib/rules/no-array-constructor.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-array-constructor.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-async-promise-executor.d.ts b/typings/eslint/lib/rules/no-async-promise-executor.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-async-promise-executor.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-await-in-loop.d.ts b/typings/eslint/lib/rules/no-await-in-loop.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-await-in-loop.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-bitwise.d.ts b/typings/eslint/lib/rules/no-bitwise.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-bitwise.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-buffer-constructor.d.ts b/typings/eslint/lib/rules/no-buffer-constructor.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-buffer-constructor.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-caller.d.ts b/typings/eslint/lib/rules/no-caller.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-caller.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-case-declarations.d.ts b/typings/eslint/lib/rules/no-case-declarations.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-case-declarations.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-catch-shadow.d.ts b/typings/eslint/lib/rules/no-catch-shadow.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-catch-shadow.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-class-assign.d.ts b/typings/eslint/lib/rules/no-class-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-class-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-compare-neg-zero.d.ts b/typings/eslint/lib/rules/no-compare-neg-zero.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-compare-neg-zero.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-cond-assign.d.ts b/typings/eslint/lib/rules/no-cond-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-cond-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-confusing-arrow.d.ts b/typings/eslint/lib/rules/no-confusing-arrow.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-confusing-arrow.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-console.d.ts b/typings/eslint/lib/rules/no-console.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-console.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-const-assign.d.ts b/typings/eslint/lib/rules/no-const-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-const-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-constant-condition.d.ts b/typings/eslint/lib/rules/no-constant-condition.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-constant-condition.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-constructor-return.d.ts b/typings/eslint/lib/rules/no-constructor-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-constructor-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-continue.d.ts b/typings/eslint/lib/rules/no-continue.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-continue.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-control-regex.d.ts b/typings/eslint/lib/rules/no-control-regex.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-control-regex.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-debugger.d.ts b/typings/eslint/lib/rules/no-debugger.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-debugger.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-delete-var.d.ts b/typings/eslint/lib/rules/no-delete-var.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-delete-var.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-div-regex.d.ts b/typings/eslint/lib/rules/no-div-regex.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-div-regex.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-dupe-args.d.ts b/typings/eslint/lib/rules/no-dupe-args.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-dupe-args.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-dupe-class-members.d.ts b/typings/eslint/lib/rules/no-dupe-class-members.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-dupe-class-members.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-dupe-else-if.d.ts b/typings/eslint/lib/rules/no-dupe-else-if.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-dupe-else-if.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-dupe-keys.d.ts b/typings/eslint/lib/rules/no-dupe-keys.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-dupe-keys.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-duplicate-case.d.ts b/typings/eslint/lib/rules/no-duplicate-case.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-duplicate-case.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-duplicate-imports.d.ts b/typings/eslint/lib/rules/no-duplicate-imports.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-duplicate-imports.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-else-return.d.ts b/typings/eslint/lib/rules/no-else-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-else-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-empty-character-class.d.ts b/typings/eslint/lib/rules/no-empty-character-class.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-empty-character-class.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-empty-function.d.ts b/typings/eslint/lib/rules/no-empty-function.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-empty-function.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-empty-pattern.d.ts b/typings/eslint/lib/rules/no-empty-pattern.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-empty-pattern.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-empty.d.ts b/typings/eslint/lib/rules/no-empty.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-empty.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-eq-null.d.ts b/typings/eslint/lib/rules/no-eq-null.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-eq-null.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-eval.d.ts b/typings/eslint/lib/rules/no-eval.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-eval.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-ex-assign.d.ts b/typings/eslint/lib/rules/no-ex-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-ex-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-extend-native.d.ts b/typings/eslint/lib/rules/no-extend-native.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-extend-native.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-extra-bind.d.ts b/typings/eslint/lib/rules/no-extra-bind.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-extra-bind.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-extra-boolean-cast.d.ts b/typings/eslint/lib/rules/no-extra-boolean-cast.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-extra-boolean-cast.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-extra-label.d.ts b/typings/eslint/lib/rules/no-extra-label.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-extra-label.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-extra-parens.d.ts b/typings/eslint/lib/rules/no-extra-parens.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-extra-parens.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-extra-semi.d.ts b/typings/eslint/lib/rules/no-extra-semi.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-extra-semi.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-fallthrough.d.ts b/typings/eslint/lib/rules/no-fallthrough.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-fallthrough.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-floating-decimal.d.ts b/typings/eslint/lib/rules/no-floating-decimal.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-floating-decimal.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-func-assign.d.ts b/typings/eslint/lib/rules/no-func-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-func-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-global-assign.d.ts b/typings/eslint/lib/rules/no-global-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-global-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-implicit-coercion.d.ts b/typings/eslint/lib/rules/no-implicit-coercion.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-implicit-coercion.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-implicit-globals.d.ts b/typings/eslint/lib/rules/no-implicit-globals.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-implicit-globals.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-implied-eval.d.ts b/typings/eslint/lib/rules/no-implied-eval.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-implied-eval.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-import-assign.d.ts b/typings/eslint/lib/rules/no-import-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-import-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-inline-comments.d.ts b/typings/eslint/lib/rules/no-inline-comments.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-inline-comments.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-inner-declarations.d.ts b/typings/eslint/lib/rules/no-inner-declarations.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-inner-declarations.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-invalid-regexp.d.ts b/typings/eslint/lib/rules/no-invalid-regexp.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-invalid-regexp.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-invalid-this.d.ts b/typings/eslint/lib/rules/no-invalid-this.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-invalid-this.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-irregular-whitespace.d.ts b/typings/eslint/lib/rules/no-irregular-whitespace.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-irregular-whitespace.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-iterator.d.ts b/typings/eslint/lib/rules/no-iterator.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-iterator.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-label-var.d.ts b/typings/eslint/lib/rules/no-label-var.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-label-var.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-labels.d.ts b/typings/eslint/lib/rules/no-labels.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-labels.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-lone-blocks.d.ts b/typings/eslint/lib/rules/no-lone-blocks.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-lone-blocks.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-lonely-if.d.ts b/typings/eslint/lib/rules/no-lonely-if.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-lonely-if.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-loop-func.d.ts b/typings/eslint/lib/rules/no-loop-func.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-loop-func.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-loss-of-precision.d.ts b/typings/eslint/lib/rules/no-loss-of-precision.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-loss-of-precision.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-magic-numbers.d.ts b/typings/eslint/lib/rules/no-magic-numbers.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-magic-numbers.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-misleading-character-class.d.ts b/typings/eslint/lib/rules/no-misleading-character-class.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-misleading-character-class.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-mixed-operators.d.ts b/typings/eslint/lib/rules/no-mixed-operators.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-mixed-operators.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-mixed-requires.d.ts b/typings/eslint/lib/rules/no-mixed-requires.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-mixed-requires.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-mixed-spaces-and-tabs.d.ts b/typings/eslint/lib/rules/no-mixed-spaces-and-tabs.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-mixed-spaces-and-tabs.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-multi-assign.d.ts b/typings/eslint/lib/rules/no-multi-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-multi-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-multi-spaces.d.ts b/typings/eslint/lib/rules/no-multi-spaces.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-multi-spaces.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-multi-str.d.ts b/typings/eslint/lib/rules/no-multi-str.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-multi-str.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-multiple-empty-lines.d.ts b/typings/eslint/lib/rules/no-multiple-empty-lines.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-multiple-empty-lines.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-native-reassign.d.ts b/typings/eslint/lib/rules/no-native-reassign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-native-reassign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-negated-condition.d.ts b/typings/eslint/lib/rules/no-negated-condition.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-negated-condition.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-negated-in-lhs.d.ts b/typings/eslint/lib/rules/no-negated-in-lhs.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-negated-in-lhs.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-nested-ternary.d.ts b/typings/eslint/lib/rules/no-nested-ternary.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-nested-ternary.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-new-func.d.ts b/typings/eslint/lib/rules/no-new-func.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-new-func.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-new-object.d.ts b/typings/eslint/lib/rules/no-new-object.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-new-object.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-new-require.d.ts b/typings/eslint/lib/rules/no-new-require.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-new-require.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-new-symbol.d.ts b/typings/eslint/lib/rules/no-new-symbol.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-new-symbol.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-new-wrappers.d.ts b/typings/eslint/lib/rules/no-new-wrappers.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-new-wrappers.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-new.d.ts b/typings/eslint/lib/rules/no-new.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-new.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-obj-calls.d.ts b/typings/eslint/lib/rules/no-obj-calls.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-obj-calls.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-octal-escape.d.ts b/typings/eslint/lib/rules/no-octal-escape.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-octal-escape.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-octal.d.ts b/typings/eslint/lib/rules/no-octal.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-octal.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-param-reassign.d.ts b/typings/eslint/lib/rules/no-param-reassign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-param-reassign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-path-concat.d.ts b/typings/eslint/lib/rules/no-path-concat.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-path-concat.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-plusplus.d.ts b/typings/eslint/lib/rules/no-plusplus.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-plusplus.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-process-env.d.ts b/typings/eslint/lib/rules/no-process-env.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-process-env.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-process-exit.d.ts b/typings/eslint/lib/rules/no-process-exit.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-process-exit.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-promise-executor-return.d.ts b/typings/eslint/lib/rules/no-promise-executor-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-promise-executor-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-proto.d.ts b/typings/eslint/lib/rules/no-proto.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-proto.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-prototype-builtins.d.ts b/typings/eslint/lib/rules/no-prototype-builtins.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-prototype-builtins.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-redeclare.d.ts b/typings/eslint/lib/rules/no-redeclare.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-redeclare.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-regex-spaces.d.ts b/typings/eslint/lib/rules/no-regex-spaces.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-regex-spaces.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-restricted-exports.d.ts b/typings/eslint/lib/rules/no-restricted-exports.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-restricted-exports.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-restricted-globals.d.ts b/typings/eslint/lib/rules/no-restricted-globals.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-restricted-globals.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-restricted-imports.d.ts b/typings/eslint/lib/rules/no-restricted-imports.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-restricted-imports.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-restricted-modules.d.ts b/typings/eslint/lib/rules/no-restricted-modules.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-restricted-modules.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-restricted-properties.d.ts b/typings/eslint/lib/rules/no-restricted-properties.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-restricted-properties.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-restricted-syntax.d.ts b/typings/eslint/lib/rules/no-restricted-syntax.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-restricted-syntax.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-return-assign.d.ts b/typings/eslint/lib/rules/no-return-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-return-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-return-await.d.ts b/typings/eslint/lib/rules/no-return-await.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-return-await.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-script-url.d.ts b/typings/eslint/lib/rules/no-script-url.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-script-url.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-self-assign.d.ts b/typings/eslint/lib/rules/no-self-assign.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-self-assign.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-self-compare.d.ts b/typings/eslint/lib/rules/no-self-compare.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-self-compare.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-sequences.d.ts b/typings/eslint/lib/rules/no-sequences.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-sequences.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-setter-return.d.ts b/typings/eslint/lib/rules/no-setter-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-setter-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-shadow-restricted-names.d.ts b/typings/eslint/lib/rules/no-shadow-restricted-names.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-shadow-restricted-names.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-shadow.d.ts b/typings/eslint/lib/rules/no-shadow.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-shadow.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-spaced-func.d.ts b/typings/eslint/lib/rules/no-spaced-func.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-spaced-func.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-sparse-arrays.d.ts b/typings/eslint/lib/rules/no-sparse-arrays.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-sparse-arrays.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-sync.d.ts b/typings/eslint/lib/rules/no-sync.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-sync.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-tabs.d.ts b/typings/eslint/lib/rules/no-tabs.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-tabs.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-template-curly-in-string.d.ts b/typings/eslint/lib/rules/no-template-curly-in-string.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-template-curly-in-string.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-ternary.d.ts b/typings/eslint/lib/rules/no-ternary.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-ternary.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-this-before-super.d.ts b/typings/eslint/lib/rules/no-this-before-super.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-this-before-super.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-throw-literal.d.ts b/typings/eslint/lib/rules/no-throw-literal.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-throw-literal.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-trailing-spaces.d.ts b/typings/eslint/lib/rules/no-trailing-spaces.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-trailing-spaces.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-undef-init.d.ts b/typings/eslint/lib/rules/no-undef-init.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-undef-init.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-undef.d.ts b/typings/eslint/lib/rules/no-undef.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-undef.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-undefined.d.ts b/typings/eslint/lib/rules/no-undefined.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-undefined.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-underscore-dangle.d.ts b/typings/eslint/lib/rules/no-underscore-dangle.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-underscore-dangle.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unexpected-multiline.d.ts b/typings/eslint/lib/rules/no-unexpected-multiline.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unexpected-multiline.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unmodified-loop-condition.d.ts b/typings/eslint/lib/rules/no-unmodified-loop-condition.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unmodified-loop-condition.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unneeded-ternary.d.ts b/typings/eslint/lib/rules/no-unneeded-ternary.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unneeded-ternary.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unreachable-loop.d.ts b/typings/eslint/lib/rules/no-unreachable-loop.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unreachable-loop.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unreachable.d.ts b/typings/eslint/lib/rules/no-unreachable.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unreachable.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unsafe-finally.d.ts b/typings/eslint/lib/rules/no-unsafe-finally.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unsafe-finally.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unsafe-negation.d.ts b/typings/eslint/lib/rules/no-unsafe-negation.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unsafe-negation.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unused-expressions.d.ts b/typings/eslint/lib/rules/no-unused-expressions.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unused-expressions.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unused-labels.d.ts b/typings/eslint/lib/rules/no-unused-labels.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unused-labels.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-unused-vars.d.ts b/typings/eslint/lib/rules/no-unused-vars.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-unused-vars.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-use-before-define.d.ts b/typings/eslint/lib/rules/no-use-before-define.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-use-before-define.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-backreference.d.ts b/typings/eslint/lib/rules/no-useless-backreference.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-backreference.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-call.d.ts b/typings/eslint/lib/rules/no-useless-call.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-call.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-catch.d.ts b/typings/eslint/lib/rules/no-useless-catch.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-catch.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-computed-key.d.ts b/typings/eslint/lib/rules/no-useless-computed-key.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-computed-key.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-concat.d.ts b/typings/eslint/lib/rules/no-useless-concat.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-concat.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-constructor.d.ts b/typings/eslint/lib/rules/no-useless-constructor.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-constructor.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-escape.d.ts b/typings/eslint/lib/rules/no-useless-escape.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-escape.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-rename.d.ts b/typings/eslint/lib/rules/no-useless-rename.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-rename.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-useless-return.d.ts b/typings/eslint/lib/rules/no-useless-return.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-useless-return.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-var.d.ts b/typings/eslint/lib/rules/no-var.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-var.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-void.d.ts b/typings/eslint/lib/rules/no-void.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-void.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-warning-comments.d.ts b/typings/eslint/lib/rules/no-warning-comments.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-warning-comments.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-whitespace-before-property.d.ts b/typings/eslint/lib/rules/no-whitespace-before-property.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-whitespace-before-property.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/no-with.d.ts b/typings/eslint/lib/rules/no-with.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/no-with.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/nonblock-statement-body-position.d.ts b/typings/eslint/lib/rules/nonblock-statement-body-position.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/nonblock-statement-body-position.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/object-curly-newline.d.ts b/typings/eslint/lib/rules/object-curly-newline.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/object-curly-newline.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/object-curly-spacing.d.ts b/typings/eslint/lib/rules/object-curly-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/object-curly-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/object-property-newline.d.ts b/typings/eslint/lib/rules/object-property-newline.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/object-property-newline.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/object-shorthand.d.ts b/typings/eslint/lib/rules/object-shorthand.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/object-shorthand.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/one-var-declaration-per-line.d.ts b/typings/eslint/lib/rules/one-var-declaration-per-line.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/one-var-declaration-per-line.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/one-var.d.ts b/typings/eslint/lib/rules/one-var.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/one-var.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/operator-assignment.d.ts b/typings/eslint/lib/rules/operator-assignment.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/operator-assignment.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/operator-linebreak.d.ts b/typings/eslint/lib/rules/operator-linebreak.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/operator-linebreak.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/padded-blocks.d.ts b/typings/eslint/lib/rules/padded-blocks.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/padded-blocks.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/padding-line-between-statements.d.ts b/typings/eslint/lib/rules/padding-line-between-statements.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/padding-line-between-statements.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-arrow-callback.d.ts b/typings/eslint/lib/rules/prefer-arrow-callback.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-arrow-callback.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-const.d.ts b/typings/eslint/lib/rules/prefer-const.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-const.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-destructuring.d.ts b/typings/eslint/lib/rules/prefer-destructuring.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-destructuring.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-exponentiation-operator.d.ts b/typings/eslint/lib/rules/prefer-exponentiation-operator.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-exponentiation-operator.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-named-capture-group.d.ts b/typings/eslint/lib/rules/prefer-named-capture-group.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-named-capture-group.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-numeric-literals.d.ts b/typings/eslint/lib/rules/prefer-numeric-literals.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-numeric-literals.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-object-spread.d.ts b/typings/eslint/lib/rules/prefer-object-spread.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-object-spread.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-promise-reject-errors.d.ts b/typings/eslint/lib/rules/prefer-promise-reject-errors.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-promise-reject-errors.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-reflect.d.ts b/typings/eslint/lib/rules/prefer-reflect.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-reflect.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-regex-literals.d.ts b/typings/eslint/lib/rules/prefer-regex-literals.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-regex-literals.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-rest-params.d.ts b/typings/eslint/lib/rules/prefer-rest-params.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-rest-params.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-spread.d.ts b/typings/eslint/lib/rules/prefer-spread.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-spread.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/prefer-template.d.ts b/typings/eslint/lib/rules/prefer-template.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/prefer-template.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/quote-props.d.ts b/typings/eslint/lib/rules/quote-props.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/quote-props.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/quotes.d.ts b/typings/eslint/lib/rules/quotes.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/quotes.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/radix.d.ts b/typings/eslint/lib/rules/radix.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/radix.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/require-atomic-updates.d.ts b/typings/eslint/lib/rules/require-atomic-updates.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/require-atomic-updates.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/require-await.d.ts b/typings/eslint/lib/rules/require-await.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/require-await.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/require-jsdoc.d.ts b/typings/eslint/lib/rules/require-jsdoc.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/require-jsdoc.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/require-unicode-regexp.d.ts b/typings/eslint/lib/rules/require-unicode-regexp.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/require-unicode-regexp.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/require-yield.d.ts b/typings/eslint/lib/rules/require-yield.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/require-yield.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/rest-spread-spacing.d.ts b/typings/eslint/lib/rules/rest-spread-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/rest-spread-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/semi-spacing.d.ts b/typings/eslint/lib/rules/semi-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/semi-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/semi-style.d.ts b/typings/eslint/lib/rules/semi-style.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/semi-style.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/semi.d.ts b/typings/eslint/lib/rules/semi.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/semi.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/sort-imports.d.ts b/typings/eslint/lib/rules/sort-imports.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/sort-imports.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/sort-keys.d.ts b/typings/eslint/lib/rules/sort-keys.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/sort-keys.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/sort-vars.d.ts b/typings/eslint/lib/rules/sort-vars.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/sort-vars.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/space-before-blocks.d.ts b/typings/eslint/lib/rules/space-before-blocks.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/space-before-blocks.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/space-before-function-paren.d.ts b/typings/eslint/lib/rules/space-before-function-paren.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/space-before-function-paren.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/space-in-parens.d.ts b/typings/eslint/lib/rules/space-in-parens.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/space-in-parens.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/space-infix-ops.d.ts b/typings/eslint/lib/rules/space-infix-ops.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/space-infix-ops.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/space-unary-ops.d.ts b/typings/eslint/lib/rules/space-unary-ops.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/space-unary-ops.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/spaced-comment.d.ts b/typings/eslint/lib/rules/spaced-comment.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/spaced-comment.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/strict.d.ts b/typings/eslint/lib/rules/strict.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/strict.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/switch-colon-spacing.d.ts b/typings/eslint/lib/rules/switch-colon-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/switch-colon-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/symbol-description.d.ts b/typings/eslint/lib/rules/symbol-description.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/symbol-description.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/template-curly-spacing.d.ts b/typings/eslint/lib/rules/template-curly-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/template-curly-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/template-tag-spacing.d.ts b/typings/eslint/lib/rules/template-tag-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/template-tag-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/unicode-bom.d.ts b/typings/eslint/lib/rules/unicode-bom.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/unicode-bom.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/use-isnan.d.ts b/typings/eslint/lib/rules/use-isnan.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/use-isnan.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/valid-jsdoc.d.ts b/typings/eslint/lib/rules/valid-jsdoc.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/valid-jsdoc.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/valid-typeof.d.ts b/typings/eslint/lib/rules/valid-typeof.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/valid-typeof.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/vars-on-top.d.ts b/typings/eslint/lib/rules/vars-on-top.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/vars-on-top.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/wrap-iife.d.ts b/typings/eslint/lib/rules/wrap-iife.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/wrap-iife.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/wrap-regex.d.ts b/typings/eslint/lib/rules/wrap-regex.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/wrap-regex.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/yield-star-spacing.d.ts b/typings/eslint/lib/rules/yield-star-spacing.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/yield-star-spacing.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/eslint/lib/rules/yoda.d.ts b/typings/eslint/lib/rules/yoda.d.ts new file mode 100644 index 00000000..146fa11b --- /dev/null +++ b/typings/eslint/lib/rules/yoda.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '../../index' +declare const rule: Rule.RuleModule +export = rule diff --git a/typings/vue-eslint-parser/index.d.ts b/typings/vue-eslint-parser/index.d.ts new file mode 100644 index 00000000..1330ce5c --- /dev/null +++ b/typings/vue-eslint-parser/index.d.ts @@ -0,0 +1,5 @@ +import * as VAST from '../../src/eslint-plugins/types/ast' + +export namespace AST { + export const NS: VAST.NS +} From 8c544edfd980fba0043d4a30a2c245210de6a975 Mon Sep 17 00:00:00 2001 From: tyankatsu <frips.ryilsufupe+dev@gmail.com> Date: Sat, 8 May 2021 11:27:47 +0900 Subject: [PATCH 4/4] style: ignore eslint --- src/eslint-plugins/utils/regexp.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/eslint-plugins/utils/regexp.ts b/src/eslint-plugins/utils/regexp.ts index 3cc99aab..096f135c 100644 --- a/src/eslint-plugins/utils/regexp.ts +++ b/src/eslint-plugins/utils/regexp.ts @@ -1,4 +1,5 @@ const RE_REGEXP_CHAR = /[\\^$.*+?()[\]{}|]/gu +// eslint-disable-next-line require-unicode-regexp const RE_HAS_REGEXP_CHAR = new RegExp(RE_REGEXP_CHAR.source) const RE_REGEXP_STR = /^\/(.+)\/(.*)$/u @@ -23,6 +24,7 @@ export function toRegExp(string: string) { if (parts) { return new RegExp(parts[1], parts[2]) } + // eslint-disable-next-line require-unicode-regexp return new RegExp(`^${escape(string)}$`) }