Skip to content

Commit

Permalink
flutter-supabase-macro-0.0.5 (#5)
Browse files Browse the repository at this point in the history
Add named parameter for each field of the class (see CHANGELOG.md)
  • Loading branch information
ThomasDevApps authored Oct 16, 2024
1 parent 63787e2 commit 2c420aa
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 81 deletions.
19 changes: 14 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
## 0.0.1
## 0.0.5 (https://github.com/ThomasDevApps/flutter_supabase_macro/pull/5)

Initial release :
- Creation of a `toJsonSupabase` which exclude the `primaryKey` from the `Map`
Add a named parameter for each field of the class.
For example, if class contain a field named `id` then `bool? removeId`
will be add as a named parameter for `toJsonSupabase`.

If `removeId` is not null and true then, `id` will not be add in the json.

## 0.0.4
## 0.0.4 (https://github.com/ThomasDevApps/flutter_supabase_macro/pull/4)

Only exclude `primaryKey` from the Map if :
- Can't be nullable then check that `!= null`
- The type is `String`, then check that the value `isNotEmpty`
- The type is `String`, then check that the value `isNotEmpty`


## 0.0.1 (https://github.com/ThomasDevApps/flutter_supabase_macro/pull/1)

Initial release :
- Creation of a `toJsonSupabase` which exclude the `primaryKey` from the `Map`
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ and the Flutter guide for
-->
# Flutter Supabase Macro

![testing workflow](https://github.com/ThomasDevApps/flutter_supabase_macro/actions/workflows/main.yml/badge.svg)

Package greatly inspired by `JsonCodable` (from Dart), makes it easy to create
a JSON format of a template for Supabase.

| Before | After |
|----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| Before | After |
|----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
| ![before](https://raw.githubusercontent.com/ThomasDevApps/flutter_supabase_macro/main/assets/before.png) | ![after](https://raw.githubusercontent.com/ThomasDevApps/flutter_supabase_macro/main/assets/after.png) |

- [What is a macro](#-what-is-a-macro)
Expand Down
Binary file modified assets/before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 5 additions & 6 deletions lib/flutter_supabase_macro.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:macros/macros.dart';
part 'src/extensions/code_extension.dart';
part 'src/extensions/iterable_extension.dart';
part 'src/extensions/named_type_annotation_extension.dart';
part 'src/extensions/string_extension.dart';
part 'src/extensions/type_declaration_extension.dart';
part 'src/mixins/shared.dart';
part 'src/mixins/to_json_supabase.dart';
Expand All @@ -21,6 +22,7 @@ macro class FlutterSupabaseMacro
implements ClassDeclarationsMacro, ClassDefinitionMacro {

/// Primary key to exclude from the `toJsonSupabase`.
@override
final String primaryKey;

const FlutterSupabaseMacro({this.primaryKey = 'id'});
Expand All @@ -43,14 +45,11 @@ macro class FlutterSupabaseMacro
ClassDeclaration clazz,
TypeDefinitionBuilder builder,
) async {
final introspectionData =
await _SharedIntrospectionData.build(builder, clazz);
await _buildToJsonSupabase(
clazz,
final introspectionData = await _SharedIntrospectionData.build(
builder,
introspectionData,
primaryKey,
clazz,
);
await _buildToJsonSupabase(clazz, builder, introspectionData);
}
}

10 changes: 10 additions & 0 deletions lib/src/extensions/string_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
part of '../../flutter_supabase_macro.dart';

extension _StringExtension on String {
/// Set the first character to upper case.
///
/// ```dart
/// 'test of the function'.firstLetterUpperCase(); // 'Test of the function'
/// ```
String _firstLetterToUpperCase() => "${this[0].toUpperCase()}${substring(1)}";
}
95 changes: 76 additions & 19 deletions lib/src/mixins/to_json_supabase.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// ignore_for_file: deprecated_member_use

part of '../../flutter_supabase_macro.dart';

mixin _ToJsonSupabase on _Shared {
String get primaryKey;

/// Declare the [_toJsonMethodName] method.
Future<void> _declareToJsonSupabase(
ClassDeclaration clazz,
Expand All @@ -10,13 +14,47 @@ mixin _ToJsonSupabase on _Shared {
// Check that no toJsonSupabase method exist
final checkNoToJson = await _checkNoToJson(builder, clazz);
if (!checkNoToJson) return;
final boolId = await builder.resolveIdentifier(_dartCore, 'bool');
final boolCode = NamedTypeAnnotationCode(name: boolId);
final fields = await builder.fieldsOf(clazz);
builder.declareInType(
DeclarationCode.fromParts(
[' external ', mapStringObject, ' $_toJsonMethodName();\n'],
),
DeclarationCode.fromParts([
' external ',
mapStringObject,
' $_toJsonMethodName(',
if (fields.isNotEmpty) '{\n',
if (fields.isNotEmpty) ..._createNamedParams(boolCode, fields),
if (fields.isNotEmpty) '\n }',
');\n'
]),
);
}

/// Create `List` of parts.
///
/// Example : [fields] contain one element named `firstField`, it will add :
/// ```dart
/// ' bool? removeFirstField,'
/// ```
List _createNamedParams(
NamedTypeAnnotationCode boolCode,
List<FieldDeclaration> fields,
) {
final list = [];
for (final field in fields) {
list.addAll([
' ',
boolCode,
'? ',
'remove',
field.identifier.name._firstLetterToUpperCase(),
',',
if (field != fields.last) '\n',
]);
}
return list;
}

/// Emits an error [Diagnostic] if there is an existing [_toJsonMethodName]
/// method on [clazz].
///
Expand Down Expand Up @@ -51,7 +89,6 @@ mixin _ToJsonSupabase on _Shared {
ClassDeclaration clazz,
TypeDefinitionBuilder typeBuilder,
_SharedIntrospectionData introspectionData,
String primaryKey,
) async {
// Get all methods of the class
final methods = await typeBuilder.methodsOf(clazz);
Expand Down Expand Up @@ -84,7 +121,6 @@ mixin _ToJsonSupabase on _Shared {
field,
builder,
introspectionData,
isPrimaryKey: field.identifier.name == primaryKey,
),
),
),
Expand All @@ -93,15 +129,33 @@ mixin _ToJsonSupabase on _Shared {
parts.add('return json;\n }');
builder.augment(
FunctionBodyCode.fromParts(parts),
docComments: CommentCode.fromParts([
' /// Map representing the model in json format for Supabase.\n',
' ///\n',
' /// The primary key [${fields.first.identifier.name}]',
' is exclude from the map if empty.'
]),
docComments: _createDocumentationForMethod(fields),
);
}

/// Create the documentation for [_toJsonMethodName] method
/// according with [fields].
CommentCode _createDocumentationForMethod(List<FieldDeclaration> fields) {
return CommentCode.fromParts([
' /// Map representing the model in json format for Supabase.\n',
' ///\n',
' /// The primary key [${fields.first.identifier.name}]',
' is exclude from the map if empty.\n',
' ///\n',
' /// ',
...fields.map((f) {
return [
'[remove',
f.identifier.name._firstLetterToUpperCase(),
']',
if (f != fields.last) ', '
].join();
}),
' can be set for remove field\n'
' /// from the json.'
]);
}

/// Returns void if [toJsonSupabase] not exist.
///
/// Otherwise it will check that [toJsonSupabase] is valid with [_checkValidToJson].
Expand Down Expand Up @@ -131,9 +185,7 @@ mixin _ToJsonSupabase on _Shared {
final methodIsMap = await methodReturnType.isExactly(
introspectionData.jsonMapType,
);
if (method.namedParameters.isNotEmpty ||
method.positionalParameters.isNotEmpty ||
!methodIsMap) {
if (!methodIsMap) {
builder.report(
Diagnostic(
DiagnosticMessage(
Expand Down Expand Up @@ -241,13 +293,17 @@ mixin _ToJsonSupabase on _Shared {
Future<Code> addEntryForField(
FieldDeclaration field,
DefinitionBuilder builder,
_SharedIntrospectionData introspectionData, {
bool isPrimaryKey = false,
}) async {
_SharedIntrospectionData introspectionData,
) async {
final parts = <Object>[];
final isPrimaryKey = field.identifier.name == primaryKey;
final doNullCheck = field.type.isNullable;
final needCondition = doNullCheck || isPrimaryKey;
final fieldName = field.identifier.name._firstLetterToUpperCase();
// Begin the definition of the condition
parts.addAll([
'if (remove$fieldName==null || !remove$fieldName) {\n ',
]);
if (needCondition) {
parts.addAll(['if (']);
}
Expand All @@ -265,7 +321,7 @@ mixin _ToJsonSupabase on _Shared {
}
}
// Close definition of the condition and open it
if (needCondition) parts.add(') {\n ');
if (needCondition) parts.add(') {\n ');
// Add the field in the json
parts.addAll([
"json[r'",
Expand All @@ -284,8 +340,9 @@ mixin _ToJsonSupabase on _Shared {
]);
// Close the condition
if (needCondition) {
parts.add('}\n ');
parts.add(' }\n ');
}
parts.add('}\n ');
return RawCode.fromParts(parts);
}

Expand Down
9 changes: 8 additions & 1 deletion lib/src/models/shared_introspection_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ final class _SharedIntrospectionData {
/// A [Code] representation of the type `dynamic`.
final NamedTypeAnnotationCode dynamicCode;

/// A [Code] representation of the type [bool].
final NamedTypeAnnotationCode boolCode;

/// A [Code] representation of the type [String].
final NamedTypeAnnotationCode stringCode;

Expand All @@ -41,18 +44,20 @@ final class _SharedIntrospectionData {
required this.mapEntry,
required this.dynamicCode,
required this.stringCode,
required this.boolCode,
required this.superclass,
});

static Future<_SharedIntrospectionData> build(
DeclarationPhaseIntrospector builder, ClassDeclaration clazz) async {
// Resolve identifiers
final (list, map, mapEntry, dynamic, string) = await (
final (list, map, mapEntry, dynamic, string, bool) = await (
builder.resolveIdentifier(_dartCore, 'List'),
builder.resolveIdentifier(_dartCore, 'Map'),
builder.resolveIdentifier(_dartCore, 'MapEntry'),
builder.resolveIdentifier(_dartCore, 'dynamic'),
builder.resolveIdentifier(_dartCore, 'String'),
builder.resolveIdentifier(_dartCore, 'bool'),
).wait;

// Get all NamedTypeAnnotationCode
Expand All @@ -65,6 +70,7 @@ final class _SharedIntrospectionData {
dynamicCode,
]);
final stringCode = NamedTypeAnnotationCode(name: string);
final boolCode = NamedTypeAnnotationCode(name: bool);

// Get the class's superclass (if exist)
final superclass = clazz.superclass;
Expand All @@ -88,6 +94,7 @@ final class _SharedIntrospectionData {
mapEntry: mapEntry,
dynamicCode: dynamicCode,
stringCode: stringCode,
boolCode: boolCode,
superclass: superclassDecl as ClassDeclaration?,
);
}
Expand Down
36 changes: 1 addition & 35 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: flutter_supabase_macro
description: "A new Flutter project."
version: 0.0.4
version: 0.0.5
homepage:

environment:
Expand All @@ -17,39 +17,5 @@ dev_dependencies:
sdk: flutter
flutter_lints: ^5.0.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter packages.
flutter:

# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images

# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package
Loading

0 comments on commit 2c420aa

Please sign in to comment.