[7.x] [kbn/optimizer] report sizes of assets produced by optimizer (#71319) (#71878)

Co-authored-by: spalger <spalger@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Spencer 2020-07-15 10:09:15 -07:00 committed by GitHub
parent ec103d978c
commit c184115288
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 87 additions and 2996 deletions

1
Jenkinsfile vendored
View file

@ -42,7 +42,6 @@ kibanaPipeline(timeoutMinutes: 155, checkPrChanges: true, setCommitStatus: true)
'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10),
'xpack-accessibility': kibanaPipeline.functionalTestProcess('xpack-accessibility', './test/scripts/jenkins_xpack_accessibility.sh'),
'xpack-savedObjectsFieldMetrics': kibanaPipeline.functionalTestProcess('xpack-savedObjectsFieldMetrics', './test/scripts/jenkins_xpack_saved_objects_field_metrics.sh'),
// 'xpack-pageLoadMetrics': kibanaPipeline.functionalTestProcess('xpack-pageLoadMetrics', './test/scripts/jenkins_xpack_page_load_metrics.sh'),
'xpack-securitySolutionCypress': { processNumber ->
whenChanged(['x-pack/plugins/security_solution/', 'x-pack/test/security_solution_cypress/']) {
kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypress', './test/scripts/jenkins_security_solution_cypress.sh')(processNumber)

File diff suppressed because one or more lines are too long

View file

@ -220,8 +220,8 @@ it('prepares assets for distribution', async () => {
expectFileMatchesSnapshotWithCompression('plugins/foo/target/public/foo.plugin.js', 'foo bundle');
expectFileMatchesSnapshotWithCompression(
'plugins/foo/target/public/1.plugin.js',
'1 async bundle'
'plugins/foo/target/public/foo.chunk.1.js',
'foo async bundle'
);
expectFileMatchesSnapshotWithCompression('plugins/bar/target/public/bar.plugin.js', 'bar bundle');
});

View file

@ -17,6 +17,9 @@
* under the License.
*/
import Fs from 'fs';
import Path from 'path';
import { materialize, mergeMap, dematerialize } from 'rxjs/operators';
import { CiStatsReporter } from '@kbn/dev-utils';
@ -24,6 +27,32 @@ import { OptimizerUpdate$ } from './run_optimizer';
import { OptimizerState, OptimizerConfig } from './optimizer';
import { pipeClosure } from './common';
const flatten = <T>(arr: Array<T | T[]>): T[] =>
arr.reduce((acc: T[], item) => acc.concat(item), []);
interface Entry {
relPath: string;
stats: Fs.Stats;
}
const getFiles = (dir: string, parent?: string) =>
flatten(
Fs.readdirSync(dir).map((name): Entry | Entry[] => {
const absPath = Path.join(dir, name);
const relPath = parent ? Path.join(parent, name) : name;
const stats = Fs.statSync(absPath);
if (stats.isDirectory()) {
return getFiles(absPath, relPath);
}
return {
relPath,
stats,
};
})
);
export function reportOptimizerStats(reporter: CiStatsReporter, config: OptimizerConfig) {
return pipeClosure((update$: OptimizerUpdate$) => {
let lastState: OptimizerState | undefined;
@ -36,16 +65,55 @@ export function reportOptimizerStats(reporter: CiStatsReporter, config: Optimize
if (n.kind === 'C' && lastState) {
await reporter.metrics(
config.bundles.map((bundle) => {
// make the cache read from the cache file since it was likely updated by the worker
bundle.cache.refresh();
flatten(
config.bundles.map((bundle) => {
// make the cache read from the cache file since it was likely updated by the worker
bundle.cache.refresh();
return {
group: `@kbn/optimizer bundle module count`,
id: bundle.id,
value: bundle.cache.getModuleCount() || 0,
};
})
const outputFiles = getFiles(bundle.outputDir).filter(
(file) => !(file.relPath.startsWith('.') || file.relPath.endsWith('.map'))
);
const entryName = `${bundle.id}.${bundle.type}.js`;
const entry = outputFiles.find((f) => f.relPath === entryName);
if (!entry) {
throw new Error(
`Unable to find bundle entry named [${entryName}] in [${bundle.outputDir}]`
);
}
const chunkPrefix = `${bundle.id}.chunk.`;
const asyncChunks = outputFiles.filter((f) => f.relPath.startsWith(chunkPrefix));
const miscFiles = outputFiles.filter(
(f) => f !== entry && !asyncChunks.includes(f)
);
const sumSize = (files: Entry[]) =>
files.reduce((acc: number, f) => acc + f.stats!.size, 0);
return [
{
group: `@kbn/optimizer bundle module count`,
id: bundle.id,
value: bundle.cache.getModuleCount() || 0,
},
{
group: `page load bundle size`,
id: bundle.id,
value: entry.stats!.size,
},
{
group: `async chunks size`,
id: bundle.id,
value: sumSize(asyncChunks),
},
{
group: `miscellaneous assets size`,
id: bundle.id,
value: sumSize(miscFiles),
},
];
})
)
);
}

View file

@ -50,7 +50,8 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker:
output: {
path: bundle.outputDir,
filename: `[name].${bundle.type}.js`,
filename: `${bundle.id}.${bundle.type}.js`,
chunkFilename: `${bundle.id}.chunk.[id].js`,
devtoolModuleFilenameTemplate: (info) =>
`/${bundle.type}:${bundle.id}/${Path.relative(
bundle.sourceRoot,

View file

@ -16,7 +16,6 @@
"@types/joi": "^13.4.2",
"@types/lodash": "^4.14.155",
"@types/parse-link-header": "^1.0.0",
"@types/puppeteer": "^3.0.0",
"@types/strip-ansi": "^5.2.1",
"@types/xml2js": "^0.4.5",
"diff": "^4.0.1"
@ -31,7 +30,6 @@
"joi": "^13.5.2",
"lodash": "^4.17.15",
"parse-link-header": "^1.0.1",
"puppeteer": "^3.3.0",
"rxjs": "^6.5.5",
"strip-ansi": "^5.2.0",
"tar-fs": "^1.16.3",

View file

@ -53,4 +53,3 @@ export { makeJunitReportPath } from './junit_report_path';
export { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix';
export * from './functional_test_runner';
export * from './page_load_metrics';

View file

@ -1,81 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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.
*/
import { ToolingLog } from '@kbn/dev-utils';
import { NavigationOptions, createUrl, navigateToApps } from './navigation';
export async function capturePageLoadMetrics(log: ToolingLog, options: NavigationOptions) {
const responsesByPageView = await navigateToApps(log, options);
const assetSizeMeasurements = new Map<string, number[]>();
const numberOfPagesVisited = responsesByPageView.size;
for (const [, frameResponses] of responsesByPageView) {
for (const [, { url, dataLength }] of frameResponses) {
if (url.length === 0) {
throw new Error('navigateToApps(); failed to identify the url of the request');
}
if (assetSizeMeasurements.has(url)) {
assetSizeMeasurements.set(url, [dataLength].concat(assetSizeMeasurements.get(url) || []));
} else {
assetSizeMeasurements.set(url, [dataLength]);
}
}
}
return Array.from(assetSizeMeasurements.entries())
.map(([url, measurements]) => {
const baseUrl = createUrl('/', options.appConfig.url);
const relativeUrl = url
// remove the baseUrl (expect the trailing slash) to make url relative
.replace(baseUrl.slice(0, -1), '')
// strip the build number from asset urls
.replace(/^\/\d+\//, '/');
return [relativeUrl, measurements] as const;
})
.filter(([url, measurements]) => {
if (measurements.length !== numberOfPagesVisited) {
// ignore urls seen only on some pages
return false;
}
if (url.startsWith('data:')) {
// ignore data urls since they are already counted by other assets
return false;
}
if (url.startsWith('/api/') || url.startsWith('/internal/')) {
// ignore api requests since they don't have deterministic sizes
return false;
}
const allMetricsAreEqual = measurements.every((x, i) =>
i === 0 ? true : x === measurements[i - 1]
);
if (!allMetricsAreEqual) {
throw new Error(`measurements for url [${url}] are not equal [${measurements.join(',')}]`);
}
return true;
})
.map(([url, measurements]) => {
return { group: 'page load asset size', id: url, value: measurements[0] };
});
}

View file

@ -1,90 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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.
*/
import Url from 'url';
import { run, createFlagError } from '@kbn/dev-utils';
import { resolve, basename } from 'path';
import { capturePageLoadMetrics } from './capture_page_load_metrics';
const defaultScreenshotsDir = resolve(__dirname, 'screenshots');
export function runPageLoadMetricsCli() {
run(
async ({ flags, log }) => {
const kibanaUrl = flags['kibana-url'];
if (!kibanaUrl || typeof kibanaUrl !== 'string') {
throw createFlagError('Expect --kibana-url to be a string');
}
const parsedUrl = Url.parse(kibanaUrl);
const [username, password] = parsedUrl.auth
? parsedUrl.auth.split(':')
: [flags.username, flags.password];
if (typeof username !== 'string' || typeof password !== 'string') {
throw createFlagError(
'Mising username and/or password, either specify in --kibana-url or pass --username and --password'
);
}
const headless = !flags.head;
const screenshotsDir = flags.screenshotsDir || defaultScreenshotsDir;
if (typeof screenshotsDir !== 'string' || screenshotsDir === basename(screenshotsDir)) {
throw createFlagError('Expect screenshotsDir to be valid path string');
}
const metrics = await capturePageLoadMetrics(log, {
headless,
appConfig: {
url: kibanaUrl,
username,
password,
},
screenshotsDir,
});
for (const metric of metrics) {
log.info(`${metric.id}: ${metric.value}`);
}
},
{
description: `Loads several pages with Puppeteer to capture the size of assets`,
flags: {
string: ['kibana-url', 'username', 'password', 'screenshotsDir'],
boolean: ['head'],
default: {
username: 'elastic',
password: 'changeme',
debug: true,
screenshotsDir: defaultScreenshotsDir,
},
help: `
--kibana-url Url for Kibana we should connect to, can include login info
--head Run puppeteer with graphical user interface
--username Set username, defaults to 'elastic'
--password Set password, defaults to 'changeme'
--screenshotsDir Set screenshots directory, defaults to '${defaultScreenshotsDir}'
`,
},
}
);
}

View file

@ -1,34 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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.
*/
export interface ResponseReceivedEvent {
frameId: string;
loaderId: string;
requestId: string;
response: Record<string, any>;
timestamp: number;
type: string;
}
export interface DataReceivedEvent {
encodedDataLength: number;
dataLength: number;
requestId: string;
timestamp: number;
}

View file

@ -1,21 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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.
*/
export * from './cli';
export { capturePageLoadMetrics } from './capture_page_load_metrics';

View file

@ -1,164 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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.
*/
import Fs from 'fs';
import Url from 'url';
import puppeteer from 'puppeteer';
import { resolve } from 'path';
import { ToolingLog } from '@kbn/dev-utils';
import { ResponseReceivedEvent, DataReceivedEvent } from './event';
export interface NavigationOptions {
headless: boolean;
appConfig: { url: string; username: string; password: string };
screenshotsDir: string;
}
export type NavigationResults = Map<string, Map<string, FrameResponse>>;
interface FrameResponse {
url: string;
dataLength: number;
}
function joinPath(pathA: string, pathB: string) {
return `${pathA.endsWith('/') ? pathA.slice(0, -1) : pathA}/${
pathB.startsWith('/') ? pathB.slice(1) : pathB
}`;
}
export function createUrl(path: string, url: string) {
const baseUrl = Url.parse(url);
return Url.format({
protocol: baseUrl.protocol,
hostname: baseUrl.hostname,
port: baseUrl.port,
pathname: joinPath(baseUrl.pathname || '', path),
});
}
async function loginToKibana(
log: ToolingLog,
browser: puppeteer.Browser,
options: NavigationOptions
) {
log.debug(`log in to the app..`);
const page = await browser.newPage();
const loginUrl = createUrl('/login', options.appConfig.url);
await page.goto(loginUrl, {
waitUntil: 'networkidle0',
});
await page.type('[data-test-subj="loginUsername"]', options.appConfig.username);
await page.type('[data-test-subj="loginPassword"]', options.appConfig.password);
await page.click('[data-test-subj="loginSubmit"]');
await page.waitForNavigation({ waitUntil: 'networkidle0' });
await page.close();
}
export async function navigateToApps(log: ToolingLog, options: NavigationOptions) {
const browser = await puppeteer.launch({ headless: options.headless, args: ['--no-sandbox'] });
const devToolsResponses: NavigationResults = new Map();
const apps = [
{ path: '/app/discover', locator: '[data-test-subj="discover-sidebar"]' },
{ path: '/app/home', locator: '[data-test-subj="homeApp"]' },
{ path: '/app/canvas', locator: '[data-test-subj="create-workpad-button"]' },
{ path: '/app/maps', locator: '[title="Maps"]' },
{ path: '/app/apm', locator: '[data-test-subj="apmMainContainer"]' },
];
await loginToKibana(log, browser, options);
await Promise.all(
apps.map(async (app) => {
const page = await browser.newPage();
page.setCacheEnabled(false);
page.setDefaultNavigationTimeout(0);
const frameResponses = new Map<string, FrameResponse>();
devToolsResponses.set(app.path, frameResponses);
const client = await page.target().createCDPSession();
await client.send('Network.enable');
function getRequestData(requestId: string) {
if (!frameResponses.has(requestId)) {
frameResponses.set(requestId, { url: '', dataLength: 0 });
}
return frameResponses.get(requestId)!;
}
client.on('Network.responseReceived', (event: ResponseReceivedEvent) => {
getRequestData(event.requestId).url = event.response.url;
});
client.on('Network.dataReceived', (event: DataReceivedEvent) => {
getRequestData(event.requestId).dataLength += event.dataLength;
});
const url = createUrl(app.path, options.appConfig.url);
log.debug(`goto ${url}`);
await page.goto(url, {
waitUntil: 'networkidle0',
});
let readyAttempt = 0;
let selectorFound = false;
while (!selectorFound) {
readyAttempt += 1;
try {
await page.waitForSelector(app.locator, { timeout: 5000 });
selectorFound = true;
} catch (error) {
log.error(
`Page '${app.path}' was not loaded properly, unable to find '${
app.locator
}', url: ${page.url()}`
);
if (readyAttempt < 6) {
continue;
}
const failureDir = resolve(options.screenshotsDir, 'failure');
const screenshotPath = resolve(
failureDir,
`${app.path.slice(1).split('/').join('_')}_navigation.png`
);
Fs.mkdirSync(failureDir, { recursive: true });
await page.bringToFront();
await page.screenshot({
path: screenshotPath,
type: 'png',
fullPage: true,
});
log.debug(`Saving screenshot to ${screenshotPath}`);
throw new Error(`Page load timeout: ${app.path} not loaded after 30 seconds`);
}
}
await page.close();
})
);
await browser.close();
return devToolsResponses;
}

View file

@ -1,21 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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.
*/
require('../src/setup_node_env');
require('@kbn/test').runPageLoadMetricsCli();

View file

@ -1,9 +0,0 @@
#!/usr/bin/env bash
source test/scripts/jenkins_test_setup_xpack.sh
checks-reporter-with-killswitch "Capture Kibana page load metrics" \
node scripts/functional_tests \
--debug --bail \
--kibana-install-dir "$installDir" \
--config test/page_load_metrics/config.ts;

View file

@ -17,6 +17,9 @@ tar -xzf "$linuxBuild" -C "$installDir" --strip=1
cd "$KIBANA_DIR"
source "test/scripts/jenkins_xpack_saved_objects_field_metrics.sh"
cd "$KIBANA_DIR"
source "test/scripts/jenkins_xpack_saved_objects_field_metrics.sh"
echo " -> running visual regression tests from x-pack directory"
cd "$XPACK_DIR"
yarn percy exec -t 10000 -- -- \

1
x-pack/.gitignore vendored
View file

@ -3,7 +3,6 @@
/target
/test/functional/failure_debug
/test/functional/screenshots
/test/page_load_metrics/screenshots
/test/functional/apps/reporting/reports/session
/test/reporting/configs/failure_debug/
/plugins/reporting/.chromium/

View file

@ -1,42 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { resolve } from 'path';
import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
import { PuppeteerTestRunner } from './runner';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const kibanaCommonTestsConfig = await readConfigFile(
require.resolve('../../../test/common/config.js')
);
const xpackFunctionalTestsConfig = await readConfigFile(
require.resolve('../functional/config.js')
);
return {
...kibanaCommonTestsConfig.getAll(),
testRunner: PuppeteerTestRunner,
esArchiver: {
directory: resolve(__dirname, 'es_archives'),
},
screenshots: {
directory: resolve(__dirname, 'screenshots'),
},
esTestCluster: {
...xpackFunctionalTestsConfig.get('esTestCluster'),
serverArgs: [...xpackFunctionalTestsConfig.get('esTestCluster.serverArgs')],
},
kbnTestServer: {
...xpackFunctionalTestsConfig.get('kbnTestServer'),
},
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,33 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { CiStatsReporter } from '@kbn/dev-utils';
import { capturePageLoadMetrics } from '@kbn/test';
// @ts-ignore not TS yet
import getUrl from '../../../src/test_utils/get_url';
import { FtrProviderContext } from './../functional/ftr_provider_context';
export async function PuppeteerTestRunner({ getService }: FtrProviderContext) {
const log = getService('log');
const config = getService('config');
const esArchiver = getService('esArchiver');
await esArchiver.load('default');
const metrics = await capturePageLoadMetrics(log, {
headless: true,
appConfig: {
url: getUrl.baseUrl(config.get('servers.kibana')),
username: config.get('servers.kibana.username'),
password: config.get('servers.kibana.password'),
},
screenshotsDir: config.get('screenshots.directory'),
});
const reporter = CiStatsReporter.fromEnv(log);
log.debug('Report page load asset size');
await reporter.metrics(metrics);
}

View file

@ -5711,13 +5711,6 @@
dependencies:
"@types/node" "*"
"@types/puppeteer@^3.0.0":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-3.0.1.tgz#053ec20facc162b25a64785affccaa3e5817c607"
integrity sha512-t03eNKCvWJXhQ8wkc5C6GYuSqMEdKLOX0GLMGtks25YZr38wKZlKTwGM/BoAPVtdysX7Bb9tdwrDS1+NrW3RRA==
dependencies:
"@types/node" "*"
"@types/q@^1.5.1":
version "1.5.2"
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
@ -8799,15 +8792,6 @@ bl@^3.0.0:
dependencies:
readable-stream "^3.0.1"
bl@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==
dependencies:
buffer "^5.5.0"
inherits "^2.0.4"
readable-stream "^3.4.0"
blob@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
@ -9314,14 +9298,6 @@ buffer@^5.1.0, buffer@^5.2.0:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffer@^5.2.1, buffer@^5.5.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
builtin-modules@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@ -22228,11 +22204,6 @@ mixin-object@^2.0.1:
for-in "^0.1.3"
is-extendable "^0.1.1"
mkdirp-classic@^0.5.2:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp@0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@ -25415,22 +25386,6 @@ puppeteer@^2.0.0:
rimraf "^2.6.1"
ws "^6.1.0"
puppeteer@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.3.0.tgz#95839af9fdc0aa4de7e5ee073a4c0adeb9e2d3d7"
integrity sha512-23zNqRltZ1PPoK28uRefWJ/zKb5Jhnzbbwbpcna2o5+QMn17F0khq5s1bdH3vPlyj+J36pubccR8wiNA/VE0Vw==
dependencies:
debug "^4.1.0"
extract-zip "^2.0.0"
https-proxy-agent "^4.0.0"
mime "^2.0.3"
progress "^2.0.1"
proxy-from-env "^1.0.0"
rimraf "^3.0.2"
tar-fs "^2.0.0"
unbzip2-stream "^1.3.3"
ws "^7.2.3"
q@^1.1.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@ -30147,16 +30102,6 @@ tar-fs@^1.16.3:
pump "^1.0.0"
tar-stream "^1.1.2"
tar-fs@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5"
integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==
dependencies:
chownr "^1.1.1"
mkdirp-classic "^0.5.2"
pump "^3.0.0"
tar-stream "^2.0.0"
tar-stream@^1.1.2, tar-stream@^1.5.2:
version "1.5.5"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
@ -30167,17 +30112,6 @@ tar-stream@^1.1.2, tar-stream@^1.5.2:
readable-stream "^2.0.0"
xtend "^4.0.0"
tar-stream@^2.0.0:
version "2.1.3"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.3.tgz#1e2022559221b7866161660f118255e20fa79e41"
integrity sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==
dependencies:
bl "^4.0.1"
end-of-stream "^1.4.1"
fs-constants "^1.0.0"
inherits "^2.0.3"
readable-stream "^3.1.1"
tar-stream@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
@ -31681,14 +31615,6 @@ unbzip2-stream@^1.0.9:
buffer "^3.0.1"
through "^2.3.6"
unbzip2-stream@^1.3.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
dependencies:
buffer "^5.2.1"
through "^2.3.8"
unc-path-regex@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
@ -33642,11 +33568,6 @@ ws@^7.0.0:
dependencies:
async-limiter "^1.0.0"
ws@^7.2.3:
version "7.3.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
ws@~3.3.1:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"