diff --git a/packages/you_cli/lib/src/code_builder_ext.dart b/packages/you_cli/lib/src/code_builder_ext.dart new file mode 100644 index 00000000..3382f4dd --- /dev/null +++ b/packages/you_cli/lib/src/code_builder_ext.dart @@ -0,0 +1,321 @@ + +import 'package:code_builder/code_builder.dart'; +import 'package:path/path.dart' as path; +import 'package:you_cli/src/urils.dart'; + +/// 自定义Allocator: 更清晰干净的前缀 +class CleanPrefixedAllocator implements Allocator { + final _importWithSymbols = >{}; + final _importsUrlAndAs = {}; + final _asKeys = {}; + + @override + String allocate(Reference reference) { + String symbol = reference.symbol!; + String? url = reference.url; + + if (url == null) { + return symbol; + } + + Set? symbols = _importWithSymbols[url]; + + if (symbols == null) { + // init + _importWithSymbols[url] = symbols = {}; + } + + for (var MapEntry(key: otherUrl, value: symbols) in _importWithSymbols.entries) { + if (otherUrl == url) { + continue; + } + if (symbols.contains(symbol)) { + // 冲突 + String as = _importsUrlAndAs.putIfAbsent(url, () { + Uri uri = Uri.parse(url); + String filename = path.basename(uri.path).replaceFirst(".dart", ""); + var dirname=path.basename(path.dirname(uri.path)); + var as = paths.pathPartToVar("_${dirname}_$filename"); + int times = _asKeys.putIfAbsent(as, ()=>0); + if(times==0){ + _asKeys[as]=++times; + return as; + }else{ + _asKeys[as]=++times; + return "$as$times"; + } + })!; + return "$as.$symbol"; + } + } + + // 未发现冲突 + symbols.add(symbol); + _importsUrlAndAs.putIfAbsent(url, () => null); + return symbol; + } + + @override + Iterable get imports => _importsUrlAndAs.keys.map( + (u) => Directive.import(u, as: _importsUrlAndAs[u]), + ); +} + +/// copy from code_builder source code +/// 【you_cli】update code: +/// - visitInvokeExpression : only remove last arguments: ',' +/// - visitClass : only remove field newline ',' +/// - visitMixin : only remove field newline ',' +/// - visitLiteralListExpression: only add ',' at last +class DartEmitterForCli extends DartEmitter { + bool _withInConstExpression = false; + + DartEmitterForCli({super.allocator, super.orderDirectives, super.useNullSafetySyntax}); + + @override + StringSink visitInvokeExpression(InvokeExpression expression, [StringSink? output]) { + final out = output ??= StringBuffer(); + return _writeConstExpression(out, expression.isConst, () { + expression.target.accept(this, out); + if (expression.name != null) { + out + ..write('.') + ..write(expression.name); + } + if (expression.typeArguments.isNotEmpty) { + out.write('<'); + visitAll(expression.typeArguments, out, (type) { + type.accept(this, out); + }); + out.write('>'); + } + out.write('('); + visitAll(expression.positionalArguments, out, (spec) { + spec.accept(this, out); + }); + if (expression.positionalArguments.isNotEmpty && expression.namedArguments.isNotEmpty) { + out.write(', '); + } + visitAll(expression.namedArguments.keys, out, (name) { + out + ..write(name) + ..write(': '); + expression.namedArguments[name]!.accept(this, out); + }); + final argumentCount = expression.positionalArguments.length + expression.namedArguments.length; + if (argumentCount > 1) { + // ***【you_cli】: only change this code:*** + // out.write(', '); + } + return out..write(')'); + }); + } + + @override + StringSink visitLiteralListExpression( + LiteralListExpression expression, [ + StringSink? output, + ]) { + final out = output ??= StringBuffer(); + + return _writeConstExpression(output, expression.isConst, () { + if (expression.type != null) { + out.write('<'); + expression.type!.accept(this, output); + out.write('>'); + } + out.write('['); + visitAll(expression.values, out, (value) { + _acceptLiteral(value, out); + }); + // ***【you_cli】: only change this code:*** + // old: if (expression.values.length > 1) { + /*new*/ if (expression.values.isNotEmpty) { + out.write(', '); + } + return out..write(']'); + }); + } + void _acceptLiteral(Object? literalOrSpec, StringSink output) { + if (literalOrSpec is Spec) { + literalOrSpec.accept(this, output); + return; + } + literal(literalOrSpec).accept(this, output); + } + + @override + StringSink visitClass(Class spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + + void writeModifier() { + if (spec.modifier != null) { + out.write('${spec.modifier!.name} '); + } + } + + if (spec.sealed) { + out.write('sealed '); + } else { + if (spec.abstract) { + out.write('abstract '); + } + writeModifier(); + if (spec.mixin) { + out.write('mixin '); + } + } + out.write('class ${spec.name}'); + visitTypeParameters(spec.types.map((r) => r.type), out); + if (spec.extend != null) { + out.write(' extends '); + spec.extend!.type.accept(this, out); + } + if (spec.mixins.isNotEmpty) { + out + ..write(' with ') + ..writeAll(spec.mixins.map((m) => m.type.accept(this)), ','); + } + if (spec.implements.isNotEmpty) { + out + ..write(' implements ') + ..writeAll(spec.implements.map((m) => m.type.accept(this)), ','); + } + out.write(' {'); + for (var c in spec.constructors) { + visitConstructor(c, spec.name, out); + out.writeln(); + } + for (var f in spec.fields) { + visitField(f, out); + // ***【you_cli】: only change this code:*** + // out.writeln(); + } + for (var m in spec.methods) { + visitMethod(m, out); + if (_isLambdaMethod(m)) { + out.writeln(';'); + } + out.writeln(); + } + out.writeln(' }'); + return out; + } + + @override + StringSink visitMixin(Mixin spec, [StringSink? output]) { + final out = output ??= StringBuffer(); + spec.docs.forEach(out.writeln); + for (var a in spec.annotations) { + visitAnnotation(a, out); + } + + if (spec.base) { + out.write('base '); + } + out.write('mixin ${spec.name}'); + visitTypeParameters(spec.types.map((r) => r.type), out); + if (spec.on != null) { + out.write(' on '); + spec.on!.type.accept(this, out); + } + if (spec.implements.isNotEmpty) { + out + ..write(' implements ') + ..writeAll(spec.implements.map((m) => m.type.accept(this)), ','); + } + out.write(' {'); + for (var f in spec.fields) { + visitField(f, out); + // ***【you_cli】: only change this code:*** + // out.writeln(); + } + for (var m in spec.methods) { + visitMethod(m, out); + if (_isLambdaMethod(m)) { + out.write(';'); + } + out.writeln(); + } + out.write(' }'); + return out; + } + + static bool _isLambdaBody(Code? code) => code is ToCodeExpression && !code.isStatement; + + /// Whether the provided [method] is considered a lambda method. + static bool _isLambdaMethod(Method method) => method.lambda ?? _isLambdaBody(method.body); + + /// Executes [visit] within a context which may alter the output if [isConst] + /// is `true`. + /// + /// This allows constant expressions to omit the `const` keyword if they + /// are already within a constant expression. + @override + void startConstCode( + bool isConst, + Null Function() visit, + ) { + final previousConstContext = _withInConstExpression; + if (isConst) { + _withInConstExpression = true; + } + + visit(); + _withInConstExpression = previousConstContext; + } + + /// Similar to [startConstCode], but handles writing `"const "` if [isConst] + /// is `true` and the invocation is not nested under other invocations where + /// [isConst] is true. + StringSink _writeConstExpression( + StringSink sink, + bool isConst, + StringSink Function() visitExpression, + ) { + final previousConstContext = _withInConstExpression; + if (isConst) { + if (!_withInConstExpression) { + sink.write('const '); + } + _withInConstExpression = true; + } + + final returnedSink = visitExpression(); + assert(identical(returnedSink, sink)); + _withInConstExpression = previousConstContext; + return sink; + } +} + + +/// Helper method improving on [StringSink.writeAll]. +/// +/// For every `Spec` in [elements], executing [visit]. +/// +/// If [elements] is at least 2 elements, inserts [separator] delimiting them. +StringSink visitAll( + Iterable elements, + StringSink output, + void Function(T) visit, [ + String separator = ', ', + ]) { + // Basically, this whole method is an improvement on + // output.writeAll(specs.map((s) => s.accept(visitor)); + // + // ... which would allocate more StringBuffer(s) for a one-time use. + if (elements.isEmpty) { + return output; + } + final iterator = elements.iterator..moveNext(); + visit(iterator.current); + while (iterator.moveNext()) { + output.write(separator); + visit(iterator.current); + } + return output; +} diff --git a/packages/you_cli/lib/src/urils.dart b/packages/you_cli/lib/src/urils.dart index 5036ed32..988fbddb 100644 --- a/packages/you_cli/lib/src/urils.dart +++ b/packages/you_cli/lib/src/urils.dart @@ -1,28 +1,15 @@ -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/visitor.dart'; -Analyzer analyzerUtils=Analyzer(); - -class Analyzer{ - Iterable findChildrenByType(Element findFrom){ - CollectChildElement e=CollectChildElement(); - findFrom.accept(e); - return e.list; - // if (findFrom is T) yield findFrom as T; - // for(var child in findFrom.children){ - // yield* findChildrenByType(child); - // } - } -} - -class CollectChildElement extends GeneralizingElementVisitor{ - List list=[]; - @override - visitElement(Element element) { - print("ffffff$element"); - if(element is T){ - list.add(element as T); - } - return super.visitElement(element); +Paths paths=Paths(); +class Paths{ + String pathPartToVar(String pathPart){ + return pathPart + // ignore: unnecessary_string_escapes + .replaceAll(RegExp("^\\d+\."), "") // 1.z.about -> note_note-self + .replaceAll(".", "_") + .replaceAll("-", "_") + .replaceAll("&", "_") + .replaceAll("*", "_") + .replaceAll("*", "_") + .replaceAll("@", "_"); } } \ No newline at end of file diff --git a/packages/you_flutter/lib/src/router.dart b/packages/you_flutter/lib/src/router.dart index 497ebbac..33502249 100644 --- a/packages/you_flutter/lib/src/router.dart +++ b/packages/you_flutter/lib/src/router.dart @@ -142,6 +142,7 @@ base class To { final PageBuilder? _builder; final PageLayoutBuilder? layout; + // TODO P1 root Node的part是routes,有问题! To(this.part, { PageBuilder? builder, this.layout,