TypeScript def builder
This commit is contained in:
parent
31006f54e2
commit
7a93ebc833
4 changed files with 172 additions and 1 deletions
|
@ -17,6 +17,22 @@ builders:
|
|||
generate_for:
|
||||
- "lib/src/models/**.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:
|
||||
_book:
|
||||
sources:
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:angel_serialize/angel_serialize.dart';
|
||||
import 'package:build/build.dart';
|
||||
import 'package:code_buffer/code_buffer.dart';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:source_gen/source_gen.dart' hide LibraryBuilder;
|
||||
|
@ -15,6 +16,8 @@ part 'model.dart';
|
|||
|
||||
part 'serialize.dart';
|
||||
|
||||
part 'typescript.dart';
|
||||
|
||||
Builder jsonModelBuilder(_) {
|
||||
return new PartBuilder(
|
||||
const [const JsonModelGenerator()],
|
||||
|
@ -29,6 +32,10 @@ Builder serializerBuilder(_) {
|
|||
);
|
||||
}
|
||||
|
||||
Builder typescriptDefinitionBuilder(_) {
|
||||
return const TypeScriptDefinitionBuilder();
|
||||
}
|
||||
|
||||
/// Converts a [DartType] to a [TypeReference].
|
||||
TypeReference convertTypeReference(DartType t) {
|
||||
return new TypeReference((b) {
|
||||
|
@ -60,7 +67,6 @@ bool isListModelType(InterfaceType t) {
|
|||
isModelClass(t.typeArguments[0]);
|
||||
}
|
||||
|
||||
|
||||
/// Determines if a [DartType] is a `Map` with the second type argument being a `Model`.
|
||||
bool isMapToModelType(InterfaceType t) {
|
||||
return t.name == 'Map' &&
|
||||
|
|
148
angel_serialize_generator/lib/typescript.dart
Normal file
148
angel_serialize_generator/lib/typescript.dart
Normal 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(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ environment:
|
|||
dependencies:
|
||||
angel_serialize: ^2.0.0-alpha
|
||||
build_config: ">=0.2.4 <2.0.0"
|
||||
code_buffer: ^1.0.0
|
||||
code_builder: ^3.0.0
|
||||
id: ^1.0.0
|
||||
recase: ^1.0.0
|
||||
|
|
Loading…
Reference in a new issue