indent-common.js 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012
  1. /**
  2. * @author Toru Nagashima <https://github.com/mysticatea>
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. const {
  7. isArrowToken,
  8. isOpeningParenToken,
  9. isClosingParenToken,
  10. isNotOpeningParenToken,
  11. isNotClosingParenToken,
  12. isOpeningBraceToken,
  13. isClosingBraceToken,
  14. isNotOpeningBraceToken,
  15. isOpeningBracketToken,
  16. isClosingBracketToken,
  17. isSemicolonToken,
  18. isNotSemicolonToken
  19. } = require('@eslint-community/eslint-utils')
  20. const {
  21. isComment,
  22. isNotComment,
  23. isWildcard,
  24. isExtendsKeyword,
  25. isNotWhitespace,
  26. isNotEmptyTextNode,
  27. isPipeOperator,
  28. last
  29. } = require('./indent-utils')
  30. const { defineVisitor: tsDefineVisitor } = require('./indent-ts')
  31. /**
  32. * @typedef {import('../../typings/eslint-plugin-vue/util-types/node').HasLocation} HasLocation
  33. * @typedef { { type: string } & HasLocation } MaybeNode
  34. */
  35. const LT_CHAR = /[\n\r\u2028\u2029]/
  36. const LINES = /[^\n\r\u2028\u2029]+(?:$|\r\n|[\n\r\u2028\u2029])/g
  37. const BLOCK_COMMENT_PREFIX = /^\s*\*/
  38. const ITERATION_OPTS = Object.freeze({
  39. includeComments: true,
  40. filter: isNotWhitespace
  41. })
  42. const PREFORMATTED_ELEMENT_NAMES = new Set(['pre', 'textarea'])
  43. /**
  44. * @typedef {object} IndentOptions
  45. * @property { " " | "\t" } IndentOptions.indentChar
  46. * @property {number} IndentOptions.indentSize
  47. * @property {number} IndentOptions.baseIndent
  48. * @property {number} IndentOptions.attribute
  49. * @property {object} IndentOptions.closeBracket
  50. * @property {number} IndentOptions.closeBracket.startTag
  51. * @property {number} IndentOptions.closeBracket.endTag
  52. * @property {number} IndentOptions.closeBracket.selfClosingTag
  53. * @property {number} IndentOptions.switchCase
  54. * @property {boolean} IndentOptions.alignAttributesVertically
  55. * @property {string[]} IndentOptions.ignores
  56. */
  57. /**
  58. * @typedef {object} IndentUserOptions
  59. * @property { " " | "\t" } [IndentUserOptions.indentChar]
  60. * @property {number} [IndentUserOptions.indentSize]
  61. * @property {number} [IndentUserOptions.baseIndent]
  62. * @property {number} [IndentUserOptions.attribute]
  63. * @property {IndentOptions['closeBracket'] | number} [IndentUserOptions.closeBracket]
  64. * @property {number} [IndentUserOptions.switchCase]
  65. * @property {boolean} [IndentUserOptions.alignAttributesVertically]
  66. * @property {string[]} [IndentUserOptions.ignores]
  67. */
  68. /**
  69. * Normalize options.
  70. * @param {number|"tab"|undefined} type The type of indentation.
  71. * @param {IndentUserOptions} options Other options.
  72. * @param {Partial<IndentOptions>} defaultOptions The default value of options.
  73. * @returns {IndentOptions} Normalized options.
  74. */
  75. function parseOptions(type, options, defaultOptions) {
  76. /** @type {IndentOptions} */
  77. const ret = Object.assign(
  78. {
  79. indentChar: ' ',
  80. indentSize: 2,
  81. baseIndent: 0,
  82. attribute: 1,
  83. closeBracket: {
  84. startTag: 0,
  85. endTag: 0,
  86. selfClosingTag: 0
  87. },
  88. switchCase: 0,
  89. alignAttributesVertically: true,
  90. ignores: []
  91. },
  92. defaultOptions
  93. )
  94. if (Number.isSafeInteger(type)) {
  95. ret.indentSize = Number(type)
  96. } else if (type === 'tab') {
  97. ret.indentChar = '\t'
  98. ret.indentSize = 1
  99. }
  100. if (options.baseIndent != null && Number.isSafeInteger(options.baseIndent)) {
  101. ret.baseIndent = options.baseIndent
  102. }
  103. if (options.attribute != null && Number.isSafeInteger(options.attribute)) {
  104. ret.attribute = options.attribute
  105. }
  106. if (Number.isSafeInteger(options.closeBracket)) {
  107. const num = Number(options.closeBracket)
  108. ret.closeBracket = {
  109. startTag: num,
  110. endTag: num,
  111. selfClosingTag: num
  112. }
  113. } else if (options.closeBracket) {
  114. ret.closeBracket = Object.assign(
  115. {
  116. startTag: 0,
  117. endTag: 0,
  118. selfClosingTag: 0
  119. },
  120. options.closeBracket
  121. )
  122. }
  123. if (options.switchCase != null && Number.isSafeInteger(options.switchCase)) {
  124. ret.switchCase = options.switchCase
  125. }
  126. if (options.alignAttributesVertically != null) {
  127. ret.alignAttributesVertically = options.alignAttributesVertically
  128. }
  129. if (options.ignores != null) {
  130. ret.ignores = options.ignores
  131. }
  132. return ret
  133. }
  134. /**
  135. * Check whether the node is at the beginning of line.
  136. * @param {MaybeNode|null} node The node to check.
  137. * @param {number} index The index of the node in the nodes.
  138. * @param {(MaybeNode|null)[]} nodes The array of nodes.
  139. * @returns {boolean} `true` if the node is at the beginning of line.
  140. */
  141. function isBeginningOfLine(node, index, nodes) {
  142. if (node != null) {
  143. for (let i = index - 1; i >= 0; --i) {
  144. const prevNode = nodes[i]
  145. if (prevNode == null) {
  146. continue
  147. }
  148. return node.loc.start.line !== prevNode.loc.end.line
  149. }
  150. }
  151. return false
  152. }
  153. /**
  154. * Check whether a given token is a closing token which triggers unindent.
  155. * @param {Token} token The token to check.
  156. * @returns {boolean} `true` if the token is a closing token.
  157. */
  158. function isClosingToken(token) {
  159. return (
  160. token != null &&
  161. (token.type === 'HTMLEndTagOpen' ||
  162. token.type === 'VExpressionEnd' ||
  163. (token.type === 'Punctuator' &&
  164. (token.value === ')' || token.value === '}' || token.value === ']')))
  165. )
  166. }
  167. /**
  168. * Checks whether a given token is a optional token.
  169. * @param {Token} token The token to check.
  170. * @returns {boolean} `true` if the token is a optional token.
  171. */
  172. function isOptionalToken(token) {
  173. return token.type === 'Punctuator' && token.value === '?.'
  174. }
  175. /**
  176. * Creates AST event handlers for html-indent.
  177. *
  178. * @param {RuleContext} context The rule context.
  179. * @param {ParserServices.TokenStore | SourceCode} tokenStore The token store object to get tokens.
  180. * @param {Partial<IndentOptions>} defaultOptions The default value of options.
  181. * @returns {NodeListener} AST event handlers.
  182. */
  183. module.exports.defineVisitor = function create(
  184. context,
  185. tokenStore,
  186. defaultOptions
  187. ) {
  188. if (!context.getFilename().endsWith('.vue')) return {}
  189. const options = parseOptions(
  190. context.options[0],
  191. context.options[1] || {},
  192. defaultOptions
  193. )
  194. const sourceCode = context.getSourceCode()
  195. /**
  196. * @typedef { { baseToken: Token | null, offset: number, baseline: boolean, expectedIndent: number | undefined } } OffsetData
  197. */
  198. /** @type {Map<Token|null, OffsetData>} */
  199. const offsets = new Map()
  200. const ignoreTokens = new Set()
  201. /**
  202. * Set offset to the given tokens.
  203. * @param {Token|Token[]|null|(Token|null)[]} token The token to set.
  204. * @param {number} offset The offset of the tokens.
  205. * @param {Token} baseToken The token of the base offset.
  206. * @returns {void}
  207. */
  208. function setOffset(token, offset, baseToken) {
  209. if (!token || token === baseToken) {
  210. return
  211. }
  212. if (Array.isArray(token)) {
  213. for (const t of token) {
  214. if (!t || t === baseToken) continue
  215. offsets.set(t, {
  216. baseToken,
  217. offset,
  218. baseline: false,
  219. expectedIndent: undefined
  220. })
  221. }
  222. } else {
  223. offsets.set(token, {
  224. baseToken,
  225. offset,
  226. baseline: false,
  227. expectedIndent: undefined
  228. })
  229. }
  230. }
  231. /**
  232. * Copy offset to the given tokens from srcToken.
  233. * @param {Token} token The token to set.
  234. * @param {Token} srcToken The token of the source offset.
  235. * @returns {void}
  236. */
  237. function copyOffset(token, srcToken) {
  238. if (!token) {
  239. return
  240. }
  241. const offsetData = offsets.get(srcToken)
  242. if (!offsetData) {
  243. return
  244. }
  245. setOffset(
  246. token,
  247. offsetData.offset,
  248. /** @type {Token} */ (offsetData.baseToken)
  249. )
  250. if (offsetData.baseline) {
  251. setBaseline(token)
  252. }
  253. const o = /** @type {OffsetData} */ (offsets.get(token))
  254. o.expectedIndent = offsetData.expectedIndent
  255. }
  256. /**
  257. * Set baseline flag to the given token.
  258. * @param {Token} token The token to set.
  259. * @returns {void}
  260. */
  261. function setBaseline(token) {
  262. const offsetInfo = offsets.get(token)
  263. if (offsetInfo != null) {
  264. offsetInfo.baseline = true
  265. }
  266. }
  267. /**
  268. * Sets preformatted tokens to the given element node.
  269. * @param {VElement} node The node to set.
  270. * @returns {void}
  271. */
  272. function setPreformattedTokens(node) {
  273. const endToken =
  274. (node.endTag && tokenStore.getFirstToken(node.endTag)) ||
  275. tokenStore.getTokenAfter(node)
  276. /** @type {SourceCode.CursorWithSkipOptions} */
  277. const cursorOptions = {
  278. includeComments: true,
  279. filter: (token) =>
  280. token != null &&
  281. (token.type === 'HTMLText' ||
  282. token.type === 'HTMLRCDataText' ||
  283. token.type === 'HTMLTagOpen' ||
  284. token.type === 'HTMLEndTagOpen' ||
  285. token.type === 'HTMLComment')
  286. }
  287. const contentTokens = endToken
  288. ? tokenStore.getTokensBetween(node.startTag, endToken, cursorOptions)
  289. : tokenStore.getTokensAfter(node.startTag, cursorOptions)
  290. for (const token of contentTokens) {
  291. ignoreTokens.add(token)
  292. }
  293. ignoreTokens.add(endToken)
  294. }
  295. /**
  296. * Get the first and last tokens of the given node.
  297. * If the node is parenthesized, this gets the outermost parentheses.
  298. * @param {MaybeNode} node The node to get.
  299. * @param {number} [borderOffset] The least offset of the first token. Defailt is 0. This value is used to prevent false positive in the following case: `(a) => {}` The parentheses are enclosing the whole parameter part rather than the first parameter, but this offset parameter is needed to distinguish.
  300. * @returns {{firstToken:Token,lastToken:Token}} The gotten tokens.
  301. */
  302. function getFirstAndLastTokens(node, borderOffset = 0) {
  303. borderOffset = Math.trunc(borderOffset)
  304. let firstToken = tokenStore.getFirstToken(node)
  305. let lastToken = tokenStore.getLastToken(node)
  306. // Get the outermost left parenthesis if it's parenthesized.
  307. let t, u
  308. while (
  309. (t = tokenStore.getTokenBefore(firstToken)) != null &&
  310. (u = tokenStore.getTokenAfter(lastToken)) != null &&
  311. isOpeningParenToken(t) &&
  312. isClosingParenToken(u) &&
  313. t.range[0] >= borderOffset
  314. ) {
  315. firstToken = t
  316. lastToken = u
  317. }
  318. return { firstToken, lastToken }
  319. }
  320. /**
  321. * Process the given node list.
  322. * The first node is offsetted from the given left token.
  323. * Rest nodes are adjusted to the first node.
  324. * @param {(MaybeNode|null)[]} nodeList The node to process.
  325. * @param {MaybeNode|Token|null} left The left parenthesis token.
  326. * @param {MaybeNode|Token|null} right The right parenthesis token.
  327. * @param {number} offset The offset to set.
  328. * @param {boolean} [alignVertically=true] The flag to align vertically. If `false`, this doesn't align vertically even if the first node is not at beginning of line.
  329. * @returns {void}
  330. */
  331. function processNodeList(nodeList, left, right, offset, alignVertically) {
  332. let t
  333. const leftToken = left && tokenStore.getFirstToken(left)
  334. const rightToken = right && tokenStore.getFirstToken(right)
  335. if (nodeList.length > 0) {
  336. let baseToken = null
  337. let lastToken = left
  338. const alignTokensBeforeBaseToken = []
  339. const alignTokens = []
  340. for (const node of nodeList) {
  341. if (node == null) {
  342. // Holes of an array.
  343. continue
  344. }
  345. const elementTokens = getFirstAndLastTokens(
  346. node,
  347. lastToken != null ? lastToken.range[1] : 0
  348. )
  349. // Collect comma/comment tokens between the last token of the previous node and the first token of this node.
  350. if (lastToken != null) {
  351. t = lastToken
  352. while (
  353. (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null &&
  354. t.range[1] <= elementTokens.firstToken.range[0]
  355. ) {
  356. if (baseToken == null) {
  357. alignTokensBeforeBaseToken.push(t)
  358. } else {
  359. alignTokens.push(t)
  360. }
  361. }
  362. }
  363. if (baseToken == null) {
  364. baseToken = elementTokens.firstToken
  365. } else {
  366. alignTokens.push(elementTokens.firstToken)
  367. }
  368. // Save the last token to find tokens between this node and the next node.
  369. lastToken = elementTokens.lastToken
  370. }
  371. // Check trailing commas and comments.
  372. if (rightToken != null && lastToken != null) {
  373. t = lastToken
  374. while (
  375. (t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null &&
  376. t.range[1] <= rightToken.range[0]
  377. ) {
  378. if (baseToken == null) {
  379. alignTokensBeforeBaseToken.push(t)
  380. } else {
  381. alignTokens.push(t)
  382. }
  383. }
  384. }
  385. // Set offsets.
  386. if (leftToken != null) {
  387. setOffset(alignTokensBeforeBaseToken, offset, leftToken)
  388. }
  389. if (baseToken != null) {
  390. // Set offset to the first token.
  391. if (leftToken != null) {
  392. setOffset(baseToken, offset, leftToken)
  393. }
  394. // Set baseline.
  395. if (nodeList.some(isBeginningOfLine)) {
  396. setBaseline(baseToken)
  397. }
  398. if (alignVertically === false && leftToken != null) {
  399. // Align tokens relatively to the left token.
  400. setOffset(alignTokens, offset, leftToken)
  401. } else {
  402. // Align the rest tokens to the first token.
  403. setOffset(alignTokens, 0, baseToken)
  404. }
  405. }
  406. }
  407. if (rightToken != null && leftToken != null) {
  408. setOffset(rightToken, 0, leftToken)
  409. }
  410. }
  411. /**
  412. * Process the given node as body.
  413. * The body node maybe a block statement or an expression node.
  414. * @param {ASTNode} node The body node to process.
  415. * @param {Token} baseToken The base token.
  416. * @returns {void}
  417. */
  418. function processMaybeBlock(node, baseToken) {
  419. const firstToken = getFirstAndLastTokens(node).firstToken
  420. setOffset(firstToken, isOpeningBraceToken(firstToken) ? 0 : 1, baseToken)
  421. }
  422. /**
  423. * Process semicolons of the given statement node.
  424. * @param {MaybeNode} node The statement node to process.
  425. * @returns {void}
  426. */
  427. function processSemicolons(node) {
  428. const firstToken = tokenStore.getFirstToken(node)
  429. const lastToken = tokenStore.getLastToken(node)
  430. if (isSemicolonToken(lastToken) && firstToken !== lastToken) {
  431. setOffset(lastToken, 0, firstToken)
  432. }
  433. // Set to the semicolon of the previous token for semicolon-free style.
  434. // E.g.,
  435. // foo
  436. // ;[1,2,3].forEach(f)
  437. const info = offsets.get(firstToken)
  438. const prevToken = tokenStore.getTokenBefore(firstToken)
  439. if (
  440. info != null &&
  441. prevToken &&
  442. isSemicolonToken(prevToken) &&
  443. prevToken.loc.end.line === firstToken.loc.start.line
  444. ) {
  445. offsets.set(prevToken, info)
  446. }
  447. }
  448. /**
  449. * Find the head of chaining nodes.
  450. * @param {ASTNode} node The start node to find the head.
  451. * @returns {Token} The head token of the chain.
  452. */
  453. function getChainHeadToken(node) {
  454. const type = node.type
  455. while (node.parent && node.parent.type === type) {
  456. const prevToken = tokenStore.getTokenBefore(node)
  457. if (isOpeningParenToken(prevToken)) {
  458. // The chaining is broken by parentheses.
  459. break
  460. }
  461. node = node.parent
  462. }
  463. return tokenStore.getFirstToken(node)
  464. }
  465. /**
  466. * Check whether a given token is the first token of:
  467. *
  468. * - ExpressionStatement
  469. * - VExpressionContainer
  470. * - A parameter of CallExpression/NewExpression
  471. * - An element of ArrayExpression
  472. * - An expression of SequenceExpression
  473. *
  474. * @param {Token} token The token to check.
  475. * @param {ASTNode} belongingNode The node that the token is belonging to.
  476. * @returns {boolean} `true` if the token is the first token of an element.
  477. */
  478. function isBeginningOfElement(token, belongingNode) {
  479. let node = belongingNode
  480. while (node != null && node.parent != null) {
  481. const parent = node.parent
  482. if (
  483. parent.type.endsWith('Statement') ||
  484. parent.type.endsWith('Declaration')
  485. ) {
  486. return parent.range[0] === token.range[0]
  487. }
  488. if (parent.type === 'VExpressionContainer') {
  489. if (node.range[0] !== token.range[0]) {
  490. return false
  491. }
  492. const prevToken = tokenStore.getTokenBefore(belongingNode)
  493. if (isOpeningParenToken(prevToken)) {
  494. // It is not the first token because it is enclosed in parentheses.
  495. return false
  496. }
  497. return true
  498. }
  499. if (parent.type === 'CallExpression' || parent.type === 'NewExpression') {
  500. const openParen = /** @type {Token} */ (
  501. tokenStore.getTokenAfter(parent.callee, isNotClosingParenToken)
  502. )
  503. return parent.arguments.some(
  504. (param) =>
  505. getFirstAndLastTokens(param, openParen.range[1]).firstToken
  506. .range[0] === token.range[0]
  507. )
  508. }
  509. if (parent.type === 'ArrayExpression') {
  510. return parent.elements.some(
  511. (element) =>
  512. element != null &&
  513. getFirstAndLastTokens(element).firstToken.range[0] ===
  514. token.range[0]
  515. )
  516. }
  517. if (parent.type === 'SequenceExpression') {
  518. return parent.expressions.some(
  519. (expr) =>
  520. getFirstAndLastTokens(expr).firstToken.range[0] === token.range[0]
  521. )
  522. }
  523. node = parent
  524. }
  525. return false
  526. }
  527. /**
  528. * Set the base indentation to a given top-level AST node.
  529. * @param {ASTNode} node The node to set.
  530. * @param {number} expectedIndent The number of expected indent.
  531. * @returns {void}
  532. */
  533. function processTopLevelNode(node, expectedIndent) {
  534. const token = tokenStore.getFirstToken(node)
  535. const offsetInfo = offsets.get(token)
  536. if (offsetInfo != null) {
  537. offsetInfo.expectedIndent = expectedIndent
  538. } else {
  539. offsets.set(token, {
  540. baseToken: null,
  541. offset: 0,
  542. baseline: false,
  543. expectedIndent
  544. })
  545. }
  546. }
  547. /**
  548. * Ignore all tokens of the given node.
  549. * @param {ASTNode} node The node to ignore.
  550. * @returns {void}
  551. */
  552. function ignore(node) {
  553. for (const token of tokenStore.getTokens(node)) {
  554. offsets.delete(token)
  555. ignoreTokens.add(token)
  556. }
  557. }
  558. /**
  559. * Define functions to ignore nodes into the given visitor.
  560. * @param {NodeListener} visitor The visitor to define functions to ignore nodes.
  561. * @returns {NodeListener} The visitor.
  562. */
  563. function processIgnores(visitor) {
  564. for (const ignorePattern of options.ignores) {
  565. const key = `${ignorePattern}:exit`
  566. if (visitor.hasOwnProperty(key)) {
  567. const handler = visitor[key]
  568. visitor[key] = function (node, ...args) {
  569. // @ts-expect-error
  570. const ret = handler.call(this, node, ...args)
  571. ignore(node)
  572. return ret
  573. }
  574. } else {
  575. visitor[key] = ignore
  576. }
  577. }
  578. return visitor
  579. }
  580. /**
  581. * Calculate correct indentation of the line of the given tokens.
  582. * @param {Token[]} tokens Tokens which are on the same line.
  583. * @returns { { expectedIndent: number, expectedBaseIndent: number } |null } Correct indentation. If it failed to calculate then `null`.
  584. */
  585. function getExpectedIndents(tokens) {
  586. const expectedIndents = []
  587. for (const [i, token] of tokens.entries()) {
  588. const offsetInfo = offsets.get(token)
  589. if (offsetInfo != null) {
  590. if (offsetInfo.expectedIndent != null) {
  591. expectedIndents.push(offsetInfo.expectedIndent)
  592. } else {
  593. const baseOffsetInfo = offsets.get(offsetInfo.baseToken)
  594. if (
  595. baseOffsetInfo != null &&
  596. baseOffsetInfo.expectedIndent != null &&
  597. (i === 0 || !baseOffsetInfo.baseline)
  598. ) {
  599. expectedIndents.push(
  600. baseOffsetInfo.expectedIndent +
  601. offsetInfo.offset * options.indentSize
  602. )
  603. if (baseOffsetInfo.baseline) {
  604. break
  605. }
  606. }
  607. }
  608. }
  609. }
  610. if (expectedIndents.length === 0) {
  611. return null
  612. }
  613. return {
  614. expectedIndent: expectedIndents[0],
  615. expectedBaseIndent: Math.min(...expectedIndents)
  616. }
  617. }
  618. /**
  619. * Get the text of the indentation part of the line which the given token is on.
  620. * @param {Token} firstToken The first token on a line.
  621. * @returns {string} The text of indentation part.
  622. */
  623. function getIndentText(firstToken) {
  624. const text = sourceCode.text
  625. let i = firstToken.range[0] - 1
  626. while (i >= 0 && !LT_CHAR.test(text[i])) {
  627. i -= 1
  628. }
  629. return text.slice(i + 1, firstToken.range[0])
  630. }
  631. /**
  632. * Define the function which fixes the problem.
  633. * @param {Token} token The token to fix.
  634. * @param {number} actualIndent The number of actual indentation.
  635. * @param {number} expectedIndent The number of expected indentation.
  636. * @returns { (fixer: RuleFixer) => Fix } The defined function.
  637. */
  638. function defineFix(token, actualIndent, expectedIndent) {
  639. if (token.type === 'Block' && token.loc.start.line !== token.loc.end.line) {
  640. // Fix indentation in multiline block comments.
  641. const lines = sourceCode.getText(token).match(LINES) || []
  642. const firstLine = lines.shift()
  643. if (lines.every((l) => BLOCK_COMMENT_PREFIX.test(l))) {
  644. return (fixer) => {
  645. /** @type {Range} */
  646. const range = [token.range[0] - actualIndent, token.range[1]]
  647. const indent = options.indentChar.repeat(expectedIndent)
  648. return fixer.replaceTextRange(
  649. range,
  650. `${indent}${firstLine}${lines
  651. .map((l) => l.replace(BLOCK_COMMENT_PREFIX, `${indent} *`))
  652. .join('')}`
  653. )
  654. }
  655. }
  656. }
  657. return (fixer) => {
  658. /** @type {Range} */
  659. const range = [token.range[0] - actualIndent, token.range[0]]
  660. const indent = options.indentChar.repeat(expectedIndent)
  661. return fixer.replaceTextRange(range, indent)
  662. }
  663. }
  664. /**
  665. * Validate the given token with the pre-calculated expected indentation.
  666. * @param {Token} token The token to validate.
  667. * @param {number} expectedIndent The expected indentation.
  668. * @param {[number, number?]} [optionalExpectedIndents] The optional expected indentation.
  669. * @returns {void}
  670. */
  671. function validateCore(token, expectedIndent, optionalExpectedIndents) {
  672. const line = token.loc.start.line
  673. const indentText = getIndentText(token)
  674. // If there is no line terminator after the `<script>` start tag,
  675. // `indentText` contains non-whitespace characters.
  676. // In that case, do nothing in order to prevent removing the `<script>` tag.
  677. if (indentText.trim() !== '') {
  678. return
  679. }
  680. const actualIndent = token.loc.start.column
  681. const unit = options.indentChar === '\t' ? 'tab' : 'space'
  682. for (const [i, char] of [...indentText].entries()) {
  683. if (char !== options.indentChar) {
  684. context.report({
  685. loc: {
  686. start: { line, column: i },
  687. end: { line, column: i + 1 }
  688. },
  689. message:
  690. 'Expected {{expected}} character, but found {{actual}} character.',
  691. data: {
  692. expected: JSON.stringify(options.indentChar),
  693. actual: JSON.stringify(char)
  694. },
  695. fix: defineFix(token, actualIndent, expectedIndent)
  696. })
  697. return
  698. }
  699. }
  700. if (
  701. actualIndent !== expectedIndent &&
  702. (optionalExpectedIndents == null ||
  703. !optionalExpectedIndents.includes(actualIndent))
  704. ) {
  705. context.report({
  706. loc: {
  707. start: { line, column: 0 },
  708. end: { line, column: actualIndent }
  709. },
  710. message:
  711. 'Expected indentation of {{expectedIndent}} {{unit}}{{expectedIndentPlural}} but found {{actualIndent}} {{unit}}{{actualIndentPlural}}.',
  712. data: {
  713. expectedIndent,
  714. actualIndent,
  715. unit,
  716. expectedIndentPlural: expectedIndent === 1 ? '' : 's',
  717. actualIndentPlural: actualIndent === 1 ? '' : 's'
  718. },
  719. fix: defineFix(token, actualIndent, expectedIndent)
  720. })
  721. }
  722. }
  723. /**
  724. * Get the expected indent of comments.
  725. * @param {Token} nextToken The next token of comments.
  726. * @param {number} nextExpectedIndent The expected indent of the next token.
  727. * @param {number|undefined} lastExpectedIndent The expected indent of the last token.
  728. * @returns {[number, number?]}
  729. */
  730. function getCommentExpectedIndents(
  731. nextToken,
  732. nextExpectedIndent,
  733. lastExpectedIndent
  734. ) {
  735. if (typeof lastExpectedIndent === 'number' && isClosingToken(nextToken)) {
  736. if (nextExpectedIndent === lastExpectedIndent) {
  737. // For solo comment. E.g.,
  738. // <div>
  739. // <!-- comment -->
  740. // </div>
  741. return [nextExpectedIndent + options.indentSize, nextExpectedIndent]
  742. }
  743. // For last comment. E.g.,
  744. // <div>
  745. // <div></div>
  746. // <!-- comment -->
  747. // </div>
  748. return [lastExpectedIndent, nextExpectedIndent]
  749. }
  750. // Adjust to next normally. E.g.,
  751. // <div>
  752. // <!-- comment -->
  753. // <div></div>
  754. // </div>
  755. return [nextExpectedIndent]
  756. }
  757. /**
  758. * Validate indentation of the line that the given tokens are on.
  759. * @param {Token[]} tokens The tokens on the same line to validate.
  760. * @param {Token[]} comments The comments which are on the immediately previous lines of the tokens.
  761. * @param {Token|null} lastToken The last validated token. Comments can adjust to the token.
  762. * @returns {void}
  763. */
  764. function validate(tokens, comments, lastToken) {
  765. // Calculate and save expected indentation.
  766. const firstToken = tokens[0]
  767. const actualIndent = firstToken.loc.start.column
  768. const expectedIndents = getExpectedIndents(tokens)
  769. if (!expectedIndents) {
  770. return
  771. }
  772. const expectedBaseIndent = expectedIndents.expectedBaseIndent
  773. const expectedIndent = expectedIndents.expectedIndent
  774. // Debug log
  775. // console.log('line', firstToken.loc.start.line, '=', { actualIndent, expectedIndent }, 'from:')
  776. // for (const token of tokens) {
  777. // const offsetInfo = offsets.get(token)
  778. // if (offsetInfo == null) {
  779. // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is unknown.')
  780. // } else if (offsetInfo.expectedIndent != null) {
  781. // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is fixed at', offsetInfo.expectedIndent, '.')
  782. // } else {
  783. // const baseOffsetInfo = offsets.get(offsetInfo.baseToken)
  784. // console.log(' ', JSON.stringify(sourceCode.getText(token)), 'is', offsetInfo.offset, 'offset from ', JSON.stringify(sourceCode.getText(offsetInfo.baseToken)), '( line:', offsetInfo.baseToken && offsetInfo.baseToken.loc.start.line, ', indent:', baseOffsetInfo && baseOffsetInfo.expectedIndent, ', baseline:', baseOffsetInfo && baseOffsetInfo.baseline, ')')
  785. // }
  786. // }
  787. // Save.
  788. const baseline = new Set()
  789. for (const token of tokens) {
  790. const offsetInfo = offsets.get(token)
  791. if (offsetInfo != null) {
  792. if (offsetInfo.baseline) {
  793. // This is a baseline token, so the expected indent is the column of this token.
  794. offsetInfo.expectedIndent =
  795. options.indentChar === ' '
  796. ? Math.max(
  797. 0,
  798. token.loc.start.column + expectedBaseIndent - actualIndent
  799. )
  800. : // In hard-tabs mode, it cannot align tokens strictly, so use one additional offset.
  801. // But the additional offset isn't needed if it's at the beginning of the line.
  802. expectedBaseIndent + (token === tokens[0] ? 0 : 1)
  803. baseline.add(token)
  804. } else if (baseline.has(offsetInfo.baseToken)) {
  805. // The base token is a baseline token on this line, so inherit it.
  806. offsetInfo.expectedIndent = /** @type {OffsetData} */ (
  807. offsets.get(offsetInfo.baseToken)
  808. ).expectedIndent
  809. baseline.add(token)
  810. } else {
  811. // Otherwise, set the expected indent of this line.
  812. offsetInfo.expectedIndent = expectedBaseIndent
  813. }
  814. }
  815. }
  816. // It does not validate ignore tokens.
  817. if (ignoreTokens.has(firstToken)) {
  818. return
  819. }
  820. // Calculate the expected indents for comments.
  821. // It allows the same indent level with the previous line.
  822. const lastOffsetInfo = offsets.get(lastToken)
  823. const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo.expectedIndent
  824. const commentOptionalExpectedIndents = getCommentExpectedIndents(
  825. firstToken,
  826. expectedIndent,
  827. lastExpectedIndent
  828. )
  829. // Validate.
  830. for (const comment of comments) {
  831. const commentExpectedIndents = getExpectedIndents([comment])
  832. const commentExpectedIndent = commentExpectedIndents
  833. ? commentExpectedIndents.expectedIndent
  834. : commentOptionalExpectedIndents[0]
  835. validateCore(
  836. comment,
  837. commentExpectedIndent,
  838. commentOptionalExpectedIndents
  839. )
  840. }
  841. validateCore(firstToken, expectedIndent)
  842. }
  843. // ------------------------------------------------------------------------------
  844. // Main
  845. // ------------------------------------------------------------------------------
  846. /** @type {Set<string>} */
  847. const knownNodes = new Set()
  848. /** @type {TemplateListener} */
  849. const visitor = {
  850. // ----------------------------------------------------------------------
  851. // Vue NODES
  852. // ----------------------------------------------------------------------
  853. /** @param {VAttribute | VDirective} node */
  854. VAttribute(node) {
  855. const keyToken = tokenStore.getFirstToken(node)
  856. const eqToken = tokenStore.getTokenAfter(node.key)
  857. if (eqToken != null && eqToken.range[1] <= node.range[1]) {
  858. setOffset(eqToken, 1, keyToken)
  859. const valueToken = tokenStore.getTokenAfter(eqToken)
  860. if (valueToken != null && valueToken.range[1] <= node.range[1]) {
  861. setOffset(valueToken, 1, keyToken)
  862. }
  863. }
  864. },
  865. /** @param {VElement} node */
  866. VElement(node) {
  867. if (!PREFORMATTED_ELEMENT_NAMES.has(node.name)) {
  868. const isTopLevel = node.parent.type !== 'VElement'
  869. const offset = isTopLevel ? options.baseIndent : 1
  870. processNodeList(
  871. node.children.filter(isNotEmptyTextNode),
  872. node.startTag,
  873. node.endTag,
  874. offset,
  875. false
  876. )
  877. } else {
  878. const startTagToken = tokenStore.getFirstToken(node)
  879. const endTagToken = node.endTag && tokenStore.getFirstToken(node.endTag)
  880. setOffset(endTagToken, 0, startTagToken)
  881. setPreformattedTokens(node)
  882. }
  883. },
  884. /** @param {VEndTag} node */
  885. VEndTag(node) {
  886. const element = node.parent
  887. const startTagOpenToken = tokenStore.getFirstToken(element.startTag)
  888. const closeToken = tokenStore.getLastToken(node)
  889. if (closeToken.type.endsWith('TagClose')) {
  890. setOffset(closeToken, options.closeBracket.endTag, startTagOpenToken)
  891. }
  892. },
  893. /** @param {VExpressionContainer} node */
  894. VExpressionContainer(node) {
  895. if (
  896. node.expression != null &&
  897. node.range[0] !== node.expression.range[0]
  898. ) {
  899. const startQuoteToken = tokenStore.getFirstToken(node)
  900. const endQuoteToken = tokenStore.getLastToken(node)
  901. const childToken = tokenStore.getTokenAfter(startQuoteToken)
  902. setOffset(childToken, 1, startQuoteToken)
  903. setOffset(endQuoteToken, 0, startQuoteToken)
  904. }
  905. },
  906. /** @param {VFilter} node */
  907. VFilter(node) {
  908. const idToken = tokenStore.getFirstToken(node)
  909. const lastToken = tokenStore.getLastToken(node)
  910. if (isClosingParenToken(lastToken)) {
  911. const leftParenToken = tokenStore.getTokenAfter(node.callee)
  912. setOffset(leftParenToken, 1, idToken)
  913. processNodeList(node.arguments, leftParenToken, lastToken, 1)
  914. }
  915. },
  916. /** @param {VFilterSequenceExpression} node */
  917. VFilterSequenceExpression(node) {
  918. if (node.filters.length === 0) {
  919. return
  920. }
  921. const firstToken = tokenStore.getFirstToken(node)
  922. /** @type {(Token|null)[]} */
  923. const tokens = []
  924. for (const filter of node.filters) {
  925. tokens.push(
  926. tokenStore.getTokenBefore(filter, isPipeOperator),
  927. tokenStore.getFirstToken(filter)
  928. )
  929. }
  930. setOffset(tokens, 1, firstToken)
  931. },
  932. /** @param {VForExpression} node */
  933. VForExpression(node) {
  934. const firstToken = tokenStore.getFirstToken(node)
  935. const lastOfLeft = last(node.left) || firstToken
  936. const inToken = /** @type {Token} */ (
  937. tokenStore.getTokenAfter(lastOfLeft, isNotClosingParenToken)
  938. )
  939. const rightToken = tokenStore.getFirstToken(node.right)
  940. if (isOpeningParenToken(firstToken)) {
  941. const rightToken = tokenStore.getTokenAfter(
  942. lastOfLeft,
  943. isClosingParenToken
  944. )
  945. processNodeList(node.left, firstToken, rightToken, 1)
  946. }
  947. setOffset(inToken, 1, firstToken)
  948. setOffset(rightToken, 1, inToken)
  949. },
  950. /** @param {VOnExpression} node */
  951. VOnExpression(node) {
  952. processNodeList(node.body, null, null, 0)
  953. },
  954. /** @param {VStartTag} node */
  955. VStartTag(node) {
  956. const openToken = tokenStore.getFirstToken(node)
  957. const closeToken = tokenStore.getLastToken(node)
  958. processNodeList(
  959. node.attributes,
  960. openToken,
  961. null,
  962. options.attribute,
  963. options.alignAttributesVertically
  964. )
  965. if (closeToken != null && closeToken.type.endsWith('TagClose')) {
  966. const offset =
  967. closeToken.type !== 'HTMLSelfClosingTagClose'
  968. ? options.closeBracket.startTag
  969. : options.closeBracket.selfClosingTag
  970. setOffset(closeToken, offset, openToken)
  971. }
  972. },
  973. /** @param {VText} node */
  974. VText(node) {
  975. const tokens = tokenStore.getTokens(node, isNotWhitespace)
  976. const firstTokenInfo = offsets.get(tokenStore.getFirstToken(node))
  977. for (const token of tokens) {
  978. offsets.set(token, Object.assign({}, firstTokenInfo))
  979. }
  980. },
  981. // ----------------------------------------------------------------------
  982. // SINGLE TOKEN NODES
  983. // ----------------------------------------------------------------------
  984. VIdentifier() {},
  985. VLiteral() {},
  986. // ----------------------------------------------------------------------
  987. // WRAPPER NODES
  988. // ----------------------------------------------------------------------
  989. VDirectiveKey() {},
  990. VSlotScopeExpression() {},
  991. // ----------------------------------------------------------------------
  992. // ES NODES
  993. // ----------------------------------------------------------------------
  994. /** @param {ArrayExpression | ArrayPattern} node */
  995. 'ArrayExpression, ArrayPattern'(node) {
  996. const firstToken = tokenStore.getFirstToken(node)
  997. const rightToken = tokenStore.getTokenAfter(
  998. node.elements[node.elements.length - 1] || firstToken,
  999. isClosingBracketToken
  1000. )
  1001. processNodeList(node.elements, firstToken, rightToken, 1)
  1002. },
  1003. /** @param {ArrowFunctionExpression} node */
  1004. ArrowFunctionExpression(node) {
  1005. const firstToken = tokenStore.getFirstToken(node)
  1006. const secondToken = tokenStore.getTokenAfter(firstToken)
  1007. const leftToken = node.async ? secondToken : firstToken
  1008. const arrowToken = tokenStore.getTokenBefore(node.body, isArrowToken)
  1009. if (node.async) {
  1010. setOffset(secondToken, 1, firstToken)
  1011. }
  1012. if (isOpeningParenToken(leftToken)) {
  1013. const rightToken = tokenStore.getTokenAfter(
  1014. last(node.params) || leftToken,
  1015. isClosingParenToken
  1016. )
  1017. processNodeList(node.params, leftToken, rightToken, 1)
  1018. }
  1019. setOffset(arrowToken, 1, firstToken)
  1020. processMaybeBlock(node.body, firstToken)
  1021. },
  1022. /** @param {AssignmentExpression | AssignmentPattern | BinaryExpression | LogicalExpression} node */
  1023. 'AssignmentExpression, AssignmentPattern, BinaryExpression, LogicalExpression'(
  1024. node
  1025. ) {
  1026. const leftToken = getChainHeadToken(node)
  1027. const opToken = /** @type {Token} */ (
  1028. tokenStore.getTokenAfter(node.left, isNotClosingParenToken)
  1029. )
  1030. const rightToken = tokenStore.getTokenAfter(opToken)
  1031. const prevToken = tokenStore.getTokenBefore(leftToken)
  1032. const shouldIndent =
  1033. prevToken == null ||
  1034. prevToken.loc.end.line === leftToken.loc.start.line ||
  1035. isBeginningOfElement(leftToken, node)
  1036. setOffset([opToken, rightToken], shouldIndent ? 1 : 0, leftToken)
  1037. },
  1038. /** @param {AwaitExpression | RestElement | SpreadElement | UnaryExpression} node */
  1039. 'AwaitExpression, RestElement, SpreadElement, UnaryExpression'(node) {
  1040. const firstToken = tokenStore.getFirstToken(node)
  1041. const nextToken = tokenStore.getTokenAfter(firstToken)
  1042. setOffset(nextToken, 1, firstToken)
  1043. },
  1044. /** @param {BlockStatement | ClassBody} node */
  1045. 'BlockStatement, ClassBody'(node) {
  1046. processNodeList(
  1047. node.body,
  1048. tokenStore.getFirstToken(node),
  1049. tokenStore.getLastToken(node),
  1050. 1
  1051. )
  1052. },
  1053. StaticBlock(node) {
  1054. const firstToken = tokenStore.getFirstToken(node)
  1055. let next = tokenStore.getTokenAfter(firstToken)
  1056. while (next && isNotOpeningBraceToken(next)) {
  1057. setOffset(next, 0, firstToken)
  1058. next = tokenStore.getTokenAfter(next)
  1059. }
  1060. setOffset(next, 0, firstToken)
  1061. processNodeList(node.body, next, tokenStore.getLastToken(node), 1)
  1062. },
  1063. /** @param {BreakStatement | ContinueStatement | ReturnStatement | ThrowStatement} node */
  1064. 'BreakStatement, ContinueStatement, ReturnStatement, ThrowStatement'(node) {
  1065. if (
  1066. ((node.type === 'ReturnStatement' || node.type === 'ThrowStatement') &&
  1067. node.argument != null) ||
  1068. ((node.type === 'BreakStatement' ||
  1069. node.type === 'ContinueStatement') &&
  1070. node.label != null)
  1071. ) {
  1072. const firstToken = tokenStore.getFirstToken(node)
  1073. const nextToken = tokenStore.getTokenAfter(firstToken)
  1074. setOffset(nextToken, 1, firstToken)
  1075. }
  1076. },
  1077. /** @param {CallExpression} node */
  1078. CallExpression(node) {
  1079. const firstToken = tokenStore.getFirstToken(node)
  1080. const rightToken = tokenStore.getLastToken(node)
  1081. const leftToken = /** @type {Token} */ (
  1082. tokenStore.getTokenAfter(
  1083. node.typeParameters || node.callee,
  1084. isOpeningParenToken
  1085. )
  1086. )
  1087. if (node.typeParameters) {
  1088. setOffset(tokenStore.getFirstToken(node.typeParameters), 1, firstToken)
  1089. }
  1090. for (const optionalToken of tokenStore.getTokensBetween(
  1091. tokenStore.getLastToken(node.typeParameters || node.callee),
  1092. leftToken,
  1093. isOptionalToken
  1094. )) {
  1095. setOffset(optionalToken, 1, firstToken)
  1096. }
  1097. setOffset(leftToken, 1, firstToken)
  1098. processNodeList(node.arguments, leftToken, rightToken, 1)
  1099. },
  1100. /** @param {ImportExpression} node */
  1101. ImportExpression(node) {
  1102. const firstToken = tokenStore.getFirstToken(node)
  1103. const rightToken = tokenStore.getLastToken(node)
  1104. const leftToken = tokenStore.getTokenAfter(
  1105. firstToken,
  1106. isOpeningParenToken
  1107. )
  1108. setOffset(leftToken, 1, firstToken)
  1109. processNodeList([node.source], leftToken, rightToken, 1)
  1110. },
  1111. /** @param {CatchClause} node */
  1112. CatchClause(node) {
  1113. const firstToken = tokenStore.getFirstToken(node)
  1114. const bodyToken = tokenStore.getFirstToken(node.body)
  1115. if (node.param != null) {
  1116. const leftToken = tokenStore.getTokenAfter(firstToken)
  1117. const rightToken = tokenStore.getTokenAfter(node.param)
  1118. setOffset(leftToken, 1, firstToken)
  1119. processNodeList([node.param], leftToken, rightToken, 1)
  1120. }
  1121. setOffset(bodyToken, 0, firstToken)
  1122. },
  1123. /** @param {ClassDeclaration | ClassExpression} node */
  1124. 'ClassDeclaration, ClassExpression'(node) {
  1125. const firstToken = tokenStore.getFirstToken(node)
  1126. const bodyToken = tokenStore.getFirstToken(node.body)
  1127. if (node.id != null) {
  1128. setOffset(tokenStore.getFirstToken(node.id), 1, firstToken)
  1129. }
  1130. if (node.superClass != null) {
  1131. const extendsToken = /** @type {Token} */ (
  1132. tokenStore.getTokenBefore(node.superClass, isExtendsKeyword)
  1133. )
  1134. const superClassToken = tokenStore.getTokenAfter(extendsToken)
  1135. setOffset(extendsToken, 1, firstToken)
  1136. setOffset(superClassToken, 1, extendsToken)
  1137. }
  1138. setOffset(bodyToken, 0, firstToken)
  1139. },
  1140. /** @param {ConditionalExpression} node */
  1141. ConditionalExpression(node) {
  1142. const prevToken = tokenStore.getTokenBefore(node)
  1143. const firstToken = tokenStore.getFirstToken(node)
  1144. const questionToken = /** @type {Token} */ (
  1145. tokenStore.getTokenAfter(node.test, isNotClosingParenToken)
  1146. )
  1147. const consequentToken = tokenStore.getTokenAfter(questionToken)
  1148. const colonToken = /** @type {Token} */ (
  1149. tokenStore.getTokenAfter(node.consequent, isNotClosingParenToken)
  1150. )
  1151. const alternateToken = tokenStore.getTokenAfter(colonToken)
  1152. const isFlat =
  1153. prevToken &&
  1154. prevToken.loc.end.line !== node.loc.start.line &&
  1155. node.test.loc.end.line === node.consequent.loc.start.line
  1156. if (isFlat) {
  1157. setOffset(
  1158. [questionToken, consequentToken, colonToken, alternateToken],
  1159. 0,
  1160. firstToken
  1161. )
  1162. } else {
  1163. setOffset([questionToken, colonToken], 1, firstToken)
  1164. setOffset([consequentToken, alternateToken], 1, questionToken)
  1165. }
  1166. },
  1167. /** @param {DoWhileStatement} node */
  1168. DoWhileStatement(node) {
  1169. const doToken = tokenStore.getFirstToken(node)
  1170. const whileToken = /** @type {Token} */ (
  1171. tokenStore.getTokenAfter(node.body, isNotClosingParenToken)
  1172. )
  1173. const leftToken = tokenStore.getTokenAfter(whileToken)
  1174. const testToken = tokenStore.getTokenAfter(leftToken)
  1175. const lastToken = tokenStore.getLastToken(node)
  1176. const rightToken = isSemicolonToken(lastToken)
  1177. ? tokenStore.getTokenBefore(lastToken)
  1178. : lastToken
  1179. processMaybeBlock(node.body, doToken)
  1180. setOffset(whileToken, 0, doToken)
  1181. setOffset(leftToken, 1, whileToken)
  1182. setOffset(testToken, 1, leftToken)
  1183. setOffset(rightToken, 0, leftToken)
  1184. },
  1185. /** @param {ExportAllDeclaration} node */
  1186. ExportAllDeclaration(node) {
  1187. const exportToken = tokenStore.getFirstToken(node)
  1188. const tokens = [
  1189. ...tokenStore.getTokensBetween(exportToken, node.source),
  1190. tokenStore.getFirstToken(node.source)
  1191. ]
  1192. if (!node.exported) {
  1193. setOffset(tokens, 1, exportToken)
  1194. } else {
  1195. // export * as foo from "mod"
  1196. const starToken = /** @type {Token} */ (tokens.find(isWildcard))
  1197. const asToken = tokenStore.getTokenAfter(starToken)
  1198. const exportedToken = tokenStore.getTokenAfter(asToken)
  1199. const afterTokens = tokens.slice(tokens.indexOf(exportedToken) + 1)
  1200. setOffset(starToken, 1, exportToken)
  1201. setOffset(asToken, 1, starToken)
  1202. setOffset(exportedToken, 1, starToken)
  1203. setOffset(afterTokens, 1, exportToken)
  1204. }
  1205. // assertions
  1206. const lastToken = /** @type {Token} */ (
  1207. tokenStore.getLastToken(node, isNotSemicolonToken)
  1208. )
  1209. const assertionTokens = tokenStore.getTokensBetween(
  1210. node.source,
  1211. lastToken
  1212. )
  1213. if (assertionTokens.length > 0) {
  1214. const assertToken = /** @type {Token} */ (assertionTokens.shift())
  1215. setOffset(assertToken, 0, exportToken)
  1216. const assertionOpen = assertionTokens.shift()
  1217. if (assertionOpen) {
  1218. setOffset(assertionOpen, 1, assertToken)
  1219. processNodeList(assertionTokens, assertionOpen, lastToken, 1)
  1220. }
  1221. }
  1222. },
  1223. /** @param {ExportDefaultDeclaration} node */
  1224. ExportDefaultDeclaration(node) {
  1225. const exportToken = tokenStore.getFirstToken(node)
  1226. const defaultToken = tokenStore.getFirstToken(node, 1)
  1227. const declarationToken = getFirstAndLastTokens(
  1228. node.declaration
  1229. ).firstToken
  1230. setOffset([defaultToken, declarationToken], 1, exportToken)
  1231. },
  1232. /** @param {ExportNamedDeclaration} node */
  1233. ExportNamedDeclaration(node) {
  1234. const exportToken = tokenStore.getFirstToken(node)
  1235. if (node.declaration) {
  1236. // export var foo = 1;
  1237. const declarationToken = tokenStore.getFirstToken(node, 1)
  1238. setOffset(declarationToken, 1, exportToken)
  1239. } else {
  1240. const firstSpecifier = node.specifiers[0]
  1241. if (!firstSpecifier || firstSpecifier.type === 'ExportSpecifier') {
  1242. // export {foo, bar}; or export {foo, bar} from "mod";
  1243. const leftBraceTokens = firstSpecifier
  1244. ? tokenStore.getTokensBetween(exportToken, firstSpecifier)
  1245. : [tokenStore.getTokenAfter(exportToken)]
  1246. const rightBraceToken = /** @type {Token} */ (
  1247. node.source
  1248. ? tokenStore.getTokenBefore(node.source, isClosingBraceToken)
  1249. : tokenStore.getLastToken(node, isClosingBraceToken)
  1250. )
  1251. setOffset(leftBraceTokens, 0, exportToken)
  1252. processNodeList(
  1253. node.specifiers,
  1254. /** @type {Token} */ (last(leftBraceTokens)),
  1255. rightBraceToken,
  1256. 1
  1257. )
  1258. if (node.source) {
  1259. const tokens = tokenStore.getTokensBetween(
  1260. rightBraceToken,
  1261. node.source
  1262. )
  1263. setOffset(
  1264. [...tokens, sourceCode.getFirstToken(node.source)],
  1265. 1,
  1266. exportToken
  1267. )
  1268. // assertions
  1269. const lastToken = /** @type {Token} */ (
  1270. tokenStore.getLastToken(node, isNotSemicolonToken)
  1271. )
  1272. const assertionTokens = tokenStore.getTokensBetween(
  1273. node.source,
  1274. lastToken
  1275. )
  1276. if (assertionTokens.length > 0) {
  1277. const assertToken = /** @type {Token} */ (assertionTokens.shift())
  1278. setOffset(assertToken, 0, exportToken)
  1279. const assertionOpen = assertionTokens.shift()
  1280. if (assertionOpen) {
  1281. setOffset(assertionOpen, 1, assertToken)
  1282. processNodeList(assertionTokens, assertionOpen, lastToken, 1)
  1283. }
  1284. }
  1285. }
  1286. } else {
  1287. // maybe babel parser
  1288. }
  1289. }
  1290. },
  1291. /** @param {ExportSpecifier | ImportSpecifier} node */
  1292. 'ExportSpecifier, ImportSpecifier'(node) {
  1293. const tokens = tokenStore.getTokens(node)
  1294. let firstToken = /** @type {Token} */ (tokens.shift())
  1295. if (firstToken.value === 'type') {
  1296. const typeToken = firstToken
  1297. firstToken = /** @type {Token} */ (tokens.shift())
  1298. setOffset(firstToken, 0, typeToken)
  1299. }
  1300. setOffset(tokens, 1, firstToken)
  1301. },
  1302. /** @param {ForInStatement | ForOfStatement} node */
  1303. 'ForInStatement, ForOfStatement'(node) {
  1304. const forToken = tokenStore.getFirstToken(node)
  1305. const awaitToken =
  1306. (node.type === 'ForOfStatement' &&
  1307. node.await &&
  1308. tokenStore.getTokenAfter(forToken)) ||
  1309. null
  1310. const leftParenToken = tokenStore.getTokenAfter(awaitToken || forToken)
  1311. const leftToken = tokenStore.getTokenAfter(leftParenToken)
  1312. const inToken = /** @type {Token} */ (
  1313. tokenStore.getTokenAfter(leftToken, isNotClosingParenToken)
  1314. )
  1315. const rightToken = tokenStore.getTokenAfter(inToken)
  1316. const rightParenToken = tokenStore.getTokenBefore(
  1317. node.body,
  1318. isNotOpeningParenToken
  1319. )
  1320. if (awaitToken != null) {
  1321. setOffset(awaitToken, 0, forToken)
  1322. }
  1323. setOffset(leftParenToken, 1, forToken)
  1324. setOffset(leftToken, 1, leftParenToken)
  1325. setOffset(inToken, 1, leftToken)
  1326. setOffset(rightToken, 1, leftToken)
  1327. setOffset(rightParenToken, 0, leftParenToken)
  1328. processMaybeBlock(node.body, forToken)
  1329. },
  1330. /** @param {ForStatement} node */
  1331. ForStatement(node) {
  1332. const forToken = tokenStore.getFirstToken(node)
  1333. const leftParenToken = tokenStore.getTokenAfter(forToken)
  1334. const rightParenToken = tokenStore.getTokenBefore(
  1335. node.body,
  1336. isNotOpeningParenToken
  1337. )
  1338. setOffset(leftParenToken, 1, forToken)
  1339. processNodeList(
  1340. [node.init, node.test, node.update],
  1341. leftParenToken,
  1342. rightParenToken,
  1343. 1
  1344. )
  1345. processMaybeBlock(node.body, forToken)
  1346. },
  1347. /** @param {FunctionDeclaration | FunctionExpression} node */
  1348. 'FunctionDeclaration, FunctionExpression'(node) {
  1349. const firstToken = tokenStore.getFirstToken(node)
  1350. let leftParenToken, bodyBaseToken
  1351. if (isOpeningParenToken(firstToken)) {
  1352. // Methods.
  1353. leftParenToken = firstToken
  1354. bodyBaseToken = tokenStore.getFirstToken(node.parent)
  1355. } else {
  1356. // Normal functions.
  1357. let nextToken = tokenStore.getTokenAfter(firstToken)
  1358. let nextTokenOffset = 0
  1359. while (
  1360. nextToken &&
  1361. !isOpeningParenToken(nextToken) &&
  1362. nextToken.value !== '<'
  1363. ) {
  1364. if (
  1365. nextToken.value === '*' ||
  1366. (node.id && nextToken.range[0] === node.id.range[0])
  1367. ) {
  1368. nextTokenOffset = 1
  1369. }
  1370. setOffset(nextToken, nextTokenOffset, firstToken)
  1371. nextToken = tokenStore.getTokenAfter(nextToken)
  1372. }
  1373. leftParenToken = nextToken
  1374. bodyBaseToken = firstToken
  1375. }
  1376. if (
  1377. !isOpeningParenToken(leftParenToken) &&
  1378. /** @type {any} */ (node).typeParameters
  1379. ) {
  1380. leftParenToken = tokenStore.getTokenAfter(
  1381. /** @type {any} */ (node).typeParameters
  1382. )
  1383. }
  1384. const rightParenToken = tokenStore.getTokenAfter(
  1385. node.params[node.params.length - 1] || leftParenToken,
  1386. isClosingParenToken
  1387. )
  1388. setOffset(leftParenToken, 1, bodyBaseToken)
  1389. processNodeList(node.params, leftParenToken, rightParenToken, 1)
  1390. const bodyToken = tokenStore.getFirstToken(node.body)
  1391. setOffset(bodyToken, 0, bodyBaseToken)
  1392. },
  1393. /** @param {IfStatement} node */
  1394. IfStatement(node) {
  1395. const ifToken = tokenStore.getFirstToken(node)
  1396. const ifLeftParenToken = tokenStore.getTokenAfter(ifToken)
  1397. const ifRightParenToken = tokenStore.getTokenBefore(
  1398. node.consequent,
  1399. isClosingParenToken
  1400. )
  1401. setOffset(ifLeftParenToken, 1, ifToken)
  1402. setOffset(ifRightParenToken, 0, ifLeftParenToken)
  1403. processMaybeBlock(node.consequent, ifToken)
  1404. if (node.alternate != null) {
  1405. const elseToken = /** @type {Token} */ (
  1406. tokenStore.getTokenAfter(node.consequent, isNotClosingParenToken)
  1407. )
  1408. setOffset(elseToken, 0, ifToken)
  1409. processMaybeBlock(node.alternate, elseToken)
  1410. }
  1411. },
  1412. /** @param {ImportDeclaration} node */
  1413. ImportDeclaration(node) {
  1414. const importToken = tokenStore.getFirstToken(node)
  1415. const tokens = tokenStore.getTokensBetween(importToken, node.source)
  1416. const fromIndex = tokens.map((t) => t.value).lastIndexOf('from')
  1417. const { fromToken, beforeTokens, afterTokens } =
  1418. fromIndex >= 0
  1419. ? {
  1420. fromToken: tokens[fromIndex],
  1421. beforeTokens: tokens.slice(0, fromIndex),
  1422. afterTokens: [
  1423. ...tokens.slice(fromIndex + 1),
  1424. tokenStore.getFirstToken(node.source)
  1425. ]
  1426. }
  1427. : {
  1428. fromToken: null,
  1429. beforeTokens: [...tokens, tokenStore.getFirstToken(node.source)],
  1430. afterTokens: []
  1431. }
  1432. /** @type {ImportSpecifier[]} */
  1433. const namedSpecifiers = []
  1434. for (const specifier of node.specifiers) {
  1435. if (specifier.type === 'ImportSpecifier') {
  1436. namedSpecifiers.push(specifier)
  1437. } else {
  1438. const removeTokens = tokenStore.getTokens(specifier)
  1439. removeTokens.shift()
  1440. for (const token of removeTokens) {
  1441. const i = beforeTokens.indexOf(token)
  1442. if (i >= 0) {
  1443. beforeTokens.splice(i, 1)
  1444. }
  1445. }
  1446. }
  1447. }
  1448. if (namedSpecifiers.length > 0) {
  1449. const leftBrace = tokenStore.getTokenBefore(namedSpecifiers[0])
  1450. const rightBrace = /** @type {Token} */ (
  1451. tokenStore.getTokenAfter(
  1452. namedSpecifiers[namedSpecifiers.length - 1],
  1453. isClosingBraceToken
  1454. )
  1455. )
  1456. processNodeList(namedSpecifiers, leftBrace, rightBrace, 1)
  1457. for (const token of [
  1458. ...tokenStore.getTokensBetween(leftBrace, rightBrace),
  1459. rightBrace
  1460. ]) {
  1461. const i = beforeTokens.indexOf(token)
  1462. if (i >= 0) {
  1463. beforeTokens.splice(i, 1)
  1464. }
  1465. }
  1466. }
  1467. if (
  1468. beforeTokens.every(
  1469. (t) => isOpeningBraceToken(t) || isClosingBraceToken(t)
  1470. )
  1471. ) {
  1472. setOffset(beforeTokens, 0, importToken)
  1473. } else {
  1474. setOffset(beforeTokens, 1, importToken)
  1475. }
  1476. if (fromToken) {
  1477. setOffset(fromToken, 1, importToken)
  1478. setOffset(afterTokens, 0, fromToken)
  1479. }
  1480. // assertions
  1481. const lastToken = /** @type {Token} */ (
  1482. tokenStore.getLastToken(node, isNotSemicolonToken)
  1483. )
  1484. const assertionTokens = tokenStore.getTokensBetween(
  1485. node.source,
  1486. lastToken
  1487. )
  1488. if (assertionTokens.length > 0) {
  1489. const assertToken = /** @type {Token} */ (assertionTokens.shift())
  1490. setOffset(assertToken, 0, importToken)
  1491. const assertionOpen = assertionTokens.shift()
  1492. if (assertionOpen) {
  1493. setOffset(assertionOpen, 1, assertToken)
  1494. processNodeList(assertionTokens, assertionOpen, lastToken, 1)
  1495. }
  1496. }
  1497. },
  1498. /** @param {ImportNamespaceSpecifier} node */
  1499. ImportNamespaceSpecifier(node) {
  1500. const tokens = tokenStore.getTokens(node)
  1501. const firstToken = /** @type {Token} */ (tokens.shift())
  1502. setOffset(tokens, 1, firstToken)
  1503. },
  1504. /** @param {LabeledStatement} node */
  1505. LabeledStatement(node) {
  1506. const labelToken = tokenStore.getFirstToken(node)
  1507. const colonToken = tokenStore.getTokenAfter(labelToken)
  1508. const bodyToken = tokenStore.getTokenAfter(colonToken)
  1509. setOffset([colonToken, bodyToken], 1, labelToken)
  1510. },
  1511. /** @param {MemberExpression | MetaProperty} node */
  1512. 'MemberExpression, MetaProperty'(node) {
  1513. const objectToken = tokenStore.getFirstToken(node)
  1514. if (node.type === 'MemberExpression' && node.computed) {
  1515. const leftBracketToken = /** @type {Token} */ (
  1516. tokenStore.getTokenBefore(node.property, isOpeningBracketToken)
  1517. )
  1518. const propertyToken = tokenStore.getTokenAfter(leftBracketToken)
  1519. const rightBracketToken = tokenStore.getTokenAfter(
  1520. node.property,
  1521. isClosingBracketToken
  1522. )
  1523. for (const optionalToken of tokenStore.getTokensBetween(
  1524. tokenStore.getLastToken(node.object),
  1525. leftBracketToken,
  1526. isOptionalToken
  1527. )) {
  1528. setOffset(optionalToken, 1, objectToken)
  1529. }
  1530. setOffset(leftBracketToken, 1, objectToken)
  1531. setOffset(propertyToken, 1, leftBracketToken)
  1532. setOffset(rightBracketToken, 0, leftBracketToken)
  1533. } else {
  1534. const dotToken = tokenStore.getTokenBefore(node.property)
  1535. const propertyToken = tokenStore.getTokenAfter(dotToken)
  1536. setOffset([dotToken, propertyToken], 1, objectToken)
  1537. }
  1538. },
  1539. /** @param {MethodDefinition | Property | PropertyDefinition} node */
  1540. 'MethodDefinition, Property, PropertyDefinition'(node) {
  1541. const firstToken = tokenStore.getFirstToken(node)
  1542. const keyTokens = getFirstAndLastTokens(node.key)
  1543. const prefixTokens = tokenStore.getTokensBetween(
  1544. firstToken,
  1545. keyTokens.firstToken
  1546. )
  1547. if (node.computed) {
  1548. prefixTokens.pop() // pop [
  1549. }
  1550. setOffset(prefixTokens, 0, firstToken)
  1551. let lastKeyToken
  1552. if (node.computed) {
  1553. const leftBracketToken = tokenStore.getTokenBefore(keyTokens.firstToken)
  1554. const rightBracketToken = (lastKeyToken = tokenStore.getTokenAfter(
  1555. keyTokens.lastToken
  1556. ))
  1557. setOffset(leftBracketToken, 0, firstToken)
  1558. processNodeList([node.key], leftBracketToken, rightBracketToken, 1)
  1559. } else {
  1560. setOffset(keyTokens.firstToken, 0, firstToken)
  1561. lastKeyToken = keyTokens.lastToken
  1562. }
  1563. if (node.value != null) {
  1564. const initToken = tokenStore.getFirstToken(node.value)
  1565. setOffset(
  1566. [...tokenStore.getTokensBetween(lastKeyToken, initToken), initToken],
  1567. 1,
  1568. lastKeyToken
  1569. )
  1570. }
  1571. },
  1572. /** @param {NewExpression} node */
  1573. NewExpression(node) {
  1574. const newToken = tokenStore.getFirstToken(node)
  1575. const calleeToken = tokenStore.getTokenAfter(newToken)
  1576. const rightToken = tokenStore.getLastToken(node)
  1577. const leftToken = isClosingParenToken(rightToken)
  1578. ? tokenStore.getFirstTokenBetween(
  1579. node.typeParameters || node.callee,
  1580. rightToken,
  1581. isOpeningParenToken
  1582. )
  1583. : null
  1584. if (node.typeParameters) {
  1585. setOffset(tokenStore.getFirstToken(node.typeParameters), 1, calleeToken)
  1586. }
  1587. setOffset(calleeToken, 1, newToken)
  1588. if (leftToken != null) {
  1589. setOffset(leftToken, 1, calleeToken)
  1590. processNodeList(node.arguments, leftToken, rightToken, 1)
  1591. }
  1592. },
  1593. /** @param {ObjectExpression | ObjectPattern} node */
  1594. 'ObjectExpression, ObjectPattern'(node) {
  1595. const firstToken = tokenStore.getFirstToken(node)
  1596. const rightToken = tokenStore.getTokenAfter(
  1597. node.properties[node.properties.length - 1] || firstToken,
  1598. isClosingBraceToken
  1599. )
  1600. processNodeList(node.properties, firstToken, rightToken, 1)
  1601. },
  1602. /** @param {SequenceExpression} node */
  1603. SequenceExpression(node) {
  1604. processNodeList(node.expressions, null, null, 0)
  1605. },
  1606. /** @param {SwitchCase} node */
  1607. SwitchCase(node) {
  1608. const caseToken = tokenStore.getFirstToken(node)
  1609. if (node.test != null) {
  1610. const testToken = tokenStore.getTokenAfter(caseToken)
  1611. const colonToken = tokenStore.getTokenAfter(
  1612. node.test,
  1613. isNotClosingParenToken
  1614. )
  1615. setOffset([testToken, colonToken], 1, caseToken)
  1616. } else {
  1617. const colonToken = tokenStore.getTokenAfter(caseToken)
  1618. setOffset(colonToken, 1, caseToken)
  1619. }
  1620. if (
  1621. node.consequent.length === 1 &&
  1622. node.consequent[0].type === 'BlockStatement'
  1623. ) {
  1624. setOffset(tokenStore.getFirstToken(node.consequent[0]), 0, caseToken)
  1625. } else if (node.consequent.length > 0) {
  1626. setOffset(tokenStore.getFirstToken(node.consequent[0]), 1, caseToken)
  1627. processNodeList(node.consequent, null, null, 0)
  1628. }
  1629. },
  1630. /** @param {SwitchStatement} node */
  1631. SwitchStatement(node) {
  1632. const switchToken = tokenStore.getFirstToken(node)
  1633. const leftParenToken = tokenStore.getTokenAfter(switchToken)
  1634. const discriminantToken = tokenStore.getTokenAfter(leftParenToken)
  1635. const leftBraceToken = /** @type {Token} */ (
  1636. tokenStore.getTokenAfter(node.discriminant, isOpeningBraceToken)
  1637. )
  1638. const rightParenToken = tokenStore.getTokenBefore(leftBraceToken)
  1639. const rightBraceToken = tokenStore.getLastToken(node)
  1640. setOffset(leftParenToken, 1, switchToken)
  1641. setOffset(discriminantToken, 1, leftParenToken)
  1642. setOffset(rightParenToken, 0, leftParenToken)
  1643. setOffset(leftBraceToken, 0, switchToken)
  1644. processNodeList(
  1645. node.cases,
  1646. leftBraceToken,
  1647. rightBraceToken,
  1648. options.switchCase
  1649. )
  1650. },
  1651. /** @param {TaggedTemplateExpression} node */
  1652. TaggedTemplateExpression(node) {
  1653. const tagTokens = getFirstAndLastTokens(node.tag, node.range[0])
  1654. const quasiToken = tokenStore.getTokenAfter(tagTokens.lastToken)
  1655. setOffset(quasiToken, 1, tagTokens.firstToken)
  1656. },
  1657. /** @param {TemplateLiteral} node */
  1658. TemplateLiteral(node) {
  1659. const firstToken = tokenStore.getFirstToken(node)
  1660. const quasiTokens = node.quasis
  1661. .slice(1)
  1662. .map((n) => tokenStore.getFirstToken(n))
  1663. const expressionToken = node.quasis
  1664. .slice(0, -1)
  1665. .map((n) => tokenStore.getTokenAfter(n))
  1666. setOffset(quasiTokens, 0, firstToken)
  1667. setOffset(expressionToken, 1, firstToken)
  1668. },
  1669. /** @param {TryStatement} node */
  1670. TryStatement(node) {
  1671. const tryToken = tokenStore.getFirstToken(node)
  1672. const tryBlockToken = tokenStore.getFirstToken(node.block)
  1673. setOffset(tryBlockToken, 0, tryToken)
  1674. if (node.handler != null) {
  1675. const catchToken = tokenStore.getFirstToken(node.handler)
  1676. setOffset(catchToken, 0, tryToken)
  1677. }
  1678. if (node.finalizer != null) {
  1679. const finallyToken = tokenStore.getTokenBefore(node.finalizer)
  1680. const finallyBlockToken = tokenStore.getFirstToken(node.finalizer)
  1681. setOffset([finallyToken, finallyBlockToken], 0, tryToken)
  1682. }
  1683. },
  1684. /** @param {UpdateExpression} node */
  1685. UpdateExpression(node) {
  1686. const firstToken = tokenStore.getFirstToken(node)
  1687. const nextToken = tokenStore.getTokenAfter(firstToken)
  1688. setOffset(nextToken, 1, firstToken)
  1689. },
  1690. /** @param {VariableDeclaration} node */
  1691. VariableDeclaration(node) {
  1692. processNodeList(
  1693. node.declarations,
  1694. tokenStore.getFirstToken(node),
  1695. null,
  1696. 1
  1697. )
  1698. },
  1699. /** @param {VariableDeclarator} node */
  1700. VariableDeclarator(node) {
  1701. if (node.init != null) {
  1702. const idToken = tokenStore.getFirstToken(node)
  1703. const eqToken = tokenStore.getTokenAfter(node.id)
  1704. const initToken = tokenStore.getTokenAfter(eqToken)
  1705. setOffset([eqToken, initToken], 1, idToken)
  1706. }
  1707. },
  1708. /** @param {WhileStatement | WithStatement} node */
  1709. 'WhileStatement, WithStatement'(node) {
  1710. const firstToken = tokenStore.getFirstToken(node)
  1711. const leftParenToken = tokenStore.getTokenAfter(firstToken)
  1712. const rightParenToken = tokenStore.getTokenBefore(
  1713. node.body,
  1714. isClosingParenToken
  1715. )
  1716. setOffset(leftParenToken, 1, firstToken)
  1717. setOffset(rightParenToken, 0, leftParenToken)
  1718. processMaybeBlock(node.body, firstToken)
  1719. },
  1720. /** @param {YieldExpression} node */
  1721. YieldExpression(node) {
  1722. if (node.argument != null) {
  1723. const yieldToken = tokenStore.getFirstToken(node)
  1724. setOffset(tokenStore.getTokenAfter(yieldToken), 1, yieldToken)
  1725. if (node.delegate) {
  1726. setOffset(tokenStore.getTokenAfter(yieldToken, 1), 1, yieldToken)
  1727. }
  1728. }
  1729. },
  1730. // ----------------------------------------------------------------------
  1731. // SINGLE TOKEN NODES
  1732. // ----------------------------------------------------------------------
  1733. DebuggerStatement() {},
  1734. Identifier() {},
  1735. ImportDefaultSpecifier() {},
  1736. Literal() {},
  1737. PrivateIdentifier() {},
  1738. Super() {},
  1739. TemplateElement() {},
  1740. ThisExpression() {},
  1741. // ----------------------------------------------------------------------
  1742. // WRAPPER NODES
  1743. // ----------------------------------------------------------------------
  1744. ExpressionStatement() {},
  1745. ChainExpression() {},
  1746. EmptyStatement() {},
  1747. // ----------------------------------------------------------------------
  1748. // COMMONS
  1749. // ----------------------------------------------------------------------
  1750. /** @param {Statement} node */
  1751. // Process semicolons.
  1752. ':statement, PropertyDefinition'(node) {
  1753. processSemicolons(node)
  1754. },
  1755. /** @param {Expression | MetaProperty | TemplateLiteral} node */
  1756. // Process parentheses.
  1757. // `:expression` does not match with MetaProperty and TemplateLiteral as a bug: https://github.com/estools/esquery/pull/59
  1758. ':expression'(node) {
  1759. let leftToken = tokenStore.getTokenBefore(node)
  1760. let rightToken = tokenStore.getTokenAfter(node)
  1761. let firstToken = tokenStore.getFirstToken(node)
  1762. while (
  1763. leftToken &&
  1764. rightToken &&
  1765. isOpeningParenToken(leftToken) &&
  1766. isClosingParenToken(rightToken)
  1767. ) {
  1768. setOffset(firstToken, 1, leftToken)
  1769. setOffset(rightToken, 0, leftToken)
  1770. firstToken = leftToken
  1771. leftToken = tokenStore.getTokenBefore(leftToken)
  1772. rightToken = tokenStore.getTokenAfter(rightToken)
  1773. }
  1774. },
  1775. .../** @type {TemplateListener} */ (
  1776. tsDefineVisitor({
  1777. processNodeList,
  1778. tokenStore,
  1779. setOffset,
  1780. copyOffset,
  1781. processSemicolons,
  1782. getFirstAndLastTokens
  1783. })
  1784. ),
  1785. /** @param {ASTNode} node */
  1786. // Ignore tokens of unknown nodes.
  1787. '*:exit'(node) {
  1788. if (!knownNodes.has(node.type)) {
  1789. ignore(node)
  1790. }
  1791. },
  1792. /** @param {Program} node */
  1793. // Top-level process.
  1794. Program(node) {
  1795. const firstToken = node.tokens[0]
  1796. const isScriptTag =
  1797. firstToken != null &&
  1798. firstToken.type === 'Punctuator' &&
  1799. firstToken.value === '<script>'
  1800. const baseIndent = isScriptTag
  1801. ? options.indentSize * options.baseIndent
  1802. : 0
  1803. for (const statement of node.body) {
  1804. processTopLevelNode(statement, baseIndent)
  1805. }
  1806. },
  1807. /** @param {VElement} node */
  1808. "VElement[parent.type!='VElement']"(node) {
  1809. processTopLevelNode(node, 0)
  1810. },
  1811. /** @param {Program | VElement} node */
  1812. // Do validation.
  1813. ":matches(Program, VElement[parent.type!='VElement']):exit"(node) {
  1814. let comments = []
  1815. /** @type {Token[]} */
  1816. let tokensOnSameLine = []
  1817. let isBesideMultilineToken = false
  1818. let lastValidatedToken = null
  1819. // Validate indentation of tokens.
  1820. for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) {
  1821. const tokenStartLine = token.loc.start.line
  1822. if (
  1823. tokensOnSameLine.length === 0 ||
  1824. tokensOnSameLine[0].loc.start.line === tokenStartLine
  1825. ) {
  1826. // This is on the same line (or the first token).
  1827. tokensOnSameLine.push(token)
  1828. } else if (tokensOnSameLine.every(isComment)) {
  1829. // New line is detected, but the all tokens of the previous line are comment.
  1830. // Comment lines are adjusted to the next code line.
  1831. comments.push(tokensOnSameLine[0])
  1832. isBesideMultilineToken =
  1833. /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line ===
  1834. tokenStartLine
  1835. tokensOnSameLine = [token]
  1836. } else {
  1837. // New line is detected, so validate the tokens.
  1838. if (!isBesideMultilineToken) {
  1839. validate(tokensOnSameLine, comments, lastValidatedToken)
  1840. lastValidatedToken = tokensOnSameLine[0]
  1841. }
  1842. isBesideMultilineToken =
  1843. /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line ===
  1844. tokenStartLine
  1845. tokensOnSameLine = [token]
  1846. comments = []
  1847. }
  1848. }
  1849. if (tokensOnSameLine.some(isNotComment)) {
  1850. validate(tokensOnSameLine, comments, lastValidatedToken)
  1851. }
  1852. }
  1853. }
  1854. for (const key of Object.keys(visitor)) {
  1855. for (const nodeName of key
  1856. .split(/\s*,\s*/gu)
  1857. .map((s) => s.trim())
  1858. .filter((s) => /[a-z]+/i.test(s))) {
  1859. knownNodes.add(nodeName)
  1860. }
  1861. }
  1862. return processIgnores(visitor)
  1863. }