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

Determine hoisted scripts via AST analysis #7707

Merged
merged 20 commits into from
Jul 20, 2023

Conversation

ottomated
Copy link
Contributor

@ottomated ottomated commented Jul 18, 2023

Changes

TODO

  • Investigate performance - there are opportunities for early returns and caching.
  • Attempt to move MDX-specific logic out of the core.
  • Add documentation
  • Make this work for <style> tags as well
  • This will current break in the case where a component is imported by another component. Fix would be to switch import tracking to the importer in this case?
  • Handle dynamic imports
  • depthsToExportNames should be a Map<number, string[]> because technically the same component could be exported twice from the same file under different names

Testing

A new test was added that tests various combinations of re-exporting style and checks to see if the built pages contain the expect components and no others.

Docs

TODO: document the exact behavior in the hoisted scripts section

/cc @withastro/maintainers-docs for feedback!

@changeset-bot
Copy link

changeset-bot bot commented Jul 18, 2023

🦋 Changeset detected

Latest commit: 097ed48

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the pkg: astro Related to the core `astro` package (scope) label Jul 18, 2023
@ottomated
Copy link
Contributor Author

ottomated commented Jul 18, 2023

This would break the edge case where a component is exported indirectly, like so:

import Component from './Component.astro';

export default getComponent() {
  return Component;
}

Not sure if that's an issue or not. It would require much deeper code analysis, tracking the imported variables, to solve that.

@matthewp
Copy link
Contributor

As long as this doesn't cause regressions I think a small change to support the common re-export pattern makes sense. I would just want the code cleaned up a little bit, move the AST logic into a separate, clearly named function.

@ottomated
Copy link
Contributor Author

Last change is the component tracking switcher needs to handle MDX. @matthewp which file extensions specifically can render astro components? Is it just .astro and .mdx?

// and start tracking this new component now
if (parentInfo.id.endsWith('.astro')) {
exportNames.push('default');
} else if (parentInfo.id.endsWith('.mdx')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume this is unacceptable - mdx logic shouldn't be in the core. Unsure on how to resolve.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, given we already have this in the code. Not ideal.

Copy link
Member

Choose a reason for hiding this comment

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

I haven't tested this yet, but shouldn't the ast already contain these exports and added them to exportNames?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is to handle a slightly different case. If component Title.astro is imported and used by TitleBar.astro, TitleBar.astro is not going to re-export Title. Therefore, this line basically switches from tracking Title to TitleBar, so the page will include Title's scripts.

@ottomated ottomated marked this pull request as ready for review July 19, 2023 11:46
@lilnasy
Copy link
Contributor

lilnasy commented Jul 19, 2023

If we only need to scan the imports, maybe we can avoid creating and walking the full AST by using es-module-lexer instead.

@ottomated
Copy link
Contributor Author

ottomated commented Jul 19, 2023

If we only need to scan the imports, maybe we can avoid creating and walking the full AST by using es-module-lexer instead.

Well, the AST is already created, but this is worth looking into I suppose

Edit: es-module-lexer does not return the names of what is imported, just where they're imported from. It won't work for this case.

@matthewp
Copy link
Contributor

This looks really good! I'm going to update the branch to see if that kicks off CI to run fully.

Copy link
Member

@bluwy bluwy left a comment

Choose a reason for hiding this comment

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

Clean tests! I think our strategy for hoisting by statically analyzing things won't always be correct, so having a best-effort implementation for now is fine by me.

There would be a slight performance hit with this as reading the ast in Rollup introduces an acorn parse, but running some build tests doesn't yield a big time difference, so maybe it is negligible.

@matthewp matthewp requested a review from a team July 20, 2023 14:38
Copy link
Member

@yanthomasdev yanthomasdev left a comment

Choose a reason for hiding this comment

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

Thanks, this looks great. Just found a small typo in the changeset! 🙌

@ottomated
Copy link
Contributor Author

Do we want to make this also work for styles before merging? It shouldn't be that hard

Copy link
Member

@sarah11918 sarah11918 left a comment

Choose a reason for hiding this comment

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

Hi @matthewp !

Now that Yan has made the minor fixes, this would be fine as-is after making <script> plural in line 7 (either with an "s" or "script TAGS")... I just think it could be better! ;)

See my updated take for your consideration and the reasons I'd be inclined to restructure. But totally your call! Take what you like from it, or take nothing at all!


Astro's static analysis to determine which `<script>` tags to bundle together just got a little smarter!

Astro create bundles that optimize script usage between pages and place them in the head of the document so that they are downloaded as early as possible. One limitation to Astro's existing approach has been that you could not dynamically use hoisted scripts. Each page received the same, all-inclusive bundle whether or not every script was needed on that page.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The existing approach actually did work in most cases (delivering different bundles per page) - this change only fixes the re-export case below.

Copy link
Member

Choose a reason for hiding this comment

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

@ottomated Can you suggest a rewording that is true? What would this paragraph have to look like so that it is accurate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

reworded in #7733 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: astro Related to the core `astro` package (scope)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Hoisted scripts break when components are re-exported from a typescript file
6 participants