Skip to content

Commit

Permalink
Merge branch 'canary' into morePerfNumbers
Browse files Browse the repository at this point in the history
  • Loading branch information
prateekbh authored Feb 14, 2020
2 parents 17b2cd6 + ae9b13e commit d6a095b
Show file tree
Hide file tree
Showing 20 changed files with 440 additions and 76 deletions.
32 changes: 29 additions & 3 deletions docs/advanced-features/amp-support/adding-amp-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,35 @@ description: Add components from the AMP community to AMP pages, and make your p

# Adding AMP Components

The AMP community provide [many components](https://amp.dev/documentation/components/) to make AMP pages more interactive. You can add these components to your page by using `next/head`, as in the following example:
The AMP community provides [many components](https://amp.dev/documentation/components/) to make AMP pages more interactive. Next.js will automatically import all components used on a page and there is no need to manually import AMP component scripts:

```jsx
export const config = { amp: true }

function MyAmpPage() {
const date = new Date()

return (
<div>
<p>Some time: {date.toJSON()}</p>
<amp-timeago
width="0"
height="15"
datetime={date.toJSON()}
layout="responsive"
>
.
</amp-timeago>
</div>
)
}

export default MyAmpPage
```

The above example uses the [`amp-timeago`](https://amp.dev/documentation/components/amp-timeago/?format=websites) component.

By default, the latest version of a component is always imported. If you want to customize the version, you can use `next/head`, as in the following example:

```jsx
import Head from 'next/head'
Expand Down Expand Up @@ -40,5 +68,3 @@ function MyAmpPage() {

export default MyAmpPage
```

The above example uses the [`amp-timeago`](https://amp.dev/documentation/components/amp-timeago/?format=websites) component.
4 changes: 2 additions & 2 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ import { normalizePagePath } from '../next-server/server/normalize-page-path'
import {
eventBuildCompleted,
eventBuildOptimize,
eventCliSession,
eventNextPlugins,
eventVersion,
} from '../telemetry/events'
import { Telemetry } from '../telemetry/storage'
import { CompilerResult, runCompiler } from './compiler'
Expand Down Expand Up @@ -161,7 +161,7 @@ export default async function build(dir: string, conf = null): Promise<void> {
let hasPublicDir = false

telemetry.record(
eventVersion({
eventCliSession(PHASE_PRODUCTION_BUILD, dir, {
cliCommand: 'build',
isSrcDir: path.relative(dir, pagesDir!).startsWith('src'),
hasNowJson: !!(await findUp('now.json', { cwd: dir })),
Expand Down
4 changes: 2 additions & 2 deletions packages/next/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import loadConfig, {
isTargetLikeServerless,
} from '../next-server/server/config'
import { eventVersion } from '../telemetry/events'
import { eventCliSession } from '../telemetry/events'
import { Telemetry } from '../telemetry/storage'
import { normalizePagePath } from '../next-server/server/normalize-page-path'

Expand Down Expand Up @@ -105,7 +105,7 @@ export default async function(

if (telemetry) {
telemetry.record(
eventVersion({
eventCliSession(PHASE_EXPORT, distDir, {
cliCommand: 'export',
isSrcDir: null,
hasNowJson: !!(await findUp('now.json', { cwd: dir })),
Expand Down
2 changes: 1 addition & 1 deletion packages/next/next-server/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ function assignDefaults(userConfig: { [key: string]: any }) {
return result
}

function normalizeConfig(phase: string, config: any) {
export function normalizeConfig(phase: string, config: any) {
if (typeof config === 'function') {
config = config(phase, { defaultConfig })

Expand Down
2 changes: 1 addition & 1 deletion packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
]
},
"dependencies": {
"@ampproject/toolbox-optimizer": "1.1.1",
"@ampproject/toolbox-optimizer": "2.0.0",
"@babel/core": "7.7.2",
"@babel/plugin-proposal-class-properties": "7.7.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.7.4",
Expand Down
4 changes: 2 additions & 2 deletions packages/next/server/next-dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { __ApiPreviewProps } from '../next-server/server/api-utils'
import Server, { ServerConstructor } from '../next-server/server/next-server'
import { normalizePagePath } from '../next-server/server/normalize-page-path'
import Router, { Params, route } from '../next-server/server/router'
import { eventVersion } from '../telemetry/events'
import { eventCliSession } from '../telemetry/events'
import { Telemetry } from '../telemetry/storage'
import ErrorDebug from './error-debug'
import HotReloader from './hot-reloader'
Expand Down Expand Up @@ -233,7 +233,7 @@ export default class DevServer extends Server {

const telemetry = new Telemetry({ distDir: this.distDir })
telemetry.record(
eventVersion({
eventCliSession(PHASE_DEVELOPMENT_SERVER, this.distDir, {
cliCommand: 'dev',
isSrcDir: relative(this.dir, this.pagesDir!).startsWith('src'),
hasNowJson: !!(await findUp('now.json', { cwd: this.dir })),
Expand Down
105 changes: 90 additions & 15 deletions packages/next/telemetry/events/version.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import findUp from 'find-up'
import path from 'path'
import {
CONFIG_FILE,
PHASE_DEVELOPMENT_SERVER,
PHASE_EXPORT,
PHASE_PRODUCTION_BUILD,
} from '../../next-server/lib/constants'
import { normalizeConfig } from '../../next-server/server/config'

const EVENT_VERSION = 'NEXT_CLI_SESSION_STARTED'

type EventCliSessionStarted = {
Expand All @@ -7,27 +17,92 @@ type EventCliSessionStarted = {
isSrcDir: boolean | null
hasNowJson: boolean
isCustomServer: boolean | null
hasNextConfig: boolean
buildTarget: string
hasWebpackConfig: boolean
hasBabelConfig: boolean
}

function hasBabelConfig(dir: string): boolean {
try {
const noopFile = path.join(dir, 'noop.js')
const res = require('@babel/core').loadPartialConfig({
cwd: dir,
filename: noopFile,
sourceFileName: noopFile,
}) as any
const isForTooling =
res.options?.presets?.every(
(e: any) => e?.file?.request === 'next/babel'
) && res.options?.plugins?.length === 0
return res.hasFilesystemConfig() && !isForTooling
} catch {
return false
}
}

type NextConfigurationPhase =
| typeof PHASE_DEVELOPMENT_SERVER
| typeof PHASE_PRODUCTION_BUILD
| typeof PHASE_EXPORT

function getNextConfig(
phase: NextConfigurationPhase,
dir: string
): { [key: string]: any } | null {
try {
const configurationPath = findUp.sync(CONFIG_FILE, {
cwd: dir,
})

if (configurationPath) {
// This should've already been loaded, and thus should be cached / won't
// be re-evaluated.
const configurationModule = require(configurationPath)

// Re-normalize the configuration.
return normalizeConfig(
phase,
configurationModule.default || configurationModule
)
}
} catch {
// ignored
}
return null
}

export function eventVersion(
event: Omit<EventCliSessionStarted, 'nextVersion' | 'nodeVersion'>
export function eventCliSession(
phase: NextConfigurationPhase,
dir: string,
event: Omit<
EventCliSessionStarted,
| 'nextVersion'
| 'nodeVersion'
| 'hasNextConfig'
| 'buildTarget'
| 'hasWebpackConfig'
| 'hasBabelConfig'
>
): { eventName: string; payload: EventCliSessionStarted }[] {
// This should be an invariant, if it fails our build tooling is broken.
if (typeof process.env.__NEXT_VERSION !== 'string') {
return []
}

return [
{
eventName: EVENT_VERSION,
payload: {
nextVersion: process.env.__NEXT_VERSION,
nodeVersion: process.version,
cliCommand: event.cliCommand,
isSrcDir: event.isSrcDir,
hasNowJson: event.hasNowJson,
isCustomServer: event.isCustomServer,
} as EventCliSessionStarted,
},
]
const userConfiguration = getNextConfig(phase, dir)

const payload: EventCliSessionStarted = {
nextVersion: process.env.__NEXT_VERSION,
nodeVersion: process.version,
cliCommand: event.cliCommand,
isSrcDir: event.isSrcDir,
hasNowJson: event.hasNowJson,
isCustomServer: event.isCustomServer,
hasNextConfig: !!userConfiguration,
buildTarget: userConfiguration?.target ?? 'default',
hasWebpackConfig: typeof userConfiguration?.webpack === 'function',
hasBabelConfig: hasBabelConfig(dir),
}
return [{ eventName: EVENT_VERSION, payload }]
}
4 changes: 2 additions & 2 deletions test/integration/amp-export-validation/pages/cat.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export const config = { amp: true }

export default () => (
<div>
{/* I show a warning since the amp-video script isn't added */}
<amp-video src="/cats.mp4" height={400} width={800} />
{/* I show a warning since the width and height attribute is missing */}
<amp-video src="/cats.mp4" layout="responsive" />
</div>
)
2 changes: 0 additions & 2 deletions test/integration/amp-export-validation/pages/dog-cat.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ export default () => (
<div>
{/* I throw an error since <amp-img/> should be used instead */}
<img src="/dog.gif" height={400} width={800} />
{/* I show a warning since the amp-video script isn't added */}
<amp-video src="/cats.mp4" height={400} width={800} />
</div>
)
7 changes: 2 additions & 5 deletions test/integration/amp-export-validation/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ describe('AMP Validation on Export', () => {
expect(buildOutput).toMatch(
/error.*The parent tag of tag 'IMG-I-AMPHTML-INTRINSIC-SIZER' is 'div', but it can only be 'i-amphtml-sizer-intrinsic'/
)
expect(buildOutput).toMatch(
/warn.*The tag 'amp-video extension .js script' is missing or incorrect, but required by 'amp-video'/
)
})

it('should export AMP pages', async () => {
Expand Down Expand Up @@ -63,7 +60,7 @@ describe('AMP Validation on Export', () => {
stderr: true,
})
expect(stdout).toMatch(
/warn.*The tag 'amp-video extension \.js script' is missing/
/error.*The mandatory attribute 'height' is missing in tag 'amp-video'\./
)
await expect(access(join(outDir, 'cat.html'))).resolves.toBe(undefined)
await expect(stderr).not.toMatch(
Expand Down Expand Up @@ -117,7 +114,7 @@ describe('AMP Validation on Export', () => {
stderr: true,
})
expect(stdout).toMatch(
/warn.*The tag 'amp-video extension .js script' is missing or incorrect, but required by 'amp-video'/
/error.*The parent tag of tag 'IMG-I-AMPHTML-INTRINSIC-SIZER' is 'div', but it can only be 'i-amphtml-sizer-intrinsic'/
)
expect(stdout).toMatch(
/error.*The parent tag of tag 'IMG-I-AMPHTML-INTRINSIC-SIZER' is 'div', but it can only be 'i-amphtml-sizer-intrinsic'/
Expand Down
10 changes: 10 additions & 0 deletions test/integration/amphtml/pages/auto-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const config = { amp: true }

export default () => (
<amp-twitter
width="390"
height="330"
layout="responsive"
data-momentid="1018806709412876288"
></amp-twitter>
)
5 changes: 5 additions & 0 deletions test/integration/amphtml/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ describe('AMP Usage', () => {
const html = await renderViaHTTP(appPort, '/only-amp')
await validateAMP(html)
})

it('should auto import extensions', async () => {
const html = await renderViaHTTP(appPort, '/auto-import')
await validateAMP(html)
})
})

describe('With AMP context', () => {
Expand Down
3 changes: 3 additions & 0 deletions test/integration/telemetry/.babelrc.default
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["next/babel"]
}
4 changes: 4 additions & 0 deletions test/integration/telemetry/.babelrc.plugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["next/babel"],
"plugins": ["@babel/plugin-proposal-object-rest-spread"]
}
3 changes: 3 additions & 0 deletions test/integration/telemetry/.babelrc.preset
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["next/babel", "@babel/preset-flow"]
}
10 changes: 10 additions & 0 deletions test/integration/telemetry/next.config.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { PHASE_PRODUCTION_BUILD } = require('next/constants')

module.exports = phase => {
if (phase === PHASE_PRODUCTION_BUILD) {
return {
target: 'experimental-serverless-trace',
}
}
return {}
}
5 changes: 5 additions & 0 deletions test/integration/telemetry/next.config.webpack
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
webpack(config) {
return config
},
}
10 changes: 10 additions & 0 deletions test/integration/telemetry/package.babel
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"babel": {
"presets": [
"next/babel"
],
"plugins": [
"@babel/plugin-proposal-object-rest-spread"
]
}
}
Loading

0 comments on commit d6a095b

Please sign in to comment.