Flutter File Analyzer in Dart
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, | |
}; | |
} | |
} |
{ | |
"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