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

FEATURE: Encourage caching by giving each build of bundle.js a unique name #27

Closed
KyleAMathews opened this issue Sep 4, 2015 · 18 comments
Labels
stale? Issue that may be closed soon due to the original author not responding any more.

Comments

@KyleAMathews
Copy link
Contributor

Currently someone can visit a new page and if they've cached the old bundle.js in their browser, after loading that, the new page will go blank as the old bundle.js doesn't know yet about it.

@gesposito
Copy link
Contributor

@jeberly
Copy link

jeberly commented Nov 11, 2015

Is there a timeline on this? Or some hints on what would have to be done to make this work? Thanks!

@KyleAMathews
Copy link
Contributor Author

No one is working or has signaled plans to work on this yet afaik. You're
welcome of course to research a solution and submit a PR :-)
On Wed, Nov 11, 2015 at 1:00 PM John Eberly notifications@github.com
wrote:

Is there a timeline on this? Or some hints on what would have to be done
to make this work? Thanks!


Reply to this email directly or view it on GitHub
#27 (comment).

@jeberly
Copy link

jeberly commented Nov 12, 2015

@KyleAMathews thanks ;) I assume we would want this to be a configurable option? Or always on?

@KyleAMathews
Copy link
Contributor Author

My gut reaction is default on but configurable.
On Wed, Nov 11, 2015 at 6:00 PM John Eberly notifications@github.com
wrote:

@KyleAMathews https://github.com/KyleAMathews thanks ;) I assume we
would want this to be a configurable option? Or always on?


Reply to this email directly or view it on GitHub
#27 (comment).

@KyleAMathews KyleAMathews changed the title Break caching by giving each build of bundle.js a unique name Encourage caching by giving each build of bundle.js a unique name Dec 28, 2015
@KyleAMathews
Copy link
Contributor Author

One tricky thing about using Webpack's default hash is we can't determine the name until after it's built meaning we wouldn't easily be able pass the bundle.js file path to the HTML template. Which means we should just generate our own unique bundle name(s). Perhaps the easiest way to do this is to use the timestamp when gatsby build starts.

@wyattanderson
Copy link

One tricky thing about using Webpack's default hash is we can't determine the name until after it's built meaning we wouldn't easily be able pass the bundle.js file path to the HTML template.

Is it crazy to suggest using some sort of template string as the path to the bundle file then doing a pass after the build to replace the template string with the generated filename?

@KyleAMathews
Copy link
Contributor Author

@wyattanderson not crazy :) just mildly tricky. Would be a great PR if you want to work on it. Just added styles.css output on prod builds so we're ready for this.

@KyleAMathews KyleAMathews changed the title Encourage caching by giving each build of bundle.js a unique name FEATURE: Encourage caching by giving each build of bundle.js a unique name Feb 26, 2016
@scottnonnenberg
Copy link
Contributor

This was my solution, from my top-level html.js. First, outside the React component:

const now = new Date();
const buster = now.getTime();

Then in render():

<script src={link('/bundle.js?t=' + buster)} />;

So every build you get one new cache buster value based on the current time.

@qixotic
Copy link

qixotic commented May 3, 2016

webpack/webpack#86 has some related discussion. It looks like if we change webpack-config.js production to output filename: '[chunkhash].bundle.js',, one could do something like the following in a gatsby-node.js:

const Path = require('path');
const FileSystem = require('fs');

function RevisionPlugin() {}
RevisionPlugin.prototype.apply = function(compiler) {
  compiler.plugin('done', (statsData) => {
    const stats = statsData.toJson();
    // FileSystem.writeFileSync(Path.join(__dirname, 'stats.json'), JSON.stringify(stats));

    if (!stats.errors.length) {
      const htmlFileName = 'index.html';
      const outfile = Path.join(__dirname, 'public', htmlFileName);
      const html = FileSystem.readFileSync(outfile, 'utf8');

      const htmlOutput = html.replace(
        /<script\s+src=(["'])(.*?)bundle\.js\1/i,
        `<script src=$1$2${stats.assetsByChunkName.main[0]}$1`
      );

      const tempOutfile = Path.join(__dirname, '.tmp', htmlFileName);
      FileSystem.writeFileSync(tempOutfile, htmlOutput);
      FileSystem.renameSync(tempOutfile, outfile);
    }
  });
};

exports.modifyWebpackConfig = function(config, env) {
  if (env === 'production') {
    config.plugin('done plugin', RevisionPlugin);
  }
  return config;
}

The regex could be a user option if it needs to be more complex.

@benstepp
Copy link
Contributor

benstepp commented May 3, 2016

A done plugin stage which opens up every page, regexes a file for a bundle tag, replaces the tag with the contenthash, and rewrites the file back to the disk (with or without a tmp file) is going to have a lot of I/O. We should try and look for a solution which writes the html files correctly the first time.


Here's my idea for getting this in. It will be a breaking change (as bundle.js will no longer exist and most people right now are doing <script src={prefixLink('bundle.js')}) />.

Create the javascript and css prior to building the pages (#253 has a pretty good start on this). We would just change the order.

Pass the compiled assets information as an argument to the static site generation stage (maybe using DefinePlugin as __GATSBY_ASSETS__).

Change the userland request of the bundle.js to something like the following. These helper components will just rip the asset information out of the defineplugin during build and just a standard bundle tag in development.

import { GatsbyJs, GatsbyCss } from 'gatsby-helpers'
// defaults to bundle.js
<GatsbyJs />

// get a different entry point
<GatsbyJs entry='custom' />

Maybe rename the default bundle.js and styles.css to gatsby-[chunkhash].js and gatsby-[contenthash].css as subtle marketing while everything is being broken.

@KyleAMathews
Copy link
Contributor Author

I agree avoiding rewriting every page would be much much better. And no need to fear breakage — that's why https://github.com/gatsbyjs/gatsby#warning is there 😝

Also to support code splitting, we'd need the ability to track and pass to different pages which additional JS chunks they should load.

@benstepp
Copy link
Contributor

benstepp commented May 3, 2016

I didn't really think about the multiple chunks. For entry points with multiple chunks we would probably have to aim for a more react-helmet like API and return arrays of components, rather than a single component.

import { assets } from 'gatsby-helpers'
// defaults 
{assets.js}
{assets.css}

// if the user wants to grab a specific chunk
{assets.byChunk('chunkname')}
{assets.byChunk('chunkname', 'js')}
{assets.byChunk('chunkname', 'css')}

// if the user wants to get it all
{assets.raw}

In order to figure out which chunks to load based on the current path, we can use the fact that node-modules are singletons and register the current path into the gatsby-helpers node module in the static-site-generator-plugin render. Then when the call stack reaches the html.js and the user calls for the assets, it will know that the user is on a specific page.

@KyleAMathews
Copy link
Contributor Author

Yeah, that sounds like a good plan.

@KyleAMathews
Copy link
Contributor Author

Until we can get hashed bundle names in, I added a cache buster per @scottnonnenberg's suggestion gatsbyjs/gatsby-starter-default@c1eee90

@KyleAMathews
Copy link
Contributor Author

(to the starters)

@jbolda jbolda added the stale? Issue that may be closed soon due to the original author not responding any more. label Sep 19, 2017
@stale
Copy link

stale bot commented Oct 22, 2017

This issue has been automatically closed due to inactivity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale? Issue that may be closed soon due to the original author not responding any more.
Projects
None yet
Development

No branches or pull requests

8 participants