index.js 86 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var eslintVisitorKeys = require('eslint-visitor-keys');
  4. /** @typedef {import("eslint").Scope.Scope} Scope */
  5. /** @typedef {import("estree").Node} Node */
  6. /**
  7. * Get the innermost scope which contains a given location.
  8. * @param {Scope} initialScope The initial scope to search.
  9. * @param {Node} node The location to search.
  10. * @returns {Scope} The innermost scope.
  11. */
  12. function getInnermostScope(initialScope, node) {
  13. const location = /** @type {[number, number]} */ (node.range)[0];
  14. let scope = initialScope;
  15. let found = false;
  16. do {
  17. found = false;
  18. for (const childScope of scope.childScopes) {
  19. const range = /** @type {[number, number]} */ (
  20. childScope.block.range
  21. );
  22. if (range[0] <= location && location < range[1]) {
  23. scope = childScope;
  24. found = true;
  25. break
  26. }
  27. }
  28. } while (found)
  29. return scope
  30. }
  31. /** @typedef {import("eslint").Scope.Scope} Scope */
  32. /** @typedef {import("eslint").Scope.Variable} Variable */
  33. /** @typedef {import("estree").Identifier} Identifier */
  34. /**
  35. * Find the variable of a given name.
  36. * @param {Scope} initialScope The scope to start finding.
  37. * @param {string|Identifier} nameOrNode The variable name to find. If this is a Node object then it should be an Identifier node.
  38. * @returns {Variable|null} The found variable or null.
  39. */
  40. function findVariable(initialScope, nameOrNode) {
  41. let name = "";
  42. /** @type {Scope|null} */
  43. let scope = initialScope;
  44. if (typeof nameOrNode === "string") {
  45. name = nameOrNode;
  46. } else {
  47. name = nameOrNode.name;
  48. scope = getInnermostScope(scope, nameOrNode);
  49. }
  50. while (scope != null) {
  51. const variable = scope.set.get(name);
  52. if (variable != null) {
  53. return variable
  54. }
  55. scope = scope.upper;
  56. }
  57. return null
  58. }
  59. /** @typedef {import("eslint").AST.Token} Token */
  60. /** @typedef {import("estree").Comment} Comment */
  61. /** @typedef {import("./types.mjs").ArrowToken} ArrowToken */
  62. /** @typedef {import("./types.mjs").CommaToken} CommaToken */
  63. /** @typedef {import("./types.mjs").SemicolonToken} SemicolonToken */
  64. /** @typedef {import("./types.mjs").ColonToken} ColonToken */
  65. /** @typedef {import("./types.mjs").OpeningParenToken} OpeningParenToken */
  66. /** @typedef {import("./types.mjs").ClosingParenToken} ClosingParenToken */
  67. /** @typedef {import("./types.mjs").OpeningBracketToken} OpeningBracketToken */
  68. /** @typedef {import("./types.mjs").ClosingBracketToken} ClosingBracketToken */
  69. /** @typedef {import("./types.mjs").OpeningBraceToken} OpeningBraceToken */
  70. /** @typedef {import("./types.mjs").ClosingBraceToken} ClosingBraceToken */
  71. /**
  72. * @template {string} Value
  73. * @typedef {import("./types.mjs").PunctuatorToken<Value>} PunctuatorToken
  74. */
  75. /** @typedef {Comment | Token} CommentOrToken */
  76. /**
  77. * Creates the negate function of the given function.
  78. * @param {function(CommentOrToken):boolean} f - The function to negate.
  79. * @returns {function(CommentOrToken):boolean} Negated function.
  80. */
  81. function negate(f) {
  82. return (token) => !f(token)
  83. }
  84. /**
  85. * Checks if the given token is a PunctuatorToken with the given value
  86. * @template {string} Value
  87. * @param {CommentOrToken} token - The token to check.
  88. * @param {Value} value - The value to check.
  89. * @returns {token is PunctuatorToken<Value>} `true` if the token is a PunctuatorToken with the given value.
  90. */
  91. function isPunctuatorTokenWithValue(token, value) {
  92. return token.type === "Punctuator" && token.value === value
  93. }
  94. /**
  95. * Checks if the given token is an arrow token or not.
  96. * @param {CommentOrToken} token - The token to check.
  97. * @returns {token is ArrowToken} `true` if the token is an arrow token.
  98. */
  99. function isArrowToken(token) {
  100. return isPunctuatorTokenWithValue(token, "=>")
  101. }
  102. /**
  103. * Checks if the given token is a comma token or not.
  104. * @param {CommentOrToken} token - The token to check.
  105. * @returns {token is CommaToken} `true` if the token is a comma token.
  106. */
  107. function isCommaToken(token) {
  108. return isPunctuatorTokenWithValue(token, ",")
  109. }
  110. /**
  111. * Checks if the given token is a semicolon token or not.
  112. * @param {CommentOrToken} token - The token to check.
  113. * @returns {token is SemicolonToken} `true` if the token is a semicolon token.
  114. */
  115. function isSemicolonToken(token) {
  116. return isPunctuatorTokenWithValue(token, ";")
  117. }
  118. /**
  119. * Checks if the given token is a colon token or not.
  120. * @param {CommentOrToken} token - The token to check.
  121. * @returns {token is ColonToken} `true` if the token is a colon token.
  122. */
  123. function isColonToken(token) {
  124. return isPunctuatorTokenWithValue(token, ":")
  125. }
  126. /**
  127. * Checks if the given token is an opening parenthesis token or not.
  128. * @param {CommentOrToken} token - The token to check.
  129. * @returns {token is OpeningParenToken} `true` if the token is an opening parenthesis token.
  130. */
  131. function isOpeningParenToken(token) {
  132. return isPunctuatorTokenWithValue(token, "(")
  133. }
  134. /**
  135. * Checks if the given token is a closing parenthesis token or not.
  136. * @param {CommentOrToken} token - The token to check.
  137. * @returns {token is ClosingParenToken} `true` if the token is a closing parenthesis token.
  138. */
  139. function isClosingParenToken(token) {
  140. return isPunctuatorTokenWithValue(token, ")")
  141. }
  142. /**
  143. * Checks if the given token is an opening square bracket token or not.
  144. * @param {CommentOrToken} token - The token to check.
  145. * @returns {token is OpeningBracketToken} `true` if the token is an opening square bracket token.
  146. */
  147. function isOpeningBracketToken(token) {
  148. return isPunctuatorTokenWithValue(token, "[")
  149. }
  150. /**
  151. * Checks if the given token is a closing square bracket token or not.
  152. * @param {CommentOrToken} token - The token to check.
  153. * @returns {token is ClosingBracketToken} `true` if the token is a closing square bracket token.
  154. */
  155. function isClosingBracketToken(token) {
  156. return isPunctuatorTokenWithValue(token, "]")
  157. }
  158. /**
  159. * Checks if the given token is an opening brace token or not.
  160. * @param {CommentOrToken} token - The token to check.
  161. * @returns {token is OpeningBraceToken} `true` if the token is an opening brace token.
  162. */
  163. function isOpeningBraceToken(token) {
  164. return isPunctuatorTokenWithValue(token, "{")
  165. }
  166. /**
  167. * Checks if the given token is a closing brace token or not.
  168. * @param {CommentOrToken} token - The token to check.
  169. * @returns {token is ClosingBraceToken} `true` if the token is a closing brace token.
  170. */
  171. function isClosingBraceToken(token) {
  172. return isPunctuatorTokenWithValue(token, "}")
  173. }
  174. /**
  175. * Checks if the given token is a comment token or not.
  176. * @param {CommentOrToken} token - The token to check.
  177. * @returns {token is Comment} `true` if the token is a comment token.
  178. */
  179. function isCommentToken(token) {
  180. return ["Block", "Line", "Shebang"].includes(token.type)
  181. }
  182. const isNotArrowToken = negate(isArrowToken);
  183. const isNotCommaToken = negate(isCommaToken);
  184. const isNotSemicolonToken = negate(isSemicolonToken);
  185. const isNotColonToken = negate(isColonToken);
  186. const isNotOpeningParenToken = negate(isOpeningParenToken);
  187. const isNotClosingParenToken = negate(isClosingParenToken);
  188. const isNotOpeningBracketToken = negate(isOpeningBracketToken);
  189. const isNotClosingBracketToken = negate(isClosingBracketToken);
  190. const isNotOpeningBraceToken = negate(isOpeningBraceToken);
  191. const isNotClosingBraceToken = negate(isClosingBraceToken);
  192. const isNotCommentToken = negate(isCommentToken);
  193. /** @typedef {import("eslint").Rule.Node} RuleNode */
  194. /** @typedef {import("eslint").SourceCode} SourceCode */
  195. /** @typedef {import("eslint").AST.Token} Token */
  196. /** @typedef {import("estree").Function} FunctionNode */
  197. /** @typedef {import("estree").FunctionDeclaration} FunctionDeclaration */
  198. /** @typedef {import("estree").FunctionExpression} FunctionExpression */
  199. /** @typedef {import("estree").SourceLocation} SourceLocation */
  200. /** @typedef {import("estree").Position} Position */
  201. /**
  202. * Get the `(` token of the given function node.
  203. * @param {FunctionExpression | FunctionDeclaration} node - The function node to get.
  204. * @param {SourceCode} sourceCode - The source code object to get tokens.
  205. * @returns {Token} `(` token.
  206. */
  207. function getOpeningParenOfParams(node, sourceCode) {
  208. return node.id
  209. ? /** @type {Token} */ (
  210. sourceCode.getTokenAfter(node.id, isOpeningParenToken)
  211. )
  212. : /** @type {Token} */ (
  213. sourceCode.getFirstToken(node, isOpeningParenToken)
  214. )
  215. }
  216. /**
  217. * Get the location of the given function node for reporting.
  218. * @param {FunctionNode} node - The function node to get.
  219. * @param {SourceCode} sourceCode - The source code object to get tokens.
  220. * @returns {SourceLocation|null} The location of the function node for reporting.
  221. */
  222. function getFunctionHeadLocation(node, sourceCode) {
  223. const parent = /** @type {RuleNode} */ (node).parent;
  224. /** @type {Position|null} */
  225. let start = null;
  226. /** @type {Position|null} */
  227. let end = null;
  228. if (node.type === "ArrowFunctionExpression") {
  229. const arrowToken = /** @type {Token} */ (
  230. sourceCode.getTokenBefore(node.body, isArrowToken)
  231. );
  232. start = arrowToken.loc.start;
  233. end = arrowToken.loc.end;
  234. } else if (
  235. parent.type === "Property" ||
  236. parent.type === "MethodDefinition" ||
  237. parent.type === "PropertyDefinition"
  238. ) {
  239. start = /** @type {SourceLocation} */ (parent.loc).start;
  240. end = getOpeningParenOfParams(node, sourceCode).loc.start;
  241. } else {
  242. start = /** @type {SourceLocation} */ (node.loc).start;
  243. end = getOpeningParenOfParams(node, sourceCode).loc.start;
  244. }
  245. return {
  246. start: { ...start },
  247. end: { ...end },
  248. }
  249. }
  250. /* globals globalThis, global, self, window */
  251. /** @typedef {import("./types.mjs").StaticValue} StaticValue */
  252. /** @typedef {import("eslint").Scope.Scope} Scope */
  253. /** @typedef {import("eslint").Scope.Variable} Variable */
  254. /** @typedef {import("estree").Node} Node */
  255. /** @typedef {import("@typescript-eslint/types").TSESTree.Node} TSESTreeNode */
  256. /** @typedef {import("@typescript-eslint/types").TSESTree.AST_NODE_TYPES} TSESTreeNodeTypes */
  257. /** @typedef {import("@typescript-eslint/types").TSESTree.MemberExpression} MemberExpression */
  258. /** @typedef {import("@typescript-eslint/types").TSESTree.Property} Property */
  259. /** @typedef {import("@typescript-eslint/types").TSESTree.RegExpLiteral} RegExpLiteral */
  260. /** @typedef {import("@typescript-eslint/types").TSESTree.BigIntLiteral} BigIntLiteral */
  261. /** @typedef {import("@typescript-eslint/types").TSESTree.Literal} Literal */
  262. const globalObject =
  263. typeof globalThis !== "undefined"
  264. ? globalThis
  265. : // @ts-ignore
  266. typeof self !== "undefined"
  267. ? // @ts-ignore
  268. self
  269. : // @ts-ignore
  270. typeof window !== "undefined"
  271. ? // @ts-ignore
  272. window
  273. : typeof global !== "undefined"
  274. ? global
  275. : {};
  276. const builtinNames = Object.freeze(
  277. new Set([
  278. "Array",
  279. "ArrayBuffer",
  280. "BigInt",
  281. "BigInt64Array",
  282. "BigUint64Array",
  283. "Boolean",
  284. "DataView",
  285. "Date",
  286. "decodeURI",
  287. "decodeURIComponent",
  288. "encodeURI",
  289. "encodeURIComponent",
  290. "escape",
  291. "Float32Array",
  292. "Float64Array",
  293. "Function",
  294. "Infinity",
  295. "Int16Array",
  296. "Int32Array",
  297. "Int8Array",
  298. "isFinite",
  299. "isNaN",
  300. "isPrototypeOf",
  301. "JSON",
  302. "Map",
  303. "Math",
  304. "NaN",
  305. "Number",
  306. "Object",
  307. "parseFloat",
  308. "parseInt",
  309. "Promise",
  310. "Proxy",
  311. "Reflect",
  312. "RegExp",
  313. "Set",
  314. "String",
  315. "Symbol",
  316. "Uint16Array",
  317. "Uint32Array",
  318. "Uint8Array",
  319. "Uint8ClampedArray",
  320. "undefined",
  321. "unescape",
  322. "WeakMap",
  323. "WeakSet",
  324. ]),
  325. );
  326. const callAllowed = new Set(
  327. [
  328. Array.isArray,
  329. Array.of,
  330. Array.prototype.at,
  331. Array.prototype.concat,
  332. Array.prototype.entries,
  333. Array.prototype.every,
  334. Array.prototype.filter,
  335. Array.prototype.find,
  336. Array.prototype.findIndex,
  337. Array.prototype.flat,
  338. Array.prototype.includes,
  339. Array.prototype.indexOf,
  340. Array.prototype.join,
  341. Array.prototype.keys,
  342. Array.prototype.lastIndexOf,
  343. Array.prototype.slice,
  344. Array.prototype.some,
  345. Array.prototype.toString,
  346. Array.prototype.values,
  347. typeof BigInt === "function" ? BigInt : undefined,
  348. Boolean,
  349. Date,
  350. Date.parse,
  351. decodeURI,
  352. decodeURIComponent,
  353. encodeURI,
  354. encodeURIComponent,
  355. escape,
  356. isFinite,
  357. isNaN,
  358. // @ts-ignore
  359. isPrototypeOf,
  360. Map,
  361. Map.prototype.entries,
  362. Map.prototype.get,
  363. Map.prototype.has,
  364. Map.prototype.keys,
  365. Map.prototype.values,
  366. .../** @type {(keyof typeof Math)[]} */ (
  367. Object.getOwnPropertyNames(Math)
  368. )
  369. .filter((k) => k !== "random")
  370. .map((k) => Math[k])
  371. .filter((f) => typeof f === "function"),
  372. Number,
  373. Number.isFinite,
  374. Number.isNaN,
  375. Number.parseFloat,
  376. Number.parseInt,
  377. Number.prototype.toExponential,
  378. Number.prototype.toFixed,
  379. Number.prototype.toPrecision,
  380. Number.prototype.toString,
  381. Object,
  382. Object.entries,
  383. Object.is,
  384. Object.isExtensible,
  385. Object.isFrozen,
  386. Object.isSealed,
  387. Object.keys,
  388. Object.values,
  389. parseFloat,
  390. parseInt,
  391. RegExp,
  392. Set,
  393. Set.prototype.entries,
  394. Set.prototype.has,
  395. Set.prototype.keys,
  396. Set.prototype.values,
  397. String,
  398. String.fromCharCode,
  399. String.fromCodePoint,
  400. String.raw,
  401. String.prototype.at,
  402. String.prototype.charAt,
  403. String.prototype.charCodeAt,
  404. String.prototype.codePointAt,
  405. String.prototype.concat,
  406. String.prototype.endsWith,
  407. String.prototype.includes,
  408. String.prototype.indexOf,
  409. String.prototype.lastIndexOf,
  410. String.prototype.normalize,
  411. String.prototype.padEnd,
  412. String.prototype.padStart,
  413. String.prototype.slice,
  414. String.prototype.startsWith,
  415. String.prototype.substr,
  416. String.prototype.substring,
  417. String.prototype.toLowerCase,
  418. String.prototype.toString,
  419. String.prototype.toUpperCase,
  420. String.prototype.trim,
  421. String.prototype.trimEnd,
  422. String.prototype.trimLeft,
  423. String.prototype.trimRight,
  424. String.prototype.trimStart,
  425. Symbol.for,
  426. Symbol.keyFor,
  427. unescape,
  428. ].filter((f) => typeof f === "function"),
  429. );
  430. const callPassThrough = new Set([
  431. Object.freeze,
  432. Object.preventExtensions,
  433. Object.seal,
  434. ]);
  435. /** @type {ReadonlyArray<readonly [Function, ReadonlySet<string>]>} */
  436. const getterAllowed = [
  437. [Map, new Set(["size"])],
  438. [
  439. RegExp,
  440. new Set([
  441. "dotAll",
  442. "flags",
  443. "global",
  444. "hasIndices",
  445. "ignoreCase",
  446. "multiline",
  447. "source",
  448. "sticky",
  449. "unicode",
  450. ]),
  451. ],
  452. [Set, new Set(["size"])],
  453. ];
  454. /**
  455. * Get the property descriptor.
  456. * @param {object} object The object to get.
  457. * @param {string|number|symbol} name The property name to get.
  458. */
  459. function getPropertyDescriptor(object, name) {
  460. let x = object;
  461. while ((typeof x === "object" || typeof x === "function") && x !== null) {
  462. const d = Object.getOwnPropertyDescriptor(x, name);
  463. if (d) {
  464. return d
  465. }
  466. x = Object.getPrototypeOf(x);
  467. }
  468. return null
  469. }
  470. /**
  471. * Check if a property is getter or not.
  472. * @param {object} object The object to check.
  473. * @param {string|number|symbol} name The property name to check.
  474. */
  475. function isGetter(object, name) {
  476. const d = getPropertyDescriptor(object, name);
  477. return d != null && d.get != null
  478. }
  479. /**
  480. * Get the element values of a given node list.
  481. * @param {(Node|TSESTreeNode|null)[]} nodeList The node list to get values.
  482. * @param {Scope|undefined|null} initialScope The initial scope to find variables.
  483. * @returns {any[]|null} The value list if all nodes are constant. Otherwise, null.
  484. */
  485. function getElementValues(nodeList, initialScope) {
  486. const valueList = [];
  487. for (let i = 0; i < nodeList.length; ++i) {
  488. const elementNode = nodeList[i];
  489. if (elementNode == null) {
  490. valueList.length = i + 1;
  491. } else if (elementNode.type === "SpreadElement") {
  492. const argument = getStaticValueR(elementNode.argument, initialScope);
  493. if (argument == null) {
  494. return null
  495. }
  496. valueList.push(.../** @type {Iterable<any>} */ (argument.value));
  497. } else {
  498. const element = getStaticValueR(elementNode, initialScope);
  499. if (element == null) {
  500. return null
  501. }
  502. valueList.push(element.value);
  503. }
  504. }
  505. return valueList
  506. }
  507. /**
  508. * Checks if a variable is a built-in global.
  509. * @param {Variable|null} variable The variable to check.
  510. * @returns {variable is Variable & {defs:[]}}
  511. */
  512. function isBuiltinGlobal(variable) {
  513. return (
  514. variable != null &&
  515. variable.defs.length === 0 &&
  516. builtinNames.has(variable.name) &&
  517. variable.name in globalObject
  518. )
  519. }
  520. /**
  521. * Checks if a variable can be considered as a constant.
  522. * @param {Variable} variable
  523. * @returns {variable is Variable & {defs: [import("eslint").Scope.Definition & { type: "Variable" }]}} True if the variable can be considered as a constant.
  524. */
  525. function canBeConsideredConst(variable) {
  526. if (variable.defs.length !== 1) {
  527. return false
  528. }
  529. const def = variable.defs[0];
  530. return Boolean(
  531. def.parent &&
  532. def.type === "Variable" &&
  533. (def.parent.kind === "const" || isEffectivelyConst(variable)),
  534. )
  535. }
  536. /**
  537. * Returns whether the given variable is never written to after initialization.
  538. * @param {Variable} variable
  539. * @returns {boolean}
  540. */
  541. function isEffectivelyConst(variable) {
  542. const refs = variable.references;
  543. const inits = refs.filter((r) => r.init).length;
  544. const reads = refs.filter((r) => r.isReadOnly()).length;
  545. if (inits === 1 && reads + inits === refs.length) {
  546. // there is only one init and all other references only read
  547. return true
  548. }
  549. return false
  550. }
  551. /**
  552. * Checks if a variable has mutation in its property.
  553. * @param {Variable} variable The variable to check.
  554. * @param {Scope|null} initialScope The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
  555. * @returns {boolean} True if the variable has mutation in its property.
  556. */
  557. function hasMutationInProperty(variable, initialScope) {
  558. for (const ref of variable.references) {
  559. let node = /** @type {TSESTreeNode} */ (ref.identifier);
  560. while (node && node.parent && node.parent.type === "MemberExpression") {
  561. node = node.parent;
  562. }
  563. if (!node || !node.parent) {
  564. continue
  565. }
  566. if (
  567. (node.parent.type === "AssignmentExpression" &&
  568. node.parent.left === node) ||
  569. (node.parent.type === "UpdateExpression" &&
  570. node.parent.argument === node)
  571. ) {
  572. // This is a mutation.
  573. return true
  574. }
  575. if (
  576. node.parent.type === "CallExpression" &&
  577. node.parent.callee === node &&
  578. node.type === "MemberExpression"
  579. ) {
  580. const methodName = getStaticPropertyNameValue(node, initialScope);
  581. if (isNameOfMutationArrayMethod(methodName)) {
  582. // This is a mutation.
  583. return true
  584. }
  585. }
  586. }
  587. return false
  588. /**
  589. * Checks if a method name is one of the mutation array methods.
  590. * @param {StaticValue|null} methodName The method name to check.
  591. * @returns {boolean} True if the method name is a mutation array method.
  592. */
  593. function isNameOfMutationArrayMethod(methodName) {
  594. if (methodName == null || methodName.value == null) {
  595. return false
  596. }
  597. const name = methodName.value;
  598. return (
  599. name === "copyWithin" ||
  600. name === "fill" ||
  601. name === "pop" ||
  602. name === "push" ||
  603. name === "reverse" ||
  604. name === "shift" ||
  605. name === "sort" ||
  606. name === "splice" ||
  607. name === "unshift"
  608. )
  609. }
  610. }
  611. /**
  612. * @template {TSESTreeNodeTypes} T
  613. * @callback VisitorCallback
  614. * @param {TSESTreeNode & { type: T }} node
  615. * @param {Scope|undefined|null} initialScope
  616. * @returns {StaticValue | null}
  617. */
  618. /**
  619. * @typedef { { [K in TSESTreeNodeTypes]?: VisitorCallback<K> } } Operations
  620. */
  621. /**
  622. * @type {Operations}
  623. */
  624. const operations = Object.freeze({
  625. ArrayExpression(node, initialScope) {
  626. const elements = getElementValues(node.elements, initialScope);
  627. return elements != null ? { value: elements } : null
  628. },
  629. AssignmentExpression(node, initialScope) {
  630. if (node.operator === "=") {
  631. return getStaticValueR(node.right, initialScope)
  632. }
  633. return null
  634. },
  635. //eslint-disable-next-line complexity
  636. BinaryExpression(node, initialScope) {
  637. if (node.operator === "in" || node.operator === "instanceof") {
  638. // Not supported.
  639. return null
  640. }
  641. const left = getStaticValueR(node.left, initialScope);
  642. const right = getStaticValueR(node.right, initialScope);
  643. if (left != null && right != null) {
  644. switch (node.operator) {
  645. case "==":
  646. return { value: left.value == right.value } //eslint-disable-line eqeqeq
  647. case "!=":
  648. return { value: left.value != right.value } //eslint-disable-line eqeqeq
  649. case "===":
  650. return { value: left.value === right.value }
  651. case "!==":
  652. return { value: left.value !== right.value }
  653. case "<":
  654. return {
  655. value:
  656. /** @type {any} */ (left.value) <
  657. /** @type {any} */ (right.value),
  658. }
  659. case "<=":
  660. return {
  661. value:
  662. /** @type {any} */ (left.value) <=
  663. /** @type {any} */ (right.value),
  664. }
  665. case ">":
  666. return {
  667. value:
  668. /** @type {any} */ (left.value) >
  669. /** @type {any} */ (right.value),
  670. }
  671. case ">=":
  672. return {
  673. value:
  674. /** @type {any} */ (left.value) >=
  675. /** @type {any} */ (right.value),
  676. }
  677. case "<<":
  678. return {
  679. value:
  680. /** @type {any} */ (left.value) <<
  681. /** @type {any} */ (right.value),
  682. }
  683. case ">>":
  684. return {
  685. value:
  686. /** @type {any} */ (left.value) >>
  687. /** @type {any} */ (right.value),
  688. }
  689. case ">>>":
  690. return {
  691. value:
  692. /** @type {any} */ (left.value) >>>
  693. /** @type {any} */ (right.value),
  694. }
  695. case "+":
  696. return {
  697. value:
  698. /** @type {any} */ (left.value) +
  699. /** @type {any} */ (right.value),
  700. }
  701. case "-":
  702. return {
  703. value:
  704. /** @type {any} */ (left.value) -
  705. /** @type {any} */ (right.value),
  706. }
  707. case "*":
  708. return {
  709. value:
  710. /** @type {any} */ (left.value) *
  711. /** @type {any} */ (right.value),
  712. }
  713. case "/":
  714. return {
  715. value:
  716. /** @type {any} */ (left.value) /
  717. /** @type {any} */ (right.value),
  718. }
  719. case "%":
  720. return {
  721. value:
  722. /** @type {any} */ (left.value) %
  723. /** @type {any} */ (right.value),
  724. }
  725. case "**":
  726. return {
  727. value:
  728. /** @type {any} */ (left.value) **
  729. /** @type {any} */ (right.value),
  730. }
  731. case "|":
  732. return {
  733. value:
  734. /** @type {any} */ (left.value) |
  735. /** @type {any} */ (right.value),
  736. }
  737. case "^":
  738. return {
  739. value:
  740. /** @type {any} */ (left.value) ^
  741. /** @type {any} */ (right.value),
  742. }
  743. case "&":
  744. return {
  745. value:
  746. /** @type {any} */ (left.value) &
  747. /** @type {any} */ (right.value),
  748. }
  749. // no default
  750. }
  751. }
  752. return null
  753. },
  754. CallExpression(node, initialScope) {
  755. const calleeNode = node.callee;
  756. const args = getElementValues(node.arguments, initialScope);
  757. if (args != null) {
  758. if (calleeNode.type === "MemberExpression") {
  759. if (calleeNode.property.type === "PrivateIdentifier") {
  760. return null
  761. }
  762. const object = getStaticValueR(calleeNode.object, initialScope);
  763. if (object != null) {
  764. if (
  765. object.value == null &&
  766. (object.optional || node.optional)
  767. ) {
  768. return { value: undefined, optional: true }
  769. }
  770. const property = getStaticPropertyNameValue(
  771. calleeNode,
  772. initialScope,
  773. );
  774. if (property != null) {
  775. const receiver =
  776. /** @type {Record<PropertyKey, (...args: any[]) => any>} */ (
  777. object.value
  778. );
  779. const methodName = /** @type {PropertyKey} */ (
  780. property.value
  781. );
  782. if (callAllowed.has(receiver[methodName])) {
  783. return {
  784. value: receiver[methodName](...args),
  785. }
  786. }
  787. if (callPassThrough.has(receiver[methodName])) {
  788. return { value: args[0] }
  789. }
  790. }
  791. }
  792. } else {
  793. const callee = getStaticValueR(calleeNode, initialScope);
  794. if (callee != null) {
  795. if (callee.value == null && node.optional) {
  796. return { value: undefined, optional: true }
  797. }
  798. const func = /** @type {(...args: any[]) => any} */ (
  799. callee.value
  800. );
  801. if (callAllowed.has(func)) {
  802. return { value: func(...args) }
  803. }
  804. if (callPassThrough.has(func)) {
  805. return { value: args[0] }
  806. }
  807. }
  808. }
  809. }
  810. return null
  811. },
  812. ConditionalExpression(node, initialScope) {
  813. const test = getStaticValueR(node.test, initialScope);
  814. if (test != null) {
  815. return test.value
  816. ? getStaticValueR(node.consequent, initialScope)
  817. : getStaticValueR(node.alternate, initialScope)
  818. }
  819. return null
  820. },
  821. ExpressionStatement(node, initialScope) {
  822. return getStaticValueR(node.expression, initialScope)
  823. },
  824. Identifier(node, initialScope) {
  825. if (initialScope != null) {
  826. const variable = findVariable(initialScope, node);
  827. if (variable != null) {
  828. // Built-in globals.
  829. if (isBuiltinGlobal(variable)) {
  830. return { value: globalObject[variable.name] }
  831. }
  832. // Constants.
  833. if (canBeConsideredConst(variable)) {
  834. const def = variable.defs[0];
  835. if (
  836. // TODO(mysticatea): don't support destructuring here.
  837. def.node.id.type === "Identifier"
  838. ) {
  839. const init = getStaticValueR(
  840. def.node.init,
  841. initialScope,
  842. );
  843. if (
  844. init &&
  845. typeof init.value === "object" &&
  846. init.value !== null
  847. ) {
  848. if (hasMutationInProperty(variable, initialScope)) {
  849. // This variable has mutation in its property.
  850. return null
  851. }
  852. }
  853. return init
  854. }
  855. }
  856. }
  857. }
  858. return null
  859. },
  860. Literal(node) {
  861. const literal =
  862. /** @type {Partial<Literal> & Partial<RegExpLiteral> & Partial<BigIntLiteral>} */ (
  863. node
  864. );
  865. //istanbul ignore if : this is implementation-specific behavior.
  866. if (
  867. (literal.regex != null || literal.bigint != null) &&
  868. literal.value == null
  869. ) {
  870. // It was a RegExp/BigInt literal, but Node.js didn't support it.
  871. return null
  872. }
  873. return { value: literal.value }
  874. },
  875. LogicalExpression(node, initialScope) {
  876. const left = getStaticValueR(node.left, initialScope);
  877. if (left != null) {
  878. if (
  879. (node.operator === "||" && Boolean(left.value) === true) ||
  880. (node.operator === "&&" && Boolean(left.value) === false) ||
  881. (node.operator === "??" && left.value != null)
  882. ) {
  883. return left
  884. }
  885. const right = getStaticValueR(node.right, initialScope);
  886. if (right != null) {
  887. return right
  888. }
  889. }
  890. return null
  891. },
  892. MemberExpression(node, initialScope) {
  893. if (node.property.type === "PrivateIdentifier") {
  894. return null
  895. }
  896. const object = getStaticValueR(node.object, initialScope);
  897. if (object != null) {
  898. if (object.value == null && (object.optional || node.optional)) {
  899. return { value: undefined, optional: true }
  900. }
  901. const property = getStaticPropertyNameValue(node, initialScope);
  902. if (property != null) {
  903. if (
  904. !isGetter(
  905. /** @type {object} */ (object.value),
  906. /** @type {PropertyKey} */ (property.value),
  907. )
  908. ) {
  909. return {
  910. value: /** @type {Record<PropertyKey, unknown>} */ (
  911. object.value
  912. )[/** @type {PropertyKey} */ (property.value)],
  913. }
  914. }
  915. for (const [classFn, allowed] of getterAllowed) {
  916. if (
  917. object.value instanceof classFn &&
  918. allowed.has(/** @type {string} */ (property.value))
  919. ) {
  920. return {
  921. value: /** @type {Record<PropertyKey, unknown>} */ (
  922. object.value
  923. )[/** @type {PropertyKey} */ (property.value)],
  924. }
  925. }
  926. }
  927. }
  928. }
  929. return null
  930. },
  931. ChainExpression(node, initialScope) {
  932. const expression = getStaticValueR(node.expression, initialScope);
  933. if (expression != null) {
  934. return { value: expression.value }
  935. }
  936. return null
  937. },
  938. NewExpression(node, initialScope) {
  939. const callee = getStaticValueR(node.callee, initialScope);
  940. const args = getElementValues(node.arguments, initialScope);
  941. if (callee != null && args != null) {
  942. const Func = /** @type {new (...args: any[]) => any} */ (
  943. callee.value
  944. );
  945. if (callAllowed.has(Func)) {
  946. return { value: new Func(...args) }
  947. }
  948. }
  949. return null
  950. },
  951. ObjectExpression(node, initialScope) {
  952. /** @type {Record<PropertyKey, unknown>} */
  953. const object = {};
  954. for (const propertyNode of node.properties) {
  955. if (propertyNode.type === "Property") {
  956. if (propertyNode.kind !== "init") {
  957. return null
  958. }
  959. const key = getStaticPropertyNameValue(
  960. propertyNode,
  961. initialScope,
  962. );
  963. const value = getStaticValueR(propertyNode.value, initialScope);
  964. if (key == null || value == null) {
  965. return null
  966. }
  967. object[/** @type {PropertyKey} */ (key.value)] = value.value;
  968. } else if (
  969. propertyNode.type === "SpreadElement" ||
  970. // @ts-expect-error -- Backward compatibility
  971. propertyNode.type === "ExperimentalSpreadProperty"
  972. ) {
  973. const argument = getStaticValueR(
  974. propertyNode.argument,
  975. initialScope,
  976. );
  977. if (argument == null) {
  978. return null
  979. }
  980. Object.assign(object, argument.value);
  981. } else {
  982. return null
  983. }
  984. }
  985. return { value: object }
  986. },
  987. SequenceExpression(node, initialScope) {
  988. const last = node.expressions[node.expressions.length - 1];
  989. return getStaticValueR(last, initialScope)
  990. },
  991. TaggedTemplateExpression(node, initialScope) {
  992. const tag = getStaticValueR(node.tag, initialScope);
  993. const expressions = getElementValues(
  994. node.quasi.expressions,
  995. initialScope,
  996. );
  997. if (tag != null && expressions != null) {
  998. const func = /** @type {(...args: any[]) => any} */ (tag.value);
  999. /** @type {any[] & { raw?: string[] }} */
  1000. const strings = node.quasi.quasis.map((q) => q.value.cooked);
  1001. strings.raw = node.quasi.quasis.map((q) => q.value.raw);
  1002. if (func === String.raw) {
  1003. return { value: func(strings, ...expressions) }
  1004. }
  1005. }
  1006. return null
  1007. },
  1008. TemplateLiteral(node, initialScope) {
  1009. const expressions = getElementValues(node.expressions, initialScope);
  1010. if (expressions != null) {
  1011. let value = node.quasis[0].value.cooked;
  1012. for (let i = 0; i < expressions.length; ++i) {
  1013. value += expressions[i];
  1014. value += /** @type {string} */ (node.quasis[i + 1].value.cooked);
  1015. }
  1016. return { value }
  1017. }
  1018. return null
  1019. },
  1020. UnaryExpression(node, initialScope) {
  1021. if (node.operator === "delete") {
  1022. // Not supported.
  1023. return null
  1024. }
  1025. if (node.operator === "void") {
  1026. return { value: undefined }
  1027. }
  1028. const arg = getStaticValueR(node.argument, initialScope);
  1029. if (arg != null) {
  1030. switch (node.operator) {
  1031. case "-":
  1032. return { value: -(/** @type {any} */ (arg.value)) }
  1033. case "+":
  1034. return { value: +(/** @type {any} */ (arg.value)) } //eslint-disable-line no-implicit-coercion
  1035. case "!":
  1036. return { value: !arg.value }
  1037. case "~":
  1038. return { value: ~(/** @type {any} */ (arg.value)) }
  1039. case "typeof":
  1040. return { value: typeof arg.value }
  1041. // no default
  1042. }
  1043. }
  1044. return null
  1045. },
  1046. TSAsExpression(node, initialScope) {
  1047. return getStaticValueR(node.expression, initialScope)
  1048. },
  1049. TSSatisfiesExpression(node, initialScope) {
  1050. return getStaticValueR(node.expression, initialScope)
  1051. },
  1052. TSTypeAssertion(node, initialScope) {
  1053. return getStaticValueR(node.expression, initialScope)
  1054. },
  1055. TSNonNullExpression(node, initialScope) {
  1056. return getStaticValueR(node.expression, initialScope)
  1057. },
  1058. TSInstantiationExpression(node, initialScope) {
  1059. return getStaticValueR(node.expression, initialScope)
  1060. },
  1061. });
  1062. /**
  1063. * Get the value of a given node if it's a static value.
  1064. * @param {Node|TSESTreeNode|null|undefined} node The node to get.
  1065. * @param {Scope|undefined|null} initialScope The scope to start finding variable.
  1066. * @returns {StaticValue|null} The static value of the node, or `null`.
  1067. */
  1068. function getStaticValueR(node, initialScope) {
  1069. if (node != null && Object.hasOwnProperty.call(operations, node.type)) {
  1070. return /** @type {VisitorCallback<any>} */ (operations[node.type])(
  1071. /** @type {TSESTreeNode} */ (node),
  1072. initialScope,
  1073. )
  1074. }
  1075. return null
  1076. }
  1077. /**
  1078. * Get the static value of property name from a MemberExpression node or a Property node.
  1079. * @param {MemberExpression|Property} node The node to get.
  1080. * @param {Scope|null} [initialScope] The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
  1081. * @returns {StaticValue|null} The static value of the property name of the node, or `null`.
  1082. */
  1083. function getStaticPropertyNameValue(node, initialScope) {
  1084. const nameNode = node.type === "Property" ? node.key : node.property;
  1085. if (node.computed) {
  1086. return getStaticValueR(nameNode, initialScope)
  1087. }
  1088. if (nameNode.type === "Identifier") {
  1089. return { value: nameNode.name }
  1090. }
  1091. if (nameNode.type === "Literal") {
  1092. if (/** @type {Partial<BigIntLiteral>} */ (nameNode).bigint) {
  1093. return { value: /** @type {BigIntLiteral} */ (nameNode).bigint }
  1094. }
  1095. return { value: String(nameNode.value) }
  1096. }
  1097. return null
  1098. }
  1099. /**
  1100. * Get the value of a given node if it's a static value.
  1101. * @param {Node} node The node to get.
  1102. * @param {Scope|null} [initialScope] The scope to start finding variable. Optional. If this scope was given, this tries to resolve identifier references which are in the given node as much as possible.
  1103. * @returns {StaticValue | null} The static value of the node, or `null`.
  1104. */
  1105. function getStaticValue(node, initialScope = null) {
  1106. try {
  1107. return getStaticValueR(node, initialScope)
  1108. } catch (_error) {
  1109. return null
  1110. }
  1111. }
  1112. /** @typedef {import("eslint").Scope.Scope} Scope */
  1113. /** @typedef {import("estree").Node} Node */
  1114. /** @typedef {import("estree").RegExpLiteral} RegExpLiteral */
  1115. /** @typedef {import("estree").BigIntLiteral} BigIntLiteral */
  1116. /** @typedef {import("estree").SimpleLiteral} SimpleLiteral */
  1117. /**
  1118. * Get the value of a given node if it's a literal or a template literal.
  1119. * @param {Node} node The node to get.
  1120. * @param {Scope|null} [initialScope] The scope to start finding variable. Optional. If the node is an Identifier node and this scope was given, this checks the variable of the identifier, and returns the value of it if the variable is a constant.
  1121. * @returns {string|null} The value of the node, or `null`.
  1122. */
  1123. function getStringIfConstant(node, initialScope = null) {
  1124. // Handle the literals that the platform doesn't support natively.
  1125. if (node && node.type === "Literal" && node.value === null) {
  1126. const literal =
  1127. /** @type {Partial<SimpleLiteral> & Partial<RegExpLiteral> & Partial<BigIntLiteral>} */ (
  1128. node
  1129. );
  1130. if (literal.regex) {
  1131. return `/${literal.regex.pattern}/${literal.regex.flags}`
  1132. }
  1133. if (literal.bigint) {
  1134. return literal.bigint
  1135. }
  1136. }
  1137. const evaluated = getStaticValue(node, initialScope);
  1138. if (evaluated) {
  1139. // `String(Symbol.prototype)` throws error
  1140. try {
  1141. return String(evaluated.value)
  1142. } catch {
  1143. // No op
  1144. }
  1145. }
  1146. return null
  1147. }
  1148. /** @typedef {import("eslint").Scope.Scope} Scope */
  1149. /** @typedef {import("estree").MemberExpression} MemberExpression */
  1150. /** @typedef {import("estree").MethodDefinition} MethodDefinition */
  1151. /** @typedef {import("estree").Property} Property */
  1152. /** @typedef {import("estree").PropertyDefinition} PropertyDefinition */
  1153. /** @typedef {import("estree").Identifier} Identifier */
  1154. /**
  1155. * Get the property name from a MemberExpression node or a Property node.
  1156. * @param {MemberExpression | MethodDefinition | Property | PropertyDefinition} node The node to get.
  1157. * @param {Scope} [initialScope] The scope to start finding variable. Optional. If the node is a computed property node and this scope was given, this checks the computed property name by the `getStringIfConstant` function with the scope, and returns the value of it.
  1158. * @returns {string|null|undefined} The property name of the node.
  1159. */
  1160. function getPropertyName(node, initialScope) {
  1161. switch (node.type) {
  1162. case "MemberExpression":
  1163. if (node.computed) {
  1164. return getStringIfConstant(node.property, initialScope)
  1165. }
  1166. if (node.property.type === "PrivateIdentifier") {
  1167. return null
  1168. }
  1169. return /** @type {Partial<Identifier>} */ (node.property).name
  1170. case "Property":
  1171. case "MethodDefinition":
  1172. case "PropertyDefinition":
  1173. if (node.computed) {
  1174. return getStringIfConstant(node.key, initialScope)
  1175. }
  1176. if (node.key.type === "Literal") {
  1177. return String(node.key.value)
  1178. }
  1179. if (node.key.type === "PrivateIdentifier") {
  1180. return null
  1181. }
  1182. return /** @type {Partial<Identifier>} */ (node.key).name
  1183. }
  1184. return null
  1185. }
  1186. /** @typedef {import("eslint").Rule.Node} RuleNode */
  1187. /** @typedef {import("eslint").SourceCode} SourceCode */
  1188. /** @typedef {import("estree").Function} FunctionNode */
  1189. /** @typedef {import("estree").FunctionDeclaration} FunctionDeclaration */
  1190. /** @typedef {import("estree").FunctionExpression} FunctionExpression */
  1191. /** @typedef {import("estree").Identifier} Identifier */
  1192. /**
  1193. * Get the name and kind of the given function node.
  1194. * @param {FunctionNode} node - The function node to get.
  1195. * @param {SourceCode} [sourceCode] The source code object to get the code of computed property keys.
  1196. * @returns {string} The name and kind of the function node.
  1197. */
  1198. // eslint-disable-next-line complexity
  1199. function getFunctionNameWithKind(node, sourceCode) {
  1200. const parent = /** @type {RuleNode} */ (node).parent;
  1201. const tokens = [];
  1202. const isObjectMethod = parent.type === "Property" && parent.value === node;
  1203. const isClassMethod =
  1204. parent.type === "MethodDefinition" && parent.value === node;
  1205. const isClassFieldMethod =
  1206. parent.type === "PropertyDefinition" && parent.value === node;
  1207. // Modifiers.
  1208. if (isClassMethod || isClassFieldMethod) {
  1209. if (parent.static) {
  1210. tokens.push("static");
  1211. }
  1212. if (parent.key.type === "PrivateIdentifier") {
  1213. tokens.push("private");
  1214. }
  1215. }
  1216. if (node.async) {
  1217. tokens.push("async");
  1218. }
  1219. if (node.generator) {
  1220. tokens.push("generator");
  1221. }
  1222. // Kinds.
  1223. if (isObjectMethod || isClassMethod) {
  1224. if (parent.kind === "constructor") {
  1225. return "constructor"
  1226. }
  1227. if (parent.kind === "get") {
  1228. tokens.push("getter");
  1229. } else if (parent.kind === "set") {
  1230. tokens.push("setter");
  1231. } else {
  1232. tokens.push("method");
  1233. }
  1234. } else if (isClassFieldMethod) {
  1235. tokens.push("method");
  1236. } else {
  1237. if (node.type === "ArrowFunctionExpression") {
  1238. tokens.push("arrow");
  1239. }
  1240. tokens.push("function");
  1241. }
  1242. // Names.
  1243. if (isObjectMethod || isClassMethod || isClassFieldMethod) {
  1244. if (parent.key.type === "PrivateIdentifier") {
  1245. tokens.push(`#${parent.key.name}`);
  1246. } else {
  1247. const name = getPropertyName(parent);
  1248. if (name) {
  1249. tokens.push(`'${name}'`);
  1250. } else if (sourceCode) {
  1251. const keyText = sourceCode.getText(parent.key);
  1252. if (!keyText.includes("\n")) {
  1253. tokens.push(`[${keyText}]`);
  1254. }
  1255. }
  1256. }
  1257. } else if (hasId(node)) {
  1258. tokens.push(`'${node.id.name}'`);
  1259. } else if (
  1260. parent.type === "VariableDeclarator" &&
  1261. parent.id &&
  1262. parent.id.type === "Identifier"
  1263. ) {
  1264. tokens.push(`'${parent.id.name}'`);
  1265. } else if (
  1266. (parent.type === "AssignmentExpression" ||
  1267. parent.type === "AssignmentPattern") &&
  1268. parent.left &&
  1269. parent.left.type === "Identifier"
  1270. ) {
  1271. tokens.push(`'${parent.left.name}'`);
  1272. } else if (
  1273. parent.type === "ExportDefaultDeclaration" &&
  1274. parent.declaration === node
  1275. ) {
  1276. tokens.push("'default'");
  1277. }
  1278. return tokens.join(" ")
  1279. }
  1280. /**
  1281. * @param {FunctionNode} node
  1282. * @returns {node is FunctionDeclaration | FunctionExpression & { id: Identifier }}
  1283. */
  1284. function hasId(node) {
  1285. return Boolean(
  1286. /** @type {Partial<FunctionDeclaration | FunctionExpression>} */ (node)
  1287. .id,
  1288. )
  1289. }
  1290. /** @typedef {import("estree").Node} Node */
  1291. /** @typedef {import("eslint").SourceCode} SourceCode */
  1292. /** @typedef {import("./types.mjs").HasSideEffectOptions} HasSideEffectOptions */
  1293. /** @typedef {import("estree").BinaryExpression} BinaryExpression */
  1294. /** @typedef {import("estree").MemberExpression} MemberExpression */
  1295. /** @typedef {import("estree").MethodDefinition} MethodDefinition */
  1296. /** @typedef {import("estree").Property} Property */
  1297. /** @typedef {import("estree").PropertyDefinition} PropertyDefinition */
  1298. /** @typedef {import("estree").UnaryExpression} UnaryExpression */
  1299. const typeConversionBinaryOps = Object.freeze(
  1300. new Set([
  1301. "==",
  1302. "!=",
  1303. "<",
  1304. "<=",
  1305. ">",
  1306. ">=",
  1307. "<<",
  1308. ">>",
  1309. ">>>",
  1310. "+",
  1311. "-",
  1312. "*",
  1313. "/",
  1314. "%",
  1315. "|",
  1316. "^",
  1317. "&",
  1318. "in",
  1319. ]),
  1320. );
  1321. const typeConversionUnaryOps = Object.freeze(new Set(["-", "+", "!", "~"]));
  1322. /**
  1323. * Check whether the given value is an ASTNode or not.
  1324. * @param {any} x The value to check.
  1325. * @returns {x is Node} `true` if the value is an ASTNode.
  1326. */
  1327. function isNode(x) {
  1328. return x !== null && typeof x === "object" && typeof x.type === "string"
  1329. }
  1330. const visitor = Object.freeze(
  1331. Object.assign(Object.create(null), {
  1332. /**
  1333. * @param {Node} node
  1334. * @param {HasSideEffectOptions} options
  1335. * @param {Record<string, string[]>} visitorKeys
  1336. */
  1337. $visit(node, options, visitorKeys) {
  1338. const { type } = node;
  1339. if (typeof (/** @type {any} */ (this)[type]) === "function") {
  1340. return /** @type {any} */ (this)[type](
  1341. node,
  1342. options,
  1343. visitorKeys,
  1344. )
  1345. }
  1346. return this.$visitChildren(node, options, visitorKeys)
  1347. },
  1348. /**
  1349. * @param {Node} node
  1350. * @param {HasSideEffectOptions} options
  1351. * @param {Record<string, string[]>} visitorKeys
  1352. */
  1353. $visitChildren(node, options, visitorKeys) {
  1354. const { type } = node;
  1355. for (const key of /** @type {(keyof Node)[]} */ (
  1356. visitorKeys[type] || eslintVisitorKeys.getKeys(node)
  1357. )) {
  1358. const value = node[key];
  1359. if (Array.isArray(value)) {
  1360. for (const element of value) {
  1361. if (
  1362. isNode(element) &&
  1363. this.$visit(element, options, visitorKeys)
  1364. ) {
  1365. return true
  1366. }
  1367. }
  1368. } else if (
  1369. isNode(value) &&
  1370. this.$visit(value, options, visitorKeys)
  1371. ) {
  1372. return true
  1373. }
  1374. }
  1375. return false
  1376. },
  1377. ArrowFunctionExpression() {
  1378. return false
  1379. },
  1380. AssignmentExpression() {
  1381. return true
  1382. },
  1383. AwaitExpression() {
  1384. return true
  1385. },
  1386. /**
  1387. * @param {BinaryExpression} node
  1388. * @param {HasSideEffectOptions} options
  1389. * @param {Record<string, string[]>} visitorKeys
  1390. */
  1391. BinaryExpression(node, options, visitorKeys) {
  1392. if (
  1393. options.considerImplicitTypeConversion &&
  1394. typeConversionBinaryOps.has(node.operator) &&
  1395. (node.left.type !== "Literal" || node.right.type !== "Literal")
  1396. ) {
  1397. return true
  1398. }
  1399. return this.$visitChildren(node, options, visitorKeys)
  1400. },
  1401. CallExpression() {
  1402. return true
  1403. },
  1404. FunctionExpression() {
  1405. return false
  1406. },
  1407. ImportExpression() {
  1408. return true
  1409. },
  1410. /**
  1411. * @param {MemberExpression} node
  1412. * @param {HasSideEffectOptions} options
  1413. * @param {Record<string, string[]>} visitorKeys
  1414. */
  1415. MemberExpression(node, options, visitorKeys) {
  1416. if (options.considerGetters) {
  1417. return true
  1418. }
  1419. if (
  1420. options.considerImplicitTypeConversion &&
  1421. node.computed &&
  1422. node.property.type !== "Literal"
  1423. ) {
  1424. return true
  1425. }
  1426. return this.$visitChildren(node, options, visitorKeys)
  1427. },
  1428. /**
  1429. * @param {MethodDefinition} node
  1430. * @param {HasSideEffectOptions} options
  1431. * @param {Record<string, string[]>} visitorKeys
  1432. */
  1433. MethodDefinition(node, options, visitorKeys) {
  1434. if (
  1435. options.considerImplicitTypeConversion &&
  1436. node.computed &&
  1437. node.key.type !== "Literal"
  1438. ) {
  1439. return true
  1440. }
  1441. return this.$visitChildren(node, options, visitorKeys)
  1442. },
  1443. NewExpression() {
  1444. return true
  1445. },
  1446. /**
  1447. * @param {Property} node
  1448. * @param {HasSideEffectOptions} options
  1449. * @param {Record<string, string[]>} visitorKeys
  1450. */
  1451. Property(node, options, visitorKeys) {
  1452. if (
  1453. options.considerImplicitTypeConversion &&
  1454. node.computed &&
  1455. node.key.type !== "Literal"
  1456. ) {
  1457. return true
  1458. }
  1459. return this.$visitChildren(node, options, visitorKeys)
  1460. },
  1461. /**
  1462. * @param {PropertyDefinition} node
  1463. * @param {HasSideEffectOptions} options
  1464. * @param {Record<string, string[]>} visitorKeys
  1465. */
  1466. PropertyDefinition(node, options, visitorKeys) {
  1467. if (
  1468. options.considerImplicitTypeConversion &&
  1469. node.computed &&
  1470. node.key.type !== "Literal"
  1471. ) {
  1472. return true
  1473. }
  1474. return this.$visitChildren(node, options, visitorKeys)
  1475. },
  1476. /**
  1477. * @param {UnaryExpression} node
  1478. * @param {HasSideEffectOptions} options
  1479. * @param {Record<string, string[]>} visitorKeys
  1480. */
  1481. UnaryExpression(node, options, visitorKeys) {
  1482. if (node.operator === "delete") {
  1483. return true
  1484. }
  1485. if (
  1486. options.considerImplicitTypeConversion &&
  1487. typeConversionUnaryOps.has(node.operator) &&
  1488. node.argument.type !== "Literal"
  1489. ) {
  1490. return true
  1491. }
  1492. return this.$visitChildren(node, options, visitorKeys)
  1493. },
  1494. UpdateExpression() {
  1495. return true
  1496. },
  1497. YieldExpression() {
  1498. return true
  1499. },
  1500. }),
  1501. );
  1502. /**
  1503. * Check whether a given node has any side effect or not.
  1504. * @param {Node} node The node to get.
  1505. * @param {SourceCode} sourceCode The source code object.
  1506. * @param {HasSideEffectOptions} [options] The option object.
  1507. * @returns {boolean} `true` if the node has a certain side effect.
  1508. */
  1509. function hasSideEffect(node, sourceCode, options = {}) {
  1510. const { considerGetters = false, considerImplicitTypeConversion = false } =
  1511. options;
  1512. return visitor.$visit(
  1513. node,
  1514. { considerGetters, considerImplicitTypeConversion },
  1515. sourceCode.visitorKeys || eslintVisitorKeys.KEYS,
  1516. )
  1517. }
  1518. /** @typedef {import("estree").Node} Node */
  1519. /** @typedef {import("@typescript-eslint/types").TSESTree.NewExpression} TSNewExpression */
  1520. /** @typedef {import("@typescript-eslint/types").TSESTree.CallExpression} TSCallExpression */
  1521. /** @typedef {import("eslint").SourceCode} SourceCode */
  1522. /** @typedef {import("eslint").AST.Token} Token */
  1523. /** @typedef {import("eslint").Rule.Node} RuleNode */
  1524. /**
  1525. * Get the left parenthesis of the parent node syntax if it exists.
  1526. * E.g., `if (a) {}` then the `(`.
  1527. * @param {Node} node The AST node to check.
  1528. * @param {SourceCode} sourceCode The source code object to get tokens.
  1529. * @returns {Token|null} The left parenthesis of the parent node syntax
  1530. */
  1531. // eslint-disable-next-line complexity
  1532. function getParentSyntaxParen(node, sourceCode) {
  1533. const parent = /** @type {RuleNode} */ (node).parent;
  1534. switch (parent.type) {
  1535. case "CallExpression":
  1536. case "NewExpression":
  1537. if (parent.arguments.length === 1 && parent.arguments[0] === node) {
  1538. return sourceCode.getTokenAfter(
  1539. // @ts-expect-error https://github.com/typescript-eslint/typescript-eslint/pull/5384
  1540. parent.typeArguments ||
  1541. /** @type {RuleNode} */ (
  1542. /** @type {unknown} */ (
  1543. /** @type {TSNewExpression | TSCallExpression} */ (
  1544. parent
  1545. ).typeParameters
  1546. )
  1547. ) ||
  1548. parent.callee,
  1549. isOpeningParenToken,
  1550. )
  1551. }
  1552. return null
  1553. case "DoWhileStatement":
  1554. if (parent.test === node) {
  1555. return sourceCode.getTokenAfter(
  1556. parent.body,
  1557. isOpeningParenToken,
  1558. )
  1559. }
  1560. return null
  1561. case "IfStatement":
  1562. case "WhileStatement":
  1563. if (parent.test === node) {
  1564. return sourceCode.getFirstToken(parent, 1)
  1565. }
  1566. return null
  1567. case "ImportExpression":
  1568. if (parent.source === node) {
  1569. return sourceCode.getFirstToken(parent, 1)
  1570. }
  1571. return null
  1572. case "SwitchStatement":
  1573. if (parent.discriminant === node) {
  1574. return sourceCode.getFirstToken(parent, 1)
  1575. }
  1576. return null
  1577. case "WithStatement":
  1578. if (parent.object === node) {
  1579. return sourceCode.getFirstToken(parent, 1)
  1580. }
  1581. return null
  1582. default:
  1583. return null
  1584. }
  1585. }
  1586. /**
  1587. * Check whether a given node is parenthesized or not.
  1588. * @param {number} times The number of parantheses.
  1589. * @param {Node} node The AST node to check.
  1590. * @param {SourceCode} sourceCode The source code object to get tokens.
  1591. * @returns {boolean} `true` if the node is parenthesized the given times.
  1592. */
  1593. /**
  1594. * Check whether a given node is parenthesized or not.
  1595. * @param {Node} node The AST node to check.
  1596. * @param {SourceCode} sourceCode The source code object to get tokens.
  1597. * @returns {boolean} `true` if the node is parenthesized.
  1598. */
  1599. /**
  1600. * Check whether a given node is parenthesized or not.
  1601. * @param {Node|number} timesOrNode The first parameter.
  1602. * @param {Node|SourceCode} nodeOrSourceCode The second parameter.
  1603. * @param {SourceCode} [optionalSourceCode] The third parameter.
  1604. * @returns {boolean} `true` if the node is parenthesized.
  1605. */
  1606. function isParenthesized(
  1607. timesOrNode,
  1608. nodeOrSourceCode,
  1609. optionalSourceCode,
  1610. ) {
  1611. /** @type {number} */
  1612. let times,
  1613. /** @type {RuleNode} */
  1614. node,
  1615. /** @type {SourceCode} */
  1616. sourceCode,
  1617. maybeLeftParen,
  1618. maybeRightParen;
  1619. if (typeof timesOrNode === "number") {
  1620. times = timesOrNode | 0;
  1621. node = /** @type {RuleNode} */ (nodeOrSourceCode);
  1622. sourceCode = /** @type {SourceCode} */ (optionalSourceCode);
  1623. if (!(times >= 1)) {
  1624. throw new TypeError("'times' should be a positive integer.")
  1625. }
  1626. } else {
  1627. times = 1;
  1628. node = /** @type {RuleNode} */ (timesOrNode);
  1629. sourceCode = /** @type {SourceCode} */ (nodeOrSourceCode);
  1630. }
  1631. if (
  1632. node == null ||
  1633. // `Program` can't be parenthesized
  1634. node.parent == null ||
  1635. // `CatchClause.param` can't be parenthesized, example `try {} catch (error) {}`
  1636. (node.parent.type === "CatchClause" && node.parent.param === node)
  1637. ) {
  1638. return false
  1639. }
  1640. maybeLeftParen = maybeRightParen = node;
  1641. do {
  1642. maybeLeftParen = sourceCode.getTokenBefore(maybeLeftParen);
  1643. maybeRightParen = sourceCode.getTokenAfter(maybeRightParen);
  1644. } while (
  1645. maybeLeftParen != null &&
  1646. maybeRightParen != null &&
  1647. isOpeningParenToken(maybeLeftParen) &&
  1648. isClosingParenToken(maybeRightParen) &&
  1649. // Avoid false positive such as `if (a) {}`
  1650. maybeLeftParen !== getParentSyntaxParen(node, sourceCode) &&
  1651. --times > 0
  1652. )
  1653. return times === 0
  1654. }
  1655. /**
  1656. * @author Toru Nagashima <https://github.com/mysticatea>
  1657. * See LICENSE file in root directory for full license.
  1658. */
  1659. const placeholder = /\$(?:[$&`']|[1-9][0-9]?)/gu;
  1660. /** @type {WeakMap<PatternMatcher, {pattern:RegExp,escaped:boolean}>} */
  1661. const internal = new WeakMap();
  1662. /**
  1663. * Check whether a given character is escaped or not.
  1664. * @param {string} str The string to check.
  1665. * @param {number} index The location of the character to check.
  1666. * @returns {boolean} `true` if the character is escaped.
  1667. */
  1668. function isEscaped(str, index) {
  1669. let escaped = false;
  1670. for (let i = index - 1; i >= 0 && str.charCodeAt(i) === 0x5c; --i) {
  1671. escaped = !escaped;
  1672. }
  1673. return escaped
  1674. }
  1675. /**
  1676. * Replace a given string by a given matcher.
  1677. * @param {PatternMatcher} matcher The pattern matcher.
  1678. * @param {string} str The string to be replaced.
  1679. * @param {string} replacement The new substring to replace each matched part.
  1680. * @returns {string} The replaced string.
  1681. */
  1682. function replaceS(matcher, str, replacement) {
  1683. const chunks = [];
  1684. let index = 0;
  1685. /**
  1686. * @param {string} key The placeholder.
  1687. * @param {RegExpExecArray} match The matched information.
  1688. * @returns {string} The replaced string.
  1689. */
  1690. function replacer(key, match) {
  1691. switch (key) {
  1692. case "$$":
  1693. return "$"
  1694. case "$&":
  1695. return match[0]
  1696. case "$`":
  1697. return str.slice(0, match.index)
  1698. case "$'":
  1699. return str.slice(match.index + match[0].length)
  1700. default: {
  1701. const i = key.slice(1);
  1702. if (i in match) {
  1703. return match[/** @type {any} */ (i)]
  1704. }
  1705. return key
  1706. }
  1707. }
  1708. }
  1709. for (const match of matcher.execAll(str)) {
  1710. chunks.push(str.slice(index, match.index));
  1711. chunks.push(
  1712. replacement.replace(placeholder, (key) => replacer(key, match)),
  1713. );
  1714. index = match.index + match[0].length;
  1715. }
  1716. chunks.push(str.slice(index));
  1717. return chunks.join("")
  1718. }
  1719. /**
  1720. * Replace a given string by a given matcher.
  1721. * @param {PatternMatcher} matcher The pattern matcher.
  1722. * @param {string} str The string to be replaced.
  1723. * @param {(substring: string, ...args: any[]) => string} replace The function to replace each matched part.
  1724. * @returns {string} The replaced string.
  1725. */
  1726. function replaceF(matcher, str, replace) {
  1727. const chunks = [];
  1728. let index = 0;
  1729. for (const match of matcher.execAll(str)) {
  1730. chunks.push(str.slice(index, match.index));
  1731. chunks.push(
  1732. String(
  1733. replace(
  1734. .../** @type {[string, ...string[]]} */ (
  1735. /** @type {string[]} */ (match)
  1736. ),
  1737. match.index,
  1738. match.input,
  1739. ),
  1740. ),
  1741. );
  1742. index = match.index + match[0].length;
  1743. }
  1744. chunks.push(str.slice(index));
  1745. return chunks.join("")
  1746. }
  1747. /**
  1748. * The class to find patterns as considering escape sequences.
  1749. */
  1750. class PatternMatcher {
  1751. /**
  1752. * Initialize this matcher.
  1753. * @param {RegExp} pattern The pattern to match.
  1754. * @param {{escaped?:boolean}} [options] The options.
  1755. */
  1756. constructor(pattern, options = {}) {
  1757. const { escaped = false } = options;
  1758. if (!(pattern instanceof RegExp)) {
  1759. throw new TypeError("'pattern' should be a RegExp instance.")
  1760. }
  1761. if (!pattern.flags.includes("g")) {
  1762. throw new Error("'pattern' should contains 'g' flag.")
  1763. }
  1764. internal.set(this, {
  1765. pattern: new RegExp(pattern.source, pattern.flags),
  1766. escaped: Boolean(escaped),
  1767. });
  1768. }
  1769. /**
  1770. * Find the pattern in a given string.
  1771. * @param {string} str The string to find.
  1772. * @returns {IterableIterator<RegExpExecArray>} The iterator which iterate the matched information.
  1773. */
  1774. *execAll(str) {
  1775. const { pattern, escaped } =
  1776. /** @type {{pattern:RegExp,escaped:boolean}} */ (internal.get(this));
  1777. let match = null;
  1778. let lastIndex = 0;
  1779. pattern.lastIndex = 0;
  1780. while ((match = pattern.exec(str)) != null) {
  1781. if (escaped || !isEscaped(str, match.index)) {
  1782. lastIndex = pattern.lastIndex;
  1783. yield match;
  1784. pattern.lastIndex = lastIndex;
  1785. }
  1786. }
  1787. }
  1788. /**
  1789. * Check whether the pattern is found in a given string.
  1790. * @param {string} str The string to check.
  1791. * @returns {boolean} `true` if the pattern was found in the string.
  1792. */
  1793. test(str) {
  1794. const it = this.execAll(str);
  1795. const ret = it.next();
  1796. return !ret.done
  1797. }
  1798. /**
  1799. * Replace a given string.
  1800. * @param {string} str The string to be replaced.
  1801. * @param {(string|((...strs:string[])=>string))} replacer The string or function to replace. This is the same as the 2nd argument of `String.prototype.replace`.
  1802. * @returns {string} The replaced string.
  1803. */
  1804. [Symbol.replace](str, replacer) {
  1805. return typeof replacer === "function"
  1806. ? replaceF(this, String(str), replacer)
  1807. : replaceS(this, String(str), String(replacer))
  1808. }
  1809. }
  1810. /** @typedef {import("eslint").Scope.Scope} Scope */
  1811. /** @typedef {import("eslint").Scope.Variable} Variable */
  1812. /** @typedef {import("eslint").Rule.Node} RuleNode */
  1813. /** @typedef {import("estree").Node} Node */
  1814. /** @typedef {import("estree").Expression} Expression */
  1815. /** @typedef {import("estree").Pattern} Pattern */
  1816. /** @typedef {import("estree").Identifier} Identifier */
  1817. /** @typedef {import("estree").SimpleCallExpression} CallExpression */
  1818. /** @typedef {import("estree").Program} Program */
  1819. /** @typedef {import("estree").ImportDeclaration} ImportDeclaration */
  1820. /** @typedef {import("estree").ExportAllDeclaration} ExportAllDeclaration */
  1821. /** @typedef {import("estree").ExportDefaultDeclaration} ExportDefaultDeclaration */
  1822. /** @typedef {import("estree").ExportNamedDeclaration} ExportNamedDeclaration */
  1823. /** @typedef {import("estree").ImportSpecifier} ImportSpecifier */
  1824. /** @typedef {import("estree").ImportDefaultSpecifier} ImportDefaultSpecifier */
  1825. /** @typedef {import("estree").ImportNamespaceSpecifier} ImportNamespaceSpecifier */
  1826. /** @typedef {import("estree").ExportSpecifier} ExportSpecifier */
  1827. /** @typedef {import("estree").Property} Property */
  1828. /** @typedef {import("estree").AssignmentProperty} AssignmentProperty */
  1829. /** @typedef {import("estree").Literal} Literal */
  1830. /** @typedef {import("@typescript-eslint/types").TSESTree.Node} TSESTreeNode */
  1831. /** @typedef {import("./types.mjs").ReferenceTrackerOptions} ReferenceTrackerOptions */
  1832. /**
  1833. * @template T
  1834. * @typedef {import("./types.mjs").TraceMap<T>} TraceMap
  1835. */
  1836. /**
  1837. * @template T
  1838. * @typedef {import("./types.mjs").TraceMapObject<T>} TraceMapObject
  1839. */
  1840. /**
  1841. * @template T
  1842. * @typedef {import("./types.mjs").TrackedReferences<T>} TrackedReferences
  1843. */
  1844. const IMPORT_TYPE = /^(?:Import|Export(?:All|Default|Named))Declaration$/u;
  1845. /**
  1846. * Check whether a given node is an import node or not.
  1847. * @param {Node} node
  1848. * @returns {node is ImportDeclaration|ExportAllDeclaration|ExportNamedDeclaration&{source: Literal}} `true` if the node is an import node.
  1849. */
  1850. function isHasSource(node) {
  1851. return (
  1852. IMPORT_TYPE.test(node.type) &&
  1853. /** @type {ImportDeclaration|ExportAllDeclaration|ExportNamedDeclaration} */ (
  1854. node
  1855. ).source != null
  1856. )
  1857. }
  1858. const has =
  1859. /** @type {<T>(traceMap: TraceMap<unknown>, v: T) => v is (string extends T ? string : T)} */ (
  1860. Function.call.bind(Object.hasOwnProperty)
  1861. );
  1862. const READ = Symbol("read");
  1863. const CALL = Symbol("call");
  1864. const CONSTRUCT = Symbol("construct");
  1865. const ESM = Symbol("esm");
  1866. const requireCall = { require: { [CALL]: true } };
  1867. /**
  1868. * Check whether a given variable is modified or not.
  1869. * @param {Variable|undefined} variable The variable to check.
  1870. * @returns {boolean} `true` if the variable is modified.
  1871. */
  1872. function isModifiedGlobal(variable) {
  1873. return (
  1874. variable == null ||
  1875. variable.defs.length !== 0 ||
  1876. variable.references.some((r) => r.isWrite())
  1877. )
  1878. }
  1879. /**
  1880. * Check if the value of a given node is passed through to the parent syntax as-is.
  1881. * For example, `a` and `b` in (`a || b` and `c ? a : b`) are passed through.
  1882. * @param {Node} node A node to check.
  1883. * @returns {node is RuleNode & {parent: Expression}} `true` if the node is passed through.
  1884. */
  1885. function isPassThrough(node) {
  1886. const parent = /** @type {TSESTreeNode} */ (node).parent;
  1887. if (parent) {
  1888. switch (parent.type) {
  1889. case "ConditionalExpression":
  1890. return parent.consequent === node || parent.alternate === node
  1891. case "LogicalExpression":
  1892. return true
  1893. case "SequenceExpression":
  1894. return (
  1895. parent.expressions[parent.expressions.length - 1] === node
  1896. )
  1897. case "ChainExpression":
  1898. return true
  1899. case "TSAsExpression":
  1900. case "TSSatisfiesExpression":
  1901. case "TSTypeAssertion":
  1902. case "TSNonNullExpression":
  1903. case "TSInstantiationExpression":
  1904. return true
  1905. default:
  1906. return false
  1907. }
  1908. }
  1909. return false
  1910. }
  1911. /**
  1912. * The reference tracker.
  1913. */
  1914. class ReferenceTracker {
  1915. /**
  1916. * Initialize this tracker.
  1917. * @param {Scope} globalScope The global scope.
  1918. * @param {object} [options] The options.
  1919. * @param {"legacy"|"strict"} [options.mode="strict"] The mode to determine the ImportDeclaration's behavior for CJS modules.
  1920. * @param {string[]} [options.globalObjectNames=["global","globalThis","self","window"]] The variable names for Global Object.
  1921. */
  1922. constructor(globalScope, options = {}) {
  1923. const {
  1924. mode = "strict",
  1925. globalObjectNames = ["global", "globalThis", "self", "window"],
  1926. } = options;
  1927. /** @private @type {Variable[]} */
  1928. this.variableStack = [];
  1929. /** @private */
  1930. this.globalScope = globalScope;
  1931. /** @private */
  1932. this.mode = mode;
  1933. /** @private */
  1934. this.globalObjectNames = globalObjectNames.slice(0);
  1935. }
  1936. /**
  1937. * Iterate the references of global variables.
  1938. * @template T
  1939. * @param {TraceMap<T>} traceMap The trace map.
  1940. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate references.
  1941. */
  1942. *iterateGlobalReferences(traceMap) {
  1943. for (const key of Object.keys(traceMap)) {
  1944. const nextTraceMap = traceMap[key];
  1945. const path = [key];
  1946. const variable = this.globalScope.set.get(key);
  1947. if (isModifiedGlobal(variable)) {
  1948. continue
  1949. }
  1950. yield* this._iterateVariableReferences(
  1951. /** @type {Variable} */ (variable),
  1952. path,
  1953. nextTraceMap,
  1954. true,
  1955. );
  1956. }
  1957. for (const key of this.globalObjectNames) {
  1958. /** @type {string[]} */
  1959. const path = [];
  1960. const variable = this.globalScope.set.get(key);
  1961. if (isModifiedGlobal(variable)) {
  1962. continue
  1963. }
  1964. yield* this._iterateVariableReferences(
  1965. /** @type {Variable} */ (variable),
  1966. path,
  1967. traceMap,
  1968. false,
  1969. );
  1970. }
  1971. }
  1972. /**
  1973. * Iterate the references of CommonJS modules.
  1974. * @template T
  1975. * @param {TraceMap<T>} traceMap The trace map.
  1976. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate references.
  1977. */
  1978. *iterateCjsReferences(traceMap) {
  1979. for (const { node } of this.iterateGlobalReferences(requireCall)) {
  1980. const key = getStringIfConstant(
  1981. /** @type {CallExpression} */ (node).arguments[0],
  1982. );
  1983. if (key == null || !has(traceMap, key)) {
  1984. continue
  1985. }
  1986. const nextTraceMap = traceMap[key];
  1987. const path = [key];
  1988. if (nextTraceMap[READ]) {
  1989. yield {
  1990. node,
  1991. path,
  1992. type: READ,
  1993. info: nextTraceMap[READ],
  1994. };
  1995. }
  1996. yield* this._iteratePropertyReferences(
  1997. /** @type {CallExpression} */ (node),
  1998. path,
  1999. nextTraceMap,
  2000. );
  2001. }
  2002. }
  2003. /**
  2004. * Iterate the references of ES modules.
  2005. * @template T
  2006. * @param {TraceMap<T>} traceMap The trace map.
  2007. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate references.
  2008. */
  2009. *iterateEsmReferences(traceMap) {
  2010. const programNode = /** @type {Program} */ (this.globalScope.block);
  2011. for (const node of programNode.body) {
  2012. if (!isHasSource(node)) {
  2013. continue
  2014. }
  2015. const moduleId = /** @type {string} */ (node.source.value);
  2016. if (!has(traceMap, moduleId)) {
  2017. continue
  2018. }
  2019. const nextTraceMap = traceMap[moduleId];
  2020. const path = [moduleId];
  2021. if (nextTraceMap[READ]) {
  2022. yield {
  2023. // eslint-disable-next-line object-shorthand -- apply type
  2024. node: /** @type {RuleNode} */ (node),
  2025. path,
  2026. type: READ,
  2027. info: nextTraceMap[READ],
  2028. };
  2029. }
  2030. if (node.type === "ExportAllDeclaration") {
  2031. for (const key of Object.keys(nextTraceMap)) {
  2032. const exportTraceMap = nextTraceMap[key];
  2033. if (exportTraceMap[READ]) {
  2034. yield {
  2035. // eslint-disable-next-line object-shorthand -- apply type
  2036. node: /** @type {RuleNode} */ (node),
  2037. path: path.concat(key),
  2038. type: READ,
  2039. info: exportTraceMap[READ],
  2040. };
  2041. }
  2042. }
  2043. } else {
  2044. for (const specifier of node.specifiers) {
  2045. const esm = has(nextTraceMap, ESM);
  2046. const it = this._iterateImportReferences(
  2047. specifier,
  2048. path,
  2049. esm
  2050. ? nextTraceMap
  2051. : this.mode === "legacy"
  2052. ? { default: nextTraceMap, ...nextTraceMap }
  2053. : { default: nextTraceMap },
  2054. );
  2055. if (esm) {
  2056. yield* it;
  2057. } else {
  2058. for (const report of it) {
  2059. report.path = report.path.filter(exceptDefault);
  2060. if (
  2061. report.path.length >= 2 ||
  2062. report.type !== READ
  2063. ) {
  2064. yield report;
  2065. }
  2066. }
  2067. }
  2068. }
  2069. }
  2070. }
  2071. }
  2072. /**
  2073. * Iterate the property references for a given expression AST node.
  2074. * @template T
  2075. * @param {Expression} node The expression AST node to iterate property references.
  2076. * @param {TraceMap<T>} traceMap The trace map.
  2077. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate property references.
  2078. */
  2079. *iteratePropertyReferences(node, traceMap) {
  2080. yield* this._iteratePropertyReferences(node, [], traceMap);
  2081. }
  2082. /**
  2083. * Iterate the references for a given variable.
  2084. * @private
  2085. * @template T
  2086. * @param {Variable} variable The variable to iterate that references.
  2087. * @param {string[]} path The current path.
  2088. * @param {TraceMapObject<T>} traceMap The trace map.
  2089. * @param {boolean} shouldReport = The flag to report those references.
  2090. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate references.
  2091. */
  2092. *_iterateVariableReferences(variable, path, traceMap, shouldReport) {
  2093. if (this.variableStack.includes(variable)) {
  2094. return
  2095. }
  2096. this.variableStack.push(variable);
  2097. try {
  2098. for (const reference of variable.references) {
  2099. if (!reference.isRead()) {
  2100. continue
  2101. }
  2102. const node = /** @type {RuleNode & Identifier} */ (
  2103. reference.identifier
  2104. );
  2105. if (shouldReport && traceMap[READ]) {
  2106. yield { node, path, type: READ, info: traceMap[READ] };
  2107. }
  2108. yield* this._iteratePropertyReferences(node, path, traceMap);
  2109. }
  2110. } finally {
  2111. this.variableStack.pop();
  2112. }
  2113. }
  2114. /**
  2115. * Iterate the references for a given AST node.
  2116. * @private
  2117. * @template T
  2118. * @param {Expression} rootNode The AST node to iterate references.
  2119. * @param {string[]} path The current path.
  2120. * @param {TraceMapObject<T>} traceMap The trace map.
  2121. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate references.
  2122. */
  2123. //eslint-disable-next-line complexity
  2124. *_iteratePropertyReferences(rootNode, path, traceMap) {
  2125. let node = rootNode;
  2126. while (isPassThrough(node)) {
  2127. node = node.parent;
  2128. }
  2129. const parent = /** @type {RuleNode} */ (node).parent;
  2130. if (parent.type === "MemberExpression") {
  2131. if (parent.object === node) {
  2132. const key = getPropertyName(parent);
  2133. if (key == null || !has(traceMap, key)) {
  2134. return
  2135. }
  2136. path = path.concat(key); //eslint-disable-line no-param-reassign
  2137. const nextTraceMap = traceMap[key];
  2138. if (nextTraceMap[READ]) {
  2139. yield {
  2140. node: parent,
  2141. path,
  2142. type: READ,
  2143. info: nextTraceMap[READ],
  2144. };
  2145. }
  2146. yield* this._iteratePropertyReferences(
  2147. parent,
  2148. path,
  2149. nextTraceMap,
  2150. );
  2151. }
  2152. return
  2153. }
  2154. if (parent.type === "CallExpression") {
  2155. if (parent.callee === node && traceMap[CALL]) {
  2156. yield { node: parent, path, type: CALL, info: traceMap[CALL] };
  2157. }
  2158. return
  2159. }
  2160. if (parent.type === "NewExpression") {
  2161. if (parent.callee === node && traceMap[CONSTRUCT]) {
  2162. yield {
  2163. node: parent,
  2164. path,
  2165. type: CONSTRUCT,
  2166. info: traceMap[CONSTRUCT],
  2167. };
  2168. }
  2169. return
  2170. }
  2171. if (parent.type === "AssignmentExpression") {
  2172. if (parent.right === node) {
  2173. yield* this._iterateLhsReferences(parent.left, path, traceMap);
  2174. yield* this._iteratePropertyReferences(parent, path, traceMap);
  2175. }
  2176. return
  2177. }
  2178. if (parent.type === "AssignmentPattern") {
  2179. if (parent.right === node) {
  2180. yield* this._iterateLhsReferences(parent.left, path, traceMap);
  2181. }
  2182. return
  2183. }
  2184. if (parent.type === "VariableDeclarator") {
  2185. if (parent.init === node) {
  2186. yield* this._iterateLhsReferences(parent.id, path, traceMap);
  2187. }
  2188. }
  2189. }
  2190. /**
  2191. * Iterate the references for a given Pattern node.
  2192. * @private
  2193. * @template T
  2194. * @param {Pattern} patternNode The Pattern node to iterate references.
  2195. * @param {string[]} path The current path.
  2196. * @param {TraceMapObject<T>} traceMap The trace map.
  2197. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate references.
  2198. */
  2199. *_iterateLhsReferences(patternNode, path, traceMap) {
  2200. if (patternNode.type === "Identifier") {
  2201. const variable = findVariable(this.globalScope, patternNode);
  2202. if (variable != null) {
  2203. yield* this._iterateVariableReferences(
  2204. variable,
  2205. path,
  2206. traceMap,
  2207. false,
  2208. );
  2209. }
  2210. return
  2211. }
  2212. if (patternNode.type === "ObjectPattern") {
  2213. for (const property of patternNode.properties) {
  2214. const key = getPropertyName(
  2215. /** @type {AssignmentProperty} */ (property),
  2216. );
  2217. if (key == null || !has(traceMap, key)) {
  2218. continue
  2219. }
  2220. const nextPath = path.concat(key);
  2221. const nextTraceMap = traceMap[key];
  2222. if (nextTraceMap[READ]) {
  2223. yield {
  2224. node: /** @type {RuleNode} */ (property),
  2225. path: nextPath,
  2226. type: READ,
  2227. info: nextTraceMap[READ],
  2228. };
  2229. }
  2230. yield* this._iterateLhsReferences(
  2231. /** @type {AssignmentProperty} */ (property).value,
  2232. nextPath,
  2233. nextTraceMap,
  2234. );
  2235. }
  2236. return
  2237. }
  2238. if (patternNode.type === "AssignmentPattern") {
  2239. yield* this._iterateLhsReferences(patternNode.left, path, traceMap);
  2240. }
  2241. }
  2242. /**
  2243. * Iterate the references for a given ModuleSpecifier node.
  2244. * @private
  2245. * @template T
  2246. * @param {ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier | ExportSpecifier} specifierNode The ModuleSpecifier node to iterate references.
  2247. * @param {string[]} path The current path.
  2248. * @param {TraceMapObject<T>} traceMap The trace map.
  2249. * @returns {IterableIterator<TrackedReferences<T>>} The iterator to iterate references.
  2250. */
  2251. *_iterateImportReferences(specifierNode, path, traceMap) {
  2252. const type = specifierNode.type;
  2253. if (type === "ImportSpecifier" || type === "ImportDefaultSpecifier") {
  2254. const key =
  2255. type === "ImportDefaultSpecifier"
  2256. ? "default"
  2257. : specifierNode.imported.type === "Identifier"
  2258. ? specifierNode.imported.name
  2259. : specifierNode.imported.value;
  2260. if (!has(traceMap, key)) {
  2261. return
  2262. }
  2263. path = path.concat(key); //eslint-disable-line no-param-reassign
  2264. const nextTraceMap = traceMap[key];
  2265. if (nextTraceMap[READ]) {
  2266. yield {
  2267. node: /** @type {RuleNode} */ (specifierNode),
  2268. path,
  2269. type: READ,
  2270. info: nextTraceMap[READ],
  2271. };
  2272. }
  2273. yield* this._iterateVariableReferences(
  2274. /** @type {Variable} */ (
  2275. findVariable(this.globalScope, specifierNode.local)
  2276. ),
  2277. path,
  2278. nextTraceMap,
  2279. false,
  2280. );
  2281. return
  2282. }
  2283. if (type === "ImportNamespaceSpecifier") {
  2284. yield* this._iterateVariableReferences(
  2285. /** @type {Variable} */ (
  2286. findVariable(this.globalScope, specifierNode.local)
  2287. ),
  2288. path,
  2289. traceMap,
  2290. false,
  2291. );
  2292. return
  2293. }
  2294. if (type === "ExportSpecifier") {
  2295. const key =
  2296. specifierNode.local.type === "Identifier"
  2297. ? specifierNode.local.name
  2298. : specifierNode.local.value;
  2299. if (!has(traceMap, key)) {
  2300. return
  2301. }
  2302. path = path.concat(key); //eslint-disable-line no-param-reassign
  2303. const nextTraceMap = traceMap[key];
  2304. if (nextTraceMap[READ]) {
  2305. yield {
  2306. node: /** @type {RuleNode} */ (specifierNode),
  2307. path,
  2308. type: READ,
  2309. info: nextTraceMap[READ],
  2310. };
  2311. }
  2312. }
  2313. }
  2314. }
  2315. ReferenceTracker.READ = READ;
  2316. ReferenceTracker.CALL = CALL;
  2317. ReferenceTracker.CONSTRUCT = CONSTRUCT;
  2318. ReferenceTracker.ESM = ESM;
  2319. /**
  2320. * This is a predicate function for Array#filter.
  2321. * @param {string} name A name part.
  2322. * @param {number} index The index of the name.
  2323. * @returns {boolean} `false` if it's default.
  2324. */
  2325. function exceptDefault(name, index) {
  2326. return !(index === 1 && name === "default")
  2327. }
  2328. /** @typedef {import("./types.mjs").StaticValue} StaticValue */
  2329. var index = {
  2330. CALL,
  2331. CONSTRUCT,
  2332. ESM,
  2333. findVariable,
  2334. getFunctionHeadLocation,
  2335. getFunctionNameWithKind,
  2336. getInnermostScope,
  2337. getPropertyName,
  2338. getStaticValue,
  2339. getStringIfConstant,
  2340. hasSideEffect,
  2341. isArrowToken,
  2342. isClosingBraceToken,
  2343. isClosingBracketToken,
  2344. isClosingParenToken,
  2345. isColonToken,
  2346. isCommaToken,
  2347. isCommentToken,
  2348. isNotArrowToken,
  2349. isNotClosingBraceToken,
  2350. isNotClosingBracketToken,
  2351. isNotClosingParenToken,
  2352. isNotColonToken,
  2353. isNotCommaToken,
  2354. isNotCommentToken,
  2355. isNotOpeningBraceToken,
  2356. isNotOpeningBracketToken,
  2357. isNotOpeningParenToken,
  2358. isNotSemicolonToken,
  2359. isOpeningBraceToken,
  2360. isOpeningBracketToken,
  2361. isOpeningParenToken,
  2362. isParenthesized,
  2363. isSemicolonToken,
  2364. PatternMatcher,
  2365. READ,
  2366. ReferenceTracker,
  2367. };
  2368. exports.CALL = CALL;
  2369. exports.CONSTRUCT = CONSTRUCT;
  2370. exports.ESM = ESM;
  2371. exports.PatternMatcher = PatternMatcher;
  2372. exports.READ = READ;
  2373. exports.ReferenceTracker = ReferenceTracker;
  2374. exports["default"] = index;
  2375. exports.findVariable = findVariable;
  2376. exports.getFunctionHeadLocation = getFunctionHeadLocation;
  2377. exports.getFunctionNameWithKind = getFunctionNameWithKind;
  2378. exports.getInnermostScope = getInnermostScope;
  2379. exports.getPropertyName = getPropertyName;
  2380. exports.getStaticValue = getStaticValue;
  2381. exports.getStringIfConstant = getStringIfConstant;
  2382. exports.hasSideEffect = hasSideEffect;
  2383. exports.isArrowToken = isArrowToken;
  2384. exports.isClosingBraceToken = isClosingBraceToken;
  2385. exports.isClosingBracketToken = isClosingBracketToken;
  2386. exports.isClosingParenToken = isClosingParenToken;
  2387. exports.isColonToken = isColonToken;
  2388. exports.isCommaToken = isCommaToken;
  2389. exports.isCommentToken = isCommentToken;
  2390. exports.isNotArrowToken = isNotArrowToken;
  2391. exports.isNotClosingBraceToken = isNotClosingBraceToken;
  2392. exports.isNotClosingBracketToken = isNotClosingBracketToken;
  2393. exports.isNotClosingParenToken = isNotClosingParenToken;
  2394. exports.isNotColonToken = isNotColonToken;
  2395. exports.isNotCommaToken = isNotCommaToken;
  2396. exports.isNotCommentToken = isNotCommentToken;
  2397. exports.isNotOpeningBraceToken = isNotOpeningBraceToken;
  2398. exports.isNotOpeningBracketToken = isNotOpeningBracketToken;
  2399. exports.isNotOpeningParenToken = isNotOpeningParenToken;
  2400. exports.isNotSemicolonToken = isNotSemicolonToken;
  2401. exports.isOpeningBraceToken = isOpeningBraceToken;
  2402. exports.isOpeningBracketToken = isOpeningBracketToken;
  2403. exports.isOpeningParenToken = isOpeningParenToken;
  2404. exports.isParenthesized = isParenthesized;
  2405. exports.isSemicolonToken = isSemicolonToken;
  2406. //# sourceMappingURL=index.js.map