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

RFC: Encoded Inline Bundles #3718

Closed
wbinnssmith opened this issue Oct 31, 2019 · 2 comments · Fixed by #3726
Closed

RFC: Encoded Inline Bundles #3718

wbinnssmith opened this issue Oct 31, 2019 · 2 comments · Fixed by #3726

Comments

@wbinnssmith
Copy link
Contributor

wbinnssmith commented Oct 31, 2019

Problem

In Parcel, we currently have multiple cases where one bundle can either reference or include the content of another bundle:

  • Inlining a child bundle's contents as text in the parent bundle (support for compiling inline <script> tags in html documents)
  • Referencing a url (relative or from the target's publicUrl) of a child bundle, when importing e.g. css from js or using new Worker('./workerEntry')

There are cases, however, when it's desirable to inline contents of another bundle (perhaps also as a url that itself contains the contents of the child bundle, rather than a relative http reference):

  • importing small images as base64 data: uris in JS, css, html
  • Passing new Worker() a base64 or blob-encoded uri instead of a relative http reference

Proposal

Using named pipelines [0], create several named pipelines that correspond to how the bundle should be inlined, and use these when depending on the asset that creates this bundle, e.g.:

  • import encodedImageUrl from 'url-base64:./image.png' (result will be a data:$mime;$base64content url)
  • new Worker('url-text:worker.js') (text would be in the form of a blob url)

This allows us to implement this functionality outside of core, with the following changes to the default config:

{
  "transforms": {
    "url-base64:*": ["@parcel/transformer-inline", "..."],
    "url-text:*": ["@parcel/transformer-inline", "..."]
    // ...
  },
  "optimizers": {
    "url-base64:*": ["@parcel/optimizer-url-base64", "..."],
    "url-text:*": ["@parcel/optimizer-url-text", "..."]
    // ...
  }
}

NOTE: This will require us to support declaring optimizers for a named pipeline. This is currently only implemented for transformers. Currently only dependencies and asset requests contain the requested pipeline. This will require us to persist the name of the pipeline on the produced asset, as well as carry it over to any bundles it forms, allowing us to influence the optimizers that run.

The role of the "@parcel/transformer-inline" transform will be to simply set asset.isInline to true.

The role of the optimizers will be to actually perform the translation to e.g. base64. These optimizers will not produce output particular to any environment. For example, the url-text optimizer will not produce the JavaScript code necessary to construct a blob url of the text. Instead, this will be the responsibility of the including asset's transformer or the parent bundle's packager.

Questions

  • Should this all just be part of core? Support for inline assets already is (for the HTML use case) and I'm not immediately sure if support for inline assets can be entirely removed from core. Either way, it would be nice to either include complete support for inline assets in core, or not include it at all.
  • asset.isInline could be an enum type of 'base64' | 'blob', but that would require a change to core, and this information is already encoded in the pipeline name.
  • It's possible that the packager for url-base64 could produce data:$mime;$base64content since like http urls, it's universal. That said, particular environments might require escaping of strings, etc. So maybe it's just best to let the packager do this.

[0] https://github.com/parcel-bundler/parcel/pull/3613/files#r333689194

@jamiekyle-eb
Copy link
Contributor

A few quick thoughts:

I really like the idea of having a named pipeline for this. I would hesitate adding too many named pipelines into core, but things like url-base64 and raw-source I think it makes sense.

I don't think named pipelines should necessarily be the default for people to inline an asset. I'd rather have someone do:

// .parcelrc
{
  "transforms": {
    "icons/*.svg": ["@parcel/transform-inline-base64-url"]
  }
}

Than to manually update every single import "icon/home.svg" (which is likely hundreds/thousands of references).

I also wonder if there are some use cases where a bundler could potentially automatically determine if an asset should be inlined.

  • This asset is not an entry point
  • It's only used in <X places.
  • Making an additional network request for this would just be slower.

Maybe some Chrome folks would be interested in some automated optimizations there

@kwelch
Copy link
Contributor

kwelch commented Nov 1, 2019

Thinking further down the line of auto determination, it would be killer to have it be slightly more generic to just url and then contain the logic to know at what point it is more efficient to base64 vs text.

As for the isInline in core, skipping the writing to dist is the only part that has to be in core, but we could have something more generalized, however I am unsure if there are any other cases that would need the ability to skip writing the output file.

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

Successfully merging a pull request may close this issue.

8 participants