diff --git a/tools/brick_generator/README.md b/tools/brick_generator/README.md index cc5567f..6858b68 100644 --- a/tools/brick_generator/README.md +++ b/tools/brick_generator/README.md @@ -1,6 +1,38 @@ -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 +project_name: projet_name + +path_to_brickify: lib/feature_brick_test_folder + +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 +``` + +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 -``` \ No newline at end of file +dart tools/brick_generator/bin/brick_generator.dart ./apps/wyatt_feature_brick +``` + +# TODO +- Work on bool variables +- Add several variables (here only one is possible) \ No newline at end of file diff --git a/tools/brick_generator/analysis_options.yaml b/tools/brick_generator/analysis_options.yaml index dee8927..e1caf22 100644 --- a/tools/brick_generator/analysis_options.yaml +++ b/tools/brick_generator/analysis_options.yaml @@ -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 diff --git a/tools/brick_generator/bin/brick_generator.dart b/tools/brick_generator/bin/brick_generator.dart index 058d5fc..b55f891 100644 --- a/tools/brick_generator/bin/brick_generator.dart +++ b/tools/brick_generator/bin/brick_generator.dart @@ -1,102 +1,141 @@ import 'dart:io'; import 'package:brick_generator/shell.dart'; +import 'package:brick_generator/yaml_reader.dart'; import 'package:path/path.dart' as path; +import 'package:yaml/yaml.dart'; + +// Keys +enum Syntax { + 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 Syntax(this.mapKey, this.id); +} + +// Other Keys +const _projectNameKey = 'project_name'; +const _brickNameKey = 'brick_name'; +const _pathToBrickifyKey = 'path_to_brickify'; +const _syntaxKey = 'syntax'; // 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 main(List 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 option = + YamlReader.readYamlFile(path.join(projectPath, _yamlFileName)); + final syntaxMap = option[_syntaxKey] as YamlMap; + stdout.writeln('🍺 get options $syntaxMap'); - final _androidPath = path.join(_targetPath, 'android'); - final _androidKotlinPath = - path.join(_androidPath, 'app', 'src', 'main', 'kotlin'); - final _orgPath = path.join(_androidKotlinPath, 'com'); - - // Remove Previously Generated Files - final targetDir = Directory(_targetPath); - if (targetDir.existsSync()) { - await targetDir.delete(recursive: true); - } - - // 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() - .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}}', - ), + for (final syntax in Syntax.values) { + if (syntaxMap[syntax.mapKey] == null) { + throw ArgumentError( + 'Yaml file is not conform : ${syntax.toString()} is missing', ); - final fileSegments = file.path.split('/').sublist(2); - if (fileSegments.contains(_projectSnakeName)) { - final newPathSegment = fileSegments.join('/').replaceAll( - _projectSnakeName, - '{{#snakeCase}}{{project_name}}{{/snakeCase}}', + } + } + + final projectName = option[_projectNameKey] as String; + final brickName = option[_brickNameKey] as String; + + // Define paths + final sourcePath = + path.join(projectPath, option[_pathToBrickifyKey] as String); + final targetPath = path.join(_bricks, 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() + .map((_) async { + var file = _; + + try { + var contents = await file.readAsString(); + + // Transform all values in variables + for (final syntax in Syntax.values) { + contents = contents.replaceAll( + syntaxMap[syntax.mapKey] as String, + '{{#${syntax.id}}}{{$projectName}}{{/${syntax.id}}}', + ); + } + + file = await file.writeAsString(contents); + + // Rename file if needed + final filePath = file.path; + if (filePath.contains(syntaxMap[Syntax.snakeCase.mapKey] as String)) { + try { + var pathList = filePath.split(Platform.pathSeparator).sublist(3); + pathList = pathList + .map( + (segment) => segment.replaceAll( + syntaxMap[Syntax.snakeCase.mapKey] as String, + '{{$projectName.snakeCase()}}', + ), + ) + .toList(); + + final newPath = path.join( + filePath + .split(Platform.pathSeparator) + .sublist(0, 3) + .join(Platform.pathSeparator), + pathList.join(Platform.pathSeparator), ); - final newPath = path.join(_targetPath, newPathSegment); - File(newPath).createSync(recursive: true); - file.renameSync(newPath); - Directory(file.parent.path).deleteSync(recursive: true); + + File(newPath).createSync(recursive: true); + file.renameSync(newPath); + Directory(file.parent.path).deleteSync(recursive: true); + } catch (_) { + stdout.writeln('❌ ${_.toString()}'); + } + } + } catch (_) { + stdout.writeln('❌ ${_.toString()}'); } - } 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); + }), + ); + } catch (_) { + stdout.writeln('❌ ${_.toString()}'); + } } diff --git a/tools/brick_generator/lib/yaml_reader.dart b/tools/brick_generator/lib/yaml_reader.dart new file mode 100644 index 0000000..a7bdf15 --- /dev/null +++ b/tools/brick_generator/lib/yaml_reader.dart @@ -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; +} diff --git a/tools/brick_generator/pubspec.yaml b/tools/brick_generator/pubspec.yaml index 65468d7..3ecdbc3 100644 --- a/tools/brick_generator/pubspec.yaml +++ b/tools/brick_generator/pubspec.yaml @@ -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