xp-forge / frontend

Web frontends
1 stars 1 forks source link

Add `xp bundle` subcommand #12

Closed thekid closed 3 years ago

thekid commented 3 years ago

See issue #11

Usage

This is the command usage:

xp help bundle

Run

Here's an example run (inside Composer) which creates two bundles (after updating some PHP packages):

composer up

The 3.5 seconds is only for bundling, Composer adds another 7 seconds to the entire run

thekid commented 3 years ago

This is how far we've come with this in our example project:

Package config

Next to the dependencies inside package.json, we now also have our bundles specified:

{
  "dependencies": {
    "dropzone": "^5.8",
    "fomantic-ui": "^2.8",
    "handlebars": "^4.7",
    "jquery": "^3.6",
    "simplemde": "^1.11",
    "transliteration": "^2.1"
  },
  "bundles": {
    "vendor": {
      "jquery": "dist/jquery.min.js",
      "fomantic-ui": "dist/semantic.min.js | dist/semantic.min.css",
      "handlebars": "dist/handlebars.min.js",
      "transliteration": "dist/browser/bundle.umd.min.js"
    },
    "editor": {
      "dropzone": "dist/dropzone.min.js",
      "simplemde": "dist/simplemde.min.js | dist/simplemde.min.css"
    }
  }
}

Composer integration

The following makes our bundler run every time composer update is executed:

diff --git a/composer.json b/composer.json
index 336fa6a..fdfd4fc 100755
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,7 @@
     "xp-forge/web-auth": "^2.2",
     "xp-forge/rest-api": "^3.0",
     "xp-forge/neo4j": "^1.0",
-    "xp-forge/frontend": "^2.0",
+    "xp-forge/frontend": "dev-feature/bundler as 2.1.0",
     "xp-forge/handlebars-templates": "^0.5",
     "xp-forge/markdown": "^6.0",
     "xp-forge/inject": "^5.0",
@@ -26,6 +26,7 @@
   },

   "scripts": {
+    "post-update-cmd": "xp bundle src/main/webapp/static",
     "dev": "xp -supervise web -m develop -c src/main/etc/dev -a 0.0.0.0:80 com.enbw.skills.App",
     "serve": "xp -supervise web -c src/main/etc/dev -a 0.0.0.0:80 com.enbw.skills.App"
   }

Changes to the codebase

We can now load our bundles instead of referring to them on a CDN. Note: We load editor.css on every page because we don't have an overrideable section like for scripts where each page can "extend" the layout, this would be a bigger diff

diff --git a/src/main/handlebars/edit.handlebars b/src/main/handlebars/edit.handlebars
index 4314582..1190a8d 100755
--- a/src/main/handlebars/edit.handlebars
+++ b/src/main/handlebars/edit.handlebars
@@ -90,7 +90,7 @@
     <br clear="all"/>
   {{/inline}}
   {{#*inline "script"}}
-    <script src="https://cdn.jsdelivr.net/npm/dropzone@5.8.1/dist/dropzone.min.js"></script>
+    <script src="/static/editor.js"></script>
     <script type="text/javascript">
       let _debounce = null;
       let editor;
diff --git a/src/main/handlebars/layout.handlebars b/src/main/handlebars/layout.handlebars
index de63f89..cbf3e30 100755
--- a/src/main/handlebars/layout.handlebars
+++ b/src/main/handlebars/layout.handlebars
@@ -5,8 +5,9 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta name="msapplication-config" content="none"/>
     <title>{{> title}} - Skills</title>
-    <link href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css" rel="stylesheet">
-    <link href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.css" rel="stylesheet">
+    <link rel="preload" href="/static/themes/default/assets/fonts/icons.woff2" as="font" type="font/woff2" crossorigin>
+    <link href="/static/vendor.css" rel="stylesheet">
+    <link href="/static/editor.css" rel="stylesheet">
     <style type="text/css">
       nav a.active {
         background-color: #3e2765 !important;
@@ -343,11 +344,7 @@
       <small><a href="https://ec.europa.eu/esco/portal">ESCO</a></small>
     </main>

-    <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
-    <script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.js"></script>
-    <script src="https://cdn.jsdelivr.net/npm/handlebars@v4.7.6/dist/handlebars.js"></script>
-    <script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
-
+    <script src="/static/vendor.js"></script>
     <script type="text/javascript">
       function use($selector, language) {
         $selector.classList.add('loading');

However, we're still missing fingerprinting - see the discussion in https://github.com/xp-forge/frontend/issues/11#issuecomment-803981739 and https://github.com/xp-forge/frontend/issues/11#issuecomment-805078527

thekid commented 3 years ago

🐛 CSS dependencies end up in the wrong path:

-src/main/webapp/static/npm/fomantic-ui@2.8.7/dist/themes
+src/main/webapp/static/themes
thekid commented 3 years ago

Not realized in this pull request:

thekid commented 3 years ago

By using the abbreviated meta data format, we save around 10 MB of memory and also a couple of hundred milliseconds execution time!

-Bundle operations: 2 bundle(s) created in 3.400 seconds using 19244.68 kB memory
+Bundle operations: 2 bundle(s) created in 3.114 seconds using 9165.90 kB memory

See https://blog.packagecloud.io/eng/2018/01/24/npm-registry-internals/ and https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-metadata-format

thekid commented 3 years ago

By using the JSDelivr API, we can improve even further:

-Bundle operations: 2 bundle(s) created in 3.114 seconds using 9165.90 kB memory
+Bundle operations: 2 bundle(s) created in 2.653 seconds using 7096.74 kB memory

We could also leave up resolving the package to JSDelivr, see https://github.com/jsdelivr/data.jsdelivr.com#resolve-a-version-range. However, using this even more streamlined service doesn't affect performance noticeably while at the same time requring a second request for looking up available versions.

thekid commented 3 years ago

Compressing bundles with gzip or brotli

This would have a significant performance effect:

$ ls -al src/main/webapp/static/vendor.*
-rwxr-xr-x 1 timmf timmf 1362383 Mar 27 14:58 src/main/webapp/static/vendor.css
-rwxr-xr-x 1 timmf timmf  815049 Mar 27 14:58 src/main/webapp/static/vendor.js

$ gzip src/main/webapp/static/vendor.*

$ ls -al src/main/webapp/static/vendor.*
-rwxr-xr-x 1 timmf timmf 161814 Mar 27 14:58 src/main/webapp/static/vendor.css.gz
-rwxr-xr-x 1 timmf timmf 237002 Mar 27 14:58 src/main/webapp/static/vendor.js.gz

...giving us a near-perfect Lighthouse score:

image