1- import * as AST from ' @eslint-react/ast' ;
2- import { ESLintUtils , AST_NODE_TYPES } from ' @typescript-eslint/utils' ;
3- import { unit } from ' @eslint-react/eff' ;
4- import { findVariable } from ' @eslint-react/var' ;
5- import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from ' @typescript-eslint/types' ;
6- import { nullThrows , NullThrowsReasons } from ' @typescript-eslint/utils/eslint-utils' ;
1+ import * as AST from " @eslint-react/ast" ;
2+ import { AST_NODE_TYPES , ESLintUtils } from " @typescript-eslint/utils" ;
3+ import { unit } from " @eslint-react/eff" ;
4+ import { findVariable } from " @eslint-react/var" ;
5+ import { AST_NODE_TYPES as AST_NODE_TYPES$1 } from " @typescript-eslint/types" ;
6+ import { NullThrowsReasons , nullThrows } from " @typescript-eslint/utils/eslint-utils" ;
77
8- // package.json
8+ //#region package.json
99var name = "@local/eslint-plugin-local" ;
1010var version = "0.0.0" ;
11+
12+ //#endregion
13+ //#region src/utils/create-rule.ts
1114function getDocsUrl ( ) {
12- return "TODO: add docs for local ESLint rules" ;
15+ return "TODO: add docs for local ESLint rules" ;
1316}
14- var createRule = ESLintUtils . RuleCreator ( getDocsUrl ) ;
15- function isInitializedFromSource ( name2 , source , initialScope ) {
16- const latestDef = findVariable ( name2 , initialScope ) ?. defs . at ( - 1 ) ;
17- if ( latestDef == null ) return false ;
18- const { node, parent } = latestDef ;
19- if ( node . type === AST_NODE_TYPES$1 . VariableDeclarator && node . init != null ) {
20- const { init } = node ;
21- if ( init . type === AST_NODE_TYPES$1 . MemberExpression && init . object . type === AST_NODE_TYPES$1 . Identifier ) {
22- return isInitializedFromSource ( init . object . name , source , initialScope ) ;
23- }
24- if ( init . type === AST_NODE_TYPES$1 . Identifier ) {
25- return isInitializedFromSource ( init . name , source , initialScope ) ;
26- }
27- const args = getRequireExpressionArguments ( init ) ;
28- const arg0 = args ?. [ 0 ] ;
29- if ( arg0 == null || ! AST . isLiteral ( arg0 , "string" ) ) {
30- return false ;
31- }
32- return arg0 . value === source || arg0 . value . startsWith ( `${ source } /` ) ;
33- }
34- return parent ?. type === AST_NODE_TYPES$1 . ImportDeclaration && parent . source . value === source ;
17+ const createRule = ESLintUtils . RuleCreator ( getDocsUrl ) ;
18+
19+ //#endregion
20+ //#region src/utils/is-initialized-from-source.ts
21+ /**
22+ * Check if an identifier is initialized from the given source
23+ * @param name The top-level identifier's name
24+ * @param source The import source to check against
25+ * @param initialScope Initial scope to search for the identifier
26+ * @returns Whether the identifier is initialized from the given source
27+ */
28+ function isInitializedFromSource ( name$1 , source , initialScope ) {
29+ const latestDef = findVariable ( name$1 , initialScope ) ?. defs . at ( - 1 ) ;
30+ if ( latestDef == null ) return false ;
31+ const { node, parent } = latestDef ;
32+ if ( node . type === AST_NODE_TYPES$1 . VariableDeclarator && node . init != null ) {
33+ const { init } = node ;
34+ if ( init . type === AST_NODE_TYPES$1 . MemberExpression && init . object . type === AST_NODE_TYPES$1 . Identifier ) return isInitializedFromSource ( init . object . name , source , initialScope ) ;
35+ if ( init . type === AST_NODE_TYPES$1 . Identifier ) return isInitializedFromSource ( init . name , source , initialScope ) ;
36+ const args = getRequireExpressionArguments ( init ) ;
37+ const arg0 = args ?. [ 0 ] ;
38+ if ( arg0 == null || ! AST . isLiteral ( arg0 , "string" ) ) return false ;
39+ return arg0 . value === source || arg0 . value . startsWith ( `${ source } /` ) ;
40+ }
41+ return parent ?. type === AST_NODE_TYPES$1 . ImportDeclaration && parent . source . value === source ;
3542}
3643function getRequireExpressionArguments ( node ) {
37- switch ( true ) {
38- // require('source')
39- case ( node . type === AST_NODE_TYPES$1 . CallExpression && node . callee . type === AST_NODE_TYPES$1 . Identifier && node . callee . name === "require" ) : {
40- return node . arguments ;
41- }
42- // require('source').variable
43- case node . type === AST_NODE_TYPES$1 . MemberExpression : {
44- return getRequireExpressionArguments ( node . object ) ;
45- }
46- }
47- return unit ;
44+ switch ( true ) {
45+ case node . type === AST_NODE_TYPES$1 . CallExpression && node . callee . type === AST_NODE_TYPES$1 . Identifier && node . callee . name === "require" : return node . arguments ;
46+ case node . type === AST_NODE_TYPES$1 . MemberExpression : return getRequireExpressionArguments ( node . object ) ;
47+ }
48+ return unit ;
4849}
4950
50- // src/rules/avoid-multiline-template-expression.ts
51- var RULE_NAME = "avoid-multiline-template-expression" ;
52- var RULE_FEATURES = [ ] ;
51+ //#endregion
52+ //#region src/rules/avoid-multiline-template-expression.ts
53+ const RULE_NAME$2 = "avoid-multiline-template-expression" ;
54+ const RULE_FEATURES$1 = [ ] ;
5355var avoid_multiline_template_expression_default = createRule ( {
54- meta : {
55- type : "problem" ,
56- docs : {
57- description : "disallow multiline template expressions" ,
58- [ Symbol . for ( "rule_features" ) ] : RULE_FEATURES
59- } ,
60- messages : {
61- avoidMultilineTemplateExpression : "Avoid multiline template expressions."
62- } ,
63- schema : [ ]
64- } ,
65- name : RULE_NAME ,
66- create,
67- defaultOptions : [ ]
56+ meta : {
57+ type : "problem" ,
58+ docs : {
59+ description : "disallow multiline template expressions" ,
60+ [ Symbol . for ( "rule_features" ) ] : RULE_FEATURES$1
61+ } ,
62+ messages : { avoidMultilineTemplateExpression : "Avoid multiline template expressions." } ,
63+ schema : [ ]
64+ } ,
65+ name : RULE_NAME$2 ,
66+ create : create$2 ,
67+ defaultOptions : [ ]
6868} ) ;
69- function create ( context ) {
70- return {
71- TemplateLiteral : ( node ) => {
72- if ( AST . isMultiLine ( node ) ) {
73- context . report ( {
74- messageId : "avoidMultilineTemplateExpression" ,
75- node
76- } ) ;
77- }
78- }
79- } ;
69+ function create$2 ( context ) {
70+ return { TemplateLiteral : ( node ) => {
71+ if ( AST . isMultiLine ( node ) ) context . report ( {
72+ messageId : "avoidMultilineTemplateExpression" ,
73+ node
74+ } ) ;
75+ } } ;
8076}
8177
82- // src/rules/no-shadow-underscore.ts
83- var RULE_NAME2 = "no-shadow-underscore" ;
84- var RULE_FEATURES2 = [ ] ;
78+ //#endregion
79+ //#region src/rules/no-shadow-underscore.ts
80+ const RULE_NAME$1 = "no-shadow-underscore" ;
81+ const RULE_FEATURES = [ ] ;
8582var no_shadow_underscore_default = createRule ( {
86- meta : {
87- type : "problem" ,
88- docs : {
89- description : "disallow shadowing of the underscore identifier" ,
90- [ Symbol . for ( "rule_features" ) ] : RULE_FEATURES2
91- } ,
92- messages : {
93- noShadowUnderscore : "In this codebase, '_' is used to represent the undefined. Avoid shadowing it."
94- } ,
95- schema : [ ]
96- } ,
97- name : RULE_NAME2 ,
98- create : create2 ,
99- defaultOptions : [ ]
83+ meta : {
84+ type : "problem" ,
85+ docs : {
86+ description : "disallow shadowing of the underscore identifier" ,
87+ [ Symbol . for ( "rule_features" ) ] : RULE_FEATURES
88+ } ,
89+ messages : { noShadowUnderscore : "In this codebase, '_' is used to represent the undefined. Avoid shadowing it." } ,
90+ schema : [ ]
91+ } ,
92+ name : RULE_NAME$1 ,
93+ create : create$1 ,
94+ defaultOptions : [ ]
10095} ) ;
101- function create2 ( context ) {
102- return {
103- "Identifier[name='_']" ( node ) {
104- const initialScope = context . sourceCode . getScope ( node ) ;
105- const isFromImport = isInitializedFromSource ( "_" , "@eslint-react/eff" , initialScope ) ;
106- if ( ! isFromImport ) {
107- context . report ( {
108- messageId : "noShadowUnderscore" ,
109- node
110- } ) ;
111- }
112- }
113- } ;
96+ function create$1 ( context ) {
97+ return { "Identifier[name='_']" ( node ) {
98+ const initialScope = context . sourceCode . getScope ( node ) ;
99+ const isFromImport = isInitializedFromSource ( "_" , "@eslint-react/eff" , initialScope ) ;
100+ if ( ! isFromImport ) context . report ( {
101+ messageId : "noShadowUnderscore" ,
102+ node
103+ } ) ;
104+ } } ;
114105}
115- var RULE_NAME3 = "prefer-eqeq-nullish-comparison" ;
106+
107+ //#endregion
108+ //#region src/rules/prefer-eqeq-nullish-comparison.ts
109+ const RULE_NAME = "prefer-eqeq-nullish-comparison" ;
116110var prefer_eqeq_nullish_comparison_default = createRule ( {
117- meta : {
118- type : "suggestion" ,
119- docs : {
120- description : "Enforces eqeqeq preferences around nullish comparisons."
121- } ,
122- fixable : "code" ,
123- hasSuggestions : true ,
124- messages : {
125- unexpectedComparison : "Unexpected strict comparison (`{{strictOperator}}`) with `{{nullishKind}}`. In this codebase, we prefer to use loose equality as a general-purpose nullish check when possible." ,
126- useLooseComparisonSuggestion : "Use loose comparison (`{{looseOperator}} null`) instead, to check both nullish values."
127- } ,
128- schema : [ ]
129- } ,
130- name : RULE_NAME3 ,
131- create : create3 ,
132- defaultOptions : [ ]
111+ meta : {
112+ type : "suggestion" ,
113+ docs : { description : "Enforces eqeqeq preferences around nullish comparisons." } ,
114+ fixable : "code" ,
115+ hasSuggestions : true ,
116+ messages : {
117+ unexpectedComparison : "Unexpected strict comparison (`{{strictOperator}}`) with `{{nullishKind}}`. In this codebase, we prefer to use loose equality as a general-purpose nullish check when possible." ,
118+ useLooseComparisonSuggestion : "Use loose comparison (`{{looseOperator}} null`) instead, to check both nullish values."
119+ } ,
120+ schema : [ ]
121+ } ,
122+ name : RULE_NAME ,
123+ create,
124+ defaultOptions : [ ]
133125} ) ;
134- function create3 ( context ) {
135- return {
136- BinaryExpression ( node ) {
137- if ( node . operator === "===" || node . operator === "!==" ) {
138- const offendingChild = [ node . left , node . right ] . find (
139- ( child ) => child . type === AST_NODE_TYPES . Identifier && child . name === "undefined" || child . type === AST_NODE_TYPES . Literal && child . raw === "null"
140- ) ;
141- if ( offendingChild == null ) {
142- return ;
143- }
144- const operatorToken = nullThrows (
145- context . sourceCode . getFirstTokenBetween (
146- node . left ,
147- node . right ,
148- ( token ) => token . value === node . operator
149- ) ,
150- NullThrowsReasons . MissingToken ( node . operator , "binary expression" )
151- ) ;
152- const wasLeft = node . left === offendingChild ;
153- const nullishKind = offendingChild . type === AST_NODE_TYPES . Identifier ? "undefined" : "null" ;
154- const looseOperator = node . operator === "===" ? "==" : "!=" ;
155- context . report ( {
156- messageId : "unexpectedComparison" ,
157- data : {
158- nullishKind,
159- strictOperator : node . operator
160- } ,
161- loc : wasLeft ? {
162- end : operatorToken . loc . end ,
163- start : node . left . loc . start
164- } : {
165- end : node . right . loc . end ,
166- start : operatorToken . loc . start
167- } ,
168- suggest : [
169- {
170- messageId : "useLooseComparisonSuggestion" ,
171- data : {
172- looseOperator
173- } ,
174- fix : ( fixer ) => [
175- fixer . replaceText ( offendingChild , "null" ) ,
176- fixer . replaceText ( operatorToken , looseOperator )
177- ]
178- }
179- ]
180- } ) ;
181- }
182- }
183- } ;
126+ function create ( context ) {
127+ return { BinaryExpression ( node ) {
128+ if ( node . operator === "===" || node . operator === "!==" ) {
129+ const offendingChild = [ node . left , node . right ] . find ( ( child ) => child . type === AST_NODE_TYPES . Identifier && child . name === "undefined" || child . type === AST_NODE_TYPES . Literal && child . raw === "null" ) ;
130+ if ( offendingChild == null ) return ;
131+ const operatorToken = nullThrows ( context . sourceCode . getFirstTokenBetween ( node . left , node . right , ( token ) => token . value === node . operator ) , NullThrowsReasons . MissingToken ( node . operator , "binary expression" ) ) ;
132+ const wasLeft = node . left === offendingChild ;
133+ const nullishKind = offendingChild . type === AST_NODE_TYPES . Identifier ? "undefined" : "null" ;
134+ const looseOperator = node . operator === "===" ? "==" : "!=" ;
135+ context . report ( {
136+ messageId : "unexpectedComparison" ,
137+ data : {
138+ nullishKind,
139+ strictOperator : node . operator
140+ } ,
141+ loc : wasLeft ? {
142+ end : operatorToken . loc . end ,
143+ start : node . left . loc . start
144+ } : {
145+ end : node . right . loc . end ,
146+ start : operatorToken . loc . start
147+ } ,
148+ suggest : [ {
149+ messageId : "useLooseComparisonSuggestion" ,
150+ data : { looseOperator } ,
151+ fix : ( fixer ) => [ fixer . replaceText ( offendingChild , "null" ) , fixer . replaceText ( operatorToken , looseOperator ) ]
152+ } ]
153+ } ) ;
154+ }
155+ } } ;
184156}
185157
186- // src/index.ts
187- var index_default = {
188- meta : {
189- name,
190- version
191- } ,
192- rules : {
193- "avoid-multiline-template-expression" : avoid_multiline_template_expression_default ,
194- "no-shadow-underscore" : no_shadow_underscore_default ,
195- "prefer-eqeq-nullish-comparison" : prefer_eqeq_nullish_comparison_default
196- }
158+ //#endregion
159+ //#region src/index.ts
160+ var src_default = {
161+ meta : {
162+ name,
163+ version
164+ } ,
165+ rules : {
166+ "avoid-multiline-template-expression" : avoid_multiline_template_expression_default ,
167+ "no-shadow-underscore" : no_shadow_underscore_default ,
168+ "prefer-eqeq-nullish-comparison" : prefer_eqeq_nullish_comparison_default
169+ }
197170} ;
198171
199- export { index_default as default } ;
172+ //#endregion
173+ export { src_default as default } ;
0 commit comments