From 687c4fdb981fc657f2040b7e1733291cee20486c Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Thu, 14 Nov 2019 21:13:44 +0100 Subject: [PATCH] fix(client): add cache-busting hashes to chunks (#37746) * fix(client): add cache-busting hashes to chunks * fix: create cache-busting names for the workers Prior to this PR the first webpack compilation gave the workers static names. This can cause caching problems, so this PR adds hashes to their names to invalidate the cache. In order for Gatsby to find them, the names are added to the config directory. --- client/.gitignore | 3 +++ .../Challenges/rechallenge/transformers.js | 6 +++++- client/src/templates/Challenges/utils/build.js | 10 ++++++++-- client/webpack-workers.js | 17 +++++++++++++++-- curriculum/test/test-challenges.js | 4 +++- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/client/.gitignore b/client/.gitignore index e4047a2e1b..cc6a71fd16 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -4,6 +4,9 @@ node_modules yarn-error.log config/env.json +config/frame-runner.json +config/sass-compile.json +config/test-evaluator.json # Build directory /public diff --git a/client/src/templates/Challenges/rechallenge/transformers.js b/client/src/templates/Challenges/rechallenge/transformers.js index de38e7934c..1618c8ba4f 100644 --- a/client/src/templates/Challenges/rechallenge/transformers.js +++ b/client/src/templates/Challenges/rechallenge/transformers.js @@ -18,6 +18,10 @@ import protect from 'loop-protect'; import * as vinyl from '../utils/polyvinyl.js'; import createWorker from '../utils/worker-executor'; +// the config files are created during the build, but not before linting +// eslint-disable-next-line import/no-unresolved +import { filename as sassCompile } from '../../../../config/sass-compile'; + const protectTimeout = 100; Babel.registerPlugin('loopProtection', protect(protectTimeout)); @@ -89,7 +93,7 @@ export const babelTransformer = cond([ [stubTrue, identity] ]); -const sassWorker = createWorker('sass-compile'); +const sassWorker = createWorker(sassCompile); async function transformSASS(element) { const styleTags = element.querySelectorAll('style[type="text/sass"]'); await Promise.all( diff --git a/client/src/templates/Challenges/utils/build.js b/client/src/templates/Challenges/utils/build.js index 46aafdbf37..a33f03823e 100644 --- a/client/src/templates/Challenges/utils/build.js +++ b/client/src/templates/Challenges/utils/build.js @@ -8,9 +8,15 @@ import { createMainFramer } from './frame'; +// the config files are created during the build, but not before linting +// eslint-disable-next-line import/no-unresolved +import { filename as runner } from '../../../../config/frame-runner'; +// eslint-disable-next-line import/no-unresolved +import { filename as testEvaluator } from '../../../../config/test-evaluator'; + const frameRunner = [ { - src: '/js/frame-runner.js' + src: `/js/${runner}.js` } ]; @@ -91,7 +97,7 @@ export function getTestRunner(buildData, proxyLogger, document) { function getJSTestRunner({ build, sources }, proxyLogger) { const code = sources && 'index' in sources ? sources['index'] : ''; - const testWorker = createWorker('test-evaluator', { terminateWorker: true }); + const testWorker = createWorker(testEvaluator, { terminateWorker: true }); return (testString, testTimeout) => { return testWorker diff --git a/client/webpack-workers.js b/client/webpack-workers.js index 442e244779..9822de100d 100644 --- a/client/webpack-workers.js +++ b/client/webpack-workers.js @@ -1,8 +1,11 @@ const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin'); +const { writeFileSync } = require('fs'); module.exports = (env = {}) => { const __DEV__ = env.production !== true; + const staticPath = path.join(__dirname, './static/js'); + const configPath = path.join(__dirname, './config'); return { mode: __DEV__ ? 'development' : 'production', entry: { @@ -13,8 +16,18 @@ module.exports = (env = {}) => { devtool: __DEV__ ? 'inline-source-map' : 'source-map', output: { publicPath: '/js/', - chunkFilename: '[name].js', - path: path.join(__dirname, './static/js') + filename: chunkData => { + // construct and output the filename here, so the client can use the + // json to find the file. + const filename = `${chunkData.chunk.name}.${chunkData.chunk.contentHash.javascript}`; + writeFileSync( + path.join(configPath, `${chunkData.chunk.name}.json`), + `{"filename": "${filename}"}` + ); + return filename + '.js'; + }, + chunkFilename: '[name].[contenthash].js', + path: staticPath }, stats: { // Display bailout reasons diff --git a/curriculum/test/test-challenges.js b/curriculum/test/test-challenges.js index 8b8c1aa19b..888255f812 100644 --- a/curriculum/test/test-challenges.js +++ b/curriculum/test/test-challenges.js @@ -49,6 +49,8 @@ const { createPoly } = require('../../client/src/templates/Challenges/utils/polyvinyl'); +const testEvaluator = require('../../client/config/test-evaluator').filename; + const oldRunnerFail = Mocha.Runner.prototype.fail; Mocha.Runner.prototype.fail = function(test, err) { if (err instanceof AssertionError) { @@ -327,7 +329,7 @@ async function createTestRunnerForJSChallenge({ files }, solution) { const { build, sources } = await buildJSChallenge({ files }); const code = sources && 'index' in sources ? sources['index'] : ''; - const testWorker = createWorker('test-evaluator', { terminateWorker: true }); + const testWorker = createWorker(testEvaluator, { terminateWorker: true }); return async ({ text, testString }) => { try { const { pass, err } = await testWorker.execute(