Merge pull request 'work on brick generator dart script' (#3) from Feature/brick-generator into master
Reviewed-on: #3
This commit is contained in:
commit
40f2377dd0
126
.vscode/launch.json
vendored
Normal file
126
.vscode/launch.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
51
tools/brick_generator/lib/models/brick_config.dart
Normal file
51
tools/brick_generator/lib/models/brick_config.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
tools/brick_generator/lib/models/brick_variable.dart
Normal file
38
tools/brick_generator/lib/models/brick_variable.dart
Normal 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',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
18
tools/brick_generator/lib/models/variable_string_syntax.dart
Normal file
18
tools/brick_generator/lib/models/variable_string_syntax.dart
Normal 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);
|
||||
}
|
16
tools/brick_generator/lib/models/variable_type.dart
Normal file
16
tools/brick_generator/lib/models/variable_type.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
tools/brick_generator/lib/yaml_reader.dart
Normal file
7
tools/brick_generator/lib/yaml_reader.dart
Normal 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;
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user