From 708552bb84508364b6e6fdf73906aa69e83854e1 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Wed, 12 Apr 2017 19:03:14 +0200 Subject: [PATCH] Other: Added infrastructure for TypeScript support of extensions --- cli/lib/tsd-jsdoc/publish.js | 1 - cli/targets/static.js | 2 +- ext/descriptor/README.md | 21 ++++ ext/descriptor/index.d.ts | 108 +++++++++++++++++++++ ext/{descriptor.js => descriptor/index.js} | 95 ++++-------------- index.d.ts | 7 ++ scripts/gentests.js | 17 +++- src/type.js | 11 ++- tests/data/mapbox/vector_tile.js | 4 +- tests/data/test.js | 20 ++-- 10 files changed, 187 insertions(+), 99 deletions(-) create mode 100644 ext/descriptor/README.md create mode 100644 ext/descriptor/index.d.ts rename ext/{descriptor.js => descriptor/index.js} (90%) diff --git a/cli/lib/tsd-jsdoc/publish.js b/cli/lib/tsd-jsdoc/publish.js index 3c8bccc9d..9c5c54bd6 100644 --- a/cli/lib/tsd-jsdoc/publish.js +++ b/cli/lib/tsd-jsdoc/publish.js @@ -393,7 +393,6 @@ function notAModuleReference(ref) { return ref.indexOf("module:") === -1; } - // handles a class or class-like function handleClass(element, parent) { var is_interface = isInterface(element); diff --git a/cli/targets/static.js b/cli/targets/static.js index 525720399..c589a67cd 100644 --- a/cli/targets/static.js +++ b/cli/targets/static.js @@ -367,7 +367,7 @@ function buildType(ref, type) { "@constructor", "@param {" + fullName + "$Properties=} [" + (config.beautify ? "properties" : "p") + "] Properties to set" ]); - buildFunction(type, type.name, Class.generate(type)); + buildFunction(type, type.name, Type.generateConstructor(type)); // default values var firstField = true; diff --git a/ext/descriptor/README.md b/ext/descriptor/README.md new file mode 100644 index 000000000..95df5a885 --- /dev/null +++ b/ext/descriptor/README.md @@ -0,0 +1,21 @@ +protobufjs/ext/descriptor +========================= + +Experimental [protobuf.js](https://github.com/dcodeIO/protobuf.js) extension for interoperability with descriptor.proto types. + +Usage +----- + +```js +var protobuf = require("protobufjs"), + descriptor = require("protobufjs/ext/descriptor"); + +var descriptor = ...; // either a FieldDescriptorSet buffer or JSON object +var root = protobuf.Root.fromDescriptor(descriptor); +var rootDescriptor = root.toDescriptor("proto3"); +``` + +API +--- + +The extension adds `.fromDescriptor(descriptor[, syntax])` and `#toDescriptor([syntax])` methods to reflection objects and exports the internally used `Root` instance that contains the types present in descriptor.proto. diff --git a/ext/descriptor/index.d.ts b/ext/descriptor/index.d.ts new file mode 100644 index 000000000..849ea236d --- /dev/null +++ b/ext/descriptor/index.d.ts @@ -0,0 +1,108 @@ +import * as $protobuf from "../.."; + +declare const descriptor: $protobuf.Root; + +interface IFileDescriptorSet { + file: IFileDescriptorProto[]; +} + +interface IFileDescriptorProto { + name?: string; + package?: string; + dependency?: any; + publicDependency?: any; + weakDependency?: any; + messageType?: IDescriptorProto[]; + enumType?: IEnumDescriptorProto[]; + service?: IServiceDescriptorProto[]; + extension?: IFieldDescriptorProto[]; + options?: any; + sourceCodeInfo?: any; + syntax?: string; +} + +interface IDescriptorProto { + name?: string; + field?: IFieldDescriptorProto[]; + extension?: IFieldDescriptorProto[]; + nestedType?: IDescriptorProto[]; + enumType?: IEnumDescriptorProto[]; + extensionRange?: IExtensionRange[]; + oneofDecl?: IOneofDescriptorProto[]; + options?: IMessageOptions; + reservedRange?: IReservedRange[]; + reservedName?: string[]; +} + +interface IMessageOptions { + mapEntry?: any; +} + +interface IExtensionRange { + start?: number; + end?: number; +} + +interface IReservedRange { + start?: number; + end?: number; +} + +interface IFieldDescriptorProto { + name?: string; + number?: number; + label?: IFieldDescriptorProto_Label; + type?: IFieldDescriptorProto_Type; + typeName?: string; + extendee?: string; + defaultValue?: any; + oneofIndex?: number; + jsonName?: any; + options?: IFieldOptions; +} + +type IFieldDescriptorProto_Label = number; + +type IFieldDescriptorProto_Type = number; + +interface IFieldOptions { + packed?: boolean; +} + +interface IEnumDescriptorProto { + name?: string; + value?: IEnumValueDescriptorProto[]; + options?: IEnumOptions; +} + +interface IEnumValueDescriptorProto { + name?: string; + number?: number; + options?: any; +} + +interface IEnumOptions { + allowAlias?: boolean; +} + +interface IOneofDescriptorProto { + name?: string; + options?: any; +} + +interface IServiceDescriptorProto { + name?: string; + method?: IMethodDescriptorProto[]; + options?: any; +} + +interface IMethodDescriptorProto { + name?: string; + inputType?: string; + outputType?: string; + options?: any; + clientStreaming?: boolean; + serverStreaming?: boolean; +} + +export = descriptor; diff --git a/ext/descriptor.js b/ext/descriptor/index.js similarity index 90% rename from ext/descriptor.js rename to ext/descriptor/index.js index 88796fab3..9959b40e0 100644 --- a/ext/descriptor.js +++ b/ext/descriptor/index.js @@ -1,35 +1,26 @@ -// [WIP] Extension for reflection interoperability with descriptor.proto types -// var protobuf = require("protobufjs"), -// descriptor = require("protobufjs/ext/descriptor"); -// ... "use strict"; -var protobuf = require(".."); +var $protobuf = require(".."); /** * Descriptor extension (ext/descriptor). - * @namespace + * @type {Root} + * @tstype $protobuf.Root + * @const */ - -var descriptor = module.exports = protobuf.Root.fromJSON(require("../google/protobuf/descriptor.json")).lookup(".google.protobuf"); +var descriptor = module.exports = $protobuf.Root.fromJSON(require("../google/protobuf/descriptor.json")).lookup(".google.protobuf"); var google = descriptor, - Root = protobuf.Root, - Enum = protobuf.Enum, - Type = protobuf.Type, - Field = protobuf.Field, - OneOf = protobuf.OneOf, - Service = protobuf.Service, - Method = protobuf.Method; + Root = $protobuf.Root, + Enum = $protobuf.Enum, + Type = $protobuf.Type, + Field = $protobuf.Field, + OneOf = $protobuf.OneOf, + Service = $protobuf.Service, + Method = $protobuf.Method; // --- Root --- -/** - * Reflected type describing a root. - * @name descriptor.FileDescriptorSet - * @type {Type} - */ - /** * @interface IFileDescriptorSet * @property {IFileDescriptorProto[]} file @@ -118,12 +109,6 @@ Root.prototype.toDescriptor = function toDescriptor(syntax) { // --- Type --- -/** - * Reflected type describing a type. - * @name descriptor.DescriptorProto - * @type {Type} - */ - /** * @interface IDescriptorProto * @property {string} [name] @@ -175,15 +160,15 @@ Type.fromDescriptor = function fromDescriptor(descriptor, syntax) { i; /* Fields */ for (i = 0; i < descriptor.field.length; ++i) - type.add(protobuf.Field.fromDescriptor(descriptor.field[i], syntax)); + type.add(Field.fromDescriptor(descriptor.field[i], syntax)); /* Extension fields */ for (i = 0; i < descriptor.extension.length; ++i) - type.add(protobuf.Field.fromDescriptor(descriptor.extension[i], syntax)); + type.add(Field.fromDescriptor(descriptor.extension[i], syntax)); /* Oneofs */ for (i = 0; i < descriptor.oneofDecl.length; ++i) - type.add(protobuf.OneOf.fromDescriptor(descriptor.oneofDecl[i])); + type.add(OneOf.fromDescriptor(descriptor.oneofDecl[i])); /* Nested types */ for (i = 0; i < descriptor.nestedType.length; ++i) - type.add(protobuf.Type.fromDescriptor(descriptor.nestedType[i], syntax)); + type.add(Type.fromDescriptor(descriptor.nestedType[i], syntax)); /* Nested enums */ for (i = 0; i < descriptor.enumType.length; ++i) - type.add(protobuf.Enum.fromDescriptor(descriptor.enumType[i])); + type.add(Enum.fromDescriptor(descriptor.enumType[i])); /* Extension ranges */ if (descriptor.extensionRange.length) { type.extensions = []; for (i = 0; i < descriptor.extensionRange.length; ++i) @@ -236,18 +221,10 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) { // --- Field --- -/** - * Reflected type describing a field. - * @name descriptor.FieldDescriptorProto - * @type {Type} - * @property {Enum} Label Reflected descriptor describing a field label (rule) - * @property {Enum} Type Reflected descriptor describing a field type - */ - /** * @interface IFieldDescriptorProto * @property {string} [name] - * @property {number} [number} + * @property {number} [number] * @property {IFieldDescriptorProto_Label} [label] * @property {IFieldDescriptorProto_Type} [type] * @property {string} [typeName] @@ -424,24 +401,6 @@ Field.prototype.toDescriptor = function toDescriptor(syntax) { // --- Enum --- -/** - * Reflected type describing an enum. - * @name descriptor.EnumDescriptorProto - * @type {Type} - */ - -/** - * Reflected type describing an enum value. - * @name descriptor.EnumValueDescriptorProto - * @type {Type} - */ - -/** - * Reflected type describing enum options. - * @name descriptor.EnumOptions - * @type {Type} - */ - /** * @interface IEnumDescriptorProto * @property {string} [name] @@ -511,12 +470,6 @@ Enum.prototype.toDescriptor = function toDescriptor() { // --- OneOf --- -/** - * Reflected type describing a oneof. - * @name descriptor.OneofDescriptorProto - * @type {Type} - */ - /** * @interface IOneofDescriptorProto * @property {string} [name] @@ -556,12 +509,6 @@ OneOf.prototype.toDescriptor = function toDescriptor() { // --- Service --- -/** - * Reflected type describing a service. - * @name descriptor.ServiceDescriptorProto - * @type {Type} - */ - /** * @interface IServiceDescriptorProto * @property {string} [name] @@ -610,12 +557,6 @@ Service.prototype.toDescriptor = function toDescriptor() { // --- Method --- -/** - * Reflected type describing a method. - * @name descriptor.MethodDescriptorProto - * @type {Type} - */ - /** * @interface IMethodDescriptorProto * @property {string} [name] diff --git a/index.d.ts b/index.d.ts index 6eb11eae6..8fb6acb99 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1719,6 +1719,13 @@ export class Type extends NamespaceBase { */ public ctor: Constructor<{}>; + /** + * Generates a constructor function for the specified type. + * @param {Type} type Type + * @returns {Codegen} Codegen instance + */ + public static generateConstructor(type: Type): Codegen; + /** * Creates a message type from a message type descriptor. * @param {string} name Message name diff --git a/scripts/gentests.js b/scripts/gentests.js index 594c1ac0f..5e1597bd9 100644 --- a/scripts/gentests.js +++ b/scripts/gentests.js @@ -42,10 +42,11 @@ var fs = require("fs"), }); [ - "tests/data/rpc.js", - "tests/data/test.js", + { file: "tests/data/rpc.js" }, + { file: "tests/data/test.js" }, + { file: "ext/descriptor/index.js", ext: true } ] -.forEach(function(file) { +.forEach(function({ file, ext }) { var out = file.replace(/\.js$/, ".d.ts"); pbts.main([ "--no-comments", @@ -54,7 +55,13 @@ var fs = require("fs"), if (err) throw err; var pathToProtobufjs = path.relative(path.dirname(out), "").replace(/\\/g, "/"); - fs.writeFileSync(out, output.replace(/"protobufjs"/g, JSON.stringify(pathToProtobufjs))); + output = output.replace(/"protobufjs"/g, JSON.stringify(pathToProtobufjs)); + if (ext) { + var extName; + output = output.replace(/export (\w+) (\w+)/, function($0, $1, $2) { extName = $2; return "declare " + $1 + " " + extName; }); + output += "\nexport = " + extName + ";\n"; + } + fs.writeFileSync(out, output); process.stdout.write("pbts: " + file + " -> " + out + "\n"); }); -}); \ No newline at end of file +}); diff --git a/src/type.js b/src/type.js index 4e16904fa..17014d355 100644 --- a/src/type.js +++ b/src/type.js @@ -152,7 +152,7 @@ Object.defineProperties(Type.prototype, { */ ctor: { get: function() { - return this._ctor || (this.ctor = generateConstructor(this).eof(this.name)); + return this._ctor || (this.ctor = Type.generateConstructor(this).eof(this.name)); }, set: function(ctor) { @@ -189,7 +189,12 @@ Object.defineProperties(Type.prototype, { } }); -function generateConstructor(type) { +/** + * Generates a constructor function for the specified type. + * @param {Type} type Type + * @returns {Codegen} Codegen instance + */ +Type.generateConstructor = function generateConstructor(type) { /* eslint-disable no-unexpected-multiline */ var gen = util.codegen("p"); // explicitly initialize mutable object/array fields so that these aren't just inherited from the prototype @@ -202,7 +207,7 @@ function generateConstructor(type) { ("if(p)for(var ks=Object.keys(p),i=0;i>> 0, message.negativeIntValue.high >>> 0).toNumber() : message.negativeIntValue; if (message.doubleValue != null && message.hasOwnProperty("doubleValue")) - object.doubleValue = message.doubleValue; + object.doubleValue = options.json && !isFinite(message.doubleValue) ? String(message.doubleValue) : message.doubleValue; if (message.stringValue != null && message.hasOwnProperty("stringValue")) object.stringValue = options.bytes === String ? $util.base64.encode(message.stringValue, 0, message.stringValue.length) : options.bytes === Array ? Array.prototype.slice.call(message.stringValue) : message.stringValue; if (message.aggregateValue != null && message.hasOwnProperty("aggregateValue"))