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

Optimize static site with Parcel v2 #8

Merged
merged 9 commits into from Oct 13, 2020
Merged

Conversation

hsablonniere
Copy link
Owner

DISCLAIMER: Everything here is highly opinionated and dependent on my own context: building my site.

Eleventy claims to be a simple static site generator and it is 😉 By default it does not care much about your assets or how you want them to be processed.

Some users are asking for an official asset pipeline but I don't think Eleventy should step into this territory. Right now, it mostly focuses on contents and I think it's a good thing. It's why I came to try it in the first place.

I strongly believe in decoupling things. Each layer should do one thing and do it well. Each layer should know as little as possible about other layers. In the context of my own site, I want to decouple this two layers:

  1. build
  2. optimize

Build step

When I say "build", I mean what Eleventy can do right now:

  • contents: list, parse, apply template, decorate with layout, add metadata...
  • assets: copy

Once this phase is done, I expect the output to be a valid static site with standard Web assets (HTML, CSS, JS, images, videos, fonts...) that I could host online as is. Nothing that cannot run in a browser.

DISCLAIMER: I don't want (and don't think I will need) to use things like TypeScript, Sass or any other transpiled languages like that for my site. Therefore, right now, achieving this standard Web output with Eleventy is easy. I'm not sure what I would do if I needed to use those.

Optimize step

The next step is to "optimize" this static site. This is what I want for my site:

  • Minify HTML
  • Minify CSS
  • Minify JS
  • Optimize images (ex: .jpg to smaller .jpg)
    • jpg
    • png
    • gif
    • svg
  • Convert optimized images to alternative formats
    • jpg => webp/avif
    • png => webp/avif
    • Expect HTML files to have their replaced with listing alternative formats
  • Add hashes to all assets (with exceptions) so they can have "never expire" cache settings
  • Pre compress compressible assets so a static file server can send them directly
    • zopfli
    • brotli
  • Keep directory/file structure (personal taste and decoupling)

Those would be a bonus:

  • Bundling CSS if necessary
  • Bundling JS if necessary
  • Resize images (not too big)
  • Prepare responsive images (multiple sizes)
  • Optimize videos
  • Convert optimized videos to alternative formats

Remember, the output from the previous step consists of standard Web assets. This means this "optimize" step doesn't need to know about Eleventy. In theory:

  • I could build my site with something else and still use the very same optimize toolchain and config.
  • I could reuse this optimize toolchain and config on a different static site.

Now which tool can achieve such a task list?

Just like visitors will mostly discover/fetch the assets of my site via the HTML pages they're browsing, I need a toolchain that takes all my HTML files as inputs and discover the dependencies from there. I need a "Web centric" toolchain.

Webpack and Rollup

I've been using Webpack and Rollup for several years on multiple projects so I looked once more at the docs and the available plugins. However, even if they can work with assets like CSS/Sass/Less or images, those tools are JavaScript bundlers by nature. They need to start from JavaScript (or JSX/TS/TSX) files because they were designed for this purpose. From there, they create the dependency graph with the different ES imports, even for assets like CSS or images which is not standard for non JavaScript files. The way I see it but it's just my opinion about those:

  • Webpack is the SPA bundler. It really works well when you give it your main JavaScript entrypoint which will be transformed in splitted optimized bundles.
  • Rollup is the JS library bundler.

This does not have the "Web centric" feeling I'm looking for.
I think I could achieve what I want with those but I would need to bend them (a lot) and I don't want to do that.

DISCLAIMER: I don't know much about Webpack v5 which just came out.

Good old Gulp

I use Gulp a lot in my past. I know what it does and it does it pretty well. Most of the task I need to achieve are one-to-one so Gulp is an awesome fit for this. They main dealbreaker is the "Add hashes to all assets" task. I used (again) gulp-rev. It works well, the task is easy. The problem is replacing the asset names/URLs with the one with hashes in all assets. An operation that should trigger a change on the hash each time you modify the content to replace a URL. It's a graph problem and Gulp is too low level to handle that properly. I use gulp-rev-rewrite but it failed in some situations and I really feel like its replacing strategy is too naïve for the challenge.

Parcel v2

When I looked at the docs and the blog I noticed a few things:

  • It can take HTML files as inputs
  • I knows how to find dependencies from HTML, CSS...
  • It knows how to hash filenames properly

Parcel markets itself as the "zero configuration web application bundler". It's nice but I'm more of a 100% configuration person 😄 so I tried to make Parcel do nothing and add the stuffs I needed step by step. I spent a lot of time understanding its plugin system and its internals. It's really close to what I need.

As you can see in this PR (step by step), I was able to achieve a good part of my tasks:

Some details about this:

  • The jpg and png custom plugins were easy to write. It would take more time to handle config and write tests but it's a first step.
  • A "Transformer" in the Parcel system can only to one-to-one right now. That's the blocker for two kinds of plugins that need to transform one file into multiple separated files:
    • "transformer-html-images" jpg => jpg + wepb + avif
    • "compression" => asset + asset.gz + asset.br
  • Parcel renames files and flatten the structure. It's their opinion, so be it but it's not that easy to disable while keeping the hash names system. I tried https://github.com/mischnic/parcel-namer-preserve-structure but it was still renaming some CSS files, I need to dig that.
  • I have a custom lib to do the pre compression so for now, I'm using it after Parcel is done.
  • I think not being able to easily add a local plugin should be adressed, see One-off/local plugins parcel-bundler/parcel#4321

I really really like this tool. Apart from some details or yet to come features, it's really what I was looking for! Thank you Parcel team 😍 👏

Limitations and questions

  • Already mentioned but I don't know how I would handle using languages like Sass or TypeScript in the buidl phase especially during dev.
  • Yes having those two steps separated will reduce the performance of the whole build but I only need to run the optimize step in production.
    • I need to see how it handles 1000K HTML documents 😆

@hsablonniere hsablonniere merged commit 1eb9499 into master Oct 13, 2020
1 check was pending
@hsablonniere hsablonniere deleted the optimize-static-site branch October 13, 2020 02:05
@nhoizey
Copy link

nhoizey commented Oct 16, 2020

What about Responsive Images?

It might be possible to build a Parcel plugin based on https://nhoizey.github.io/images-responsiver/ !

@hsablonniere
Copy link
Owner Author

@nhoizey That's clearly something I also want to address. I'll ping you once I go down this path 😉

@nhoizey
Copy link

nhoizey commented Oct 19, 2020

👍

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

2 participants