forked from lxm_flutter/json_config_generator
Initial release of the generator package.
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Files and directories created by pub
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
|
||||||
|
# Conventional directory for build outputs
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
doc/api/
|
||||||
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
- Initial release of the annotation package.
|
||||||
29
LICENSE
Normal file
29
LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, Diego Cardenas
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
221
README.md
Normal file
221
README.md
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
A generator to create config class from json files that support many environments.
|
||||||
|
|
||||||
|
# Motivation
|
||||||
|
|
||||||
|
If you use a json file to config your applications, perphaps you need to write a dart class that contain all variables you define with their data types. When you need to manage nested objects is more complicated. Even if you want to add or delete a value in your json, you need to modify your dart class.
|
||||||
|
|
||||||
|
If you want to manage environments you need to writte manually the file path for every enviroment you need.
|
||||||
|
|
||||||
|
[json_config_generator] wants to help you to have an easy process with the manage of config files and environments.
|
||||||
|
|
||||||
|
# Install
|
||||||
|
|
||||||
|
To use [json_config_generator], you will need [build_runner]/code-generator setup.\
|
||||||
|
First, install [build_runner] and [json_config_generator] by adding them to your `pubspec.yaml` file:
|
||||||
|
|
||||||
|
> pubspec.yaml
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
json_config_annotation: ^0.1.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
build_runner:
|
||||||
|
json_config_generator: ^0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
- [build_runner], the tool to run code-generators
|
||||||
|
- [json_config_generator], the code generator
|
||||||
|
- [json_config_annotation], a package containing annotations for [json_config_generator]
|
||||||
|
|
||||||
|
# How to use
|
||||||
|
|
||||||
|
To use this generator need to create an empty config dart class that begins with `$` using the annotation `Configuration` defining the environments that you want to have. Each environment has `name` and `path`, if have more that one, the generator create an `Enum` to environments. Also you can specify the name of that `Enum`. By default it's name is `Environment`. Also, you need to add some imports.
|
||||||
|
|
||||||
|
> config.dart
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:json_config_annotation/json_config_annotation.dart';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
part 'config.g.dart'; //{dart file name}.g.dart
|
||||||
|
|
||||||
|
@Configuration(
|
||||||
|
environmentEnumName: 'Env',
|
||||||
|
environments:[
|
||||||
|
Environment(name:'dev', path:'assets/config/dev.json'),
|
||||||
|
Environment(name:'prd', path:'assets/config/prd.json'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
class $Config{}
|
||||||
|
```
|
||||||
|
|
||||||
|
suposing that have the next json file
|
||||||
|
|
||||||
|
> dev.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"base_url": "https://example.com",
|
||||||
|
"custom_class": {
|
||||||
|
"value_1": "dfgdfgdfgwqrrqwrqwrqweqwe324523b252dghfdhd",
|
||||||
|
"value_2": "6Lez7aIaAAAAAN6qZG2343c252bv66b7yn5m8m6"
|
||||||
|
},
|
||||||
|
"int_value": 3,
|
||||||
|
"double_value": 3.5,
|
||||||
|
"boolean_value": true,
|
||||||
|
"string_list": ["hello", "world"],
|
||||||
|
"int_list": [1, 23, 5],
|
||||||
|
"bool_list": [false, true, true],
|
||||||
|
"custom_list": [
|
||||||
|
{
|
||||||
|
"value_1": "hello"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value_1": "world"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
the generator creates
|
||||||
|
|
||||||
|
> config.g.dart
|
||||||
|
|
||||||
|
```dart
|
||||||
|
enum Env { dev, prd }
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
Config._();
|
||||||
|
|
||||||
|
static final instance = Config._();
|
||||||
|
|
||||||
|
late String baseUrl;
|
||||||
|
|
||||||
|
late _CustomClass customClass;
|
||||||
|
|
||||||
|
late int intValue;
|
||||||
|
|
||||||
|
late double doubleValue;
|
||||||
|
|
||||||
|
late bool booleanValue;
|
||||||
|
|
||||||
|
late List<String> stringList;
|
||||||
|
|
||||||
|
late List<int> intList;
|
||||||
|
|
||||||
|
late List<bool> boolList;
|
||||||
|
|
||||||
|
late List<_CustomList> customList;
|
||||||
|
|
||||||
|
Future<void> init(Env env) async {
|
||||||
|
String path = '';
|
||||||
|
switch (env) {
|
||||||
|
case Env.dev:
|
||||||
|
path = 'assets/config/dev.json';
|
||||||
|
break;
|
||||||
|
case Env.prd:
|
||||||
|
path = 'assets/config/prd.json';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final jsonString = await rootBundle.loadString(path);
|
||||||
|
final config = json.decode(jsonString) as Map<String, dynamic>;
|
||||||
|
baseUrl = config['base_url'] as String;
|
||||||
|
customClass =
|
||||||
|
_CustomClass.fromJson(config['custom_class'] as Map<String, dynamic>);
|
||||||
|
intValue = config['int_value'] as int;
|
||||||
|
doubleValue = config['double_value'] as double;
|
||||||
|
booleanValue = config['boolean_value'] as bool;
|
||||||
|
stringList = (config['string_list'] as List).cast<String>();
|
||||||
|
intList = (config['int_list'] as List).cast<int>();
|
||||||
|
boolList = (config['bool_list'] as List).cast<bool>();
|
||||||
|
customList = _CustomList.listFromJson(config['custom_list'] as List);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomClass {
|
||||||
|
const _CustomClass({required this.value1, required this.value2});
|
||||||
|
|
||||||
|
factory _CustomClass.fromJson(Map<String, dynamic> customClass) =>
|
||||||
|
_CustomClass(
|
||||||
|
value1: customClass['value_1'] as String,
|
||||||
|
value2: customClass['value_2'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
final String value1;
|
||||||
|
|
||||||
|
final String value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomList {
|
||||||
|
const _CustomList({required this.value1});
|
||||||
|
|
||||||
|
factory _CustomList.fromJson(Map<String, dynamic> customList) => _CustomList(
|
||||||
|
value1: customList['value_1'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
final String value1;
|
||||||
|
|
||||||
|
static List<_CustomList> listFromJson(List data) =>
|
||||||
|
data.map((e) => _CustomList.fromJson(e as Map<String, dynamic>)).toList();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To use the generated configuration you need to call `init` method to initialize all values. Could be called in `main` defining the Environment that you want to use.
|
||||||
|
|
||||||
|
> main.dart
|
||||||
|
|
||||||
|
```dart
|
||||||
|
void main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await Config.instance.init(Env.dev);
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now can access to fields easy using the `instance`
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Config.instance.baseUrl;
|
||||||
|
Config.instance.customClass.value1;
|
||||||
|
```
|
||||||
|
|
||||||
|
# Supported data types
|
||||||
|
|
||||||
|
```dart
|
||||||
|
- String
|
||||||
|
- int
|
||||||
|
- double
|
||||||
|
- bool
|
||||||
|
- CustomClass
|
||||||
|
- List<String>
|
||||||
|
- List<int>
|
||||||
|
- List<double>
|
||||||
|
- List<bool>
|
||||||
|
- List<CustomClass>
|
||||||
|
```
|
||||||
|
|
||||||
|
Also can create nested classes!
|
||||||
|
|
||||||
|
**Important**
|
||||||
|
|
||||||
|
- Do not support nullable values
|
||||||
|
- Do not support dynamic values
|
||||||
|
|
||||||
|
# Run the generator
|
||||||
|
|
||||||
|
To run the generator use one of the next commands:
|
||||||
|
|
||||||
|
- `flutter pub run build_runner build`
|
||||||
|
- `dart pub run build_runner build`
|
||||||
|
|
||||||
|
## Considerations
|
||||||
|
|
||||||
|
Can also create a config with only one `Environment`. If that so, the `Enum` do not be created and when call `init` method, do not need to pass any argument.
|
||||||
|
|
||||||
|
It is important to known that the generator takes the first value in lists to known the type of the list, so be caferull
|
||||||
|
|
||||||
|
[json_config_generator]: https://pub.dev/packages/json_config_generator
|
||||||
|
[json_config_annotation]: https://pub.dev/packages/json_config_annotation
|
||||||
|
[build_runner]: https://pub.dev/packages/build_runner
|
||||||
9
build.yaml
Normal file
9
build.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
builders:
|
||||||
|
json_config_generator:
|
||||||
|
target: ":json_config_annotations"
|
||||||
|
import: "package:json_config_generator/json_config_generator.dart"
|
||||||
|
builder_factories: ["configBuilder"]
|
||||||
|
build_extensions: { ".dart": [".json_config_generator.g.part"] }
|
||||||
|
auto_apply: dependents
|
||||||
|
build_to: cache
|
||||||
|
applies_builders: ["source_gen|combining_builder"]
|
||||||
8
lib/json_config_generator.dart
Normal file
8
lib/json_config_generator.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'package:build/build.dart' show Builder, BuilderOptions;
|
||||||
|
import 'package:json_config_generator/src/generator.dart';
|
||||||
|
import 'package:source_gen/source_gen.dart';
|
||||||
|
|
||||||
|
Builder configBuilder(BuilderOptions options) {
|
||||||
|
return SharedPartBuilder(
|
||||||
|
const [JsonConfigGenerator()], 'json_config_generator');
|
||||||
|
}
|
||||||
296
lib/src/generator.dart
Normal file
296
lib/src/generator.dart
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
import 'package:analyzer/dart/constant/value.dart';
|
||||||
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
|
import 'package:build/build.dart';
|
||||||
|
import 'package:built_collection/built_collection.dart';
|
||||||
|
import 'package:json_config_generator/src/utils.dart';
|
||||||
|
import 'package:source_gen/source_gen.dart';
|
||||||
|
import 'package:json_config_annotation/json_config_annotation.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:recase/recase.dart';
|
||||||
|
import 'package:code_builder/code_builder.dart';
|
||||||
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
|
||||||
|
class JsonConfigGenerator extends GeneratorForAnnotation<Configuration> {
|
||||||
|
const JsonConfigGenerator();
|
||||||
|
|
||||||
|
@override
|
||||||
|
generateForAnnotatedElement(
|
||||||
|
Element element, ConstantReader annotation, BuildStep buildStep) async {
|
||||||
|
throwIf(
|
||||||
|
!element.displayName.startsWith('\$'), 'class name must start with \$',
|
||||||
|
element: element);
|
||||||
|
final className = element.displayName.replaceAll('\$', '');
|
||||||
|
final environmentEnumName =
|
||||||
|
annotation.read('environmentEnumName').stringValue;
|
||||||
|
final environments = annotation.read('environments').listValue;
|
||||||
|
throwIf(environments.isEmpty, 'environments could not be empty',
|
||||||
|
element: element);
|
||||||
|
final environmentMap = generateEnvironmentMap(environments, element);
|
||||||
|
final path = environmentMap.values.first;
|
||||||
|
final Map<String, dynamic> config = await getConfigMap(path);
|
||||||
|
if (environments.length == 1) {
|
||||||
|
return mainClassGenerator(className, config, configFile: path);
|
||||||
|
} else {
|
||||||
|
throwIf(environmentEnumName.isEmpty,
|
||||||
|
'environemntEnumName could not be empty');
|
||||||
|
final environmentEnum =
|
||||||
|
environmentEnumGenerator(environmentEnumName, environmentMap);
|
||||||
|
return stringConverter(environmentEnum) +
|
||||||
|
mainClassGenerator(className, config,
|
||||||
|
environmentName: environmentEnumName,
|
||||||
|
environmentMap: environmentMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getConfigMap(String path) async {
|
||||||
|
final jsonString = await File(path).readAsString();
|
||||||
|
throwIf(jsonString.isEmpty, 'file in path "$path" could not be empty');
|
||||||
|
Map<String, dynamic> map = await json.decode(jsonString);
|
||||||
|
throwIf(map.isEmpty, 'file in path "$path" do not have fields');
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> generateEnvironmentMap(
|
||||||
|
List<DartObject> environments, Element element) {
|
||||||
|
final map = Map<String, String>();
|
||||||
|
environments.forEach((env) {
|
||||||
|
final name = env.getField('name')?.toStringValue();
|
||||||
|
throwIf(name == null || name.trim().isEmpty,
|
||||||
|
'name could not be empty in $env',
|
||||||
|
element: element);
|
||||||
|
final path = env.getField('path')?.toStringValue();
|
||||||
|
throwIf(path == null || path.trim().isEmpty,
|
||||||
|
'path could not be empty in $env',
|
||||||
|
element: element);
|
||||||
|
throwIf(
|
||||||
|
map.containsKey(name),
|
||||||
|
'every environment need to have distinct name. '
|
||||||
|
'$name was already used',
|
||||||
|
element: element);
|
||||||
|
final fileName = path!.split('/').last;
|
||||||
|
throwIf(!fileName.contains('.json'),
|
||||||
|
'environment file "$fileName" must have extension ".json"',
|
||||||
|
element: element);
|
||||||
|
map[name!] = path;
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
Enum environmentEnumGenerator(String enumName, Map<String, String> map) {
|
||||||
|
final builder = EnumBuilder()
|
||||||
|
..name = enumName
|
||||||
|
..values =
|
||||||
|
ListBuilder(map.keys.map((e) => EnumValue((v) => v..name = e)));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
String mainClassGenerator(String name, Map<String, dynamic> config,
|
||||||
|
{String? environmentName,
|
||||||
|
Map<String, String>? environmentMap,
|
||||||
|
String? configFile}) {
|
||||||
|
String initMethodBody = '';
|
||||||
|
if (configFile != null) {
|
||||||
|
initMethodBody +=
|
||||||
|
'''
|
||||||
|
final jsonString = await rootBundle.loadString('$configFile');
|
||||||
|
final ${name.camelCase} = json.decode(jsonString) as Map<String, dynamic>;
|
||||||
|
''';
|
||||||
|
} else {
|
||||||
|
initMethodBody =
|
||||||
|
'''
|
||||||
|
String path = '';
|
||||||
|
switch(${environmentName!.camelCase}){
|
||||||
|
''';
|
||||||
|
environmentMap!.entries.forEach((entry) => initMethodBody +=
|
||||||
|
'''
|
||||||
|
case ${environmentName}.${entry.key}:
|
||||||
|
path = '${entry.value}';
|
||||||
|
break;
|
||||||
|
''');
|
||||||
|
initMethodBody +=
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
final jsonString = await rootBundle.loadString(path);
|
||||||
|
final ${name.camelCase} = json.decode(jsonString) as Map<String, dynamic>;
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
|
||||||
|
String subClasses = '';
|
||||||
|
config.forEach((key, value) {
|
||||||
|
final type = getType(key, value);
|
||||||
|
if (value is Map) {
|
||||||
|
subClasses += subClassesGenerator(key, value);
|
||||||
|
initMethodBody +=
|
||||||
|
'${key.camelCase}= _${key.pascalCase}.fromJson(${name.camelCase}[\'${key.snakeCase}\'] as Map<String,dynamic>);';
|
||||||
|
} else if (type == 'List<_${key.pascalCase}>') {
|
||||||
|
subClasses += subClassesGenerator(key, value);
|
||||||
|
initMethodBody +=
|
||||||
|
'${key.camelCase}= _${key.pascalCase}.listFromJson(${name.camelCase}[\'${key.snakeCase}\'] as List);';
|
||||||
|
} else if (type.contains('List')) {
|
||||||
|
final castType = type.replaceAll('List', '');
|
||||||
|
initMethodBody +=
|
||||||
|
'${key.camelCase}= (${name.camelCase}[\'${key.snakeCase}\'] as List).cast$castType();';
|
||||||
|
} else {
|
||||||
|
initMethodBody +=
|
||||||
|
'${key.camelCase}= ${name.camelCase}[\'${key.snakeCase}\'] as $type;';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final mainClass = Class(
|
||||||
|
(c) => c
|
||||||
|
..name = name
|
||||||
|
..constructors.addAll([Constructor((c) => c..name = '_')])
|
||||||
|
..fields.addAll([
|
||||||
|
Field((f) => f
|
||||||
|
..name = 'instance'
|
||||||
|
..static = true
|
||||||
|
..modifier = FieldModifier.final$
|
||||||
|
..assignment = Code('$name._()')),
|
||||||
|
...config
|
||||||
|
.map((key, value) {
|
||||||
|
return MapEntry(
|
||||||
|
key,
|
||||||
|
Field((f) => f
|
||||||
|
..name = key.camelCase
|
||||||
|
..late = true
|
||||||
|
..type = refer(getType(key, value))),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.values
|
||||||
|
.toList()
|
||||||
|
])
|
||||||
|
..methods.addAll(
|
||||||
|
[
|
||||||
|
Method((m) => m
|
||||||
|
..name = 'init'
|
||||||
|
..modifier = MethodModifier.async
|
||||||
|
..requiredParameters = ListBuilder([
|
||||||
|
if (environmentName != null && environmentMap != null)
|
||||||
|
Parameter((p) => p
|
||||||
|
..name = environmentName.camelCase
|
||||||
|
..type = refer(environmentName))
|
||||||
|
])
|
||||||
|
..body = Code(initMethodBody)
|
||||||
|
..returns = refer('Future<void>'))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return stringConverter(mainClass) + subClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
String subClassesGenerator(String name, dynamic values) {
|
||||||
|
Map<String, dynamic> data;
|
||||||
|
String subClasses = '';
|
||||||
|
bool isList = false;
|
||||||
|
if (getType(name, values) == 'List<_${name.pascalCase}>') {
|
||||||
|
data = (values as List).first;
|
||||||
|
isList = true;
|
||||||
|
} else if (values is Map<String, dynamic>) {
|
||||||
|
data = values;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
String factoryCode = '';
|
||||||
|
factoryCode += '_${name.pascalCase}(';
|
||||||
|
data.forEach((key, value) {
|
||||||
|
final type = getType(key, value);
|
||||||
|
if (value is Map) {
|
||||||
|
subClasses += subClassesGenerator(key, value);
|
||||||
|
factoryCode +=
|
||||||
|
'${key.camelCase}: _${key.pascalCase}.fromJson(${name.camelCase}[\'${key.snakeCase}\'] as Map<String,dynamic>),';
|
||||||
|
} else if (type == 'List<_${key.pascalCase}>') {
|
||||||
|
subClasses += subClassesGenerator(key, value);
|
||||||
|
factoryCode +=
|
||||||
|
'${key.camelCase}: _${key.pascalCase}.listFromJson(${name.camelCase}[\'${key.snakeCase}\'] as List),';
|
||||||
|
} else if (type.contains('List')) {
|
||||||
|
final castType = type.replaceAll('List', '');
|
||||||
|
factoryCode +=
|
||||||
|
'${key.camelCase}= (${name.camelCase}[\'${key.snakeCase}\'] as List).cast$castType();';
|
||||||
|
} else {
|
||||||
|
factoryCode +=
|
||||||
|
'${key.camelCase}: ${name.camelCase}[\'${key.snakeCase}\'] as $type,';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
factoryCode += ')';
|
||||||
|
final subclass = Class(
|
||||||
|
(c) => c
|
||||||
|
..name = '_${name.pascalCase}'
|
||||||
|
..fields.addAll(data
|
||||||
|
.map(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
key,
|
||||||
|
Field((f) => f
|
||||||
|
..name = key.camelCase
|
||||||
|
..type = refer(getType(key, value))
|
||||||
|
..modifier = FieldModifier.final$)),
|
||||||
|
)
|
||||||
|
.values
|
||||||
|
.toList())
|
||||||
|
..constructors.addAll(
|
||||||
|
[
|
||||||
|
Constructor((e) => e
|
||||||
|
..constant = true
|
||||||
|
..optionalParameters.addAll(data
|
||||||
|
.map(
|
||||||
|
(key, value) => MapEntry(
|
||||||
|
key,
|
||||||
|
Parameter((f) => f
|
||||||
|
..name = key.camelCase
|
||||||
|
..toThis = true
|
||||||
|
..named = true
|
||||||
|
..required = true),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.values
|
||||||
|
.toList())),
|
||||||
|
Constructor((e) => e
|
||||||
|
..factory = true
|
||||||
|
..name = 'fromJson'
|
||||||
|
..lambda = true
|
||||||
|
..requiredParameters.add(Parameter(
|
||||||
|
(p) => p
|
||||||
|
..type = refer('Map<String, dynamic>')
|
||||||
|
..name = name.camelCase,
|
||||||
|
))
|
||||||
|
..body = Code(factoryCode)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
..methods.addAll([
|
||||||
|
if (isList)
|
||||||
|
Method((m) => m
|
||||||
|
..static = true
|
||||||
|
..returns = refer('List<_${name.pascalCase}>')
|
||||||
|
..name = 'listFromJson'
|
||||||
|
..requiredParameters.add(
|
||||||
|
Parameter((p) => p
|
||||||
|
..name = 'data'
|
||||||
|
..type = refer('List')),
|
||||||
|
)
|
||||||
|
..lambda = true
|
||||||
|
..body = Code(
|
||||||
|
'data.map((e) => _${name.pascalCase}.fromJson(e as Map<String,dynamic>)).toList()'))
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
return stringConverter(subclass) + subClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getType(String key, dynamic value) {
|
||||||
|
if (value is String)
|
||||||
|
return 'String';
|
||||||
|
else if (value is bool)
|
||||||
|
return 'bool';
|
||||||
|
else if (value is int)
|
||||||
|
return 'int';
|
||||||
|
else if (value is double)
|
||||||
|
return 'double';
|
||||||
|
else if (value is List)
|
||||||
|
return 'List<${getType(key, value.first)}>';
|
||||||
|
else
|
||||||
|
return '_${key.pascalCase}';
|
||||||
|
}
|
||||||
|
|
||||||
|
String stringConverter(Spec obj) {
|
||||||
|
final emitter = DartEmitter(useNullSafetySyntax: true);
|
||||||
|
return DartFormatter().format(obj.accept(emitter).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
23
lib/src/utils.dart
Normal file
23
lib/src/utils.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
|
|
||||||
|
void throwIf(bool condition, String message, {Element? element}) {
|
||||||
|
if (condition) throwError(message, element: element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void throwError(String message, {Element? element}) {
|
||||||
|
throw InvalidGenerationSourceError(
|
||||||
|
message,
|
||||||
|
element: element,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidGenerationSourceError implements Exception {
|
||||||
|
final String message;
|
||||||
|
final Element? element;
|
||||||
|
|
||||||
|
const InvalidGenerationSourceError(this.message, {this.element});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'[ERROR] $message ${element != null ? 'in $element' : ''}';
|
||||||
|
}
|
||||||
271
pubspec.lock
Normal file
271
pubspec.lock
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
_fe_analyzer_shared:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _fe_analyzer_shared
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "27.0.0"
|
||||||
|
analyzer:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.0"
|
||||||
|
build:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: build
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
build_config:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: build_config
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
built_collection:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: built_collection
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
built_value:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_value
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "8.0.5"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
checked_yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: checked_yaml
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
cli_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_util
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
|
code_builder:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: code_builder
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.15.0"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
|
dart_style:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dart_style
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
glob:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: glob
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.1"
|
||||||
|
json_config_annotation:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: json_config_annotation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.10"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.7.0"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.0"
|
||||||
|
pedantic:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pedantic
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.0"
|
||||||
|
pub_semver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
pubspec_parse:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pubspec_parse
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
recase:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: recase
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
|
source_gen:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: source_gen
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.1"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.0"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
sdks:
|
||||||
|
dart: ">=2.14.0 <3.0.0"
|
||||||
20
pubspec.yaml
Normal file
20
pubspec.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: json_config_generator
|
||||||
|
description: Configuration generator from json files.
|
||||||
|
version: 0.1.0
|
||||||
|
homepage: https://dev.azure.com/fcorp-infraestructura-servicios-ti/Packages/_git/Flutter.ConfigGenerator
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
analyzer: ^2.4.0
|
||||||
|
build: ^2.1.1
|
||||||
|
dart_style: ^2.2.0
|
||||||
|
source_gen: ^1.1.1
|
||||||
|
code_builder: ^4.1.0
|
||||||
|
build_config: ^1.0.0
|
||||||
|
built_collection: ^5.1.1
|
||||||
|
json_config_annotation: ^0.1.0
|
||||||
|
recase: ^4.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
Reference in New Issue
Block a user