Skip to content

Commit

Permalink
Check SSG Page via Route Lookup (vercel#10971)
Browse files Browse the repository at this point in the history
  • Loading branch information
Timer authored and ScriptedAlchemy committed Mar 17, 2020
1 parent f4d3590 commit 32d2e4e
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/next/next-server/server/spr-cache.ts
Expand Up @@ -11,6 +11,10 @@ const mkdirp = promisify(mkdirpOrig)
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)

function toRoute(pathname: string): string {
return pathname.replace(/\/$/, '').replace(/\/index$/, '') || '/'
}

type SprCacheValue = {
html: string
pageData: any
Expand All @@ -34,6 +38,8 @@ const getSeedPath = (pathname: string, ext: string): string => {
}

export const calculateRevalidate = (pathname: string): number | false => {
pathname = toRoute(pathname)

// in development we don't have a prerender-manifest
// and default to always revalidating to allow easier debugging
const curTime = new Date().getTime()
Expand Down
27 changes: 27 additions & 0 deletions test/integration/prerender-no-revalidate/pages/index.js
@@ -0,0 +1,27 @@
let runs = 0

export async function getStaticProps() {
if (runs++) {
throw new Error('GSP was re-run.')
}

return {
props: {
world: 'world',
time: new Date().getTime(),
other: Math.random(),
},
}
}

const Page = ({ world, time, other }) => {
return (
<div>
<p>hello {world}</p>
<span>time: {time}</span>
<span>other: {other}</span>
</div>
)
}

export default Page
27 changes: 27 additions & 0 deletions test/integration/prerender-no-revalidate/pages/named.js
@@ -0,0 +1,27 @@
let runs = 0

export async function getStaticProps() {
if (runs++) {
throw new Error('GSP was re-run.')
}

return {
props: {
world: 'world',
time: new Date().getTime(),
other: Math.random(),
},
}
}

const Page = ({ world, time, other }) => {
return (
<div>
<p>hello {world}</p>
<span>time: {time}</span>
<span>other: {other}</span>
</div>
)
}

export default Page
27 changes: 27 additions & 0 deletions test/integration/prerender-no-revalidate/pages/nested/index.js
@@ -0,0 +1,27 @@
let runs = 0

export async function getStaticProps() {
if (runs++) {
throw new Error('GSP was re-run.')
}

return {
props: {
world: 'world',
time: new Date().getTime(),
other: Math.random(),
},
}
}

const Page = ({ world, time, other }) => {
return (
<div>
<p>hello {world}</p>
<span>time: {time}</span>
<span>other: {other}</span>
</div>
)
}

export default Page
27 changes: 27 additions & 0 deletions test/integration/prerender-no-revalidate/pages/nested/named.js
@@ -0,0 +1,27 @@
let runs = 0

export async function getStaticProps() {
if (runs++) {
throw new Error('GSP was re-run.')
}

return {
props: {
world: 'world',
time: new Date().getTime(),
other: Math.random(),
},
}
}

const Page = ({ world, time, other }) => {
return (
<div>
<p>hello {world}</p>
<span>time: {time}</span>
<span>other: {other}</span>
</div>
)
}

export default Page
129 changes: 129 additions & 0 deletions test/integration/prerender-no-revalidate/test/index.test.js
@@ -0,0 +1,129 @@
/* eslint-env jest */
/* global jasmine */
import fs from 'fs-extra'
import {
findPort,
killApp,
nextBuild,
nextStart,
renderViaHTTP,
waitFor,
} from 'next-test-utils'
import { join } from 'path'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
const appDir = join(__dirname, '..')
const nextConfig = join(appDir, 'next.config.js')
let app
let appPort
let buildId
let stderr

function runTests(route, routePath, serverless) {
it(`[${route}] should not revalidate when set to false`, async () => {
const fileName = join(
appDir,
`.next`,
...(serverless ? ['serverless'] : ['server', 'static', buildId]),
`pages/${routePath}.html`
)
const initialHtml = await renderViaHTTP(appPort, route)
const initialFileHtml = await fs.readFile(fileName, 'utf8')

let newHtml = await renderViaHTTP(appPort, route)
expect(initialHtml).toBe(newHtml)
expect(await fs.readFile(fileName, 'utf8')).toBe(initialFileHtml)

await waitFor(500)

newHtml = await renderViaHTTP(appPort, route)
expect(initialHtml).toBe(newHtml)
expect(await fs.readFile(fileName, 'utf8')).toBe(initialFileHtml)

await waitFor(500)

newHtml = await renderViaHTTP(appPort, route)
expect(initialHtml).toBe(newHtml)
expect(await fs.readFile(fileName, 'utf8')).toBe(initialFileHtml)

expect(stderr).not.toContain('GSP was re-run')
})

it(`[${route}] should not revalidate /_next/data when set to false`, async () => {
const route = join(`/_next/data/${buildId}`, `${routePath}.json`)
const fileName = join(
appDir,
`.next`,
...(serverless ? ['serverless'] : ['server', 'static', buildId]),
`pages/${routePath}.json`
)

const initialData = JSON.parse(await renderViaHTTP(appPort, route))
const initialFileJson = await fs.readFile(fileName, 'utf8')

expect(JSON.parse(await renderViaHTTP(appPort, route))).toEqual(initialData)
expect(await fs.readFile(fileName, 'utf8')).toBe(initialFileJson)
await waitFor(500)

expect(JSON.parse(await renderViaHTTP(appPort, route))).toEqual(initialData)
expect(await fs.readFile(fileName, 'utf8')).toBe(initialFileJson)
await waitFor(500)

expect(JSON.parse(await renderViaHTTP(appPort, route))).toEqual(initialData)
expect(await fs.readFile(fileName, 'utf8')).toBe(initialFileJson)

expect(stderr).not.toContain('GSP was re-run')
})
}

describe('SSG Prerender No Revalidate', () => {
afterAll(() => fs.remove(nextConfig))

describe('serverless mode', () => {
beforeAll(async () => {
await fs.writeFile(
nextConfig,
`module.exports = { target: 'experimental-serverless-trace' }`,
'utf8'
)
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
appPort = await findPort()
stderr = ''
app = await nextStart(appDir, appPort, {
onStderr: msg => {
stderr += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(() => killApp(app))

runTests('/', '/index', true)
runTests('/named', '/named', true)
runTests('/nested', '/nested', true)
runTests('/nested/named', '/nested/named', true)
})

describe('production mode', () => {
beforeAll(async () => {
await fs.remove(nextConfig)
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir, [])
appPort = await findPort()
stderr = ''
app = await nextStart(appDir, appPort, {
onStderr: msg => {
stderr += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})
afterAll(() => killApp(app))

runTests('/', '/index')
runTests('/named', '/named')
runTests('/nested', '/nested')
runTests('/nested/named', '/nested/named')
})
})

0 comments on commit 32d2e4e

Please sign in to comment.