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:
parent
3c95f21d8c
commit
9121a9d22e
|
@ -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
|
||||||
|
|
|
@ -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}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
Loading…
Reference in a new issue