Bläddra i källkod

add: 完成注解自动化路由生成功能(匹配i2School)

hwh97 4 år sedan
förälder
incheckning
a5dc6b1330

+ 27 - 2
build.yaml

@@ -6,10 +6,35 @@ targets:
         enabled: true
         generate_for:
           exclude: ['**.g.dart']
+      router_gen|model_import_builder:
+        options: { 'write': true }
+        enabled: true
+        generate_for:
+          exclude: [ '**.g.dart' ]
+      router_gen|table_builder:
+        options: { 'write': true }
+        enabled: true
+        generate_for:
+          exclude: [ '**.g.dart' ]
 
 builders:
   router_gen_build:
     import: "package:router_gen/builder.dart"
     builder_factories: ["generateRouterProvider"]
-    build_extensions: {".dart": ['.g.dart']}
-    auto_apply: dependents
+    build_extensions: {".dart": ['aa.dart']}
+    auto_apply: dependents
+    build_to: cache
+    runs_before: [ 'router_gen|model_import_builder' ]
+  model_import_builder:
+    import: "package:router_gen/builder.dart"
+    builder_factories: [ "modelImportBuilder" ]
+    build_extensions: { ".dart": [ 'bb.dart' ] }
+    auto_apply: dependents
+    build_to: cache
+    runs_before: [ 'router_gen|table_builder']
+  table_builder:
+    import: "package:router_gen/builder.dart"
+    builder_factories: [ "generateRouterTableProvider" ]
+    build_extensions: { ".dart": [ 'route.dart' ] }
+    auto_apply: dependents
+    build_to: source

+ 2 - 2
lib/annotation/router_page.dart

@@ -1,5 +1,5 @@
 class RouterPage {
-  final String name;
+  final String path;
 
-  const RouterPage({this.name});
+  const RouterPage({this.path});
 }

+ 3 - 3
lib/annotation/router_param.dart

@@ -1,5 +1,5 @@
 class RouterParam {
-  final bool required;
+  const RouterParam();
+}
 
-  const RouterParam({this.required=false});
-}
+const routerParam = RouterParam();

+ 5 - 0
lib/annotation/router_table.dart

@@ -0,0 +1,5 @@
+class RouterTable {
+  const RouterTable();
+}
+
+const routerTable = RouterTable();

+ 7 - 3
lib/builder.dart

@@ -1,10 +1,14 @@
 import 'package:build/build.dart';
+import 'package:router_gen/generator/model_import_generator.dart';
+import 'package:router_gen/generator/router_table_generator.dart';
 import 'package:source_gen/source_gen.dart';
 import 'generator/router_generator.dart';
 
-
 Builder generateRouterProvider(BuilderOptions options) =>
-    LibraryBuilder(RouterGenerator(), generatedExtension: ".g.dart");
-
+    LibraryBuilder(RouterGenerator(), generatedExtension: ".aa.dart");
 
+Builder modelImportBuilder(BuilderOptions options) =>
+    LibraryBuilder(ModelImportGenerator(), generatedExtension: ".bb.dart");
 
+Builder generateRouterTableProvider(BuilderOptions options) =>
+    LibraryBuilder(RouterTableGenerator(), generatedExtension: ".route.dart");

+ 84 - 0
lib/generator/model_import_generator.dart

@@ -0,0 +1,84 @@
+import 'package:analyzer/dart/element/element.dart';
+import 'package:router_gen/model/router.dart';
+import 'package:router_gen/util/utils.dart';
+import 'package:source_gen/source_gen.dart';
+
+/// find class path from argument
+class ModelImportGenerator extends Generator {
+  static List<String> modelImports = [];
+
+  const ModelImportGenerator();
+
+  @override
+  Future<String> generate(LibraryReader library, _) async {
+    if (library.classes.isNotEmpty) {
+      Map routerMap = router.routerMap;
+      for (String key in routerMap.keys) {
+        Page page = routerMap[key];
+        for (Argument argument in page.arguments) {
+          if (argument.isImported) continue;
+          if (_isSpeciousType(argument.type)) {
+            // 处理type
+            List<String> type = getType(argument.type);
+            type.removeWhere((element) => _isSpeciousType(element) == false);
+            if (type.length == 0) {
+              continue;
+            }
+            // 比较class name
+            bool isInThisClass = false;
+            for (ClassElement element in library.classes) {
+              if (type.contains(element.displayName)) {
+                isInThisClass = true;
+                break;
+              }
+            }
+            // 添加import
+            if (isInThisClass) {
+              argument.isImported = true;
+              router.imports.add(getImportStr(_));
+            }
+          } else {
+            argument.isImported = true;
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+  bool _isSpeciousType(String type) {
+    if (type == "int" ||
+        type == "double" ||
+        type == "num" ||
+        type == "Runes" ||
+        type == "String" ||
+        type == "bool" ||
+        type == "dynamic") {
+      return false;
+    }
+    return true;
+  }
+
+  /// Notice: not contain all situation
+  /// 递归获取type
+  List<String> getType(String type) {
+    if (type.startsWith("List<") == false && type.startsWith("Map<") == false) {
+      return [type];
+    }
+
+    List<String> results = [];
+    if(type.startsWith("List<")) {
+      String result = type.substring(type.indexOf("List<") + 5, type.lastIndexOf(">"));
+      results.addAll(getType(result.trim()));
+    }
+
+    if (type.startsWith("Map<")) {
+      List<String> splits = type.substring(type.indexOf("Map<") + 4, type.lastIndexOf(">")).split(",");
+      results.addAll(getType(splits[0].trim()));
+      results.addAll(getType(splits[1].trim()));
+    }
+
+    return results;
+  }
+}

+ 41 - 2
lib/generator/router_generator.dart

@@ -1,12 +1,51 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:build/build.dart';
 import 'package:router_gen/annotation/router_page.dart';
+import 'package:router_gen/annotation/router_param.dart';
+import 'package:router_gen/model/router.dart';
+import 'package:router_gen/util/utils.dart';
 import 'package:source_gen/source_gen.dart';
+import 'package:path/path.dart' as Path;
 
+/// save router annotation to router
 class RouterGenerator extends GeneratorForAnnotation<RouterPage> {
+  final _paramChecker = const TypeChecker.fromRuntime(RouterParam);
+
   @override
-  generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) {
-    print(element);
+  generateForAnnotatedElement(
+      Element element, ConstantReader annotation, BuildStep buildStep) async {
+    if (element.kind == ElementKind.CLASS) {
+      router.imports.add(getImportStr(buildStep));
+
+      String path = buildStep.inputId.path; // lib/xxx.dart
+      String basename = Path.basename(buildStep.inputId.path); // xxx.dart
+
+      // router name
+      String className = element.name;
+      String aptName = annotation.peek("path")?.stringValue;
+      String routerName =
+          toCamelCase(aptName == null || aptName.isEmpty ? className : aptName);
+
+      var page = Page();
+      page.name = className;
+      page.path = Path.join(
+          path.replaceFirst("lib", "").replaceFirst(basename, ""), routerName);
+      page.arguments = [];
 
+      for (FieldElement e in (element as ClassElement).fields) {
+        if (_paramChecker.hasAnnotationOf(e)) {
+          String type = e.type.getDisplayString(withNullability: false);
+          bool required =
+              e.computeConstantValue()?.getField("required")?.toBoolValue();
+          page.arguments.add(Argument(
+            e.name,
+            type,
+            required ?? true,
+          ));
+        }
+      }
+      router.routerMap[routerName] = page;
+    }
+    return null;
   }
 }

+ 81 - 0
lib/generator/router_table_generator.dart

@@ -0,0 +1,81 @@
+import 'package:analyzer/dart/element/element.dart';
+import 'package:build/build.dart';
+import 'package:router_gen/annotation/router_table.dart';
+import 'package:router_gen/model/router.dart';
+import 'package:source_gen/source_gen.dart';
+import 'package:path/path.dart' as Path;
+
+class RouterTableGenerator extends GeneratorForAnnotation<RouterTable> {
+  @override
+  generateForAnnotatedElement(
+      Element element, ConstantReader annotation, BuildStep buildStep) {
+    // print("find table annotation ${buildStep.inputId.path}");
+    // print("RouterTable imports ${router.imports.toString()}");
+    // print("RouterTable map  ${router.routerMap.toString()}");
+
+    String path = buildStep.inputId.path; // lib/xxx.dart
+    String relatedFileName = Path.basename(path); // xxx.dart
+    String relatedClassName = element.name;
+
+    String imports = "";
+    for (String import in router.imports) {
+      imports = imports + "import '" + import + "';\n";
+    }
+    // all router's name
+    List<String> routeNames = router.routerMap.keys.toList();
+    String pathFiled = _generateFields(routeNames, router.routerMap);
+    String register = _generateRouterRegister(routeNames, router.routerMap);
+
+    // i2 school
+    // import 'package:fluro/fluro.dart';
+    // import 'package:school_parent/base_plugin/routes.dart';
+    // example
+    // import 'package:example/routes.dart';
+
+    return """
+import '${relatedFileName}';
+import 'package:fluro/fluro.dart';
+import 'package:school_parent/base_plugin/routes.dart';
+${imports}
+
+class \$${relatedClassName} implements ${relatedClassName} {
+  ${pathFiled}
+  @override
+  void registerRoutes(Router router) {
+    ${register}
+  }
+}
+""";
+  }
+
+  _generateFields(List<String> routeNames, Map<String, Page> page) {
+    String field = "";
+    routeNames.forEach((e) {
+      field += "static final ${e} = \"${page[e].path}\";\n";
+    });
+    return field;
+  }
+
+  _generateRouterRegister(List<String> routeNames, Map<String, Page> page) {
+    String register = "";
+    routeNames.forEach((e) {
+      String params = "";
+      page[e].arguments.forEach((element) {
+        params +=
+        "${element.name}: IRoutesProvider.getParams<${element.type}>(p, \"${element.name}\"),\n";
+      });
+
+      register += """
+     router.define(${e},
+        handler: Handler(
+          handlerFunc: (c, p) => ${page[e].name}(
+            ${params}
+          ),
+        ),
+     ); 
+""";
+    });
+
+    return register;
+  }
+}

+ 32 - 0
lib/model/router.dart

@@ -0,0 +1,32 @@
+/// global instance
+final _Router router = _Router();
+
+class _Router {
+  Set<String> imports = Set<String>();
+  Map<String, Page> routerMap = <String, Page>{};
+}
+
+class Page {
+  String path;
+  String name;
+  List<Argument> arguments;
+
+  @override
+  String toString() {
+    return "{routerPath:${this.path},name:${this.name},arguments:${this.arguments.toString()}}";
+  }
+}
+
+class Argument {
+  String name;
+  String type;
+  bool isImported;
+  bool required;
+
+  Argument(this.name, this.type, this.required, {this.isImported=false});
+
+  @override
+  String toString() {
+    return "{name:${this.name},type:${this.type},imported:${this.isImported},required:${this.required}}";
+  }
+}

+ 2 - 1
lib/router_gen.dart

@@ -1,4 +1,5 @@
 library router_gen;
 
 export 'annotation/router_page.dart';
-export 'annotation/router_param.dart';
+export 'annotation/router_param.dart';
+export 'annotation/router_table.dart';

+ 19 - 0
lib/util/utils.dart

@@ -0,0 +1,19 @@
+import 'package:build/build.dart';
+
+String toCamelCase(String s) {
+  if (s.length < 2) {
+    return s.toLowerCase();
+  }
+  return s[0].toLowerCase() + s.substring(1);
+}
+
+String getImportStr(BuildStep buildStep) {
+  var importStr = "";
+  if (buildStep.inputId.path.contains('lib/')) {
+    importStr =
+    "package:${buildStep.inputId.package}/${buildStep.inputId.path.replaceFirst('lib/', '')}";
+  } else {
+    importStr = "${buildStep.inputId.path}";
+  }
+  return importStr;
+}

+ 22 - 93
pubspec.lock

@@ -7,14 +7,14 @@ packages:
       name: _fe_analyzer_shared
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "7.0.0"
+    version: "14.0.0"
   analyzer:
     dependency: "direct main"
     description:
       name: analyzer
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "0.39.17"
+    version: "0.41.2"
   args:
     dependency: transitive
     description:
@@ -28,28 +28,21 @@ packages:
       name: async
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "2.5.0-nullsafety.1"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "2.1.0-nullsafety.1"
+    version: "2.4.2"
   build:
     dependency: "direct main"
     description:
       name: build
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.3.0"
+    version: "1.6.2"
   build_config:
     dependency: "direct main"
     description:
       name: build_config
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "0.4.2"
+    version: "0.4.5"
   build_daemon:
     dependency: transitive
     description:
@@ -63,21 +56,21 @@ packages:
       name: build_resolvers
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.3.11"
+    version: "1.5.3"
   build_runner:
     dependency: "direct dev"
     description:
       name: build_runner
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.10.2"
+    version: "1.11.1"
   build_runner_core:
     dependency: transitive
     description:
       name: build_runner_core
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "6.0.1"
+    version: "6.1.7"
   built_collection:
     dependency: transitive
     description:
@@ -92,20 +85,13 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "7.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "1.1.0-nullsafety.3"
   charcode:
     dependency: transitive
     description:
       name: charcode
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.2.0-nullsafety.1"
+    version: "1.1.3"
   checked_yaml:
     dependency: transitive
     description:
@@ -120,13 +106,6 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.0"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "1.1.0-nullsafety.1"
   code_builder:
     dependency: transitive
     description:
@@ -140,7 +119,7 @@ packages:
       name: collection
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.15.0-nullsafety.3"
+    version: "1.14.13"
   convert:
     dependency: transitive
     description:
@@ -155,27 +134,13 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.5"
-  csslib:
-    dependency: transitive
-    description:
-      name: csslib
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "0.16.2"
   dart_style:
     dependency: transitive
     description:
       name: dart_style
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.3.6"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "1.2.0-nullsafety.1"
+    version: "1.3.12"
   file:
     dependency: transitive
     description:
@@ -190,16 +155,6 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.10.11"
-  flutter:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
   glob:
     dependency: transitive
     description:
@@ -214,13 +169,6 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.0"
-  html:
-    dependency: transitive
-    description:
-      name: html
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "0.14.0+4"
   http_multi_server:
     dependency: transitive
     description:
@@ -276,14 +224,14 @@ packages:
       name: matcher
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "0.12.10-nullsafety.1"
+    version: "0.12.9"
   meta:
     dependency: transitive
     description:
       name: meta
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.3.0-nullsafety.3"
+    version: "1.2.4"
   mime:
     dependency: transitive
     description:
@@ -318,7 +266,7 @@ packages:
       name: path
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.8.0-nullsafety.1"
+    version: "1.7.0"
   pedantic:
     dependency: transitive
     description:
@@ -368,39 +316,34 @@ packages:
       url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.4+1"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
   source_gen:
     dependency: "direct main"
     description:
       name: source_gen
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "0.9.7+1"
+    version: "0.9.10+3"
   source_span:
     dependency: transitive
     description:
       name: source_span
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.8.0-nullsafety.2"
+    version: "1.7.0"
   stack_trace:
     dependency: transitive
     description:
       name: stack_trace
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.10.0-nullsafety.1"
+    version: "1.9.6"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "2.1.0-nullsafety.1"
+    version: "2.0.0"
   stream_transform:
     dependency: transitive
     description:
@@ -414,21 +357,14 @@ packages:
       name: string_scanner
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.1.0-nullsafety.1"
+    version: "1.0.5"
   term_glyph:
     dependency: transitive
     description:
       name: term_glyph
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.2.0-nullsafety.1"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "0.2.19-nullsafety.2"
+    version: "1.1.0"
   timing:
     dependency: transitive
     description:
@@ -442,14 +378,7 @@ packages:
       name: typed_data
       url: "https://pub.flutter-io.cn"
     source: hosted
-    version: "1.3.0-nullsafety.3"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.flutter-io.cn"
-    source: hosted
-    version: "2.1.0-nullsafety.3"
+    version: "1.2.0"
   watcher:
     dependency: transitive
     description:
@@ -472,4 +401,4 @@ packages:
     source: hosted
     version: "2.2.1"
 sdks:
-  dart: ">=2.10.0 <2.11.0"
+  dart: ">=2.10.0 <3.0.0"

+ 3 - 3
pubspec.yaml

@@ -8,10 +8,10 @@ environment:
   sdk: ">=2.7.0 <3.0.0"
 
 dependencies:
-  analyzer: ^0.39.4
-  build: ^1.1.6
+  analyzer: any
+  build: any
   build_config: ">=0.3.0"
   source_gen: ^0.9.7
 
 dev_dependencies:
-  build_runner: ^1.10.0
+  build_runner: any