Skip to content

Instantly share code, notes, and snippets.

@rodydavis
Created September 19, 2020 23:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rodydavis/682fd2a40a69708d419a4c478d3b3529 to your computer and use it in GitHub Desktop.
Save rodydavis/682fd2a40a69708d419a4c478d3b3529 to your computer and use it in GitHub Desktop.
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