From b7302bd674fe4620248298486292175b888450b3 Mon Sep 17 00:00:00 2001 From: pluschen Date: Fri, 23 Dec 2022 16:45:31 +0800 Subject: [PATCH 1/6] feat: add message filter for cli --- cli/pbjs.js | 18 ++++++++++++++- cli/util.js | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/cli/pbjs.js b/cli/pbjs.js index 23750a4a8..c6f6180da 100644 --- a/cli/pbjs.js +++ b/cli/pbjs.js @@ -40,7 +40,7 @@ exports.main = function main(args, callback) { "force-long": "strict-long", "force-message": "strict-message" }, - string: [ "target", "out", "path", "wrap", "dependency", "root", "lint" ], + string: [ "target", "out", "path", "wrap", "dependency", "root", "lint", 'filter' ], boolean: [ "create", "encode", "decode", "verify", "convert", "delimited", "typeurl", "beautify", "comments", "service", "es6", "sparse", "keep-case", "alt-comment", "force-long", "force-number", "force-enum-string", "force-message", "null-defaults" ], default: { target: "json", @@ -98,6 +98,9 @@ exports.main = function main(args, callback) { "", " -p, --path Adds a directory to the include path.", "", + " --filter Set up a filter to configure only those messages you need and their dependencies to compile, this will effectively reduce the final file size", + " A json file, Example of file content: { rootName: mypackagename, messageNames:[messageName1, messageName2]} ", + "", " -o, --out Saves to a file instead of writing to stdout.", "", " --sparse Exports only those types referenced from a main file (experimental).", @@ -308,7 +311,20 @@ exports.main = function main(args, callback) { root.resolveAll(); } + function filterMessage() { + if (argv.filter && fs.existsSync(argv.filter)) { + // This is a piece of degradable logic + try { + const needMessage = JSON.parse(fs.readFileSync(argv.filter)); + util.filterMessage(root, needMessage); + } catch (error) { + process.stderr.write('The filter file parsing failed, please check whether the file format is correct.') + } + } + } + function callTarget() { + filterMessage(); target(root, argv, function targetCallback(err, output) { if (err) { if (callback) diff --git a/cli/util.js b/cli/util.js index 931362280..b3de17d15 100644 --- a/cli/util.js +++ b/cli/util.js @@ -125,3 +125,69 @@ exports.pad = function(str, len, l) { return str; }; +/** + * DFS to get all message you need and their dependencies, cache in filterMap. + * @param {*} root The protobuf root instance + * @param {*} needMessage { + * rootName: the entry proto pakcage name + * messageNames: the message in the root namespace you need to gen. + * } + * @param {*} filterMap The result of message you need and their dependencies. + * @param {*} flatMap A flag to record whether the message was searched. + * @returns + */ +function doFilterMessage(root, needMessage, filterMap, flatMap) { + let rootName = needMessage.rootName; + let messageNames = needMessage.messageNames; + + const rootNs = root.nested[rootName] + if (!rootNs) { + return; + } + + + let set = filterMap.get(rootName); + if (!filterMap.has(rootName)) { + set = new Set(); + filterMap.set(rootName, set); + } + + for (let messageName of messageNames) { + const message = rootNs.nested[messageName]; + if (!message) throw new Error(`message not foud ${rootName}.${message}`); + set.add(messageName); + if (message instanceof protobuf.Type) { + if (flatMap.get(`${rootName}.${message.name}`)) continue; + flatMap.set(`${rootName}.${message.name}`, true); + for (let field of message.fieldsArray) { + if (field.resolvedType) { + const rootName = field.resolvedType.parent.name; + const typeName = field.resolvedType.name; + doFilterMessage(root, { rootName, messageNames: [typeName] }, filterMap, flatMap); + } + } + } + } +} + +/** + * filter the message you need and their dependencies, all others will be delete from root. + * @param {*} root the protobuf root instance + * @param {*} needMessage { + * rootName: the entry proto pakcage name + * messageNames: the message in the root namespace you need to gen. + * } + */ +exports.filterMessage = function (root, needMessage) { + const filterMap = new Map(); + const flatMap = new Map(); + doFilterMessage(root, needMessage, filterMap, flatMap); + root._nestedArray = root._nestedArray.filter(ns => filterMap.has(ns.name)); + for (let ns of root.nestedArray) { + ns._nestedArray = ns._nestedArray.filter(nns => { + const nnsSet = filterMap.get(ns.name); + return nnsSet.has(nns.name); + }); + } +}; + From 7a16a5349be9b17a0427af33df4f6e51c4a81fad Mon Sep 17 00:00:00 2001 From: pluschen Date: Fri, 23 Dec 2022 21:07:32 +0800 Subject: [PATCH 2/6] feat: add test --- cli/util.js | 137 ++++++++++++++++-------- tests/cli.js | 52 +++++++++ tests/data/cli/filter.json | 3 + tests/data/cli/test-filter-import.proto | 8 ++ tests/data/cli/test-filter.proto | 21 ++++ 5 files changed, 179 insertions(+), 42 deletions(-) create mode 100644 tests/data/cli/filter.json create mode 100644 tests/data/cli/test-filter-import.proto create mode 100644 tests/data/cli/test-filter.proto diff --git a/cli/util.js b/cli/util.js index b3de17d15..5100cd70e 100644 --- a/cli/util.js +++ b/cli/util.js @@ -125,47 +125,92 @@ exports.pad = function(str, len, l) { return str; }; + +/** + * DFS to get all message dependencies, cache in filterMap. + * @param {*} root The protobuf root instance + * @param {*} message the message need to process. + * @param {*} needMessageConfig { + * messageNames: the message names array in the root namespace you need to gen. example: [msg1, msg2] + * } + * @param {*} filterMap The result of message you need and their dependencies. + * @param {*} flatMap A flag to record whether the message was searched. + * @returns + */ +function dfsFilterMessageDependencies(root, message, filterMap, flatMap) { + if (message instanceof protobuf.Type) { + if (flatMap.get(`${message.fullName}`)) return; + flatMap.set(`${message.fullName}`, true); + for (var field of message.fieldsArray) { + if (field.resolvedType) { + // a nested message + if (field.resolvedType.parent.name === message.name) { + var nestedMessage = message.nested[field.resolvedType.name]; + dfsFilterMessageDependencies(root, nestedMessage, filterMap, flatMap); + continue; + } + var packageName = field.resolvedType.parent.name; + var typeName = field.resolvedType.name; + var fullName = packageName ? `${packageName}.${typeName}` : typeName; + doFilterMessage(root, { messageNames: [fullName] }, filterMap, flatMap, packageName); + } + } + } +} + /** * DFS to get all message you need and their dependencies, cache in filterMap. * @param {*} root The protobuf root instance - * @param {*} needMessage { - * rootName: the entry proto pakcage name - * messageNames: the message in the root namespace you need to gen. + * @param {*} needMessageConfig { + * messageNames: the message names array in the root namespace you need to gen. example: [msg1, msg2] * } * @param {*} filterMap The result of message you need and their dependencies. * @param {*} flatMap A flag to record whether the message was searched. * @returns */ -function doFilterMessage(root, needMessage, filterMap, flatMap) { - let rootName = needMessage.rootName; - let messageNames = needMessage.messageNames; +function doFilterMessage(root, needMessageConfig, filterMap, flatMap, currentPackageName) { + var needMessageNames = needMessageConfig.messageNames; - const rootNs = root.nested[rootName] - if (!rootNs) { - return; - } + for (var messageFullName of needMessageNames) { + var nameSplit = messageFullName.split('.'); + var packageName = ''; + var messageName = ''; + if (nameSplit.length > 1) { + packageName = nameSplit[0]; + messageName = nameSplit[1]; + } else { + messageName = nameSplit[0]; + } + // in Namespace + if (packageName) { + var ns = root.nested[packageName]; + if (!ns || !(ns instanceof protobuf.Namespace)) { + throw new Error(`package not foud ${currentPackageName}.${messageName}`); + } - let set = filterMap.get(rootName); - if (!filterMap.has(rootName)) { - set = new Set(); - filterMap.set(rootName, set); - } + doFilterMessage(root, { messageNames: [messageName] }, filterMap, flatMap, packageName); + } else { + var message = root.nested[messageName]; - for (let messageName of messageNames) { - const message = rootNs.nested[messageName]; - if (!message) throw new Error(`message not foud ${rootName}.${message}`); - set.add(messageName); - if (message instanceof protobuf.Type) { - if (flatMap.get(`${rootName}.${message.name}`)) continue; - flatMap.set(`${rootName}.${message.name}`, true); - for (let field of message.fieldsArray) { - if (field.resolvedType) { - const rootName = field.resolvedType.parent.name; - const typeName = field.resolvedType.name; - doFilterMessage(root, { rootName, messageNames: [typeName] }, filterMap, flatMap); - } + if (currentPackageName) { + message = root.nested[currentPackageName].nested[messageName]; } + + if (!message) { + throw new Error(`message not foud ${nsName}.${messageName}`); + } + + var set = filterMap.get(currentPackageName); + if (!filterMap.has(currentPackageName)) { + set = new Set(); + filterMap.set(currentPackageName, set); + } + + set.add(messageName); + + // dfs to find all dependencies + dfsFilterMessageDependencies(root, message, filterMap, flatMap, currentPackageName); } } } @@ -173,21 +218,29 @@ function doFilterMessage(root, needMessage, filterMap, flatMap) { /** * filter the message you need and their dependencies, all others will be delete from root. * @param {*} root the protobuf root instance - * @param {*} needMessage { - * rootName: the entry proto pakcage name - * messageNames: the message in the root namespace you need to gen. + * @param {*} needMessageConfig { + * messageNames: the message names array in the root namespace you need to gen. example: [msg1, msg2] * } */ -exports.filterMessage = function (root, needMessage) { - const filterMap = new Map(); - const flatMap = new Map(); - doFilterMessage(root, needMessage, filterMap, flatMap); - root._nestedArray = root._nestedArray.filter(ns => filterMap.has(ns.name)); - for (let ns of root.nestedArray) { - ns._nestedArray = ns._nestedArray.filter(nns => { - const nnsSet = filterMap.get(ns.name); - return nnsSet.has(nns.name); - }); - } +exports.filterMessage = function (root, needMessageConfig) { + var filterMap = new Map(); + var flatMap = new Map(); + doFilterMessage(root, needMessageConfig, filterMap, flatMap, ''); + root._nestedArray = root._nestedArray.filter(ns => { + if (ns instanceof protobuf.Type || ns instanceof protobuf.Enum) { + return filterMap.get("").has(ns.name); + } else if (ns instanceof protobuf.Namespace) { + if (!filterMap.has(ns.name)) { + return false; + } else { + ns._nestedArray = ns._nestedArray.filter(nns => { + const nnsSet = filterMap.get(ns.name); + return nnsSet.has(nns.name); + }); + + return true; + } + } + }); }; diff --git a/tests/cli.js b/tests/cli.js index 57440bc21..11f32a0f1 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -4,6 +4,7 @@ var tape = require("tape"); var path = require("path"); var Module = require("module"); var protobuf = require(".."); +var fs = require("fs"); function cliTest(test, testFunc) { // pbjs does not seem to work with Node v4, so skip this test if we're running on it @@ -162,3 +163,54 @@ tape.test("with null-defaults, absent optional fields have null values", functio }); }); }); + + +tape.test("pbjs generates static code with message filter", function (test) { + cliTest(test, function () { + var root = protobuf.loadSync("tests/data/cli/test-filter.proto"); + root.resolveAll(); + + var staticTarget = require("../cli/targets/static"); + var util = require("../cli/util"); + + const needMessageConfig = JSON.parse(fs.readFileSync("tests/data/cli/filter.json")); + + util.filterMessage(root, needMessageConfig); + + staticTarget(root, { + create: true, + decode: true, + encode: true, + convert: true, + "null-defaults": true, + }, function (err, jsCode) { + test.error(err, 'static code generation worked'); + + // jsCode is the generated code; we'll eval it + // (since this is what we normally does with the code, right?) + // This is a test code. Do not use this in production. + var $protobuf = protobuf; + eval(jsCode); + + console.log(protobuf.roots); + + var NeedMessage1 = protobuf.roots.default.filtertest.NeedMessage1; + var NeedMessage2 = protobuf.roots.default.filtertest.NeedMessage2; + var DependentMessage1 = protobuf.roots.default.filtertest.DependentMessage1; + var DependentMessageFromImport = protobuf.roots.default.DependentMessageFromImport; + + var NotNeedMessageInRootFile = protobuf.roots.default.filtertest.NotNeedMessageInRootFile; + var NotNeedMessageInImportFile = protobuf.roots.default.NotNeedMessageInImportFile; + + test.ok(NeedMessage1, "NeedMessage1 is loaded"); + test.ok(NeedMessage2, "NeedMessage2 is loaded"); + test.ok(DependentMessage1, "DependentMessage1 is loaded"); + test.ok(DependentMessageFromImport, "DependentMessageFromImport is loaded"); + + test.notOk(NotNeedMessageInImportFile, "NotNeedMessageInImportFile is not loaded"); + test.notOk(NotNeedMessageInRootFile, "NotNeedMessageInRootFile is not loaded"); + + test.end(); + }); + }); +}); diff --git a/tests/data/cli/filter.json b/tests/data/cli/filter.json new file mode 100644 index 000000000..d6d1d00e3 --- /dev/null +++ b/tests/data/cli/filter.json @@ -0,0 +1,3 @@ +{ + "messageNames": ["filtertest.NeedMessage1", "filtertest.NeedMessage2"] +} \ No newline at end of file diff --git a/tests/data/cli/test-filter-import.proto b/tests/data/cli/test-filter-import.proto new file mode 100644 index 000000000..30c6406d2 --- /dev/null +++ b/tests/data/cli/test-filter-import.proto @@ -0,0 +1,8 @@ + +message DependentMessageFromImport { + optional int32 test1 = 1; +} + +message NotNeedMessageInImportFile { + optional int32 test1 = 1; +} \ No newline at end of file diff --git a/tests/data/cli/test-filter.proto b/tests/data/cli/test-filter.proto new file mode 100644 index 000000000..aa50ca17c --- /dev/null +++ b/tests/data/cli/test-filter.proto @@ -0,0 +1,21 @@ +package filtertest; +import "./test-filter-import.proto"; + +message NeedMessage1 { + optional uint32 test1 = 1; + optional NeedMessage2 needMessage2 = 2; + optional DependentMessage1 dependentMessage1 = 3; + optional DependentMessageFromImport dependentMessage2 = 4; +} + +message NeedMessage2 { + optional uint32 test1 = 1; +} + +message DependentMessage1 { + optional uint32 test1 = 1; +} + +message NotNeedMessageInRootFile { + optional uint32 test1 = 1; +} \ No newline at end of file From d7b458051ed3dcf1925c9dae83e8ec6daa6525a2 Mon Sep 17 00:00:00 2001 From: pluschen Date: Fri, 23 Dec 2022 21:10:44 +0800 Subject: [PATCH 3/6] fix: update comment --- cli/pbjs.js | 2 +- cli/test.json | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 cli/test.json diff --git a/cli/pbjs.js b/cli/pbjs.js index c6f6180da..3ef211a7f 100644 --- a/cli/pbjs.js +++ b/cli/pbjs.js @@ -99,7 +99,7 @@ exports.main = function main(args, callback) { " -p, --path Adds a directory to the include path.", "", " --filter Set up a filter to configure only those messages you need and their dependencies to compile, this will effectively reduce the final file size", - " A json file, Example of file content: { rootName: mypackagename, messageNames:[messageName1, messageName2]} ", + " Set A json file path, Example of file content: {\"messageNames\":[\"mypackage.messageName1\", \"messageName2\"] } ", "", " -o, --out Saves to a file instead of writing to stdout.", "", diff --git a/cli/test.json b/cli/test.json new file mode 100644 index 000000000..7d949e57d --- /dev/null +++ b/cli/test.json @@ -0,0 +1,5 @@ +{ + "messageNames": [ + "CashierResponse" + ] +} \ No newline at end of file From f09c6623deb5b91369fc703debad1b5c4eb6a6f1 Mon Sep 17 00:00:00 2001 From: pluschen Date: Fri, 23 Dec 2022 21:28:37 +0800 Subject: [PATCH 4/6] fix: update error message --- cli/pbjs.js | 4 ++-- cli/util.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/pbjs.js b/cli/pbjs.js index 3ef211a7f..d15eed3a1 100644 --- a/cli/pbjs.js +++ b/cli/pbjs.js @@ -312,13 +312,13 @@ exports.main = function main(args, callback) { } function filterMessage() { - if (argv.filter && fs.existsSync(argv.filter)) { + if (argv.filter) { // This is a piece of degradable logic try { const needMessage = JSON.parse(fs.readFileSync(argv.filter)); util.filterMessage(root, needMessage); } catch (error) { - process.stderr.write('The filter file parsing failed, please check whether the file format is correct.') + process.stderr.write(`The filter not work, please check whether the file is correct: ${error.message}\n`); } } } diff --git a/cli/util.js b/cli/util.js index 5100cd70e..d5bc30193 100644 --- a/cli/util.js +++ b/cli/util.js @@ -198,7 +198,7 @@ function doFilterMessage(root, needMessageConfig, filterMap, flatMap, currentPac } if (!message) { - throw new Error(`message not foud ${nsName}.${messageName}`); + throw new Error(`message not foud ${currentPackageName}.${messageName}`); } var set = filterMap.get(currentPackageName); From d65862dfa9683b58da0fb225e1742fb2a3940afd Mon Sep 17 00:00:00 2001 From: pluschen Date: Fri, 23 Dec 2022 21:30:40 +0800 Subject: [PATCH 5/6] fix: remove test file --- cli/test.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 cli/test.json diff --git a/cli/test.json b/cli/test.json deleted file mode 100644 index 7d949e57d..000000000 --- a/cli/test.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "messageNames": [ - "CashierResponse" - ] -} \ No newline at end of file From 78890199703b87d66e4e363957fbade14951f726 Mon Sep 17 00:00:00 2001 From: Alexander Fenster Date: Tue, 24 Jan 2023 19:30:49 +0000 Subject: [PATCH 6/6] fix: lint, jsdoc comments, return values --- cli/package-lock.json | 3 ++- cli/pbjs.js | 2 +- cli/util.js | 63 +++++++++++++++++++++---------------------- package.json | 8 ++++++ 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/cli/package-lock.json b/cli/package-lock.json index a33753236..718269087 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -35,7 +35,8 @@ } }, "..": { - "version": "7.1.0", + "name": "protobufjs", + "version": "7.1.2", "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", diff --git a/cli/pbjs.js b/cli/pbjs.js index d15eed3a1..d4ec741dc 100644 --- a/cli/pbjs.js +++ b/cli/pbjs.js @@ -40,7 +40,7 @@ exports.main = function main(args, callback) { "force-long": "strict-long", "force-message": "strict-message" }, - string: [ "target", "out", "path", "wrap", "dependency", "root", "lint", 'filter' ], + string: [ "target", "out", "path", "wrap", "dependency", "root", "lint", "filter" ], boolean: [ "create", "encode", "decode", "verify", "convert", "delimited", "typeurl", "beautify", "comments", "service", "es6", "sparse", "keep-case", "alt-comment", "force-long", "force-number", "force-enum-string", "force-message", "null-defaults" ], default: { target: "json", diff --git a/cli/util.js b/cli/util.js index d5bc30193..f87f50ead 100644 --- a/cli/util.js +++ b/cli/util.js @@ -128,14 +128,11 @@ exports.pad = function(str, len, l) { /** * DFS to get all message dependencies, cache in filterMap. - * @param {*} root The protobuf root instance - * @param {*} message the message need to process. - * @param {*} needMessageConfig { - * messageNames: the message names array in the root namespace you need to gen. example: [msg1, msg2] - * } - * @param {*} filterMap The result of message you need and their dependencies. - * @param {*} flatMap A flag to record whether the message was searched. - * @returns + * @param {Root} root The protobuf root instance + * @param {Message} message The message need to process. + * @param {Map} filterMap The result of message you need and their dependencies. + * @param {Map} flatMap A flag to record whether the message was searched. + * @returns {undefined} Does not return a value */ function dfsFilterMessageDependencies(root, message, filterMap, flatMap) { if (message instanceof protobuf.Type) { @@ -143,7 +140,7 @@ function dfsFilterMessageDependencies(root, message, filterMap, flatMap) { flatMap.set(`${message.fullName}`, true); for (var field of message.fieldsArray) { if (field.resolvedType) { - // a nested message + // a nested message if (field.resolvedType.parent.name === message.name) { var nestedMessage = message.nested[field.resolvedType.name]; dfsFilterMessageDependencies(root, nestedMessage, filterMap, flatMap); @@ -160,21 +157,21 @@ function dfsFilterMessageDependencies(root, message, filterMap, flatMap) { /** * DFS to get all message you need and their dependencies, cache in filterMap. - * @param {*} root The protobuf root instance - * @param {*} needMessageConfig { - * messageNames: the message names array in the root namespace you need to gen. example: [msg1, msg2] - * } - * @param {*} filterMap The result of message you need and their dependencies. - * @param {*} flatMap A flag to record whether the message was searched. - * @returns + * @param {Root} root The protobuf root instance + * @param {object} needMessageConfig Need message config: + * @param {string[]} needMessageConfig.messageNames The message names array in the root namespace you need to gen. example: [msg1, msg2] + * @param {Map} filterMap The result of message you need and their dependencies. + * @param {Map} flatMap A flag to record whether the message was searched. + * @param {string} currentPackageName Current package name + * @returns {undefined} Does not return a value */ function doFilterMessage(root, needMessageConfig, filterMap, flatMap, currentPackageName) { var needMessageNames = needMessageConfig.messageNames; for (var messageFullName of needMessageNames) { - var nameSplit = messageFullName.split('.'); - var packageName = ''; - var messageName = ''; + var nameSplit = messageFullName.split("."); + var packageName = ""; + var messageName = ""; if (nameSplit.length > 1) { packageName = nameSplit[0]; messageName = nameSplit[1]; @@ -190,13 +187,13 @@ function doFilterMessage(root, needMessageConfig, filterMap, flatMap, currentPac } doFilterMessage(root, { messageNames: [messageName] }, filterMap, flatMap, packageName); - } else { + } else { var message = root.nested[messageName]; if (currentPackageName) { message = root.nested[currentPackageName].nested[messageName]; } - + if (!message) { throw new Error(`message not foud ${currentPackageName}.${messageName}`); } @@ -217,30 +214,30 @@ function doFilterMessage(root, needMessageConfig, filterMap, flatMap, currentPac /** * filter the message you need and their dependencies, all others will be delete from root. - * @param {*} root the protobuf root instance - * @param {*} needMessageConfig { - * messageNames: the message names array in the root namespace you need to gen. example: [msg1, msg2] - * } + * @param {Root} root Root the protobuf root instance + * @param {object} needMessageConfig Need message config: + * @param {string[]} needMessageConfig.messageNames Tthe message names array in the root namespace you need to gen. example: [msg1, msg2] + * @returns {boolean} True if a message should present in the generated files */ exports.filterMessage = function (root, needMessageConfig) { var filterMap = new Map(); var flatMap = new Map(); - doFilterMessage(root, needMessageConfig, filterMap, flatMap, ''); + doFilterMessage(root, needMessageConfig, filterMap, flatMap, ""); root._nestedArray = root._nestedArray.filter(ns => { if (ns instanceof protobuf.Type || ns instanceof protobuf.Enum) { return filterMap.get("").has(ns.name); } else if (ns instanceof protobuf.Namespace) { if (!filterMap.has(ns.name)) { return false; - } else { - ns._nestedArray = ns._nestedArray.filter(nns => { - const nnsSet = filterMap.get(ns.name); - return nnsSet.has(nns.name); - }); - - return true; } + ns._nestedArray = ns._nestedArray.filter(nns => { + const nnsSet = filterMap.get(ns.name); + return nnsSet.has(nns.name); + }); + + return true; } + return true; }); }; diff --git a/package.json b/package.json index 864357000..fa1729e9e 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,14 @@ "engines": { "node": ">=12.0.0" }, + "eslintConfig": { + "env": { + "es6": true + }, + "parserOptions": { + "ecmaVersion": 6 + } + }, "keywords": [ "protobuf", "protocol-buffers",