1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00

Never cache assets and HTML in the dev mode. (#2045)

* Never cache assets and HTML in the dev mode.

* Move etags test to the production.

Now it won't work in dev because of no-cache settings.
This commit is contained in:
Arunoda Susiripala 2017-05-25 21:58:08 +05:30 committed by Tim Neutkens
parent 3c95f21d8c
commit 9121a9d22e
5 changed files with 32 additions and 20 deletions

View file

@ -36,8 +36,8 @@ export class Head extends Component {
getChunkPreloadLink (filename) { getChunkPreloadLink (filename) {
const { __NEXT_DATA__ } = this.context._documentProps const { __NEXT_DATA__ } = this.context._documentProps
let { buildStats, assetPrefix } = __NEXT_DATA__ let { buildStats, assetPrefix, buildId } = __NEXT_DATA__
const hash = buildStats ? buildStats[filename].hash : '-' const hash = buildStats ? buildStats[filename].hash : buildId
return ( return (
<link <link
@ -103,8 +103,8 @@ export class NextScript extends Component {
getChunkScript (filename, additionalProps = {}) { getChunkScript (filename, additionalProps = {}) {
const { __NEXT_DATA__ } = this.context._documentProps const { __NEXT_DATA__ } = this.context._documentProps
let { buildStats, assetPrefix } = __NEXT_DATA__ let { buildStats, assetPrefix, buildId } = __NEXT_DATA__
const hash = buildStats ? buildStats[filename].hash : '-' const hash = buildStats ? buildStats[filename].hash : buildId
return ( return (
<script <script

View file

@ -233,7 +233,7 @@ export default class Server {
res.setHeader('X-Powered-By', `Next.js ${pkg.version}`) res.setHeader('X-Powered-By', `Next.js ${pkg.version}`)
} }
const html = await this.renderToHTML(req, res, pathname, query) const html = await this.renderToHTML(req, res, pathname, query)
return sendHTML(req, res, html, req.method) return sendHTML(req, res, html, req.method, this.renderOpts)
} }
async renderToHTML (req, res, pathname, query) { async renderToHTML (req, res, pathname, query) {
@ -261,7 +261,7 @@ export default class Server {
async renderError (err, req, res, pathname, query) { async renderError (err, req, res, pathname, query) {
const html = await this.renderErrorToHTML(err, req, res, pathname, query) const html = await this.renderErrorToHTML(err, req, res, pathname, query)
return sendHTML(req, res, html, req.method) return sendHTML(req, res, html, req.method, this.renderOpts)
} }
async renderErrorToHTML (err, req, res, pathname, query) { async renderErrorToHTML (err, req, res, pathname, query) {
@ -343,6 +343,7 @@ export default class Server {
handleBuildHash (filename, hash, res) { handleBuildHash (filename, hash, res) {
if (this.dev) return if (this.dev) return
if (hash !== this.buildStats[filename].hash) { if (hash !== this.buildStats[filename].hash) {
throw new Error(`Invalid Build File Hash(${hash}) for chunk: ${filename}`) throw new Error(`Invalid Build File Hash(${hash}) for chunk: ${filename}`)
} }

View file

@ -15,7 +15,7 @@ import ErrorDebug from '../lib/error-debug'
export async function render (req, res, pathname, query, opts) { export async function render (req, res, pathname, query, opts) {
const html = await renderToHTML(req, res, pathname, opts) const html = await renderToHTML(req, res, pathname, opts)
sendHTML(req, res, html, req.method) sendHTML(req, res, html, req.method, opts)
} }
export function renderToHTML (req, res, pathname, query, opts) { export function renderToHTML (req, res, pathname, query, opts) {
@ -24,7 +24,7 @@ export function renderToHTML (req, res, pathname, query, opts) {
export async function renderError (err, req, res, pathname, query, opts) { export async function renderError (err, req, res, pathname, query, opts) {
const html = await renderErrorToHTML(err, req, res, query, opts) const html = await renderErrorToHTML(err, req, res, query, opts)
sendHTML(req, res, html, req.method) sendHTML(req, res, html, req.method, opts)
} }
export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) { export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) {
@ -87,6 +87,11 @@ async function doRender (req, res, pathname, query, {
} }
const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage }) const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })
// While developing, we should not cache any assets.
// So, we use a different buildId for each page load.
// With that we can ensure, we have unique URL for assets per every page load.
// So, it'll prevent issues like this: https://git.io/vHLtb
const devBuildId = Date.now()
if (res.finished) return if (res.finished) return
@ -96,7 +101,7 @@ async function doRender (req, res, pathname, query, {
props, props,
pathname, pathname,
query, query,
buildId, buildId: dev ? devBuildId : buildId,
buildStats, buildStats,
assetPrefix, assetPrefix,
err: (err) ? serializeError(dev, err) : null err: (err) ? serializeError(dev, err) : null
@ -156,7 +161,7 @@ export async function renderScriptError (req, res, page, error, customFields, op
`) `)
} }
export function sendHTML (req, res, html, method) { export function sendHTML (req, res, html, method, { dev }) {
if (res.finished) return if (res.finished) return
const etag = generateETag(html) const etag = generateETag(html)
@ -166,6 +171,12 @@ export function sendHTML (req, res, html, method) {
return return
} }
if (dev) {
// In dev, we should not cache pages for any reason.
// That's why we do this.
res.setHeader('Cache-Control', 'no-store, must-revalidate')
}
res.setHeader('ETag', etag) res.setHeader('ETag', etag)
res.setHeader('Content-Type', 'text/html') res.setHeader('Content-Type', 'text/html')
res.setHeader('Content-Length', Buffer.byteLength(html)) res.setHeader('Content-Length', Buffer.byteLength(html))

View file

@ -1,5 +1,4 @@
/* global describe, test, expect */ /* global describe, test, expect */
import fetch from 'node-fetch'
export default function (context) { export default function (context) {
describe('Misc', () => { describe('Misc', () => {
@ -13,14 +12,5 @@ export default function (context) {
const html = await context.app.renderToHTML({}, res, '/finish-response', {}) const html = await context.app.renderToHTML({}, res, '/finish-response', {})
expect(html).toBeFalsy() expect(html).toBeFalsy()
}) })
test('allow etag header support', async () => {
const url = `http://localhost:${context.appPort}/stateless`
const etag = (await fetch(url)).headers.get('ETag')
const headers = { 'If-None-Match': etag }
const res2 = await fetch(url, { headers })
expect(res2.status).toBe(304)
})
}) })
} }

View file

@ -9,6 +9,7 @@ import {
renderViaHTTP renderViaHTTP
} from 'next-test-utils' } from 'next-test-utils'
import webdriver from 'next-webdriver' import webdriver from 'next-webdriver'
import fetch from 'node-fetch'
const appDir = join(__dirname, '../') const appDir = join(__dirname, '../')
let appPort let appPort
@ -35,6 +36,15 @@ describe('Production Usage', () => {
const html = await renderViaHTTP(appPort, '/') const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/Hello World/) expect(html).toMatch(/Hello World/)
}) })
it('should allow etag header support', async () => {
const url = `http://localhost:${appPort}/`
const etag = (await fetch(url)).headers.get('ETag')
const headers = { 'If-None-Match': etag }
const res2 = await fetch(url, { headers })
expect(res2.status).toBe(304)
})
}) })
describe('With navigation', () => { describe('With navigation', () => {