Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "bundle" cli option to make js bundling optional #1634

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions cli/pbjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ exports.main = function main(args, callback) {
"force-message": "strict-message"
},
string: [ "target", "out", "path", "wrap", "dependency", "root", "lint" ],
boolean: [ "create", "encode", "decode", "verify", "convert", "delimited", "typeurl", "beautify", "comments", "service", "es6", "sparse", "keep-case", "force-long", "force-number", "force-enum-string", "force-message", "null-defaults" ],
boolean: [ "create", "encode", "decode", "verify", "convert", "delimited", "typeurl", "beautify", "comments", "service", "es6", "sparse", "bundle", "keep-case", "force-long", "force-number", "force-enum-string", "force-message", "null-defaults" ],
default: {
target: "json",
create: true,
Expand All @@ -55,6 +55,7 @@ exports.main = function main(args, callback) {
comments: true,
service: true,
es6: null,
bundle: true,
lint: lintDefault,
"keep-case": false,
"force-long": false,
Expand Down Expand Up @@ -99,7 +100,7 @@ exports.main = function main(args, callback) {
"",
" -o, --out Saves to a file instead of writing to stdout.",
"",
" --sparse Exports only those types referenced from a main file (experimental).",
" --sparse Exports only those types referenced from a main file (experimental). Ignored with --no-bundle.",
"",
chalk.bold.gray(" Module targets only:"),
"",
Expand Down Expand Up @@ -144,7 +145,7 @@ exports.main = function main(args, callback) {
"",
" --null-defaults Default value for optional fields is null instead of zero value.",
"",
"usage: " + chalk.bold.green("pbjs") + " [options] file1.proto file2.json ..." + chalk.gray(" (or pipe) ") + "other | " + chalk.bold.green("pbjs") + " [options] -",
"usage: " + chalk.bold.green("pbjs") + " [options] file1.proto file2.json ..." + chalk.gray(" (or pipe) ") + "other | " + chalk.bold.green("pbjs") + " [options] - | " + chalk.bold.green("pbjs") + " --no-bundle [options] file.proto",
""
].join("\n"));
return 1;
Expand Down Expand Up @@ -235,9 +236,12 @@ exports.main = function main(args, callback) {

// Load from disk
} else {
if (!argv.bundle && files.length > 1) {
throw Error("Only one file may be specified with --no-bundle.");
}
try {
root.loadSync(files, parseOptions).resolveAll(); // sync is deterministic while async is not
if (argv.sparse)
if (argv.sparse && argv.bundle)
sparsify(root);
callTarget();
} catch (err) {
Expand Down
24 changes: 19 additions & 5 deletions cli/pbts.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ exports.main = function(args, callback) {
import: "i"
},
string: [ "name", "out", "global", "import" ],
boolean: [ "comments", "main" ],
boolean: [ "comments", "main", "copy-imports" ],
default: {
comments: true,
main: false
main: false,
"copy-imports": false,
}
});

Expand Down Expand Up @@ -89,10 +90,20 @@ exports.main = function(args, callback) {

// Load from disk
} else {
if (!argv.bundle && files.length > 1) {
throw Error("Only one file may be specified with --copy-imports.");
}
callJsdoc();
}

function callJsdoc() {
var copiedImports = [];

if (argv["copy-imports"]) {
copiedImports = fs.readFileSync(files[0], "utf-8").split("\n").filter(function(line) {
return line.startsWith("import *");
});
}

// There is no proper API for jsdoc, so this executes the CLI and pipes the output
var basedir = path.join(__dirname, ".");
Expand Down Expand Up @@ -158,9 +169,7 @@ exports.main = function(args, callback) {
var importArray = typeof argv.import === "string" ? argv.import.split(",") : argv.import || [];

// Build an object of imports and paths
var imports = {
$protobuf: "protobufjs"
};
var imports = {};
importArray.forEach(function(importItem) {
imports[getImportName(importItem)] = importItem;
});
Expand All @@ -169,6 +178,11 @@ exports.main = function(args, callback) {
Object.keys(imports).forEach(function(key) {
output.push("import * as " + key + " from \"" + imports[key] + "\";");
});

if (copiedImports) {
output = output.concat(copiedImports);
}
output.push("");
}

output = output.join("\n") + "\n" + out.join("");
Expand Down
142 changes: 113 additions & 29 deletions cli/targets/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,30 @@ var config = {};
static_target.description = "Static code without reflection (non-functional on its own)";

function static_target(root, options, callback) {
var importInfo = {
aliasToModule: {},
moduleToAlias: {},
exportNames: {},
};
config = options;
try {
if (!config.bundle && root.imports && root.imports.length) {
// An import of the form `import * as module from "module"` is needed
// for use in the jsdoc comments. But since these imports are only used
// for types, the typescript compiler elides them. So an import of
// of the form `import "module"` is also included.
for (let i of root.imports) {
var moduleName = getModuleName(i);
assignAlias(moduleName, importInfo);
push("import \"" + moduleName + "\";");
}
push("");
for (let i of root.imports) {
var moduleName = getModuleName(i);
push("import * as " + importInfo.moduleToAlias[moduleName] + " from \"" + moduleName + "\";");
}
push("");
}
var aliases = [];
if (config.decode)
aliases.push("Reader");
Expand All @@ -43,7 +65,8 @@ function static_target(root, options, callback) {
}
var rootProp = util.safeProp(config.root || "default");
push((config.es6 ? "const" : "var") + " $root = $protobuf.roots" + rootProp + " || ($protobuf.roots" + rootProp + " = {});");
buildNamespace(null, root);
var filename = util.path.isAbsolute(config._[0]) ? config._[0] : process.cwd() + '/' + config._[0];
buildNamespace(null, root, config.bundle, filename, importInfo);
return callback(null, out.join("\n"));
} catch (err) {
return callback(err);
Expand All @@ -54,6 +77,28 @@ function static_target(root, options, callback) {
}
}

function assignAlias(moduleName, importInfo) {
if (importInfo.moduleToAlias[moduleName])
return importInfo.moduleToAlias[moduleName];

let alias = escapeName(moduleName.replace(/^([A-Z])|[\s.\/_-]+(\w)/g, function(match, p1, p2, offset) {
if (p2) return p2.toUpperCase();
return p1.toLowerCase();
}));
while (importInfo.aliasToModule[alias])
alias += '_';
importInfo.moduleToAlias[moduleName] = alias;
importInfo.aliasToModule[alias] = moduleName;
return alias
}

function getModuleName(path) {
if (path.endsWith(".proto"))
return path.slice(0, -".proto".length);
else
return path;
}

function push(line) {
if (line === "")
return out.push("");
Expand Down Expand Up @@ -106,47 +151,51 @@ function aOrAn(name) {
: "a ") + name;
}

function buildNamespace(ref, ns) {
function buildNamespace(ref, ns, bundle, filename, importInfo) {
if (!ns)
return;

if (ns instanceof Service && !config.service)
return;

// With the no-bundle option, only output namespaces that are defined in the
// file that's being output
if (!bundle && ns.name != '' && (ns.filename === null || (ns.filename && ns.filename !== filename)))
return;

if (ns.name !== "") {
if (!(ns instanceof Type) && !(ns instanceof Service)) {
push("");
pushComment([
ns.comment || "Namespace " + ns.name + ".",
ns.parent instanceof protobuf.Root ? "@exports " + escapeName(ns.name) : "@memberof " + exportName(ns.parent),
"@namespace"
]);
}
push("");
if (!ref && config.es6)
push("export const " + escapeName(ns.name) + " = " + escapeName(ref) + "." + escapeName(ns.name) + " = (() => {");
push("export const " + escapeName(ns.name) + " = " + escapeName(ref) + "." + escapeName(ns.name) + " = ((" + escapeName(ns.name) + ") => {");
else
push(escapeName(ref) + "." + escapeName(ns.name) + " = (function() {");
push(escapeName(ref) + "." + escapeName(ns.name) + " = (function(" + escapeName(ns.name) + ") {");
++indent;
}

if (ns instanceof Type) {
buildType(undefined, ns);
buildType(undefined, ns, bundle, importInfo);
} else if (ns instanceof Service)
buildService(undefined, ns);
else if (ns.name !== "") {
push("");
pushComment([
ns.comment || "Namespace " + ns.name + ".",
ns.parent instanceof protobuf.Root ? "@exports " + escapeName(ns.name) : "@memberof " + exportName(ns.parent),
"@namespace"
]);
push((config.es6 ? "const" : "var") + " " + escapeName(ns.name) + " = {};");
}
buildService(undefined, ns, bundle, importInfo);

ns.nestedArray.forEach(function(nested) {
if (nested instanceof Enum)
buildEnum(ns.name, nested);
else if (nested instanceof Namespace)
buildNamespace(ns.name, nested);
buildNamespace(ns.name, nested, bundle, filename, importInfo);
});
if (ns.name !== "") {
push("");
push("return " + escapeName(ns.name) + ";");
--indent;
push("})();");
push("})(" + escapeName(ref) + util.safeProp(escapeName(ns.name)) + " || {});");
}
}

Expand Down Expand Up @@ -311,7 +360,40 @@ function buildFunction(type, functionName, gen, scope) {
push("};");
}

function toJsType(field) {
function getAliasedType(resolvedType, bundle, importInfo) {
var type = exportName(resolvedType, !(resolvedType instanceof protobuf.Enum || config.forceMessage));
if (!bundle) {
var memberModuleName;
if (resolvedType.filename === null) {
// The set of common types are handled differently than other types
// and don't have an associated filename, so we'll just look up
// the module name
var memberModuleName = {
"google.protobuf.Any": "google/protobuf/any",
"google.protobuf.Empty": "google/protobuf/empty",
"google.protobuf.FieldMask": "google/protobuf/field_mask",
"google.protobuf.Struct": "google/protobuf/struct",
"google.protobuf.Value": "google/protobuf/struct",
"google.protobuf.NullValue": "google/protobuf/struct",
"google.protobuf.ListValue": "google/protobuf/struct",
"google.protobuf.Timestamp": "google/protobuf/timestamp",
"google.protobuf.Wrappers": "google/protobuf/wrappers",
}[resolvedType.__exportName];
}
else
memberModuleName = getModuleName(resolvedType.filename);

for (var moduleName in importInfo.moduleToAlias) {
if (memberModuleName && memberModuleName.endsWith(moduleName)) {
type = importInfo.moduleToAlias[moduleName] + "." + type;
break;
}
}
}
return type
}

function toJsType(field, bundle, importInfo) {
var type;

switch (field.type) {
Expand Down Expand Up @@ -342,7 +424,7 @@ function toJsType(field) {
break;
default:
if (field.resolve().resolvedType)
type = exportName(field.resolvedType, !(field.resolvedType instanceof protobuf.Enum || config.forceMessage));
type = getAliasedType(field.resolvedType, bundle, importInfo)
else
type = "*"; // should not happen
break;
Expand All @@ -354,7 +436,7 @@ function toJsType(field) {
return type;
}

function buildType(ref, type) {
function buildType(ref, type, bundle, importInfo) {

if (config.comments) {
var typeDef = [
Expand All @@ -365,7 +447,7 @@ function buildType(ref, type) {
type.fieldsArray.forEach(function(field) {
var prop = util.safeProp(field.name); // either .name or ["name"]
prop = prop.substring(1, prop.charAt(0) === "[" ? prop.length - 1 : prop.length);
var jsType = toJsType(field);
var jsType = toJsType(field, bundle, importInfo);
if (field.optional)
jsType = jsType + "|null";
typeDef.push("@property {" + jsType + "} " + (field.optional ? "[" + prop + "]" : prop) + " " + (field.comment || type.name + " " + field.name));
Expand Down Expand Up @@ -393,7 +475,7 @@ function buildType(ref, type) {
var prop = util.safeProp(field.name);
if (config.comments) {
push("");
var jsType = toJsType(field);
var jsType = toJsType(field, bundle, importInfo);
if (field.optional && !field.map && !field.repeated && (field.resolvedType instanceof Type || config["null-defaults"]) || field.partOf)
jsType = jsType + "|null|undefined";
pushComment([
Expand Down Expand Up @@ -589,7 +671,6 @@ function buildType(ref, type) {
--indent;
push("};");
}

if (config.typeurl) {
push("");
pushComment([
Expand All @@ -601,13 +682,13 @@ function buildType(ref, type) {
]);
push(escapeName(type.name) + ".getTypeUrl = function getTypeUrl() {");
++indent;
push("return \"type.googleapis.com/" + exportName(type) + "\";");
push("return \"type.googleapis.com/" + exportName(type) + "\";");
--indent;
push("};");
}
}

function buildService(ref, service) {
function buildService(ref, service, bundle, importInfo) {

push("");
pushComment([
Expand Down Expand Up @@ -649,6 +730,9 @@ function buildService(ref, service) {

service.methodsArray.forEach(function(method) {
method.resolve();
var requestType = getAliasedType(method.resolvedRequestType, bundle, importInfo);
var responseType = getAliasedType(method.resolvedResponseType, bundle, importInfo);

var lcName = protobuf.util.lcFirst(method.name),
cbName = escapeName(method.name + "Callback");
push("");
Expand All @@ -659,15 +743,15 @@ function buildService(ref, service) {
"@typedef " + cbName,
"@type {function}",
"@param {Error|null} error Error, if any",
"@param {" + exportName(method.resolvedResponseType) + "} [response] " + method.resolvedResponseType.name
"@param {" + responseType + "} [response] " + method.resolvedResponseType.name
]);
push("");
pushComment([
method.comment || "Calls " + method.name + ".",
"@function " + lcName,
"@memberof " + exportName(service),
"@instance",
"@param {" + exportName(method.resolvedRequestType, !config.forceMessage) + "} request " + method.resolvedRequestType.name + " message or plain object",
"@param {" + requestType + "} request " + method.resolvedRequestType.name + " message or plain object",
"@param {" + exportName(service) + "." + cbName + "} callback Node-style callback called with the error, if any, and " + method.resolvedResponseType.name,
"@returns {undefined}",
"@variation 1"
Expand All @@ -684,8 +768,8 @@ function buildService(ref, service) {
"@function " + lcName,
"@memberof " + exportName(service),
"@instance",
"@param {" + exportName(method.resolvedRequestType, !config.forceMessage) + "} request " + method.resolvedRequestType.name + " message or plain object",
"@returns {Promise<" + exportName(method.resolvedResponseType) + ">} Promise",
"@param {" + requestType + "} request " + method.resolvedRequestType.name + " message or plain object",
"@returns {Promise<" + responseType + ">} Promise",
"@variation 2"
]);
});
Expand Down
Loading