diff --git a/README.md b/README.md index 4f0d4d5cb..a4bfd41ee 100644 --- a/README.md +++ b/README.md @@ -295,7 +295,10 @@ The `pbjs` command line utility can be used to bundle and translate between .pro Consolidates imports and converts between file formats. -t, --target Specifies the target format. [json, proto2, proto3] + Also accepts a path to require a custom target. + -p, --path Adds a directory to the include path. + -o, --out Saves to a file instead of writing to stdout. usage: pbjs [options] file1.proto file2.json ... diff --git a/cli/pbjs.js b/cli/pbjs.js index bf94a3a75..3f0864466 100644 --- a/cli/pbjs.js +++ b/cli/pbjs.js @@ -16,13 +16,15 @@ exports.main = function(args) { alias: { target : "t", out : "o", - path : "p" + path : "p", + wrap : "w" }, - string: [ "target", "out", "path" ], + string: [ "target", "out", "path", "wrap" ], default: { target: "json" } }); + var target = targets[argv.target], files = argv._, paths = typeof argv.path === 'string' ? [ argv.path ] : argv.path || []; @@ -35,17 +37,18 @@ exports.main = function(args) { "", " -t, --target Specifies the target format. [" + Object.keys(targets).filter(function(key) { return !targets[key].private; }).join(', ') + "]", " Also accepts a path to require a custom target.", + "", " -p, --path Adds a directory to the include path.", + "", " -o, --out Saves to a file instead of writing to stdout.", "", + " -w, --wrap Specifies an alternative wrapper for the static target.", + "", "usage: " + chalk.bold.green(path.basename(process.argv[1])) + " [options] file1.proto file2.json ..." ].join("\n")); return 1; } - if (!target) - target = require(path.resolve(process.cwd(), argv.target)); - // Resolve glob expressions for (var i = 0; i < files.length;) { if (glob.hasMagic(files[i])) { @@ -56,9 +59,13 @@ exports.main = function(args) { ++i; } + // Require custom target + if (!target) + target = require(path.resolve(process.cwd(), argv.target)); + var root = new protobuf.Root(); - // Fall back to include paths when resolving imports + // Search include paths when resolving imports root.resolvePath = function pbjsResolvePath(origin, target) { var filepath = protobuf.util.resolvePath(origin, target); if (fs.existsSync(filepath)) @@ -71,12 +78,10 @@ exports.main = function(args) { return filepath; }; - var options = {}; - root.load(files, function(err) { if (err) throw err; - target(root, options, function(err, output) { + target(root, argv, function(err, output) { if (err) throw err; if (output !== "") { diff --git a/cli/targets/json.js b/cli/targets/json.js index 932a1525f..722390106 100644 --- a/cli/targets/json.js +++ b/cli/targets/json.js @@ -1,3 +1,4 @@ +"use strict"; module.exports = json_target; var protobuf = require("../.."); diff --git a/cli/targets/proto.js b/cli/targets/proto.js index a15394dc3..66f2d37e0 100644 --- a/cli/targets/proto.js +++ b/cli/targets/proto.js @@ -1,3 +1,4 @@ +"use strict"; module.exports = proto_target; proto_target.private = true; diff --git a/cli/targets/proto2.js b/cli/targets/proto2.js index 5c1114d46..c5fcc3217 100644 --- a/cli/targets/proto2.js +++ b/cli/targets/proto2.js @@ -1,3 +1,4 @@ +"use strict"; module.exports = proto2_target; var protobuf = require("../.."); diff --git a/cli/targets/proto3.js b/cli/targets/proto3.js index 2bc155458..40dacfe76 100644 --- a/cli/targets/proto3.js +++ b/cli/targets/proto3.js @@ -1,3 +1,4 @@ +"use strict"; module.exports = proto3_target; var protobuf = require("../.."); diff --git a/cli/targets/static.js b/cli/targets/static.js index e4383c36e..67d108687 100644 --- a/cli/targets/static.js +++ b/cli/targets/static.js @@ -1,15 +1,15 @@ +"use strict"; module.exports = static_target; -static_target.private = true; +// - Static code does not have any reflection or JSON features. +// - The default wrapper supports AMD, CommonJS and the global scope (as window.root), in this order. +// - You can specify a custom wrapper with the --wrap argument. +// - CommonJS modules depend on the minimal static runtime for reduced package size with browserify. +// - AMD and global scope depend on the full library for now. +// - Services aren't supported, yet. -// Currently, this target builds single file CommonJS modules. -// - There is no reflection and no message inheritance from Prototype. -// - Generated code is tailored for browerify build processes (minimal runtime). - -// TBD: -// - Generate a single file or scaffold an entire project directory? Both? -// - Targets: ES5, ES6, TypeScript? CommonJS? AMD? -// - What about generating typescript definitions for non-ts targets? +var path = require("path"), + fs = require("fs"); var protobuf = require("../.."); @@ -26,14 +26,21 @@ var out = []; var indent = 0; function static_target(root, options, callback) { - tree = {}; + if (options.wrap) + options.wrap = path.resolve(process.cwd(), options.wrap); + else + options.wrap = path.join(__dirname, "static.tpl"); try { - buildNamespace("module.exports", root); - callback(null, out.join('\n')); + var wrap = fs.readFileSync(options.wrap).toString("utf8"); + ++indent; + buildNamespace(null, root); + --indent; + callback(null, wrap.replace(/\r?\n%OUTPUT%/, out.join('\n'))); } catch (err) { callback(err); } finally { out = []; + indent = 0; } } @@ -63,17 +70,7 @@ function name(name) { function buildNamespace(ref, ns) { if (!ns) return; - if (ns.name === "") { // root - push(name(ref) + " = (function() {"); - ++indent; - push('"use strict";'); - push(""); - push("// Minimal static codegen runtime"); - push("var $runtime = require(\"protobufjs/runtime\");") - push(""); - push("// Lazily resolved type references"); - push("var $lazyTypes = [];"); - } else { + if (ns.name !== "") { push(""); push("/** @alias " + ns.fullName.substring(1) + " */"); push(name(ref) + "." + name(ns.name) + " = (function() {"); @@ -84,7 +81,7 @@ function buildNamespace(ref, ns) { buildType(undefined, ns); } else if (ns instanceof Service) buildService(undefined, ns); - else { + else if (ns.name !== "") { push(""); push("/** @alias " + (ns.name && ns.fullName.substring(1) || "exports") + " */"); push("var " + name(ns.name) + " = {};"); @@ -96,13 +93,12 @@ function buildNamespace(ref, ns) { else if (nested instanceof Namespace) buildNamespace(ns.name, nested); }); - push(""); - if (ns.name === "") // root - push("return $runtime.resolve($root, $lazyTypes);"); - else + if (ns.name !== "") { + push(""); push("return " + name(ns.name) + ";"); - --indent; - push("})();"); + --indent; + push("})();"); + } } function buildFunction(type, functionName, gen, scope) { @@ -235,17 +231,24 @@ function buildType(ref, type) { function buildService(ref, service) { push(""); - push(name(ref) + "." + name(service.name) + " = {};"); // currently just an empty object + push(name(ref) + "." + name(service.name) + " = {};"); + // TODO: Services are just empty objects currently } function buildEnum(ref, enm) { push(""); - push(ref + "." + enm.name + " = {"); + pushComment([ + enm.name + " values.", + "@exports " + enm.fullName.substring(1), + "@type {Object.}" + ]); + push(name(ref) + "." + name(enm.name) + " = {"); ++indent; push(""); - Object.keys(enm.values).forEach(function(key) { - push(name(key) + ": " + enm.values[key].toString(10) + ","); - }); + var keys = Object.keys(enm.values); + for (var i = 0; i < keys.length; ++i) { + push(name(keys[i]) + ": " + enm.values[keys[i]].toString(10) + (i < keys.length - 1 ? "," : "")); + } --indent; push("};"); } diff --git a/cli/targets/static.tpl b/cli/targets/static.tpl new file mode 100644 index 000000000..e5ea05044 --- /dev/null +++ b/cli/targets/static.tpl @@ -0,0 +1,37 @@ +;(function(global, factory) { + + /* AMD */ if (typeof define === 'function' && define.amd) + define(["protobuf"], factory); + + /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) + module.exports = factory(require("protobufjs/runtime")); + + /* Global */ else + global.root = factory(global.protobuf); + +})(this, function($runtime) { + "use strict"; + + // Lazily resolved type references + var $lazyTypes = []; + + // Exported root namespace + var $root = {}; + +%OUTPUT% + + // Resolve lazy types + $lazyTypes.forEach(function(types) { + types.forEach(function(path, i) { + if (!path) + return; + path = path.split('.'); + var ptr = $root; + while (path.length) + ptr = ptr[path.shift()]; + types[i] = ptr; + }); + }); + + return $root; +}); diff --git a/runtime.js b/runtime.js index 195ea9bce..87b8292d5 100644 --- a/runtime.js +++ b/runtime.js @@ -21,24 +21,3 @@ runtime.Writer = require("./src/writer"); * @memberof runtime */ runtime.util = require("./src/util/runtime"); - -/** - * Resolves lazy type references. - * @param {Object} root Root object - * @param {string[][]} lazyTypes Lazy type references - * @returns {Object} `root` - */ -runtime.resolve = function resolve(root, lazyTypes) { - lazyTypes.forEach(function(types) { - types.forEach(function(path, i) { - if (!path) - return; - path = path.split('.'); - var ptr = root; - while (path.length) - ptr = ptr[path.shift()]; - types[i] = ptr; - }); - }); - return root; -}; diff --git a/tests/data/package.js b/tests/data/package.js index 8e9989fd8..428047d47 100644 --- a/tests/data/package.js +++ b/tests/data/package.js @@ -1,13 +1,21 @@ -module.exports = (function() { - "use strict"; +;(function(global, factory) { + + /* AMD */ if (typeof define === 'function' && define.amd) + define(["protobuf"], factory); + + /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) + module.exports = factory(require("../../runtime")); + + /* Global */ else + global.root = factory(global.protobuf); - // Minimal static codegen runtime - var $runtime = require("../../runtime"); +})(this, function($runtime) { + "use strict"; // Lazily resolved type references var $lazyTypes = []; - /** @alias exports */ + // Exported root namespace var $root = {}; /** @alias Package */ @@ -416,5 +424,18 @@ module.exports = (function() { return Package; })(); - return $runtime.resolve($root, $lazyTypes); -})(); \ No newline at end of file + // Resolve lazy types + $lazyTypes.forEach(function(types) { + types.forEach(function(path, i) { + if (!path) + return; + path = path.split('.'); + var ptr = $root; + while (path.length) + ptr = ptr[path.shift()]; + types[i] = ptr; + }); + }); + + return $root; +});