Skip to content

Commit

Permalink
[Release] Add initial capabilities (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
aherreDev committed May 26, 2022
1 parent f0d10e5 commit 7e86847
Show file tree
Hide file tree
Showing 15 changed files with 480 additions and 22 deletions.
Binary file added .DS_Store
Binary file not shown.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ doc/api/
*.js_
*.js.deps
*.js.map

lib/src/graphql

build.yaml.dev
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 1.0.0

- Initial version.
21 changes: 0 additions & 21 deletions LICENSE

This file was deleted.

40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
# graphql_types_builder
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->

TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.

## Features

TODO: List what your package can do. Maybe include images, gifs, or videos.

## Getting started

TODO: List prerequisites and provide or point to information on how to
start using the package.

## Usage

TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.

```dart
const like = 'sample';
```

## Additional information

TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.
30 changes: 30 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.

include: package:lints/recommended.yaml

# Uncomment the following section to specify additional rules.

# linter:
# rules:
# - camel_case_types

# analyzer:
# exclude:
# - path/to/excluded/files/**

# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints

# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options
18 changes: 18 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
targets:
graphql_types_builder:graphql_types_builder:
builders:
graphql_types_builder|graphqlTypesBuilder:
enabled: True
generate_for:
# TODO: Parse also queries and mutations
- lib/src/**/*schema.graphql

builders:
graphqlTypesBuilder:
import: 'package:graphql_types_builder/graphql_types_builder.dart'
builder_factories: ['graphqlTypesBuilder']
build_extensions:
.graphql:
- .graphql.dart
build_to: source
auto_apply: dependents
6 changes: 6 additions & 0 deletions lib/graphql_types_builder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
library graphql_types_builder;

import 'package:build/build.dart';
import 'package:graphql_types_builder/src/graphql_types_builder.dart';

Builder graphqlTypesBuilder(BuilderOptions options) => GraphqlTypesBuilder();
26 changes: 26 additions & 0 deletions lib/src/graphql_types_builder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:build/build.dart';

import 'services/graphql_parser_service.dart';

class GraphqlTypesBuilder implements Builder {
@override
Map<String, List<String>> get buildExtensions => {
'.graphql': ['.graphql.dart'],
};

@override
Future<void> build(BuildStep buildStep) async {
// Retrive the current matched assets
AssetId currentAssetId = buildStep.inputId;

// Create a new target ID
var copyAssetId = currentAssetId.changeExtension('.graphql.dart');
var assetContent = await buildStep.readAsString(currentAssetId);

// Generate the dart classes based on the graphql schema
String newAssetContent = GraphqlParserService().parse(assetContent);

// Write the new asset
await buildStep.writeAsString(copyAssetId, newAssetContent);
}
}
76 changes: 76 additions & 0 deletions lib/src/models/graphql_attribute.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
final _graphqlDefaultScalarTypes = ["ID", "String", "Boolean", "Int", "Float"];

final _isListRegex = RegExp(r'\[(\w*|\w*!)\]');

String _buildDartType(attributeType) {
bool isList = _isListRegex.hasMatch(attributeType);
if (isList) {
attributeType = attributeType.replaceAll(r'\W', '');
return 'List<${_parseScalarType(attributeType)}>';
}

return _parseScalarType(attributeType);
}

String _parseScalarType(attributeType) {
switch (attributeType) {
case 'ID':
return 'String';
case 'Boolean':
return 'bool';
case 'Int':
return 'int';
case 'Float':
return 'double';
default:
return attributeType;
}
}

class GraphqlAttribute {
String name;
bool isRequired;
String type;
String dartType;

GraphqlAttribute({required this.name, required this.isRequired, required this.type, required this.dartType});


factory GraphqlAttribute.fromJson(Map<String, dynamic> json) => GraphqlAttribute(
name: json['name'],
isRequired: json['isRequired'],
type: json['type'],
dartType: _buildDartType(json['type'])
);

// ? String should be like:
// attributeName: type!
// attributeName: type
factory GraphqlAttribute.fromString(String rawAttribute){
final List<String> attributeParts = rawAttribute.split(':');

final String attributeName = attributeParts.first.trim();
final bool attributeRquired = attributeParts.last.trim().endsWith('!');
final String attributeType = attributeParts.last.trim().replaceFirst('!', '');

return GraphqlAttribute(
name: attributeName,
isRequired: attributeRquired,
type: attributeType,
dartType: _buildDartType(attributeType),
);
}

get attributeSymbol => isRequired ? '' : '?';

String get attributeDartType => isRequired ? '' : '?';

bool get isScalar => _graphqlDefaultScalarTypes.contains(type);

bool get isList => dartType.startsWith('List<');

String get prettyType => _parseScalarType(type);

// ignore: unnecessary_string_interpolations
String get dartTypeWithoutList => '$dartType'.replaceAll('List<', '').replaceAll('>', '');
}
144 changes: 144 additions & 0 deletions lib/src/services/graphql_class_generator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import 'package:collection/collection.dart';
import 'package:graphql_types_builder/src/models/graphql_attribute.dart';

// ? Get type input | type
final typeInputRegex = RegExp(r'input [A-Z]');
final typeTypeRegex = RegExp(r'type [A-Z]');

// ? Get attributes
final attributesRegex = RegExp(r'\w{2,}?: [A-Z]\w*(!(?!.)|)');

// ? Get Name
final nameRegex = RegExp(r'(?<=(input|type) )(.*?)(?= \{)');

const String tab1 = '\t';
const String tab2 = '\t\t';
const String tab3 = '\t\t\t';


class GraphQLClassGenerator {
String rawGraphQLType = '';
bool identifyRequiredAttributes;

String? _name = '';
// ignore: unused_field
String? _type = '';
List<GraphqlAttribute> _attributes = [];


GraphQLClassGenerator(this.rawGraphQLType, {this.identifyRequiredAttributes = false});

String generate() {
_setUpClass();

return _writeClass();
}

void _setUpClass() {
_initializeType();
_initializeAttributes();
_initializeName();
}

void _initializeType() {
if(typeInputRegex.hasMatch(rawGraphQLType)) {
_type = 'input';
return;
} else if(typeTypeRegex.hasMatch(rawGraphQLType)) {
_type = 'type';
return;
}

throw Exception('Unknown type for: $rawGraphQLType');
}

void _initializeAttributes() {
List<RegExpMatch> matches = attributesRegex.allMatches(rawGraphQLType).toList();

List<String> rawAttributes = matches.map((match) => match.group(0)).whereNotNull().toList();

_attributes = rawAttributes.map((rawAttribute) => GraphqlAttribute.fromString(rawAttribute)).toList();
}

void _initializeName() {
_name = nameRegex.stringMatch(rawGraphQLType);
}

String _writeClass() {
final StringBuffer sb = StringBuffer();

sb.writeln('class $_name {');
sb.writeln(_writeAttributesAndConstructor());
sb.writeln('}');

return sb.toString();
}

String _writeAttributesAndConstructor() {
final String attributesString = _writeAttributes();
final String constructorString = _writeConstructor();
final String namedConstructorString = _writeNamedConstructor();

// TODO: Write `fromMap` contructor in another method and also write `toMap` method in another method and define the class contrsuctor.
final StringBuffer sb = StringBuffer();

sb.writeln(attributesString);
sb.writeln(constructorString);
sb.writeln(namedConstructorString);

return sb.toString();
}

String _writeAttributes() {
final StringBuffer sb = StringBuffer();

for (GraphqlAttribute attribute in _attributes) {
String attributeSymbol = identifyRequiredAttributes ? attribute.attributeSymbol : '?';

sb.writeln('$tab1 final ${attribute.prettyType}$attributeSymbol ${attribute.name};');
}

return sb.toString();
}

String _writeConstructor() {
final StringBuffer sb = StringBuffer();
sb.writeln('$tab1$_name({');

for (GraphqlAttribute attribute in _attributes) {
sb.writeln('$tab2 this.${attribute.name},');
}

sb.writeln('$tab1});');

return sb.toString();
}

// ? .fromMap constructor
String _writeNamedConstructor() {
final StringBuffer sb = StringBuffer();
sb.writeln('$tab1 factory $_name.fromMap(Map<String, dynamic> map) {');
sb.writeln('$tab2 return $_name(');

for (GraphqlAttribute attribute in _attributes) {
_writeAttributeInitilization(sb, attribute);
}

sb.writeln('$tab2);');
sb.writeln('$tab1}');

return sb.toString();
}

void _writeAttributeInitilization(StringBuffer sb, GraphqlAttribute attribute) {
if (attribute.isScalar) {
return sb.writeln('$tab3${attribute.name}: map["${attribute.name}"],');
}

if (attribute.isList) {
return sb.writeln('$tab3${attribute.name}: ${attribute.dartType}.from(map["${attribute.name}"]?.map((x) => ${attribute.dartTypeWithoutList}.fromMap(x))),');
}

sb.writeln('$tab3${attribute.name}: ${attribute.dartType}.fromMap(map["${attribute.name}"]),');
}
}
Loading

0 comments on commit 7e86847

Please sign in to comment.