From 4bfe0c239b9c337f8fa64ea64f6a71baf5639b84 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Fri, 24 Mar 2017 22:41:04 +0100 Subject: [PATCH] CLI: More progress on decoupling the CLI; Docs: Explained the JSON structure in README and moved CLI specific information to the CLI package --- README.md | 175 +++++----------------------------- cli/README.md | 185 ++++++++++++++++++++++++++++++++++++ cli/package.json | 33 +------ cli/package.standalone.json | 32 +++++++ cli/pbjs.js | 6 +- cli/pbts.js | 2 +- cli/util.js | 11 ++- 7 files changed, 255 insertions(+), 189 deletions(-) create mode 100644 cli/README.md create mode 100644 cli/package.standalone.json diff --git a/README.md b/README.md index caa0ed7c8..68ce7a0f9 100644 --- a/README.md +++ b/README.md @@ -252,9 +252,7 @@ protobuf.load("awesome.proto") ### Using JSON descriptors -The library utilizes a JSON format that is equivalent to a .proto definition (see also: [Command line usage](#command-line)). - -The following is identical to the .proto definition seen above, but it can also be used with just the light library because it doesn't require the parser: +The library utilizes a JSON format that is equivalent to a .proto definition. For example, the following is identical to the .proto definition seen above: ```json // awesome.json @@ -272,6 +270,25 @@ The following is identical to the .proto definition seen above, but it can also } ``` +The JSON format closely resembles the internal reflection structure: + +| Type (T) | Extends | Type-specific properties +|--------------------|--------------------|------------------------- +| *ReflectionObject* | | options +| *Namespace* | *ReflectionObject* | nested +| Type | *Namespace* | **fields** +| Enum | *ReflectionObject* | **values** +| Field | *ReflectionObject* | rule, **type**, **id** +| MapField | Field | **keyType** +| Service | *Namespace* | **methods** +| Method | *ReflectionObject* | *type*, **requestType**, **responseType**, requestStream, responseStream + +* **Bold** properties are required. *Italic* types are abstract. +* `T.fromJSON(name, json)` creates the respective reflection object from a JSON descriptor +* `T#toJSON()` creates a JSON descriptor from the respective reflection object (`name` is used as the key within the parent) + +Exclusively using JSON instead of .proto files enables the use of just the light library (the parser isn't required in this case). + A JSON descriptor can either be loaded the usual way: ```js @@ -438,9 +455,7 @@ protobuf.load("awesome.proto", function(err, root) { }); ``` -To achieve the same with static code generated by [pbjs](#command-line), there is the [pbts](#generating-typescript-definitions-from-static-modules) command line utility to generate type definitions from static code as well. - -Let's say you generated your static code to `bundle.js` and its type definitions to `bundle.d.ts`, then you can do: +If you generated static code using the CLI to `bundle.js` and its type definitions to `bundle.d.ts`, then you can do: ```ts import * as root from "./bundle.js"; @@ -470,6 +485,7 @@ Documentation #### protobuf.js * [API Documentation](http://dcode.io/protobuf.js) +* [CLI Documentation](./cli/README.md) * [CHANGELOG](https://github.com/dcodeIO/protobuf.js/blob/master/CHANGELOG.md) * [Frequently asked questions](https://github.com/dcodeIO/protobuf.js/wiki) on our wiki @@ -478,152 +494,7 @@ Documentation Command line ------------ - -The `pbjs` command line utility can be used to bundle and translate between .proto and .json files. It also generates static code. - -``` -Consolidates imports and converts between file formats. - - -t, --target Specifies the target format. Also accepts a path to require a custom target. - - json JSON representation - json-module JSON representation as a module - proto2 Protocol Buffers, Version 2 - proto3 Protocol Buffers, Version 3 - static Static code without reflection - static-module Static code without reflection as a module - - -p, --path Adds a directory to the include path. - - -o, --out Saves to a file instead of writing to stdout. - - Module targets only: - - -w, --wrap Specifies the wrapper to use. Also accepts a path to require a custom wrapper. - - default Default wrapper supporting both CommonJS and AMD - commonjs CommonJS wrapper - amd AMD wrapper - es6 ES6 wrapper (implies --es6) - - -r, --root Specifies an alternative protobuf.roots name. - - -l, --lint Linter configuration. Defaults to protobuf.js-compatible rules: - - eslint-disable block-scoped-var, no-redeclare, no-control-regex, no-prototype-builtins - - --es6 Enables ES6 syntax (const/let instead of var) - - Proto sources only: - - --keep-case Keeps field casing instead of converting to camel case. - - Static targets only: - - --no-create Does not generate create functions used for reflection compatibility. - --no-encode Does not generate encode functions. - --no-decode Does not generate decode functions. - --no-verify Does not generate verify functions. - --no-convert Does not generate convert functions like from/toObject - --no-delimited Does not generate delimited encode/decode functions. - --no-beautify Does not beautify generated code. - --no-comments Does not output any JSDoc comments. - -usage: pbjs [options] file1.proto file2.json ... (or) other | pbjs [options] - -``` - -For production environments it is recommended to bundle all your .proto files to a single .json file, which minimizes the number of network requests and avoids any parser overhead (hint: works with just the [light library](#distributions)): - -``` -$> pbjs -t json file1.proto file2.proto > bundle.json -``` - -Now, either include this file in your final bundle: - -```js -var root = protobuf.Root.fromJSON(require("./bundle.json")); -``` - -or load it the usual way: - -```js -protobuf.load("bundle.json", function(err, root) { - ... -}); -``` - -The `pbjs` utility is also capable of generating static code (hint: works with just the [minimal library](#distributions)). For example - -``` -$> pbjs -t static-module -w commonjs -o compiled.js file1.proto file2.proto -``` - -will generate static code for definitions within `file1.proto` and `file2.proto` to a CommonJS module `compiled.js`. - -**ProTip!** Documenting your .proto files with `/** ... */`-blocks or (trailing) `/// ...` lines translates to generated static code. - -### Generating TypeScript definitions from static modules - -Likewise, the `pbts` command line utility can be used to generate TypeScript definitions from `pbjs`-generated static modules. - -``` -Generates TypeScript definitions from annotated JavaScript files. - - -o, --out Saves to a file instead of writing to stdout. - - -g, --global Name of the global object in browser environments, if any. - - --no-comments Does not output any JSDoc comments. - - Internal flags: - - -n, --name Wraps everything in a module of the specified name. - - -m, --main Whether building the main library without any imports. - -usage: pbts [options] file1.js file2.js ... (or) other | pbts [options] - -``` - -Picking up on the example above, the following not just generates static code to a CommonJS module `compiled.js` but also its respective TypeScript definitions to `compiled.d.ts`: - -``` -$> pbjs -t static-module -w commonjs -o compiled.js file1.proto file2.proto -$> pbts -o compiled.d.ts compiled.js -``` - -Additionally, TypeScript definitions of static modules are compatible with their reflection-based counterparts (i.e. as exported by JSON modules), as long as the following conditions are met: - -1. Instead of using `new SomeMessage(...)`, always use `SomeMessage.create(...)` because reflection objects do not provide a constructor. -2. Types, services and enums must start with an uppercase letter to become available as properties of the reflected types as well (i.e. to be able to use `MyMessage.MyEnum` instead of `root.lookup("MyMessage.MyEnum")`). - -For example, the following generates a JSON module `bundle.js` and a `bundle.d.ts`, but no static code: - -``` -$> pbjs -t json-module -w commonjs -o bundle.js file1.proto file2.proto -$> pbjs -t static-module file1.proto file2.proto | pbts -o bundle.d.ts - -``` - -### On reflection vs. static code - -While using .proto files directly requires the full library respectively pure reflection/JSON the light library, pretty much all code but the relatively short descriptors is shared. - -Static code, on the other hand, requires just the minimal library, but generates additional, albeit editable, source code without any reflection features. - -There is no significant difference performance-wise as the code generated statically is pretty much the same as generated at runtime and both are largely interchangeable as seen in the previous section. - -### Using pbjs and pbts programmatically - -Both utilities can be used programmatically by providing command line arguments and a callback to their respective `main` functions: - -```js -var pbjs = require("protobufjs/cli/pbjs"); - -pbjs.main([ "--target", "json-module", "path/to/myproto.proto" ], function(err, output) { - if (err) - throw err; - // do something with output -}); -``` +Command line usage has moved to the (soon to be decoupled) [CLI package]((./cli/README.md)) Performance ----------- diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 000000000..2bc3b589e --- /dev/null +++ b/cli/README.md @@ -0,0 +1,185 @@ +protobufjs-cli +============== +[![npm](https://img.shields.io/npm/v/protobufjscli.svg)](https://www.npmjs.com/package/protobufjs-cli) + +Command line interface (CLI) for [protobuf.js](https://github.com/dcodeIO/protobuf.js). Translates between file formats and generates static code as well as TypeScript definitions. + +Contents +-------- + + + +* [Usage](#usage)
+ An introduction to the toolset. + +* [API](#api)
+ Details on using pbjs and pbts programmatically. + + + +Usage +----- + +### pbjs + +``` +Translates between file formats and generates static code. + + -t, --target Specifies the target format. Also accepts a path to require a custom target. + + json JSON representation + json-module JSON representation as a module + proto2 Protocol Buffers, Version 2 + proto3 Protocol Buffers, Version 3 + static Static code without reflection + static-module Static code without reflection as a module + + -p, --path Adds a directory to the include path. + + -o, --out Saves to a file instead of writing to stdout. + + Module targets only: + + -w, --wrap Specifies the wrapper to use. Also accepts a path to require a custom wrapper. + + default Default wrapper supporting both CommonJS and AMD + commonjs CommonJS wrapper + amd AMD wrapper + es6 ES6 wrapper (implies --es6) + + -r, --root Specifies an alternative protobuf.roots name. + + -l, --lint Linter configuration. Defaults to protobuf.js-compatible rules: + + eslint-disable block-scoped-var, no-redeclare, no-control-regex, no-prototype-builtins + + --es6 Enables ES6 syntax (const/let instead of var) + + Proto sources only: + + --keep-case Keeps field casing instead of converting to camel case. + + Static targets only: + + --no-create Does not generate create functions used for reflection compatibility. + --no-encode Does not generate encode functions. + --no-decode Does not generate decode functions. + --no-verify Does not generate verify functions. + --no-convert Does not generate convert functions like from/toObject + --no-delimited Does not generate delimited encode/decode functions. + --no-beautify Does not beautify generated code. + --no-comments Does not output any JSDoc comments. + +usage: pbjs [options] file1.proto file2.json ... (or) other | pbjs [options] - +``` + +For production environments it is recommended to bundle all your .proto files to a single .json file, which minimizes the number of network requests and avoids any parser overhead (hint: works with just the **light** library): + +``` +$> pbjs -t json file1.proto file2.proto > bundle.json +``` + +Now, either include this file in your final bundle: + +```js +var root = protobuf.Root.fromJSON(require("./bundle.json")); +``` + +or load it the usual way: + +```js +protobuf.load("bundle.json", function(err, root) { + ... +}); +``` + +Generated static code, on the other hand, works with just the **minimal** library. For example + +``` +$> pbjs -t static-module -w commonjs -o compiled.js file1.proto file2.proto +``` + +will generate static code for definitions within `file1.proto` and `file2.proto` to a CommonJS module `compiled.js`. + +**ProTip!** Documenting your .proto files with `/** ... */`-blocks or (trailing) `/// ...` lines translates to generated static code. + + +### pbts + +``` +Generates TypeScript definitions from annotated JavaScript files. + + -o, --out Saves to a file instead of writing to stdout. + + -g, --global Name of the global object in browser environments, if any. + + --no-comments Does not output any JSDoc comments. + + Internal flags: + + -n, --name Wraps everything in a module of the specified name. + + -m, --main Whether building the main library without any imports. + +usage: pbts [options] file1.js file2.js ... (or) other | pbts [options] - +``` + +Picking up on the example above, the following not just generates static code to a CommonJS module `compiled.js` but also its respective TypeScript definitions to `compiled.d.ts`: + +``` +$> pbjs -t static-module -w commonjs -o compiled.js file1.proto file2.proto +$> pbts -o compiled.d.ts compiled.js +``` + +Additionally, TypeScript definitions of static modules are compatible with their reflection-based counterparts (i.e. as exported by JSON modules), as long as the following conditions are met: + +1. Instead of using `new SomeMessage(...)`, always use `SomeMessage.create(...)` because reflection objects do not provide a constructor. +2. Types, services and enums must start with an uppercase letter to become available as properties of the reflected types as well (i.e. to be able to use `MyMessage.MyEnum` instead of `root.lookup("MyMessage.MyEnum")`). + +For example, the following generates a JSON module `bundle.js` and a `bundle.d.ts`, but no static code: + +``` +$> pbjs -t json-module -w commonjs -o bundle.js file1.proto file2.proto +$> pbjs -t static-module file1.proto file2.proto | pbts -o bundle.d.ts - +``` + +### Reflection vs. static code + +While using .proto files directly requires the full library respectively pure reflection/JSON the light library, pretty much all code but the relatively short descriptors is shared. + +Static code, on the other hand, requires just the minimal library, but generates additional, albeit editable, source code without any reflection features. + +There is no significant difference performance-wise as the code generated statically is pretty much the same as generated at runtime and both are largely interchangeable as seen in the previous section. + +API +--- + +Both utilities can be used programmatically by providing command line arguments and a callback to their respective `main` functions: + +```js +var pbjs = require("protobufjs-cli/pbjs"); // or require("protobufjs-cli").pbjs / .pbts + +pbjs.main([ "--target", "json-module", "path/to/myproto.proto" ], function(err, output) { + if (err) + throw err; + // do something with output +}); +``` + +**License:** [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause) diff --git a/cli/package.json b/cli/package.json index a21f4f2c8..9e26dfeeb 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,32 +1 @@ -{ - "name": "protobufjs-cli", - "description": "protobuf.js command line interface (CLI).", - "version": "6.7.0", - "author": "Daniel Wirtz ", - "repository": { - "type": "git", - "url": "https://github.com/dcodeIO/protobuf.js.git" - }, - "license": "BSD-3-Clause", - "main": "index.js", - "types": "index.d.ts", - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - }, - "peerDependencies": { - "protobufjs": "6.7.0" - }, - "dependencies": { - "chalk": "^1.1.3", - "escodegen": "^1.8.1", - "espree": "^3.1.3", - "estraverse": "^4.2.0", - "glob": "^7.1.1", - "jsdoc": "^3.4.2", - "minimist": "^1.2.0", - "semver": "^5.3.0", - "tmp": "0.0.31", - "uglify-js": "^2.8.15" - } -} \ No newline at end of file +{} \ No newline at end of file diff --git a/cli/package.standalone.json b/cli/package.standalone.json new file mode 100644 index 000000000..aacd040dc --- /dev/null +++ b/cli/package.standalone.json @@ -0,0 +1,32 @@ +{ + "name": "protobufjs-cli", + "description": "Translates between file formats and generates static code as well as TypeScript definitions.", + "version": "6.7.0", + "author": "Daniel Wirtz ", + "repository": { + "type": "git", + "url": "https://github.com/dcodeIO/protobuf.js.git" + }, + "license": "BSD-3-Clause", + "main": "index.js", + "types": "index.d.ts", + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + }, + "peerDependencies": { + "protobufjs": "~6.7.0" + }, + "dependencies": { + "chalk": "^1.1.3", + "escodegen": "^1.8.1", + "espree": "^3.1.3", + "estraverse": "^4.2.0", + "glob": "^7.1.1", + "jsdoc": "^3.4.2", + "minimist": "^1.2.0", + "semver": "^5.3.0", + "tmp": "0.0.31", + "uglify-js": "^2.8.15" + } +} \ No newline at end of file diff --git a/cli/pbjs.js b/cli/pbjs.js index b062125bd..553467613 100644 --- a/cli/pbjs.js +++ b/cli/pbjs.js @@ -1,12 +1,12 @@ "use strict"; var path = require("path"), fs = require("fs"), - pkg = require(path.join(__dirname, "package.json")), + pkg = require("./package.json"), util = require("./util"); util.setup(); -var protobuf = require(".."), +var protobuf = require(util.pathToProtobufJs), minimist = require("minimist"), chalk = require("chalk"), glob = require("glob"); @@ -66,7 +66,7 @@ exports.main = function main(args, callback) { process.stderr.write([ "protobuf.js v" + pkg.version + " CLI for JavaScript", "", - chalk.bold.white("Consolidates imports and converts between file formats."), + chalk.bold.white("Translates between file formats and generates static code."), "", " -t, --target Specifies the target format. Also accepts a path to require a custom target.", "", diff --git a/cli/pbts.js b/cli/pbts.js index 68b6c501e..e47571668 100644 --- a/cli/pbts.js +++ b/cli/pbts.js @@ -2,7 +2,7 @@ var child_process = require("child_process"), path = require("path"), fs = require("fs"), - pkg = require(path.join(__dirname, "./package.json")), + pkg = require("./package.json"), util = require("./util"); util.setup(); diff --git a/cli/util.js b/cli/util.js index 389302331..294e7e0ad 100644 --- a/cli/util.js +++ b/cli/util.js @@ -5,7 +5,16 @@ var fs = require("fs"), var semver; -var protobuf = require(".."); +try { + // installed as a peer dependency + require.resolve("protobufjs"); + exports.pathToProtobufJs = "protobufjs"; +} catch (e) { + // local development, i.e. forked from github + exports.pathToProtobufJs = ".."; +} + +var protobuf = require(exports.pathToProtobufJs); function basenameCompare(a, b) { var aa = String(a).replace(/\.\w+$/, "").split(/(-?\d*\.?\d+)/g),