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

Conversation

taylorcode
Copy link
Collaborator

Copied from #1390 (comment):

pbjs generates a single bundle of JS for all of the transitive .protos. Therefore if you run pbjs on proto A and then proto B, and both depend on C, then you'll end up with two copies of C's generated JS code -- a.js will contain it, as well as b.js. This is a problem if you need to load proto A and B on the same page for two reasons:

  • code bloat - you're loading C's code twice. If you need to load many protos and they share dependencies, you'll end up with an explosion of duplicate code.

  • orphaned object references - because of the way protobuf creates the roots data structure at runtime, every time C is loaded into memory it replaces the previous instance of C. Therefore you can potentially end up with references to different instances of C. Practically I'm not sure what issues this could cause but best to be avoided.

To work around this you could use pbjs to generate one bundle of JS bundle per page, but this isn't ideal for two reasons:

  • http caching - if two pages both need C's code, then ideally you have one copy of it that can be shared.
  • on-demand loading - if you only have one bundle then you can't load parts of it as needed.

You could also generate one bundle for all pages, but this does not scale.

The fact that pbjs bundles at all is a little weird. The protoc tool doesn't do this for any language, including JS. And even for web browsers this isn't ideal for the reasons stated. If bundling is needed, asset bundlers (e.g. rollup / webpack) should be used for this.

Example

my/protos/c.proto

package my_protos_c;

message C {
   string some_field = 1;
}

my/protos/a.proto

package my_protos_a;
import "my/protos/c.proto";

message A {
  my_proto_c.C nested_msg_c = 1;
}

my/protos/b.proto

package my_protos_b;
import "my/protos/c.proto";

message B {
   my_proto_c.C nested_msg_c = 1;
}

Strategy 1: run pbjs on each proto
pbjs --target static-module --out a.js a.proto
pbjs --target static-module --out b.js b.proto

import {my_protos_a} from 'my/protos/a';
import {my_protos_b} from 'my/protos/b';

page loads C's code twice

Strategy 2: generate one js bundle per page with pbjs
pbjs --target static-module --out page_1.js a.proto
pbjs --target static-module --out page_2.js b.proto

page 1

import {my_protos_a} from 'my/protos/page_1';

page 2

import {my_protos_b} from 'my/protos/page_2';

page 1 loads different copy of C than page 2

Strategy 3: generate one .js file for every .proto

pbjs --target static-module --path out/ --no-bundle a.proto
pbjs --target static-module --path out/ --no-bundle b.proto
pbjs --target static-module --path out/ --no-bundle c.proto

import {my_protos_a} from 'my/protos/a';
import {my_protos_b} from 'my/protos/b';

only 1 copy of C is loaded

page 1

import {my_protos_a} from 'my/protos/page_1';

page 2

import {my_protos_b} from 'my/protos/page_2';

page 1 loads same copy of C as page 2

@beldur
Copy link

beldur commented Apr 4, 2023

Hi @taylorcode, is this feature still something that could be merged in the future?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants