indent-common.js 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945
  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. } = require('eslint-utils')
  22. const {
  23. isComment,
  24. isNotComment,
  25. isWildcard,
  26. isExtendsKeyword,
  27. isNotWhitespace,
  28. isNotEmptyTextNode,
  29. isPipeOperator,
  30. last
  31. } = require('./indent-utils')
  32. const { defineVisitor: tsDefineVisitor } = require('./indent-ts')
  33. /**
  34. * @typedef {import('../../typings/eslint-plugin-vue/util-types/node').HasLocation} HasLocation
  35. * @typedef { { type: string } & HasLocation } MaybeNode
  36. */
  37. // ------------------------------------------------------------------------------
  38. // Helpers
  39. // ------------------------------------------------------------------------------
  40. const LT_CHAR = /[\r\n\u2028\u2029]/
  41. const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g
  42. const BLOCK_COMMENT_PREFIX = /^\s*\*/
  43. const ITERATION_OPTS = Object.freeze({
  44. includeComments: true,
  45. filter: isNotWhitespace
  46. })
  47. const PREFORMATTED_ELEMENT_NAMES = ['pre', 'textarea']
  48. /**
  49. * @typedef {object} IndentOptions
  50. * @property { " " | "\t" } IndentOptions.indentChar
  51. * @property {number} IndentOptions.indentSize
  52. * @property {number} IndentOptions.baseIndent
  53. * @property {number} IndentOptions.attribute
  54. * @property {object} IndentOptions.closeBracket
  55. * @property {number} IndentOptions.closeBracket.startTag
  56. * @property {number} IndentOptions.closeBracket.endTag
  57. * @property {number} IndentOptions.closeBracket.selfClosingTag
  58. * @property {number} IndentOptions.switchCase
  59. * @property {boolean} IndentOptions.alignAttributesVertically
  60. * @property {string[]} IndentOptions.ignores
  61. */
  62. /**
  63. * @typedef {object} IndentUserOptions
  64. * @property { " " | "\t" } [IndentUserOptions.indentChar]
  65. * @property {number} [IndentUserOptions.indentSize]
  66. * @property {number} [IndentUserOptions.baseIndent]
  67. * @property {number} [IndentUserOptions.attribute]
  68. * @property {IndentOptions['closeBracket'] | number} [IndentUserOptions.closeBracket]
  69. * @property {number} [IndentUserOptions.switchCase]
  70. * @property {boolean} [IndentUserOptions.alignAttributesVertically]
  71. * @property {string[]} [IndentUserOptions.ignores]
  72. */
  73. /**
  74. * Normalize options.
  75. * @param {number|"tab"|undefined} type The type of indentation.
  76. * @param {IndentUserOptions} options Other options.
  77. * @param {Partial<IndentOptions>} defaultOptions The default value of options.
  78. * @returns {IndentOptions} Normalized options.
  79. */
  80. function parseOptions(type, options, defaultOptions) {
  81. /** @type {IndentOptions} */
  82. const ret = Object.assign(
  83. {
  84. indentChar: ' ',
  85. indentSize: 2,
  86. baseIndent: 0,
  87. attribute: 1,
  88. closeBracket: {
  89. startTag: 0,
  90. endTag: 0,
  91. selfClosingTag: 0
  92. },
  93. switchCase: 0,
  94. alignAttributesVertically: true,
  95. ignores: []
  96. },
  97. defaultOptions
  98. )
  99. if (Number.isSafeInteger(type)) {
  100. ret.indentSize = Number(type)
  101. } else if (type === 'tab') {
  102. ret.indentChar = '\t'
  103. ret.indentSize = 1
  104. }
  105. if (options.baseIndent != null && Number.isSafeInteger(options.baseIndent)) {
  106. ret.baseIndent = options.baseIndent
  107. }
  108. if (options.attribute != null && Number.isSafeInteger(options.attribute)) {
  109. ret.attribute = options.attribute
  110. }
  111. if (Number.isSafeInteger(options.closeBracket)) {
  112. const num = Number(options.closeBracket)
  113. ret.closeBracket = {
  114. startTag: num,
  115. endTag: num,
  116. selfClosingTag: num
  117. }
  118. } else if (options.closeBracket) {
  119. ret.closeBracket = Object.assign(
  120. {
  121. startTag: 0,
  122. endTag: 0,
  123. selfClosingTag: 0
  124. },
  125. options.closeBracket
  126. )
  127. }
  128. if (options.switchCase != null && Number.isSafeInteger(options.switchCase)) {
  129. ret.switchCase = options.switchCase
  130. }
  131. if (options.alignAttributesVertically != null) {
  132. ret.alignAttributesVertically = options.alignAttributesVertically
  133. }
  134. if (options.ignores != null) {
  135. ret.ignores = options.ignores
  136. }
  137. return ret
  138. }
  139. /**
  140. * Check whether the node is at the beginning of line.
  141. * @param {MaybeNode|null} node The node to check.
  142. * @param {number} index The index of the node in the nodes.
  143. * @param {(MaybeNode|null)[]} nodes The array of nodes.
  144. * @returns {boolean} `true` if the node is at the beginning of line.
  145. */
  146. function isBeginningOfLine(node, index, nodes) {
  147. if (node != null) {
  148. for (let i = index - 1; i >= 0; --i) {
  149. const prevNode = nodes[i]
  150. if (prevNode == null) {
  151. continue
  152. }
  153. return node.loc.start.line !== prevNode.loc.end.line
  154. }
  155. }
  156. return false
  157. }
  158. /**
  159. * Check whether a given token is a closing token which triggers unindent.
  160. * @param {Token} token The token to check.
  161. * @returns {boolean} `true` if the token is a closing token.
  162. */
  163. function isClosingToken(token) {
  164. return (
  165. token != null &&
  166. (token.type === 'HTMLEndTagOpen' ||
  167. token.type === 'VExpressionEnd' ||
  168. (token.type === 'Punctuator' &&
  169. (token.value === ')' || token.value === '}' || token.value === ']')))
  170. )
  171. }
  172. /**
  173. * Checks whether a given token is a optional token.
  174. * @param {Token} token The token to check.
  175. * @returns {boolean} `true` if the token is a optional token.
  176. */
  177. function isOptionalToken(token) {
  178. return token.type === 'Punctuator' && token.value === '?.'
  179. }
  180. /**
  181. * Creates AST event handlers for html-indent.
  182. *
  183. * @param {RuleContext} context The rule context.
  184. * @param {ParserServices.TokenStore | SourceCode} tokenStore The token store object to get tokens.
  185. * @param {Partial<IndentOptions>} defaultOptions The default value of options.
  186. * @returns {NodeListener} AST event handlers.
  187. */
  188. module.exports.defineVisitor = function create(
  189. context,
  190. tokenStore,
  191. defaultOptions
  192. ) {
  193. if (!context.getFilename().endsWith('.vue')) return {}
  194. const options = parseOptions(
  195. context.options[0],
  196. context.options[1] || {},
  197. defaultOptions
  198. )
  199. const sourceCode = context.getSourceCode()
  200. /**
  201. * @typedef { { baseToken: Token | null, offset: number, baseline: boolean, expectedIndent: number | undefined } } OffsetData
  202. */
  203. /** @type {Map<Token|null, OffsetData>} */
  204. const offsets = new Map()
  205. const ignoreTokens = new Set()
  206. /**
  207. * Set offset to the given tokens.
  208. * @param {Token|Token[]|null|(Token|null)[]} token The token to set.
  209. * @param {number} offset The offset of the tokens.
  210. * @param {Token} baseToken The token of the base offset.
  211. * @returns {void}
  212. */
  213. function setOffset(token, offset, baseToken) {
  214. if (!token || token === baseToken) {
  215. return
  216. }
  217. if (Array.isArray(token)) {
  218. for (const t of token) {
  219. if (!t || t === baseToken) continue
  220. offsets.set(t, {
  221. baseToken,
  222. offset,
  223. baseline: false,
  224. expectedIndent: undefined
  225. })
  226. }
  227. } else {
  228. offsets.set(token, {
  229. baseToken,
  230. offset,
  231. baseline: false,
  232. expectedIndent: undefined
  233. })
  234. }
  235. }
  236. /**
  237. * Copy offset to the given tokens from srcToken.
  238. * @param {Token} token The token to set.
  239. * @param {Token} srcToken The token of the source offset.
  240. * @returns {void}
  241. */
  242. function copyOffset(token, srcToken) {
  243. if (!token) {
  244. return
  245. }
  246. const offsetData = offsets.get(srcToken)
  247. if (!offsetData) {
  248. return
  249. }
  250. setOffset(
  251. token,
  252. offsetData.offset,
  253. /** @type {Token} */ (offsetData.baseToken)
  254. )
  255. if (offsetData.baseline) {
  256. setBaseline(token)
  257. }
  258. const o = /** @type {OffsetData} */ (offsets.get(token))
  259. o.expectedIndent = offsetData.expectedIndent
  260. }
  261. /**
  262. * Set baseline flag to the given token.
  263. * @param {Token} token The token to set.
  264. * @returns {void}
  265. */
  266. function setBaseline(token) {
  267. const offsetInfo = offsets.get(token)
  268. if (offsetInfo != null) {
  269. offsetInfo.baseline = true
  270. }
  271. }
  272. /**
  273. * Sets preformatted tokens to the given element node.
  274. * @param {VElement} node The node to set.
  275. * @returns {void}
  276. */
  277. function setPreformattedTokens(node) {
  278. const endToken =
  279. (node.endTag && tokenStore.getFirstToken(node.endTag)) ||
  280. tokenStore.getTokenAfter(node)
  281. /** @type {SourceCode.CursorWithSkipOptions} */
  282. const option = {
  283. includeComments: true,
  284. filter: (token) =>
  285. token != null &&
  286. (token.type === 'HTMLText' ||
  287. token.type === 'HTMLRCDataText' ||
  288. token.type === 'HTMLTagOpen' ||
  289. token.type === 'HTMLEndTagOpen' ||
  290. token.type === 'HTMLComment')
  291. }
  292. for (const token of tokenStore.getTokensBetween(
  293. node.startTag,
  294. endToken,
  295. option
  296. )) {
  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 tokens = tokenStore.getTokens(node)
  1192. const firstToken = /** @type {Token} */ (tokens.shift())
  1193. if (isSemicolonToken(tokens[tokens.length - 1])) {
  1194. tokens.pop()
  1195. }
  1196. if (!node.exported) {
  1197. setOffset(tokens, 1, firstToken)
  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, firstToken)
  1205. setOffset(asToken, 1, starToken)
  1206. setOffset(exportedToken, 1, starToken)
  1207. setOffset(afterTokens, 1, firstToken)
  1208. }
  1209. },
  1210. /** @param {ExportDefaultDeclaration} node */
  1211. ExportDefaultDeclaration(node) {
  1212. const exportToken = tokenStore.getFirstToken(node)
  1213. const defaultToken = tokenStore.getFirstToken(node, 1)
  1214. const declarationToken = getFirstAndLastTokens(
  1215. node.declaration
  1216. ).firstToken
  1217. setOffset([defaultToken, declarationToken], 1, exportToken)
  1218. },
  1219. /** @param {ExportNamedDeclaration} node */
  1220. ExportNamedDeclaration(node) {
  1221. const exportToken = tokenStore.getFirstToken(node)
  1222. if (node.declaration) {
  1223. // export var foo = 1;
  1224. const declarationToken = tokenStore.getFirstToken(node, 1)
  1225. setOffset(declarationToken, 1, exportToken)
  1226. } else {
  1227. const firstSpecifier = node.specifiers[0]
  1228. if (!firstSpecifier || firstSpecifier.type === 'ExportSpecifier') {
  1229. // export {foo, bar}; or export {foo, bar} from "mod";
  1230. const leftParenToken = tokenStore.getFirstToken(node, 1)
  1231. const rightParenToken = /** @type {Token} */ (
  1232. tokenStore.getLastToken(node, isClosingBraceToken)
  1233. )
  1234. setOffset(leftParenToken, 0, exportToken)
  1235. processNodeList(node.specifiers, leftParenToken, rightParenToken, 1)
  1236. const maybeFromToken = tokenStore.getTokenAfter(rightParenToken)
  1237. if (maybeFromToken != null && maybeFromToken.value === 'from') {
  1238. const fromToken = maybeFromToken
  1239. const nameToken = tokenStore.getTokenAfter(fromToken)
  1240. setOffset([fromToken, nameToken], 1, exportToken)
  1241. }
  1242. } else {
  1243. // maybe babel parser
  1244. }
  1245. }
  1246. },
  1247. /** @param {ExportSpecifier} node */
  1248. ExportSpecifier(node) {
  1249. const tokens = tokenStore.getTokens(node)
  1250. const firstToken = /** @type {Token} */ (tokens.shift())
  1251. setOffset(tokens, 1, firstToken)
  1252. },
  1253. /** @param {ForInStatement | ForOfStatement} node */
  1254. 'ForInStatement, ForOfStatement'(node) {
  1255. const forToken = tokenStore.getFirstToken(node)
  1256. const awaitToken =
  1257. (node.type === 'ForOfStatement' &&
  1258. node.await &&
  1259. tokenStore.getTokenAfter(forToken)) ||
  1260. null
  1261. const leftParenToken = tokenStore.getTokenAfter(awaitToken || forToken)
  1262. const leftToken = tokenStore.getTokenAfter(leftParenToken)
  1263. const inToken = /** @type {Token} */ (
  1264. tokenStore.getTokenAfter(leftToken, isNotClosingParenToken)
  1265. )
  1266. const rightToken = tokenStore.getTokenAfter(inToken)
  1267. const rightParenToken = tokenStore.getTokenBefore(
  1268. node.body,
  1269. isNotOpeningParenToken
  1270. )
  1271. if (awaitToken != null) {
  1272. setOffset(awaitToken, 0, forToken)
  1273. }
  1274. setOffset(leftParenToken, 1, forToken)
  1275. setOffset(leftToken, 1, leftParenToken)
  1276. setOffset(inToken, 1, leftToken)
  1277. setOffset(rightToken, 1, leftToken)
  1278. setOffset(rightParenToken, 0, leftParenToken)
  1279. processMaybeBlock(node.body, forToken)
  1280. },
  1281. /** @param {ForStatement} node */
  1282. ForStatement(node) {
  1283. const forToken = tokenStore.getFirstToken(node)
  1284. const leftParenToken = tokenStore.getTokenAfter(forToken)
  1285. const rightParenToken = tokenStore.getTokenBefore(
  1286. node.body,
  1287. isNotOpeningParenToken
  1288. )
  1289. setOffset(leftParenToken, 1, forToken)
  1290. processNodeList(
  1291. [node.init, node.test, node.update],
  1292. leftParenToken,
  1293. rightParenToken,
  1294. 1
  1295. )
  1296. processMaybeBlock(node.body, forToken)
  1297. },
  1298. /** @param {FunctionDeclaration | FunctionExpression} node */
  1299. 'FunctionDeclaration, FunctionExpression'(node) {
  1300. const firstToken = tokenStore.getFirstToken(node)
  1301. let leftParenToken, bodyBaseToken
  1302. if (isOpeningParenToken(firstToken)) {
  1303. // Methods.
  1304. leftParenToken = firstToken
  1305. bodyBaseToken = tokenStore.getFirstToken(node.parent)
  1306. } else {
  1307. // Normal functions.
  1308. let nextToken = tokenStore.getTokenAfter(firstToken)
  1309. let nextTokenOffset = 0
  1310. while (
  1311. nextToken &&
  1312. !isOpeningParenToken(nextToken) &&
  1313. nextToken.value !== '<'
  1314. ) {
  1315. if (
  1316. nextToken.value === '*' ||
  1317. (node.id && nextToken.range[0] === node.id.range[0])
  1318. ) {
  1319. nextTokenOffset = 1
  1320. }
  1321. setOffset(nextToken, nextTokenOffset, firstToken)
  1322. nextToken = tokenStore.getTokenAfter(nextToken)
  1323. }
  1324. leftParenToken = nextToken
  1325. bodyBaseToken = firstToken
  1326. }
  1327. if (
  1328. !isOpeningParenToken(leftParenToken) &&
  1329. /** @type {any} */ (node).typeParameters
  1330. ) {
  1331. leftParenToken = tokenStore.getTokenAfter(
  1332. /** @type {any} */ (node).typeParameters
  1333. )
  1334. }
  1335. const rightParenToken = tokenStore.getTokenAfter(
  1336. node.params[node.params.length - 1] || leftParenToken,
  1337. isClosingParenToken
  1338. )
  1339. setOffset(leftParenToken, 1, bodyBaseToken)
  1340. processNodeList(node.params, leftParenToken, rightParenToken, 1)
  1341. const bodyToken = tokenStore.getFirstToken(node.body)
  1342. setOffset(bodyToken, 0, bodyBaseToken)
  1343. },
  1344. /** @param {IfStatement} node */
  1345. IfStatement(node) {
  1346. const ifToken = tokenStore.getFirstToken(node)
  1347. const ifLeftParenToken = tokenStore.getTokenAfter(ifToken)
  1348. const ifRightParenToken = tokenStore.getTokenBefore(
  1349. node.consequent,
  1350. isClosingParenToken
  1351. )
  1352. setOffset(ifLeftParenToken, 1, ifToken)
  1353. setOffset(ifRightParenToken, 0, ifLeftParenToken)
  1354. processMaybeBlock(node.consequent, ifToken)
  1355. if (node.alternate != null) {
  1356. const elseToken = /** @type {Token} */ (
  1357. tokenStore.getTokenAfter(node.consequent, isNotClosingParenToken)
  1358. )
  1359. setOffset(elseToken, 0, ifToken)
  1360. processMaybeBlock(node.alternate, elseToken)
  1361. }
  1362. },
  1363. /** @param {ImportDeclaration} node */
  1364. ImportDeclaration(node) {
  1365. const importToken = tokenStore.getFirstToken(node)
  1366. const tokens = tokenStore.getTokensBetween(importToken, node.source)
  1367. const fromIndex = tokens.map((t) => t.value).lastIndexOf('from')
  1368. const { fromToken, beforeTokens, afterTokens } =
  1369. fromIndex >= 0
  1370. ? {
  1371. fromToken: tokens[fromIndex],
  1372. beforeTokens: tokens.slice(0, fromIndex),
  1373. afterTokens: [
  1374. ...tokens.slice(fromIndex + 1),
  1375. tokenStore.getFirstToken(node.source)
  1376. ]
  1377. }
  1378. : {
  1379. fromToken: null,
  1380. beforeTokens: [...tokens, tokenStore.getFirstToken(node.source)],
  1381. afterTokens: []
  1382. }
  1383. /** @type {ImportSpecifier[]} */
  1384. const namedSpecifiers = []
  1385. for (const specifier of node.specifiers) {
  1386. if (specifier.type === 'ImportSpecifier') {
  1387. namedSpecifiers.push(specifier)
  1388. } else {
  1389. const removeTokens = tokenStore.getTokens(specifier)
  1390. removeTokens.shift()
  1391. for (const token of removeTokens) {
  1392. const i = beforeTokens.indexOf(token)
  1393. if (i >= 0) {
  1394. beforeTokens.splice(i, 1)
  1395. }
  1396. }
  1397. }
  1398. }
  1399. if (namedSpecifiers.length) {
  1400. const leftBrace = tokenStore.getTokenBefore(namedSpecifiers[0])
  1401. const rightBrace = /** @type {Token} */ (
  1402. tokenStore.getTokenAfter(
  1403. namedSpecifiers[namedSpecifiers.length - 1],
  1404. isClosingBraceToken
  1405. )
  1406. )
  1407. processNodeList(namedSpecifiers, leftBrace, rightBrace, 1)
  1408. for (const token of tokenStore.getTokensBetween(
  1409. leftBrace,
  1410. rightBrace
  1411. )) {
  1412. const i = beforeTokens.indexOf(token)
  1413. if (i >= 0) {
  1414. beforeTokens.splice(i, 1)
  1415. }
  1416. }
  1417. }
  1418. if (
  1419. beforeTokens.every(
  1420. (t) => isOpeningBraceToken(t) || isClosingBraceToken(t)
  1421. )
  1422. ) {
  1423. setOffset(beforeTokens, 0, importToken)
  1424. } else {
  1425. setOffset(beforeTokens, 1, importToken)
  1426. }
  1427. if (fromToken) {
  1428. setOffset(fromToken, 1, importToken)
  1429. setOffset(afterTokens, 0, fromToken)
  1430. }
  1431. },
  1432. /** @param {ImportSpecifier} node */
  1433. ImportSpecifier(node) {
  1434. if (node.local.range[0] !== node.imported.range[0]) {
  1435. const tokens = tokenStore.getTokens(node)
  1436. const firstToken = /** @type {Token} */ (tokens.shift())
  1437. setOffset(tokens, 1, firstToken)
  1438. }
  1439. },
  1440. /** @param {ImportNamespaceSpecifier} node */
  1441. ImportNamespaceSpecifier(node) {
  1442. const tokens = tokenStore.getTokens(node)
  1443. const firstToken = /** @type {Token} */ (tokens.shift())
  1444. setOffset(tokens, 1, firstToken)
  1445. },
  1446. /** @param {LabeledStatement} node */
  1447. LabeledStatement(node) {
  1448. const labelToken = tokenStore.getFirstToken(node)
  1449. const colonToken = tokenStore.getTokenAfter(labelToken)
  1450. const bodyToken = tokenStore.getTokenAfter(colonToken)
  1451. setOffset([colonToken, bodyToken], 1, labelToken)
  1452. },
  1453. /** @param {MemberExpression | MetaProperty} node */
  1454. 'MemberExpression, MetaProperty'(node) {
  1455. const objectToken = tokenStore.getFirstToken(node)
  1456. if (node.type === 'MemberExpression' && node.computed) {
  1457. const leftBracketToken = /** @type {Token} */ (
  1458. tokenStore.getTokenBefore(node.property, isOpeningBracketToken)
  1459. )
  1460. const propertyToken = tokenStore.getTokenAfter(leftBracketToken)
  1461. const rightBracketToken = tokenStore.getTokenAfter(
  1462. node.property,
  1463. isClosingBracketToken
  1464. )
  1465. for (const optionalToken of tokenStore.getTokensBetween(
  1466. tokenStore.getLastToken(node.object),
  1467. leftBracketToken,
  1468. isOptionalToken
  1469. )) {
  1470. setOffset(optionalToken, 1, objectToken)
  1471. }
  1472. setOffset(leftBracketToken, 1, objectToken)
  1473. setOffset(propertyToken, 1, leftBracketToken)
  1474. setOffset(rightBracketToken, 0, leftBracketToken)
  1475. } else {
  1476. const dotToken = tokenStore.getTokenBefore(node.property)
  1477. const propertyToken = tokenStore.getTokenAfter(dotToken)
  1478. setOffset([dotToken, propertyToken], 1, objectToken)
  1479. }
  1480. },
  1481. /** @param {MethodDefinition | Property | PropertyDefinition} node */
  1482. 'MethodDefinition, Property, PropertyDefinition'(node) {
  1483. const firstToken = tokenStore.getFirstToken(node)
  1484. const keyTokens = getFirstAndLastTokens(node.key)
  1485. const prefixTokens = tokenStore.getTokensBetween(
  1486. firstToken,
  1487. keyTokens.firstToken
  1488. )
  1489. if (node.computed) {
  1490. prefixTokens.pop() // pop [
  1491. }
  1492. setOffset(prefixTokens, 0, firstToken)
  1493. let lastKeyToken
  1494. if (node.computed) {
  1495. const leftBracketToken = tokenStore.getTokenBefore(keyTokens.firstToken)
  1496. const rightBracketToken = (lastKeyToken = tokenStore.getTokenAfter(
  1497. keyTokens.lastToken
  1498. ))
  1499. setOffset(leftBracketToken, 0, firstToken)
  1500. processNodeList([node.key], leftBracketToken, rightBracketToken, 1)
  1501. } else {
  1502. setOffset(keyTokens.firstToken, 0, firstToken)
  1503. lastKeyToken = keyTokens.lastToken
  1504. }
  1505. if (node.value != null) {
  1506. const initToken = tokenStore.getFirstToken(node.value)
  1507. setOffset(
  1508. [...tokenStore.getTokensBetween(lastKeyToken, initToken), initToken],
  1509. 1,
  1510. lastKeyToken
  1511. )
  1512. }
  1513. },
  1514. /** @param {NewExpression} node */
  1515. NewExpression(node) {
  1516. const newToken = tokenStore.getFirstToken(node)
  1517. const calleeToken = tokenStore.getTokenAfter(newToken)
  1518. const rightToken = tokenStore.getLastToken(node)
  1519. const leftToken = isClosingParenToken(rightToken)
  1520. ? tokenStore.getFirstTokenBetween(
  1521. node.callee,
  1522. rightToken,
  1523. isOpeningParenToken
  1524. )
  1525. : null
  1526. setOffset(calleeToken, 1, newToken)
  1527. if (leftToken != null) {
  1528. setOffset(leftToken, 1, calleeToken)
  1529. processNodeList(node.arguments, leftToken, rightToken, 1)
  1530. }
  1531. },
  1532. /** @param {ObjectExpression | ObjectPattern} node */
  1533. 'ObjectExpression, ObjectPattern'(node) {
  1534. const firstToken = tokenStore.getFirstToken(node)
  1535. const rightToken = tokenStore.getTokenAfter(
  1536. node.properties[node.properties.length - 1] || firstToken,
  1537. isClosingBraceToken
  1538. )
  1539. processNodeList(node.properties, firstToken, rightToken, 1)
  1540. },
  1541. /** @param {SequenceExpression} node */
  1542. SequenceExpression(node) {
  1543. processNodeList(node.expressions, null, null, 0)
  1544. },
  1545. /** @param {SwitchCase} node */
  1546. SwitchCase(node) {
  1547. const caseToken = tokenStore.getFirstToken(node)
  1548. if (node.test != null) {
  1549. const testToken = tokenStore.getTokenAfter(caseToken)
  1550. const colonToken = tokenStore.getTokenAfter(
  1551. node.test,
  1552. isNotClosingParenToken
  1553. )
  1554. setOffset([testToken, colonToken], 1, caseToken)
  1555. } else {
  1556. const colonToken = tokenStore.getTokenAfter(caseToken)
  1557. setOffset(colonToken, 1, caseToken)
  1558. }
  1559. if (
  1560. node.consequent.length === 1 &&
  1561. node.consequent[0].type === 'BlockStatement'
  1562. ) {
  1563. setOffset(tokenStore.getFirstToken(node.consequent[0]), 0, caseToken)
  1564. } else if (node.consequent.length >= 1) {
  1565. setOffset(tokenStore.getFirstToken(node.consequent[0]), 1, caseToken)
  1566. processNodeList(node.consequent, null, null, 0)
  1567. }
  1568. },
  1569. /** @param {SwitchStatement} node */
  1570. SwitchStatement(node) {
  1571. const switchToken = tokenStore.getFirstToken(node)
  1572. const leftParenToken = tokenStore.getTokenAfter(switchToken)
  1573. const discriminantToken = tokenStore.getTokenAfter(leftParenToken)
  1574. const leftBraceToken = /** @type {Token} */ (
  1575. tokenStore.getTokenAfter(node.discriminant, isOpeningBraceToken)
  1576. )
  1577. const rightParenToken = tokenStore.getTokenBefore(leftBraceToken)
  1578. const rightBraceToken = tokenStore.getLastToken(node)
  1579. setOffset(leftParenToken, 1, switchToken)
  1580. setOffset(discriminantToken, 1, leftParenToken)
  1581. setOffset(rightParenToken, 0, leftParenToken)
  1582. setOffset(leftBraceToken, 0, switchToken)
  1583. processNodeList(
  1584. node.cases,
  1585. leftBraceToken,
  1586. rightBraceToken,
  1587. options.switchCase
  1588. )
  1589. },
  1590. /** @param {TaggedTemplateExpression} node */
  1591. TaggedTemplateExpression(node) {
  1592. const tagTokens = getFirstAndLastTokens(node.tag, node.range[0])
  1593. const quasiToken = tokenStore.getTokenAfter(tagTokens.lastToken)
  1594. setOffset(quasiToken, 1, tagTokens.firstToken)
  1595. },
  1596. /** @param {TemplateLiteral} node */
  1597. TemplateLiteral(node) {
  1598. const firstToken = tokenStore.getFirstToken(node)
  1599. const quasiTokens = node.quasis
  1600. .slice(1)
  1601. .map((n) => tokenStore.getFirstToken(n))
  1602. const expressionToken = node.quasis
  1603. .slice(0, -1)
  1604. .map((n) => tokenStore.getTokenAfter(n))
  1605. setOffset(quasiTokens, 0, firstToken)
  1606. setOffset(expressionToken, 1, firstToken)
  1607. },
  1608. /** @param {TryStatement} node */
  1609. TryStatement(node) {
  1610. const tryToken = tokenStore.getFirstToken(node)
  1611. const tryBlockToken = tokenStore.getFirstToken(node.block)
  1612. setOffset(tryBlockToken, 0, tryToken)
  1613. if (node.handler != null) {
  1614. const catchToken = tokenStore.getFirstToken(node.handler)
  1615. setOffset(catchToken, 0, tryToken)
  1616. }
  1617. if (node.finalizer != null) {
  1618. const finallyToken = tokenStore.getTokenBefore(node.finalizer)
  1619. const finallyBlockToken = tokenStore.getFirstToken(node.finalizer)
  1620. setOffset([finallyToken, finallyBlockToken], 0, tryToken)
  1621. }
  1622. },
  1623. /** @param {UpdateExpression} node */
  1624. UpdateExpression(node) {
  1625. const firstToken = tokenStore.getFirstToken(node)
  1626. const nextToken = tokenStore.getTokenAfter(firstToken)
  1627. setOffset(nextToken, 1, firstToken)
  1628. },
  1629. /** @param {VariableDeclaration} node */
  1630. VariableDeclaration(node) {
  1631. processNodeList(
  1632. node.declarations,
  1633. tokenStore.getFirstToken(node),
  1634. null,
  1635. 1
  1636. )
  1637. },
  1638. /** @param {VariableDeclarator} node */
  1639. VariableDeclarator(node) {
  1640. if (node.init != null) {
  1641. const idToken = tokenStore.getFirstToken(node)
  1642. const eqToken = tokenStore.getTokenAfter(node.id)
  1643. const initToken = tokenStore.getTokenAfter(eqToken)
  1644. setOffset([eqToken, initToken], 1, idToken)
  1645. }
  1646. },
  1647. /** @param {WhileStatement | WithStatement} node */
  1648. 'WhileStatement, WithStatement'(node) {
  1649. const firstToken = tokenStore.getFirstToken(node)
  1650. const leftParenToken = tokenStore.getTokenAfter(firstToken)
  1651. const rightParenToken = tokenStore.getTokenBefore(
  1652. node.body,
  1653. isClosingParenToken
  1654. )
  1655. setOffset(leftParenToken, 1, firstToken)
  1656. setOffset(rightParenToken, 0, leftParenToken)
  1657. processMaybeBlock(node.body, firstToken)
  1658. },
  1659. /** @param {YieldExpression} node */
  1660. YieldExpression(node) {
  1661. if (node.argument != null) {
  1662. const yieldToken = tokenStore.getFirstToken(node)
  1663. setOffset(tokenStore.getTokenAfter(yieldToken), 1, yieldToken)
  1664. if (node.delegate) {
  1665. setOffset(tokenStore.getTokenAfter(yieldToken, 1), 1, yieldToken)
  1666. }
  1667. }
  1668. },
  1669. // ----------------------------------------------------------------------
  1670. // SINGLE TOKEN NODES
  1671. // ----------------------------------------------------------------------
  1672. DebuggerStatement() {},
  1673. Identifier() {},
  1674. ImportDefaultSpecifier() {},
  1675. Literal() {},
  1676. PrivateIdentifier() {},
  1677. Super() {},
  1678. TemplateElement() {},
  1679. ThisExpression() {},
  1680. // ----------------------------------------------------------------------
  1681. // WRAPPER NODES
  1682. // ----------------------------------------------------------------------
  1683. ExpressionStatement() {},
  1684. ChainExpression() {},
  1685. EmptyStatement() {},
  1686. // ----------------------------------------------------------------------
  1687. // COMMONS
  1688. // ----------------------------------------------------------------------
  1689. /** @param {Statement} node */
  1690. // Process semicolons.
  1691. ':statement, PropertyDefinition'(node) {
  1692. processSemicolons(node)
  1693. },
  1694. /** @param {Expression | MetaProperty | TemplateLiteral} node */
  1695. // Process parentheses.
  1696. // `:expression` does not match with MetaProperty and TemplateLiteral as a bug: https://github.com/estools/esquery/pull/59
  1697. ':expression'(node) {
  1698. let leftToken = tokenStore.getTokenBefore(node)
  1699. let rightToken = tokenStore.getTokenAfter(node)
  1700. let firstToken = tokenStore.getFirstToken(node)
  1701. while (
  1702. leftToken &&
  1703. rightToken &&
  1704. isOpeningParenToken(leftToken) &&
  1705. isClosingParenToken(rightToken)
  1706. ) {
  1707. setOffset(firstToken, 1, leftToken)
  1708. setOffset(rightToken, 0, leftToken)
  1709. firstToken = leftToken
  1710. leftToken = tokenStore.getTokenBefore(leftToken)
  1711. rightToken = tokenStore.getTokenAfter(rightToken)
  1712. }
  1713. },
  1714. .../** @type {TemplateListener} */ (
  1715. tsDefineVisitor({
  1716. processNodeList,
  1717. tokenStore,
  1718. setOffset,
  1719. copyOffset,
  1720. processSemicolons,
  1721. getFirstAndLastTokens
  1722. })
  1723. ),
  1724. /** @param {ASTNode} node */
  1725. // Ignore tokens of unknown nodes.
  1726. '*:exit'(node) {
  1727. if (!knownNodes.has(node.type)) {
  1728. ignore(node)
  1729. }
  1730. },
  1731. /** @param {Program} node */
  1732. // Top-level process.
  1733. Program(node) {
  1734. const firstToken = node.tokens[0]
  1735. const isScriptTag =
  1736. firstToken != null &&
  1737. firstToken.type === 'Punctuator' &&
  1738. firstToken.value === '<script>'
  1739. const baseIndent = isScriptTag
  1740. ? options.indentSize * options.baseIndent
  1741. : 0
  1742. for (const statement of node.body) {
  1743. processTopLevelNode(statement, baseIndent)
  1744. }
  1745. },
  1746. /** @param {VElement} node */
  1747. "VElement[parent.type!='VElement']"(node) {
  1748. processTopLevelNode(node, 0)
  1749. },
  1750. /** @param {Program | VElement} node */
  1751. // Do validation.
  1752. ":matches(Program, VElement[parent.type!='VElement']):exit"(node) {
  1753. let comments = []
  1754. /** @type {Token[]} */
  1755. let tokensOnSameLine = []
  1756. let isBesideMultilineToken = false
  1757. let lastValidatedToken = null
  1758. // Validate indentation of tokens.
  1759. for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) {
  1760. const tokenStartLine = token.loc.start.line
  1761. if (
  1762. tokensOnSameLine.length === 0 ||
  1763. tokensOnSameLine[0].loc.start.line === tokenStartLine
  1764. ) {
  1765. // This is on the same line (or the first token).
  1766. tokensOnSameLine.push(token)
  1767. } else if (tokensOnSameLine.every(isComment)) {
  1768. // New line is detected, but the all tokens of the previous line are comment.
  1769. // Comment lines are adjusted to the next code line.
  1770. comments.push(tokensOnSameLine[0])
  1771. isBesideMultilineToken =
  1772. /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line ===
  1773. tokenStartLine
  1774. tokensOnSameLine = [token]
  1775. } else {
  1776. // New line is detected, so validate the tokens.
  1777. if (!isBesideMultilineToken) {
  1778. validate(tokensOnSameLine, comments, lastValidatedToken)
  1779. lastValidatedToken = tokensOnSameLine[0]
  1780. }
  1781. isBesideMultilineToken =
  1782. /** @type {Token} */ (last(tokensOnSameLine)).loc.end.line ===
  1783. tokenStartLine
  1784. tokensOnSameLine = [token]
  1785. comments = []
  1786. }
  1787. }
  1788. if (tokensOnSameLine.length >= 1 && tokensOnSameLine.some(isNotComment)) {
  1789. validate(tokensOnSameLine, comments, lastValidatedToken)
  1790. }
  1791. }
  1792. }
  1793. for (const key of Object.keys(visitor)) {
  1794. for (const nodeName of key
  1795. .split(/\s*,\s*/gu)
  1796. .map((s) => s.trim())
  1797. .filter((s) => /[a-z]+/i.test(s))) {
  1798. knownNodes.add(nodeName)
  1799. }
  1800. }
  1801. return processIgnores(visitor)
  1802. }