Add custom amp optimizer and skip validation mode (#10705)
* Add custom amp optimizer and skip validation mode * fix: type * fix worker lint errors
This commit is contained in:
parent
1690a756e2
commit
aadb31fa5b
10 changed files with 116 additions and 7 deletions
|
@ -234,6 +234,8 @@ export default async function(
|
|||
canonicalBase: nextConfig.amp?.canonicalBase || '',
|
||||
isModern: nextConfig.experimental.modern,
|
||||
ampValidatorPath: nextConfig.experimental.amp?.validator || undefined,
|
||||
ampSkipValidation: nextConfig.experimental.amp?.skipValidation || false,
|
||||
ampOptimizerConfig: nextConfig.experimental.amp?.optimizer || undefined,
|
||||
}
|
||||
|
||||
const { serverRuntimeConfig, publicRuntimeConfig } = nextConfig
|
||||
|
|
|
@ -223,7 +223,7 @@ export default async function({
|
|||
}
|
||||
}
|
||||
|
||||
if (curRenderOpts.inAmpMode) {
|
||||
if (curRenderOpts.inAmpMode && !curRenderOpts.ampSkipValidation) {
|
||||
await validateAmp(html, path, curRenderOpts.ampValidatorPath)
|
||||
} else if (curRenderOpts.hybridAmp) {
|
||||
// we need to render the AMP version
|
||||
|
@ -252,7 +252,9 @@ export default async function({
|
|||
)
|
||||
}
|
||||
|
||||
await validateAmp(ampHtml, page + '?amp=1')
|
||||
if (!curRenderOpts.ampSkipValidation) {
|
||||
await validateAmp(ampHtml, page + '?amp=1')
|
||||
}
|
||||
await mkdir(ampBaseDir, { recursive: true })
|
||||
await writeFileP(ampHtmlFilepath, ampHtml, 'utf8')
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ export default class Server {
|
|||
dev?: boolean
|
||||
previewProps: __ApiPreviewProps
|
||||
customServer?: boolean
|
||||
ampOptimizerConfig?: { [key: string]: any }
|
||||
}
|
||||
private compression?: Middleware
|
||||
private onErrorMiddleware?: ({ err }: { err: Error }) => Promise<void>
|
||||
|
@ -172,6 +173,7 @@ export default class Server {
|
|||
generateEtags,
|
||||
previewProps: this.getPreviewProps(),
|
||||
customServer: customServer === true ? true : undefined,
|
||||
ampOptimizerConfig: this.nextConfig.experimental.amp?.optimizer,
|
||||
}
|
||||
|
||||
// Only the `publicRuntimeConfig` key is exposed to the client side
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
export default async function optimize(html: string): Promise<string> {
|
||||
export default async function optimize(
|
||||
html: string,
|
||||
config: any
|
||||
): Promise<string> {
|
||||
let AmpOptimizer
|
||||
try {
|
||||
AmpOptimizer = require('@ampproject/toolbox-optimizer')
|
||||
} catch (_) {
|
||||
return html
|
||||
}
|
||||
const optimizer = AmpOptimizer.create()
|
||||
return optimizer.transformHtml(html)
|
||||
const optimizer = AmpOptimizer.create(config)
|
||||
return optimizer.transformHtml(html, config)
|
||||
}
|
||||
|
|
|
@ -146,6 +146,8 @@ export type RenderOptsPartial = {
|
|||
hybridAmp?: boolean
|
||||
ErrorDebug?: React.ComponentType<{ error: Error }>
|
||||
ampValidator?: (html: string, pathname: string) => Promise<void>
|
||||
ampSkipValidation?: boolean
|
||||
ampOptimizerConfig?: { [key: string]: any }
|
||||
documentMiddlewareEnabled?: boolean
|
||||
isDataReq?: boolean
|
||||
params?: ParsedUrlQuery
|
||||
|
@ -743,9 +745,9 @@ export async function renderToHTML(
|
|||
html.substring(0, ampRenderIndex) +
|
||||
`<!-- __NEXT_DATA__ -->${docProps.html}` +
|
||||
html.substring(ampRenderIndex + AMP_RENDER_TARGET.length)
|
||||
html = await optimizeAmp(html)
|
||||
html = await optimizeAmp(html, renderOpts.ampOptimizerConfig)
|
||||
|
||||
if (renderOpts.ampValidator) {
|
||||
if (!renderOpts.ampSkipValidation && renderOpts.ampValidator) {
|
||||
await renderOpts.ampValidator(html, pathname)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,8 @@ export default class DevServer extends Server {
|
|||
this.devReady = new Promise(resolve => {
|
||||
this.setDevReady = resolve
|
||||
})
|
||||
;(this.renderOpts as any).ampSkipValidation =
|
||||
this.nextConfig.experimental?.amp?.skipValidation ?? false
|
||||
;(this.renderOpts as any).ampValidator = (
|
||||
html: string,
|
||||
pathname: string
|
||||
|
|
12
test/integration/amphtml-custom-optimizer/next.config.js
Normal file
12
test/integration/amphtml-custom-optimizer/next.config.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
experimental: {
|
||||
amp: {
|
||||
optimizer: {
|
||||
ampRuntimeVersion: '001515617716922',
|
||||
ampUrlPrefix: '/amp',
|
||||
verbose: true,
|
||||
},
|
||||
skipValidation: true,
|
||||
},
|
||||
},
|
||||
}
|
20
test/integration/amphtml-custom-optimizer/pages/dynamic.js
Normal file
20
test/integration/amphtml-custom-optimizer/pages/dynamic.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
export const config = {
|
||||
amp: true,
|
||||
}
|
||||
|
||||
const Dynamic = props => (
|
||||
<amp-img
|
||||
width="500"
|
||||
height="500"
|
||||
layout="responsive"
|
||||
src={props.src}
|
||||
></amp-img>
|
||||
)
|
||||
|
||||
Dynamic.getInitialProps = () => {
|
||||
return {
|
||||
src: 'https://amp.dev/static/samples/img/story_dog2_portrait.jpg',
|
||||
}
|
||||
}
|
||||
|
||||
export default Dynamic
|
12
test/integration/amphtml-custom-optimizer/pages/index.js
Normal file
12
test/integration/amphtml-custom-optimizer/pages/index.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
export const config = {
|
||||
amp: true,
|
||||
}
|
||||
|
||||
export default () => (
|
||||
<amp-twitter
|
||||
width="500"
|
||||
height="500"
|
||||
layout="responsive"
|
||||
data-tweetid="1159145442896166912"
|
||||
></amp-twitter>
|
||||
)
|
52
test/integration/amphtml-custom-optimizer/test/index.test.js
Normal file
52
test/integration/amphtml-custom-optimizer/test/index.test.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
import { join } from 'path'
|
||||
import {
|
||||
nextBuild,
|
||||
findPort,
|
||||
nextStart,
|
||||
killApp,
|
||||
renderViaHTTP,
|
||||
} from 'next-test-utils'
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1
|
||||
|
||||
let app
|
||||
let appPort
|
||||
const appDir = join(__dirname, '../')
|
||||
|
||||
describe('AMP Custom Optimizer', () => {
|
||||
it('should build and start for static page', async () => {
|
||||
const { code } = await nextBuild(appDir, undefined)
|
||||
expect(code).toBe(0)
|
||||
|
||||
appPort = await findPort()
|
||||
app = await nextStart(appDir, appPort)
|
||||
|
||||
const html = await renderViaHTTP(appPort, '/')
|
||||
await killApp(app)
|
||||
|
||||
expect(html).toContain(
|
||||
'amp-twitter width="500" height="500" layout="responsive" data-tweetid="1159145442896166912"'
|
||||
)
|
||||
expect(html).toContain('i-amphtml-version="001515617716922"')
|
||||
expect(html).toContain('script async src="/amp/rtv/001515617716922/v0.js"')
|
||||
})
|
||||
|
||||
it('should build and start for dynamic page', async () => {
|
||||
const { code } = await nextBuild(appDir, undefined)
|
||||
expect(code).toBe(0)
|
||||
|
||||
appPort = await findPort()
|
||||
app = await nextStart(appDir, appPort)
|
||||
|
||||
const html = await renderViaHTTP(appPort, '/dynamic')
|
||||
await killApp(app)
|
||||
|
||||
expect(html).toContain(
|
||||
'amp-img width="500" height="500" layout="responsive" src="https://amp.dev/static/samples/img/story_dog2_portrait.jpg"'
|
||||
)
|
||||
expect(html).toContain('i-amphtml-version="001515617716922"')
|
||||
expect(html).toContain('script async src="/amp/rtv/001515617716922/v0.js"')
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue