Skip to content

Commit

Permalink
完美生成routes.g.dart sync代码,待async
Browse files Browse the repository at this point in the history
  • Loading branch information
chen56 committed Apr 29, 2024
1 parent c89f4ca commit 9d9e67b
Show file tree
Hide file tree
Showing 8 changed files with 404 additions and 300 deletions.
7 changes: 4 additions & 3 deletions notes/flutter_web/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import 'package:you_flutter/router.dart';
import 'package:you_note_dart/note.dart';
import 'package:shared_preferences/shared_preferences.dart';

final routes=Routes();

final YouRouter router = YouRouter(
root: root,
initial: routes.notes_widgets_specific_widgets_button_overview.toUri(),
root: routes.root,
initial: routes.routes_root.toUri(),
navigatorKey: GlobalKey<NavigatorState>(debugLabel: "mainNavigator"),
);

class App extends StatelessWidget {
final NoteSystem noteSystem;
final SharedPreferences sharedPreferences;
Expand Down
419 changes: 211 additions & 208 deletions notes/flutter_web/lib/routes.g.dart

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions notes/flutter_web/lib/routes/layout.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:you_flutter/router.dart';

/// ref: [PageLayoutBuilder]
Widget layout(BuildContext context, ToUri uri, PageBuilder builder) {
// ignore: unnecessary_type_check
assert(layout is PageLayoutBuilder);
return RootLayout(
uri: uri,
builder: builder,
);
}

@immutable
final class RootLayout extends StatelessWidget {
final PageBuilder builder;
final ToUri uri;

const RootLayout({super.key, required this.uri, required this.builder});

@override
Widget build(BuildContext context) {
var child = builder(context, uri);


NavigationRailDestination rail({required String title, required IconData icon}) {
return NavigationRailDestination(
icon: Tooltip(message: title, child: Icon(icon)),
label: Text(title),
);
}

return Scaffold(
primary: true,
// content...
appBar: AppBar(toolbarHeight: 38, title: const Text("widget.title"), actions: [
IconButton(iconSize: 24, icon: const Icon(Icons.color_lens_outlined), onPressed: () {}),
IconButton(iconSize: 24, icon: const Icon(Icons.settings), onPressed: () {}),
if (kDebugMode) const Text("debug模式"),
]),
floatingActionButton: FloatingActionButton(onPressed: () {}, tooltip: 'Increment', child: const Icon(Icons.add)),
body: SafeArea(
child: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
NavigationRail(onDestinationSelected: (index) {}, minWidth: 24, minExtendedWidth: 24, selectedIndex: null, groupAlignment: -1, labelType: NavigationRailLabelType.none, destinations: <NavigationRailDestination>[
rail(title: "文件夹", icon: Icons.folder_outlined),
]),
Drawer(
width: 200,
child: ListView(scrollDirection: Axis.vertical, children: [
const Divider(),
ListTile(title: const Text('根页面'), subtitle: const Text("xxx"), onTap: () {}),
const Divider(),
])),
Expanded(child: child),
]),
),
);
}
}
18 changes: 18 additions & 0 deletions notes/flutter_web/lib/routes/page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// ignore_for_file: non_constant_identifier_names,camel_case_types
import 'package:flutter/material.dart';
import 'package:you_flutter/router.dart';

RootPage build(BuildContext context, ToUri uri) {
return RootPage(uri: uri);
}

class RootPage extends StatelessWidget {
final ToUri uri;

const RootPage({required this.uri, super.key});

@override
Widget build(BuildContext context) {
return const Text("/ root page");
}
}
126 changes: 66 additions & 60 deletions packages/you_cli/bin/cli.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:args/command_runner.dart';
import 'package:code_builder/code_builder.dart' as code;
import 'package:code_builder/code_builder.dart';

import 'package:dart_style/dart_style.dart';
Expand All @@ -10,6 +9,8 @@ import 'package:you_cli/src/cli_core.dart';

import 'dart:io' as io;

import 'package:you_cli/src/code_builder_ext.dart';

main(List<String> args) async {
_log("Platform.script : ${io.Platform.script}");
_log("Directory.current: ${io.Directory.current}");
Expand Down Expand Up @@ -81,7 +82,7 @@ class Cmd_gen_routes_g_dart extends Command {
@override
final description = "gen routes.g.dart .";
final FileSystem fs;
final Allocator _allocator=Allocator();
final CleanPrefixedAllocator _allocator = CleanPrefixedAllocator();
YouCli? _cli;

YouCli get cli => _cli != null ? _cli! : _cli = YouCli(projectDir: dir);
Expand All @@ -92,23 +93,23 @@ class Cmd_gen_routes_g_dart extends Command {
// - async layout + page :
// notes_layout.layout((context, print) async => await notes_i18n_.loadLibrary().then((_) => notes_i18n_.build(context, print)))
@Deprecated("已废弃,待完成重构后删除")
code.Expression? builderExpression(RouteNode node) {
Expression? builderExpression(RouteNode node) {
if (!node.file_page_dart.existsSync()) {
return null;
}
code.Expression builder = code.refer("${node.flatName}_").property("build");
Expression builder = refer("${node.flatName}_").property("build");
RouteNode? layout = node.findLayoutSync();
if (layout != null) {
builder = refer("${layout.flatName}__").property("layout2").call([builder]);
}

if (async) {
return code.Method((b) => b
return Method((b) => b
..modifier = MethodModifier.async
..body = code.Block.of(
..body = Block.of(
[
code.refer("${node.flatName}_").property("loadLibrary").call([]).awaited.statement,
if (layout != null) code.refer("${layout.flatName}__").property("loadLibrary").call([]).awaited.statement,
refer("${node.flatName}_").property("loadLibrary").call([]).awaited.statement,
if (layout != null) refer("${layout.flatName}__").property("loadLibrary").call([]).awaited.statement,
builder.returned.statement,
],
)).closure;
Expand All @@ -117,20 +118,16 @@ class Cmd_gen_routes_g_dart extends Command {
}
}

// 没用code_builder是因为它会格式化换行,很多换行,很乱。
Future<String> _genRouteTreeCode(RouteNode node) async {
String buildArg = !node.file_page_dart.existsSync() ? "" : ",builder:${node.flatName}_.build";
String layoutArg = !node.file_layout_dart.existsSync() ? "" : ",layout:${node.flatName}__.layout";
String padding = "".padLeft(node.level, ' ');
var toType = node.findToType();
if (node.children.isEmpty) {
return '''$padding${_allocator.allocate(toType)}${async ? ".lazy" : ""}("${node.dir.basename}" $layoutArg $buildArg) ''';
}
List<String> children=await Future.wait(node.children.map((child) async => await _genRouteTreeCode(child)));

return '''$padding${_allocator.allocate(toType)}${async ? ".lazy" : ""}("${node.dir.basename}" $layoutArg $buildArg, children:[
${children.map((e) => "$e,").join("\n")}
$padding])''';
Expression _genRouteRootExpression(RouteNode node) {
var toType = node.findToType();
return toType.newInstance([
literalString(node.dir.basename)
], {
if (node.file_page_dart.existsSync()) "builder": refer("build",node.pagePackageUrl),
if (node.file_layout_dart.existsSync()) "layout": refer("layout",node.layoutPackageUrl),
if (node.children.isNotEmpty) "children": literalList(node.children.map((e) => _genRouteRootExpression(e))),
});
}

@override
Expand All @@ -147,17 +144,8 @@ $padding])''';
var rootRoute = await cli.rootRoute;
Iterable<RouteNode> routes = rootRoute.toList();

var nameMaxLen = routes.map((e) => e.flatName.length).reduce((value, element) => value > element ? value : element);
// 因我们要自己Padding,所以没用code_builder
var newRoutes = routes.where((e) => e.file_page_dart.existsSync()).map((routeDir) {
var varWithPadding = routeDir.flatName.padRight(nameMaxLen);
return """ final $varWithPadding = root.find("${routeDir.routePath}")! ; """;
}).join("\n");

var routeTreeCode=await _genRouteTreeCode(rootRoute);

Library all = Library(
(b) => b
(b) => b
..generatedByComment = """
// Generated by https://github.com/chen56/you
// - This file setup routes tree and strong type `routes` reference
Expand All @@ -167,41 +155,59 @@ $padding])''';
// ignore_for_file: library_prefixes
// ignore_for_file: non_constant_identifier_names
"""
..directives.addAll(
routes.where((e) => e.file_page_dart.existsSync()).map((lib) {
if (async) {
return Directive.importDeferredAs(lib.pageImportUri(cli.pubspec.name, cli.dir_lib), "${lib.flatName}_");
} else {
return Directive.import(lib.pageImportUri(cli.pubspec.name, cli.dir_lib), as: "${lib.flatName}_");
}
}),
)
..directives.addAll(
routes.where((e) => e.file_layout_dart.existsSync()).map((lib) {
if (async) {
return Directive.importDeferredAs(lib.layoutImportUri(cli.pubspec.name, cli.dir_lib), "${lib.flatName}__");
} else {
return Directive.import(lib.layoutImportUri(cli.pubspec.name, cli.dir_lib), as: "${lib.flatName}__");
}
}),
// ..directives.addAll(
// routes.where((e) => e.file_page_dart.existsSync()).map((lib) {
// if (async) {
// return Directive.importDeferredAs(lib.pagePackageUrl, "${lib.flatName}_");
// } else {
// return Directive.import(lib.pagePackageUrl, as: "${lib.flatName}_");
// }
// }),
// )
// ..directives.addAll(
// routes.where((e) => e.file_layout_dart.existsSync()).map((lib) {
// if (async) {
// return Directive.importDeferredAs(lib.layoutPackageUrl, "${lib.flatName}__");
// } else {
// return Directive.import(lib.layoutPackageUrl, as: "${lib.flatName}__");
// }
// }),
// )
..body.add(
Mixin(
(b) => b
..name = "RoutesMixin"
..fields.add(
Field((f) => f
..name = "root"
..modifier = FieldModifier.final$
..type = rootRoute.findToType()
..assignment = _genRouteRootExpression(rootRoute).code),
)
..fields.addAll(
routes.where((e) => e.file_page_dart.existsSync()).map(
(routeDir) => Field((f) => f
..modifier = FieldModifier.final$
..late = true
..type = YouCli.toTypeDefault
..name = "routes_${routeDir.flatName}"
..assignment = refer("root.find").call([literalString(routeDir.routePath)]).nullChecked.code),
),
),
),
)
..body.add(
Code("""
var root=$routeTreeCode ;
final Routes routes=Routes();
class Routes{
$newRoutes
}
"""),
Class(
(b) => b
..name = "Routes"
..mixins.add(refer("RoutesMixin"))
),
),
);

var fmt = DartFormatter(pageWidth: 200);
var dartEmitter = DartEmitter(allocator: _allocator, orderDirectives: false, useNullSafetySyntax: true);
var allCode ='${all.accept(dartEmitter)}';
var dartEmitter = DartEmitterForCli(allocator: _allocator, orderDirectives: false, useNullSafetySyntax: true);
var allCode = '${all.accept(dartEmitter)}';
_log("gen: ${cli.file_routes_g_dart.path} : $allCode");
allCode = fmt.format(allCode);
await cli.file_routes_g_dart.writeAsString(allCode);
Expand Down
24 changes: 12 additions & 12 deletions packages/you_cli/lib/src/cli_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ class YouCli {
Future<RouteNode> get rootRoute async {
Future<RouteNode> from(Directory dir) async {
if (!dir.existsSync()) {
return RouteNode(dir: dir, children: []);
return RouteNode(cli: this, dir: dir, children: []);
}

var children = await Future.wait(dir.listSync(recursive: false).whereType<Directory>().map((e) async => await from(e)));
var (layout: layoutFunction, toType: toType) = await analysisLayout(dir.childFile(layoutDart));
return RouteNode(
cli: this,
dir: dir,
pageBuild: await analysisPage(dir.childFile(pageDart)),
layoutFunction: layoutFunction,
Expand Down Expand Up @@ -136,14 +137,15 @@ class YouCli {
}

class RouteNode {
final YouCli cli;
final List<RouteNode> children;
final Directory dir;
final Reference? toType;
final FunctionElement? layoutFunction;
final FunctionElement? pageBuild;
late RouteNode _parent = this;

RouteNode({required this.dir, this.toType, required this.children, this.layoutFunction, this.pageBuild}) {
RouteNode({required this.dir, this.toType, required this.children, this.layoutFunction, this.pageBuild, required this.cli}) {
for (var child in children) {
child._parent = this;
}
Expand All @@ -159,23 +161,21 @@ class RouteNode {

File get file_layout_dart => dir.childFile("layout.dart");

String pageImportUri(String pkgName, Directory libDir) {
var pageDartRelativePath = path.relative(file_page_dart.path, from: libDir.path);
return "package:$pkgName/$pageDartRelativePath";
}

String layoutImportUri(String pkgName, Directory libDir) {
var pageDartRelativePath = path.relative(file_layout_dart.path, from: libDir.path);
return "package:$pkgName/$pageDartRelativePath";
}

String get routePath {
if (isRoot) {
return "/";
}
return "/${path.relative(dir.path, from: root.dir.path)}";
}

String get pagePackageUrl {
return "package:${cli.pubspec.name}/${path.relative(file_page_dart.path, from: cli.dir_lib.path)}";
}

String get layoutPackageUrl {
return "package:${cli.pubspec.name}/${path.relative(file_layout_dart.path, from: cli.dir_lib.path)}";
}

/// note name平整化,可作为变量名:
/// lib/routes/1.a/b/page.dart ---> a_b
String get flatName {
Expand Down
17 changes: 0 additions & 17 deletions packages/you_cli/test/bin/analyzer_test.dart

This file was deleted.

Loading

0 comments on commit 9d9e67b

Please sign in to comment.