From 3c6026430f30481ab6b9ec4b642bdfdcc85a4c9f Mon Sep 17 00:00:00 2001 From: Saavan Nanavati <66381097+saavannanavati@users.noreply.github.com> Date: Thu, 13 Aug 2020 17:12:21 -0500 Subject: [PATCH] new_audit: add large-javascript-libraries audit (#11096) --- .../test/cli/__snapshots__/index-test.js.snap | 8 + .../audits/large-javascript-libraries.js | 165 ++++++++ lighthouse-core/config/default-config.js | 2 + lighthouse-core/lib/i18n/locales/en-US.json | 15 + lighthouse-core/lib/i18n/locales/en-XL.json | 15 + .../bundlephobia-database.json | 62 +++ .../library-suggestions.js | 21 + .../scripts/generate-bundlephobia-database.js | 187 +++++++++ .../large-javascript-libraries-test.js | 136 +++++++ lighthouse-core/test/results/sample_v2.json | 39 ++ package.json | 1 + types/bundle-phobia-cli/index.d.ts | 40 ++ yarn.lock | 372 +++++++++++++++++- 13 files changed, 1058 insertions(+), 5 deletions(-) create mode 100644 lighthouse-core/audits/large-javascript-libraries.js create mode 100644 lighthouse-core/lib/large-javascript-libraries/bundlephobia-database.json create mode 100644 lighthouse-core/lib/large-javascript-libraries/library-suggestions.js create mode 100644 lighthouse-core/scripts/generate-bundlephobia-database.js create mode 100644 lighthouse-core/test/audits/byte-efficiency/large-javascript-libraries-test.js create mode 100644 types/bundle-phobia-cli/index.d.ts diff --git a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap index e626c54d11..1cdb7207fb 100644 --- a/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap +++ b/lighthouse-cli/test/cli/__snapshots__/index-test.js.snap @@ -168,6 +168,9 @@ Object { Object { "path": "unsized-images", }, + Object { + "path": "large-javascript-libraries", + }, Object { "path": "manual/pwa-cross-browser", }, @@ -1017,6 +1020,11 @@ Object { "id": "unsized-images", "weight": 0, }, + Object { + "group": "diagnostics", + "id": "large-javascript-libraries", + "weight": 0, + }, Object { "id": "network-requests", "weight": 0, diff --git a/lighthouse-core/audits/large-javascript-libraries.js b/lighthouse-core/audits/large-javascript-libraries.js new file mode 100644 index 0000000000..83244d4568 --- /dev/null +++ b/lighthouse-core/audits/large-javascript-libraries.js @@ -0,0 +1,165 @@ +/** + * @license Copyright 2020 The Lighthouse 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. + */ + +/** + * @fileoverview This audit checks a page for any large JS libraries with smaller alternatives. + * These libraries can be replaced with functionally equivalent, smaller ones. + */ + +'use strict'; +/** @typedef {{repository: string, lastScraped: number|'Error', versions: Record}} BundlePhobiaLibrary */ + +/** @typedef {{gzip: number, name: string, repository: string}} MinifiedBundlePhobiaLibrary */ + +/** @type {Record} */ +const libStats = require('../lib/large-javascript-libraries/bundlephobia-database.json'); + +/** @type {Record} */ +const librarySuggestions = require('../lib/large-javascript-libraries/library-suggestions.js') + .suggestions; + +const Audit = require('./audit.js'); +const i18n = require('../lib/i18n/i18n.js'); + +const UIStrings = { + /** Title of a Lighthouse audit that provides detail on large Javascript libraries that are used on the page that have better alternatives. This descriptive title is shown when to users when no known unnecessarily large libraries are detected on the page.*/ + title: 'Avoids large JavaScript libraries with smaller alternatives', + /** Title of a Lighthouse audit that provides detail on large Javascript libraries that are used on the page that have better alternatives. This descriptive title is shown when to users when some known unnecessarily large libraries are detected on the page.*/ + failureTitle: 'Replace unnecessarily large JavaScript libraries', + /** Description of a Lighthouse audit that tells the user why they should care about the large Javascript libraries that have better alternatives. This is displayed after a user expands the section to see more. No character length limits. */ + description: 'Large JavaScript libraries can lead to poor performance. ' + + 'Prefer smaller, functionally equivalent libraries to reduce your bundle size.' + + ' [Learn more](https://developers.google.com/web/fundamentals/performance/webpack/decrease-frontend-size#optimize_dependencies).', + /** Label for a column in a data table. Entries will be names of large JavaScript libraries that could be replaced. */ + columnLibraryName: 'Library', + /** [ICU Syntax] Label for the Large JavaScrip Libraries audit identifying how many large libraries were found. */ + displayValue: `{libraryCount, plural, + =1 {1 large library found} + other {# large libraries found} + }`, +}; + +const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings); + +class LargeJavascriptLibraries extends Audit { + /** + * @return {LH.Audit.Meta} + */ + static get meta() { + return { + id: 'large-javascript-libraries', + title: str_(UIStrings.title), + failureTitle: str_(UIStrings.failureTitle), + description: str_(UIStrings.description), + requiredArtifacts: ['Stacks'], + }; + } + + /** + * @param {LH.Artifacts} artifacts + * @return {LH.Audit.Product} + */ + static audit(artifacts) { + /** @type {Array<{original: MinifiedBundlePhobiaLibrary, suggestions: MinifiedBundlePhobiaLibrary[]}>} */ + const libraryPairings = []; + const detectedLibs = artifacts.Stacks; + + const seenLibraries = new Set(); + + for (const detectedLib of detectedLibs) { + if (!detectedLib.npm || !libStats[detectedLib.npm]) continue; + + const suggestions = librarySuggestions[detectedLib.npm]; + if (!suggestions) continue; + + if (seenLibraries.has(detectedLib.npm)) continue; + seenLibraries.add(detectedLib.npm); + + let version = 'latest'; + if (detectedLib.version && libStats[detectedLib.npm].versions[detectedLib.version]) { + version = detectedLib.version; + } + + const originalLib = libStats[detectedLib.npm].versions[version]; + + /** @type {Array<{name: string, repository: string, gzip: number}>} */ + const smallerSuggestions = []; + for (const suggestion of suggestions) { + if (libStats[suggestion].versions['latest'].gzip >= originalLib.gzip) continue; + + smallerSuggestions.push({ + name: suggestion, + repository: libStats[suggestion].repository, + gzip: libStats[suggestion].versions['latest'].gzip, + }); + } + + smallerSuggestions.sort((a, b) => a.gzip - b.gzip); + if (!smallerSuggestions.length) continue; + + libraryPairings.push({ + original: { + gzip: originalLib.gzip, + name: detectedLib.npm, + repository: libStats[detectedLib.npm].repository, + }, + suggestions: smallerSuggestions, + }); + } + + /** @type {LH.Audit.Details.Table['items']} */ + const tableDetails = libraryPairings.map(libraryPairing => { + const original = libraryPairing.original; + const suggestions = libraryPairing.suggestions; + const suggestionItems = suggestions.map(suggestion => { + return { + suggestion: /** @type {LH.Audit.Details.LinkValue} */ ({ + type: 'link', + text: suggestion.name, + url: suggestion.repository, + }), + transferSize: suggestion.gzip, + wastedBytes: original.gzip - suggestion.gzip, + }; + }); + + return { + name: /** @type {LH.Audit.Details.TableItem} */ { + type: 'link', + text: original.name, + url: original.repository, + }, + transferSize: original.gzip, + subItems: /** @type {LH.Audit.Details.TableSubItems} */ ({ + type: 'subitems', + items: suggestionItems, + }), + }; + }); + + /** @type {LH.Audit.Details.TableColumnHeading[]} */ + const headings = [ + /* eslint-disable max-len */ + {key: 'name', itemType: 'text', subItemsHeading: {key: 'suggestion'}, text: str_(UIStrings.columnLibraryName)}, + {key: 'transferSize', itemType: 'bytes', subItemsHeading: {key: 'transferSize'}, text: str_(i18n.UIStrings.columnTransferSize)}, + {key: null, itemType: 'bytes', subItemsHeading: {key: 'wastedBytes'}, text: str_(i18n.UIStrings.columnWastedBytes)}, + /* eslint-enable max-len */ + ]; + + const displayValue = str_(UIStrings.displayValue, {libraryCount: tableDetails.length}); + + const details = Audit.makeTableDetails(headings, tableDetails, {}); + + return { + score: libraryPairings.length > 0 ? 0 : 1, + displayValue, + details, + }; + } +} + +module.exports = LargeJavascriptLibraries; +module.exports.UIStrings = UIStrings; diff --git a/lighthouse-core/config/default-config.js b/lighthouse-core/config/default-config.js index 0a60914b7c..80468cf491 100644 --- a/lighthouse-core/config/default-config.js +++ b/lighthouse-core/config/default-config.js @@ -241,6 +241,7 @@ const defaultConfig = { 'no-unload-listeners', 'non-composited-animations', 'unsized-images', + 'large-javascript-libraries', 'manual/pwa-cross-browser', 'manual/pwa-page-transitions', 'manual/pwa-each-page-has-url', @@ -472,6 +473,7 @@ const defaultConfig = { {id: 'long-tasks', weight: 0, group: 'diagnostics'}, {id: 'non-composited-animations', weight: 0, group: 'diagnostics'}, {id: 'unsized-images', weight: 0, group: 'diagnostics'}, + {id: 'large-javascript-libraries', weight: 0, group: 'diagnostics'}, // Audits past this point don't belong to a group and will not be shown automatically {id: 'network-requests', weight: 0}, {id: 'network-rtt', weight: 0}, diff --git a/lighthouse-core/lib/i18n/locales/en-US.json b/lighthouse-core/lib/i18n/locales/en-US.json index e52c80dd0b..802d2329c7 100644 --- a/lighthouse-core/lib/i18n/locales/en-US.json +++ b/lighthouse-core/lib/i18n/locales/en-US.json @@ -824,6 +824,21 @@ "lighthouse-core/audits/is-on-https.js | warning": { "message": "Allowed with warning" }, + "lighthouse-core/audits/large-javascript-libraries.js | columnLibraryName": { + "message": "Library" + }, + "lighthouse-core/audits/large-javascript-libraries.js | description": { + "message": "Large JavaScript libraries can lead to poor performance. Prefer smaller, functionally equivalent libraries to reduce your bundle size. [Learn more](https://developers.google.com/web/fundamentals/performance/webpack/decrease-frontend-size#optimize_dependencies)." + }, + "lighthouse-core/audits/large-javascript-libraries.js | displayValue": { + "message": "{libraryCount, plural,\n =1 {1 large library found}\n other {# large libraries found}\n }" + }, + "lighthouse-core/audits/large-javascript-libraries.js | failureTitle": { + "message": "Replace unnecessarily large JavaScript libraries" + }, + "lighthouse-core/audits/large-javascript-libraries.js | title": { + "message": "Avoids large JavaScript libraries with smaller alternatives" + }, "lighthouse-core/audits/largest-contentful-paint-element.js | description": { "message": "This is the largest contentful element painted within the viewport. [Learn More](https://web.dev/lighthouse-largest-contentful-paint/)" }, diff --git a/lighthouse-core/lib/i18n/locales/en-XL.json b/lighthouse-core/lib/i18n/locales/en-XL.json index 2adc04a0f9..4021780a3f 100644 --- a/lighthouse-core/lib/i18n/locales/en-XL.json +++ b/lighthouse-core/lib/i18n/locales/en-XL.json @@ -824,6 +824,21 @@ "lighthouse-core/audits/is-on-https.js | warning": { "message": "Âĺl̂óŵéd̂ ẃît́ĥ ẃâŕn̂ín̂ǵ" }, + "lighthouse-core/audits/large-javascript-libraries.js | columnLibraryName": { + "message": "L̂íb̂ŕâŕŷ" + }, + "lighthouse-core/audits/large-javascript-libraries.js | description": { + "message": "L̂ár̂ǵê J́âv́âŚĉŕîṕt̂ ĺîb́r̂ár̂íêś ĉán̂ ĺêád̂ t́ô ṕôór̂ ṕêŕf̂ór̂ḿâńĉé. P̂ŕêf́êŕ ŝḿâĺl̂ér̂, f́ûńĉt́îón̂ál̂ĺŷ éq̂úîv́âĺêńt̂ ĺîb́r̂ár̂íêś t̂ó r̂éd̂úĉé ŷóûŕ b̂ún̂d́l̂é ŝíẑé. [L̂éâŕn̂ ḿôŕê](https://developers.google.com/web/fundamentals/performance/webpack/decrease-frontend-size#optimize_dependencies)." + }, + "lighthouse-core/audits/large-javascript-libraries.js | displayValue": { + "message": "{libraryCount, plural,\n =1 {1 l̂ár̂ǵê ĺîb́r̂ár̂ý f̂óûńd̂}\n other {# ĺâŕĝé l̂íb̂ŕâŕîéŝ f́ôún̂d́}\n }" + }, + "lighthouse-core/audits/large-javascript-libraries.js | failureTitle": { + "message": "R̂ép̂ĺâćê ún̂ńêćêśŝár̂íl̂ý l̂ár̂ǵê J́âv́âŚĉŕîṕt̂ ĺîb́r̂ár̂íêś" + }, + "lighthouse-core/audits/large-javascript-libraries.js | title": { + "message": "Âv́ôíd̂ś l̂ár̂ǵê J́âv́âŚĉŕîṕt̂ ĺîb́r̂ár̂íêś ŵít̂h́ ŝḿâĺl̂ér̂ ál̂t́êŕn̂át̂ív̂éŝ" + }, "lighthouse-core/audits/largest-contentful-paint-element.js | description": { "message": "T̂h́îś îś t̂h́ê ĺâŕĝéŝt́ ĉón̂t́êńt̂f́ûĺ êĺêḿêńt̂ ṕâín̂t́êd́ ŵít̂h́îń t̂h́ê v́îéŵṕôŕt̂. [Ĺêár̂ń M̂ór̂é](https://web.dev/lighthouse-largest-contentful-paint/)" }, diff --git a/lighthouse-core/lib/large-javascript-libraries/bundlephobia-database.json b/lighthouse-core/lib/large-javascript-libraries/bundlephobia-database.json new file mode 100644 index 0000000000..d926d4d37f --- /dev/null +++ b/lighthouse-core/lib/large-javascript-libraries/bundlephobia-database.json @@ -0,0 +1,62 @@ +{ + "moment": { + "repository": "https://github.com/moment/moment.git", + "lastScraped": 1597344573775, + "versions": { + "latest": { + "gzip": 72054 + }, + "2.25.3": { + "gzip": 71090 + }, + "2.25.2": { + "gzip": 71119 + }, + "2.25.1": { + "gzip": 20811 + }, + "2.25.0": { + "gzip": 20876 + }, + "2.24.0": { + "gzip": 67489 + }, + "2.23.0": { + "gzip": 66675 + }, + "2.22.2": { + "gzip": 65962 + }, + "2.22.1": { + "gzip": 65949 + } + } + }, + "date-fns": { + "repository": "https://github.com/date-fns/date-fns.git", + "lastScraped": 1597344603463, + "versions": { + "latest": { + "gzip": 18463 + } + } + }, + "luxon": { + "repository": "https://github.com/moment/luxon.git", + "lastScraped": 1597344608583, + "versions": { + "latest": { + "gzip": 20912 + } + } + }, + "dayjs": { + "repository": "https://github.com/iamkun/dayjs.git", + "lastScraped": 1597344613371, + "versions": { + "latest": { + "gzip": 2856 + } + } + } +} \ No newline at end of file diff --git a/lighthouse-core/lib/large-javascript-libraries/library-suggestions.js b/lighthouse-core/lib/large-javascript-libraries/library-suggestions.js new file mode 100644 index 0000000000..a66c5cba75 --- /dev/null +++ b/lighthouse-core/lib/large-javascript-libraries/library-suggestions.js @@ -0,0 +1,21 @@ +/** + * @license Copyright 2020 The Lighthouse 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. + */ + +/** + * @fileoverview This file contains a mapping of large JavaScript libraries to smaller alternatives. + * These suggestions have been cherry-picked from BundlePhobia's open-source list of recommendations which can be found here (https://github.com/pastelsky/bundlephobia/blob/b244a53bc55af067bb0edfa3ace867c87fec17e7/server/middlewares/similar-packages/fixtures.js). + * Googlers: see here (https://docs.google.com/document/d/1TgKO3cWqMpcS4dn0xbjDG5fyuqgVvYYoXg--knaxJnM/edit?usp=sharing). + */ + +'use strict'; + +/** @type {Record} */ +const suggestions = { + // general-purpose-date-time + 'moment': ['date-fns', 'luxon', 'dayjs'], +}; + +module.exports = {suggestions}; diff --git a/lighthouse-core/scripts/generate-bundlephobia-database.js b/lighthouse-core/scripts/generate-bundlephobia-database.js new file mode 100644 index 0000000000..5572deb94f --- /dev/null +++ b/lighthouse-core/scripts/generate-bundlephobia-database.js @@ -0,0 +1,187 @@ +/** + * @license Copyright 2020 The Lighthouse 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. + */ + +/** + * @fileoverview This script generates a database of library statistics required for + * the large-javascript-libraries audit. The data is scraped from BundlePhobia and + * includes things like the library transfer size, and GitHub URL for each version of + * a library. This script must be run ever so often to keep the database up-to-date. + */ + +'use strict'; + +/* eslint-disable no-console */ + +/** @typedef {import('bundle-phobia-cli').BundlePhobiaLibrary} BundlePhobiaLibrary */ + +const fs = require('fs'); +const path = require('path'); +const getPackageVersionList = require('bundle-phobia-cli').fetchPackageStats.getPackageVersionList; +const fetchPackageStats = require('bundle-phobia-cli').fetchPackageStats.fetchPackageStats; +const databasePath = path.join(__dirname, + '../lib/large-javascript-libraries/bundlephobia-database.json'); + +/** @type {Record} */ +const suggestionsJSON = require('../lib/large-javascript-libraries/library-suggestions.js') + .suggestions; + +/** @type {string[]} */ +const largeLibraries = Object.keys(suggestionsJSON); + +/** @type {string[]} */ +const suggestedLibraries = Object.values( + suggestionsJSON).reduce((arr, lib) => arr.concat(lib), []); + +const totalLibraries = largeLibraries.length + suggestedLibraries.length; + +/** @type {Record} */ +let database = {}; +if (fs.existsSync(databasePath)) { + database = require(databasePath); +} + +/** + * Returns true if this library has been scraped from BundlePhobia in the past hour. + * This is used to rate-limit the number of network requests we make to BundlePhobia. + * @param {string} library + * @return {boolean} + */ +function hasBeenRecentlyScraped(library) { + if (!database[library]) return false; + + const lastScraped = database[library].lastScraped; + if (lastScraped === 'Error') return false; + + return (Date.now() - lastScraped) / (1000 * 60 * 60) < 1; +} + +/** + * Returns true if the object represents valid BundlePhobia JSON. + * The version string must not match this false-positive expression: '{number} packages'. + * @param {any} library + * @return {library is BundlePhobiaLibrary} + */ +function validateLibraryObject(library) { + return library.hasOwnProperty('name') && + library.hasOwnProperty('size') && + library.hasOwnProperty('gzip') && + library.hasOwnProperty('description') && + library.hasOwnProperty('repository') && + library.hasOwnProperty('version') && + !library.version.match(/^([0-9]+) packages$/); +} + +/** + * Save BundlePhobia stats for a given npm library to the database. + * @param {string} library + * @param {number} index + * @param {number} numVersionsToFetchLimit + */ +async function collectLibraryStats(library, index, numVersionsToFetchLimit) { + console.log(`\n◉ (${index}/${totalLibraries}) ${library} `); + + if (hasBeenRecentlyScraped(library)) { + console.log(` ❕ Skipping`); + return; + } + + /** @type {Array} */ + const libraries = []; + /** @type {'Error'|number} */ + let lastScraped = Date.now(); + + const versions = await getPackageVersionList(library, numVersionsToFetchLimit); + for (const version of versions) { + try { + const libraryJSON = await fetchPackageStats(version); + if (validateLibraryObject(libraryJSON)) libraries.push(libraryJSON); + } catch (e) { + console.log(` ❌ Failed to fetch stats | ${version}`); + lastScraped = 'Error'; + } + } + + for (let index = 0; index < libraries.length; index++) { + const library = libraries[index]; + + if (index === 0) { + database[library.name] = { + repository: library.repository, + lastScraped, + versions: {}, + }; + } + + if (index === 0 || + // Only include the version information if it's sufficiently different from latest. + Math.abs(library.gzip - database[library.name].versions['latest'].gzip) > 512) { + database[library.name] = { + ...database[library.name], + versions: { + ...database[library.name].versions, + [library.version]: { + gzip: library.gzip, + }, + }, + }; + } + + if (index === 0) { + database[library.name].versions['latest'] = + database[library.name].versions[library.version]; + delete database[library.name].versions[library.version]; + } + + if (lastScraped === 'Error') { + database[library.name] = { + ...database[library.name], + lastScraped, + }; + } + + console.log(` ✔ ${library.version}` + (index === 0 ? ' (latest)' : '')); + } +} + +(async () => { + const startTime = new Date(); + console.log(`Collecting ${totalLibraries} libraries...`); + + // Fetch up to 10 versions of the large libraries + let foundError = false; + for (let i = 0; i < largeLibraries.length; i++) { + try { + await collectLibraryStats(largeLibraries[i], i + 1, 10); + } catch (err) { + console.log(`Exiting early...\n | ${err}`); + foundError = true; + break; + } + } + + // Fetch only the latest version of the suggested libraries + if (!foundError) { + for (let i = 0; i < suggestedLibraries.length; i++) { + try { + const index = i + 1 + largeLibraries.length; + await collectLibraryStats(suggestedLibraries[i], index, 1); + } catch (err) { + console.log(`Exiting early...\n | ${err}`); + break; + } + } + } + + console.log(`\n◉ Saving database to ${databasePath}...`); + fs.writeFile(databasePath, JSON.stringify(database, null, 2), (err) => { + if (err) { + console.log(` ❌ Failed saving | ${err}`); + } else { + console.log(` ✔ Done!`); + } + console.log(`\nElapsed Time: ${(new Date().getTime() - startTime.getTime()) / 1000}`); + }); +})(); diff --git a/lighthouse-core/test/audits/byte-efficiency/large-javascript-libraries-test.js b/lighthouse-core/test/audits/byte-efficiency/large-javascript-libraries-test.js new file mode 100644 index 0000000000..2ee5718658 --- /dev/null +++ b/lighthouse-core/test/audits/byte-efficiency/large-javascript-libraries-test.js @@ -0,0 +1,136 @@ +/** + * @license Copyright 2020 The Lighthouse 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. + */ +'use strict'; + +/* eslint-disable max-len */ +const LargeJavaScriptLibrariesAudit = require('../../../audits/large-javascript-libraries.js'); +const assert = require('assert').strict; +const libStats = require('../../../lib/large-javascript-libraries/bundlephobia-database.json'); +const librarySuggestions = require('../../../lib/large-javascript-libraries/library-suggestions.js').suggestions; +/* eslint-enable max-len */ + +/** + * @param {string} detector + * @param {string} id + * @param {string} name + * @param {string} version + * @param {string} npm + * @return {object} + */ +function makeStack(detector, id, name, version, npm) { + return { + detector, + id, + name, + version, + npm, + }; +} + +/* eslint-env jest */ + +describe('Large JavaScript libraries audit', () => { + it('passes when no libraries were detected', () => { + const auditResult = LargeJavaScriptLibrariesAudit.audit({ + Stacks: [], + }); + assert.equal(auditResult.score, 1); + assert.equal(auditResult.details.items.length, 0); + }); + + it('passes when none of the detected libraries exist in the database', () => { + const auditResult = LargeJavaScriptLibrariesAudit.audit({ + Stacks: [ + makeStack('js', 'fakeLibrary1', 'FakeLibrary1', '1.0.0', 'FakeLibrary1'), + makeStack('css', 'fakeLibrary2', 'FakeLibrary2', '2.0.0', 'FakeLibrary2'), + ], + }); + + assert.equal(auditResult.score, 1); + assert.equal(auditResult.details.items.length, 0); + }); + + it('fails when a detected library is found in the database and gives suggestions', () => { + const auditResult = LargeJavaScriptLibrariesAudit.audit({ + Stacks: [ + makeStack('js', 'fakeLibrary1', 'FakeLibrary1', '1.0.0', 'FakeLibrary1'), + makeStack('js', 'momentjs', 'Moment.js', '2.27.0', 'moment'), + ], + }); + + assert.equal(auditResult.score, 0); + assert.equal(auditResult.details.items.length, 1); + assert.equal(auditResult.details.items[0].subItems.items.length, 3); + }); + + it('falls back to the latest version of a library if its version is unknown', () => { + const auditResult = LargeJavaScriptLibrariesAudit.audit({ + Stacks: [ + makeStack('js', 'momentjs', 'Moment.js', '0', 'moment'), + ], + }); + + assert.equal(auditResult.details.items[0].transferSize, + libStats['moment'].versions['latest'].gzip); + }); + + it('does not provide duplicate suggestions when a library appears twice', () => { + const auditResult = LargeJavaScriptLibrariesAudit.audit({ + Stacks: [ + makeStack('js', 'momentjs', 'Moment.js', '2.27.0', 'moment'), + makeStack('js', 'momentjs', 'Moment.js', '1.0.0', 'moment'), + ], + }); + + assert.equal(auditResult.score, 0); + assert.equal(auditResult.details.items.length, 1); + assert.equal(auditResult.details.items[0].subItems.items.length, 3); + }); + + it('gives suggestions in order of ascending KiB size', () => { + const auditResult = LargeJavaScriptLibrariesAudit.audit({ + Stacks: [ + makeStack('js', 'momentjs', 'Moment.js', '2.27.0', 'moment'), + ], + }); + + const subItems = auditResult.details.items[0].subItems.items; + + for (let i = 0; i < subItems.length - 1; i++) { + if (subItems[i].transferSize > subItems[i + 1].transferSize) { + assert.fail('Suggestions are not in ascending order'); + } + } + }); + + it('uses a BundlePhobiaStats database that contains all necessary libraries', () => { + const libraries = []. + concat(...Object.values(librarySuggestions)). + concat(Object.keys(librarySuggestions)); + + for (const library of libraries) { + if (!libStats[library] || !libStats[library].versions['latest']) { + assert.fail('The library "' + library + '" does not have any stats. ' + + 'Please re-run the generate-bundlephobia-database script ' + + 'to keep the database up-to-date.'); + } + } + }); + + it('uses a BundlePhobiaStats database that does not contain errors', () => { + const libraries = []. + concat(...Object.values(librarySuggestions)). + concat(Object.keys(librarySuggestions)); + + for (const library of libraries) { + if (libStats[library].lastScraped === 'Error') { + assert.fail('The library "' + library + '" encountered an error when scraping recently. ' + + 'Please re-run the generate-bundlephobia-database script to fix this, or check if ' + + 'the library no longer exists on npm (in which case remove it from our list).'); + } + } + }); +}); diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index 9dbfae8428..aa697db91b 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -1918,6 +1918,20 @@ ] } }, + "large-javascript-libraries": { + "id": "large-javascript-libraries", + "title": "Avoids large JavaScript libraries with smaller alternatives", + "description": "Large JavaScript libraries can lead to poor performance. Prefer smaller, functionally equivalent libraries to reduce your bundle size. [Learn more](https://developers.google.com/web/fundamentals/performance/webpack/decrease-frontend-size#optimize_dependencies).", + "score": 1, + "scoreDisplayMode": "binary", + "displayValue": "0 large libraries found", + "details": { + "type": "table", + "headings": [], + "items": [], + "summary": {} + } + }, "pwa-cross-browser": { "id": "pwa-cross-browser", "title": "Site works cross-browser", @@ -4388,6 +4402,11 @@ "weight": 0, "group": "diagnostics" }, + { + "id": "large-javascript-libraries", + "weight": 0, + "group": "diagnostics" + }, { "id": "network-requests", "weight": 0 @@ -5571,6 +5590,12 @@ "duration": 100, "entryType": "measure" }, + { + "startTime": 0, + "name": "lh:audit:large-javascript-libraries", + "duration": 100, + "entryType": "measure" + }, { "startTime": 0, "name": "lh:audit:pwa-cross-browser", @@ -6870,6 +6895,20 @@ "audits[object-alt].details.headings[0].text", "audits[password-inputs-can-be-pasted-into].details.headings[0].text" ], + "lighthouse-core/audits/large-javascript-libraries.js | title": [ + "audits[large-javascript-libraries].title" + ], + "lighthouse-core/audits/large-javascript-libraries.js | description": [ + "audits[large-javascript-libraries].description" + ], + "lighthouse-core/audits/large-javascript-libraries.js | displayValue": [ + { + "values": { + "libraryCount": 0 + }, + "path": "audits[large-javascript-libraries].displayValue" + } + ], "lighthouse-core/audits/manual/pwa-cross-browser.js | title": [ "audits[pwa-cross-browser].title" ], diff --git a/package.json b/package.json index 229cb78a77..7ebfa3eecf 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,7 @@ "babel-plugin-syntax-async-generators": "^6.13.0", "babel-plugin-syntax-object-rest-spread": "^6.13.0", "browserify": "^16.2.3", + "bundle-phobia-cli": "^0.14.6", "chalk": "^2.4.1", "chrome-devtools-frontend": "1.0.727089", "codecov": "^3.7.0", diff --git a/types/bundle-phobia-cli/index.d.ts b/types/bundle-phobia-cli/index.d.ts new file mode 100644 index 0000000000..8482bc7dc2 --- /dev/null +++ b/types/bundle-phobia-cli/index.d.ts @@ -0,0 +1,40 @@ +/** + * @license Copyright 2020 The Lighthouse 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. + */ + +declare module 'bundle-phobia-cli' { + interface Assets { + gzip: number; + name: string; + size: number; + type: string; + } + + interface DependencySize { + approximateSize: number; + name: string; + } + + interface BundlePhobiaLibrary { + assets: Assets[]; + dependencyCount: number; + dependencySizes: DependencySize[]; + description: string; + gzip: number; + hasJSModule: boolean; + hasJSNext: boolean; + hasSideEffects: boolean; + name: string; + repository: string; + scoped: boolean; + size: number; + version: string; + } + + export namespace fetchPackageStats { + export function getPackageVersionList(packageName: string, limit: number): string[]; + export function fetchPackageStats(packageName: string): Array; + } +} diff --git a/yarn.lock b/yarn.lock index 45d98597b3..f54b24f933 100644 --- a/yarn.lock +++ b/yarn.lock @@ -658,6 +658,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.15.9.tgz#656b2f61ebe6af278769dfc24e6ab00528a373eb" integrity sha512-NcOiyA/gxMAounNa4IPm/e13kYqU48onEarMnbLzz3ynEdlxFKYFoBbMBSefAHJR77r9MCtD88J0Z2TVtNsBbw== +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + "@types/raven@^2.5.1": version "2.5.1" resolved "https://registry.yarnpkg.com/@types/raven/-/raven-2.5.1.tgz#62ef0a59e29691945e1f295b62ed199619cbd9b6" @@ -948,6 +953,14 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +aggregate-error@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" + integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-keywords@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0" @@ -985,6 +998,13 @@ ansi-escapes@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" integrity sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ== +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1226,6 +1246,13 @@ async@^2.0.0, async@^2.6.1: dependencies: lodash "^4.17.10" +async@^2.5.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + async@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" @@ -1723,6 +1750,34 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + +bundle-phobia-cli@^0.14.6: + version "0.14.6" + resolved "https://registry.yarnpkg.com/bundle-phobia-cli/-/bundle-phobia-cli-0.14.6.tgz#5048eca13a25d76046b137a11e19e81a8b61d55d" + integrity sha512-aRvmMndA7ZPx4rs2OpgUb7OV2vxmXAsJayJIFes8VWlcJJKt1cMbVORbutJZEumu1ub9YzHauki3miwtbFVHGQ== + dependencies: + bytes "^3.1.0" + chalk "^3.0.0" + inquirer "^7.1.0" + lodash "^4.17.15" + node-fetch "^2.6.0" + ora "^4.0.3" + p-map "^3.0.0" + read-pkg-up "^7.0.1" + resolve-package-json "^1.4.0" + shelljs "^0.8.3" + update-notifier "^4.1.0" + yargs "^15.3.1" + +bytes@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -1870,6 +1925,19 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + chownr@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" @@ -1920,6 +1988,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-boxes@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" @@ -1939,11 +2012,33 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" + integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== + cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" integrity sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao= +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +client-request@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/client-request/-/client-request-2.3.0.tgz#c4604f71f81c94b280659579a17bbc0446fec212" + integrity sha1-xGBPcfgclLKAZZV5oXu8BEb+whI= + cliui@^3.0.3: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -1987,6 +2082,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2605,7 +2705,7 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.1.0, debug@^3.2.6: +debug@^3.0.1, debug@^3.1.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -2646,6 +2746,13 @@ default-require-extensions@^2.0.0: dependencies: strip-bom "^3.0.0" +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -3248,6 +3355,15 @@ external-editor@^2.0.4: jschardet "^1.4.2" tmp "^0.0.33" +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -3338,6 +3454,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -3902,6 +4025,11 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== +hosted-git-info@^2.4.2: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -3971,7 +4099,7 @@ humanize-url@^1.0.0: normalize-url "^1.0.0" strip-url-auth "^1.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -4038,6 +4166,11 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -4118,6 +4251,25 @@ inquirer@^3.0.6, inquirer@^3.3.0: strip-ansi "^4.0.0" through "^2.3.6" +inquirer@^7.1.0: + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.19" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.6.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + insert-module-globals@^7.0.0: version "7.2.0" resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.0.tgz#ec87e5b42728479e327bd5c5c71611ddfb4752ba" @@ -4134,6 +4286,11 @@ insert-module-globals@^7.0.0: undeclared-identifiers "^1.1.2" xtend "^4.0.0" +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + intl-messageformat-parser@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.8.1.tgz#0eb14c5618333be4c95c409457b66c8c33ddcc01" @@ -4331,6 +4488,11 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" @@ -5256,6 +5418,11 @@ lighthouse-plugin-publisher-ads@^1.1.0-beta.0: intl-messageformat "^4.1.2" yargs "^13.3.0" +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -5382,11 +5549,23 @@ lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + log-driver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== +log-symbols@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + lookup-closest-locale@6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/lookup-closest-locale/-/lookup-closest-locale-6.0.4.tgz#1279fed7546a601647bbc980f64423ee990a8590" @@ -5614,6 +5793,11 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" integrity sha1-5md4PZLonb00KBi1IwudYqZyrRg= +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -5722,6 +5906,11 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + nan@^2.12.1: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -5792,6 +5981,11 @@ node-fetch@^2.2.0: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== +node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + node-forge@^0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" @@ -5842,7 +6036,7 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" -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.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -5884,6 +6078,16 @@ npm-bundled@^1.0.1: resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== +npm-package-arg@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-5.1.2.tgz#fb18d17bb61e60900d6312619919bd753755ab37" + integrity sha512-wJBsrf0qpypPT7A0LART18hCdyhpCMxeTtcb0X4IZO2jsP6Om7EHN1d9KSKiqD+KVH030RVNpWS9thk+pb7wzA== + dependencies: + hosted-git-info "^2.4.2" + osenv "^0.1.4" + semver "^5.1.0" + validate-npm-package-name "^3.0.0" + npm-packlist@^1.1.6: version "1.4.6" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4" @@ -6029,6 +6233,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + open@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" @@ -6056,6 +6267,20 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" +ora@^4.0.3: + version "4.0.5" + resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.5.tgz#7410b5cc2d99fa637fd5099bbb9f02bfbb5a361e" + integrity sha512-jCDgm9DqvRcNIAEv2wZPrh7E5PcQiDUnbnWbAfu4NGAE2ZNqPFbDixmWldy1YG2QfLeQhuiu6/h5VRrk6cG50w== + dependencies: + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-spinners "^2.2.0" + is-interactive "^1.0.0" + log-symbols "^3.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + os-browserify@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -6155,6 +6380,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + p-reduce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" @@ -6241,6 +6473,16 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" +parse-json@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" + integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + lines-and-columns "^1.1.6" + parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" @@ -6664,6 +6906,15 @@ read-pkg-up@^4.0.0: find-up "^3.0.0" read-pkg "^3.0.0" +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + read-pkg@^1.0.0, read-pkg@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -6682,6 +6933,16 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -6715,6 +6976,13 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -6871,6 +7139,18 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-package-json@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve-package-json/-/resolve-package-json-1.4.0.tgz#3f3373c5b7fb61fa078ba3cc936cf3e70bd23c7d" + integrity sha512-9UJjpAS+zjRP3C0NJuW5YqHxvd5bIZ+EKnxrhlVvVdEh2p6hQQ23Hg+O5Qqir2uEWAfealWE+ODIYoJF25CuRg== + dependencies: + async "^2.5.0" + client-request "^2.2.0" + debug "^3.0.1" + npm-package-arg "^5.1.2" + semver "^5.4.1" + which "^1.3.0" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -6888,6 +7168,13 @@ resolve@^1.1.4, resolve@^1.1.5, resolve@^1.3.2, resolve@^1.4.0: dependencies: path-parse "^1.0.6" +resolve@^1.1.6: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + resolve@^1.10.0: version "1.15.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" @@ -6918,6 +7205,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -6967,6 +7262,11 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + rx-lite-aggregates@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" @@ -6984,6 +7284,13 @@ rx@^4.1.0: resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I= +rxjs@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84" + integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg== + dependencies: + tslib "^1.9.0" + safe-buffer@^5.0.1, safe-buffer@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" @@ -7052,7 +7359,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.4.1: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -7137,6 +7444,15 @@ shell-quote@^1.6.1: array-reduce "~0.0.0" jsonify "~0.0.0" +shelljs@^0.8.3: + version "0.8.4" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" + integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -7908,6 +8224,11 @@ tslib@1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== +tslib@^1.9.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + tty-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" @@ -7932,6 +8253,16 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -8129,6 +8460,13 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validate-npm-package-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + dependencies: + builtins "^1.0.3" + verror@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" @@ -8160,6 +8498,13 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -8421,7 +8766,7 @@ yargs-parser@^13.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.1, yargs-parser@^18.1.3: +yargs-parser@^18.1.1, yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -8493,6 +8838,23 @@ yargs@^15.0.0: y18n "^4.0.0" yargs-parser "^18.1.1" +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"