Skip to content

Commit

Permalink
enhancement/issue 629 cache unchanged assets in development (#760)
Browse files Browse the repository at this point in the history
* cache import map

* etag responses

* clean up

* clean up console logging

* cleanup console and guard against external calls and API call types requests

* update for ESM

* reuse hashing utils lib

* only apply during development

* add no-cache headers for development assets

* comment

* add a note about E-Tags and caching to how it works section
  • Loading branch information
thescientist13 authored Mar 5, 2022
1 parent 187e12f commit 18cff9a
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 17 deletions.
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"acorn-walk": "^8.0.0",
"commander": "^2.20.0",
"cssnano": "^5.0.11",
"es-module-shims": "^0.5.2",
"es-module-shims": "^1.2.0",
"front-matter": "^4.0.2",
"koa": "^2.13.0",
"livereload": "^0.9.1",
Expand Down
14 changes: 14 additions & 0 deletions packages/cli/src/lib/hashing-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0#gistcomment-2775538
function hashString(inputString) {
let h = 0;

for (let i = 0; i < inputString.length; i += 1) {
h = Math.imul(31, h) + inputString.charCodeAt(i) | 0; // eslint-disable-line no-bitwise
}

return Math.abs(h).toString();
}

export {
hashString
};
33 changes: 32 additions & 1 deletion packages/cli/src/lifecycles/serve.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BrowserRunner } from '../lib/browser.js';
import fs from 'fs';
import { hashString } from '../lib/hashing-utils.js';
import path from 'path';
import Koa from 'koa';
import { ResourceInterface } from '../lib/resource-interface.js';
Expand Down Expand Up @@ -88,7 +89,7 @@ async function getDevServer(compilation) {
});

// allow intercepting of urls (response)
app.use(async (ctx) => {
app.use(async (ctx, next) => {
const responseAccumulator = {
body: ctx.body,
contentType: ctx.response.headers['content-type']
Expand Down Expand Up @@ -120,6 +121,36 @@ async function getDevServer(compilation) {

ctx.set('Content-Type', reducedResponse.contentType);
ctx.body = reducedResponse.body;

await next();
});

// ETag Support - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
// https://stackoverflow.com/questions/43659756/chrome-ignores-the-etag-header-and-just-uses-the-in-memory-cache-disk-cache
app.use(async (ctx) => {
const body = ctx.response.body;
const { url } = ctx;

// don't interfere with external requests or API calls
// and only run in development
if (process.env.__GWD_COMMAND__ === 'develop' && path.extname(url) !== '' && url.indexOf('http') !== 0) { // eslint-disable-line no-underscore-dangle
if (Buffer.isBuffer(body)) {
// console.warn(`no body for => ${ctx.url}`);
} else {
const inm = ctx.headers['if-none-match'];
const etagHash = hashString(body);

if (inm && inm === etagHash) {
ctx.status = 304;
ctx.body = null;
ctx.set('Etag', etagHash);
ctx.set('Cache-Control', 'no-cache');
} else if (!inm || inm !== etagHash) {
ctx.set('Etag', etagHash);
}
}
}

});

return Promise.resolve(app);
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/plugins/resource/plugin-node-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,9 @@ class NodeModulesResource extends ResourceInterface {
// for each entry found in dependencies, find its entry point
// then walk its entry point (e.g. index.js) for imports / exports to add to the importMap
// and then walk its package.json for transitive dependencies and all those import / exports
await walkPackageJson(userPackageJson);
if (Object.keys(importMap).length === 0) {
await walkPackageJson(userPackageJson);
}

// apply import map and shim for users
newContents = newContents.replace('<head>', `
Expand Down
11 changes: 1 addition & 10 deletions packages/plugin-graphql/src/core/common.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
// https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0#gistcomment-2775538
function hashString(queryKeysString) {
let h = 0;

for (let i = 0; i < queryKeysString.length; i += 1) {
h = Math.imul(31, h) + queryKeysString.charCodeAt(i) | 0; // eslint-disable-line no-bitwise
}

return Math.abs(h).toString();
}
import { hashString } from '@greenwood/cli/src/lib/hashing-utils.js';

function getQueryHash(query, variables = {}) {
const queryKeys = query;
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-graphql/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class GraphQLResource extends ResourceInterface {
: ',';
const shimmedBody = body.replace('"imports": {', `
"imports": {
"@greenwood/cli/src/lib/hashing-utils.js": "/node_modules/@greenwood/cli/src/lib/hashing-utils.js",
"@greenwood/plugin-graphql/core/client": "/node_modules/@greenwood/plugin-graphql/src/core/client.js",
"@greenwood/plugin-graphql/core/common": "/node_modules/@greenwood/plugin-graphql/src/core/common.js",
"@greenwood/plugin-graphql/queries/children": "/node_modules/@greenwood/plugin-graphql/src/queries/children.gql",
Expand Down
1 change: 1 addition & 0 deletions www/pages/about/how-it-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ During _development_ the CLI will:
- Process requests on the fly only for the content or code you need for a given page.
- Supports loading dependencies from _node_modules_ using an [`importMap`](https://github.com/WICG/import-maps) to avoid bundling.
- While Greenwood is ESM first, we have a [plugin](/plugins/custom-plugins/) to transform CommonJS into ESM (🤞)
- Leverage `E-Tag` headers to apply [caching techniques for unchanged assets](/blog/release/v0-24-0/#local-development-enhancements)

For _production_ builds:
- Combine all your code and dependencies into efficient modern bundles including minifying your JavaScript and CSS.
Expand Down
10 changes: 6 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5173,10 +5173,12 @@ es-abstract@^1.18.0-next.2:
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.0"

es-module-shims@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/es-module-shims/-/es-module-shims-0.5.2.tgz#9bea003e84a11bdc052b9f52464671176509f520"
integrity sha512-cXSy7EPyZc6Iq4AjyWXMqMURXgFguduPN7nxtfI0WsV4C83wNHrdxf0oxhzDPZU/8zB6YFllR24HMG/EBVN/GQ==
es-module-shims@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/es-module-shims/-/es-module-shims-1.2.0.tgz#f3b447826c4f4b9d9597b24a7aaa0c639893de2f"
integrity sha512-Kupc9HFwmScot1v8vO/3CX9MjNprarG4wdlBk2bv5R76SD63UBhXG53MUTjg8USgHaCMLBOQyGSl7lAEqGUb1g==
dependencies:
rimraf "^3.0.2"

es-to-primitive@^1.2.1:
version "1.2.1"
Expand Down

0 comments on commit 18cff9a

Please sign in to comment.