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

Regroup non-static files #6285

Closed
wants to merge 37 commits into from
Closed

Regroup non-static files #6285

wants to merge 37 commits into from

Conversation

brotzky
Copy link
Contributor

@brotzky brotzky commented Jul 4, 2018

Hi,

This is my first attempt at trying to clean up Gatsby's build output. Instead of dumping all the generated files into the root of public by default it puts the scripts and styles into their own directories.

The naming of scripts and styles stems from the variables in the repository.

  public/
      {page}/
      scripts/
      styles/
      static/
      index.html  
      webpack.stats.json

Example output based on "hello-world" gatsby v2

To do

  • Clean up static/ output on each build Not required
  • Test with images and other static assets
  • Test with CSS output
  • Enabled through a feature flag for real world testing

Copy link
Contributor

@m-allanson m-allanson left a comment

Choose a reason for hiding this comment

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

Nice!

Re: clean up static output on each build. We actually don't want to do that. Maybe that will change in the future but I think that's out of scope for this PR. See discussion in #529, @pieh's comment in particular.

This could be a breaking change for plugins that rely on the current public directory structure. I wonder if this should be enabled via a feature flag so it can get some solid real-world testing?

@@ -221,7 +222,7 @@ export default (pagePath, callback) => {
as="script"
rel={script.rel}
key={script.name}
href={urlJoin(pathPrefix, script.name)}
href={urlJoin(`scripts`, pathPrefix, script.name)}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think pathPrefix should be first here. We'll need to ensure this PR works for sites using path prefixes. More info at https://www.gatsbyjs.org/docs/path-prefix/

Copy link
Contributor

Choose a reason for hiding this comment

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

You can test this locally by setting a pathPrefix in gatsby-config.js and running gatsby build --prefix-paths

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, you're correct that the pathPrefix should be leading. I've updated the locations that had it reversed.

I've also tested it locally.

module.exports = {
  pathPrefix: `/blog`
};

Output links become

# For gatsby Links
# Without prefix
/jobs 

# With prefix
/blog/jobs


# For file paths
# Without prefix
/scripts/component---src-pages-index-js-35049c33efafbda27bb4.js

# With prefix
/blog/scripts/component---src-pages-index-js-35049c33efafbda27bb4.js

Copy link
Contributor Author

@brotzky brotzky Jul 4, 2018

Choose a reason for hiding this comment

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

@m-allanson, to confirm, when running gatsby serve locally on a build that has pathPrefix enabled it shouldn't work? All the assets return 404 because Gatsby is serving the the public folder without a prefix (/blog)?

image

Copy link
Contributor

Choose a reason for hiding this comment

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

@brotzky yep, that sounds right. Check out the notes here on how to make this work for testing: https://github.com/gatsbyjs/gatsby/tree/master/examples/using-path-prefix

@@ -252,7 +253,7 @@ export default (pagePath, callback) => {
as="style"
rel={style.rel}
key={style.name}
href={urlJoin(pathPrefix, style.name)}
href={urlJoin(`styles`, pathPrefix, style.name)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment about pathPrefix

@@ -291,7 +292,10 @@ export default (pagePath, callback) => {
// Filter out prefetched bundles as adding them as a script tag
// would force high priority fetching.
const bodyScripts = scripts.filter(s => s.rel !== `prefetch`).map(s => {
const scriptPath = `${pathPrefix}${JSON.stringify(s.name).slice(1, -1)}`
const scriptPath = `/scripts${pathPrefix}${JSON.stringify(s.name).slice(
Copy link
Contributor

Choose a reason for hiding this comment

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

and here

publicPath: program.prefixPaths
? `${store.getState().config.pathPrefix}/`
: `/`,
? `/scripts${store.getState().config.pathPrefix}/`
Copy link
Contributor

Choose a reason for hiding this comment

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

pathPrefix before scripts

publicPath: program.prefixPaths
? `${store.getState().config.pathPrefix}/`
: `/`,
? `/scripts${store.getState().config.pathPrefix}/`
Copy link
Contributor

Choose a reason for hiding this comment

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

pathPrefix before scripts

tsiq-swyx and others added 2 commits July 4, 2018 13:17
* first attempt at lifecycle sequence docs

closes #6197

* address Kyle's review

I hope this is right...

* address Shannon's comments

address Shannon's comments
v2 should import Link directly from gastby
@brotzky
Copy link
Contributor Author

brotzky commented Jul 4, 2018

Thanks so much for checking over everything!
Will go in again today and clean it up with a fresh mind and a better understanding of the codebase!

Will not implement "clean up static output on each build"

default: false,
describe: `Build site without uglifying JS bundles (for debugging).`,
})
.option(`group-files`, {
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 added a flag to enable grouping of files.
gatsby build --group-files

Copy link
Contributor

Choose a reason for hiding this comment

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

Why a flag? We try to avoid adding config wherever possible.

Copy link
Contributor Author

@brotzky brotzky Jul 4, 2018

Choose a reason for hiding this comment

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

@KyleAMathews -- my bad!
What's the best way to add a "feature flag" for this? I tried looking through the code but couldn't grasp it right away. Is there a standard way to handle this?

I was trying to address this comment
#6285 (review)

This could be a breaking change for plugins that rely on the current public directory structure. I wonder if this should be enabled via a feature flag so it can get some solid real-world testing?

The flag would be temporary for people to try it out and then enabled by default after some time. After that the config option would be removed. The purpose is to provide a smoother transition with a testing phase before implementing a potential breaking change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, there shouldn't be any plugins which directly operate on outputted css/js so I think this change is pretty safe.

Copy link
Contributor

Choose a reason for hiding this comment

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

@m-allanson @pieh @jquense can you think of any reason we shouldn't just make this change?

Copy link
Contributor

Choose a reason for hiding this comment

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

w/o the flag

Copy link
Contributor Author

@brotzky brotzky Jul 6, 2018

Choose a reason for hiding this comment

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

Without the flags the code would be a lot cleaner. Less config options exposed to users and I can remove ifs, globals, and other logic from this PR. I'm in favour of removing the flag if it only impacts 1-2 plugins.

@@ -86,6 +86,25 @@ module.exports = async (
return hmrBasePath + hmrSuffix
}

function getOutputPaths() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There was duplicate code in the webpack output configs, so I moved it into a function that also handles the grouping of files.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@gatsbybot
Copy link
Collaborator

gatsbybot commented Jul 4, 2018

Deploy preview for using-drupal ready!

Built with commit d8a8377

https://deploy-preview-6285--using-drupal.netlify.com

@gatsbybot
Copy link
Collaborator

gatsbybot commented Jul 4, 2018

Deploy preview for gatsbygram ready!

Built with commit d8a8377

https://deploy-preview-6285--gatsbygram.netlify.com

@brotzky
Copy link
Contributor Author

brotzky commented Jul 4, 2018

To extend this, there is the possibility of adding configuration for the naming of the folders instead of hardcoding scripts and styles. I've left it off this PR, but it theory it would be possible to add.

#5270

@brotzky
Copy link
Contributor Author

brotzky commented Jul 5, 2018

Let me know if there's anything else required to get this merged in 👍

Here's the output I'm generating with a local sample project.

image

@brotzky brotzky changed the title WIP: Regroup non-static files Regroup non-static files Jul 5, 2018
@szimek
Copy link
Contributor

szimek commented Jul 6, 2018

While it’s great to clean it up, it will make it probably harder to implement a feature like #5270, which would be really useful in cases where there are multiple apps hosted at the same domain.

fk and others added 6 commits July 6, 2018 11:35
* First shot at fixing #6295

* basically `section.js` becomes `item.js`—we don't expect every top-level sidebar `yaml` item to be "just" a container for a "section" (or group) of subitems anymore
* got rid of the "base directory" setting in `section-list.js` to allow linking to pages not living in the main directory associated with the current sidebar (e.g. `docs` for `doc-links.yaml`)
  * solving the need of being able to put a link to "Tutorial" in the sidebar of "Docs": https://github.com/gatsbyjs/gatsby/blob/ba55b66e169a16bfb3333658ad804b7ef0d3d197/www/src/data/sidebars/doc-links.yaml#L7-L8
  * all links in the sidebar `yaml` files have to be explicit again, e.g. `/docs/` can't be ommitted anymore; that allows the code to be quite a bit simpler

* fix false multiple active items on the "Features" page
* adjust sidebar item layout a bit
* restore `doc-links.yaml` to fit current docs, moved some things for testing
* naming is confusing in a couple of places right now

* Fix component name

* Fix false multiple active items on the "Features" page

* move getActiveItem from item.js to body.js and loop over all items in sidebar (as opposed to run getActiveItem on each item individually)
* rename `subitems` key to `items`

* Styles for miles

* reduce letter spacing for sidebar „headlines“/accordion toggle buttons (for both showcase and doc sidebar)
* darken sidebar horizontal rules a little
* a healthy amount of whitespace fixes for the top-level-item/-section mix
* quick bespoke spacing fixes for the tutorial via `ui:tutorial` in `yaml` (fixes #6193)
* tutorial-style bullets for subitems everywhere
* blockMarginBottom for vertical whitespace

* Rename

* No hover for section title when accordions are disabled

* Rename

* Rename

* Fix "Features" scrollspy

* Restore „Docs“ sidebar
- This line didn't make sense in either English or Spanish.
- Added title to links in ES version.
* Pass apiRunner into loader

* Define apiRunner for tests

* add pageresources into props
Initial tracedSVG support did not include fragments for gatsby-image in 2.0, which uses Fixed and Fluid instead of Resolutions and Sizes. Add support for these new fragments.
`<Link>` components currently break jest tests (#5824), as gatsby-browser-entry.js tries to import pages.json from cache-dir. This fails, because pages.json is created in .cache at build time. This PR adds an empty pages.json to cache-dir.
@brotzky
Copy link
Contributor Author

brotzky commented Jul 6, 2018

@szimek would the ability to name the folder outputs solve your issue? For example, allowing the ability to name the output for all JS and CSS to be /blog-assets? In that case it would simplify the rules you'd have to create.

#6285 (comment)

I want to respect the minimal configurability approach of Gatsby with this.

Copy link
Contributor

@KyleAMathews KyleAMathews left a comment

Choose a reason for hiding this comment

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

I don't see much (any?) value in making these directories configurable. Gatsby doesn't care about catering to idiosyncratic aesthetics for outputted code ;-)

It seems reasonable to move js/css into subdirectories. Can see that it'd simplify the experience for people looking around the outputted code + it'd make it easy to write caching rules for js/css.

@@ -55,6 +55,11 @@ const createElement = React.createElement
export default (pagePath, callback) => {
const pathPrefix = `${__PATH_PREFIX__}/`

// If enabled, group files in their respective folders otherwise generated
// non-static files will be left in the root of the output directory.
const pathToGroupStyles = __GROUP_FILES__ ? `styles/` : ``
Copy link
Contributor

Choose a reason for hiding this comment

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

how about we use js/css instead? Those feel more standard to me + they're shorter (save all the bytes).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@KyleAMathews agreed. The original names were based off variables in the code. +1 for saving all the bytes.

pieh and others added 14 commits July 6, 2018 17:37
* cache page components and static query components states

* Track query addition/removal and text changes between builds

* fix redux-state (de)serialization after changing components reducer to map
* Made gatsby-plugin-netlify-cms work with gatsby v2

* gatsby-plugin-netlify-cms: use mini-css-extract-plugin
 - gatsby@2.0.0-beta.18
 - gatsby-plugin-netlify-cms@2.0.0-beta.4
 - gatsby-source-contentful@2.0.1-beta.9
…e globs (#6316)

* refactor: add criticlal scripts and links to static file globs

* fix incorrect type given to staticFileGlobs
 - gatsby-plugin-offline@2.0.0-beta.3
@@ -57,8 +57,8 @@ export default (pagePath, callback) => {

// If enabled, group files in their respective folders otherwise generated
// non-static files will be left in the root of the output directory.
const pathToGroupStyles = __GROUP_FILES__ ? `styles/` : ``
const pathToGroupScripts = __GROUP_FILES__ ? `scripts/` : ``
const pathToGroupStyles = __GROUP_FILES__ ? `css/` : ``
Copy link
Contributor Author

@brotzky brotzky Jul 6, 2018

Choose a reason for hiding this comment

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

@KyleAMathews renamed to css and js

Here's the output now

screen shot 2018-07-06 at 10 18 33 am

edit: just noticed deploy failed. Going to fix that.

@szimek
Copy link
Contributor

szimek commented Jul 6, 2018

@KyleAMathews @brotzky Here's an example where being able to name output folders is really useful.

We have many frontend apps hosted on a single www.freeletics.com domain. The blog app (actually it's 6 Gatsby apps, each with different pathPrefix, because of the issue we're talking about here) is at www.freeletics.com/:locale/blog/*, there's another app at www.freeletics.com/:locale/targeting/*, another at www.freeletics.com/:locale/press/* and all other paths are handled by yet another, old Angular app. I don't think it's a rare case to have many apps hosted this way, especially in bigger companies.

We've got nginx server in front of it all that decides, based on the path, which app should handle given request. At first, we built our blog app only once for all 6 languages and mounted it at /. While there was no problem with serving HTML files, there was a problem with serving JS files, because Gatsby outputs all JS files directly to public folder, so when a request came for one of Gatsby JS files, like https://www.freeletics.com/app-123456.js, there was no way for us to write proxy rules for it.

We ended up with 6 separate Gatsby apps, each with pathPrefix like /en/blog, /de/blog etc. It causes some issues - it's harder to set it up in development (we build only one language there, it's also faster this way), gatsby serve doesn't work correctly with pathPrefix, so I had to write our own simple dev server, our Dockerfile is much more complicated, we have to clear cache between each build that makes the whole thing slower to build etc.

If I could tell our Gatsby blog app to output all its JS files e.g. to assets-blog-js, so that the request looks like https://www.freeletics.com/assets-blog-js/app-123456.js, then I no longer have problem with writing a proxy rule for it and I could easily host multiple Gatsby and non-Gatsby apps at the same domain.

@KyleAMathews
Copy link
Contributor

@szimek with the perf improvements in v2, wouldn't it be easier to just build that as a single app now?

@szimek
Copy link
Contributor

szimek commented Jul 6, 2018

@KyleAMathews But it doesn't solve the problem with figuring out which app should handle given request for non-html files. Let's say I do build all languages as a single app. Gatsby will still output all JS files to its public folder. When someone visits e.g. https://www.freeletics.com/en/blog/posts/freeletics-getting-you-football-fit/ and it triggers a request to e.g. https://www.freeletics.com/app-123456.js file, how can I know which app (Gatsby or not) should handle it?

With 6 separate builds and using pathPrefix, all non-html files are scoped by pathPrefix, so requests come for https://www.freeletics.com/en/blog/app-123456.js, https://www.freeletics.com/de/blog/app-654321.js etc., so it's easy to figure out which app should handle it (we've got a single nginx server that handles all blog apps/builds).

@KyleAMathews
Copy link
Contributor

There'd only be one app.js for all the pages? I'm not sure I understand.

@szimek
Copy link
Contributor

szimek commented Jul 8, 2018

Hopefully, this clarifies it a bit more:

All our apps are built and deployed independently using Docker and Kubernetes. Even though Gatsby generates unique file names based on the content, I can't put all files from all our apps in a single folder and mount it at /.

We've got nginx proxy in front of all our apps and, based on request path, it proxies the request to a specific app. Thus, I need to be able to write rules for each app based on the path, e.g. proxy all request that match ^/[a-z]{2}/blog/ to the blog app (even though we build it 6 times, each time with different pathPrefix, we treat it as a single app). If Gatsby generates non-html files in root folder or subfolders that are not unique, like https://www.freeletics.com/app-123456.js or https://www.freeletics.com/js/app-123456.js, I can't write such rules and can't figure out which app should handle such request.

@brotzky
Copy link
Contributor Author

brotzky commented Jul 8, 2018

@szimek thanks for the detailed explainations!
To solve this issue would you have to create a unique output folder name for each Gatsby project? My gut tells me there's a different way to do this other than creating a custom output folder name for each project, but it's hard to fully grasp your problem over text without seeing the full architecture.

In my opinion, custom name output is out of scope of this PR and could be submitted in a new PR if it's deemed to be valuable feature for the long term of Gatsby. It would be better to discuss the API for such a feature and the implementation in a new issue/PR.

Sidenote

  • Need confirmation on removing the --group-files flag and making this default
  • Sorry for all the commits, I rebased upstream master onto this branch 😬

@KyleAMathews
Copy link
Contributor

Could you figure out why there's so many changes? It seems your rebasing went wrong some where.

Yes, please remove the flag code. Also agree that your question @szimek isn't very related to this PR so let's move ahead with getting this in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.