From 9681854526f1813a6ef08becf130ef2fbc28b638 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Mon, 2 Jan 2017 22:02:25 +0100 Subject: [PATCH] CLI: Added customizable linter configuration to pbjs; CLI: Added stdin support to pbjs and pbts --- cli/pbjs.js | 104 +++++++++++++++------- cli/pbts.js | 146 ++++++++++++++++++------------- cli/targets/json-module.js | 2 +- cli/targets/static-module.js | 2 +- cli/targets/static.js | 4 +- cli/util.js | 13 +-- cli/wrappers/amd.js | 1 - cli/wrappers/commonjs.js | 1 - cli/wrappers/default.js | 1 - cli/wrappers/es6.js | 3 +- package.json | 1 + tests/data/ambiguous-names.js | 5 +- tests/data/mapbox/vector_tile.js | 5 +- tests/data/package.js | 5 +- tests/data/rpc.js | 9 +- tests/data/test.d.ts | 2 +- tests/data/test.js | 5 +- 17 files changed, 193 insertions(+), 116 deletions(-) diff --git a/cli/pbjs.js b/cli/pbjs.js index 6e8238e87..c75a96bd4 100644 --- a/cli/pbjs.js +++ b/cli/pbjs.js @@ -17,15 +17,17 @@ var protobuf = require(".."), * @returns {number|undefined} Exit code, if known */ exports.main = function(args, callback) { + var lintDefault = "eslint-disable block-scoped-var, no-redeclare, no-control-regex"; var argv = minimist(args, { alias: { target : "t", out : "o", path : "p", wrap : "w", - root : "r" + root : "r", + lint : "l" }, - string: [ "target", "out", "path", "wrap", "root" ], + string: [ "target", "out", "path", "wrap", "root", "lint" ], boolean: [ "keep-case", "create", "encode", "decode", "verify", "convert", "delimited", "beautify", "comments" ], default: { target : "json", @@ -36,7 +38,8 @@ exports.main = function(args, callback) { convert : true, delimited : true, beautify : true, - comments : true + comments : true, + lint : lintDefault } }); @@ -52,9 +55,9 @@ exports.main = function(args, callback) { callback(Error("usage")); else console.error([ - "protobuf.js v" + pkg.version + " cli", + "protobuf.js v" + pkg.version + " CLI for JavaScript", "", - "Consolidates imports and converts between file formats.", + chalk.bold.white("Consolidates imports and converts between file formats."), "", " -t, --target Specifies the target format. Also accepts a path to require a custom target.", "", @@ -64,21 +67,26 @@ exports.main = function(args, callback) { "", " -o, --out Saves to a file instead of writing to stdout.", "", - " Module targets only:", + chalk.bold.gray(" 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 only wrapper", - " amd AMD only wrapper", + " commonjs CommonJS wrapper", + " amd AMD wrapper", + " es6 ES6 wrapper", "", " -r, --root Specifies an alternative protobuf.roots name.", "", - " Proto sources only:", + " -l, --lint Linter configuration. Defaults to protobuf.js-compatible rules:", + "", + " " + lintDefault, + "", + chalk.bold.gray(" Proto sources only:"), "", " --keep-case Keeps field casing instead of converting to camel case (not recommended).", "", - " Static targets only:", + chalk.bold.gray(" Static targets only:"), "", " --no-create Does not generate create functions used for runtime compatibility.", " --no-encode Does not generate encode functions.", @@ -89,7 +97,7 @@ exports.main = function(args, callback) { " --no-beautify Does not beautify generated code.", " --no-comments Does not output any JSDoc comments.", "", - "usage: " + chalk.bold.green("pbjs") + " [options] file1.proto file2.json ..." + "usage: " + chalk.bold.green("pbjs") + " [options] file1.proto file2.json ..." + chalk.gray(" (or) ") + "other | " + chalk.bold.green("pbjs") + " [options] -" ].join("\n")); return 1; } @@ -127,30 +135,58 @@ exports.main = function(args, callback) { "keepCase": argv["keep-case"] || false }; - try { - root.loadSync(files, parseOptions); // sync is deterministic while async is not - } catch (err) { - if (callback) { - callback(err); - return undefined; - } - throw err; - } + // Read from stdin + if (files.length === 1 && files[0] === "-") { + var data = []; + process.stdin.on("data", function(chunk) { + data.push(chunk); + }); + process.stdin.on("end", function() { + var source = Buffer.concat(data).toString("utf8"); + if (source.charAt(0) !== "{") { + protobuf.parse(source, root, parseOptions); + } else { + var json = JSON.parse(source); + root.setOptions(json.options).addJSON(json); + } + callTarget(); + }); - target(root, argv, function targetCallback(err, output) { - if (err) { - if (callback) - return callback(err); + // Load from disk + } else { + try { + root.loadSync(files, parseOptions); // sync is deterministic while async is not + callTarget(); + } catch (err) { + if (callback) { + callback(err); + return undefined; + } throw err; } - if (output !== "") { - if (argv.out) - fs.writeFileSync(argv.out, output, { encoding: "utf8" }); - else - process.stdout.write(output, "utf8"); - } - return callback - ? callback(null) - : undefined; - }); + } + + function callTarget() { + target(root, argv, function targetCallback(err, output) { + if (err) { + if (callback) + return callback(err); + throw err; + } + if (output !== "") { + output = [ + "// $> pbjs " + args.join(" "), + "// Generated " + (new Date()).toUTCString().replace(/GMT/, "UTC"), + "" + ].join("\n") + "\n" + output; + if (argv.out) + fs.writeFileSync(argv.out, output, { encoding: "utf8" }); + else + process.stdout.write(output, "utf8"); + } + return callback + ? callback(null) + : undefined; + }); + } }; diff --git a/cli/pbts.js b/cli/pbts.js index 349859190..ff5ac8f73 100644 --- a/cli/pbts.js +++ b/cli/pbts.js @@ -6,7 +6,8 @@ var child_process = require("child_process"); var minimist = util.require("minimist", pkg.devDependencies.minimist), chalk = util.require("chalk", pkg.devDependencies.chalk), - glob = util.require("glob", pkg.devDependencies.glob); + glob = util.require("glob", pkg.devDependencies.glob), + tmp = util.require("tmp", pkg.devDependencies.glob); var jsdoc = util.require("jsdoc/package.json", pkg.devDependencies.jsdoc); @@ -41,9 +42,9 @@ exports.main = function(args, callback) { callback(Error("usage")); else console.error([ - "protobuf.js v" + pkg.version + " cli for TypeScript", + "protobuf.js v" + pkg.version + " CLI for TypeScript", "", - "Generates TypeScript definitions from annotated JavaScript files.", + chalk.bold.white("Generates TypeScript definitions from annotated JavaScript files."), "", " -n, --name Wraps everything in a module of the specified name.", "", @@ -55,7 +56,7 @@ exports.main = function(args, callback) { "", " --no-comments Does not output any JSDoc comments.", "", - "usage: " + chalk.bold.green("pbts") + " [options] file1.js file2.js ..." + "usage: " + chalk.bold.green("pbts") + " [options] file1.js file2.js ..." + chalk.bold.gray(" (or) ") + "other | " + chalk.bold.green("pbts") + " [options] -" ].join("\n")); if (callback) callback(Error("usage")); @@ -72,63 +73,90 @@ exports.main = function(args, callback) { ++i; } - // There is no proper API for jsdoc, so this executes the CLI and pipes the output - var basedir = path.join(__dirname, ".."); - var moduleName = argv.name || "null"; - var child = child_process.exec("node \"" + basedir + "/node_modules/jsdoc/jsdoc.js\" -c \"" + basedir + "/jsdoc.types.json\" -q \"module=" + encodeURIComponent(moduleName) + "&comments=" + Boolean(argv.comments) + "\" " + files.map(function(file) { return '"' + file + '"'; }).join(' '), { - cwd: process.cwd(), - argv0: "node", - stdio: "pipe", - maxBuffer: 1 << 24 // 16mb - }); - var out = []; - child.stdout.on("data", function(data) { - out.push(data); - }); - child.stderr.pipe(process.stderr); - child.on("close", function(code) { - if (code) { - out = out.join('').replace(/\s*JSDoc \d+\.\d+\.\d+ [^$]+/, ""); - process.stderr.write(out); - var err = Error("code " + code); - if (callback) - callback(err); - else - throw err; - return; - } + var cleanup = []; - var output = [ - "// $> pbts " + args.join(" "), - "// Generated " + (new Date()).toUTCString().replace(/GMT/, "UTC"), - "" - ]; - if (argv.global) - output.push( - "export as namespace " + argv.global + ";", - "" - ); - if (!argv.main) - output.push( - "import * as $protobuf from \"protobufjs\";", + // Read from stdin (to a temporary file) + if (files.length === 1 && files[0] === "-") { + var data = []; + process.stdin.on("data", function(chunk) { + data.push(chunk); + }); + process.stdin.on("end", function() { + files[0] = tmp.tmpNameSync() + ".js"; + fs.writeFileSync(files[0], Buffer.concat(data)); + cleanup.push(files[0]); + callJsdoc(); + }); + + // Load from disk + } else { + callJsdoc(); + } + + function callJsdoc() { + + // There is no proper API for jsdoc, so this executes the CLI and pipes the output + var basedir = path.join(__dirname, ".."); + var moduleName = argv.name || "null"; + var cmd = "node \"" + basedir + "/node_modules/jsdoc/jsdoc.js\" -c \"" + basedir + "/jsdoc.types.json\" -q \"module=" + encodeURIComponent(moduleName) + "&comments=" + Boolean(argv.comments) + "\" " + files.map(function(file) { return '"' + file + '"'; }).join(' '); + var child = child_process.exec(cmd, { + cwd: process.cwd(), + argv0: "node", + stdio: "pipe", + maxBuffer: 1 << 24 // 16mb + }); + var out = []; + child.stdout.on("data", function(data) { + out.push(data); + }); + child.stderr.pipe(process.stderr); + child.on("close", function(code) { + // clean up temporary files, no matter what + try { cleanup.forEach(fs.unlinkSync); } catch(e) {} cleanup = []; + + if (code) { + out = out.join('').replace(/\s*JSDoc \d+\.\d+\.\d+ [^$]+/, ""); + process.stderr.write(out); + var err = Error("code " + code); + if (callback) + callback(err); + else + throw err; + return; + } + + var output = [ + "// $> pbts " + args.join(" "), + "// Generated " + (new Date()).toUTCString().replace(/GMT/, "UTC"), "" - ); - output = output.join('\n') + "\n" + out.join(''); - - try { - if (argv.out) - fs.writeFileSync(argv.out, output); - else - process.stdout.write(output, "utf8"); - if (callback) - callback(null); - } catch (err) { - if (callback) - callback(err); - else - throw err; - } - }); + ]; + if (argv.global) + output.push( + "export as namespace " + argv.global + ";", + "" + ); + if (!argv.main) + output.push( + "import * as $protobuf from \"protobufjs\";", + "" + ); + output = output.join('\n') + "\n" + out.join(''); + + try { + if (argv.out) + fs.writeFileSync(argv.out, output); + else + process.stdout.write(output, "utf8"); + if (callback) + callback(null); + } catch (err) { + if (callback) + callback(err); + else + throw err; + } + }); + } return undefined; }; diff --git a/cli/targets/json-module.js b/cli/targets/json-module.js index 505269bfd..a5bd3eb7a 100644 --- a/cli/targets/json-module.js +++ b/cli/targets/json-module.js @@ -8,7 +8,7 @@ json_module.description = "JSON representation as a module" function json_module(root, options, callback) { try { var output = "var $root = protobuf.Root.fromJSON(" + JSON.stringify(root, null, 2).replace(/^(?!$)/mg, " ").trim() + ").resolveAll();"; - output = util.wrap(options.wrap || "default", output, options.root); + output = util.wrap(output, options); process.nextTick(function() { callback(null, output); }); diff --git a/cli/targets/static-module.js b/cli/targets/static-module.js index 798f730ed..28dd392e7 100644 --- a/cli/targets/static-module.js +++ b/cli/targets/static-module.js @@ -19,7 +19,7 @@ function static_module_target(root, options, callback) { if (err) return callback(err); try { - output = util.wrap(options.wrap || "default", output, options.root); + output = util.wrap(output, options); } catch (e) { callback(e); return; diff --git a/cli/targets/static.js b/cli/targets/static.js index 98225c0da..2cbdbdf29 100644 --- a/cli/targets/static.js +++ b/cli/targets/static.js @@ -537,7 +537,7 @@ function buildService(ref, service) { ]); push("this.responseDelimited = Boolean(responseDelimited);"); --indent; - push("};"); + push("}"); service.methodsArray.forEach(function(method) { method.resolve(); @@ -567,7 +567,7 @@ function buildService(ref, service) { --indent; push("} catch (err) {"); ++indent; - push("(typeof setImmediate === 'function' ? setImmediate : setTimeout)(function() { callback(err); });"); + push("(typeof setImmediate === \"function\" ? setImmediate : setTimeout)(function() { callback(err); });"); push("return;"); --indent; push("}"); diff --git a/cli/util.js b/cli/util.js index 7b24b32dd..aeee7fe80 100644 --- a/cli/util.js +++ b/cli/util.js @@ -90,9 +90,8 @@ exports.require = function(name, version) { return require(name + sub); }; -exports.wrap = function(name, OUTPUT, ROOT) { - if (!ROOT) - ROOT = "default"; +exports.wrap = function(OUTPUT, options) { + var name = options.wrap || "default"; var wrap; try { // try built-in wrappers first @@ -101,11 +100,13 @@ exports.wrap = function(name, OUTPUT, ROOT) { // otherwise fetch the custom one wrap = fs.readFileSync(path.resolve(process.cwd(), name)).toString("utf8"); } - wrap = wrap.replace(/%ROOT%/g, JSON.stringify(ROOT)); + wrap = wrap.replace(/%ROOT%/g, JSON.stringify(options.root || "default")); wrap = wrap.replace(/( *)%OUTPUT%/, function($0, $1) { return $1.length ? OUTPUT.replace(/^/mg, $1) : OUTPUT; }); - return wrap; + if (options.lint !== "") + wrap = "/*" + options.lint + "*/\n" + wrap; + return wrap.replace(/\r?\n/, "\n"); }; exports.pad = function(str, len, l) { @@ -116,4 +117,4 @@ exports.pad = function(str, len, l) { exports.reserved = function(name) { return /^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/.test(name); -}; \ No newline at end of file +}; diff --git a/cli/wrappers/amd.js b/cli/wrappers/amd.js index 1df4f3f18..d6af6bbf7 100644 --- a/cli/wrappers/amd.js +++ b/cli/wrappers/amd.js @@ -1,4 +1,3 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ define(["protobuf"], function($protobuf) { "use strict"; diff --git a/cli/wrappers/commonjs.js b/cli/wrappers/commonjs.js index b52ac4535..e4c942b25 100644 --- a/cli/wrappers/commonjs.js +++ b/cli/wrappers/commonjs.js @@ -1,4 +1,3 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ "use strict"; var $protobuf = require("protobufjs/runtime"); diff --git a/cli/wrappers/default.js b/cli/wrappers/default.js index e463897b9..288c6e801 100644 --- a/cli/wrappers/default.js +++ b/cli/wrappers/default.js @@ -1,4 +1,3 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ (function(global, factory) { /* global define, require, module */ /* AMD */ if (typeof define === 'function' && define.amd) diff --git a/cli/wrappers/es6.js b/cli/wrappers/es6.js index d57d6725f..326ade151 100644 --- a/cli/wrappers/es6.js +++ b/cli/wrappers/es6.js @@ -1,8 +1,7 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex */ import * as $protobuf from "protobufjs"; %OUTPUT% $protobuf.roots[%ROOT%] = $root; -export default $root; +export { $root as default }; diff --git a/package.json b/package.json index 1728bf3e4..1ba75489d 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "node-zopfli": "^2.0.2", "tap-spec": "^4.1.1", "tape": "^4.6.3", + "tmp": "0.0.31", "typescript": "^2.1.4", "uglify-js": "^2.7.5", "vinyl-buffer": "^1.0.0", diff --git a/tests/data/ambiguous-names.js b/tests/data/ambiguous-names.js index 0f97be001..cf96fb37e 100644 --- a/tests/data/ambiguous-names.js +++ b/tests/data/ambiguous-names.js @@ -1,4 +1,7 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ +// $> pbjs --target static-module --wrap commonjs --root test_ambiguous-names --out tests/data/ambiguous-names.js tests/data/ambiguous-names.proto +// Generated Mon, 02 Jan 2017 21:00:30 UTC + +/*eslint-disable block-scoped-var, no-redeclare, no-control-regex*/ "use strict"; var $protobuf = require("../../runtime"); diff --git a/tests/data/mapbox/vector_tile.js b/tests/data/mapbox/vector_tile.js index becaba7e7..cbd8dae52 100644 --- a/tests/data/mapbox/vector_tile.js +++ b/tests/data/mapbox/vector_tile.js @@ -1,4 +1,7 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ +// $> pbjs --target static-module --wrap commonjs --root test_vector_tile --out tests/data/mapbox/vector_tile.js tests/data/mapbox/vector_tile.proto +// Generated Mon, 02 Jan 2017 21:00:30 UTC + +/*eslint-disable block-scoped-var, no-redeclare, no-control-regex*/ "use strict"; var $protobuf = require("../../../runtime"); diff --git a/tests/data/package.js b/tests/data/package.js index 6348e0d22..c294d5c56 100644 --- a/tests/data/package.js +++ b/tests/data/package.js @@ -1,4 +1,7 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ +// $> pbjs --target static-module --wrap commonjs --root test_package --out tests/data/package.js tests/data/package.proto +// Generated Mon, 02 Jan 2017 21:00:30 UTC + +/*eslint-disable block-scoped-var, no-redeclare, no-control-regex*/ "use strict"; var $protobuf = require("../../runtime"); diff --git a/tests/data/rpc.js b/tests/data/rpc.js index 3de41c5e8..d33de3d49 100644 --- a/tests/data/rpc.js +++ b/tests/data/rpc.js @@ -1,4 +1,7 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ +// $> pbjs --target static-module --wrap commonjs --root test_rpc --out tests/data/rpc.js tests/data/rpc.proto +// Generated Mon, 02 Jan 2017 21:00:30 UTC + +/*eslint-disable block-scoped-var, no-redeclare, no-control-regex*/ "use strict"; var $protobuf = require("../../runtime"); @@ -57,7 +60,7 @@ $root.MyService = (function() { * @type {boolean} */ this.responseDelimited = Boolean(responseDelimited); - }; + } /** * Callback as used by {@link MyService#myMethod}. @@ -78,7 +81,7 @@ $root.MyService = (function() { try { requestData = (this.requestDelimited ? $root.MyRequest.encodeDelimited(request) : $root.MyRequest.encode(request)).finish(); } catch (err) { - (typeof setImmediate === 'function' ? setImmediate : setTimeout)(function() { callback(err); }); + (typeof setImmediate === "function" ? setImmediate : setTimeout)(function() { callback(err); }); return; } var self = this; diff --git a/tests/data/test.d.ts b/tests/data/test.d.ts index 741936d34..0a6d819e3 100644 --- a/tests/data/test.d.ts +++ b/tests/data/test.d.ts @@ -1,5 +1,5 @@ // $> pbts --out tests/data/test.d.ts --no-comments tests/data/test.js -// Generated Mon, 02 Jan 2017 15:20:55 UTC +// Generated Mon, 02 Jan 2017 21:00:32 UTC import * as $protobuf from "../.."; diff --git a/tests/data/test.js b/tests/data/test.js index 42c812c02..7a6addf65 100644 --- a/tests/data/test.js +++ b/tests/data/test.js @@ -1,4 +1,7 @@ -/* eslint-disable block-scoped-var, no-redeclare, no-control-regex, strict */ +// $> pbjs --target static-module --wrap commonjs --root test_test --out tests/data/test.js tests/data/test.proto +// Generated Mon, 02 Jan 2017 21:00:30 UTC + +/*eslint-disable block-scoped-var, no-redeclare, no-control-regex*/ "use strict"; var $protobuf = require("../../runtime");