"use strict"; // A “chunk” is a sequence of statements of a certain type with only comments // and whitespace between. function extractChunks(parentNode, isPartOfChunk) { const chunks = []; let chunk = []; let lastNode = undefined; for (const node of parentNode.body) { const result = isPartOfChunk(node, lastNode); switch (result) { case "PartOfChunk": chunk.push(node); break; case "PartOfNewChunk": if (chunk.length > 0) { chunks.push(chunk); } chunk = [node]; break; case "NotPartOfChunk": if (chunk.length > 0) { chunks.push(chunk); chunk = []; } break; // istanbul ignore next default: throw new Error(`Unknown chunk result: ${result}`); } lastNode = node; } if (chunk.length > 0) { chunks.push(chunk); } return chunks; } function maybeReportSorting(context, sorted, start, end) { const sourceCode = context.getSourceCode(); const original = sourceCode.getText().slice(start, end); if (original !== sorted) { context.report({ messageId: "sort", loc: { start: sourceCode.getLocFromIndex(start), end: sourceCode.getLocFromIndex(end), }, fix: (fixer) => fixer.replaceTextRange([start, end], sorted), }); } } function printSortedItems(sortedItems, originalItems, sourceCode) { const newline = guessNewline(sourceCode); const sorted = sortedItems .map((groups) => groups .map((groupItems) => groupItems.map((item) => item.code).join(newline)) .join(newline) ) .join(newline + newline); // Edge case: If the last import/export (after sorting) ends with a line // comment and there’s code (or a multiline block comment) on the same line, // add a newline so we don’t accidentally comment stuff out. const flattened = flatMap(sortedItems, (groups) => [].concat(...groups)); const lastSortedItem = flattened[flattened.length - 1]; const lastOriginalItem = originalItems[originalItems.length - 1]; const nextToken = lastSortedItem.needsNewline ? sourceCode.getTokenAfter(lastOriginalItem.node, { includeComments: true, filter: (token) => !isLineComment(token) && !( isBlockComment(token) && token.loc.end.line === lastOriginalItem.node.loc.end.line ), }) : undefined; const maybeNewline = nextToken != null && nextToken.loc.start.line === lastOriginalItem.node.loc.end.line ? newline : ""; return sorted + maybeNewline; } // Wrap the import/export nodes in `passedChunk` in objects with more data about // the import/export. Most importantly there’s a `code` property that contains // the node as a string, with comments (if any). Finding the corresponding // comments is the hard part. function getImportExportItems( passedChunk, sourceCode, isSideEffectImport, getSpecifiers ) { const chunk = handleLastSemicolon(passedChunk, sourceCode); return chunk.map((node, nodeIndex) => { const lastLine = nodeIndex === 0 ? node.loc.start.line - 1 : chunk[nodeIndex - 1].loc.end.line; // Get all comments before the import/export, except: // // - Comments on another line for the first import/export. // - Comments that belong to the previous import/export (if any) – that is, // comments that are on the same line as the previous import/export. But // multiline block comments always belong to this import/export, not the // previous. const commentsBefore = sourceCode .getCommentsBefore(node) .filter( (comment) => comment.loc.start.line <= node.loc.start.line && comment.loc.end.line > lastLine && (nodeIndex > 0 || comment.loc.start.line > lastLine) ); // Get all comments after the import/export that are on the same line. // Multiline block comments belong to the _next_ import/export (or the // following code in case of the last import/export). const commentsAfter = sourceCode .getCommentsAfter(node) .filter((comment) => comment.loc.end.line === node.loc.end.line); const before = printCommentsBefore(node, commentsBefore, sourceCode); const after = printCommentsAfter(node, commentsAfter, sourceCode); // Print the indentation before the import/export or its first comment, if // any, to support indentation in `