Created
September 19, 2020 23:31
-
-
Save rodydavis/682fd2a40a69708d419a4c478d3b3529 to your computer and use it in GitHub Desktop.
Flutter File Analyzer in Dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:convert'; | |
import 'package:analyzer/dart/analysis/utilities.dart'; | |
// import 'package:analyzer/dart/ast/syntactic_entity.dart'; | |
import 'package:analyzer/dart/ast/ast.dart'; | |
import 'package:analyzer/src/dart/ast/ast.dart'; | |
const kTestFlutterData = ''' | |
import 'package:flutter/material.dart'; | |
enum MyEnum { one, type, three } | |
const int kGlobalField = 1.0; | |
/// This is a doc comment | |
class MyScreen extends StatelessWidget { | |
const MyScreen(this.position, {Key key, this.myField = 1}) : super(key: key); | |
const MyScreen.alt(this.position, {Key key}) : this.myField = 0, super(key: key); | |
static const String routeName = '/my_route'; | |
final int myField; | |
final int position; | |
// This is a normal comment | |
Map<String, dynamic> toJson() { | |
return {}; | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Container( | |
child: Center( | |
child: Text('Hello World'), | |
), | |
); | |
} | |
} | |
void myGlobalMethod() { | |
} | |
class Simple { | |
String value; | |
} | |
'''; | |
Map<String, dynamic> parseFlutterSource(String source) { | |
assert(source != null && source.isNotEmpty); | |
assert(source.contains('package:flutter/material.dart')); | |
final output = parseString( | |
content: source, | |
path: null, | |
); | |
final root = output.unit.root; | |
final file = DartFile.fromNode(root); | |
print('$file'); | |
return file.toJson(); | |
} | |
class DartFile { | |
DartFile(); | |
factory DartFile.fromNode(AstNode root) { | |
final base = DartFile(); | |
for (final node in root.childEntities.whereType<ImportDirectiveImpl>()) { | |
final ImportDirectiveImpl _node = node; | |
final _url = _node.uri.stringValue; | |
base.imports.add(_url); | |
} | |
for (final node | |
in root.childEntities.whereType<TopLevelVariableDeclarationImpl>()) { | |
base.fields.add(DartField.fromTopLevelNode(node)); | |
} | |
for (final node | |
in root.childEntities.whereType<FunctionDeclarationImpl>()) { | |
base.methods.add(DartMethod.fromTopLevelNode(node)); | |
} | |
for (final node in root.childEntities.whereType<ClassDeclarationImpl>()) { | |
final ClassDeclarationImpl _node = node; | |
bool isWidget = false; | |
final _extends = _node.extendsClause.toString(); | |
if (_extends.contains('StatelessWidget') || | |
_extends.contains('StatefulWidget')) { | |
isWidget = true; | |
} | |
// Check for Build Method second | |
if (isWidget) { | |
base.classes.add(FlutterClass.fromNode(_node, base)); | |
} else { | |
base.classes.add(DartClass.fromNode(_node, base)); | |
} | |
} | |
for (final node in root.childEntities.whereType<EnumDeclarationImpl>()) { | |
final EnumDeclarationImpl _node = node; | |
base.enums.add(DartEnum.fromNode(_node)); | |
} | |
return base; | |
} | |
final List<String> imports = []; | |
final List<DartClass> classes = []; | |
final List<DartEnum> enums = []; | |
final List<DartField> fields = []; | |
final List<DartMethod> methods = []; | |
Map<String, dynamic> toJson() { | |
return { | |
'name': null, | |
'imports': imports, | |
'classes': classes, | |
'enums': enums, | |
'fields': fields, | |
'methods': methods, | |
}; | |
} | |
@override | |
String toString() { | |
JsonEncoder encoder = new JsonEncoder.withIndent(' '); | |
return encoder.convert(toJson()); | |
} | |
} | |
class DartClass { | |
DartClass(); | |
String name; | |
final List<DartConstructor> constructors = []; | |
final List<DartField> fields = []; | |
final List<DartMethod> methods = []; | |
factory DartClass.fromNode(ClassDeclarationImpl root, DartFile parent) { | |
final base = DartClass(); | |
base.name = root.name.toString(); | |
for (final item in root.childEntities.whereType<FieldDeclarationImpl>()) { | |
base.fields.add(DartField.fromNode(item, base)); | |
} | |
for (final item | |
in root.childEntities.whereType<ConstructorDeclarationImpl>()) { | |
base.constructors.add(DartConstructor.fromNode(item, base)); | |
} | |
for (final item in root.childEntities.whereType<MethodDeclarationImpl>()) { | |
base.methods.add(DartMethod.fromNode(item, base)); | |
} | |
return base; | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'name': name, | |
'fields': fields, | |
'constructors': constructors, | |
'methods': methods, | |
}; | |
} | |
} | |
class DartConstructor { | |
String name; | |
List<DartProperty> properties = []; | |
DartConstructor.fromNode(ConstructorDeclarationImpl root, DartClass parent) { | |
name = root.toString(); | |
for (final node in root.childEntities) { | |
if (node is SimpleIdentifierImpl) { | |
name = node.name; | |
} | |
if (node is DeclaredSimpleIdentifier) { | |
name = node.name; | |
} | |
if (node is FormalParameterListImpl) { | |
for (final child in node.childEntities) { | |
if (child is DefaultFormalParameterImpl) { | |
properties.add(DartProperty.fromNode(child, parent)); | |
} | |
} | |
} | |
} | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'name': name, | |
'properties': properties, | |
}; | |
} | |
} | |
class DartProperty { | |
String name; | |
String type; | |
DartProperty.fromNode(DefaultFormalParameterImpl root, DartClass parent) { | |
name = root.toString(); | |
for (final node in root.childEntities) { | |
if (node is SimpleFormalParameterImpl) { | |
for (final child in node.childEntities) { | |
if (child is DeclaredSimpleIdentifier) { | |
name = child.toString(); | |
} | |
if (child is TypeNameImpl) { | |
type = child.toString(); | |
} | |
} | |
} | |
if (node is FieldFormalParameterImpl) { | |
for (final child in node.childEntities) { | |
if (child is SimpleIdentifierImpl) { | |
name = child.toString(); | |
} | |
} | |
for (final field in parent.fields) { | |
if (field.name == name) { | |
type = field.type; | |
} | |
} | |
} | |
} | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'name': name, | |
'type': type, | |
}; | |
} | |
} | |
class DartField { | |
String name; | |
String type; | |
DartField.fromNode(FieldDeclarationImpl root, DartClass parent) { | |
for (final node in root.childEntities) { | |
if (node is VariableDeclarationListImpl) { | |
_process(node); | |
} | |
} | |
} | |
DartField.fromTopLevelNode(TopLevelVariableDeclarationImpl root) { | |
for (final node in root.childEntities) { | |
if (node is VariableDeclarationListImpl) { | |
_process(node); | |
} | |
} | |
} | |
void _process(VariableDeclarationListImpl node) { | |
for (final child in node.childEntities) { | |
if (child is TypeNameImpl) { | |
final TypeNameImpl _node = child; | |
type = _node.toString(); | |
} | |
if (child is VariableDeclarationImpl) { | |
name = child.name.toString(); | |
} | |
} | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'name': name, | |
'type': type, | |
}; | |
} | |
} | |
class DartMethod { | |
String name; | |
DartMethod.fromNode(MethodDeclarationImpl root, DartClass parent) { | |
for (final node in root.childEntities) { | |
if (node is DeclaredSimpleIdentifier) { | |
_process(node); | |
} | |
} | |
} | |
DartMethod.fromTopLevelNode(FunctionDeclarationImpl root) { | |
for (final node in root.childEntities) { | |
if (node is DeclaredSimpleIdentifier) { | |
_process(node); | |
} | |
} | |
} | |
void _process(DeclaredSimpleIdentifier node) { | |
name = node.toString(); | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'name': name, | |
}; | |
} | |
} | |
class DartEnum { | |
String name; | |
List<String> values; | |
DartEnum.fromNode(EnumDeclarationImpl root) { | |
name = root.name.toString(); | |
values = root.constants.map((e) => e.name.toString()).toList(); | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'name': name, | |
'values': values, | |
}; | |
} | |
} | |
class FlutterClass extends DartClass { | |
FlutterClass(); | |
FlutterTree tree; | |
factory FlutterClass.fromNode(ClassDeclarationImpl root, DartFile parent) { | |
final base = FlutterClass(); | |
final dartClass = DartClass.fromNode(root, parent); | |
base.name = dartClass.name; | |
base.fields.addAll(dartClass.fields); | |
base.constructors.addAll(dartClass.constructors); | |
base.methods.addAll(dartClass.methods); | |
for (final node in root.childEntities.whereType<MethodDeclarationImpl>()) { | |
final _name = DartMethod.fromNode(node, base); | |
if (_name.name == 'build') { | |
final _blocks = node.childEntities.whereType<BlockFunctionBodyImpl>(); | |
if (_blocks != null) { | |
base.tree = FlutterTree.fromNode(_blocks.first, base); | |
} | |
} | |
} | |
return base; | |
} | |
@override | |
Map<String, dynamic> toJson() { | |
return { | |
"name": this.name, | |
"fields": this.fields, | |
"constructors": this.constructors, | |
"methods": this.methods, | |
'tree': tree, | |
}; | |
} | |
} | |
class FlutterTree { | |
FlutterTree(); | |
String name; | |
FlutterTree.fromNode(BlockFunctionBodyImpl root, FlutterClass parent) { | |
// print(root.toString()); | |
name = root.toString(); | |
} | |
Map<String, dynamic> toJson() { | |
return { | |
'name': name, | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": null, | |
"imports": [ | |
"package:flutter/material.dart" | |
], | |
"classes": [ | |
{ | |
"name": "MyScreen", | |
"fields": [ | |
{ | |
"name": "routeName", | |
"type": "String" | |
}, | |
{ | |
"name": "myField", | |
"type": "int" | |
}, | |
{ | |
"name": "position", | |
"type": "int" | |
} | |
], | |
"constructors": [ | |
{ | |
"name": "MyScreen", | |
"properties": [ | |
{ | |
"name": "key", | |
"type": "Key" | |
}, | |
{ | |
"name": "myField", | |
"type": "int" | |
} | |
] | |
}, | |
{ | |
"name": "alt", | |
"properties": [ | |
{ | |
"name": "key", | |
"type": "Key" | |
} | |
] | |
} | |
], | |
"methods": [ | |
{ | |
"name": "toJson" | |
}, | |
{ | |
"name": "build" | |
} | |
], | |
"tree": { | |
"name": "{return Container(child: Center(child: Text('Hello World')));}" | |
} | |
}, | |
{ | |
"name": "Simple", | |
"fields": [ | |
{ | |
"name": "value", | |
"type": "String" | |
} | |
], | |
"name": "build" | |
} | |
], | |
"tree": { | |
"name": "{return Container(child: Center(child: Text('Hello World')));}" | |
} | |
}, | |
{ | |
"name": "Simple", | |
"fields": [ | |
{ | |
"name": "value", | |
"type": "String" | |
"constructors": [], | |
"methods": [] | |
} | |
], | |
"enums": [ | |
{ | |
"name": "MyEnum", | |
"values": [ | |
"one", | |
"type", | |
"three" | |
] | |
} | |
], | |
"fields": [ | |
{ | |
"name": "kGlobalField", | |
"type": "int" | |
} | |
], | |
"methods": [ | |
{ | |
"name": "myGlobalMethod" | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment