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