feat: add generator

This commit is contained in:
Hugo Pointcheval 2022-07-28 11:06:12 +02:00
parent a2ef01dcad
commit 4f86d6dbbd
Signed by: hugo
GPG Key ID: A9E8E9615379254F
8 changed files with 216 additions and 0 deletions

6
tools/brick_generator/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# Files and directories created by pub.
.dart_tool/
.packages
# Conventional directory for build output.
build/

View File

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

View File

@ -0,0 +1,6 @@
A sample command-line application with an entrypoint in `bin/`, library code
in `lib/`.
```sh
dart /bin/brick_generator.dart wyatt_clean_code wyatt_clean_code wyatt-clean-code "Wyatt Demo" app.wyatt.io
```

View File

@ -0,0 +1,30 @@
# 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

View File

@ -0,0 +1,6 @@
package {{#dotCase}}{{org_name}}{{/dotCase}}.{{#snakeCase}}{{project_name}}{{/snakeCase}}
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -0,0 +1,102 @@
import 'dart:io';
import 'package:brick_generator/shell.dart';
import 'package:path/path.dart' as path;
// Constants
final _apps = 'apps';
final _bricks = 'bricks';
final _staticDir = path.join('tools', 'brick_generator', 'assets');
Future<void> main(List<String> arguments) async {
final _appName = arguments[0];
final _projectSnakeName = arguments[1];
final _projectParamName = arguments[2];
final _projectTitleName = arguments[3];
final _orgName = arguments[4];
final _sourcePath = path.join(_apps, _appName);
final _targetPath = path.join(_bricks, _appName, '__brick__');
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<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,52 @@
import 'dart:io';
class Shell {
static Future<void> cp(String source, String destination) {
return _Cmd.run('cp', ['-Rf', source, destination]);
}
static Future<void> mkdir(String destination) {
return _Cmd.run('mkdir', ['-p', destination]);
}
}
class _Cmd {
static Future<ProcessResult> run(
String cmd,
List<String> args, {
bool throwOnError = true,
String? processWorkingDir,
}) async {
final result = await Process.run(cmd, args,
workingDirectory: processWorkingDir, runInShell: true);
if (throwOnError) {
_throwIfProcessFailed(result, cmd, args);
}
return result;
}
static void _throwIfProcessFailed(
ProcessResult pr,
String process,
List<String> args,
) {
if (pr.exitCode != 0) {
final values = {
'Standard out': pr.stdout.toString().trim(),
'Standard error': pr.stderr.toString().trim()
}..removeWhere((k, v) => v.isEmpty);
String message;
if (values.isEmpty) {
message = 'Unknown error';
} else if (values.length == 1) {
message = values.values.single;
} else {
message = values.entries.map((e) => '${e.key}\n${e.value}').join('\n');
}
throw ProcessException(process, args, message, pr.exitCode);
}
}
}

View File

@ -0,0 +1,11 @@
name: brick_generator
description: A template generator from real app.
version: 1.0.0
publish_to: none
environment:
sdk: '>=2.17.0 <3.0.0'
dependencies:
path: ^1.8.2