11import * as tsParser from "@typescript-eslint/parser" ;
22import type { TSESTree } from "@typescript-eslint/types" ;
3- import type { RuleContext } from "@typescript-eslint/utils/ts-eslint" ;
4- import { Linter } from "eslint" ;
3+ import { Linter , type RuleContext , type RuleListener } from "@typescript-eslint/utils/ts-eslint" ;
54
65/**
76 * The rule context surface handed to unit-test harness callbacks.
@@ -15,35 +14,47 @@ const testRuleLanguageOptions = {
1514} as const ;
1615
1716/**
18- * Runs `code` through a real `Linter` with an inline test rule and calls `fn`
19- * from its `Program` listener, giving the callback a real rule context
20- * (scope manager, static evaluation, ...) .
17+ * Runs `code` through a real `Linter` with an inline test rule.
18+ * The supplied `createVisitor` callback receives the real rule context
19+ * and returns the visitor that the rule should use .
2120 */
22- export function runInRule < T > ( code : string , fn : ( context : TestRuleContext , program : TSESTree . Program ) => T ) : T {
23- const state = { called : false } ;
24- let fact : T | undefined ;
21+ function runInlineRule ( code : string , createVisitor : ( context : TestRuleContext ) => RuleListener ) : TestRuleContext {
22+ let context : TestRuleContext | null = null ;
2523 new Linter ( ) . verify ( code , {
2624 languageOptions : testRuleLanguageOptions ,
2725 plugins : {
2826 test : {
2927 rules : {
3028 "test-rule" : {
3129 meta : { type : "problem" , messages : { } , schema : [ ] } ,
32- create ( context : unknown ) {
33- return {
34- Program ( program : TSESTree . Program ) {
35- state . called = true ;
36- // tsl-ignore dx/no-unsafe-as
37- fact = fn ( context as TestRuleContext , program ) ;
38- } ,
39- } ;
30+ create ( ctx : unknown ) {
31+ // tsl-ignore dx/no-unsafe-as
32+ context = ctx as TestRuleContext ;
33+ return createVisitor ( context ) ;
4034 } ,
4135 } ,
4236 } ,
4337 } ,
4438 } ,
4539 rules : { "test/test-rule" : "error" } ,
4640 } ) ;
41+ return context ! ;
42+ }
43+
44+ /**
45+ * Runs `code` through a real `Linter` with an inline test rule and calls `fn`
46+ * from its `Program` listener, giving the callback a real rule context
47+ * (scope manager, static evaluation, ...).
48+ */
49+ export function runInRule < T > ( code : string , fn : ( context : TestRuleContext , program : TSESTree . Program ) => T ) : T {
50+ const state = { called : false } ;
51+ let fact : T | undefined ;
52+ runInlineRule ( code , ( context ) => ( {
53+ Program ( program : TSESTree . Program ) {
54+ state . called = true ;
55+ fact = fn ( context , program ) ;
56+ } ,
57+ } ) ) ;
4758 if ( ! state . called ) {
4859 throw new Error ( "runInRule: the rule's Program listener was not invoked" ) ;
4960 }
@@ -55,37 +66,17 @@ export function runInRule<T>(code: string, fn: (context: TestRuleContext, progra
5566 * Runs `code` through a real `Linter` and captures the first node visited by
5667 * `visitorKey` (e.g. `"JSXElement"`) together with the rule context.
5768 */
58- export function getNodeInRule < T extends TSESTree . Node > (
59- code : string ,
60- visitorKey : string ,
61- ) : { context : TestRuleContext ; node : T } {
62- const found : { context : TestRuleContext | null ; node : T | null } = { context : null , node : null } ;
63- new Linter ( ) . verify ( code , {
64- languageOptions : testRuleLanguageOptions ,
65- plugins : {
66- test : {
67- rules : {
68- "test-rule" : {
69- meta : { type : "problem" , messages : { } , schema : [ ] } ,
70- create ( context : unknown ) {
71- // tsl-ignore dx/no-unsafe-as
72- found . context = context as TestRuleContext ;
73- return {
74- [ visitorKey ] ( node : T ) {
75- found . node ??= node ;
76- } ,
77- } ;
78- } ,
79- } ,
80- } ,
81- } ,
69+ export function getNodeInRule < T extends TSESTree . Node > ( code : string , visitorKey : string ) : { context : TestRuleContext ; node : T } {
70+ const found : { node : T | null } = { node : null } ;
71+ const context = runInlineRule ( code , ( ) => ( {
72+ [ visitorKey ] ( node : T ) {
73+ found . node ??= node ;
8274 } ,
83- rules : { "test/test-rule" : "error" } ,
84- } ) ;
85- if ( found . context == null || found . node == null ) {
75+ } ) ) ;
76+ if ( found . node == null ) {
8677 throw new Error ( `expected a node matching "${ visitorKey } " in the code` ) ;
8778 }
88- return { context : found . context , node : found . node } ;
79+ return { context, node : found . node } ;
8980}
9081
9182/**
@@ -95,39 +86,27 @@ export function getNodeInRule<T extends TSESTree.Node>(
9586 */
9687export function runCollector < A , R > (
9788 code : string ,
98- getCollector : ( context : TestRuleContext ) => { api : A ; visitor : object } ,
89+ getCollector : ( context : TestRuleContext ) => { api : A ; visitor : RuleListener } ,
9990 harvest : ( api : A , program : TSESTree . Program ) => R ,
10091) : R {
10192 const state = { harvested : false } ;
10293 let program : TSESTree . Program | null = null ;
10394 let fact : R | undefined ;
104- new Linter ( ) . verify ( code , {
105- languageOptions : testRuleLanguageOptions ,
106- plugins : {
107- test : {
108- rules : {
109- "test-rule" : {
110- meta : { type : "problem" , messages : { } , schema : [ ] } ,
111- create ( context : unknown ) {
112- // tsl-ignore dx/no-unsafe-as
113- const { api, visitor } = getCollector ( context as TestRuleContext ) ;
114- return {
115- ...visitor ,
116- Program ( node : TSESTree . Program ) {
117- program = node ;
118- } ,
119- "Program:exit" ( ) {
120- if ( program == null ) return ;
121- state . harvested = true ;
122- fact = harvest ( api , program ) ;
123- } ,
124- } ;
125- } ,
126- } ,
127- } ,
95+ let api : A | undefined ;
96+ runInlineRule ( code , ( context ) => {
97+ const collector = getCollector ( context ) ;
98+ api = collector . api ;
99+ return {
100+ ...collector . visitor ,
101+ Program ( node : TSESTree . Program ) {
102+ program = node ;
128103 } ,
129- } ,
130- rules : { "test/test-rule" : "error" } ,
104+ "Program:exit" ( ) {
105+ if ( program == null || api == null ) return ;
106+ state . harvested = true ;
107+ fact = harvest ( api , program ) ;
108+ } ,
109+ } ;
131110 } ) ;
132111 if ( ! state . harvested ) {
133112 throw new Error ( "runCollector: the harvest callback was not invoked" ) ;
0 commit comments