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

Add assets to entries sorted according to their dep graph. #8092

Closed
wants to merge 4 commits into from

Conversation

jquense
Copy link
Contributor

@jquense jquense commented Sep 12, 2018

This steals borrows the chunk sorting algo from html-plugin. At the moment assets are included in sort of a random order which doesn't work for css especially. There is a bunch here that i'm a little iffy on but in my tests it seems to work...


return {
js: stats.assets.js.filter(isNotRootAndMatchesPage),
css: stats.assets.css.filter(isNotRootAndMatchesPage),
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 removed the sort by preload because it's not always stable :/ so messes up the order

})

if (groups[chunkName]) {
Object.entries(groups[chunkName].childAssets).forEach(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

it is very unclear when this is populated, iv'e tried a bunch of import() calls to stuff and never seen this have anything in it..


const webpackStats = {
...jsonStats,
assetsByChunkName,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

idk if this is still needed

Copy link
Contributor

@DSchau DSchau left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, per usual :) From what I can tell, this looks good, but I'm going to ask a few questions so I can make sure we're on the same page.

I'll probably want to pull this down and write some tests against it to validate.

Speaking of... the unit tests in Travis are failing, so those are probably worth looking into.

@pieh want to weigh in here?

packages/gatsby/src/webpack/stats-extractor-plugin.js Outdated Show resolved Hide resolved
as="script"
rel={script.rel}
key={script.file}
href={`${__PATH_PREFIX__}/${script.file}`}
Copy link
Contributor

Choose a reason for hiding this comment

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

Does script.file === script.name here? Why the switch?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm presuming it's because you changed the algo in assetsForCurrentPage() so it's returning a slightly different structure?

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 moved some of the work that was done here to the stats plugin, which creates an asset object like: { file, chunkName, rel }, file felt more descriptive i guess esp, next to the added "chunkName".

chunk. Thus topological sorting orders chunks from bottom-layer chunks to
highest level chunks that use the lower-level chunks.
*/
module.exports = (chunks, chunkGroups) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

100% out of scope, but I wish we could create a utility containing this algo, and then have both us and html-webpack-plugin use it 🙃

}

apply(compiler) {
compiler.hooks.done.tapPromise(
Copy link
Contributor

Choose a reason for hiding this comment

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

So this was tapAsync previously, now tapPromise?

I'm not a webpack guru, so not sure if it it's a meaningful difference 🤷‍♂️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same thing, the only difference is you can use a promise instead of a callback, which is more inline with what we do elsewhere

Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't look closely enough to see if it was a refactor or a straight copy 🙃 Makes sense!

// Add <link>s for styles that should be prefetched
// otherwise, inline as a <style> tag
headComponents.push(
...styles
Copy link
Contributor

Choose a reason for hiding this comment

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

Overall, what you're doing here is pushing non-prefetch elements to the start of the array, then everything else, correct?

What if we re-wrote this slightly for clarity (or at least I think it's clearer), e.g.

const prefetchedAssets = styles
  .filter(s => s.rel === `prefetch`)
  .map(style => (
    <link
      as="style"
      rel={style.rel}
      key={style.file}
      href={`${__PATH_PREFIX__}/${style.file}`}
    />
  ))

const nonPrefetchedAssets = styles
    .filter(s => s.rel === `prefetch`)
    .map(style => (
      <style
        key={style.file}
        data-href={`${__PATH_PREFIX__}/${style.file}`}
        dangerouslySetInnerHTML={{
          __html: fs.readFileSync(
            join(process.cwd(), `public`, style.file), 'utf8'
          )
        }}
      />
    ))

headComponents.unshift(...nonPrefetchedAssets) // this clarifies things a bit, i.e. that these are first
headComponents.push(...prefetchedAssets)

)
},
},
new StatsExtractorPlugin(),
Copy link
Contributor

Choose a reason for hiding this comment

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

I like this change a lot! Extracting to a separate file is a nice win.

@jquense
Copy link
Contributor Author

jquense commented Sep 13, 2018

yeah i've been meaning to fix the tests, just wanted to get some feedback first before workin on it

@jquense jquense force-pushed the sort-chunks branch 2 times, most recently from 80f3bfe to 6436ce9 Compare September 17, 2018 20:59
@jquense
Copy link
Contributor Author

jquense commented Sep 17, 2018

updated, rebases and fixed tests. Anyone want to take a chance on this ;) I could use the fix :0

@jquense
Copy link
Contributor Author

jquense commented Sep 17, 2018

@jquense
Copy link
Contributor Author

jquense commented Sep 18, 2018

I've spent a bit more time on this and i've got a few questions...why are we auto sorting the inline styles before everything? Consider linking font-awesome and bootstrap.css, we definitely want those to show up first, before any app styles. At the moment that's pretty hard to do without implementing onPreRenderHTML despite it being a very common pattern. Ya'll need some people who use css :P

@m-allanson
Copy link
Contributor

Hmm yeah I was looking at this earlier (using the demo repro at #6302) and noticed similar. Do you have time to make a basic repro site using plain CSS and gatsby-starter-hello-world? Then we can take that and write tests from it.

@jquense
Copy link
Contributor Author

jquense commented Sep 18, 2018

yup should have a moment today to do it

@jquense
Copy link
Contributor Author

jquense commented Sep 18, 2018

err, a quick try didn't have much luck. This is pretty hard to reproduce on a simple site. Reasons, i think, because there isn't enough complexity in the css requires, and this issue is sort of by definition non-deterministic...

@m-allanson
Copy link
Contributor

m-allanson commented Sep 18, 2018

Here's a kinda demo that shows the auto-sorting inlining styles resulting in different styles being applied in gatsby develop vs gatsby build. https://github.com/m-allanson/gatsby-v2-css-ordering.

It doesn't demo the main issue that you're fixing though.

Edit: pushing all the styles straight to headComponents will fix the behaviour in the above demo.

@KyleAMathews
Copy link
Contributor

People use CSS still??? :-P

@jquense
Copy link
Contributor Author

jquense commented Sep 20, 2018

another data point, i've got this in react-bootstrap as a hacky plugin and it seems to work great: https://github.com/react-bootstrap/react-bootstrap/tree/master/www/plugins/gatsby-plugin-sorted-assets

@jquense
Copy link
Contributor Author

jquense commented Sep 20, 2018

and here is a great example of how it was broken prior: https://5ba00f734ed62f7dda3cf9e5--react-bootstrap.netlify.com/components/badge/

@dustinhorton
Copy link
Contributor

Any updates here? Going to try the aforementioned plugin in the meantime—currently blocking a launch for me.

@dustinhorton
Copy link
Contributor

#8560 possibly related. also several others issues with other weirdness of styles popping up this week.

@princefishthrower
Copy link

princefishthrower commented Nov 19, 2018

If it helps, I've also seen this issue, similarly as described by @topherauyeung:

If I land directly or refresh a page... the CSS is broken for those components. The classes are present in the source but they aren't applied to the elements. If I follow a to the same page, however, everything works fine.

This is the same for me, when I go to non-homepage links directly, the styles are wonky.

HOWEVER (possibly the helpful part) When I hover over a <Link> component on such a page, (namely the title of my blog back to the homepage, all the styles are injected and it looks as expected). So something in is triggering the styles to load correctly.

Feel free to take a look at a live version of this behaviour here:

https://chrisfrew.in/getting-started-in-2018-with-magento-apache-and-php/

just hover the mouse over anywhere on the top of the post to see the styles injected (white background should go to gray, code highlights work, etc)

@dustinhorton
Copy link
Contributor

@frewinchristopher can see it even clearer in yours—thanks for sharing.

@dustinhorton
Copy link
Contributor

we've had to resort to not using gatsby-link, which is a huge loss in regards to perf.

@robertcoopercode
Copy link
Contributor

I left a detailed comment on issue #9911 mentioning how I reproduced this issue along with a repository & live demo of the issue. It's very similar to @frewinchristopher's description of the improper styles loading and taking precedence when a link is being hovered over.

@jquense
Copy link
Contributor Author

jquense commented Dec 3, 2018

I think the Gatsby link issue is slightly different than what is being solved here. That problem is that the resources for each page are not the same as what's needed for the next navigation. From what i can tell when you hover a link it preloads the next page's assets. However in the case of CSS, that's likely to include a bunch of styles that are already loaded, e.g. global site styles.

The problem with this is it realllly messes with the cascade since files that are supposed to loaded first are being loaded again after more specific styles. this doesn't always work well. What should happen is that Gatsby should only load new style resources, not all resources for the page. @pieh as far as you know does that describe the current behavior of gatsby-link, and the page resource pipeline as a whole?

@dustinhorton
Copy link
Contributor

@jquense Agreed this PR is not solving the issue I was experiencing—meant to add that explanation to my comment @ #8092 (comment). I thought it was happening on load and not just after hovering a gatsby-link at some point, but as of now it's only the latter.

What should happen is that Gatsby should only load new style resources, not all resources for the page.

I think that's correct. I thought I could get around this by importing global styles to gatsby-browser.js, but that didn't fix the problem.

Off the top of my head, other ways to fix might be 1. doing whatever happens in dev because it doesn't happen there 2. disable css prefetching (or make it disable-able).

@pieh
Copy link
Contributor

pieh commented Dec 3, 2018

The problem with this is it realllly messes with the cascade since files that are supposed to loaded first are being loaded again after more specific styles. this doesn't always work well. What should happen is that Gatsby should only load new style resources, not all resources for the page. @pieh as far as you know does that describe the current behavior of gatsby-link, and the page resource pipeline as a whole?

We don't inject same styles again. This is different problem - see #9911 (comment)

---edit:
I should rather say - we don't inject same stylesheet files. In this case styles are duplicated in multiple files causing injection of same styles.

@dustinhorton
Copy link
Contributor

@pieh Did you get a chance to check out my repro repo? https://github.com/dustinhorton/marketing

Just realized the docs (https://www.gatsbyjs.org/docs/creating-global-styles/) still recommend loading global styles in the layout component, if it exists—I could revert to that.

@jquense
Copy link
Contributor Author

jquense commented Dec 3, 2018

I should rather say - we don't inject same stylesheet files. In this case styles are duplicated in multiple files causing injection of same styles.

yeah, i think i had noticed tho that the way webpack splits css chunks with the SSR setup you get a lot duplication across chunks. e.g. the stylesheet is different (page--foo, page--bar.css, etc) but much of the content is the way. Might need a more aggressive splitChunks setup perhaps?

@jquense
Copy link
Contributor Author

jquense commented Dec 19, 2018

any chance we could merge this? I relize it may not solve all the problems but it definitely solves some of them. I'm tired of copying this plugin into every site i build :P

@aspiers
Copy link

aspiers commented Jan 6, 2019

I would love to see this merged too. Seems like it's affecting several people. I just tried rebasing it and it rebased cleanly against master, as shown in master...aspiers:sort-chunks. @jquense are you able to rebase and force-push?

@princefishthrower
Copy link

princefishthrower commented Jan 12, 2019

For what it's worth, I have a workaround for the time being, which is simply loading any custom CSS from index.html To do this in Gatsby, just follow this page to create a proper html.js file, then customize it with a nice <link> tag in the <head>, ex:

<link rel="stylesheet" type="text/css" href="https://yoursitehere.com/styles.css"/>

Make sure your style file(s) are in your static/ folder (in this case, the root of the static/ folder) before you issue gatsby build! It's not a very clean solution, but it works for the time being.

@KyleAMathews
Copy link
Contributor

@jquense is this still needed w/ #11072

@DSchau
Copy link
Contributor

DSchau commented Apr 8, 2019

This PR has gotten in a pretty gnarly state, and I think it's extremely plausible that it's not needed with #11072.

As such--let's close this out. If I'm off base or if this is still worth re-visiting, let one of us know or re-open and we'll take another look.

As always, thanks @jquense for the PR. Sorry this didn't get merged!

@DSchau DSchau closed this Apr 8, 2019
@jquense
Copy link
Contributor Author

jquense commented Apr 8, 2019

hmm #11072 seems like a worse solution to this? This is already a solved problem webpack html plugin does this and its what most apps use as a default. not splitting css seems like a worse default.

@jquense
Copy link
Contributor Author

jquense commented Apr 8, 2019

also this handles js assets as well, which should also be sorted and included in the right order

@DSchau DSchau reopened this Apr 8, 2019
@DSchau
Copy link
Contributor

DSchau commented Apr 8, 2019

@jquense let's revisit this, then.

When you get a minute, want to fix up the merge conflicts? Otherwise I'm happy to do this later, as well.

@jquense
Copy link
Contributor Author

jquense commented Apr 8, 2019

ya definitely i'll take some time to clean it up 👍

@DSchau DSchau added the status: awaiting author response Additional information has been requested from the author label May 21, 2019
@freiksenet
Copy link
Contributor

Hi @jquense!

We are going through old PRs with the core team. We really appreciate this contribution and we would like to have it in the Gatsby repo eventually. However, as there haven't any activity with the branch lately, we are going to close it.

Feel free to reopen it when you get the time to fix the merge conflicts.

Thank you so much for your contributions and looking forward for more of them. 💜 💪

@freiksenet freiksenet closed this Jun 10, 2019
@jquense
Copy link
Contributor Author

jquense commented Jun 10, 2019

i've published a hacky third party version at: https://www.npmjs.com/package/gatsby-plugin-sorted-assets

Ya'll sorry i haven't had time to update this! I probably won't have more time in the near future but i do want to nominate this issue as something that the team should explicitly mark as needed. The current way gatsby handles adding css assets is broken, and perhaps the severity of it is offset by folks using css-in-js solutions, but topo-sorting the css files is essentially required for css-modules at least, and is way webpack-html-plugin does it as a default. 👍

@wildeyes
Copy link

Sadly, @jquense the plugin doesn't seem to work with gatsby@^2.15.15 :(

@MichaelDeBoey MichaelDeBoey deleted the sort-chunks branch January 20, 2020 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: awaiting author response Additional information has been requested from the author
Projects
None yet
Development

Successfully merging this pull request may close these issues.