[code_builder] Emit ignore_for_file bits correctly (#2406)
diff --git a/pkgs/code_builder/CHANGELOG.md b/pkgs/code_builder/CHANGELOG.md
index 7b11a5c..8c73217 100644
--- a/pkgs/code_builder/CHANGELOG.md
+++ b/pkgs/code_builder/CHANGELOG.md
@@ -1,5 +1,8 @@
## 4.12.0-wip
+* Ensure scoped lint ignores
+ (such as `// ignore_for_file: no_leading_underscores_for_library_prefixes`)
+ are emitted at the top of the file before library annotations and directives.
* Allow single quotes in strings passed to `literalString(raw:true)`. This
argument no longer guarantees a raw string is used, but results will have the
same behavior.
diff --git a/pkgs/code_builder/lib/src/emitter.dart b/pkgs/code_builder/lib/src/emitter.dart
index d095a91..e82a9fe 100644
--- a/pkgs/code_builder/lib/src/emitter.dart
+++ b/pkgs/code_builder/lib/src/emitter.dart
@@ -480,20 +480,6 @@
..writeln();
}
- if (spec.ignoreForFile.isNotEmpty) {
- final ignores = spec.ignoreForFile.toList()..sort();
- final lines = ['// ignore_for_file: ${ignores.first}'];
- for (var ignore in ignores.skip(1)) {
- if (lines.last.length + 2 + ignore.length > 80) {
- lines.add('// ignore_for_file: $ignore');
- } else {
- lines[lines.length - 1] = '${lines.last}, $ignore';
- }
- }
- lines.forEach(output.writeln);
- output.writeln();
- }
-
// Process the body first in order to prime the allocators.
final body = StringBuffer();
for (final spec in spec.body) {
@@ -503,16 +489,17 @@
}
}
- spec.docs.forEach(output.writeln);
+ final header = StringBuffer();
+ spec.docs.forEach(header.writeln);
for (var a in spec.annotations) {
- visitAnnotation(a, output);
+ visitAnnotation(a, header);
}
if (spec.name != null) {
- output.write('library ${spec.name!};');
+ header.write('library ${spec.name!};');
} else if (spec.annotations.isNotEmpty || spec.docs.isNotEmpty) {
// An explicit _unnamed_ library directive is only required if there are
// annotations or doc comments on the library.
- output.write('library;');
+ header.write('library;');
}
final directives = <Directive>[...allocator.imports, ...spec.directives];
@@ -521,12 +508,32 @@
directives.sort();
}
- Directive? previous;
- if (directives.any((d) => d.as?.startsWith('_') ?? false)) {
- output.writeln(
- '// ignore_for_file: no_leading_underscores_for_library_prefixes',
- );
+ final ignores = [
+ ...spec.ignoreForFile,
+ if (directives.any((d) => d.as?.startsWith('_') ?? false))
+ 'no_leading_underscores_for_library_prefixes',
+ ];
+
+ if (ignores.isNotEmpty) {
+ ignores.sort();
+ const prefix = '// ignore_for_file: ';
+ final lines = [StringBuffer(prefix)..write(ignores.first)];
+ for (var ignore in ignores.skip(1)) {
+ if (lines.last.length + 2 + ignore.length > 80) {
+ lines.add(StringBuffer(prefix)..write(ignore));
+ } else {
+ lines.last
+ ..write(', ')
+ ..write(ignore);
+ }
+ }
+ lines.forEach(output.writeln);
+ output.writeln();
}
+
+ output.write(header);
+
+ Directive? previous;
for (final directive in directives) {
if (_newLineBetween(orderDirectives, previous, directive)) {
// Note: dartfmt handles creating new lines between directives.
diff --git a/pkgs/code_builder/test/directive_test.dart b/pkgs/code_builder/test/directive_test.dart
index 31bd2a4..33485cd 100644
--- a/pkgs/code_builder/test/directive_test.dart
+++ b/pkgs/code_builder/test/directive_test.dart
@@ -73,6 +73,7 @@
library,
equalsDart(r'''
// ignore_for_file: no_leading_underscores_for_library_prefixes
+
import '../relative.dart' as _i1;
import 'package:foo/foo.dart' as _i2;
import 'package:foo/bar.dart' as _i3;
@@ -94,6 +95,7 @@
equalsDart(
r'''
// ignore_for_file: no_leading_underscores_for_library_prefixes
+
import 'dart:collection' as _i4;
import 'package:foo/bar.dart' as _i3;
diff --git a/pkgs/code_builder/test/specs/library_test.dart b/pkgs/code_builder/test/specs/library_test.dart
index 426f442..d8446c9 100644
--- a/pkgs/code_builder/test/specs/library_test.dart
+++ b/pkgs/code_builder/test/specs/library_test.dart
@@ -148,7 +148,7 @@
),
equalsDart(r'''
import 'dart:collection';
-
+
final test = LinkedHashMap();
''', DartEmitter()),
);
@@ -224,7 +224,7 @@
),
equalsDart(r'''
import 'dart:collection';
-
+
final test = LinkedHashMap();
''', DartEmitter(allocator: Allocator())),
);
@@ -249,8 +249,9 @@
),
equalsDart(r'''
// ignore_for_file: no_leading_underscores_for_library_prefixes
+
import 'dart:collection' as _i1;
-
+
final test = _i1.LinkedHashMap();
''', DartEmitter(allocator: Allocator.simplePrefixing())),
);
@@ -312,5 +313,41 @@
'''),
);
});
+
+ test(
+ 'should emit scoped lint ignores before library annotations, directives',
+ () {
+ expect(
+ Library(
+ (b) =>
+ b
+ ..annotations.add(
+ refer(
+ 'JS',
+ 'dart:js_interop',
+ ).call([literalString('vscode')]),
+ )
+ ..body.add(
+ Method(
+ (m) =>
+ m
+ ..name = 'foo'
+ ..external = true,
+ ),
+ ),
+ ),
+ equalsDart(r'''
+ // ignore_for_file: no_leading_underscores_for_library_prefixes
+
+ @_i1.JS('vscode')
+ library;
+
+ import 'dart:js_interop' as _i1;
+
+ external foo();
+ ''', DartEmitter.scoped()),
+ );
+ },
+ );
});
}