indent-common.js 67 KB

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