-
Notifications
You must be signed in to change notification settings - Fork 8.2k
/
optimizer_config.ts
279 lines (255 loc) · 9.24 KB
/
optimizer_config.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/*
* 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 Path from 'path';
import Os from 'os';
import {
Bundle,
WorkerConfig,
CacheableWorkerConfig,
ThemeTag,
ThemeTags,
parseThemeTags,
} from '../common';
import { findKibanaPlatformPlugins, KibanaPlatformPlugin } from './kibana_platform_plugins';
import { getPluginBundles } from './get_plugin_bundles';
import { filterById } from './filter_by_id';
function pickMaxWorkerCount(dist: boolean) {
// don't break if cpus() returns nothing, or an empty array
const cpuCount = Math.max(Os.cpus()?.length, 1);
// if we're buiding the dist then we can use more of the system's resources to get things done a little quicker
const maxWorkers = dist ? cpuCount - 1 : Math.ceil(cpuCount / 3);
// ensure we always have at least two workers
return Math.max(maxWorkers, 2);
}
function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
const result: any = {};
for (const [key, value] of Object.entries(obj) as any) {
if (!keys.includes(key)) {
result[key] = value;
}
}
return result as Omit<T, K>;
}
interface Options {
/** absolute path to root of the repo/build */
repoRoot: string;
/**
* absolute path to the root directory where output should be written to. This
* defaults to the repoRoot but can be customized to write output somewhere else.
*
* This is how we write output to the build directory in the Kibana build tasks.
*/
outputRoot?: string;
/** enable to run the optimizer in watch mode */
watch?: boolean;
/** the maximum number of workers that will be created */
maxWorkerCount?: number;
/** set to false to disabling writing/reading of caches */
cache?: boolean;
/** build assets suitable for use in the distributable */
dist?: boolean;
/** enable webpack profiling, writes stats.json files to the root of each plugin's output dir */
profileWebpack?: boolean;
/** set to true to inspecting workers when the parent process is being inspected */
inspectWorkers?: boolean;
/** include only oss plugins in default scan dirs */
oss?: boolean;
/** include examples in default scan dirs */
examples?: boolean;
/** absolute paths to specific plugins that should be built */
pluginPaths?: string[];
/** absolute paths to directories that should be built, overrides the default scan dirs */
pluginScanDirs?: string[];
/** absolute paths that should be added to the default scan dirs */
extraPluginScanDirs?: string[];
/**
* array of comma separated patterns that will be matched against bundle ids.
* bundles will only be built if they match one of the specified patterns.
* `*` can exist anywhere in each pattern and will match anything, `!` inverts the pattern
*
* examples:
* --filter foo --filter bar # [foo, bar], excludes [foobar]
* --filter foo,bar # [foo, bar], excludes [foobar]
* --filter foo* # [foo, foobar], excludes [bar]
* --filter f*r # [foobar], excludes [foo, bar]
*/
filter?: string[];
/** flag that causes the core bundle to be built along with plugins */
includeCoreBundle?: boolean;
/**
* style themes that sass files will be converted to, the correct style will be
* loaded in the browser automatically by checking the global `__kbnThemeTag__`.
* Specifying additional styles increases build time.
*
* Defaults:
* - "*" when building the dist
* - comma separated list of themes in the `KBN_OPTIMIZER_THEMES` env var
* - "k7light"
*/
themes?: ThemeTag | '*' | ThemeTag[];
}
export interface ParsedOptions {
repoRoot: string;
outputRoot: string;
watch: boolean;
maxWorkerCount: number;
profileWebpack: boolean;
cache: boolean;
dist: boolean;
pluginPaths: string[];
pluginScanDirs: string[];
filters: string[];
inspectWorkers: boolean;
includeCoreBundle: boolean;
themeTags: ThemeTags;
}
export class OptimizerConfig {
static parseOptions(options: Options): ParsedOptions {
const watch = !!options.watch;
const oss = !!options.oss;
const dist = !!options.dist;
const examples = !!options.examples;
const profileWebpack = !!options.profileWebpack;
const inspectWorkers = !!options.inspectWorkers;
const cache = options.cache !== false && !process.env.KBN_OPTIMIZER_NO_CACHE;
const includeCoreBundle = !!options.includeCoreBundle;
const filters = options.filter || [];
const repoRoot = options.repoRoot;
if (!Path.isAbsolute(repoRoot)) {
throw new TypeError('repoRoot must be an absolute path');
}
const outputRoot = options.outputRoot ?? repoRoot;
if (!Path.isAbsolute(outputRoot)) {
throw new TypeError('outputRoot must be an absolute path');
}
/**
* BEWARE: this needs to stay roughly synchronized with
* `src/core/server/config/env.ts` which determines which paths
* should be searched for plugins to load
*/
const pluginScanDirs = options.pluginScanDirs || [
Path.resolve(repoRoot, 'src/plugins'),
...(oss ? [] : [Path.resolve(repoRoot, 'x-pack/plugins')]),
Path.resolve(repoRoot, 'plugins'),
...(examples ? [Path.resolve('examples'), Path.resolve('x-pack/examples')] : []),
Path.resolve(repoRoot, '../kibana-extra'),
];
if (!pluginScanDirs.every((p) => Path.isAbsolute(p))) {
throw new TypeError('pluginScanDirs must all be absolute paths');
}
for (const extraPluginScanDir of options.extraPluginScanDirs || []) {
if (!Path.isAbsolute(extraPluginScanDir)) {
throw new TypeError('extraPluginScanDirs must all be absolute paths');
}
pluginScanDirs.push(extraPluginScanDir);
}
const pluginPaths = options.pluginPaths || [];
if (!pluginPaths.every((s) => Path.isAbsolute(s))) {
throw new TypeError('pluginPaths must all be absolute paths');
}
const maxWorkerCount = process.env.KBN_OPTIMIZER_MAX_WORKERS
? parseInt(process.env.KBN_OPTIMIZER_MAX_WORKERS, 10)
: options.maxWorkerCount ?? pickMaxWorkerCount(dist);
if (typeof maxWorkerCount !== 'number' || !Number.isFinite(maxWorkerCount)) {
throw new TypeError('worker count must be a number');
}
const themeTags = parseThemeTags(
options.themes || (dist ? '*' : process.env.KBN_OPTIMIZER_THEMES)
);
return {
watch,
dist,
repoRoot,
outputRoot,
maxWorkerCount,
profileWebpack,
cache,
pluginScanDirs,
pluginPaths,
filters,
inspectWorkers,
includeCoreBundle,
themeTags,
};
}
static create(inputOptions: Options) {
const options = OptimizerConfig.parseOptions(inputOptions);
const plugins = findKibanaPlatformPlugins(options.pluginScanDirs, options.pluginPaths);
const bundles = [
...(options.includeCoreBundle
? [
new Bundle({
type: 'entry',
id: 'core',
publicDirNames: ['public', 'public/utils'],
sourceRoot: options.repoRoot,
contextDir: Path.resolve(options.repoRoot, 'src/core'),
outputDir: Path.resolve(options.outputRoot, 'src/core/target/public'),
}),
]
: []),
...getPluginBundles(plugins, options.repoRoot, options.outputRoot),
];
return new OptimizerConfig(
filterById(options.filters, bundles),
options.cache,
options.watch,
options.inspectWorkers,
plugins,
options.repoRoot,
options.maxWorkerCount,
options.dist,
options.profileWebpack,
options.themeTags
);
}
constructor(
public readonly bundles: Bundle[],
public readonly cache: boolean,
public readonly watch: boolean,
public readonly inspectWorkers: boolean,
public readonly plugins: KibanaPlatformPlugin[],
public readonly repoRoot: string,
public readonly maxWorkerCount: number,
public readonly dist: boolean,
public readonly profileWebpack: boolean,
public readonly themeTags: ThemeTags
) {}
getWorkerConfig(optimizerCacheKey: unknown): WorkerConfig {
return {
cache: this.cache,
dist: this.dist,
profileWebpack: this.profileWebpack,
repoRoot: this.repoRoot,
watch: this.watch,
optimizerCacheKey,
themeTags: this.themeTags,
browserslistEnv: this.dist ? 'production' : process.env.BROWSERSLIST_ENV || 'dev',
};
}
getCacheableWorkerConfig(): CacheableWorkerConfig {
return omit(this.getWorkerConfig('♻'), [
// these config options don't change the output of the bundles, so
// should not invalidate caches when they change
'watch',
'profileWebpack',
'cache',
]);
}
}