Merge pull request 'work on brick generator dart script' (#3) from Feature/brick-generator into master

Reviewed-on: #3
This commit is contained in:
Malo Léon 2022-08-09 10:06:51 +00:00
commit 40f2377dd0
11 changed files with 446 additions and 125 deletions

126
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,126 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "wyatt_clean_code",
"cwd": "apps/wyatt_clean_code",
"request": "launch",
"type": "dart"
},
{
"name": "wyatt_clean_code (profile mode)",
"cwd": "apps/wyatt_clean_code",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "wyatt_clean_code (release mode)",
"cwd": "apps/wyatt_clean_code",
"request": "launch",
"type": "dart",
"flutterMode": "release"
},
{
"name": "brick_generator",
"cwd": "tools/brick_generator",
"request": "launch",
"type": "dart"
},
{
"name": "__brick__",
"cwd": "bricks/core_app_brick/__brick__",
"request": "launch",
"type": "dart"
},
{
"name": "__brick__ (profile mode)",
"cwd": "bricks/core_app_brick/__brick__",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "__brick__ (release mode)",
"cwd": "bricks/core_app_brick/__brick__",
"request": "launch",
"type": "dart",
"flutterMode": "release"
},
{
"name": "__brick__",
"cwd": "bricks/wyatt_clean_code/__brick__",
"request": "launch",
"type": "dart"
},
{
"name": "__brick__ (profile mode)",
"cwd": "bricks/wyatt_clean_code/__brick__",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "__brick__ (release mode)",
"cwd": "bricks/wyatt_clean_code/__brick__",
"request": "launch",
"type": "dart",
"flutterMode": "release"
},
{
"name": "hooks",
"cwd": "bricks/wyatt_clean_code/hooks",
"request": "launch",
"type": "dart"
},
{
"name": "__brick__",
"cwd": "bricks/wyatt_package/__brick__",
"request": "launch",
"type": "dart"
},
{
"name": "__brick__ (profile mode)",
"cwd": "bricks/wyatt_package/__brick__",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "__brick__ (release mode)",
"cwd": "bricks/wyatt_package/__brick__",
"request": "launch",
"type": "dart",
"flutterMode": "release"
},
{
"name": "hooks",
"cwd": "bricks/wyatt_package/hooks",
"request": "launch",
"type": "dart"
},
{
"name": "example",
"cwd": "bricks/wyatt_package/__brick__/example",
"request": "launch",
"type": "dart"
},
{
"name": "example (profile mode)",
"cwd": "bricks/wyatt_package/__brick__/example",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "example (release mode)",
"cwd": "bricks/wyatt_package/__brick__/example",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

View File

@ -1,6 +1,57 @@
A sample command-line application with an entrypoint in `bin/`, library code
A sample command-line application to generate which allows to generate the template of a brick from a project which compiles. with an entrypoint in `bin/`, library code
in `lib/`.
# How to use
- Add your app in `apps`.
- Add `brick_config.yaml` in you app folder and add this fields :
```yaml
brick_name: wyatt_feature_brick
path_to_brickify: lib/feature_brick_folder
variables:
feature_brick:
variable_name: feature_brick
type: string
syntax:
camel_case: featureBrick
constant_case: FEATURE_BRICK
dot_case: feature.brick
header_case: Feature-Brick
lower_case: feature brick
pascal_case: FeatureBrick
param_case: feature-brick
sentence_case: Feature brick
snake_case: feature_brick
title_case: Feature Brick
upper_case: FEATURE BRICK
description:
variable_name: description
type: string
syntax:
camel_case: description
constant_case: DESCRIPTION
dot_case: description
header_case: Description
lower_case: description
pascal_case: Description
param_case: description
sentence_case: Description
snake_case: description
title_case: Description
upper_case: DESCRIPTION
isBloc:
variable_name: bloc
type: bool
```
then run command with project path
```sh
dart /bin/brick_generator.dart wyatt_clean_code wyatt_clean_code wyatt-clean-code "Wyatt Demo" app.wyatt.io
```
dart tools/brick_generator/bin/brick_generator.dart ./apps/wyatt_feature_brick
```
# TODO
- Work on bool variables

View File

@ -1,30 +1 @@
# 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
include: package:wyatt_analysis/analysis_options.yaml

View File

@ -1,102 +1,133 @@
import 'dart:io';
import 'package:brick_generator/models/brick_config.dart';
import 'package:brick_generator/models/variable_string_syntax.dart';
import 'package:brick_generator/models/variable_type.dart';
import 'package:brick_generator/shell.dart';
import 'package:brick_generator/yaml_reader.dart';
import 'package:path/path.dart' as path;
// Constants
final _apps = 'apps';
final _bricks = 'bricks';
final _staticDir = path.join('tools', 'brick_generator', 'assets');
const _bricks = 'bricks';
const _brickFolderLabel = '__brick__';
const _yamlFileName = 'brick_config.yaml';
Future<void> main(List<String> arguments) async {
final _appName = arguments[0];
try {
if (arguments.length != 1) {
throw ArgumentError('Please entry exemple project path');
}
final _projectSnakeName = arguments[1];
final _projectParamName = arguments[2];
final _projectTitleName = arguments[3];
final _orgName = arguments[4];
final projectPath = arguments[0];
final _sourcePath = path.join(_apps, _appName);
final _targetPath = path.join(_bricks, _appName, '__brick__');
// Store options from yaml file
final configs =
YamlReader.readYamlFile(path.join(projectPath, _yamlFileName));
final brickConfig = BrickConfig.from(configs);
stdout.writeln('🍺 config retrieved');
final _androidPath = path.join(_targetPath, 'android');
final _androidKotlinPath =
path.join(_androidPath, 'app', 'src', 'main', 'kotlin');
final _orgPath = path.join(_androidKotlinPath, 'com');
brickConfig?.checkFormat();
// Remove Previously Generated Files
final targetDir = Directory(_targetPath);
if (targetDir.existsSync()) {
await targetDir.delete(recursive: true);
// Define paths
final sourcePath = path.join(
projectPath,
brickConfig!.pathToBrickify,
);
final targetPath = path.join(
_bricks,
brickConfig.brickName,
_brickFolderLabel,
);
stdout.writeln('🍺 path defined');
// Remove previously generated files
final targetDir = Directory(targetPath);
if (targetDir.existsSync()) {
await targetDir.delete(recursive: true);
}
// create target folder
await Shell.mkdir(targetPath);
// Copy project files
await Shell.cp(sourcePath, targetPath);
stdout.writeln('🍺 files copied');
// Convert values to variables
await Future.wait(
Directory(targetPath)
.listSync(recursive: true)
.whereType<File>()
.map((_) async {
var file = _;
var contents = await file.readAsString();
// Transform all values in variables
if (brickConfig.variables != null) {
for (final variable in brickConfig.variables!) {
if (variable?.type == VariabelType.string) {
for (final syntax in VariableStringSyntax.values) {
final toReplace = variable?.syntax?[syntax.mapKey] as String?;
if (toReplace != null) {
contents = contents.replaceAll(
toReplace,
'{{#${syntax.id}}}{{${variable?.name}}}{{/${syntax.id}}}',
);
}
}
}
}
}
// Replace content
file = await file.writeAsString(contents);
stdout.writeln('🍺 variables added');
// Rename file if needed
final filePath = file.path;
if (brickConfig.variables != null) {
for (final variable in brickConfig.variables!) {
if (variable?.type == VariabelType.string &&
variable?.syntax?[VariableStringSyntax.snakeCase.mapKey] !=
null) {
if (filePath.contains(
variable!.syntax![VariableStringSyntax.snakeCase.mapKey]
as String,
)) {
var pathList =
filePath.split(Platform.pathSeparator).sublist(3);
pathList = pathList
.map(
(segment) => segment.replaceAll(
variable.syntax![VariableStringSyntax.snakeCase.mapKey]
as String,
'{{${variable.name}.snakeCase()}}',
),
)
.toList();
final newPath = path.join(
filePath
.split(Platform.pathSeparator)
.sublist(0, 3)
.join(Platform.pathSeparator),
pathList.join(Platform.pathSeparator),
);
File(newPath).createSync(recursive: true);
file.renameSync(newPath);
Directory(file.parent.path).deleteSync(recursive: true);
}
}
}
}
stdout.writeln('🍺 folders and files renamed');
}),
);
stdout.writeln('🍺 done');
} catch (_) {
stdout.writeln('${_.toString()}');
}
// Copy Project Files
await Shell.cp(_sourcePath, _targetPath);
// Delete Android's Organization Folder Hierarchy
Directory(_orgPath).deleteSync(recursive: true);
// Convert Values to Variables
await Future.wait(
Directory(_targetPath)
.listSync(recursive: true)
.whereType<File>()
.map((_) async {
var file = _;
try {
if (path.extension(file.path) == '.dart') {
final contents = await file.readAsString();
file = await file.writeAsString(contents);
}
final contents = await file.readAsString();
file = await file.writeAsString(
contents
.replaceAll(
_projectSnakeName,
'{{#snakeCase}}{{project_name}}{{/snakeCase}}',
)
.replaceAll(
_projectParamName,
'{{#paramCase}}{{project_name}}{{/paramCase}}',
)
.replaceAll('A new Flutter project.', '{{{description}}}')
.replaceAll(
_projectTitleName,
'{{#titleCase}}{{project_name}}{{/titleCase}}',
)
.replaceAll(
_orgName,
path.isWithin(_androidPath, file.path)
? '{{#dotCase}}{{org_name}}{{/dotCase}}.{{#snakeCase}}{{project_name}}{{/snakeCase}}'
: '{{#dotCase}}{{org_name}}{{/dotCase}}.{{#paramCase}}{{project_name}}{{/paramCase}}',
),
);
final fileSegments = file.path.split('/').sublist(2);
if (fileSegments.contains(_projectSnakeName)) {
final newPathSegment = fileSegments.join('/').replaceAll(
_projectSnakeName,
'{{#snakeCase}}{{project_name}}{{/snakeCase}}',
);
final newPath = path.join(_targetPath, newPathSegment);
File(newPath).createSync(recursive: true);
file.renameSync(newPath);
Directory(file.parent.path).deleteSync(recursive: true);
}
} catch (_) {}
}),
);
final mainActivityKt = File(
path.join(
_androidKotlinPath,
'{{#pathCase}}{{org_name}}{{/pathCase}}',
'MainActivity.kt',
),
);
await Shell.mkdir(mainActivityKt.parent.path);
await Shell.cp(path.join(_staticDir, 'MainActivity.kt'), mainActivityKt.path);
}

View File

@ -0,0 +1,51 @@
import 'package:brick_generator/models/brick_variable.dart';
import 'package:yaml/yaml.dart';
const _brickNameKey = 'brick_name';
const _pathToBrickifyKey = 'path_to_brickify';
const _variablesKey = 'variables';
class BrickConfig {
String? brickName;
String? pathToBrickify;
List<BrickVariable?>? variables;
BrickConfig({
required this.brickName,
required this.pathToBrickify,
required this.variables,
});
BrickConfig._();
factory BrickConfig.parser() => BrickConfig._();
static BrickConfig? from(YamlMap? data) => data != null
? BrickConfig(
brickName: data[_brickNameKey] as String?,
pathToBrickify: data[_pathToBrickifyKey] as String?,
variables: (data[_variablesKey] as YamlMap?)
?.map<dynamic, BrickVariable?>(
(dynamic key, dynamic value) =>
MapEntry<dynamic, BrickVariable?>(
key,
BrickVariable.from(value as YamlMap?),
),
)
.values
.toList(),
)
: null;
void checkFormat() {
if (brickName == null || pathToBrickify == null) {
throw ArgumentError(
'Yaml file is not conform',
);
}
if (variables != null) {
for (final variable in variables!) {
variable?.checkFormat();
}
}
}
}

View File

@ -0,0 +1,38 @@
import 'package:brick_generator/models/variable_type.dart';
import 'package:yaml/yaml.dart';
const _variableNameKey = 'variable_name';
const _typeKey = 'type';
const _syntaxKey = 'syntax';
class BrickVariable {
String? name;
VariabelType? type;
YamlMap? syntax;
BrickVariable({
required this.name,
required this.type,
required this.syntax,
});
BrickVariable._();
factory BrickVariable.parser() => BrickVariable._();
static BrickVariable? from(YamlMap? data) => data != null
? BrickVariable(
name: data[_variableNameKey] as String?,
type: VariabelType.stringToEnum(data[_typeKey] as String?),
syntax: data[_syntaxKey] as YamlMap?,
)
: null;
void checkFormat() {
if (name == null || type == null) {
throw ArgumentError(
'Yaml file is not conform',
);
}
}
}

View File

@ -0,0 +1,18 @@
enum VariableStringSyntax {
camelCase('camel_case', 'camelCase'),
constantCase('constant_case', 'constantCase'),
dotCase('dot_case', 'dotCase'),
headerCase('header_case', 'headerCase'),
lowerCase('lower_case', 'lowerCase'),
pascalCase('pascal_case', 'pascalCase'),
paramCase('param_case', 'paramCase'),
sentenceCase('sentence_case', 'sentenceCase'),
snakeCase('snake_case', 'snakeCase'),
titleCase('title_case', 'titleCase'),
upperCase('upper_case', 'upperCase');
final String mapKey;
final String id;
const VariableStringSyntax(this.mapKey, this.id);
}

View File

@ -0,0 +1,16 @@
enum VariabelType {
none,
string,
bool;
static VariabelType stringToEnum(String? type) {
switch (type) {
case 'string':
return string;
case 'bool':
return bool;
default:
return none;
}
}
}

View File

@ -17,8 +17,12 @@ class _Cmd {
bool throwOnError = true,
String? processWorkingDir,
}) async {
final result = await Process.run(cmd, args,
workingDirectory: processWorkingDir, runInShell: true);
final result = await Process.run(
cmd,
args,
workingDirectory: processWorkingDir,
runInShell: true,
);
if (throwOnError) {
_throwIfProcessFailed(result, cmd, args);
@ -49,4 +53,4 @@ class _Cmd {
throw ProcessException(process, args, message, pr.exitCode);
}
}
}
}

View File

@ -0,0 +1,7 @@
import 'dart:io';
import 'package:yaml/yaml.dart';
class YamlReader {
static YamlMap readYamlFile(String path) =>
loadYaml(File(path).readAsStringSync()) as YamlMap;
}

View File

@ -5,7 +5,15 @@ version: 1.0.0
publish_to: none
environment:
sdk: '>=2.17.0 <3.0.0'
sdk: ">=2.17.0 <3.0.0"
dependencies:
path: ^1.8.2
yaml: ^3.1.1
dev_dependencies:
wyatt_analysis:
git:
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
ref: wyatt_analysis-v2.1.0
path: packages/wyatt_analysis