Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

Fetch scripts in the <head> on client-side navigations #164

Merged
merged 19 commits into from
Mar 23, 2023

Conversation

michalczaplinski
Copy link
Collaborator

@michalczaplinski michalczaplinski commented Feb 28, 2023

Fixes #153

This PR is a very crude first attempt to fetch the scripts on client-side navigations. It does not work with inline scripts, violates the defer and async attributes and probably breaks heaps of other things.

This functionality is however needed in order for the WP Movies Demo to showcase it's full capability so we should include (even a half-working) solution:

For example, in this movies demo, if we land on the homepage and navigate to a movie page, https://github.com/c4rl0sbr4v0/wp-movies-demo/pull/19#issuecomment-1436547876 (a block only present in the movies pages) won't work because the actions needed haven't been defined.

Copy link
Member

@luisherranz luisherranz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's ok for a temporary fix 🙂


Right now it only works for the head, so maybe we should pass document to the fetchHead function and rename it to fetchAssets or something. It can still return the head, though.

const fetchHead = async (head) => {

@@ -105,6 +126,11 @@ export const init = async () => {
const body = toVdom(document.body);
hydrate(body, rootFragment);

// Cache the scripts. Has to be called before fetching the head.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess "has to" is a strong word. It should say "should be called".

It's because if we don't cache the scripts that are present on the initial page load, then they will be fetched again inside of the fetchHead() because we call fetchHead() on the initial page load as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, we should probably do the same for the styles 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take into account that styles and scripts are not equal. Styles need to be added each time you navigate to a page because they may have been removed, but scripts only need to be executed once, even if they exist on multiple pages.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, but in this case, I think it's okay. I'm only talking about a minor optimization: Add the stylesheets to the cache on the initial page load so that we don't have to download them again when we navigate to a new page.

As you said, we still have to add both the stylesheets currently present in the head and the "new" ones:

https://github.com/WordPress/block-hydration-experiments/blob/1d67b24047f4c7cd7692adc3468e642298a8acbe/src/runtime/router.js#L67-L68

But for scripts, we can only add the "new" ones:

https://github.com/WordPress/block-hydration-experiments/blob/1d67b24047f4c7cd7692adc3468e642298a8acbe/src/runtime/router.js#L65

src/runtime/router.js Outdated Show resolved Hide resolved
Comment on lines 59 to 61
const scriptEl = document.createElement('script');
scriptEl.textContent = script;
return scriptEl;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't execute the code until you add it to the DOM, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right. Why do you ask? 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity. I wasn't 100% sure 😄

BTW, we would need to copy the type="module" here because those scripts are different.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely 🙁

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 0152fde 🙂

michalczaplinski and others added 2 commits February 28, 2023 13:10
Co-authored-by: Luis Herranz <luisherranz@gmail.com>
@michalczaplinski michalczaplinski marked this pull request as ready for review March 17, 2023 01:09
@michalczaplinski
Copy link
Collaborator Author

Weird, the e2e tests seem to pass just fine locally:

Screenshot 2023-03-16 at 20 11 16

@michalczaplinski
Copy link
Collaborator Author

michalczaplinski commented Mar 17, 2023

Weird thing number 2: Locally there are 69 tests running, but in github actions there are 90 tests:

https://github.com/WordPress/block-interactivity-experiments/actions/runs/4346748522/jobs/7800093352?pr=164#step:7:15

Possibly related to the number of workers? Locally, the tests run in 5 workers, whereas in CI, it's 1 worker.

@SantosGuillamot
Copy link
Contributor

I have just merged the main branch into this one. Now, I can see all the tests (96) locally, and the ones failing also fail locally.

@SantosGuillamot
Copy link
Contributor

I've been triaging the issue and I think we had two problems:

  • The fetchScriptOrStyle function wasn't async and we were using Promise.all(...).map.
  • We weren't passing head to the function, although we are using it.

I made a commit with what I considered a fix. Tests are passing now. Feel free to revert or modify it.

Apart from that, I've realized that we don't have e2e tests for this PR, nor for client-side navigation. I believe we should create some tests covering that:

  • The content gets updated after client-side navigation.
  • Styles are applied.
  • New scripts work.

@SantosGuillamot
Copy link
Contributor

Right now it only works for the head, so maybe we should pass document to the fetchHead function and rename it to fetchAssets or something. It can still return the head, though.

I believe we need this as well. Right now, in the Movies demo for example, the view.js files are included in the <body> because they are needed after the runtime.

@SantosGuillamot
Copy link
Contributor

SantosGuillamot commented Mar 17, 2023

I've created initial tests to cover client-side navigation in this pull request.

EDIT: From what I can see there, the new scripts seem to work in the e2e tests when:

However, I am not able to make it work with the Movies demo.

@michalczaplinski
Copy link
Collaborator Author

However, I am not able to make it work with the Movies demo.

What do you mean by that? In what way did it not work?

@SantosGuillamot
Copy link
Contributor

Right now it only works for the head, so maybe we should pass document to the fetchHead function and rename it to fetchAssets or something. It can still return the head, though.

I made this commit that aims to fetch the scripts in the whole document and not only the head.

It seems that this passes the client-side navigation tests, and I've checked it in the Movies demo, and it seems to work fine as well. Please take a look at the code because I am not sure the changes I made are 100% correct.

Create e2e tests for client-side navigation
@michalczaplinski
Copy link
Collaborator Author

michalczaplinski commented Mar 23, 2023

I'm going to merge this now 🙁 Great work, Mario!

My only concern is that I'd like us not to block when calling fetchAssets() in the future. Right now we always await it, for example inside of the init() or when calling fetchPage() but we should be able to delay awaiting until the new head is retrieved from the pages cache, right before navigation to a new page happens.

I've also realized that we can simplify the code a little bit in the future because querySelectorAll() can match a group of elements like: querySelectorAll('link[rel=stylesheet], script[type=module], script[src]').

@michalczaplinski michalczaplinski merged commit ebd60ec into main-wp-directives-plugin Mar 23, 2023
@luisherranz luisherranz deleted the fetch-scripts branch March 23, 2023 10:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fetch directives scripts after client-side navigation
3 participants