--- /dev/null
+/*\r
+ Copyright 2013 Daniel Wirtz <dcode@dcode.io>\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+ */\r
+var ProtoBuf = require(__dirname+"/../index.js"),\r
+ fs = require("fs"),\r
+ path = require("path"),\r
+ cli = require("ascli")("pbjs"),\r
+ yargs = require("yargs"),\r
+ util = require("./pbjs/util.js"),\r
+ glob = require("glob"),\r
+ pkg = require("../package.json");\r
+\r
+/**\r
+ * pbjs namespace.\r
+ * @exports pbjs\r
+ * @namespace\r
+ */\r
+var pbjs = module.exports = {};\r
+\r
+/**\r
+ * @alias pbjs/util\r
+ */\r
+pbjs.util = util;\r
+\r
+/**\r
+ * Source formats.\r
+ * @type {!Object.<string,!function(string,!Object.<string,*>)>}\r
+ */\r
+pbjs.sources = {};\r
+fs.readdirSync(__dirname+"/pbjs/sources").forEach(function(source) {\r
+ if (/\.js$/.test(source)) {\r
+ var src = require(__dirname + "/pbjs/sources/" + source);\r
+ if (!src.exclude)\r
+ pbjs.sources[source.substring(0, source.lastIndexOf("."))] = src;\r
+ }\r
+});\r
+\r
+/**\r
+ * Target formats.\r
+ * @type {!Object.<string,!function(!ProtoBuf.Builder,!Object.<string,*>)>}\r
+ */\r
+pbjs.targets = {};\r
+fs.readdirSync(__dirname+"/pbjs/targets").forEach(function(target) {\r
+ if (/\.js$/.test(target))\r
+ pbjs.targets[target.substring(0, target.lastIndexOf("."))] = require(__dirname + "/pbjs/targets/" + target);\r
+});\r
+\r
+/**\r
+ * Status code: Operation successful\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_OK = 0;\r
+\r
+/**\r
+ * Status code: Displaying usage information\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_USAGE = 1;\r
+\r
+/**\r
+ * Status code: No such include directory\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_ERR_INCLUDE_DIR = 2;\r
+\r
+/**\r
+ * Status code: No such source format\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_ERR_SOURCE_FORMAT = 3;\r
+\r
+/**\r
+ * Status code: No such target format\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_ERR_TARGET_FORMAT = 4;\r
+\r
+/**\r
+ * Status code: No such namespace\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_ERR_NAMESPACE = 5;\r
+\r
+/**\r
+ * Status code: Illegal dependency\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_ERR_DEPENDENCY = 6;\r
+\r
+/**\r
+ * Status code: No matching source files\r
+ * @type {number}\r
+ */\r
+pbjs.STATUS_ERR_NOSOURCE = 7;\r
+\r
+// Makes a table of available source or target formats\r
+function mkOptions(obj) {\r
+ var str = '';\r
+ Object.keys(obj).forEach(function(key) {\r
+ str += "\n "+util.pad(key, 10)+" "+obj[key].description;\r
+ });\r
+ return str;\r
+}\r
+\r
+/**\r
+ * Executes the program.\r
+ * @param {!Array.<string>} argv Command line arguments\r
+ * @returns {number} Status code\r
+ */\r
+pbjs.main = function(argv) {\r
+ var options = yargs\r
+ .usage(cli("pb".white.bold+"js".green.bold, util.pad("ProtoBuf.js v"+pkg['version'], 31, true)+" "+pkg['homepage'].grey) + "\n" +\r
+ "CLI utility to convert between .proto and JSON syntax / to generate classes.\n\n" +\r
+ "Usage: ".white.bold+path.basename(argv[1]).green.bold+" <source files...> [options] [> outFile]")\r
+ .help("help")\r
+ .version(pkg["version"])\r
+ .wrap(null)\r
+ .options({\r
+ source: {\r
+ alias: "s",\r
+ describe: "Specifies the source format. Valid formats are:\n" + mkOptions(pbjs.sources)+"\n"\r
+ },\r
+ target: {\r
+ alias: "t",\r
+ describe: "Specifies the target format. Valid formats are:\n" + mkOptions(pbjs.targets)+"\n"\r
+ },\r
+ using: {\r
+ alias: "u",\r
+ describe: "Specifies an option to apply to the volatile builder\nloading the source, e.g. convertFieldsToCamelCase.",\r
+ type: "array"\r
+ },\r
+ min: {\r
+ alias: "m",\r
+ describe: "Minifies the output.",\r
+ default: false\r
+ },\r
+ path: {\r
+ alias: "p",\r
+ describe: "Adds a directory to the include path."\r
+ },\r
+ legacy: {\r
+ alias: "l",\r
+ describe: "Includes legacy descriptors from google/protobuf/ if\nexplicitly referenced.",\r
+ default: false\r
+ },\r
+ quiet: {\r
+ alias: "q",\r
+ describe: "Suppresses any informatory output to stderr.",\r
+ default: false\r
+ },\r
+ out: {\r
+ alias: "o",\r
+ describe: "Send output to file instead of stdout.",\r
+ },\r
+ use: {\r
+ alias: "i",\r
+ describe: "Specifies an option to apply to the emitted builder\nutilized by your program, e.g. populateAccessors.",\r
+ type: "array"\r
+ },\r
+ exports: {\r
+ alias: "e",\r
+ describe: "Specifies the namespace to export. Defaults to export\nthe root namespace."\r
+ },\r
+ dependency: {\r
+ alias: "d",\r
+ describe: "Library dependency to use when generating classes.\nDefaults to 'protobufjs' for CommonJS, 'protobuf' for\nAMD modules and 'dcodeIO.ProtoBuf' for classes."\r
+ }\r
+ })\r
+ .alias("help", "h")\r
+ .alias("version", "v")\r
+ .check(function (args) {\r
+ if (args.source && typeof pbjs.sources[args.source] !== "function") {\r
+ return "Unrecognized source format: '" + args.source + "'";\r
+ }\r
+\r
+ if (args.target && typeof pbjs.targets[args.target] !== "function") {\r
+ return "Unrecognized target format: '" + args.target + "'";\r
+ }\r
+\r
+ if (args._.length < 3) {\r
+ return "The filename to parse is required.";\r
+ }\r
+\r
+ return true;\r
+ })\r
+ .parse(argv);\r
+\r
+ var start = Date.now(),\r
+ sourceFiles = options._.slice(2);\r
+\r
+ // Expand glob expressions\r
+ var sourceFilesExpand = [];\r
+ for (var i=0; i<sourceFiles.length; ++i) {\r
+ var filename = sourceFiles[i],\r
+ files = glob.sync(filename);\r
+ if (files.length === 0) {\r
+ cli.fail("No matching source files: "+filename);\r
+ return pbjs.STATUS_ERR_NOSOURCE;\r
+ }\r
+ files.forEach(function(filename) {\r
+ if (sourceFilesExpand.indexOf(filename) === -1)\r
+ sourceFilesExpand.push(filename);\r
+ });\r
+ }\r
+ sourceFiles = sourceFilesExpand;\r
+\r
+ if (!options.target)\r
+ options.target = "json";\r
+\r
+ // Set up include paths\r
+ var includePath = Array.isArray(options['path']) ? options['path'] : (typeof options['path'] === 'string' ? [options['path']] : []);\r
+ sourceFiles.forEach(function (sourceFile) {\r
+ var dir = path.dirname(sourceFile);\r
+ if (includePath.indexOf(dir) === -1) {\r
+ includePath.push(dir);\r
+ }\r
+ });\r
+ includePath.forEach(function(path) { // Verify that include paths actually exist\r
+ if (!fs.existsSync(path)) {\r
+ if (!options.quiet)\r
+ cli.fail("No such include directory: "+path);\r
+ return pbjs.STATUS_ERR_INCLUDE_DIR;\r
+ }\r
+ });\r
+ options.path = includePath;\r
+\r
+ // Detect source format if not specified, then verify\r
+ if (typeof options.source !== 'string') {\r
+ var source = fs.readFileSync(sourceFiles[0]).toString("utf8").trim();\r
+ if (source.substring(0,1) === "{")\r
+ options.source = "json";\r
+ else\r
+ options.source = "proto";\r
+ }\r
+\r
+ // Load the source files to a common builder\r
+ var builder = pbjs.sources[options.source](sourceFiles, options);\r
+\r
+ // Validate exports and dependency if set\r
+ if (typeof options.exports !== 'undefined') {\r
+ if (!(builder.lookup(options.exports) instanceof ProtoBuf.Reflect.Namespace)) {\r
+ if (!options.quiet)\r
+ cli.fail("No such export namespace: "+options.exports);\r
+ return pbjs.STATUS_ERR_NAMESPACE;\r
+ }\r
+ if (options.exports.charAt(0) === '.')\r
+ options.exports = options.exports.substring(1);\r
+ }\r
+ if (typeof options.dependency !== 'undefined')\r
+ if (typeof options.dependency !== 'string' || !options.dependency) {\r
+ if (!options.quiet)\r
+ cli.fail("Illegal dependency: "+options.dependency);\r
+ return pbjs.STATUS_ERR_DEPENDENCY;\r
+ }\r
+\r
+ // Perform target conversion\r
+ if (!options.quiet)\r
+ cli.error("\nProcessing: "+sourceFiles.join(", ")+" ...\n");\r
+ var res = pbjs.targets[options.target](builder, options);\r
+ if (options.out){\r
+ fs.writeFileSync(options.out, res);\r
+ }else\r
+ process.stdout.write(res);\r
+ if (!options.quiet)\r
+ cli.error(""),\r
+ cli.ok("Converted "+sourceFiles.length+" source files to "+options.target+" ("+res.length+" bytes, "+(Date.now()-start)+" ms)");\r
+\r
+ return pbjs.STATUS_OK;\r
+};\r