sortPubspec function
Alphabetically sorts the dependency sections of a pubspec.yaml.
Sorts dependencies, dev_dependencies, and dependency_overrides by key
(case-insensitive). This is a line-based sort: each dependency keeps its
nested value lines (e.g. a git:/version: block) and any comment lines
directly above it, so comments and formatting are preserved. See issue
import_sorter#89.
Returns the rewritten YAML with a trailing newline. If nothing needs reordering, the content is returned unchanged (trailing newline ensured).
Implementation
String sortPubspec(String pubspecContent) {
final lines = pubspecContent.split('\n');
final output = <String>[];
var i = 0;
while (i < lines.length) {
final line = lines[i];
final section = _sectionHeader(line);
if (section == null) {
output.add(line);
i++;
continue;
}
// Keep the `dependencies:` header line itself.
output.add(line);
i++;
// Gather entry blocks until the section ends (a non-indented, non-blank
// line) or the file ends.
final blocks = <_Entry>[];
final pendingComments = <String>[];
while (i < lines.length) {
final current = lines[i];
// Blank line: attach to pending comments so it travels with the next
// entry, unless we are at the very start (then it separates sections).
if (current.trim().isEmpty) {
// A blank line ends the section only if the next meaningful line is
// not part of this section's entries.
if (_endsSection(lines, i)) break;
pendingComments.add(current);
i++;
continue;
}
// A line that is not indented ends the section.
if (!current.startsWith(' ')) break;
final key = _entryKey(current);
if (key != null) {
// Start of a new entry: collect its continuation lines.
final blockLines = <String>[...pendingComments, current];
pendingComments.clear();
i++;
// Continuation lines are nested values, indented deeper than the key
// (3+ spaces). A comment aligned with entries (2 spaces) is treated as
// a leading comment of the *next* entry instead.
while (i < lines.length &&
lines[i].startsWith(' ') &&
lines[i].trim().isNotEmpty) {
blockLines.add(lines[i]);
i++;
}
blocks.add(_Entry(key, blockLines));
} else {
// Indented but not a recognizable entry key — leave as-is.
pendingComments.add(current);
i++;
}
}
blocks.sort(
(a, b) => a.key.toLowerCase().compareTo(b.key.toLowerCase()),
);
for (final block in blocks) {
output.addAll(block.lines);
}
// Re-emit any trailing comments/blank lines that were not attached.
output.addAll(pendingComments);
}
final result = output.join('\n');
return result.endsWith('\n') ? result : '$result\n';
}