UNPKG

9.21 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
7 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
8 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
9 * Google as part of the polymer project is also subject to an additional IP
10 * rights grant found at http://polymer.github.io/PATENTS.txt
11 */
12var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13 if (k2 === undefined) k2 = k;
14 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
15}) : (function(o, m, k, k2) {
16 if (k2 === undefined) k2 = k;
17 o[k2] = m[k];
18}));
19var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20 Object.defineProperty(o, "default", { enumerable: true, value: v });
21}) : function(o, v) {
22 o["default"] = v;
23});
24var __importStar = (this && this.__importStar) || function (mod) {
25 if (mod && mod.__esModule) return mod;
26 var result = {};
27 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28 __setModuleDefault(result, mod);
29 return result;
30};
31Object.defineProperty(exports, "__esModule", { value: true });
32exports.writeBackSchemaIfNeeded = exports.parseConfigFile = void 0;
33const fsExtra = __importStar(require("fs-extra"));
34const jsonschema = __importStar(require("jsonschema"));
35const browser_1 = require("./browser");
36const config_1 = require("./config");
37const defaults = __importStar(require("./defaults"));
38const types_1 = require("./types");
39const util_1 = require("./util");
40/**
41 * Validate the given JSON object parsed from a config file, and expand it into
42 * a fully specified configuration.
43 */
44async function parseConfigFile(parsedJson) {
45 const schema = require('../config.schema.json');
46 const result = jsonschema.validate(parsedJson, schema, { propertyName: 'config' });
47 if (result.errors.length > 0) {
48 throw new Error([...new Set(result.errors.map(customizeJsonSchemaError))].join('\n'));
49 }
50 const validated = parsedJson;
51 const root = validated.root || '.';
52 const benchmarks = [];
53 for (const benchmark of validated.benchmarks) {
54 for (const expanded of applyExpansions(benchmark)) {
55 benchmarks.push(applyDefaults(await parseBenchmark(expanded, root)));
56 }
57 }
58 return {
59 root,
60 sampleSize: validated.sampleSize,
61 timeout: validated.timeout,
62 horizons: validated.horizons !== undefined ?
63 config_1.parseHorizons(validated.horizons) :
64 undefined,
65 benchmarks,
66 resolveBareModules: validated.resolveBareModules,
67 };
68}
69exports.parseConfigFile = parseConfigFile;
70/**
71 * Some of the automatically generated jsonschema errors are unclear, e.g. when
72 * there is a union of complex types they are reported as "[schema1],
73 * [schema2]" etc.
74 */
75function customizeJsonSchemaError(error) {
76 if (error.property.match(/^config\.benchmarks\[\d+\]\.measurement$/)) {
77 return `${error.property} is not one of: ${[...types_1.measurements].join(', ')}` +
78 ' or an object like `performanceEntry: string`';
79 }
80 return error.toString();
81}
82async function parseBenchmark(benchmark, root) {
83 const spec = {};
84 if (benchmark.name !== undefined) {
85 spec.name = benchmark.name;
86 }
87 if (benchmark.browser !== undefined) {
88 let browser;
89 if (typeof benchmark.browser === 'string') {
90 browser = Object.assign(Object.assign({}, browser_1.parseBrowserConfigString(benchmark.browser)), { windowSize: {
91 width: defaults.windowWidth,
92 height: defaults.windowHeight,
93 } });
94 }
95 else {
96 browser = parseBrowserObject(benchmark.browser);
97 }
98 browser_1.validateBrowserConfig(browser);
99 spec.browser = browser;
100 }
101 if (benchmark.measurement === 'callback') {
102 spec.measurement = [{
103 mode: 'callback',
104 }];
105 }
106 else if (benchmark.measurement === 'fcp') {
107 spec.measurement = [{
108 mode: 'performance',
109 entryName: 'first-contentful-paint',
110 }];
111 }
112 else if (benchmark.measurement === 'global') {
113 spec.measurement = [{
114 mode: 'expression',
115 expression: benchmark.measurementExpression || defaults.measurementExpression,
116 }];
117 }
118 else if (Array.isArray(benchmark.measurement)) {
119 spec.measurement = benchmark.measurement;
120 }
121 else if (benchmark.measurement !== undefined) {
122 spec.measurement = [benchmark.measurement];
123 }
124 const url = benchmark.url;
125 if (url !== undefined) {
126 if (util_1.isHttpUrl(url)) {
127 spec.url = {
128 kind: 'remote',
129 url,
130 };
131 }
132 else {
133 let urlPath, queryString;
134 const q = url.indexOf('?');
135 if (q !== -1) {
136 urlPath = url.substring(0, q);
137 queryString = url.substring(q);
138 }
139 else {
140 urlPath = url;
141 queryString = '';
142 }
143 spec.url = {
144 kind: 'local',
145 urlPath: await config_1.urlFromLocalPath(root, urlPath),
146 queryString,
147 };
148 if (benchmark.packageVersions !== undefined) {
149 spec.url.version = {
150 label: benchmark.packageVersions.label,
151 dependencyOverrides: benchmark.packageVersions.dependencies,
152 };
153 }
154 }
155 }
156 return spec;
157}
158function parseBrowserObject(config) {
159 const parsed = {
160 name: config.name,
161 headless: ('headless' in config && config.headless) || defaults.headless,
162 windowSize: ('windowSize' in config && config.windowSize) || {
163 width: defaults.windowWidth,
164 height: defaults.windowHeight,
165 },
166 };
167 if ('cpuThrottlingRate' in config) {
168 parsed.cpuThrottlingRate = config.cpuThrottlingRate;
169 }
170 if (config.remoteUrl) {
171 parsed.remoteUrl = config.remoteUrl;
172 }
173 if ('binary' in config && config.binary) {
174 parsed.binary = config.binary;
175 }
176 if ('addArguments' in config && config.addArguments) {
177 parsed.addArguments = config.addArguments;
178 }
179 if ('removeArguments' in config && config.removeArguments) {
180 parsed.removeArguments = config.removeArguments;
181 }
182 if ('preferences' in config && config.preferences) {
183 parsed.preferences = config.preferences;
184 }
185 return parsed;
186}
187function applyExpansions(bench) {
188 if (bench.expand === undefined || bench.expand.length === 0) {
189 return [bench];
190 }
191 const expanded = [];
192 for (const expansion of bench.expand) {
193 for (const expandedBench of applyExpansions(expansion)) {
194 expanded.push(Object.assign(Object.assign({}, bench), expandedBench));
195 }
196 }
197 return expanded;
198}
199function applyDefaults(partialSpec) {
200 const url = partialSpec.url;
201 let { name, measurement, browser } = partialSpec;
202 if (url === undefined) {
203 // Note we can't validate this with jsonschema, because we only need to
204 // ensure we have a URL after recursive expansion; so at any given level
205 // the URL could be optional.
206 throw new Error('No URL specified');
207 }
208 if (url.kind === 'remote') {
209 if (name === undefined) {
210 name = url.url;
211 }
212 }
213 else {
214 if (name === undefined) {
215 name = url.urlPath + url.queryString;
216 }
217 }
218 if (browser === undefined) {
219 browser = {
220 name: defaults.browserName,
221 headless: defaults.headless,
222 windowSize: {
223 width: defaults.windowWidth,
224 height: defaults.windowHeight,
225 },
226 };
227 }
228 if (measurement === undefined) {
229 measurement = [defaults.measurement(url)];
230 }
231 return { name, url, browser, measurement };
232}
233async function writeBackSchemaIfNeeded(rawConfigObj, configFile) {
234 // Add the $schema field to the original config file if it's absent.
235 // We only want to do this if the file validated though, so we don't mutate
236 // a file that's not actually a tachometer config file.
237 if (!('$schema' in rawConfigObj)) {
238 const $schema = 'https://raw.githubusercontent.com/Polymer/tachometer/master/config.schema.json';
239 // Extra IDE features can be activated if the config file has a schema.
240 const withSchema = Object.assign({ $schema }, rawConfigObj);
241 const contents = JSON.stringify(withSchema, null, 2);
242 await fsExtra.writeFile(configFile, contents, { encoding: 'utf-8' });
243 }
244}
245exports.writeBackSchemaIfNeeded = writeBackSchemaIfNeeded;
246//# sourceMappingURL=configfile.js.map
\No newline at end of file