Comparing version 1.2.2 to 1.3.0
@@ -40,2 +40,3 @@ "use strict"; | ||
.addHashEntry(generateFromJson(typeMap, fullName, message)) | ||
.addHashEntry(generateFromPartial(typeMap, fullName, message)) | ||
.addHashEntry(generateToJson(typeMap, fullName, message)) | ||
@@ -62,2 +63,3 @@ .endHash() | ||
file = addLongUtilityMethod(file); | ||
file = addDeepPartialType(file); | ||
let hasAnyTimestamps = false; | ||
@@ -82,2 +84,18 @@ visit(fileDesc, (_, messageType) => { | ||
} | ||
function addDeepPartialType(file) { | ||
return file.addCode(ts_poet_1.CodeBlock.empty() | ||
.add('export type DeepPartial<T> = {%>\n') | ||
.add('[P in keyof T]?: T[P] extends Array<infer U>\n') | ||
.add('? Array<DeepPartial<U>>\n') | ||
.add(': T[P] extends ReadonlyArray<infer U>\n') | ||
.add('? ReadonlyArray<DeepPartial<U>>\n') | ||
.add(': T[P] extends Date | Function | Uint8Array | undefined\n') | ||
.add('? T[P]\n') | ||
.add(': T[P] extends infer U | undefined\n') | ||
.add('? DeepPartial<U>\n') | ||
.add(': T[P] extends object\n') | ||
.add('? DeepPartial<T[P]>\n') | ||
.add(': T[P]\n%<') | ||
.add('};')); | ||
} | ||
function addTimestampMethods(file) { | ||
@@ -456,2 +474,52 @@ const timestampType = 'Timestamp@./google/protobuf/timestamp'; | ||
} | ||
function generateFromPartial(typeMap, fullName, messageDesc) { | ||
// create the basic function declaration | ||
let func = ts_poet_1.FunctionSpec.create('fromPartial') | ||
.addParameter('object', `DeepPartial<${fullName}>`) | ||
.returns(fullName); | ||
func = func.addStatement('const message = Object.create(base%L) as %L', fullName, fullName); | ||
// initialize all lists | ||
messageDesc.field.filter(types_1.isRepeated).forEach(field => { | ||
const value = types_1.isMapType(typeMap, messageDesc, field) ? '{}' : '[]'; | ||
func = func.addStatement('message.%L = %L', snakeToCamel(field.name), value); | ||
}); | ||
// add a check for each incoming field | ||
messageDesc.field.forEach(field => { | ||
const fieldName = snakeToCamel(field.name); | ||
const readSnippet = (from) => { | ||
if (types_1.isEnum(field) || types_1.isPrimitive(field) || types_1.isTimestamp(field) || types_1.isValueType(field)) { | ||
return ts_poet_1.CodeBlock.of(from); | ||
} | ||
else if (types_1.isMessage(field)) { | ||
return ts_poet_1.CodeBlock.of('%T.fromPartial(%L)', types_1.basicTypeName(typeMap, field), from); | ||
} | ||
else { | ||
throw new Error(`Unhandled field ${field}`); | ||
} | ||
}; | ||
// and then use the snippet to handle repeated fields if necessary | ||
func = func.beginControlFlow('if (object.%L)', fieldName); | ||
if (types_1.isRepeated(field)) { | ||
if (types_1.isMapType(typeMap, messageDesc, field)) { | ||
func = func | ||
.addStatement(`const entry = %L`, readSnippet(`object.${fieldName}`)) | ||
.beginControlFlow('if (entry.value)') | ||
.addStatement('message.%L[entry.key] = entry.value', fieldName) | ||
.endControlFlow(); | ||
} | ||
else { | ||
func = func | ||
.beginControlFlow('for (const e of object.%L)', fieldName) | ||
.addStatement(`message.%L.push(%L)`, fieldName, readSnippet('e')) | ||
.endControlFlow(); | ||
} | ||
} | ||
else { | ||
func = func.addStatement(`message.%L = %L`, fieldName, readSnippet(`object.${fieldName}`)); | ||
} | ||
func = func.endControlFlow(); | ||
}); | ||
// and then wrap up the switch/while/return | ||
return func.addStatement('return message'); | ||
} | ||
function generateService(typeMap, fileDesc, serviceDesc) { | ||
@@ -458,0 +526,0 @@ let service = ts_poet_1.InterfaceSpec.create(serviceDesc.name).addModifiers(ts_poet_1.Modifier.EXPORT); |
@@ -231,3 +231,3 @@ "use strict"; | ||
const mappedTypes = { | ||
'.google.protobuf.Timestamp': ts_poet_1.TypeNames.unionType(ts_poet_1.TypeNames.DATE, ts_poet_1.TypeNames.UNDEFINED) | ||
'.google.protobuf.Timestamp': ts_poet_1.TypeNames.DATE | ||
}; | ||
@@ -234,0 +234,0 @@ function isTimestamp(field) { |
{ | ||
"name": "ts-proto", | ||
"version": "1.2.2", | ||
"version": "1.3.0", | ||
"description": "", | ||
@@ -25,5 +25,5 @@ "main": "build/plugin.js", | ||
"prettier": "^1.16.4", | ||
"ts-jest": "^24.0.1", | ||
"ts-node": "^8.0.3", | ||
"typescript": "^3.4.1" | ||
"ts-jest": "^24.0.2", | ||
"ts-node": "^8.3.0", | ||
"typescript": "^3.5.2" | ||
}, | ||
@@ -30,0 +30,0 @@ "dependencies": { |
@@ -84,2 +84,3 @@ import { | ||
.addHashEntry(generateFromJson(typeMap, fullName, message)) | ||
.addHashEntry(generateFromPartial(typeMap, fullName, message)) | ||
.addHashEntry(generateToJson(typeMap, fullName, message)) | ||
@@ -111,2 +112,3 @@ .endHash() | ||
file = addLongUtilityMethod(file); | ||
file = addDeepPartialType(file); | ||
@@ -138,2 +140,21 @@ let hasAnyTimestamps = false; | ||
function addDeepPartialType(file: FileSpec): FileSpec { | ||
return file.addCode( | ||
CodeBlock.empty() | ||
.add('export type DeepPartial<T> = {%>\n') | ||
.add('[P in keyof T]?: T[P] extends Array<infer U>\n') | ||
.add('? Array<DeepPartial<U>>\n') | ||
.add(': T[P] extends ReadonlyArray<infer U>\n') | ||
.add('? ReadonlyArray<DeepPartial<U>>\n') | ||
.add(': T[P] extends Date | Function | Uint8Array | undefined\n') | ||
.add('? T[P]\n') | ||
.add(': T[P] extends infer U | undefined\n') | ||
.add('? DeepPartial<U>\n') | ||
.add(': T[P] extends object\n') | ||
.add('? DeepPartial<T[P]>\n') | ||
.add(': T[P]\n%<') | ||
.add('};') | ||
); | ||
} | ||
function addTimestampMethods(file: FileSpec): FileSpec { | ||
@@ -559,2 +580,54 @@ const timestampType = 'Timestamp@./google/protobuf/timestamp'; | ||
function generateFromPartial(typeMap: TypeMap, fullName: string, messageDesc: DescriptorProto): FunctionSpec { | ||
// create the basic function declaration | ||
let func = FunctionSpec.create('fromPartial') | ||
.addParameter('object', `DeepPartial<${fullName}>`) | ||
.returns(fullName); | ||
func = func.addStatement('const message = Object.create(base%L) as %L', fullName, fullName); | ||
// initialize all lists | ||
messageDesc.field.filter(isRepeated).forEach(field => { | ||
const value = isMapType(typeMap, messageDesc, field) ? '{}' : '[]'; | ||
func = func.addStatement('message.%L = %L', snakeToCamel(field.name), value); | ||
}); | ||
// add a check for each incoming field | ||
messageDesc.field.forEach(field => { | ||
const fieldName = snakeToCamel(field.name); | ||
const readSnippet = (from: string): CodeBlock => { | ||
if (isEnum(field) || isPrimitive(field) || isTimestamp(field) || isValueType(field)) { | ||
return CodeBlock.of(from); | ||
} else if (isMessage(field)) { | ||
return CodeBlock.of('%T.fromPartial(%L)', basicTypeName(typeMap, field), from); | ||
} else { | ||
throw new Error(`Unhandled field ${field}`); | ||
} | ||
}; | ||
// and then use the snippet to handle repeated fields if necessary | ||
func = func.beginControlFlow('if (object.%L)', fieldName); | ||
if (isRepeated(field)) { | ||
if (isMapType(typeMap, messageDesc, field)) { | ||
func = func | ||
.addStatement(`const entry = %L`, readSnippet(`object.${fieldName}`)) | ||
.beginControlFlow('if (entry.value)') | ||
.addStatement('message.%L[entry.key] = entry.value', fieldName) | ||
.endControlFlow(); | ||
} else { | ||
func = func | ||
.beginControlFlow('for (const e of object.%L)', fieldName) | ||
.addStatement(`message.%L.push(%L)`, fieldName, readSnippet('e')) | ||
.endControlFlow(); | ||
} | ||
} else { | ||
func = func.addStatement(`message.%L = %L`, fieldName, readSnippet(`object.${fieldName}`)); | ||
} | ||
func = func.endControlFlow(); | ||
}); | ||
// and then wrap up the switch/while/return | ||
return func.addStatement('return message'); | ||
} | ||
function generateService( | ||
@@ -651,3 +724,3 @@ typeMap: TypeMap, | ||
const inputTypeDesc = inputType[2] as DescriptorProto; | ||
const outputTypeDesc= outputType[2] as DescriptorProto; | ||
const outputTypeDesc = outputType[2] as DescriptorProto; | ||
if (hasSingleRepeatedField(inputTypeDesc) && hasSingleRepeatedField(outputTypeDesc)) { | ||
@@ -654,0 +727,0 @@ const singleMethodName = methodDesc.name.replace('Batch', 'Get'); |
@@ -237,3 +237,3 @@ import { google } from '../build/pbjs'; | ||
const mappedTypes: { [key: string]: TypeName } = { | ||
'.google.protobuf.Timestamp': TypeNames.unionType(TypeNames.DATE, TypeNames.UNDEFINED) | ||
'.google.protobuf.Timestamp': TypeNames.DATE | ||
}; | ||
@@ -240,0 +240,0 @@ |
@@ -0,4 +1,8 @@ | ||
import { google } from '../build/pbjs'; | ||
import { messageToTypeName } from '../src/types'; | ||
import { TypeNames } from 'ts-poet'; | ||
import DescriptorProto = google.protobuf.DescriptorProto; | ||
const fakeProto = (undefined as any) as DescriptorProto; | ||
describe('main', () => { | ||
@@ -8,4 +12,4 @@ describe('messageToTypeName', () => { | ||
// these are not very useful tests now that this is just a map lookup | ||
const typeMap = new Map<string, [string, string]>(); | ||
typeMap.set('namespace.Message', ['namespace', 'Message']); | ||
const typeMap = new Map<string, [string, string, DescriptorProto]>(); | ||
typeMap.set('namespace.Message', ['namespace', 'Message', fakeProto]); | ||
expect(messageToTypeName(typeMap, '.namespace.Message')).toEqual(TypeNames.anyType('Message@./namespace')); | ||
@@ -15,4 +19,4 @@ }); | ||
it('handles nested messages', () => { | ||
const typeMap = new Map<string, [string, string]>(); | ||
typeMap.set('namespace.Message.Inner', ['namespace', 'Message_Inner']); | ||
const typeMap = new Map<string, [string, string, DescriptorProto]>(); | ||
typeMap.set('namespace.Message.Inner', ['namespace', 'Message_Inner', fakeProto]); | ||
expect(messageToTypeName(typeMap, '.namespace.Message.Inner')).toEqual( | ||
@@ -24,3 +28,3 @@ TypeNames.anyType('Message_Inner@./namespace') | ||
it('handles value types', () => { | ||
const typeMap = new Map<string, [string, string]>(); | ||
const typeMap = new Map<string, [string, string, DescriptorProto]>(); | ||
expect(messageToTypeName(typeMap, '.google.protobuf.StringValue')).toEqual( | ||
@@ -27,0 +31,0 @@ TypeNames.unionType(TypeNames.STRING, TypeNames.UNDEFINED) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
836822
11926