Skip to content

Commit

Permalink
重构生成routes.g.dart的逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
chen56 committed Apr 27, 2024
1 parent 107d753 commit ac1b95b
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 347 deletions.
2 changes: 1 addition & 1 deletion bake
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ flutter_web.clean() { _run flutter_web flutter clean;
rm -rf build; }
flutter_web.upgrade() { _run flutter_web flutter pub upgrade ; }
flutter_web.gen() { #_run root dart run packages/you_cli/bin/cli.dart gen all --dir notes/flutter_web/;
_run root dart run packages/you_cli/bin/cli.dart gen routes.g.dart --dir notes/flutter_web/ # --async
_run root dart run packages/you_cli/bin/cli.dart gen routes.g.dart --dir notes/flutter_web/ --async
}
flutter_web.dev() { flutter_web.dev_html ; }
flutter_web.build() { flutter_web.build_html ;}
Expand Down
320 changes: 160 additions & 160 deletions notes/flutter_web/lib/routes.g.dart

Large diffs are not rendered by default.

121 changes: 20 additions & 101 deletions packages/you_cli/bin/cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,10 @@ import 'package:code_builder/code_builder.dart';
import 'package:dart_style/dart_style.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:glob/glob.dart';
import 'package:path/path.dart' as path;
import 'package:you_cli/src/page.dart';
import 'package:you_cli/src/cli_core.dart';

import 'dart:io' as io;
import 'package:you_cli/src/yaml.dart';

// ignore: constant_identifier_names
const String _LIB_ROOT = "lib";
// ignore: constant_identifier_names
const String _NOTES_ROOT = "lib/routes/notes";
// ignore: constant_identifier_names
const String _PAGES_ROOT = "lib/routes";
// ignore: non_constant_identifier_names
final Glob _PAGE_GLOB = Glob("{**/page.dart,page.dart}");

main(List<String> args) async {
_log("Platform.script : ${io.Platform.script}");
Expand All @@ -36,34 +25,6 @@ main(List<String> args) async {
await runner.run(args);
}

class CliSystem {
CliSystem({required this.pkgDir})
: fs = pkgDir.fileSystem,
pubspec = Pubspec.parseFileSync(pkgDir.childFile("pubspec.yaml"))
;

Directory pkgDir;
FileSystem fs;
Pubspec pubspec;

RouteNode get routeRoot=> RouteNode.fromSync(routeDir);

Directory get routeDir => pkgDir.childDirectory(_PAGES_ROOT);

Directory get notesRouteDir => pkgDir.childDirectory(_NOTES_ROOT);

Directory get libDir => pkgDir.childDirectory(_LIB_ROOT);

Stream<PageLib> routes() {
var noteRootDir = pkgDir.childDirectory(_NOTES_ROOT);
return _PAGE_GLOB.listFileSystem(fs, root: noteRootDir.path).where((e) => e is File).map((e) => PageLib(
file: e as File,
pkgName: pubspec.name,
pkgDir: pkgDir,
));
}
}

// ignore: camel_case_types
class Cmd_gen_all extends Command {
// The [name] and [description] properties must be defined by every
Expand Down Expand Up @@ -126,25 +87,35 @@ class Cmd_gen_routes_g_dart extends Command {
// (context, print) async => await notes_i18n_.loadLibrary().then((_) => notes_i18n_.build(context, print))
// - async layout + page :
// notes_layout.layout((context, print) async => await notes_i18n_.loadLibrary().then((_) => notes_i18n_.build(context, print)))
String builderExpression(RouteNode node) {
code.Expression? builderExpression(RouteNode node) {
if (!node.page_dart.existsSync()) {
return '';
return null;
}
String builder = '${node.flatName}_.build';
code.Expression builder = code.refer("${node.flatName}_").property("build");
RouteNode? layout = node.findLayoutSync();
if (layout != null) {
builder = "${layout.flatName}__.layout($builder)";
builder = code.refer("${layout.flatName}__").property("layout").call([builder]);
}
if (async) {
// return '()async{ await ${node.flatName}_.loadLibrary(); await ${node.flatName}__.loadLibrary(); return ${node.flatName}__.layout(${node.flatName}_.build); }';
return '(context,print)async=> await ${node.flatName}_.loadLibrary().then((_) => $builder(context,print))';
return code.Method((b) => b
..modifier = MethodModifier.async
..body = code.Block.of(
[
code.refer("${node.flatName}_").property("loadLibrary").call([]).awaited.statement,
if(layout != null) code.refer("${layout.flatName}__").property("loadLibrary").call([]).awaited.statement,
builder.returned.statement,
],
)).closure;
} else {
return builder;
}
}

String _genRouteTreeCode(RouteNode node) {
String buildArg = !node.page_dart.existsSync() ? "" : ",${async ? "builderAsync" : "builder"}:${builderExpression(node)}";
code.Expression? builder=builderExpression(node);
String builderStr=builder==null?"":builder.accept(code.DartEmitter()).toString().split("\n").join();

String buildArg = !node.page_dart.existsSync() ? "" : ",${async ? "builderAsync" : "builder"}:$builderStr";
String padding = "".padLeft(node.level, ' ');
if (node.children.isEmpty) {
return '''${padding}To("${node.dir.basename}" $buildArg) ''';
Expand All @@ -166,7 +137,7 @@ ${node.children.map((child) => _genRouteTreeCode(child)).map((e) => "$e,").join(
throw AssertionError("【--dir $dir】 not exists");
}

CliSystem cli = CliSystem(pkgDir: fs.directory(dir));
YouCli cli = YouCli(projectDir: fs.directory(dir));
var rootRoute = RouteNode.fromSync(cli.routeDir);
Iterable<RouteNode> pageDirs = rootRoute.toList();

Expand Down Expand Up @@ -244,64 +215,12 @@ $newRoutes
""";

await cli.pkgDir.childFile("lib/routes.g.dart").writeAsString(allCode);
await cli.projectDir.childFile("lib/routes.g.dart").writeAsString(allCode);
// 暂时不格式化,因为要保持变量名后的padding,对齐变量更好看
// file.writeAsString(_fmt.format(toCode));
}
}

class PageLib {
final FileSystem fs;
final Directory routesRootDir;
final Directory libDir;
final String pkgName;
final File file;
final Directory pkgDir;

PageLib({
required this.file,
required this.pkgName,
required this.pkgDir,
}) : fs = file.fileSystem,
libDir = pkgDir.childDirectory(_LIB_ROOT),
routesRootDir = pkgDir.childDirectory(_PAGES_ROOT);

String get noteRoutePath {
String result = path.dirname(path.relative(file.path, from: routesRootDir.path));
return result == "." ? "/" : path.join("/", result);
}

// String get noteName => path.basenameWithoutExtension(file.path);
String get packageImportUri => "package:$pkgName/${path.relative(file.path, from: libDir.path)}";

String get asset => "${path.relative(file.parent.path, from: pkgDir.path)}/";

/// note name平整化,可作为变量名:
/// lib/routes/1.a/b/page.dart ---> a_b
String get flatName {
String dir = noteRoutePath;
if (dir == "/") {
return "root";
}
var names = dir.split(path.separator).where((e) => e.isNotEmpty);
return names
.map((e) => e
// ignore: unnecessary_string_escapes
.replaceAll(RegExp("^\\d+\."), "") // 1.z.about -> note_note-self
.replaceAll(".", "_")
.replaceAll("-", "_")
.replaceAll("&", "_")
.replaceAll("*", "_")
.replaceAll("*", "_")
.replaceAll("@", "_"))
.join("_");
}

File get noteConfFile => fs.file(file.parent.childFile("page.json"));

File get noteGenConfFile => fs.file(file.parent.childFile("page.g.json"));
}

_log(Object? o) {
// ignore: avoid_print
print("${DateTime.now()} - $o");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
import 'package:file/file.dart';
import 'package:path/path.dart' as path;
import 'package:you_cli/src/yaml.dart';

// ignore: non_constant_identifier_names
// final Glob _PAGE_GLOB = Glob("{**/page.dart,page.dart}");

class YouCli {
YouCli({required this.projectDir}) : fs = projectDir.fileSystem;

final Directory projectDir;
final FileSystem fs;
Pubspec? _pubspec;
RouteNode? _rootRoute;

Directory get routeDir => projectDir.childDirectory("lib/routes");

Directory get notesRouteDir => projectDir.childDirectory("lib/routes/notes");

Directory get libDir => projectDir.childDirectory("lib");

File get pubspecYamlFile => projectDir.childFile("pubspec.yaml");

Pubspec get pubspec => _pubspec ??= Pubspec.parseFileSync(pubspecYamlFile);

RouteNode get rootRoute => _rootRoute ??= RouteNode.fromSync(routeDir);
}

class RouteNode {
List<RouteNode> children;
Expand All @@ -15,6 +40,9 @@ class RouteNode {
int get level => isRoot ? 0 : _parent.level + 1;

static RouteNode fromSync(Directory dir) {
if (!dir.existsSync()) {
return RouteNode(dir: dir, children: []);
}
var children = dir.listSync(recursive: false).whereType<Directory>().map((e) => fromSync(e));
return RouteNode(dir: dir, children: children.toList());
}
Expand Down
73 changes: 73 additions & 0 deletions packages/you_cli/test/bin/cli_commands_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import 'package:checks/checks.dart';
import 'package:code_builder/code_builder.dart';
import 'package:file/memory.dart';
import 'package:test/test.dart';
import 'package:you_cli/src/cli_core.dart';

import '../../bin/cli.dart';

void main() {
late MemoryFileSystem fs;
late YouCli cli;

setUp(() {
fs = MemoryFileSystem();
cli = YouCli(projectDir: fs.directory("/app"));

// YouCli需要
fs.file("/app/pubspec.yaml")
..createSync(recursive: true)
..writeAsString("""name: app""");
});
group("Cmd_gen_routes_g_dart", () {
void checkBuilder(String? expected, {required bool async}) {
Cmd_gen_routes_g_dart cmd = Cmd_gen_routes_g_dart.libMode(fs: fs, async: async, dir: fs.directory("/app/lib/routes"));
var result = cmd.builderExpression(cli.rootRoute);
if (expected == null) {
check(result).isNull();
} else {
check(cmd.builderExpression(cli.rootRoute)!.accept(DartEmitter()).toString().split("\n").join()).equals(expected);
}
}

test('builderExpression: no page + no layout', () {
checkBuilder(async: false, null);
checkBuilder(async: true, null);
});

test('builderExpression: page + no layout', () {
fs.file("/app/lib/routes/page.dart").createSync(recursive: true);

checkBuilder(async: false, "root_.build");
checkBuilder(async: true, "() async { await root_.loadLibrary();return root_.build; } ");
});

test('builderExpression: page + layout', () {
fs.file("/app/lib/routes/page.dart").createSync(recursive: true);
fs.file("/app/lib/routes/layout.dart").createSync(recursive: true);

checkBuilder(async: false, "root__.layout(root_.build)");
checkBuilder(async: true, "() async { await root_.loadLibrary(); await root__.loadLibrary();return root__.layout(root_.build); } ");
});
});
}

Expression genRouteExpression(RouteNode rootPage) {
final $To = refer('To', 'package:you_flutter/router.dart');
return $To.newInstance([
literalString(rootPage.dir.basename)
], {
"builderAsync": refer(rootPage.flatName).property('build'),
// "children": literalList(rootPage.children.map((e)=>createRoutes(e))),
"children": literalList(rootPage.children.map((e) => genRouteExpression(e)).toList()),
});
}

String genRouteString(RouteNode rootPage) {
if (rootPage.children.isEmpty) {
return '''To("${rootPage.dir.basename}", builderAsync: ${rootPage.flatName}.build)''';
}
return '''To("${rootPage.dir.basename}", builderAsync: ${rootPage.flatName}.build, children:[
${rootPage.children.map((e) => genRouteString(e)).join(",")}
])''';
}
25 changes: 25 additions & 0 deletions packages/you_cli/test/bin/cli_core_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:checks/checks.dart';
import 'package:file/memory.dart';
import 'package:test/test.dart';
import 'package:you_cli/src/cli_core.dart';

void main() {
group("RouteNode", () {
test('create from directory', () {
late MemoryFileSystem fs = MemoryFileSystem();
fs.directory("/note/lib/routes/notes/page_1").createSync(recursive: true);
fs.directory("/note/lib/routes/notes/page_1/page_1_1").createSync(recursive: true);
fs.directory("/note/lib/routes/notes/page_1/page_1_2").createSync(recursive: true);

YouCli cli = YouCli(projectDir: fs.directory("/note"));
check(cli.rootRoute.toList().map((e) => e.routePath)).deepEquals([
"/",
"/notes",
"/notes/page_1",
"/notes/page_1/page_1_1",
"/notes/page_1/page_1_2",
]);
});
});

}
Loading

0 comments on commit ac1b95b

Please sign in to comment.