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

Filter duplicate extensions by src URL #1192

Merged
merged 11 commits into from
Apr 8, 2021
Merged

Filter duplicate extensions by src URL #1192

merged 11 commits into from
Apr 8, 2021

Conversation

sebastianbenz
Copy link
Collaborator

Fixes #1191

@westonruter
Copy link
Member

Testing PHP implementation: ampproject/amp-toolbox-php#130

Comment on lines 11 to 13
<link rel="preload" href="https://cdn.ampproject.org/v0.css" as="style">
<link href="https://fonts.googleapis.com/css?foobar" rel="stylesheet" type="text/css">
<link as="script" crossorigin="anonymous" href="https://cdn.ampproject.org/v0.mjs" rel="modulepreload">
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't the link[rel=preload] and link[rel=modulepreload] be moved to the beginning of the head?

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

@westonruter
Copy link
Member

Here's the current order in the PHP implementation:

  <link as="script" crossorigin="anonymous" href="https://cdn.ampproject.org/v0.mjs" rel="modulepreload">
  <link rel="preload" href="https://cdn.ampproject.org/v0.css" as="style">
  <link rel="stylesheet" href="https://cdn.ampproject.org/v0.css">
  <script async nomodule src="https://cdn.ampproject.org/v0.js"></script>
  <script async src="https://cdn.ampproject.org/v0.mjs" type="module" crossorigin="anonymous"></script>
  <script async nomodule src="https://cdn.ampproject.org/v0/amp-experiment-0.1.js" custom-element="amp-experiment"></script>
  <script async custom-element="amp-experiment" src="https://cdn.ampproject.org/v0/amp-experiment-0.1.mjs" type="module" crossorigin="anonymous"></script>
  <script async="" nomodule="" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.js" custom-template="amp-mustache"></script>
  <script async="" custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.mjs" type="module" crossorigin="anonymous"></script>
  <link href="https://example.com/favicon.ico" rel="icon">
  <link href="https://fonts.googleapis.com/css?foobar" rel="stylesheet" type="text/css">

When outputting AMP scripts, it seems that the module/nomodule versions should be next to each other in general, right? Also, it seems the module version should always come first? That isn't yet the case for the PHP transformer.

@sebastianbenz
Copy link
Collaborator Author

When outputting AMP scripts, it seems that the module/nomodule versions should be next to each other in general, right? Also, it seems the module version should always come first? That isn't yet the case for the PHP transformer.

Not sure if that really makes a difference. I'd assume that the browser will simply ignore the nomodule tag and move on.

<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.mjs" type="module" crossorigin="anonymous"></script>
<link href="https://example.com/favicon.ico" rel="icon">
<link href="https://fonts.googleapis.com/css?foobar" rel="stylesheet" type="text/css">
<link as="script" crossorigin="anonymous" href="https://cdn.ampproject.org/v0.mjs" rel="modulepreload">
Copy link
Member

@westonruter westonruter Apr 6, 2021

Choose a reason for hiding this comment

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

This modulepreload link needs to be moved up along with the preload as well. Ref ampproject/amp-toolbox-php@a3a99fd

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the patience taking it up with my empty brain today...

<!doctype html>
<html ⚡>
<head>
<script async nomodule src="https://cdn.ampproject.org/v0.js"></script>
Copy link
Member

Choose a reason for hiding this comment

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

Big problem: The nomodule v0.js script is getting dropped in the output. Here's one thing I had to do in the PHP version to account for this: ampproject/amp-toolbox-php@4bad718

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

<link as="script" crossorigin="anonymous" href="https://cdn.ampproject.org/v0.mjs" rel="modulepreload">
<link rel="preload" href="https://cdn.ampproject.org/v0.css" as="style">
<link rel="stylesheet" href="https://cdn.ampproject.org/v0.css">
<script async src="https://cdn.ampproject.org/v0.mjs" type="module" crossorigin="anonymous"></script>

This comment was marked as resolved.

@westonruter
Copy link
Member

Not sure if that really makes a difference. I'd assume that the browser will simply ignore the nomodule tag and move on.

Can we go ahead and do that so that there is consistency in the ordering? I've implemented this in the PHP version now. The PHP version now generates the head as follows for ReorderHeadTransformer/supports_esm/input.html:

  <link as="script" crossorigin="anonymous" href="https://cdn.ampproject.org/v0.mjs" rel="modulepreload">
  <link rel="preload" href="https://cdn.ampproject.org/v0.css" as="style">
  <link rel="stylesheet" href="https://cdn.ampproject.org/v0.css">
  <script async src="https://cdn.ampproject.org/v0.mjs" type="module" crossorigin="anonymous"></script>
  <script async nomodule src="https://cdn.ampproject.org/v0.js"></script>
  <script async custom-element="amp-experiment" src="https://cdn.ampproject.org/v0/amp-experiment-0.1.mjs" type="module" crossorigin="anonymous"></script>
  <script async nomodule src="https://cdn.ampproject.org/v0/amp-experiment-0.1.js" custom-element="amp-experiment"></script>
  <script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.mjs" type="module" crossorigin="anonymous"></script>
  <script async nomodule src="https://cdn.ampproject.org/v0/amp-mustache-0.2.js" custom-template="amp-mustache"></script>
  <link href="https://example.com/favicon.ico" rel="icon">
  <link href="https://fonts.googleapis.com/css?foobar" rel="stylesheet" type="text/css">

This ordering aligns with the AMP boilerplate usage as well, where we have style[amp-boilerplate] followed by noscript > noscript[amp-boilerplate]. In general it seems preferable for the thing which is the normal case to be first. For extremely slow connections, the module version could be loaded sooner than the non-module version if the HTML loading gets interrupted.

@westonruter
Copy link
Member

The PHP version is also sorting the script components by name, so amp-experiment script(s) will come before amp-mustache for example.

Comment on lines 52 to 59
_removeDuplicateCustomExtensions(extensions) {
const nodesByName = new Map();
const nodesBySrc = new Map();
for (const node of extensions) {
const name = this._getName(node);
nodesByName.set(name, node);
const src = node.attribs['src'];
nodesBySrc.set(src, node);
}
return Array.from(nodesByName.values());
return Array.from(nodesBySrc.values());
}
Copy link
Member

Choose a reason for hiding this comment

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

I took an alternative approach to this in PHP, to do the deduplication in the registerScript function itself: https://github.com/ampproject/amp-toolbox-php/pull/130/files#diff-b0da2cc892f74bf1ebc7a84e63d7065894b24541a48f3a26089f23860ee1209fR156-R190

This also accounts for module and nomodule variations.

for (const node of extensions) {
const name = this._getName(node);
nodesByName.set(name, node);
const src = node.attribs['src'];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this remove the host to make sure a same extension is not downloaded twice from two different hosts?

Comment on lines 11 to 12
<script async nomodule src="https://cdn.ampproject.org/v0/amp-mustache-0.2.js" custom-template="amp-mustache"></script>
<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.mjs" type="module" crossorigin="anonymous"></script>
Copy link
Member

Choose a reason for hiding this comment

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

Can the order be reversed, with the nomodule one coming after?

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

@@ -100,31 +96,39 @@ class HeadNodes {
}

_registerScript(node) {
const moduleIndex = hasAttribute(node, 'nomodule') ? 0 : 1;
Copy link
Member

Choose a reason for hiding this comment

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

This should ensure nomodule comes after type=module.

Suggested change
const moduleIndex = hasAttribute(node, 'nomodule') ? 0 : 1;
const moduleIndex = hasAttribute(node, 'nomodule') ? 1 : 0;

Copy link
Collaborator Author

@sebastianbenz sebastianbenz left a comment

Choose a reason for hiding this comment

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

Addressed the feedback. Scripts should now have the right order.

Not super happy about the changes as the implementation is now more complex (and expensive) than before (without a real need, as the module scripts are injected in a later stage of the transformation).

Comment on lines 11 to 12
<script async nomodule src="https://cdn.ampproject.org/v0/amp-mustache-0.2.js" custom-template="amp-mustache"></script>
<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.mjs" type="module" crossorigin="anonymous"></script>
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

<!doctype html>
<html ⚡>
<head>
<script async nomodule src="https://cdn.ampproject.org/v0.js"></script>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 60 to 61
appendAll(head, this._metaOther);
appendAll(head, this._resourceHintLinks);
Copy link
Collaborator

Choose a reason for hiding this comment

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

The metaOther can potentially includes lots of non-critical stuff like OpenGraph tags. I think in terms of performance, the resource hints should come right after the charset and before these other meta tags.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

good point! done

Copy link
Collaborator

@schlessera schlessera left a comment

Choose a reason for hiding this comment

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

I'm still wondering about the following priorities:

  1. ) Is the viewport needed for making decisions about the resource hints? I don't think so, but I'm not entirely sure.
  2. ) The boilerplate style is now much further down, after the scripts. Are we sure this does not lead to FOUC?

Comment on lines 47 to 48
// needs to run after ReorderHeadTransformer
'RewriteAmpUrls',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// needs to run after ReorderHeadTransformer
'RewriteAmpUrls',
'RewriteAmpUrls',

Comment on lines 89 to 90
// needs to run after ReorderHeadTransformer
'RewriteAmpUrls',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// needs to run after ReorderHeadTransformer
'RewriteAmpUrls',
'RewriteAmpUrls',

Comment on lines 122 to 123
// needs to run after ReorderHeadTransformer
'RewriteAmpUrls',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// needs to run after ReorderHeadTransformer
'RewriteAmpUrls',
'RewriteAmpUrls',

@sebastianbenz
Copy link
Collaborator Author

re 1) Yes, viewport needs to come first (I think that was the reason why I added metaOthers before the resource hints originally. The correct way is to also special case viewport.
re 2) I'm not sure about this. My thinking for the current order is: component downloads should start asap hence they should be placed before custom css. Boilerplate should come after custom CSS to avoid custom CSS accidentally overriding the boilerplate. FOUC should not be a problem as the browser will parse all CSS before rendering the page.

@sebastianbenz
Copy link
Collaborator Author

Hm. Following that logic, we should also move v0.css down.

Copy link
Member

@westonruter westonruter left a comment

Choose a reason for hiding this comment

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

LGTM, but I defer to @schlessera.

@schlessera
Copy link
Collaborator

:shipit:

@sebastianbenz
Copy link
Collaborator Author

Thanks a lot for the patience and detailed review!

@sebastianbenz sebastianbenz merged commit 359aea3 into main Apr 8, 2021
@sebastianbenz sebastianbenz deleted the reorder-esm branch April 8, 2021 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ReorderHead deduplicates based on name only
3 participants