@@ -18,6 +18,7 @@ const KNOWN_NODES = new Set(['ArrayExpression', 'ArrayPattern', 'ArrowFunctionEx
1818const LT_CHAR = / [ \r \n \u2028 \u2029 ] /
1919const LINES = / [ ^ \r \n \u2028 \u2029 ] + (?: $ | \r \n | [ \r \n \u2028 \u2029 ] ) / g
2020const BLOCK_COMMENT_PREFIX = / ^ \s * \* /
21+ const ITERATION_OPTS = Object . freeze ( { includeComments : true , filter : isNotWhitespace } )
2122
2223/**
2324 * Normalize options.
@@ -194,6 +195,15 @@ function isNotComment (token) {
194195 return token != null && token . type !== 'Block' && token . type !== 'Line' && token . type !== 'Shebang' && ! token . type . endsWith ( 'Comment' )
195196}
196197
198+ /**
199+ * Check whether the given node is not an empty text node.
200+ * @param {Node } node The node to check.
201+ * @returns {boolean } `false` if the token is empty text node.
202+ */
203+ function isNotEmptyTextNode ( node ) {
204+ return ! ( node . type === 'VText' && node . value . trim ( ) === '' )
205+ }
206+
197207/**
198208 * Get the last element.
199209 * @param {Array } xs The array to get the last element.
@@ -295,7 +305,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
295305 * @param {Token } token The token to set.
296306 * @returns {void }
297307 */
298- function setBaseline ( token , hardTabAdditional ) {
308+ function setBaseline ( token ) {
299309 const offsetInfo = offsets . get ( token )
300310 if ( offsetInfo != null ) {
301311 offsetInfo . baseline = true
@@ -353,17 +363,21 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
353363 * The first node is offsetted from the given left token.
354364 * Rest nodes are adjusted to the first node.
355365 * @param {Node[] } nodeList The node to process.
356- * @param {Node|null } leftToken The left parenthesis token.
357- * @param {Node|null } rightToken The right parenthesis token.
366+ * @param {Node|Token| null } left The left parenthesis token.
367+ * @param {Node|Token| null } right The right parenthesis token.
358368 * @param {number } offset The offset to set.
359- * @param {Node } [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.
369+ * @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.
360370 * @returns {void }
361371 */
362- function processNodeList ( nodeList , leftToken , rightToken , offset , alignVertically ) {
372+ function processNodeList ( nodeList , left , right , offset , alignVertically ) {
363373 let t
374+ const leftToken = ( left && tokenStore . getFirstToken ( left ) ) || left
375+ const rightToken = ( right && tokenStore . getFirstToken ( right ) ) || right
364376
365377 if ( nodeList . length >= 1 ) {
366- let lastToken = leftToken
378+ let baseToken = null
379+ let lastToken = left
380+ const alignTokensBeforeBaseToken = [ ]
367381 const alignTokens = [ ]
368382
369383 for ( let i = 0 ; i < nodeList . length ; ++ i ) {
@@ -374,30 +388,50 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
374388 }
375389 const elementTokens = getFirstAndLastTokens ( node , lastToken != null ? lastToken . range [ 1 ] : 0 )
376390
377- // Collect related tokens.
378- // Commas between this and the previous, and the first token of this node.
391+ // Collect comma/comment tokens between the last token of the previous node and the first token of this node.
379392 if ( lastToken != null ) {
380393 t = lastToken
381- while ( ( t = tokenStore . getTokenAfter ( t ) ) != null && t . range [ 1 ] <= elementTokens . firstToken . range [ 0 ] ) {
382- alignTokens . push ( t )
394+ while (
395+ ( t = tokenStore . getTokenAfter ( t , ITERATION_OPTS ) ) != null &&
396+ t . range [ 1 ] <= elementTokens . firstToken . range [ 0 ]
397+ ) {
398+ if ( baseToken == null ) {
399+ alignTokensBeforeBaseToken . push ( t )
400+ } else {
401+ alignTokens . push ( t )
402+ }
383403 }
384404 }
385- alignTokens . push ( elementTokens . firstToken )
386405
387- // Save the last token to find tokens between the next token.
406+ if ( baseToken == null ) {
407+ baseToken = elementTokens . firstToken
408+ } else {
409+ alignTokens . push ( elementTokens . firstToken )
410+ }
411+
412+ // Save the last token to find tokens between this node and the next node.
388413 lastToken = elementTokens . lastToken
389414 }
390415
391- // Check trailing commas.
416+ // Check trailing commas and comments .
392417 if ( rightToken != null && lastToken != null ) {
393418 t = lastToken
394- while ( ( t = tokenStore . getTokenAfter ( t ) ) != null && t . range [ 1 ] <= rightToken . range [ 0 ] ) {
395- alignTokens . push ( t )
419+ while (
420+ ( t = tokenStore . getTokenAfter ( t , ITERATION_OPTS ) ) != null &&
421+ t . range [ 1 ] <= rightToken . range [ 0 ]
422+ ) {
423+ if ( baseToken == null ) {
424+ alignTokensBeforeBaseToken . push ( t )
425+ } else {
426+ alignTokens . push ( t )
427+ }
396428 }
397429 }
398430
399431 // Set offsets.
400- const baseToken = alignTokens . shift ( )
432+ if ( leftToken != null ) {
433+ setOffset ( alignTokensBeforeBaseToken , offset , leftToken )
434+ }
401435 if ( baseToken != null ) {
402436 // Set offset to the first token.
403437 if ( leftToken != null ) {
@@ -409,7 +443,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
409443 setBaseline ( baseToken )
410444 }
411445
412- if ( alignVertically === false ) {
446+ if ( alignVertically === false && leftToken != null ) {
413447 // Align tokens relatively to the left token.
414448 setOffset ( alignTokens , offset , leftToken )
415449 } else {
@@ -657,10 +691,10 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
657691 * Validate the given token with the pre-calculated expected indentation.
658692 * @param {Token } token The token to validate.
659693 * @param {number } expectedIndent The expected indentation.
660- * @param {number|undefined } optionalExpectedIndent The optional expected indentation.
694+ * @param {number[] |undefined } optionalExpectedIndents The optional expected indentation.
661695 * @returns {void }
662696 */
663- function validateCore ( token , expectedIndent , optionalExpectedIndent ) {
697+ function validateCore ( token , expectedIndent , optionalExpectedIndents ) {
664698 const line = token . loc . start . line
665699 const indentText = getIndentText ( token )
666700
@@ -692,7 +726,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
692726 }
693727 }
694728
695- if ( actualIndent !== expectedIndent && ( optionalExpectedIndent === undefined || actualIndent !== optionalExpectedIndent ) ) {
729+ if ( actualIndent !== expectedIndent && ( optionalExpectedIndents == null || ! optionalExpectedIndents . includes ( actualIndent ) ) ) {
696730 context . report ( {
697731 loc : {
698732 start : { line, column : 0 } ,
@@ -716,7 +750,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
716750 * @param {Token|null } nextToken The next token of comments.
717751 * @param {number|undefined } nextExpectedIndent The expected indent of the next token.
718752 * @param {number|undefined } lastExpectedIndent The expected indent of the last token.
719- * @returns {{primary: number|undefined,secondary:number|undefined} }
753+ * @returns {number[] }
720754 */
721755 function getCommentExpectedIndents ( nextToken , nextExpectedIndent , lastExpectedIndent ) {
722756 if ( typeof lastExpectedIndent === 'number' && isClosingToken ( nextToken ) ) {
@@ -725,26 +759,23 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
725759 // <div>
726760 // <!-- comment -->
727761 // </div>
728- return {
729- primary : nextExpectedIndent + options . indentSize ,
730- secondary : undefined
731- }
762+ return [ nextExpectedIndent + options . indentSize , nextExpectedIndent ]
732763 }
733764
734765 // For last comment. E.g.,
735766 // <div>
736767 // <div></div>
737768 // <!-- comment -->
738769 // </div>
739- return { primary : lastExpectedIndent , secondary : nextExpectedIndent }
770+ return [ lastExpectedIndent , nextExpectedIndent ]
740771 }
741772
742773 // Adjust to next normally. E.g.,
743774 // <div>
744775 // <!-- comment -->
745776 // <div></div>
746777 // </div>
747- return { primary : nextExpectedIndent , secondary : undefined }
778+ return [ nextExpectedIndent ]
748779 }
749780
750781 /**
@@ -815,11 +846,17 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
815846 // It allows the same indent level with the previous line.
816847 const lastOffsetInfo = offsets . get ( lastToken )
817848 const lastExpectedIndent = lastOffsetInfo && lastOffsetInfo . expectedIndent
818- const commentExpectedIndents = getCommentExpectedIndents ( firstToken , expectedIndent , lastExpectedIndent )
849+ const commentOptionalExpectedIndents = getCommentExpectedIndents ( firstToken , expectedIndent , lastExpectedIndent )
819850
820851 // Validate.
821852 for ( const comment of comments ) {
822- validateCore ( comment , commentExpectedIndents . primary , commentExpectedIndents . secondary )
853+ const commentExpectedIndents = getExpectedIndents ( [ comment ] )
854+ const commentExpectedIndent =
855+ commentExpectedIndents
856+ ? commentExpectedIndents . expectedIndent
857+ : commentOptionalExpectedIndents [ 0 ]
858+
859+ validateCore ( comment , commentExpectedIndent , commentOptionalExpectedIndents )
823860 }
824861 validateCore ( firstToken , expectedIndent )
825862 }
@@ -844,16 +881,14 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
844881 } ,
845882
846883 VElement ( node ) {
847- const startTagToken = tokenStore . getFirstToken ( node )
848- const endTagToken = node . endTag && tokenStore . getFirstToken ( node . endTag )
849-
850884 if ( node . name !== 'pre' ) {
851- const childTokens = node . children . map ( n => tokenStore . getFirstToken ( n ) )
852- setOffset ( childTokens , 1 , startTagToken )
885+ processNodeList ( node . children . filter ( isNotEmptyTextNode ) , node . startTag , node . endTag , 1 )
853886 } else {
887+ const startTagToken = tokenStore . getFirstToken ( node )
888+ const endTagToken = node . endTag && tokenStore . getFirstToken ( node . endTag )
889+ setOffset ( endTagToken , 0 , startTagToken )
854890 setPreformattedTokens ( node )
855891 }
856- setOffset ( endTagToken , 0 , startTagToken )
857892 } ,
858893
859894 VEndTag ( node ) {
@@ -1116,7 +1151,6 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
11161151
11171152 setOffset ( leftParenToken , 1 , forToken )
11181153 processNodeList ( [ node . init , node . test , node . update ] , leftParenToken , rightParenToken , 1 )
1119- setOffset ( rightParenToken , 0 , leftParenToken )
11201154 processMaybeBlock ( node . body , forToken )
11211155 } ,
11221156
@@ -1544,7 +1578,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
15441578 let lastValidatedToken = null
15451579
15461580 // Validate indentation of tokens.
1547- for ( const token of tokenStore . getTokens ( node , { includeComments : true , filter : isNotWhitespace } ) ) {
1581+ for ( const token of tokenStore . getTokens ( node , ITERATION_OPTS ) ) {
15481582 if ( tokensOnSameLine . length === 0 || tokensOnSameLine [ 0 ] . loc . start . line === token . loc . start . line ) {
15491583 // This is on the same line (or the first token).
15501584 tokensOnSameLine . push ( token )
0 commit comments