ef4 / prember

Prerender Ember apps with Fastboot at build time.
MIT License
194 stars 17 forks source link

Pre-rendering URLs and fingerprinting assets issue #29

Open chrism opened 5 years ago

chrism commented 5 years ago

The problem: broccoli-asset-rev works perfectly for paths which are simple strings but does not match for dynamically generated strings like assets/img/${this.baseSrc}-1280.jpg.

The solution(s): There seems to be quite a few possible approaches to solving this, like addons like Ember CLI IFA which use the postBuild hook to insert a serialized asset map into the html document, or a similar approach @ryanto shared inserting the serialized json into the vendor.js instead.

The issue: Unfortuntately I've been unable to get any of them to work with Prember. Initially, I used ember-cli-ifa as the basis for a solution which would also work with Fastboot—by using dedicated initializers to extract the meta content via document in browser and via JSDOM with Fastboot.

You can see the Fastboot initializer here but unfortunately despite trying to block the rendering with fastboot.deferRendering() the first initial render in Fastboot still seems to miss the fingerprinting.

Rendering with Prember the fastboot.request.host is correct as localhost:7784, but the fastboot.request.protocol seems absent. Worse, it seems like even hardcoding the protocol the initializer is running before the postBuild hook has finished—because the error message relates to the pre-replaced content ie

Unexpected token _ in JSON at position 0

Due to the fact that I'm also using Broccoli Static Site JSON for the CMS and it seems like there is some ongoing work on the integration with Prember I decided that the injecting json into vendor.js approach might be simpler to achieve my aims.

Unfortunately that seems to bring a different set of problems which I think are more due to the synchronisation necessary between addons.

I notice you have done a lot of work on trying to disentangle broccoli-asset-rev from Fasboot but so far I have not been able to order the addons correctly to achieve the goals I want.

I have 3 in-repo-addons

My aim is to run them so that ember-cli-asset-map runs before prember-urls-crawler so that the fingerprinted URLs are available in Fastboot/Prember.

So far I haven't been able to find a combination of ordering which results in that. No matter what I use in the package.json ie using

{
  "name": "prember-urls-crawler",
  "keywords": [
    "ember-addon",
    "prember-plugin"
  ],
  "ember-addon": {
    "after": "ember-cli-asset-map"
  }
}

If I output a message from the postBuild hook in the ember-cli-asset-map addon it always comes after the output of the Prember generation, like this:

merging via json-api
pre-render / 200 OK
asset map written to vendor.js

Have I missed something or is this a bug with Prember and the way it interacts with other addons?

Many thanks.

ef4 commented 5 years ago

There are some misconceptions in here that may be the source of your challenges.

One is that addon ordering only affects things that happen during the build, not during the running of your app. So things like fastboot.deferRendering() fundamentally aren't capable of changing anything about the relative timing of build-time things like fingerprinting.

Another is that URL crawling isn't really a stage you can separate from prember. It's just some configuration that goes into prember. So "before" or "after" on a crawler plugin won't have any effect.

The addon ordering that probably matters here is the relative order of ember-cli-fastboot, prember, and ember-cli-ifa. There may be some ordering that would just work without other changes.

chrism commented 5 years ago

Hi @ef4 thanks very much for replying.

I'm sorry that my initial post was so broad as there were quite a few gaps in my understanding at the time I wrote it.

The explanation that URL crawling is integral to Prember and part of the build process makes clear why ordering the addons didn't have the effect I initially anticipated it would.

In the process of getting a working version there were several tangential issues which at the time I wasn't sure were due to misunderstanding on my part, or genuine issues. I see that the protocol issue has now been raised separately and there was also an issue with ember-fetch which has already been merged and released.

I've now been able to determine the base cause of my issue—and what I'd still really appreciate some help with as it doesn't seem like the expected behaviour of Prember to me.

It's that the urlsForPrember hook always runs before the /assets/assetMap.json file gets generated.

To try to reduce the issue down to it's simplest form I've forked this addon and created a new branch where I've added a very simple example.

Adding fingerprinting and generating the asset map to ember-cli-build.js and duplicating the use-static-asset route with an asset-map one that loads the asset map instead.

This works fine with Fastboot, ie http://localhost:4200/asset-map — but the build breaks pre-rendering that URL once it is added to the urls array in url-tester.js.

Checking to see which files already exist at that point shows that the static asset is there, but the asset map isn't.

DEBUG: -------------------------------
DEBUG: Ember : 3.0.0
DEBUG: -------------------------------
static file? true
asset map? false
pre-render / 200 OK
pre-render /redirects 307 //localhost:7784/from-sample-data
pre-render /use-static-asset 200 OK
pre-render /discovered 200 OK
pre-render /from-sample-data 200 OK

Build successful (5414ms) – Serving on http://localhost:4200/

In my application I need to access this file to retrieve the fingerprinted values, but Prember crawls the URLs before it gets created.

Do you consider this an actual issue with the addon—perhaps it's more a necessity of Prember which needs to be worked around?

If it's to be worked around could I perhaps ask what would be your recommended approach?

So far everything I've tried has been unsuccessful. I think this is why I was tying myself up in knots trying to use preBuild postBuild etc etc addon hooks and still don't feel like I've yet found a clean solution.

Thanks again for you time.