diff --git a/docs/advanced-features/amp-support/adding-amp-components.md b/docs/advanced-features/amp-support/adding-amp-components.md index 1c11ffe8c630c..63305791d5448 100644 --- a/docs/advanced-features/amp-support/adding-amp-components.md +++ b/docs/advanced-features/amp-support/adding-amp-components.md @@ -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 ( +
+

Some time: {date.toJSON()}

+ + . + +
+ ) +} + +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' @@ -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. diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index f95e028f64dd8..2548161d4d48f 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -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' @@ -161,7 +161,7 @@ export default async function build(dir: string, conf = null): Promise { 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 })), diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts index 386928105af99..e43f65e243417 100644 --- a/packages/next/export/index.ts +++ b/packages/next/export/index.ts @@ -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' @@ -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 })), diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 4416fbd277dd6..9d675c14d2ded 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -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 }) diff --git a/packages/next/package.json b/packages/next/package.json index faf0bbefa2abc..8e49e66c676b9 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -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", diff --git a/packages/next/server/next-dev-server.ts b/packages/next/server/next-dev-server.ts index 79d2318504010..3df13d0499adf 100644 --- a/packages/next/server/next-dev-server.ts +++ b/packages/next/server/next-dev-server.ts @@ -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' @@ -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 })), diff --git a/packages/next/telemetry/events/version.ts b/packages/next/telemetry/events/version.ts index 8c23ff516676b..a4b9ef4803a02 100644 --- a/packages/next/telemetry/events/version.ts +++ b/packages/next/telemetry/events/version.ts @@ -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 = { @@ -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 +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 }] } diff --git a/test/integration/amp-export-validation/pages/cat.js b/test/integration/amp-export-validation/pages/cat.js index 176502a163330..3fa8f4b72fe40 100644 --- a/test/integration/amp-export-validation/pages/cat.js +++ b/test/integration/amp-export-validation/pages/cat.js @@ -2,7 +2,7 @@ export const config = { amp: true } export default () => (
- {/* I show a warning since the amp-video script isn't added */} - + {/* I show a warning since the width and height attribute is missing */} +
) diff --git a/test/integration/amp-export-validation/pages/dog-cat.js b/test/integration/amp-export-validation/pages/dog-cat.js index 0a190fb53ab38..8388a6e7c0403 100644 --- a/test/integration/amp-export-validation/pages/dog-cat.js +++ b/test/integration/amp-export-validation/pages/dog-cat.js @@ -4,7 +4,5 @@ export default () => (
{/* I throw an error since should be used instead */} - {/* I show a warning since the amp-video script isn't added */} -
) diff --git a/test/integration/amp-export-validation/test/index.test.js b/test/integration/amp-export-validation/test/index.test.js index affda3c55ca88..361ae8f35350b 100644 --- a/test/integration/amp-export-validation/test/index.test.js +++ b/test/integration/amp-export-validation/test/index.test.js @@ -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 () => { @@ -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( @@ -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'/ diff --git a/test/integration/amphtml/pages/auto-import.js b/test/integration/amphtml/pages/auto-import.js new file mode 100644 index 0000000000000..3c143238592a7 --- /dev/null +++ b/test/integration/amphtml/pages/auto-import.js @@ -0,0 +1,10 @@ +export const config = { amp: true } + +export default () => ( + +) diff --git a/test/integration/amphtml/test/index.test.js b/test/integration/amphtml/test/index.test.js index f10f5851f39bd..2ef48fee32655 100644 --- a/test/integration/amphtml/test/index.test.js +++ b/test/integration/amphtml/test/index.test.js @@ -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', () => { diff --git a/test/integration/telemetry/.babelrc.default b/test/integration/telemetry/.babelrc.default new file mode 100644 index 0000000000000..1ff94f7ed28e1 --- /dev/null +++ b/test/integration/telemetry/.babelrc.default @@ -0,0 +1,3 @@ +{ + "presets": ["next/babel"] +} diff --git a/test/integration/telemetry/.babelrc.plugin b/test/integration/telemetry/.babelrc.plugin new file mode 100644 index 0000000000000..e79918c689a29 --- /dev/null +++ b/test/integration/telemetry/.babelrc.plugin @@ -0,0 +1,4 @@ +{ + "presets": ["next/babel"], + "plugins": ["@babel/plugin-proposal-object-rest-spread"] +} diff --git a/test/integration/telemetry/.babelrc.preset b/test/integration/telemetry/.babelrc.preset new file mode 100644 index 0000000000000..f9a98e420859b --- /dev/null +++ b/test/integration/telemetry/.babelrc.preset @@ -0,0 +1,3 @@ +{ + "presets": ["next/babel", "@babel/preset-flow"] +} diff --git a/test/integration/telemetry/next.config.target b/test/integration/telemetry/next.config.target new file mode 100644 index 0000000000000..d1a88cac38d9c --- /dev/null +++ b/test/integration/telemetry/next.config.target @@ -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 {} +} diff --git a/test/integration/telemetry/next.config.webpack b/test/integration/telemetry/next.config.webpack new file mode 100644 index 0000000000000..32facfdb9d19b --- /dev/null +++ b/test/integration/telemetry/next.config.webpack @@ -0,0 +1,5 @@ +module.exports = { + webpack(config) { + return config + }, +} diff --git a/test/integration/telemetry/package.babel b/test/integration/telemetry/package.babel new file mode 100644 index 0000000000000..74074a210b716 --- /dev/null +++ b/test/integration/telemetry/package.babel @@ -0,0 +1,10 @@ +{ + "babel": { + "presets": [ + "next/babel" + ], + "plugins": [ + "@babel/plugin-proposal-object-rest-spread" + ] + } +} diff --git a/test/integration/telemetry/test/index.test.js b/test/integration/telemetry/test/index.test.js index 7b2a9e4c7b42a..f059b50af04be 100644 --- a/test/integration/telemetry/test/index.test.js +++ b/test/integration/telemetry/test/index.test.js @@ -138,6 +138,182 @@ describe('Telemetry CLI', () => { expect(event2).toMatch(/hasTestPages.*?true/) }) + it('detects correct cli session defaults', async () => { + const { stderr } = await runNextCommand(['build', appDir], { + stderr: true, + env: { + NEXT_TELEMETRY_DEBUG: 1, + }, + }) + + const event = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/ + .exec(stderr) + .pop() + + expect(event).toMatch(/"hasNextConfig": false/) + expect(event).toMatch(/"buildTarget": "default"/) + expect(event).toMatch(/"hasWebpackConfig": false/) + expect(event).toMatch(/"hasBabelConfig": false/) + }) + + it('cli session: babel tooling config', async () => { + await fs.rename( + path.join(appDir, '.babelrc.default'), + path.join(appDir, '.babelrc') + ) + const { stderr } = await runNextCommand(['build', appDir], { + stderr: true, + env: { + NEXT_TELEMETRY_DEBUG: 1, + }, + }) + await fs.rename( + path.join(appDir, '.babelrc'), + path.join(appDir, '.babelrc.default') + ) + + const event = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/ + .exec(stderr) + .pop() + + expect(event).toMatch(/"hasNextConfig": false/) + expect(event).toMatch(/"buildTarget": "default"/) + expect(event).toMatch(/"hasWebpackConfig": false/) + expect(event).toMatch(/"hasBabelConfig": false/) + }) + + it('cli session: custom babel config (plugin)', async () => { + await fs.rename( + path.join(appDir, '.babelrc.plugin'), + path.join(appDir, '.babelrc') + ) + const { stderr } = await runNextCommand(['build', appDir], { + stderr: true, + env: { + NEXT_TELEMETRY_DEBUG: 1, + }, + }) + await fs.rename( + path.join(appDir, '.babelrc'), + path.join(appDir, '.babelrc.plugin') + ) + + const event = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/ + .exec(stderr) + .pop() + + expect(event).toMatch(/"hasNextConfig": false/) + expect(event).toMatch(/"buildTarget": "default"/) + expect(event).toMatch(/"hasWebpackConfig": false/) + expect(event).toMatch(/"hasBabelConfig": true/) + }) + + it('cli session: package.json custom babel config (plugin)', async () => { + await fs.rename( + path.join(appDir, 'package.babel'), + path.join(appDir, 'package.json') + ) + const { stderr } = await runNextCommand(['build', appDir], { + stderr: true, + env: { + NEXT_TELEMETRY_DEBUG: 1, + }, + }) + await fs.rename( + path.join(appDir, 'package.json'), + path.join(appDir, 'package.babel') + ) + + console.log(stderr) + + const event = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/ + .exec(stderr) + .pop() + + expect(event).toMatch(/"hasNextConfig": false/) + expect(event).toMatch(/"buildTarget": "default"/) + expect(event).toMatch(/"hasWebpackConfig": false/) + expect(event).toMatch(/"hasBabelConfig": true/) + }) + + it('cli session: custom babel config (preset)', async () => { + await fs.rename( + path.join(appDir, '.babelrc.preset'), + path.join(appDir, '.babelrc') + ) + const { stderr } = await runNextCommand(['build', appDir], { + stderr: true, + env: { + NEXT_TELEMETRY_DEBUG: 1, + }, + }) + await fs.rename( + path.join(appDir, '.babelrc'), + path.join(appDir, '.babelrc.preset') + ) + + const event = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/ + .exec(stderr) + .pop() + + expect(event).toMatch(/"hasNextConfig": false/) + expect(event).toMatch(/"buildTarget": "default"/) + expect(event).toMatch(/"hasWebpackConfig": false/) + expect(event).toMatch(/"hasBabelConfig": true/) + }) + + it('cli session: next config with target', async () => { + await fs.rename( + path.join(appDir, 'next.config.target'), + path.join(appDir, 'next.config.js') + ) + const { stderr } = await runNextCommand(['build', appDir], { + stderr: true, + env: { + NEXT_TELEMETRY_DEBUG: 1, + }, + }) + await fs.rename( + path.join(appDir, 'next.config.js'), + path.join(appDir, 'next.config.target') + ) + + const event = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/ + .exec(stderr) + .pop() + + expect(event).toMatch(/"hasNextConfig": true/) + expect(event).toMatch(/"buildTarget": "experimental-serverless-trace"/) + expect(event).toMatch(/"hasWebpackConfig": false/) + expect(event).toMatch(/"hasBabelConfig": false/) + }) + + it('cli session: next config with webpack', async () => { + await fs.rename( + path.join(appDir, 'next.config.webpack'), + path.join(appDir, 'next.config.js') + ) + const { stderr } = await runNextCommand(['build', appDir], { + stderr: true, + env: { + NEXT_TELEMETRY_DEBUG: 1, + }, + }) + await fs.rename( + path.join(appDir, 'next.config.js'), + path.join(appDir, 'next.config.webpack') + ) + + const event = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/ + .exec(stderr) + .pop() + + expect(event).toMatch(/"hasNextConfig": true/) + expect(event).toMatch(/"buildTarget": "default"/) + expect(event).toMatch(/"hasWebpackConfig": true/) + expect(event).toMatch(/"hasBabelConfig": false/) + }) + it('detect static 404 correctly for `next build`', async () => { const { stderr } = await nextBuild(appDir, [], { stderr: true, diff --git a/yarn.lock b/yarn.lock index 87e1fcbeaa82d..4685fc13441d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,36 +2,47 @@ # yarn lockfile v1 -"@ampproject/toolbox-core@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-1.1.1.tgz#540c8f3ab0f5d1faa1ba35282cd5f5f3f0e16a76" - integrity sha512-jcuVJUnGDRUEJgMYO6QVdf1dBy/oLZX3NjN2hYG48biFcPCvXevuv4xYFZMJsnsHSvXKg8y0qB8rANNyhTUN/A== +"@ampproject/toolbox-core@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.0.0.tgz#3329a01eec8f767614385cc56cee8be32ccf7f32" + integrity sha512-xAJOmh6MPS2mdHNsK8mj1t8TLh6mlehirh0fOBsRhKCNCJXgg4Gfd2u5igy8VFq9sYnuWP/npFyjGX36qpXW5Q== dependencies: - node-fetch "2.6.0" + cross-fetch "3.0.4" -"@ampproject/toolbox-optimizer@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-optimizer/-/toolbox-optimizer-1.1.1.tgz#be66245c966ba9b0f5e3020109f87fea90ea377d" - integrity sha512-LTtTM5FSOrWuTJ6mOwPfZmpUDI6polrNz3tX2EmDmDkjDK+43vSpq1OHtukivIFHafdixJuoeki5dF3PC/ZoWw== +"@ampproject/toolbox-optimizer@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.0.0.tgz#96ea3d26bc7a9f19718a27c721e021a121003601" + integrity sha512-ZYRi7vB4ALC8DnTHQLchAeHMGsFml/Zr4fNWBlTiMGfvWGL0XTV9YyP4s24IDwAlunEgynmz0FTrGMJdRpNf2Q== dependencies: - "@ampproject/toolbox-core" "^1.1.1" - "@ampproject/toolbox-runtime-version" "^1.1.1" - "@ampproject/toolbox-script-csp" "^1.1.1" + "@ampproject/toolbox-core" "^2.0.0" + "@ampproject/toolbox-runtime-version" "^2.0.0" + "@ampproject/toolbox-script-csp" "^2.0.0" + "@ampproject/toolbox-validator-rules" "^2.0.0" css "2.2.4" - parse5 "5.1.0" - parse5-htmlparser2-tree-adapter "5.1.0" + domhandler "3.0.0" + domutils "2.0.0" + htmlparser2 "4.0.0" + normalize-html-whitespace "1.0.0" + terser "4.6.3" -"@ampproject/toolbox-runtime-version@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-runtime-version/-/toolbox-runtime-version-1.1.1.tgz#628fe5091db4f90b68960620e22ad64f9f2563bd" - integrity sha512-ibmw5p+0Sz+wingbX/Dyboe8a0+XDkMfFGSM7KFE0h2z3Op9MADup8ZPLeHT54Z7cYKmB6ob60FVHtQQDhEXNw== +"@ampproject/toolbox-runtime-version@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-runtime-version/-/toolbox-runtime-version-2.0.0.tgz#01202be490ce8baf8654127de35dff5f93e922a0" + integrity sha512-7XDlo7l4Ozpc/XWmJeYNF0ZfXCy7Vdh07spN+xEOst8gF99yaavZRNVkdgyTxLR3BpY9RIqqhSs6OxfkOKlRZQ== dependencies: - "@ampproject/toolbox-core" "^1.1.1" + "@ampproject/toolbox-core" "^2.0.0" -"@ampproject/toolbox-script-csp@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-script-csp/-/toolbox-script-csp-1.1.1.tgz#0b049a1c86c99f300162a10e1b9ce83c6e354a45" - integrity sha512-gACGfsVKinCy/977FSrlVgo6jxTZ0lcTCvCnRlNwvSOcxJVm+jJR3sP7/F43fpak9Gsq/EwFaatfnNMbunPc+w== +"@ampproject/toolbox-script-csp@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-script-csp/-/toolbox-script-csp-2.0.0.tgz#7aa55fb693749e657b63985ebe49e1d3489a8b79" + integrity sha512-9mW3yiKwjORi0ViuayphVZii9MwiPhneZGZWy+kN44xr3SpN7iQC52/WWWTBOZX9z1zaUh8DqGc//VFY5ILSAw== + +"@ampproject/toolbox-validator-rules@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-validator-rules/-/toolbox-validator-rules-2.0.0.tgz#eefdce2f7f773e4675d72c7a1068c70105a647c3" + integrity sha512-5f2IvT1m/zNtqfNXFE9V4ZtKofIttST65QL1wf4lKjBhJsbZgAAeR/u4DgOvftRVRmgn6IpUiWY1pt9xWaI5yA== + dependencies: + cross-fetch "3.0.4" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": version "7.5.5" @@ -5352,6 +5363,14 @@ cross-env@6.0.3: dependencies: cross-spawn "^7.0.0" +cross-fetch@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c" + integrity sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw== + dependencies: + node-fetch "2.6.0" + whatwg-fetch "3.0.0" + cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -6084,7 +6103,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-serializer@0: +dom-serializer@0, dom-serializer@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== @@ -6127,6 +6146,13 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" +domhandler@3.0.0, domhandler@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" + integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw== + dependencies: + domelementtype "^2.0.1" + domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -6142,6 +6168,15 @@ domutils@1.5.1: dom-serializer "0" domelementtype "1" +domutils@2.0.0, domutils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08" + integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg== + dependencies: + dom-serializer "^0.2.1" + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -8093,6 +8128,16 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= +htmlparser2@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.0.0.tgz#6034658db65b7713a572a9ebf79f650832dceec8" + integrity sha512-cChwXn5Vam57fyXajDtPXL1wTYc8JtLbr2TN76FYu05itVVVealxLowe2B3IEznJG4p9HAYn/0tJaRlGuEglFQ== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + htmlparser2@^3.9.1: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" @@ -11172,6 +11217,11 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" +normalize-html-whitespace@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34" + integrity sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA== + normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5, normalize-package-data@^2.4.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -11949,28 +11999,11 @@ parse-url@^5.0.0: parse-path "^4.0.0" protocols "^1.4.0" -parse5-htmlparser2-tree-adapter@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-5.1.0.tgz#a8244ee12bbd6b8937ad2a16ea43fe348aebcc86" - integrity sha512-OrI4DNmghGcwDB3XN8FKKN7g5vBmau91uqj+VYuwuj/r6GhFBMBNymsM+Z9z+Z1p4HHgI0UuQirQRgh3W5d88g== - dependencies: - parse5 "^5.1.0" - parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== - -parse5@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -15859,6 +15892,15 @@ terser@4.4.2: source-map "~0.6.1" source-map-support "~0.5.12" +terser@4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.3.tgz#e33aa42461ced5238d352d2df2a67f21921f8d87" + integrity sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + terser@^3.8.2: version "3.17.0" resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2"