feat(npm-scripts): add minification to build process#233
Conversation
4357b8a to
4ae5b6b
Compare
|
Ok, so I just force-pushed with some updates:
And I checked the errors caused by files that contain non-top-level imports; those really are the only kind of error. Tried it on other modules with some success. So now I am going to look at doing some minification of JS inside JSP — that might be rather tricky though because the technique we use to run Prettier on JS inside JSP may not transfer across so well (because it is pretty whitespace-sensitive), but I'll investigate and report back what I find here. |
This is a WIP which will need some tweaking before it actually works, because just as I foreshadowed here: #233 (comment) > that might be rather tricky though because the technique we use to run > Prettier on JS inside JSP may not transfer across so well (because it > is pretty whitespace-sensitive) You can see this in the output of accounts-admin-web, were a file like - src/main/resources/META-INF/resources/account_users_admin/select_account_entry.jsp (contents: https://gist.github.com/wincent/af4157dd55eecc3a862dc75cd560f572) gets mangled to: - build/node/packageRunBuild/resources/account_users_admin/select_account_entry.jsp (contents: https://gist.github.com/wincent/df3db72e83ac0d3e523b9bb7012417d3) Here's the diff: https://gist.github.com/wincent/fb57fdd77c3a694405487f38cf1b6a03 It actually doesn't look too shabby at all, but will need some tweaking to get it totally right. Notably not handled by this approach are sneaky JSP files that contain JS outside of `<script>` (or `<aui:script>`) tags. For example: - JSP: https://github.com/liferay/liferay-portal/blob/510836e72b25d47d8e7095d60506bfc34b104769/modules/apps/frontend-js/frontend-js-web/src/main/resources/META-INF/resources/liferay/available_languages.jsp - Which is exposed sneakily here, as far as I can tell: https://github.com/liferay/liferay-portal/blob/510836e72b25d47d8e7095d60506bfc34b104769/portal-web/docroot/html/common/themes/top_js.jspf#L315
|
Added a (WIP) commit that formats JS in JSP. You can see this in the output of accounts-admin-web, were a file like Here's the diff; it actually doesn't look too shabby at all, but will need some tweaking to get it totally right. Notably not handled by this approach are sneaky JSP files that contain JS outside of
I'll take this up again tomorrow to see if I can polish it up enough to mark this as ready for review. |
|
Hey @wincent, I think this makes sense! Just to clarify, we're always producing I'd say we can park JSP minification unless you think it's very low-hanging fruit and then figure out whether it's worth it or not. Do you think we have enough already in the analysis phase to wrap it up and start planning this for real? (It feels like we're already well past that 😂 ) |
We overwrite all existing
So I added another commit polishing that up. I'd say it's pretty "solid" at this point. I see one
Indeed we are, but the thing with abstract stuff like this is that it is much easier to discuss when you have a concrete artifact. Questions like "is it really going to be that much simpler to do a single build rather than a dual build", or "how many bytes is this actually going to save us", or "is it worth the effort of minifying code in JSP" all become trivially easy to answer at this point. To that effect I think on Monday I am going to run this over all of DXP to get some before-and-after numbers, and I'll do some quick smoke testing to show that DXP has a chance of running properly after a fully minified And after that I don't know what planning is left to do. |
Exactly 😂
Yeah, I think we're done with analysis the moment we make an educated guess as to which direction we wanted to push for and work on this in If (as many times happens) our assumptions are proven wrong, then we simply step back and approach the problem differently. |
|
Cut a v34.1.0-pre.0 in order to test this and gather some metrics after a full |
|
Was going to rebase this on master because of the merge conflicts, but it seems the So I'll just push what I have over this stale history; this change kind of blows my mind:
|
This should ensure that we don't run into the kind of issue mentioned
here:
#233 (comment)
Note that `--frozen-lockfile` is supposed to take care of this for us,
but it is a known issue that it doesn't work properly in monorepos:
yarnpkg/yarn#5840
There is a fix for it:
yarnpkg/yarn#6554
But the issue is nearly two years old and the PR is almost as old. Maintainer replies here:
yarnpkg/yarn#6554 (comment)
that:
> The change appears sound, but we're currently working on the v2
> (scheduled before the end of the year) and I'm somewhat worried about
> introducing subtle bugs right before releasing the next major (the
> way I see it, the current v1 is, despite its shortcomings, relatively
> stable)."
We face a similar concern in liferay-portal and have a ticket for that:
https://issues.liferay.com/browse/LPS-110313
This should ensure that we don't run into the kind of issue mentioned
here:
#233 (comment)
Note that `--frozen-lockfile` is supposed to take care of this for us,
but it is a known issue that it doesn't work properly in monorepos:
yarnpkg/yarn#5840
There is a fix for it:
yarnpkg/yarn#6554
But the issue is nearly two years old and the PR is almost as old. Maintainer replies here:
yarnpkg/yarn#6554 (comment)
that:
> The change appears sound, but we're currently working on the v2
> (scheduled before the end of the year) and I'm somewhat worried about
> introducing subtle bugs right before releasing the next major (the
> way I see it, the current v1 is, despite its shortcomings, relatively
> stable)."
We face a similar concern in liferay-portal and have a ticket for that:
https://issues.liferay.com/browse/LPS-110313
These are all independent (they all end with `break`) so we may as well have them in alphabetical order. Sorting this before I add another one in commit real soon now.
This implements the first part of the idea described here:
https://docs.google.com/document/d/1JYSSSVFQlEwy1CRv1ZD6hfql8AzuXTS39qY5HMnvUe4/edit
Namely, if we're not in development mode, we pre-minify JS resources
before they are served. At the moment, I developed this just enough to
get some numbers. Here is what we see in `frontend-js-react-web`, which
is a good example of an OSGi module containing some "own JS" +
dependencies in `node_modules`.
Summary:
Files minified: 806
Files with errors: 169
Before size: 3561639
After size: 878708
Delta: 2682931
Elapsed: 8.88
Sizes are just raw bytes, ignoring gzip, so the "real" size in a
production environment would be much less, obviously (but so will the
savings).
Lots of files with errors, but at a glance, most of those look legit,
because our bundler will take every file in the graph and AMD wrap it
even if it shouldn't be wrapped (due to config, or whatever), so we wind
up with illegal JS in the JAR that will never get used (like files with
`import` statements inside AMD factory definitions).
The approach is to minify all the JS as a last step in the build
process. This way we cover JS that came in from `yarn install` and the
bundler, JS that we wrote and built with Babel, and even JS that came
out of webpack. This way we don't have to figure out how to jam a
minifier into three places, and it also means that we might be able to
do some better minification (eg. by minifying pre-bundled source where
we are more likely to be able to detect useful amounts of dead code
etc). Note that this also helps "future proof" us a little bit for the
arrival of Bundler v3 or v4 or whatever it ends up being called, because
the approach can be applied there as well with few or no modifications.
This is a WIP which will need some tweaking before it actually works, because just as I foreshadowed here: #233 (comment) > that might be rather tricky though because the technique we use to run > Prettier on JS inside JSP may not transfer across so well (because it > is pretty whitespace-sensitive) You can see this in the output of accounts-admin-web, were a file like - src/main/resources/META-INF/resources/account_users_admin/select_account_entry.jsp (contents: https://gist.github.com/wincent/af4157dd55eecc3a862dc75cd560f572) gets mangled to: - build/node/packageRunBuild/resources/account_users_admin/select_account_entry.jsp (contents: https://gist.github.com/wincent/df3db72e83ac0d3e523b9bb7012417d3) Here's the diff: https://gist.github.com/wincent/fb57fdd77c3a694405487f38cf1b6a03 It actually doesn't look too shabby at all, but will need some tweaking to get it totally right. Notably not handled by this approach are sneaky JSP files that contain JS outside of `<script>` (or `<aui:script>`) tags. For example: - JSP: https://github.com/liferay/liferay-portal/blob/510836e72b25d47d8e7095d60506bfc34b104769/modules/apps/frontend-js/frontend-js-web/src/main/resources/META-INF/resources/liferay/available_languages.jsp - Which is exposed sneakily here, as far as I can tell: https://github.com/liferay/liferay-portal/blob/510836e72b25d47d8e7095d60506bfc34b104769/portal-web/docroot/html/common/themes/top_js.jspf#L315
This may end up being a throw-away commit — I am not sure whether this
is legit — or I will squash it into the parent before merging.
Yesterday when I tested this there were ".jsp" files in the "build"
folder, which is how I was able to produce this diff showing the
minified JS:
https://gist.github.com/wincent/fb57fdd77c3a694405487f38cf1b6a03
Well, today after cleaning "build" and "classes", I only have JSP in
the latter. So I am hard-coding this modification in the most horrible
way, but it does work. The minified JS winds up in the JSP files that
you can find in the JAR; in the case of the module that I'm currently
testing, that's:
build/tmp/jar/com.liferay.account.admin.web-2.0.0.jar
Doubts:
- Is this even remotely kosher?
- Should it be configurable?
- Will modifying files in "classes/" cause trouble (redundant builds
stale caches) with Gradle or with "deploy fast" mode?
- Made comment-handling more robust. - Tweaked settings (eg. to turn off mangling). - Added tests. Required a bit of refactoring which is hopefully is safe thanks to our test coverage here.
Based on testing across the liferay-portal repo, the property mangling
needs to be turned off because it does some IMO surprising things. For
example, in:
https://github.com/liferay/liferay-portal/blob/c74cbb3487428c54ae2122c205af3c954e0e01a3/modules/apps/frontend-js/frontend-js-aui-web/src/main/resources/META-INF/resources/liferay/modules.js#L18-L21
This:
```
(function () {
var LiferayAUI = Liferay.AUI;
var COMBINE = LiferayAUI.getCombine();
```
gets mangled to:
```
!function(){var e = Liferay.AUI,a=e.i()
```
ie. global `Liferay.AUI` gets preserved (good), but a call to its
`getCombine()` method gets unfathomably rewritten to `i()`. No idea why
it would think it's legit to do that, but whatever, we can turn off that
kind of mangling with the following effects:
- With unwanted mangling (ie. prior to this commit):
Before size: 616616
After size: 201246
Delta: 415370
- With `mangle: false` (ie. no mangling at all):
Before size: 616616
After size: 353013
Delta: 263603
- With `mangle: {properties: false}` (ie. this commit):
Before size: 616616
After size: 266414
Delta: 350202
So we lose a bit of minification but not all of it (ie. instead of
trimming 405K, we trim 341K, which is not quite as bad as if we'd turned
off all mangling, which would mean trimming only 257K).
The other step in a conservative direction that I'm taking in this
commit is turning off some other renames that might cause problems when
minifying JSP. I haven't seen any evidence that this is needed yet, but
am erring on the side of caution.
795ad39 to
aa22b54
Compare
|
Resolved merge conflicts and force-pushed; no other changes. |
|
Are we going with minified JS in JSP, then? |
| } | ||
|
|
||
| if (process.env.NODE_ENV !== 'development') { | ||
| await minify(); |
There was a problem hiding this comment.
I was wondering where in the process would CSS be minified... Since terser can only minify JS, where do you see CSS build pipeline going?
There was a problem hiding this comment.
I focussed specifically on JS because that's what the LPS called for, but was thinking that once @bryceosterhaus's Dart Sass stuff has gone in, we tack on minification to that — depending on how long that work takes, we could look at doing it sooner, but I figured it was the path of least churn to wait.
I'm testing in DXP again today. If it goes poorly, we can rip out the minifying-in-JSP to expedite this. If it goes well, we may as well use it. |
Here's to it going well!! 🥂 |
|
Ok, summarizing my testing. I think this breaks little or nothing — screenshot shows a minified Clay file, but similar results for other node modules, and DXP-internal modules: Also all the tests are green over on my fork: ✔️ ci:test:stable - 9 out of 9 jobs passed✔️ ci:test:relevant - 20 out of 20 jobs passed in 2 hours 54 minutesClick here for more details.Base Branch:Branch Name: master Copied in Private Modules Branch:Branch Name: master-private ci:test:stable - 9 out of 9 jobs PASSED9 Successful Jobs:
ci:test:relevant - 20 out of 20 jobs PASSED20 Successful Jobs:
For more details click here.But some things are escaping minification; I'll look into these to see if I can discover a pattern: And source maps don't seem to work. They are detected, but I don't see the original source: Finally — minification in JSP didn't happen, because of ordering and/or caching issues. While it worked previously in my per-module test deployments, it doesn't seem to have taken effect globally during That's corresponds to these lines from More concerning though, is that it's not working for modules like this file in If I run and I can deploy that and see it working (ie. see the
So I'll keep poking around a bit to see if I can get the JSP thing working, but I suspect that the fiendish devilry of Gradle's dependency analysis and caching means that it is probably going to fall into the "not worth it category". |
I think the
I think that's also what @bryceosterhaus saw in the analysis for LPS-123287 - Analyze Sass compilation pipeline
|
|
FWIW I asked the build wizards over in Full text of my question for posterity:
|
docs: describe team repo sync script






This is a far-from-finished WIP, but parking it here anyway for safe-keeping:(upgrading this from WIP to POC)Final status:
Discussed in Slack here:
- The JS-in-JSP minification works if you run
yarn buildby hand, but when run as part of agradleinvocation the jobs happen in the wrong order for our purposes — we can't minify the JS in JSP inclasses/because it only gets put there afterpackageRunBuild. The conclusion of the Slack thread is that we can leave the code in here as a kind of benign no-op, and if we decide to prioritize the necessary changes on the Gradle side it will "magically" start working. If it's still there months from now and it looks like we're never going to get to it we can just rip it out.Excerpts from the Slack thread:
and:
Also discussed release here. I still haven't got source maps working, but so as to keep this rolling forward I am going to proceed with the merge and fast-follow with a fix as soon as I find it. Otherwise this could drag on for days or more.
Here's the gist. This implements the first couple of parts of the idea described in the research doc.
Namely, if we're not in development mode, we pre-minify JS resources before they are served. Using Terser because it's the current king (16.7 M weekly downloads) and the former champion (Uglify) is no longer maintained. babel-minify is still labeled as in beta and sees little activity. For some more context, here is a comparison from 2019.
At the moment, I developed this just enough to get some numbers. Here is what we see in
frontend-js-react-web, which is a good example of an OSGi module containing some "own JS" + dependencies innode_modules.Sizes are just raw bytes, ignoring gzip, so the "real" size in a production environment would be much less, obviously (but so will the savings).
Lots of files with errors, but at a glance, most of those look legit, because our bundler will take every file in the graph and AMD wrap it even if it shouldn't be wrapped (due to config, or whatever), so we wind up with illegal JS in the JAR that will never get used (like files with
importstatements inside AMD factory definitions).The approach is to minify all the JS as a last step in the build process. This way we cover JS that came in from
yarn installand the bundler, JS that we wrote and built with Babel, and even JS that came out of webpack. This way we don't have to figure out how to jam a minifier into three places, and it also means that we might be able to do some better minification (eg. by minifying pre-bundled source where we are more likely to be able to detect useful amounts of dead code etc). Note that this also helps "future proof" us a little bit for the arrival of Bundler v3 or v4 or whatever it ends up being called, because the approach can be applied there as well with few or no modifications.Sub-tasks: