Skip to content

Commit 0429164

Browse files
committed
compat-eslint: migrate 11 mechanical classes to SHAPES factory
Each class had only `type` plus simple convertChild/convertChildren slot getters — exactly the mechanical pattern the factory was designed to absorb. Migration eliminates 11 hand-written subclass + switch-case pairs and pushes them through the single-source-of-truth registry that both top-down getters and bottom-up materialise consult. Migrated: - Pure type tag: SuperNode, ThisExpressionNode, TSThisTypeNode, EmptyStatementNode, JSXOpeningFragmentNode, JSXClosingFragmentNode - defaults (static fields): NullLiteralNode (value/raw) - consts (derived from tsNode): PrivateIdentifierNode (name) - single-slot convertChild: JSXSpreadAttributeNode - slot via callback: JSXClosingElementNode (convertJSXTagName), StaticBlockNode (decorators default + body via callback for nested block.statements access) Hand-written class count 91 → 81; SHAPES entries 46 → 57. Net -56 lines. Tested: predicate-coverage / lazy-estree / scope-compat / selector-analysis / ts-ast-scan / compat-pipeline / jsx-react-x + dogfood (107 rules × 30 files clean) + Dify cold/warm bench (no regression).
1 parent 7a81e2d commit 0429164

1 file changed

Lines changed: 36 additions & 92 deletions

File tree

packages/compat-eslint/lib/lazy-estree.ts

Lines changed: 36 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,6 +1813,42 @@ defineShape<ts.PropertyAssignment>(SK.PropertyAssignment, {
18131813
value: { tsField: 'initializer' },
18141814
},
18151815
});
1816+
// Pure type-tag shapes — no slots, no fields. The ESTree node carries
1817+
// only the `type` discriminator and inherited range/loc/parent.
1818+
defineShape<ts.SuperExpression>(SK.SuperKeyword, { type: 'Super', slots: {} });
1819+
defineShape<ts.ThisExpression>(SK.ThisKeyword, { type: 'ThisExpression', slots: {} });
1820+
defineShape<ts.ThisTypeNode>(SK.ThisType, { type: 'TSThisType', slots: {} });
1821+
defineShape<ts.EmptyStatement>(SK.EmptyStatement, { type: 'EmptyStatement', slots: {} });
1822+
defineShape<ts.JsxOpeningFragment>(SK.JsxOpeningFragment, { type: 'JSXOpeningFragment', slots: {} });
1823+
defineShape<ts.JsxClosingFragment>(SK.JsxClosingFragment, { type: 'JSXClosingFragment', slots: {} });
1824+
// `null` literal in expression position. The LiteralType wrapper case
1825+
// (`type X = null`) is handled separately in convertLiteralType, which
1826+
// emits TSNullKeyword instead.
1827+
defineShape<ts.NullLiteral>(SK.NullKeyword, {
1828+
type: 'Literal',
1829+
slots: {},
1830+
defaults: { value: null, raw: 'null' },
1831+
});
1832+
defineShape<ts.PrivateIdentifier>(SK.PrivateIdentifier, {
1833+
type: 'PrivateIdentifier',
1834+
slots: {},
1835+
consts: tn => ({ name: tn.text.slice(1) }),
1836+
});
1837+
defineShape<ts.JsxSpreadAttribute>(SK.JsxSpreadAttribute, {
1838+
type: 'JSXSpreadAttribute',
1839+
slots: { argument: { tsField: 'expression' } },
1840+
});
1841+
defineShape<ts.JsxClosingElement>(SK.JsxClosingElement, {
1842+
type: 'JSXClosingElement',
1843+
slots: { name: { tsField: 'tagName', via: convertJSXTagName } },
1844+
});
1845+
defineShape<ts.ClassStaticBlockDeclaration>(SK.ClassStaticBlockDeclaration, {
1846+
type: 'StaticBlock',
1847+
slots: {
1848+
body: { tsField: 'body', via: (block, parent) => convertChildren(block.statements, parent) },
1849+
},
1850+
defaults: { decorators: EMPTY_ARRAY },
1851+
});
18161852

18171853
function convertChildInner(child: ts.Node, parent: LazyNode): LazyNode | null {
18181854
const ShapeCls = SHAPE_CLASSES.get(child.kind);
@@ -1862,8 +1898,6 @@ function convertChildInner(child: ts.Node, parent: LazyNode): LazyNode | null {
18621898
}
18631899
return wrapChainIfNeeded(new CallExpressionNode(ce, parent), ce, parent);
18641900
}
1865-
case SK.ClassStaticBlockDeclaration:
1866-
return new StaticBlockNode(child, parent);
18671901
case SK.MetaProperty:
18681902
return new MetaPropertyNode(child as ts.MetaProperty, parent);
18691903
case SK.TrueKeyword:
@@ -1977,8 +2011,6 @@ function convertChildInner(child: ts.Node, parent: LazyNode): LazyNode | null {
19772011
return new BreakOrContinueNode('BreakStatement', child as ts.BreakStatement, parent);
19782012
case SK.ContinueStatement:
19792013
return new BreakOrContinueNode('ContinueStatement', child as ts.ContinueStatement, parent);
1980-
case SK.EmptyStatement:
1981-
return new EmptyStatementNode(child, parent);
19822014
case SK.ClassDeclaration:
19832015
return new ClassNode('ClassDeclaration', child as ts.ClassDeclaration, parent);
19842016
case SK.ClassExpression:
@@ -2035,12 +2067,6 @@ function convertChildInner(child: ts.Node, parent: LazyNode): LazyNode | null {
20352067
}
20362068
case SK.OmittedExpression:
20372069
return null;
2038-
case SK.NullKeyword:
2039-
return new NullLiteralNode(child, parent);
2040-
case SK.SuperKeyword:
2041-
return new SuperNode(child, parent);
2042-
case SK.ThisKeyword:
2043-
return new ThisExpressionNode(child, parent);
20442070
case SK.ExpressionWithTypeArguments: {
20452071
// Parent-aware shape (mirrors eager line 1858). The TS parent
20462072
// chain — not our lazy parent — is what carries this signal:
@@ -2060,12 +2086,8 @@ function convertChildInner(child: ts.Node, parent: LazyNode): LazyNode | null {
20602086
}
20612087
return new ExpressionWithTypeArgumentsNode(ewta, parent, tag);
20622088
}
2063-
case SK.PrivateIdentifier:
2064-
return new PrivateIdentifierNode(child as ts.PrivateIdentifier, parent);
20652089
case SK.MappedType:
20662090
return new TSMappedTypeNode(child as ts.MappedTypeNode, parent);
2067-
case SK.ThisType:
2068-
return new TSThisTypeNode(child, parent);
20692091
case SK.TypePredicate:
20702092
return new TSTypePredicateNode(child as ts.TypePredicateNode, parent);
20712093
case SK.EnumDeclaration:
@@ -2079,18 +2101,10 @@ function convertChildInner(child: ts.Node, parent: LazyNode): LazyNode | null {
20792101
return new JSXElementNode(child, parent);
20802102
case SK.JsxOpeningElement:
20812103
return new JSXOpeningElementNode(child as ts.JsxOpeningElement, parent);
2082-
case SK.JsxClosingElement:
2083-
return new JSXClosingElementNode(child, parent);
20842104
case SK.JsxFragment:
20852105
return new JSXFragmentNode(child, parent);
2086-
case SK.JsxOpeningFragment:
2087-
return new JSXOpeningFragmentNode(child, parent);
2088-
case SK.JsxClosingFragment:
2089-
return new JSXClosingFragmentNode(child, parent);
20902106
case SK.JsxAttribute:
20912107
return new JSXAttributeNode(child, parent);
2092-
case SK.JsxSpreadAttribute:
2093-
return new JSXSpreadAttributeNode(child, parent);
20942108
case SK.JsxExpression:
20952109
return (child as ts.JsxExpression).dotDotDotToken
20962110
? new JSXSpreadChildNode(child, parent)
@@ -3087,29 +3101,6 @@ class BindingElementNode extends LazyNode {
30873101
}
30883102
}
30893103

3090-
class NullLiteralNode extends LazyNode {
3091-
readonly type = 'Literal' as const;
3092-
readonly value = null;
3093-
readonly raw = 'null';
3094-
}
3095-
3096-
class SuperNode extends LazyNode {
3097-
readonly type = 'Super' as const;
3098-
}
3099-
3100-
class ThisExpressionNode extends LazyNode {
3101-
readonly type = 'ThisExpression' as const;
3102-
}
3103-
3104-
class PrivateIdentifierNode extends LazyNode {
3105-
readonly type = 'PrivateIdentifier' as const;
3106-
readonly name: string;
3107-
constructor(tsNode: ts.PrivateIdentifier, parent: LazyNode) {
3108-
super(tsNode, parent);
3109-
this.name = tsNode.text.slice(1);
3110-
}
3111-
}
3112-
31133104
class TSMappedTypeNode extends LazyNode {
31143105
readonly type = 'TSMappedType' as const;
31153106
readonly readonly: boolean | '+' | '-' | undefined;
@@ -3143,10 +3134,6 @@ class TSMappedTypeNode extends LazyNode {
31433134
}
31443135
}
31453136

3146-
class TSThisTypeNode extends LazyNode {
3147-
readonly type = 'TSThisType' as const;
3148-
}
3149-
31503137
class TSTypePredicateNode extends LazyNode {
31513138
readonly type = 'TSTypePredicate' as const;
31523139
readonly asserts: boolean;
@@ -3461,10 +3448,6 @@ class BreakOrContinueNode extends LazyNode {
34613448
}
34623449
}
34633450

3464-
class EmptyStatementNode extends LazyNode {
3465-
readonly type = 'EmptyStatement' as const;
3466-
}
3467-
34683451
// NamedTupleMember: with `...` becomes TSRestType wrapping the member.
34693452
function convertNamedTupleMember(tsNode: ts.NamedTupleMember, parent: LazyNode): LazyNode {
34703453
if (tsNode.dotDotDotToken) {
@@ -4565,18 +4548,6 @@ class ImportExpressionNode extends LazyNode {
45654548
}
45664549

45674550
// `class C { static { ... } }` — class static initialiser block.
4568-
class StaticBlockNode extends LazyNode {
4569-
readonly type = 'StaticBlock' as const;
4570-
readonly decorators: never[] = EMPTY_ARRAY;
4571-
private _body?: (LazyNode | null)[];
4572-
get body() {
4573-
return this._body ??= convertChildren(
4574-
(this._ts as ts.ClassStaticBlockDeclaration).body.statements,
4575-
this,
4576-
);
4577-
}
4578-
}
4579-
45804551
// `new.target` and `import.meta`. typescript-estree emits
45814552
// MetaProperty { meta: Identifier, property: Identifier } where the
45824553
// `meta` Identifier is synthetic (TS has only the keyword tokens).
@@ -4773,16 +4744,6 @@ class JSXOpeningElementNode extends LazyNode {
47734744
}
47744745
}
47754746

4776-
class JSXClosingElementNode extends LazyNode {
4777-
readonly type = 'JSXClosingElement' as const;
4778-
private _name?: LazyNode;
4779-
get name(): LazyNode {
4780-
if (this._name) return this._name;
4781-
const t = this._ts as ts.JsxClosingElement;
4782-
return this._name = convertJSXTagName(t.tagName, this);
4783-
}
4784-
}
4785-
47864747
class JSXFragmentNode extends LazyNode {
47874748
readonly type = 'JSXFragment' as const;
47884749
private _openingFragment?: LazyNode;
@@ -4803,14 +4764,6 @@ class JSXFragmentNode extends LazyNode {
48034764
}
48044765
}
48054766

4806-
class JSXOpeningFragmentNode extends LazyNode {
4807-
readonly type = 'JSXOpeningFragment' as const;
4808-
}
4809-
4810-
class JSXClosingFragmentNode extends LazyNode {
4811-
readonly type = 'JSXClosingFragment' as const;
4812-
}
4813-
48144767
class JSXAttributeNode extends LazyNode {
48154768
readonly type = 'JSXAttribute' as const;
48164769
private _name?: LazyNode;
@@ -4826,15 +4779,6 @@ class JSXAttributeNode extends LazyNode {
48264779
}
48274780
}
48284781

4829-
class JSXSpreadAttributeNode extends LazyNode {
4830-
readonly type = 'JSXSpreadAttribute' as const;
4831-
private _argument?: LazyNode | null;
4832-
get argument(): LazyNode | null {
4833-
if (this._argument !== undefined) return this._argument;
4834-
return this._argument = convertChild((this._ts as ts.JsxSpreadAttribute).expression, this);
4835-
}
4836-
}
4837-
48384782
class JSXExpressionContainerNode extends LazyNode {
48394783
readonly type = 'JSXExpressionContainer' as const;
48404784
private _expression?: LazyNode;

0 commit comments

Comments
 (0)