Skip to content

Commit 3395821

Browse files
committed
chore(flutter): ignore private widgets
1 parent 3ad5268 commit 3395821

File tree

5 files changed

+344
-241
lines changed

5 files changed

+344
-241
lines changed

flutter/packages/measure_build/lib/builder.dart

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,16 @@ import 'package:build/build.dart';
22
import 'package:msr_build/src/builders/widget_analyzer_builder.dart';
33

44
Builder widgetAnalyzerBuilder(BuilderOptions options) {
5-
// Read widgets_output_path from options, default to 'lib/msr_widgets.g.dart'
6-
final outputPath = options.config['widgets_output_path'] as String? ?? 'lib/msr_widgets.g.dart';
5+
final outputPath = options.config['widgets_output_path'] as String? ?? kDefaultOutputPath;
76

8-
// Read scan_directories from options, default to ['lib']
97
final scanDirs = options.config['scan_directories'];
108
final List<String> scanDirectories;
119
if (scanDirs is List) {
1210
scanDirectories = scanDirs.cast<String>();
1311
} else {
14-
scanDirectories = ['lib'];
12+
scanDirectories = kDefaultScanDirectories;
1513
}
16-
17-
// Extract the path relative to lib/ for buildExtensions
18-
// If outputPath is 'lib/src/msr/msr_widgets.g.dart', we need 'src/msr/msr_widgets.g.dart'
19-
final relativePath = outputPath.startsWith('lib/')
20-
? outputPath.substring(4) // Remove 'lib/' prefix
21-
: outputPath;
14+
final relativePath = outputPath.startsWith(kLibPrefix) ? outputPath.substring(kLibPrefixLength) : outputPath;
2215

2316
return WidgetAnalyzerBuilder(
2417
buildExtensions: {
@@ -27,4 +20,4 @@ Builder widgetAnalyzerBuilder(BuilderOptions options) {
2720
outputPath: outputPath,
2821
scanDirectories: scanDirectories,
2922
);
30-
}
23+
}

flutter/packages/measure_build/lib/src/builders/widget_analyzer_builder.dart

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,40 @@ import 'package:analyzer/dart/element/type.dart';
88
import 'package:build/build.dart';
99
import 'package:glob/glob.dart';
1010

11+
const String kDefaultOutputPath = 'lib/msr_widgets.g.dart';
12+
const List<String> kDefaultScanDirectories = ['lib'];
13+
const String kLibPrefix = 'lib/';
14+
const int kLibPrefixLength = 4; // Length of 'lib/'
15+
16+
const String _kSyntheticInputSuffix = r'lib/$lib$';
17+
const String _kGeneratedFilePattern = '.g.dart';
18+
const String _kDartFileGlob = '/**.dart';
19+
20+
const Set<String> _kExcludedWidgets = {
21+
'Widget',
22+
'StatelessWidget',
23+
'StatefulWidget',
24+
};
25+
26+
const Set<String> _kFlutterBaseWidgetTypes = {
27+
'Widget',
28+
'StatelessWidget',
29+
'StatefulWidget',
30+
'InheritedWidget',
31+
'RenderObjectWidget',
32+
'ProxyWidget',
33+
};
34+
35+
const String _kGeneratedMapName = 'msrWidgetsForLayoutSnapshot';
36+
const String _kGeneratedFileHeader = '// GENERATED CODE - DO NOT MODIFY BY HAND';
37+
const String _kGeneratedFileIgnoreDirective = '// ignore_for_file: unused_import, implementation_imports';
38+
39+
const String _kPackageUriPrefix = 'package:';
40+
const String _kDartUriPrefix = 'dart:';
41+
const String _kFileUriPrefix = 'file:';
42+
const String _kLibPathSeparator = '/lib/';
43+
const String _kFlutterLibraryIdentifier = 'flutter';
44+
1145
/// Finds all Flutter widgets used in a project and writes them
1246
/// to a dart file as a map of widget name to class name.
1347
///
@@ -53,48 +87,47 @@ class WidgetAnalyzerBuilder extends Builder {
5387

5488
WidgetAnalyzerBuilder({
5589
required this.buildExtensions,
56-
this.outputPath = 'lib/msr_widgets.g.dart',
57-
this.scanDirectories = const ['lib'],
90+
this.outputPath = kDefaultOutputPath,
91+
this.scanDirectories = kDefaultScanDirectories,
5892
});
5993

6094
@override
6195
FutureOr<void> build(BuildStep buildStep) async {
6296
// Only run on lib/$lib$ synthetic input
63-
if (!buildStep.inputId.path.endsWith(r'lib/$lib$')) {
97+
if (!buildStep.inputId.path.endsWith(_kSyntheticInputSuffix)) {
6498
return;
6599
}
66100

67101
final allWidgets = <String, InterfaceElement>{};
68102

69103
// Scan all configured directories
70104
for (final directory in scanDirectories) {
71-
final dartFiles = Glob('$directory/**.dart');
105+
final dartFiles = Glob('$directory$_kDartFileGlob');
72106
await for (final input in buildStep.findAssets(dartFiles)) {
73-
if (input.path.contains('.g.dart') || input.path == outputPath) {
74-
continue;
75-
}
76-
try {
77-
final resolver = buildStep.resolver;
78-
if (!await resolver.isLibrary(input)) {
107+
if (input.path.contains(_kGeneratedFilePattern) || input.path == outputPath) {
79108
continue;
80109
}
81-
final library = await resolver.libraryFor(input);
82-
for (final classElement in library.classes) {
83-
if (_isWidgetClass(classElement)) {
84-
final name = classElement.name;
85-
if (name != null) {
86-
allWidgets[name] = classElement;
110+
try {
111+
final resolver = buildStep.resolver;
112+
if (!await resolver.isLibrary(input)) {
113+
continue;
114+
}
115+
final library = await resolver.libraryFor(input);
116+
for (final classElement in library.classes) {
117+
if (_isWidgetClass(classElement)) {
118+
final name = classElement.name;
119+
if (name != null && !_isPrivateWidget(name)) {
120+
allWidgets[name] = classElement;
121+
}
87122
}
88123
}
124+
await _scanLibraryForWidgets(library, allWidgets);
125+
} catch (e) {
126+
log.warning('Error processing ${input.path}: $e');
89127
}
90-
await _scanLibraryForWidgets(library, allWidgets);
91-
} catch (e) {
92-
log.warning('Error processing ${input.path}: $e');
93128
}
94129
}
95-
}
96-
allWidgets
97-
.removeWhere((name, element) => name == 'Widget' || name == 'StatelessWidget' || name == 'StatefulWidget');
130+
allWidgets.removeWhere((name, element) => _kExcludedWidgets.contains(name));
98131
final outputId = AssetId(buildStep.inputId.package, outputPath);
99132
final dartCode = _generateDartFile(allWidgets, buildStep.inputId.package);
100133
await buildStep.writeAsString(outputId, dartCode);
@@ -155,7 +188,7 @@ class WidgetAnalyzerBuilder extends Builder {
155188
if (element is InterfaceElement) {
156189
if (_checkExtendsWidgetRecursively(element, <InterfaceElement>{})) {
157190
final name = element.name;
158-
if (name != null) {
191+
if (name != null && !_isPrivateWidget(name)) {
159192
widgets[name] = element;
160193
}
161194
}
@@ -176,17 +209,17 @@ class WidgetAnalyzerBuilder extends Builder {
176209
for (final element in widgets.values) {
177210
final libraryUri = element.library.firstFragment.source.uri.toString();
178211

179-
if (libraryUri.startsWith('package:') || libraryUri.startsWith('dart:')) {
212+
if (libraryUri.startsWith(_kPackageUriPrefix) || libraryUri.startsWith(_kDartUriPrefix)) {
180213
imports.add("import '$libraryUri';");
181-
} else if (libraryUri.startsWith('file:') && libraryUri.contains('/lib/')) {
182-
final libPath = libraryUri.split('/lib/').last;
183-
imports.add("import 'package:$packageName/$libPath';");
214+
} else if (libraryUri.startsWith(_kFileUriPrefix) && libraryUri.contains(_kLibPathSeparator)) {
215+
final libPath = libraryUri.split(_kLibPathSeparator).last;
216+
imports.add("import '$_kPackageUriPrefix$packageName/$libPath';");
184217
}
185218
}
186219

187220
// Write header comment
188-
buffer.writeln('// GENERATED CODE - DO NOT MODIFY BY HAND');
189-
buffer.writeln('// ignore_for_file: unused_import, implementation_imports');
221+
buffer.writeln(_kGeneratedFileHeader);
222+
buffer.writeln(_kGeneratedFileIgnoreDirective);
190223
buffer.writeln();
191224

192225
// Write imports
@@ -197,7 +230,7 @@ class WidgetAnalyzerBuilder extends Builder {
197230
buffer.writeln();
198231

199232
// Write the map
200-
buffer.writeln('const Map<Type, String> msrWidgetsForLayoutSnapshot = {');
233+
buffer.writeln('const Map<Type, String> $_kGeneratedMapName = {');
201234

202235
final sortedWidgets = widgets.keys.toList()..sort();
203236
for (final widgetName in sortedWidgets) {
@@ -217,7 +250,7 @@ class WidgetAnalyzerBuilder extends Builder {
217250
final libraryElement = element.library;
218251
final library = libraryElement.firstFragment.source.uri.toString();
219252

220-
if (element.name == 'Widget' && library.contains('flutter')) {
253+
if (element.name == 'Widget' && library.contains(_kFlutterLibraryIdentifier)) {
221254
return true;
222255
}
223256

@@ -238,6 +271,10 @@ class WidgetAnalyzerBuilder extends Builder {
238271

239272
return false;
240273
}
274+
275+
bool _isPrivateWidget(String name) {
276+
return name.startsWith('_');
277+
}
241278
}
242279

243280
/// AST visitor for method bodies to find widget instantiations
@@ -251,7 +288,7 @@ class _MethodBodyVisitor extends RecursiveAstVisitor<void> {
251288
final element = type.element;
252289
if (element is InterfaceElement) {
253290
final name = element.name;
254-
if (name != null) {
291+
if (name != null && !name.startsWith('_')) {
255292
widgets[name] = element;
256293
}
257294
}
@@ -276,16 +313,11 @@ class _MethodBodyVisitor extends RecursiveAstVisitor<void> {
276313
final libraryElement = element.library;
277314
final library = libraryElement.firstFragment.source.uri.toString();
278315

279-
if (element.name == 'Widget' && library.contains('flutter')) {
316+
if (element.name == 'Widget' && library.contains(_kFlutterLibraryIdentifier)) {
280317
return true;
281318
}
282319

283-
if ((element.name == 'StatelessWidget' ||
284-
element.name == 'StatefulWidget' ||
285-
element.name == 'InheritedWidget' ||
286-
element.name == 'RenderObjectWidget' ||
287-
element.name == 'ProxyWidget') &&
288-
library.contains('flutter')) {
320+
if (_kFlutterBaseWidgetTypes.contains(element.name) && library.contains(_kFlutterLibraryIdentifier)) {
289321
return true;
290322
}
291323

0 commit comments

Comments
 (0)