diff --git a/packages/next-server/server/render.tsx b/packages/next-server/server/render.tsx index d37a29a523b227954a56e2112b4b74019c926a3f..bf499dfc28227a2ea3035d39aac8f69a67e7ea26 100644 --- a/packages/next-server/server/render.tsx +++ b/packages/next-server/server/render.tsx @@ -115,6 +115,7 @@ type RenderOpts = { err?: Error | null nextExport?: boolean dev?: boolean + ampPath?: string amphtml?: boolean hasAmp?: boolean buildManifest: BuildManifest @@ -140,6 +141,7 @@ function renderDocument( dynamicImportsIds, err, dev, + ampPath, amphtml, hasAmp, staticMarkup, @@ -151,6 +153,7 @@ function renderDocument( docProps: any pathname: string query: ParsedUrlQuery + ampPath: string, amphtml: boolean hasAmp: boolean, dynamicImportsIds: string[] @@ -177,6 +180,7 @@ function renderDocument( err: err ? serializeError(dev, err) : undefined, // Error if one happened, otherwise don't sent in the resulting HTML }} ampEnabled={ampEnabled} + ampPath={ampPath} amphtml={amphtml} hasAmp={hasAmp} staticMarkup={staticMarkup} @@ -205,6 +209,7 @@ export async function renderToHTML( staticMarkup = false, amphtml = false, hasAmp = false, + ampPath = '', App, Document, Component, @@ -314,6 +319,7 @@ export async function renderToHTML( props, docProps, pathname, + ampPath, amphtml, hasAmp, query, diff --git a/packages/next-server/server/require.ts b/packages/next-server/server/require.ts index 01685e381ad9073cecbcdf9f45f0412488367136..061ce5adab9f2ec2658fb19ffc301556f4ed0326 100644 --- a/packages/next-server/server/require.ts +++ b/packages/next-server/server/require.ts @@ -1,4 +1,5 @@ import {join} from 'path' +import { isAmpFile } from './utils' import {PAGES_MANIFEST, SERVER_DIRECTORY} from 'next-server/constants' import { normalizePagePath } from './normalize-page-path' @@ -53,13 +54,12 @@ export function getPagePath(page: string, distDir: string, opts: PagePathOptions export function requirePage(page: string, distDir: string, opts: PagePathOptions = {}): any { const pagePath = getPagePath(page, distDir, opts) - const isAmp = pagePath.indexOf('.amp.') > -1 + const isAmp = isAmpFile(pagePath) let hasAmp = false if (!isAmp) { try { - const ampPage = getPagePath(page, distDir, { amphtml: true }) - hasAmp = Boolean(ampPage && ampPage.indexOf('.amp') > -1) + hasAmp = isAmpFile(getPagePath(page, distDir, { amphtml: true })) } catch (_) {} } opts.amphtml = opts.amphtml || isAmp diff --git a/packages/next-server/server/utils.ts b/packages/next-server/server/utils.ts index a1bdb4fa1b42d0fae5fc5ab4238cc17d4bb54afb..25b6c6097aadb20353497cf41d4b05db55488aab 100644 --- a/packages/next-server/server/utils.ts +++ b/packages/next-server/server/utils.ts @@ -18,3 +18,21 @@ export function isInternalUrl(url: string): boolean { export function isBlockedPage(pathname: string): boolean { return (BLOCKED_PAGES.indexOf(pathname) !== -1) } + +export function cleanAmpPath(pathname: string): string { + return (pathname || '') + .replace(/\.amp$/, '') + .replace(/\index$/, '') +} + +export function isAmpPath(pathname: string): boolean { + return (pathname || '').endsWith('.amp') +} + +export function isAmpFile(pathname: string): boolean { + if (isAmpPath(pathname)) return true + pathname = pathname || '' + const parts = pathname.split('.') + parts.pop() // remove extension + return isAmpPath(parts.join('.')) +} diff --git a/packages/next/export/index.js b/packages/next/export/index.js index 797b4ab4b29b7848c45e0a98f735534503b083c1..623eadeec8cc6661c88b81e881859e780124aa6f 100644 --- a/packages/next/export/index.js +++ b/packages/next/export/index.js @@ -5,6 +5,7 @@ import mkdirpModule from 'mkdirp' import { resolve, join } from 'path' import { existsSync, readFileSync } from 'fs' import loadConfig from 'next-server/next-config' +import { tryAmp } from 'next-server/dist/server/require' import { PHASE_EXPORT, SERVER_DIRECTORY, PAGES_MANIFEST, CONFIG_FILE, BUILD_ID_FILE, CLIENT_STATIC_FILES_PATH } from 'next-server/constants' import createProgress from 'tty-aware-progress' import { promisify } from 'util' @@ -52,6 +53,22 @@ export default async function (dir, options, configuration) { defaultPathMap[page] = { page } } + Object.keys(defaultPathMap).forEach(path => { + const isAmp = path.indexOf('.amp') > -1 + + if (isAmp) { + defaultPathMap[path].query = { amp: 1 } + if (!defaultPathMap[path.split('.amp')[0]]) { + defaultPathMap[path].query.ampOnly = true + } + } else { + const ampPath = tryAmp(defaultPathMap, path) + if (ampPath !== path) { + defaultPathMap[path].query = { hasAmp: true, ampPath } + } + } + }) + // Initialize the output directory const outDir = options.outdir await recursiveDelete(join(outDir)) @@ -93,7 +110,8 @@ export default async function (dir, options, configuration) { distDir, dev: false, staticMarkup: false, - hotReloader: null + hotReloader: null, + ampEnabled: nextConfig.experimental.amp } const { serverRuntimeConfig, publicRuntimeConfig } = nextConfig diff --git a/packages/next/export/worker.js b/packages/next/export/worker.js index 0f260ab6882df7918e2f2e8a2e46178dbd379c8e..463cb0edfa22a0c15b3dd134ffd9d47dcb9b591e 100644 --- a/packages/next/export/worker.js +++ b/packages/next/export/worker.js @@ -1,5 +1,6 @@ import mkdirpModule from 'mkdirp' import { promisify } from 'util' +import { cleanAmpPath } from 'next/dist/server/lib/utils' import { extname, join, dirname, sep } from 'path' import { renderToHTML } from 'next-server/dist/server/render' import { writeFile } from 'fs' @@ -30,6 +31,10 @@ process.on( const work = async path => { await sema.acquire() const { page, query = {} } = exportPathMap[path] + const ampOpts = { amphtml: Boolean(query.amp), hasAmp: query.hasAmp, ampPath: query.ampPath } + delete query.hasAmp + delete query.ampPath + const req = { url: path } const res = {} envConfig.setConfig({ @@ -37,6 +42,11 @@ process.on( publicRuntimeConfig: renderOpts.runtimeConfig }) + if (query.ampOnly) { + delete query.ampOnly + path = cleanAmpPath(path) + } + let htmlFilename = `${path}${sep}index.html` const pageExt = extname(page) const pathExt = extname(path) @@ -53,7 +63,7 @@ process.on( await mkdirp(baseDir) const components = await loadComponents(distDir, buildId, page) - const html = await renderToHTML(req, res, page, query, { ...components, ...renderOpts }) + const html = await renderToHTML(req, res, page, query, { ...components, ...renderOpts, ...ampOpts }) await new Promise((resolve, reject) => writeFile( htmlFilepath, diff --git a/packages/next/pages/_document.js b/packages/next/pages/_document.js index c087ae3923c4815c129817fe046634a56c3c4f85..5a5482ba899ce4e40965e1b5a7a04c96cbeb3693 100644 --- a/packages/next/pages/_document.js +++ b/packages/next/pages/_document.js @@ -1,6 +1,7 @@ /* eslint-disable */ import React, { Component } from 'react' import PropTypes from 'prop-types' +import { cleanAmpPath } from 'next-server/dist/server/utils' import { htmlEscapeJsonString } from '../server/htmlescape' import flush from 'styled-jsx/server' import { @@ -151,6 +152,7 @@ export class Head extends Component { styles, amphtml, hasAmp, + ampPath, assetPrefix, __NEXT_DATA__, } = this.context._documentProps @@ -202,7 +204,7 @@ export class Head extends Component { name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" /> - + {/* https://www.ampproject.org/docs/fundamentals/optimize_amp#optimize-the-amp-runtime-loading */} - {ampEnabled && hasAmp && } + {ampEnabled && hasAmp && } {page !== '/_error' && (