indent-common.js 68 KB

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