diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index ed2f249bbdb336..ef6e2302d8fc92 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -838,8 +838,12 @@ export default async function build(dir: string, conf = null): Promise { await moveExportedPage(page, file, isSsg, 'html') } - if (hasAmp) { + if (hasAmp && (!isSsg || (isSsg && !isDynamic))) { await moveExportedPage(`${page}.amp`, `${file}.amp`, isSsg, 'html') + + if (isSsg) { + await moveExportedPage(`${page}.amp`, `${file}.amp`, isSsg, 'json') + } } if (isSsg) { @@ -862,6 +866,22 @@ export default async function build(dir: string, conf = null): Promise { for (const route of extraRoutes) { await moveExportedPage(route, route, true, 'html') await moveExportedPage(route, route, true, 'json') + + if (hasAmp) { + await moveExportedPage( + `${route}.amp`, + `${route}.amp`, + true, + 'html' + ) + await moveExportedPage( + `${route}.amp`, + `${route}.amp`, + true, + 'json' + ) + } + finalPrerenderRoutes[route] = { initialRevalidateSeconds: exportConfig.initialPageRevalidationMap[route], diff --git a/packages/next/export/worker.js b/packages/next/export/worker.js index ada50652170b44..34dd89efedde78 100644 --- a/packages/next/export/worker.js +++ b/packages/next/export/worker.js @@ -269,6 +269,14 @@ export default async function ({ JSON.stringify(curRenderOpts.pageData), 'utf8' ) + + if (curRenderOpts.hybridAmp) { + await promises.writeFile( + dataFile.replace(/\.json$/, '.amp.json'), + JSON.stringify(curRenderOpts.pageData), + 'utf8' + ) + } } results.fromBuildExportRevalidate = curRenderOpts.revalidate diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 7ce91183e2ab4a..51b79a6f6dcba0 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -1025,9 +1025,7 @@ export default class Server { } // Compute the iSSG cache key - let urlPathname = `${parseUrl(req.url || '').pathname!}${ - query.amp ? '.amp' : '' - }` + let urlPathname = `${parseUrl(req.url || '').pathname!}` // remove /_next/data prefix from urlPathname so it matches // for direct page visit and /_next/data visit @@ -1039,10 +1037,11 @@ export default class Server { const ssgCacheKey = isPreviewMode ? undefined // Preview mode bypasses the cache - : urlPathname + : `${urlPathname}${query.amp ? '.amp' : ''}` // Complete the response with cached data if its present const cachedData = ssgCacheKey ? await getSprCache(ssgCacheKey) : undefined + if (cachedData) { const data = isDataReq ? JSON.stringify(cachedData.pageData) diff --git a/test/integration/amphtml-ssg/pages/blog/[slug].js b/test/integration/amphtml-ssg/pages/blog/[slug].js new file mode 100644 index 00000000000000..38f28cd7f387b1 --- /dev/null +++ b/test/integration/amphtml-ssg/pages/blog/[slug].js @@ -0,0 +1,27 @@ +import { useAmp } from 'next/amp' + +export const config = { + amp: 'hybrid', +} + +export const getStaticProps = () => { + return { + props: { + hello: 'hello', + random: Math.random(), + }, + } +} + +export const getStaticPaths = () => ({ + paths: ['/blog/post-1', '/blog/post-2'], + fallback: false, +}) + +export default ({ hello, random }) => ( + <> +

useAmp: {useAmp() ? 'yes' : 'no'}

+

{hello}

+

{random}

+ +) diff --git a/test/integration/amphtml-ssg/test/index.test.js b/test/integration/amphtml-ssg/test/index.test.js index 0e1f6c401083e2..8fae756665f500 100644 --- a/test/integration/amphtml-ssg/test/index.test.js +++ b/test/integration/amphtml-ssg/test/index.test.js @@ -46,6 +46,20 @@ const runTests = (isDev = false) => { expect($('#hello').text()).toContain('hello') }) + it('should load dynamic hybrid SSG/AMP page', async () => { + const html = await renderViaHTTP(appPort, '/blog/post-1') + const $ = cheerio.load(html) + expect($('#use-amp').text()).toContain('no') + expect($('#hello').text()).toContain('hello') + }) + + it('should load dynamic hybrid SSG/AMP page with query', async () => { + const html = await renderViaHTTP(appPort, '/blog/post-1?amp=1') + const $ = cheerio.load(html) + expect($('#use-amp').text()).toContain('yes') + expect($('#hello').text()).toContain('hello') + }) + it('should load a hybrid amp page with query correctly', async () => { const html = await renderViaHTTP(appPort, '/hybrid?amp=1') @@ -139,6 +153,8 @@ describe('AMP SSG Support', () => { expect(await fsExists(outFile('hybrid.html'))).toBe(true) expect(await fsExists(outFile('amp.amp.html'))).toBe(false) expect(await fsExists(outFile('hybrid.amp.html'))).toBe(true) + expect(await fsExists(outFile('blog/post-1.html'))).toBe(true) + expect(await fsExists(outFile('blog/post-1.amp.html'))).toBe(true) expect( await fsExists(outFile(join('_next/data', buildId, 'amp.json')))