TypeScript def builder

This commit is contained in:
Tobe O 2018-03-29 12:14:22 -04:00
parent 31006f54e2
commit 7a93ebc833
4 changed files with 172 additions and 1 deletions

View file

@ -17,6 +17,22 @@ builders:
generate_for: generate_for:
- "lib/src/models/**.dart" - "lib/src/models/**.dart"
- "test/**.dart" - "test/**.dart"
typescript_definition:
target: "angel_serialize_generator"
import: "package:angel_serialize_generator/angel_serialize_generator.dart"
builder_factories:
- typescriptDefinitionBuilder
auto_apply: root_package
build_to: source
build_extensions:
.dart:
- ".d.ts"
required_inputs:
- .dart
defaults:
generate_for:
- "lib/src/models/**.dart"
- "test/**.dart"
targets: targets:
_book: _book:
sources: sources:

View file

@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/dart/element/type.dart';
import 'package:angel_serialize/angel_serialize.dart'; import 'package:angel_serialize/angel_serialize.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:code_buffer/code_buffer.dart';
import 'package:code_builder/code_builder.dart'; import 'package:code_builder/code_builder.dart';
import 'package:recase/recase.dart'; import 'package:recase/recase.dart';
import 'package:source_gen/source_gen.dart' hide LibraryBuilder; import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
@ -15,6 +16,8 @@ part 'model.dart';
part 'serialize.dart'; part 'serialize.dart';
part 'typescript.dart';
Builder jsonModelBuilder(_) { Builder jsonModelBuilder(_) {
return new PartBuilder( return new PartBuilder(
const [const JsonModelGenerator()], const [const JsonModelGenerator()],
@ -29,6 +32,10 @@ Builder serializerBuilder(_) {
); );
} }
Builder typescriptDefinitionBuilder(_) {
return const TypeScriptDefinitionBuilder();
}
/// Converts a [DartType] to a [TypeReference]. /// Converts a [DartType] to a [TypeReference].
TypeReference convertTypeReference(DartType t) { TypeReference convertTypeReference(DartType t) {
return new TypeReference((b) { return new TypeReference((b) {
@ -60,7 +67,6 @@ bool isListModelType(InterfaceType t) {
isModelClass(t.typeArguments[0]); isModelClass(t.typeArguments[0]);
} }
/// Determines if a [DartType] is a `Map` with the second type argument being a `Model`. /// Determines if a [DartType] is a `Map` with the second type argument being a `Model`.
bool isMapToModelType(InterfaceType t) { bool isMapToModelType(InterfaceType t) {
return t.name == 'Map' && return t.name == 'Map' &&

View file

@ -0,0 +1,148 @@
part of angel_serialize_generator;
class TypeScriptDefinitionBuilder implements Builder {
final bool autoSnakeCaseNames;
const TypeScriptDefinitionBuilder({this.autoSnakeCaseNames: true});
@override
Map<String, List<String>> get buildExtensions {
return {
'.dart': ['.d.ts']
};
}
Future<String> compileToTypeScriptType(String fieldName, InterfaceType type,
List<CodeBuffer> ext, BuildStep buildStep) async {
String typeScriptType = 'any';
var types = const {
num: 'number',
bool: 'boolean',
String: 'string',
Symbol: 'Symbol',
};
types.forEach((t, tsType) {
if (new TypeChecker.fromRuntime(t).isAssignableFromType(type))
typeScriptType = tsType;
});
if (isListModelType(type)) {
var arg = await compileToTypeScriptType(
fieldName, type.typeArguments[0], ext, buildStep);
typeScriptType = '$arg[]';
} else if (isMapToModelType(type)) {
var key = await compileToTypeScriptType(
fieldName, type.typeArguments[0], ext, buildStep);
var value = await compileToTypeScriptType(
fieldName, type.typeArguments[1], ext, buildStep);
var modelType = type.typeArguments[1];
var ctx = await buildContext(
modelType.element,
new ConstantReader(
serializableTypeChecker.firstAnnotationOf(modelType.element)),
buildStep,
buildStep.resolver,
autoSnakeCaseNames,
true,
);
typeScriptType = ctx.modelClassNameRecase.pascalCase +
new ReCase(fieldName).pascalCase;
ext.add(new CodeBuffer()
..writeln('interface $typeScriptType {')
..indent()
..writeln('[key: $key]: $value;')
..outdent()
..writeln('}'));
} else if (const TypeChecker.fromRuntime(List).isAssignableFromType(type)) {
typeScriptType = 'any[]';
} else if (isModelClass(type)) {
var ctx = await buildContext(
type.element,
new ConstantReader(
serializableTypeChecker.firstAnnotationOf(type.element)),
buildStep,
buildStep.resolver,
autoSnakeCaseNames,
true,
);
typeScriptType = ctx.modelClassNameRecase.pascalCase;
}
return typeScriptType;
}
@override
Future build(BuildStep buildStep) async {
var contexts = <BuildContext>[];
var lib = new LibraryReader(await buildStep.inputLibrary);
var elements =
lib.annotatedWith(const TypeChecker.fromRuntime(Serializable));
for (var element in elements) {
if (element.element.kind != ElementKind.CLASS)
throw 'Only classes can be annotated with a @Serializable() annotation.';
contexts.add(await buildContext(
element.element,
element.annotation,
buildStep,
await buildStep.resolver,
true,
autoSnakeCaseNames != false));
}
if (contexts.isEmpty) return;
var buf = new CodeBuffer(
trailingNewline: true,
sourceUrl: buildStep.inputId.uri,
);
// declare module `foo` {
//buf
// ..writeln("declare module '${buildStep.inputId.package}' {")
// ..indent();
for (var ctx in contexts) {
// interface Bar { ... }
buf
..writeln('interface ${ctx.modelClassNameRecase.pascalCase} {')
..indent();
var ext = <CodeBuffer>[];
for (var field in ctx.fields) {
// Skip excluded fields
if (ctx.excluded[field.name]?.canSerialize == false) continue;
var alias = ctx.resolveFieldName(field.name);
var typeScriptType = await compileToTypeScriptType(
field.name, field.type, ext, buildStep);
// foo: string;
buf.writeln('$alias: $typeScriptType;');
}
buf
..outdent()
..writeln('}');
for (var b in ext) {
b.copyInto(buf);
}
}
//buf
// ..outdent()
// ..writeln('}');
buildStep.writeAsString(
buildStep.inputId.changeExtension('.d.ts'),
buf.toString(),
);
}
}

View file

@ -8,6 +8,7 @@ environment:
dependencies: dependencies:
angel_serialize: ^2.0.0-alpha angel_serialize: ^2.0.0-alpha
build_config: ">=0.2.4 <2.0.0" build_config: ">=0.2.4 <2.0.0"
code_buffer: ^1.0.0
code_builder: ^3.0.0 code_builder: ^3.0.0
id: ^1.0.0 id: ^1.0.0
recase: ^1.0.0 recase: ^1.0.0