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

Invalidate cache for link[preload] in DEV (#6183)

Fixes #5860
This commit is contained in:
Giuseppe 2019-02-03 01:12:49 +01:00 committed by Tim Neutkens
parent 230ae52de2
commit 5a4176cffe
3 changed files with 71 additions and 17 deletions

View file

@ -10,7 +10,8 @@ const Fragment = React.Fragment || function Fragment ({ children }) {
export default class Document extends Component {
static childContextTypes = {
_documentProps: PropTypes.any
_documentProps: PropTypes.any,
_devOnlyInvalidateCacheQueryString: PropTypes.string,
}
static getInitialProps ({ renderPage }) {
@ -20,7 +21,13 @@ export default class Document extends Component {
}
getChildContext () {
return { _documentProps: this.props }
return {
_documentProps: this.props,
// In dev we invalidate the cache by appending a timestamp to the resource URL.
// This is a workaround to fix https://github.com/zeit/next.js/issues/5860
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
_devOnlyInvalidateCacheQueryString: process.env.NODE_ENV !== 'production' ? '?ts=' + Date.now() : ''
}
}
render () {
@ -36,7 +43,8 @@ export default class Document extends Component {
export class Head extends Component {
static contextTypes = {
_documentProps: PropTypes.any
_documentProps: PropTypes.any,
_devOnlyInvalidateCacheQueryString: PropTypes.string,
}
static propTypes = {
@ -68,11 +76,13 @@ export class Head extends Component {
getPreloadDynamicChunks () {
const { dynamicImports, assetPrefix } = this.context._documentProps
const { _devOnlyInvalidateCacheQueryString } = this.context
return dynamicImports.map((bundle) => {
return <link
rel='preload'
key={bundle.file}
href={`${assetPrefix}/_next/${bundle.file}`}
href={`${assetPrefix}/_next/${bundle.file}${_devOnlyInvalidateCacheQueryString}`}
as='script'
nonce={this.props.nonce}
crossOrigin={this.props.crossOrigin || process.crossOrigin}
@ -82,9 +92,10 @@ export class Head extends Component {
getPreloadMainLinks () {
const { assetPrefix, files } = this.context._documentProps
if(!files || files.length === 0) {
if (!files || files.length === 0) {
return null
}
const { _devOnlyInvalidateCacheQueryString } = this.context
return files.map((file) => {
// Only render .js files here
@ -96,7 +107,7 @@ export class Head extends Component {
key={file}
nonce={this.props.nonce}
rel='preload'
href={`${assetPrefix}/_next/${file}`}
href={`${assetPrefix}/_next/${file}${_devOnlyInvalidateCacheQueryString}`}
as='script'
crossOrigin={this.props.crossOrigin || process.crossOrigin}
/>
@ -105,6 +116,7 @@ export class Head extends Component {
render () {
const { head, styles, assetPrefix, __NEXT_DATA__ } = this.context._documentProps
const { _devOnlyInvalidateCacheQueryString } = this.context
const { page, buildId } = __NEXT_DATA__
const pagePathname = getPagePathname(page)
@ -119,12 +131,11 @@ export class Head extends Component {
})
if (this.props.crossOrigin) console.warn('Warning: `Head` attribute `crossOrigin` is deprecated. https://err.sh/next.js/doc-crossorigin-deprecated')
}
return <head {...this.props}>
{children}
{head}
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} as='script' nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />}
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} as='script' nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}${_devOnlyInvalidateCacheQueryString}`} as='script' nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />}
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_app.js${_devOnlyInvalidateCacheQueryString}`} as='script' nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />
{this.getPreloadDynamicChunks()}
{this.getPreloadMainLinks()}
{this.getCssLinks()}
@ -135,7 +146,8 @@ export class Head extends Component {
export class Main extends Component {
static contextTypes = {
_documentProps: PropTypes.any
_documentProps: PropTypes.any,
_devOnlyInvalidateCacheQueryString: PropTypes.string,
}
render () {
@ -148,7 +160,8 @@ export class Main extends Component {
export class NextScript extends Component {
static contextTypes = {
_documentProps: PropTypes.any
_documentProps: PropTypes.any,
_devOnlyInvalidateCacheQueryString: PropTypes.string,
}
static propTypes = {
@ -158,11 +171,13 @@ export class NextScript extends Component {
getDynamicChunks () {
const { dynamicImports, assetPrefix } = this.context._documentProps
const { _devOnlyInvalidateCacheQueryString } = this.context
return dynamicImports.map((bundle) => {
return <script
async
key={bundle.file}
src={`${assetPrefix}/_next/${bundle.file}`}
src={`${assetPrefix}/_next/${bundle.file}${_devOnlyInvalidateCacheQueryString}`}
nonce={this.props.nonce}
crossOrigin={this.props.crossOrigin || process.crossOrigin}
/>
@ -171,9 +186,10 @@ export class NextScript extends Component {
getScripts () {
const { assetPrefix, files } = this.context._documentProps
if(!files || files.length === 0) {
if (!files || files.length === 0) {
return null
}
const { _devOnlyInvalidateCacheQueryString } = this.context
return files.map((file) => {
// Only render .js files here
@ -183,7 +199,7 @@ export class NextScript extends Component {
return <script
key={file}
src={`${assetPrefix}/_next/${file}`}
src={`${assetPrefix}/_next/${file}${_devOnlyInvalidateCacheQueryString}`}
nonce={this.props.nonce}
async
crossOrigin={this.props.crossOrigin || process.crossOrigin}
@ -206,6 +222,7 @@ export class NextScript extends Component {
render () {
const { staticMarkup, assetPrefix, devFiles, __NEXT_DATA__ } = this.context._documentProps
const { _devOnlyInvalidateCacheQueryString } = this.context
const { page, buildId } = __NEXT_DATA__
const pagePathname = getPagePathname(page)
@ -214,12 +231,12 @@ export class NextScript extends Component {
}
return <Fragment>
{devFiles ? devFiles.map((file) => <script key={file} src={`${assetPrefix}/_next/${file}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />) : null}
{devFiles ? devFiles.map((file) => <script key={file} src={`${assetPrefix}/_next/${file}${_devOnlyInvalidateCacheQueryString}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />) : null}
{staticMarkup ? null : <script id="__NEXT_DATA__" type="application/json" nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} dangerouslySetInnerHTML={{
__html: NextScript.getInlineScriptSource(this.context._documentProps)
}} />}
{page !== '/_error' && <script async id={`__NEXT_PAGE__${page}`} src={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />}
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />
{page !== '/_error' && <script async id={`__NEXT_PAGE__${page}`} src={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}${_devOnlyInvalidateCacheQueryString}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />}
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/static/${buildId}/pages/_app.js${_devOnlyInvalidateCacheQueryString}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />
{staticMarkup ? null : this.getDynamicChunks()}
{staticMarkup ? null : this.getScripts()}
</Fragment>

View file

@ -67,6 +67,22 @@ export default function ({ app }, suiteName, render, fetch) {
expect($('#render-page-enhance-app').text().includes(nonce)).toBe(true)
expect($('#render-page-enhance-component').text().includes(nonce)).toBe(true)
})
// This is a workaround to fix https://github.com/zeit/next.js/issues/5860
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
test('It adds a timestamp to link tags with preload attribute to invalidate the cache (DEV only)', async () => {
const $ = await get$('/')
$('link[rel=preload]').each((index, element) => {
const href = $(element).attr('href')
expect(href.match(/\?/g)).toHaveLength(1)
expect(href).toMatch(/\?ts=/)
})
$('script[src]').each((index, element) => {
const src = $(element).attr('src')
expect(src.match(/\?/g)).toHaveLength(1)
expect(src).toMatch(/\?ts=/)
})
})
})
describe('_app', () => {

View file

@ -241,6 +241,27 @@ describe('Production Usage', () => {
browser.close()
})
// This is a workaround to fix https://github.com/zeit/next.js/issues/5860
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
it('It does not add a timestamp to link tags with preload attribute', async () => {
const browser = await webdriver(appPort, '/prefetch')
const links = await browser.elementsByCss('link[rel=preload]')
await Promise.all(
links.map(async (element) => {
const href = await element.getAttribute('href')
expect(href).not.toMatch(/\?ts=/)
})
)
const scripts = await browser.elementsByCss('script[src]')
await Promise.all(
scripts.map(async (element) => {
const src = await element.getAttribute('src')
expect(src).not.toMatch(/\?ts=/)
})
)
browser.close()
})
it('should reload the page on page script error with prefetch', async () => {
const browser = await webdriver(appPort, '/counter')
const counter = await browser