From 6226a798126eeb42747c42584c2cab5e0b161edd Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Tue, 2 Mar 2021 13:50:51 +0100 Subject: [PATCH 1/5] v2.8.0-canary.4 --- .prettierignore | 1 + package-lock.json | 89 ++++++++++++--------- package.json | 2 + packages/cache-url/package.json | 3 +- packages/cli/package.json | 2 +- packages/cors/package.json | 2 +- packages/lighthouse-plugin-amp/package.json | 2 +- packages/linter/package.json | 2 +- packages/optimizer-express/package.json | 2 +- packages/optimizer/package.json | 2 +- packages/page-experience | 1 + packages/update-cache/package.json | 2 +- 12 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 .prettierignore create mode 160000 packages/page-experience diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..2d19fc766 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.html diff --git a/package-lock.json b/package-lock.json index c66317dfc..41b0fa7bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "eslint-plugin-prettier": "3.3.1", "execa": "5.0.0", "fetch-mock": "9.11.0", + "file-url": "3.0.0", "html-minifier": "4.0.0", "husky": "5.1.3", "jest": "26.6.3", @@ -9335,6 +9336,15 @@ "node": ">=6" } }, + "node_modules/file-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", + "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -23193,7 +23203,7 @@ }, "packages/cache-url": { "name": "@ampproject/toolbox-cache-url", - "version": "2.8.0-canary.0", + "version": "2.8.0-canary.4", "license": "Apache-2.0", "dependencies": { "punycode": "2.1.1", @@ -23211,17 +23221,17 @@ }, "packages/cli": { "name": "@ampproject/toolbox-cli", - "version": "2.8.0-canary.3", + "version": "2.8.0-canary.4", "license": "Apache-2.0", "dependencies": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", "@ampproject/toolbox-core": "^2.8.0-canary.0", - "@ampproject/toolbox-linter": "^2.8.0-canary.3", - "@ampproject/toolbox-optimizer": "^2.8.0-canary.3", + "@ampproject/toolbox-linter": "^2.8.0-canary.4", + "@ampproject/toolbox-optimizer": "^2.8.0-canary.4", "@ampproject/toolbox-runtime-fetch": "^2.8.0-canary.0", "@ampproject/toolbox-runtime-version": "^2.8.0-canary.0", - "@ampproject/toolbox-update-cache": "^2.8.0-canary.2", + "@ampproject/toolbox-update-cache": "^2.8.0-canary.4", "minimist": "1.2.5", "minimist-options": "4.1.0", "node-fetch": "2.6.1" @@ -23599,11 +23609,11 @@ }, "packages/cors": { "name": "@ampproject/toolbox-cors", - "version": "2.8.0-canary.0", + "version": "2.8.0-canary.4", "license": "Apache-2.0", "dependencies": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", "@ampproject/toolbox-core": "^2.8.0-canary.0" } }, @@ -23634,7 +23644,7 @@ } }, "packages/lighthouse-plugin-amp": { - "version": "2.8.0-canary.2", + "version": "2.8.0-canary.4", "dependencies": { "amphtml-validator": "1.0.34", "node-fetch": "^2.6.1" @@ -23679,12 +23689,12 @@ }, "packages/linter": { "name": "@ampproject/toolbox-linter", - "version": "2.8.0-canary.3", + "version": "2.8.0-canary.4", "license": "Apache-2.0", "dependencies": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", - "@ampproject/toolbox-optimizer": "^2.8.0-canary.3", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", + "@ampproject/toolbox-optimizer": "^2.8.0-canary.4", "amphtml-validator": "1.0.34", "chalk": "4.1.0", "cheerio": "1.0.0-rc.5", @@ -24128,7 +24138,7 @@ }, "packages/optimizer": { "name": "@ampproject/toolbox-optimizer", - "version": "2.8.0-canary.3", + "version": "2.8.0-canary.4", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -24161,11 +24171,11 @@ }, "packages/optimizer-express": { "name": "@ampproject/toolbox-optimizer-express", - "version": "2.8.0-canary.3", + "version": "2.8.0-canary.4", "license": "Apache-2.0", "dependencies": { "@ampproject/toolbox-core": "^2.8.0-canary.0", - "@ampproject/toolbox-optimizer": "^2.8.0-canary.3", + "@ampproject/toolbox-optimizer": "^2.8.0-canary.4", "@ampproject/toolbox-runtime-version": "^2.8.0-canary.0", "mime-types": "2.1.29", "whatwg-url": "8.4.0" @@ -24578,11 +24588,11 @@ }, "packages/update-cache": { "name": "@ampproject/toolbox-update-cache", - "version": "2.8.0-canary.2", + "version": "2.8.0-canary.4", "license": "Apache-2.0", "dependencies": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", "jsrsasign": "10.1.12" } }, @@ -24730,13 +24740,13 @@ "version": "file:packages/cli", "requires": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", "@ampproject/toolbox-core": "^2.8.0-canary.0", - "@ampproject/toolbox-linter": "^2.8.0-canary.3", - "@ampproject/toolbox-optimizer": "^2.8.0-canary.3", + "@ampproject/toolbox-linter": "^2.8.0-canary.4", + "@ampproject/toolbox-optimizer": "^2.8.0-canary.4", "@ampproject/toolbox-runtime-fetch": "^2.8.0-canary.0", "@ampproject/toolbox-runtime-version": "^2.8.0-canary.0", - "@ampproject/toolbox-update-cache": "^2.8.0-canary.2", + "@ampproject/toolbox-update-cache": "^2.8.0-canary.4", "minimist": "1.2.5", "minimist-options": "4.1.0", "node-fetch": "2.6.1" @@ -24769,8 +24779,7 @@ } }, "@ampproject/toolbox-linter": { - "version": "2.8.0-canary.3", - "resolved": "https://registry.npmjs.org/@ampproject/toolbox-linter/-/toolbox-linter-2.8.0-canary.3.tgz", + "version": "https://registry.npmjs.org/@ampproject/toolbox-linter/-/toolbox-linter-2.8.0-canary.3.tgz", "integrity": "sha512-qNASV9RxAt4niTSvOLTf5SFIsWkG/9MrpIKrEXyaR0qiun6sxZWGDpTZZ7l+phRIgq9So/BzEJs5zO2jqIwYjQ==", "requires": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", @@ -24912,8 +24921,7 @@ } }, "@ampproject/toolbox-update-cache": { - "version": "2.8.0-canary.2", - "resolved": "https://registry.npmjs.org/@ampproject/toolbox-update-cache/-/toolbox-update-cache-2.8.0-canary.2.tgz", + "version": "https://registry.npmjs.org/@ampproject/toolbox-update-cache/-/toolbox-update-cache-2.8.0-canary.2.tgz", "integrity": "sha512-OeaDbnHH8tlFKbZAwNaIFir0xFsHQLvkx6O04ozlCx6wXeCs120uzO5gBXRESxebMZQmO2DZaS+tfcmaXenY0A==", "requires": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", @@ -25061,7 +25069,7 @@ "version": "file:packages/cors", "requires": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", "@ampproject/toolbox-core": "^2.8.0-canary.0" }, "dependencies": { @@ -25074,8 +25082,7 @@ } }, "@ampproject/toolbox-cache-url": { - "version": "2.8.0-canary.0", - "resolved": "https://registry.npmjs.org/@ampproject/toolbox-cache-url/-/toolbox-cache-url-2.8.0-canary.0.tgz", + "version": "https://registry.npmjs.org/@ampproject/toolbox-cache-url/-/toolbox-cache-url-2.8.0-canary.0.tgz", "integrity": "sha512-xeuG7KIkC8RvByNS/OvSPlOFKMtVt8RX5VUh/H2c+4BRsUI4Toa0qOzj8KjNDsGts/xmQAc4IAX2xDMBaNyV1A==", "requires": { "punycode": "2.1.1", @@ -25097,8 +25104,8 @@ "version": "file:packages/linter", "requires": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", - "@ampproject/toolbox-optimizer": "^2.8.0-canary.3", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", + "@ampproject/toolbox-optimizer": "^2.8.0-canary.4", "amphtml-validator": "1.0.34", "chalk": "4.1.0", "cheerio": "1.0.0-rc.5", @@ -25124,8 +25131,7 @@ } }, "@ampproject/toolbox-cache-url": { - "version": "2.8.0-canary.0", - "resolved": "https://registry.npmjs.org/@ampproject/toolbox-cache-url/-/toolbox-cache-url-2.8.0-canary.0.tgz", + "version": "https://registry.npmjs.org/@ampproject/toolbox-cache-url/-/toolbox-cache-url-2.8.0-canary.0.tgz", "integrity": "sha512-xeuG7KIkC8RvByNS/OvSPlOFKMtVt8RX5VUh/H2c+4BRsUI4Toa0qOzj8KjNDsGts/xmQAc4IAX2xDMBaNyV1A==", "requires": { "punycode": "2.1.1", @@ -25133,8 +25139,7 @@ } }, "@ampproject/toolbox-optimizer": { - "version": "2.8.0-canary.3", - "resolved": "https://registry.npmjs.org/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.8.0-canary.3.tgz", + "version": "https://registry.npmjs.org/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.8.0-canary.3.tgz", "integrity": "sha512-q4Z/Gp2TQXpevTwJdu6tvgsJmURf7Ti1auJYOGHes0ye0tTbyVsn3fI3d1r38V2ocDSVYLsOueUc+s8+41UPeQ==", "requires": { "@ampproject/toolbox-core": "^2.8.0-canary.0", @@ -25630,7 +25635,7 @@ "version": "file:packages/optimizer-express", "requires": { "@ampproject/toolbox-core": "^2.8.0-canary.0", - "@ampproject/toolbox-optimizer": "^2.8.0-canary.3", + "@ampproject/toolbox-optimizer": "^2.8.0-canary.4", "@ampproject/toolbox-runtime-version": "^2.8.0-canary.0", "mime-types": "2.1.29", "whatwg-url": "8.4.0" @@ -25646,8 +25651,7 @@ } }, "@ampproject/toolbox-optimizer": { - "version": "2.8.0-canary.3", - "resolved": "https://registry.npmjs.org/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.8.0-canary.3.tgz", + "version": "https://registry.npmjs.org/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.8.0-canary.3.tgz", "integrity": "sha512-q4Z/Gp2TQXpevTwJdu6tvgsJmURf7Ti1auJYOGHes0ye0tTbyVsn3fI3d1r38V2ocDSVYLsOueUc+s8+41UPeQ==", "requires": { "@ampproject/toolbox-core": "^2.8.0-canary.0", @@ -25850,7 +25854,7 @@ "version": "file:packages/update-cache", "requires": { "@ampproject/toolbox-cache-list": "^2.8.0-canary.0", - "@ampproject/toolbox-cache-url": "^2.8.0-canary.0", + "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", "jsrsasign": "10.1.12" }, "dependencies": { @@ -25863,8 +25867,7 @@ } }, "@ampproject/toolbox-cache-url": { - "version": "2.8.0-canary.0", - "resolved": "https://registry.npmjs.org/@ampproject/toolbox-cache-url/-/toolbox-cache-url-2.8.0-canary.0.tgz", + "version": "https://registry.npmjs.org/@ampproject/toolbox-cache-url/-/toolbox-cache-url-2.8.0-canary.0.tgz", "integrity": "sha512-xeuG7KIkC8RvByNS/OvSPlOFKMtVt8RX5VUh/H2c+4BRsUI4Toa0qOzj8KjNDsGts/xmQAc4IAX2xDMBaNyV1A==", "requires": { "punycode": "2.1.1", @@ -33582,6 +33585,12 @@ "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==", "dev": true }, + "file-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", + "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==", + "dev": true + }, "filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", diff --git a/package.json b/package.json index 1b5c2251b..6b939dd15 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "test": "npm-run-all build test:node test:browser lint", "test:node": "jest", "test:optimizer:snapshot": "OPTIMIZER_SNAPSHOT=true jest", + "test:page-experience:snapshot": "PAGE_EXPERIENCE_SNAPSHOT=true jest", "test:browser": "karma start --single-run --browsers ChromeHeadless karma.conf.js", "format": "prettier --write **/*.{js,ts}", "lint": "eslint -c .eslintrc.json .", @@ -44,6 +45,7 @@ "eslint-plugin-prettier": "3.3.1", "execa": "5.0.0", "fetch-mock": "9.11.0", + "file-url": "3.0.0", "html-minifier": "4.0.0", "husky": "5.1.3", "jest": "26.6.3", diff --git a/packages/cache-url/package.json b/packages/cache-url/package.json index 38c411b95..b5348e818 100644 --- a/packages/cache-url/package.json +++ b/packages/cache-url/package.json @@ -8,7 +8,6 @@ "browser": "dist/amp-toolbox-cache-url.umd.js", "scripts": { "build": "npx rollup -c", - "prepublish": "npm run build", "serve": "npx rollup -c -w" }, "repository": { @@ -37,5 +36,5 @@ "punycode": "2.1.1", "url-parse": "1.5.1" }, - "gitHead": "9b159391d776396512b01ec250fa76941713c763" + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b" } diff --git a/packages/cli/package.json b/packages/cli/package.json index 5c9fba596..ca55fbd3d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -39,5 +39,5 @@ "minimist-options": "4.1.0", "node-fetch": "2.6.1" }, - "gitHead": "b86ff10f2a20a9e5f810f31a694c7f020eb291ae" + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b" } diff --git a/packages/cors/package.json b/packages/cors/package.json index ac834318e..70c92dae6 100644 --- a/packages/cors/package.json +++ b/packages/cors/package.json @@ -27,5 +27,5 @@ "url": "https://github.com/ampproject/amp-toolbox/issues" }, "homepage": "https://github.com/ampproject/amp-toolbox/tree/main/packages/cors", - "gitHead": "9b159391d776396512b01ec250fa76941713c763" + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b" } diff --git a/packages/lighthouse-plugin-amp/package.json b/packages/lighthouse-plugin-amp/package.json index ac38837fc..49fa3b3f6 100644 --- a/packages/lighthouse-plugin-amp/package.json +++ b/packages/lighthouse-plugin-amp/package.json @@ -15,5 +15,5 @@ "peerDependencies": { "lighthouse": ">=7.2.0" }, - "gitHead": "b86ff10f2a20a9e5f810f31a694c7f020eb291ae" + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b" } diff --git a/packages/linter/package.json b/packages/linter/package.json index 7b8d302af..42cc5ce91 100644 --- a/packages/linter/package.json +++ b/packages/linter/package.json @@ -40,7 +40,7 @@ "prettier": { "quoteProps": "consistent" }, - "gitHead": "b86ff10f2a20a9e5f810f31a694c7f020eb291ae", + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b", "devDependencies": { "prettier": "2.2.1", "tap-parser": "10.1.0", diff --git a/packages/optimizer-express/package.json b/packages/optimizer-express/package.json index 6283155bf..8c1385223 100644 --- a/packages/optimizer-express/package.json +++ b/packages/optimizer-express/package.json @@ -30,5 +30,5 @@ "mime-types": "2.1.29", "whatwg-url": "8.4.0" }, - "gitHead": "b86ff10f2a20a9e5f810f31a694c7f020eb291ae" + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b" } diff --git a/packages/optimizer/package.json b/packages/optimizer/package.json index b2a6bc45d..cb5846d8f 100644 --- a/packages/optimizer/package.json +++ b/packages/optimizer/package.json @@ -51,5 +51,5 @@ "url": "https://github.com/ampproject/amp-toolbox/issues" }, "homepage": "https://github.com/ampproject/amp-toolbox/tree/main/packages/optimizer", - "gitHead": "b86ff10f2a20a9e5f810f31a694c7f020eb291ae" + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b" } diff --git a/packages/page-experience b/packages/page-experience new file mode 160000 index 000000000..4682a57a3 --- /dev/null +++ b/packages/page-experience @@ -0,0 +1 @@ +Subproject commit 4682a57a38e0e8d8c3700d711045e4dbbc57a7b0 diff --git a/packages/update-cache/package.json b/packages/update-cache/package.json index cc45c4131..0cd3b759d 100644 --- a/packages/update-cache/package.json +++ b/packages/update-cache/package.json @@ -29,5 +29,5 @@ "@ampproject/toolbox-cache-url": "^2.8.0-canary.4", "jsrsasign": "10.1.12" }, - "gitHead": "b86ff10f2a20a9e5f810f31a694c7f020eb291ae" + "gitHead": "0c3af53a631fffeffbe3da1f2d626172fc996f1b" } From 8f7e159c823b6f5e1428acae775be0c693423a88 Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Fri, 5 Mar 2021 19:03:15 +0100 Subject: [PATCH 2/5] add page experience guide --- package-lock.json | 556 ++++++++- package.json | 1 + packages/page-experience | 1 - packages/page-experience/README.md | 29 + packages/page-experience/bin/cli.js | 24 + packages/page-experience/index.js | 19 + .../page-experience/lib/PageDataGatherer.js | 203 +++ .../lib/PageExperienceGuide.js | 138 +++ .../page-experience/lib/checks/fontDisplay.js | 70 ++ .../lib/checks/fontPreloading.js | 63 + .../lib/helpers/parseFontface.js | 52 + .../lib/helpers/parseFontface.test.js | 82 ++ .../lib/helpers/parseFontfaceSrc.js | 37 + .../lib/helpers/parseFontfaceSrc.test.js | 42 + packages/page-experience/package-lock.json | 1092 +++++++++++++++++ packages/page-experience/package.json | 38 + packages/page-experience/prettier.config.js | 39 + packages/page-experience/run.js | 14 + .../fontDisplay/font-display-optional.html | 25 + .../fontDisplay/font-display-optional.json | 1 + .../font-display-swap-google-fonts.html | 20 + .../font-display-swap-google-fonts.json | 12 + .../font-display-swap-non-critical.html | 30 + .../font-display-swap-non-critical.json | 1 + .../fontDisplay/font-display-swap.html | 25 + .../fontDisplay/font-display-swap.json | 12 + .../missing-fontface-is-ignored.html | 19 + .../missing-fontface-is-ignored.json | 1 + .../no-font-display-non-critical.html | 29 + .../no-font-display-non-critical.json | 1 + .../fontDisplay/no-font-display.html | 24 + .../fontDisplay/no-font-display.json | 11 + .../critical-font-not-preloaded.html | 25 + .../critical-font-not-preloaded.json | 9 + .../critical-font-preloaded.html | 26 + .../critical-font-preloaded.json | 1 + .../test-data/fontPreloading/google-font.html | 20 + .../test-data/fontPreloading/google-font.json | 9 + .../non-critical-font-not-preloaded.html | 30 + .../non-critical-font-not-preloaded.json | 1 + .../non-critical-font-preloaded.html | 31 + .../non-critical-font-preloaded.json | 9 + packages/page-experience/tests/Checks.test.js | 66 + 43 files changed, 2870 insertions(+), 68 deletions(-) delete mode 160000 packages/page-experience create mode 100644 packages/page-experience/README.md create mode 100755 packages/page-experience/bin/cli.js create mode 100644 packages/page-experience/index.js create mode 100644 packages/page-experience/lib/PageDataGatherer.js create mode 100644 packages/page-experience/lib/PageExperienceGuide.js create mode 100644 packages/page-experience/lib/checks/fontDisplay.js create mode 100644 packages/page-experience/lib/checks/fontPreloading.js create mode 100644 packages/page-experience/lib/helpers/parseFontface.js create mode 100644 packages/page-experience/lib/helpers/parseFontface.test.js create mode 100644 packages/page-experience/lib/helpers/parseFontfaceSrc.js create mode 100644 packages/page-experience/lib/helpers/parseFontfaceSrc.test.js create mode 100644 packages/page-experience/package-lock.json create mode 100644 packages/page-experience/package.json create mode 100644 packages/page-experience/prettier.config.js create mode 100644 packages/page-experience/run.js create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-optional.html create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-optional.json create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.html create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.json create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.html create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.json create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-swap.html create mode 100644 packages/page-experience/test-data/fontDisplay/font-display-swap.json create mode 100644 packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.html create mode 100644 packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.json create mode 100644 packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.html create mode 100644 packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.json create mode 100644 packages/page-experience/test-data/fontDisplay/no-font-display.html create mode 100644 packages/page-experience/test-data/fontDisplay/no-font-display.json create mode 100644 packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.html create mode 100644 packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.json create mode 100644 packages/page-experience/test-data/fontPreloading/critical-font-preloaded.html create mode 100644 packages/page-experience/test-data/fontPreloading/critical-font-preloaded.json create mode 100644 packages/page-experience/test-data/fontPreloading/google-font.html create mode 100644 packages/page-experience/test-data/fontPreloading/google-font.json create mode 100644 packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.html create mode 100644 packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.json create mode 100644 packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.html create mode 100644 packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.json create mode 100644 packages/page-experience/tests/Checks.test.js diff --git a/package-lock.json b/package-lock.json index 41b0fa7bd..fbcb22928 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@ampproject/toolbox-linter": "file:packages/linter", "@ampproject/toolbox-optimizer": "file:packages/optimizer", "@ampproject/toolbox-optimizer-express": "file:packages/optimizer-express", + "@ampproject/toolbox-page-experience": "file:packages/page-experience", "@ampproject/toolbox-runtime-fetch": "file:packages/runtime-fetch", "@ampproject/toolbox-runtime-version": "file:packages/runtime-version", "@ampproject/toolbox-script-csp": "file:packages/script-csp", @@ -186,6 +187,10 @@ "resolved": "packages/optimizer-express", "link": true }, + "node_modules/@ampproject/toolbox-page-experience": { + "resolved": "packages/page-experience", + "link": true + }, "node_modules/@ampproject/toolbox-runtime-fetch": { "resolved": "packages/runtime-fetch", "link": true @@ -4389,6 +4394,15 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, + "node_modules/@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "4.16.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.16.1.tgz", @@ -5421,8 +5435,7 @@ "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "node_modules/base64id": { "version": "2.0.0", @@ -5456,6 +5469,16 @@ "node": ">=8" } }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -5687,12 +5710,19 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "engines": { + "node": "*" + } + }, "node_modules/buffer-equal": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", @@ -6138,8 +6168,7 @@ "node_modules/chownr": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "dev": true + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" }, "node_modules/chrome-launcher": { "version": "0.13.4", @@ -7402,14 +7431,6 @@ "node-fetch": "2.6.1" } }, - "node_modules/cross-fetch/node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "engines": { - "node": "4.x || >=6.0.0" - } - }, "node_modules/cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -7451,6 +7472,11 @@ "node": ">=8" } }, + "node_modules/css-font-face-src": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-face-src/-/css-font-face-src-1.0.0.tgz", + "integrity": "sha1-qMYwfUjZr8hKQECCyxd2zUWIZLE=" + }, "node_modules/cssnano-simple": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cssnano-simple/-/cssnano-simple-1.2.2.tgz", @@ -7853,6 +7879,11 @@ "node": ">=8" } }, + "node_modules/devtools-protocol": { + "version": "0.0.847576", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.847576.tgz", + "integrity": "sha512-0M8kobnSQE0Jmly7Mhbeq0W/PpZfnuK+WjN2ZRVPbGqYwCHCioAVp84H0TcLimgECcN5H976y5QiXMGBC9JKmg==" + }, "node_modules/dezalgo": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", @@ -9190,6 +9221,25 @@ "node": ">=0.10.0" } }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -9277,6 +9327,14 @@ "bser": "2.1.1" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/fetch-mock": { "version": "9.11.0", "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz", @@ -9403,7 +9461,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -9583,6 +9640,11 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -11041,8 +11103,7 @@ "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "node_modules/iferr": { "version": "0.1.5", @@ -15290,7 +15351,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -16441,6 +16501,11 @@ "node": ">=10" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "node_modules/mkdirp-promise": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", @@ -16692,10 +16757,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "dev": true, + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "engines": { "node": "4.x || >=6.0.0" } @@ -17726,7 +17790,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, "dependencies": { "p-try": "^2.0.0" }, @@ -17738,7 +17801,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -17807,7 +17869,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "engines": { "node": ">=6" } @@ -18255,7 +18316,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } @@ -18310,6 +18370,11 @@ "node": ">=4" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -18400,7 +18465,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "dependencies": { "find-up": "^4.0.0" }, @@ -18448,6 +18512,38 @@ "node": ">=6.0.0" } }, + "node_modules/postcss-safe-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-5.0.2.tgz", + "integrity": "sha512-jDUfCPJbKOABhwpUKcqCVbbXiloe/QXMcbJ6Iipf3sDIihEzTqRCeMBfRaOHxhBuTYqtASrI1KJWxzztZU4qUQ==", + "dependencies": { + "postcss": "^8.1.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-safe-parser/node_modules/postcss": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.7.tgz", + "integrity": "sha512-DsVLH3xJzut+VT+rYr0mtvOtpTjSyqDwPf5EZWXcb0uAKfitGpTY9Ec+afi2+TgdN8rWS9Cs88UDYehKo/RvOw==", + "dependencies": { + "colorette": "^1.2.2", + "nanoid": "^3.1.20", + "source-map": "^0.6.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "node_modules/postcss/node_modules/supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -18710,7 +18806,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -18807,6 +18902,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/ps-list": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/ps-list/-/ps-list-7.2.0.tgz", @@ -18884,6 +18984,66 @@ "node": ">=8" } }, + "node_modules/puppeteer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-7.1.0.tgz", + "integrity": "sha512-lqOLzqCKdh7yUAHvK6LxgOpQrL8Bv1/jvS8MLDXxcNms2rlM3E8p/Wlwc7efbRZ0twxTzUeqjN5EqrTwxOwc9g==", + "hasInstallScript": true, + "dependencies": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.847576", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "engines": { + "node": ">=10.18.1" + } + }, + "node_modules/puppeteer/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/puppeteer/node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/puppeteer/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -19208,7 +19368,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20925,7 +21084,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", - "dev": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -21565,6 +21723,32 @@ "node": ">= 10" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tar/node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -21943,6 +22127,14 @@ "punycode": "^2.1.0" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/trim-newlines": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", @@ -22146,6 +22338,15 @@ "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=", "dev": true }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/underscore": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", @@ -22422,8 +22623,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "node_modules/util-promisify": { "version": "2.1.0", @@ -22976,7 +23176,6 @@ "version": "7.4.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", - "dev": true, "engines": { "node": ">=8.3.0" } @@ -23184,6 +23383,15 @@ "node": ">=8" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "packages/cache-list": { "name": "@ampproject/toolbox-cache-list", "version": "2.8.0-canary.0", @@ -24479,6 +24687,37 @@ "node": "^10 || ^12 || >=14" } }, + "packages/page-experience": { + "version": "2.8.0-canary.4", + "license": "Apache-2.0", + "dependencies": { + "css-font-face-src": "^1.0.0", + "postcss": "8.2.6", + "postcss-safe-parser": "5.0.2", + "puppeteer": "7.1.0", + "tree-kill": "1.2.2" + }, + "bin": { + "px": "bin/cli.js" + } + }, + "packages/page-experience/node_modules/postcss": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.6.tgz", + "integrity": "sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==", + "dependencies": { + "colorette": "^1.2.1", + "nanoid": "^3.1.20", + "source-map": "^0.6.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, "packages/runtime-fetch": { "name": "@ampproject/toolbox-runtime-fetch", "version": "2.8.0-canary.0", @@ -25755,6 +25994,28 @@ } } }, + "@ampproject/toolbox-page-experience": { + "version": "file:packages/page-experience", + "requires": { + "css-font-face-src": "^1.0.0", + "postcss": "8.2.6", + "postcss-safe-parser": "5.0.2", + "puppeteer": "7.1.0", + "tree-kill": "1.2.2" + }, + "dependencies": { + "postcss": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.6.tgz", + "integrity": "sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==", + "requires": { + "colorette": "^1.2.1", + "nanoid": "^3.1.20", + "source-map": "^0.6.1" + } + } + } + }, "@ampproject/toolbox-runtime-fetch": { "version": "file:packages/runtime-fetch", "requires": { @@ -29553,6 +29814,15 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "optional": true, + "requires": { + "@types/node": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "4.16.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.16.1.tgz", @@ -30337,8 +30607,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "base64id": { "version": "2.0.0", @@ -30366,6 +30635,16 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -30558,12 +30837,16 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, "buffer-equal": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", @@ -30944,8 +31227,7 @@ "chownr": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "dev": true + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" }, "chrome-launcher": { "version": "0.13.4", @@ -32000,13 +32282,6 @@ "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", "requires": { "node-fetch": "2.6.1" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - } } }, "cross-spawn": { @@ -32040,6 +32315,11 @@ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, + "css-font-face-src": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-font-face-src/-/css-font-face-src-1.0.0.tgz", + "integrity": "sha1-qMYwfUjZr8hKQECCyxd2zUWIZLE=" + }, "cssnano-simple": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cssnano-simple/-/cssnano-simple-1.2.2.tgz", @@ -32370,6 +32650,11 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, + "devtools-protocol": { + "version": "0.0.847576", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.847576.tgz", + "integrity": "sha512-0M8kobnSQE0Jmly7Mhbeq0W/PpZfnuK+WjN2ZRVPbGqYwCHCioAVp84H0TcLimgECcN5H976y5QiXMGBC9JKmg==" + }, "dezalgo": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", @@ -33456,6 +33741,17 @@ } } }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -33538,6 +33834,14 @@ "bser": "2.1.1" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, "fetch-mock": { "version": "9.11.0", "resolved": "https://registry.npmjs.org/fetch-mock/-/fetch-mock-9.11.0.tgz", @@ -33642,7 +33946,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -33795,6 +34098,11 @@ } } }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -34942,8 +35250,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "iferr": { "version": "0.1.5", @@ -38392,7 +38699,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, "requires": { "p-locate": "^4.1.0" } @@ -39363,6 +39669,11 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, "mkdirp-promise": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", @@ -39584,10 +39895,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "dev": true + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-fetch-npm": { "version": "2.0.4", @@ -40417,7 +40727,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, "requires": { "p-try": "^2.0.0" } @@ -40426,7 +40735,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, "requires": { "p-limit": "^2.2.0" } @@ -40478,8 +40786,7 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "p-waterfall": { "version": "1.0.0", @@ -40859,8 +41166,7 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" }, "path-is-absolute": { "version": "1.0.1", @@ -40902,6 +41208,11 @@ } } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -40968,7 +41279,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, "requires": { "find-up": "^4.0.0" } @@ -41014,6 +41324,26 @@ } } }, + "postcss-safe-parser": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-5.0.2.tgz", + "integrity": "sha512-jDUfCPJbKOABhwpUKcqCVbbXiloe/QXMcbJ6Iipf3sDIihEzTqRCeMBfRaOHxhBuTYqtASrI1KJWxzztZU4qUQ==", + "requires": { + "postcss": "^8.1.0" + }, + "dependencies": { + "postcss": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.7.tgz", + "integrity": "sha512-DsVLH3xJzut+VT+rYr0mtvOtpTjSyqDwPf5EZWXcb0uAKfitGpTY9Ec+afi2+TgdN8rWS9Cs88UDYehKo/RvOw==", + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.20", + "source-map": "^0.6.1" + } + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -41210,8 +41540,7 @@ "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" }, "promise": { "version": "8.0.1", @@ -41293,6 +41622,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "ps-list": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/ps-list/-/ps-list-7.2.0.tgz", @@ -41360,6 +41694,52 @@ "escape-goat": "^2.0.0" } }, + "puppeteer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-7.1.0.tgz", + "integrity": "sha512-lqOLzqCKdh7yUAHvK6LxgOpQrL8Bv1/jvS8MLDXxcNms2rlM3E8p/Wlwc7efbRZ0twxTzUeqjN5EqrTwxOwc9g==", + "requires": { + "debug": "^4.1.0", + "devtools-protocol": "0.0.847576", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "pkg-dir": "^4.2.0", + "progress": "^2.0.1", + "proxy-from-env": "^1.1.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -41605,7 +41985,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -43000,7 +43379,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -43558,6 +43936,29 @@ } } }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", @@ -43823,6 +44224,11 @@ "punycode": "^2.1.0" } }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" + }, "trim-newlines": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", @@ -43972,6 +44378,15 @@ "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=", "dev": true }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "underscore": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", @@ -44202,8 +44617,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util-promisify": { "version": "2.1.0", @@ -44669,8 +45083,7 @@ "ws": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", - "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", - "dev": true + "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==" }, "xdg-basedir": { "version": "4.0.0", @@ -44840,6 +45253,15 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } } } } diff --git a/package.json b/package.json index 6b939dd15..5124eceb9 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "@ampproject/toolbox-linter": "file:packages/linter", "@ampproject/toolbox-optimizer": "file:packages/optimizer", "@ampproject/toolbox-optimizer-express": "file:packages/optimizer-express", + "@ampproject/toolbox-page-experience": "file:packages/page-experience", "@ampproject/toolbox-runtime-fetch": "file:packages/runtime-fetch", "@ampproject/toolbox-runtime-version": "file:packages/runtime-version", "@ampproject/toolbox-script-csp": "file:packages/script-csp", diff --git a/packages/page-experience b/packages/page-experience deleted file mode 160000 index 4682a57a3..000000000 --- a/packages/page-experience +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4682a57a38e0e8d8c3700d711045e4dbbc57a7b0 diff --git a/packages/page-experience/README.md b/packages/page-experience/README.md new file mode 100644 index 000000000..63e9dd06a --- /dev/null +++ b/packages/page-experience/README.md @@ -0,0 +1,29 @@ +# `page-experience` + +** EXPERIMENTAL ** + +## Usage + +```js +const PageExperienceGuide = require('@ammproject/toolbox-page-experience'); + +(async ()=> { + const pageExperienceGuide = new PageExperienceGuide(); + const result = pageExperienceGuide.analyze('https://amp.dev'); + console.log('result') +}) +``` + +## Development + +Run tests via: + +``` +npm t +``` + +Update check result snapshots via: + +``` +npm run test:snapshot +``` \ No newline at end of file diff --git a/packages/page-experience/bin/cli.js b/packages/page-experience/bin/cli.js new file mode 100755 index 000000000..5bf429127 --- /dev/null +++ b/packages/page-experience/bin/cli.js @@ -0,0 +1,24 @@ +#!/usr/bin/env node +/** + * Copyright 2021 The AMP HTML Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const PageExperienceGuide = require('../lib/PageExperienceGuide'); + +const url = process.argv[2]; + +(async () => { + const result = await new PageExperienceGuide().analyze(url); + console.log(result); +})(); diff --git a/packages/page-experience/index.js b/packages/page-experience/index.js new file mode 100644 index 000000000..0f278f9b4 --- /dev/null +++ b/packages/page-experience/index.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node +/** + * Copyright 2021 The AMP HTML Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const PageExperienceGuide = require('./lib/PageExperienceGuide'); + +module.exports = PageExperienceGuide; diff --git a/packages/page-experience/lib/PageDataGatherer.js b/packages/page-experience/lib/PageDataGatherer.js new file mode 100644 index 000000000..fd3cde9c9 --- /dev/null +++ b/packages/page-experience/lib/PageDataGatherer.js @@ -0,0 +1,203 @@ +/** + * Copyright 2021 The AMP HTML Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const puppeteer = require('puppeteer'); +const treeKill = require('tree-kill'); +const parseFontfaces = require('./helpers/parseFontface'); + +// Pixel 5 XL +const DEFAULT_VIEWPORT = { + width: 393, + height: 851, +}; + +/** + * Renders a page in Puppeteer and collects all data required for the page experience recommendations. + */ +class PageAnalyzer { + constructor(config = {}) { + this.viewport = config.viewport || DEFAULT_VIEWPORT; + this.debug = config.debug || false; + } + + async start() { + this.browser = await puppeteer.launch(); + } + + async execute(url) { + const {page, remoteStyles} = await this.setupPage(); + await page.goto(url, {waitUntil: 'load'}); + return await this.gatherPageData(page, remoteStyles); + } + + async gatherPageData(page, remoteStyles) { + const result = await page.evaluate(async () => { + /* global document, window */ + + /** + * Returns true if the given element is in the first viewport. + * + * @param {Element} el + * @return {boolean} + */ + const isElementInViewport = (el) => { + const rect = el.getBoundingClientRect(); + return ( + rect.top <= (window.innerHeight || document.documentElement.clientHeight) && + rect.left <= (window.innerWidth || document.documentElement.clientWidth) + ); + }; + + /** + * Returns a list of all inline ` + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontDisplay/font-display-optional.json b/packages/page-experience/test-data/fontDisplay/font-display-optional.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/font-display-optional.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.html b/packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.html new file mode 100644 index 000000000..d65654d9b --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.html @@ -0,0 +1,20 @@ + + + + + My AMP Page + + + + + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.json b/packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.json new file mode 100644 index 000000000..6adb12ec2 --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/font-display-swap-google-fonts.json @@ -0,0 +1,12 @@ +{ + "fontDisplay": { + "title": "Avoid content shift caused by font swapping", + "url": "https://web.dev/preload-optional-fonts/", + "message": "Consider loading the critical webfont 'Open Sans' using `font-display: optional` instead of `font-display: swap`. This can avoid content shifts on page load caused by font swapping. Add the `&display=optional` parameter to the end of your Google Fonts URL to enable ([read more](https://web.dev/font-display/#google-fonts)).", + "info": { + "font": "Open Sans", + "fontDisplay": "swap" + }, + "status": "WARN" + } +} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.html b/packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.html new file mode 100644 index 000000000..93f0559f0 --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.html @@ -0,0 +1,30 @@ + + + + + My AMP Page + + + + + + + +
+

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.json b/packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/font-display-swap-non-critical.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontDisplay/font-display-swap.html b/packages/page-experience/test-data/fontDisplay/font-display-swap.html new file mode 100644 index 000000000..8660d749b --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/font-display-swap.html @@ -0,0 +1,25 @@ + + + + + My AMP Page + + + + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontDisplay/font-display-swap.json b/packages/page-experience/test-data/fontDisplay/font-display-swap.json new file mode 100644 index 000000000..f2c40f82e --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/font-display-swap.json @@ -0,0 +1,12 @@ +{ + "fontDisplay": { + "title": "Avoid content shift caused by font swapping", + "url": "https://web.dev/preload-optional-fonts/", + "message": "Consider loading the critical webfont 'Open Sans' using `font-display: optional` instead of `font-display: swap`. This can avoid content shifts on page load caused by font swapping. ", + "info": { + "font": "Open Sans", + "fontDisplay": "swap" + }, + "status": "WARN" + } +} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.html b/packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.html new file mode 100644 index 000000000..d7ac9976a --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.html @@ -0,0 +1,19 @@ + + + + + My AMP Page + + + + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.json b/packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/missing-fontface-is-ignored.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.html b/packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.html new file mode 100644 index 000000000..e1b852262 --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.html @@ -0,0 +1,29 @@ + + + + + My AMP Page + + + + + + + +
+

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.json b/packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/no-font-display-non-critical.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontDisplay/no-font-display.html b/packages/page-experience/test-data/fontDisplay/no-font-display.html new file mode 100644 index 000000000..9f003cda7 --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/no-font-display.html @@ -0,0 +1,24 @@ + + + + + My AMP Page + + + + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontDisplay/no-font-display.json b/packages/page-experience/test-data/fontDisplay/no-font-display.json new file mode 100644 index 000000000..bf15cc40d --- /dev/null +++ b/packages/page-experience/test-data/fontDisplay/no-font-display.json @@ -0,0 +1,11 @@ +{ + "fontDisplay": { + "title": "Improve LCP using `font-display: optional`", + "url": "https://web.dev/optimize-webfont-loading/", + "message": "Load the critical webfont 'Open Sans' using `font-display: optional` to improve LCP on slow connections and avoid content shifts. ", + "info": { + "font": "Open Sans" + }, + "status": "WARN" + } +} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.html b/packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.html new file mode 100644 index 000000000..51635c261 --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.html @@ -0,0 +1,25 @@ + + + + + My AMP Page + + + + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.json b/packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.json new file mode 100644 index 000000000..39fe75fda --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/critical-font-not-preloaded.json @@ -0,0 +1,9 @@ +{ + "fontPreloading": { + "title": "Preload critical fonts", + "url": "https://web.dev/codelab-preload-web-fonts/", + "message": "Critical font 'Open Sans' is not preloaded. Add `` to your ``.", + "info": "", + "status": "FAIL" + } +} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontPreloading/critical-font-preloaded.html b/packages/page-experience/test-data/fontPreloading/critical-font-preloaded.html new file mode 100644 index 000000000..cdedd546d --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/critical-font-preloaded.html @@ -0,0 +1,26 @@ + + + + + My AMP Page + + + + + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontPreloading/critical-font-preloaded.json b/packages/page-experience/test-data/fontPreloading/critical-font-preloaded.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/critical-font-preloaded.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontPreloading/google-font.html b/packages/page-experience/test-data/fontPreloading/google-font.html new file mode 100644 index 000000000..5596614cb --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/google-font.html @@ -0,0 +1,20 @@ + + + + + My AMP Page + + + + + + + + +

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontPreloading/google-font.json b/packages/page-experience/test-data/fontPreloading/google-font.json new file mode 100644 index 000000000..6622f637d --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/google-font.json @@ -0,0 +1,9 @@ +{ + "fontPreloading": { + "title": "Self-host Google Fonts", + "url": "https://www.tunetheweb.com/blog/should-you-self-host-google-fonts/", + "message": "Consider self-hosting and preloading 'Open Sans'. The font is used in the first viewport and self-hosting Google Fonts can improve LCP times. Important: run a few tests first, because if you are not using a CDN or your server response times are slow, this might not have a positive impact.", + "info": "", + "status": "WARN" + } +} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.html b/packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.html new file mode 100644 index 000000000..0b3d5d8d6 --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.html @@ -0,0 +1,30 @@ + + + + + My AMP Page + + + + + + + +
+

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.json b/packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/non-critical-font-not-preloaded.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.html b/packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.html new file mode 100644 index 000000000..99ad1de54 --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.html @@ -0,0 +1,31 @@ + + + + + My AMP Page + + + + + + + + +
+

Hello AMPHTML World!

+ + diff --git a/packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.json b/packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.json new file mode 100644 index 000000000..58221ce55 --- /dev/null +++ b/packages/page-experience/test-data/fontPreloading/non-critical-font-preloaded.json @@ -0,0 +1,9 @@ +{ + "fontPreloading": { + "title": "Preload only critical fonts", + "url": "https://web.dev/optimize-webfont-loading/#preload-your-webfont-resources", + "message": "Avoid preloading non-critical font 'file:///fonts/OpenSans-Regular-webfont.woff2' as it is not used in the first viewport.", + "info": "", + "status": "FAIL" + } +} \ No newline at end of file diff --git a/packages/page-experience/tests/Checks.test.js b/packages/page-experience/tests/Checks.test.js new file mode 100644 index 000000000..a08d8937a --- /dev/null +++ b/packages/page-experience/tests/Checks.test.js @@ -0,0 +1,66 @@ +/** + * Copyright 2021 The AMPHtml Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const fs = require('fs'); +const path = require('path'); +const fileUrl = require('file-url'); + +const PageExperienceGuide = require('../lib/PageExperienceGuide'); +const TEST_DATA_DIR = path.join(__dirname, '../test-data'); +let CREATE_SNAPSHOT = process.env.PAGE_EXPERIENCE_SNAPSHOT; + +const pageExperienceGuide = new PageExperienceGuide(); +let checks; + +beforeAll(async () => { + await pageExperienceGuide.setup(); + checks = pageExperienceGuide.loadChecks(); +}); + +describe('Checks', () => { + const testDirs = fs.readdirSync(TEST_DATA_DIR); + for (const testDir of testDirs) { + const testCaseDir = path.join(TEST_DATA_DIR, testDir); + const testCases = fs.readdirSync(testCaseDir).filter((file) => file.endsWith('.html')); + for (const testCase of testCases) { + test(testCase, async () => { + const check = (await checks).find((check) => check.id === testDir); + if (!check) { + fail(`no check with name ${testDir}`); + return; + } + const url = fileUrl(path.join(testCaseDir, testCase)); + const result = await pageExperienceGuide.runChecks(url, check.id); + const resultString = JSON.stringify(result, null, 2); + const expectedResultPath = path.join(testCaseDir, testCase.replace('.html', '.json')); + if (CREATE_SNAPSHOT) { + fs.promises.writeFile(expectedResultPath, resultString, 'utf-8'); + } else { + let expectedResult; + try { + expectedResult = await fs.promises.readFile(expectedResultPath, 'utf-8'); + } catch (e) { + fail(`No expected results for ${testCase}. Run 'npm run test:snapshot' to generate.`); + } + expect(resultString).toEqual(expectedResult); + } + }); + } + } +}); + +afterAll(async () => { + await pageExperienceGuide.teardown(); +}); From 6ba0f472e173f7196bd64a316f98ce7b141531bb Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Wed, 10 Mar 2021 22:47:25 +0100 Subject: [PATCH 3/5] Addressed comments * Fixed JSDocs * More tests --- .../page-experience/lib/PageDataGatherer.js | 43 ++++++++++++++++--- .../lib/helpers/parseFontface.test.js | 3 ++ packages/page-experience/prettier.config.js | 39 ----------------- 3 files changed, 41 insertions(+), 44 deletions(-) delete mode 100644 packages/page-experience/prettier.config.js diff --git a/packages/page-experience/lib/PageDataGatherer.js b/packages/page-experience/lib/PageDataGatherer.js index fd3cde9c9..68ce23b15 100644 --- a/packages/page-experience/lib/PageDataGatherer.js +++ b/packages/page-experience/lib/PageDataGatherer.js @@ -27,21 +27,55 @@ const DEFAULT_VIEWPORT = { * Renders a page in Puppeteer and collects all data required for the page experience recommendations. */ class PageAnalyzer { + /** + * @param config optional configuration + * @param config.debug enable debug output, default false + * @param config.viewport the viewport size, default Pixel 5XL + */ constructor(config = {}) { this.viewport = config.viewport || DEFAULT_VIEWPORT; this.debug = config.debug || false; + this.started = false; } + /** + * Start puppeteer + */ async start() { this.browser = await puppeteer.launch(); + this.started = true; } + /** + * Load a page in Puppeteer and collect data required by checks. Needs to be called after 'start'. + * + * @param {string} url the URL to analyze + * @return {Object} all data collected on the page + */ async execute(url) { + if (!this.started) { + throw new Error('Puppeteer not running, please call `start` first.'); + } const {page, remoteStyles} = await this.setupPage(); await page.goto(url, {waitUntil: 'load'}); return await this.gatherPageData(page, remoteStyles); } + /** + * Shutdown Puppeteer. + */ + async shutdown() { + try { + await this.browser.close(); + } finally { + treeKill(this.browser.process().pid, 'SIGKILL'); + this.started = false; + } + } + + /** + * @private + */ async gatherPageData(page, remoteStyles) { const result = await page.evaluate(async () => { /* global document, window */ @@ -134,6 +168,7 @@ class PageAnalyzer { }); return { criticalFonts: Array.from(criticalFonts), + // Make sure to remove later discovered critical fonts from the list of non-critical ones nonCriticalFonts: Array.from(nonCriticalFonts).filter((font) => !criticalFonts.has(font)), }; }; @@ -155,6 +190,9 @@ class PageAnalyzer { }; } + /** + * @private + */ async setupPage() { const page = await this.browser.newPage(); const remoteStyles = []; @@ -193,11 +231,6 @@ class PageAnalyzer { remoteStyles, }; } - - async shutdown() { - await this.browser.close(); - treeKill(this.browser.process().pid, 'SIGKILL'); - } } module.exports = PageAnalyzer; diff --git a/packages/page-experience/lib/helpers/parseFontface.test.js b/packages/page-experience/lib/helpers/parseFontface.test.js index 1c0f0681a..58a2f8fb1 100644 --- a/packages/page-experience/lib/helpers/parseFontface.test.js +++ b/packages/page-experience/lib/helpers/parseFontface.test.js @@ -20,6 +20,9 @@ test('parses src', () => { const styles = ` @font-face { font-family: "Open Sans"; + font-style: normal; + font-weight: normal; + unicode-range: U+A,U+20,U+26-29,U+2C-39,U+3F,U+41-59,U+61-69,U+6B-70,U+72-7A,U+A9,U+2019; src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); } `; diff --git a/packages/page-experience/prettier.config.js b/packages/page-experience/prettier.config.js deleted file mode 100644 index c50489f05..000000000 --- a/packages/page-experience/prettier.config.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2019 The AMP HTML Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module.exports = { - bracketSpacing: false, - singleQuote: true, - printWidth: 100, - trailingComma: 'es5', - quoteProps: 'consistent', - jsxSingleQuote: true, - parser: 'typescript', - overrides: [ - { - files: ['.eslintrc', '.prettierrc', '.renovaterc.json', '*.json'], - options: {parser: 'json'}, - }, - { - files: ['*.html'], - options: {parser: 'html'}, - }, - { - files: ['*.md'], - options: {parser: 'markdown'}, - }, - ], -}; From 0a2dd1f1a90440b5be47f7595f0f9d50223c4979 Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Fri, 12 Mar 2021 15:27:03 +0100 Subject: [PATCH 4/5] fix code snippet --- packages/page-experience/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/page-experience/README.md b/packages/page-experience/README.md index 63e9dd06a..8c50c266e 100644 --- a/packages/page-experience/README.md +++ b/packages/page-experience/README.md @@ -11,7 +11,7 @@ const PageExperienceGuide = require('@ammproject/toolbox-page-experience'); const pageExperienceGuide = new PageExperienceGuide(); const result = pageExperienceGuide.analyze('https://amp.dev'); console.log('result') -}) +})() ``` ## Development From 4a34bb5aeda8732ae3f90ccb6a11fa1aea327eab Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Fri, 12 Mar 2021 15:27:10 +0100 Subject: [PATCH 5/5] set mobile viewport --- packages/page-experience/lib/PageDataGatherer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/page-experience/lib/PageDataGatherer.js b/packages/page-experience/lib/PageDataGatherer.js index 68ce23b15..10b8d2b6f 100644 --- a/packages/page-experience/lib/PageDataGatherer.js +++ b/packages/page-experience/lib/PageDataGatherer.js @@ -21,6 +21,8 @@ const parseFontfaces = require('./helpers/parseFontface'); const DEFAULT_VIEWPORT = { width: 393, height: 851, + isMobile: true, + hasTouch: true, }; /** @@ -42,7 +44,9 @@ class PageAnalyzer { * Start puppeteer */ async start() { - this.browser = await puppeteer.launch(); + this.browser = await puppeteer.launch({ + viewport: this.viewport, + }); this.started = true; } @@ -199,7 +203,6 @@ class PageAnalyzer { if (this.debug) { page.on('console', (msg) => console.log('[PAGE LOG] ', msg.text())); } - page.setViewport(this.viewport); page.setRequestInterception(true); // Abort requests not needed for rendering the page