kibana/src/optimize/base_optimizer.js
Spencer c65da14d7c
[optimizer] extract plugin discovery (#14745)
* [plugins] extract plugin discover from the kibana server

* integrate plugin discovery module with server

* [pluginDiscovery] fully extend config before checking enabled status

* [pluginDiscovery] limit arbitrary defaults in PluginSpec

* [ui/navLink] fix tests

* [ui/injectedVars] fix tests

* [ui/app] fix tests

* [server/plugins] convert init to callPluginHook tests

* [build/verifyTranslations] update verify logic

* [pluginDiscovery] remove rx utils

* fix i18n transaltion key name

* [pluginDiscovery] do kibana version checks as a part of discovery

* [pluginDiscovery/createPacksInDirectory$] clarify error handling

* [eslint] fix lint errors

* [uiApp/modules] ensure load order matches master

* [uiBundle] use known uiExport type for providers

* [uiExports] use the `home` export type

* [uiExports] validate that all uiExport types are known

* [timelion] remove archaic/broken bwc check

* revert some stragler changes

* [pluginSpecs] reformat comments

* [uiBundle] rebel and use more fcb 😬

* correct comment

* [server/waitForPluginsInit] describe queues var

* [server/plugins] prevent multiple calls to next() by using single then()

* [uiApp] remove archaic deprecation warning

* [uiApp] tighten up tests

* [pluginDiscovery/errors] remove $ from symbol var

* [pluginDiscovery/reduceExportSpecs] update docs

* [pluginDiscovery/findPluginSpecs] rightVersion -> isRightVersion

* [pluginDiscovery/findPluginSpecs] fix typos

* [uiApps/getById] use Map() rather than memoize

* save

* [savedObjects/mappings] use uiExports.savedObjectMappings

* [server/mapping/indexMapping] update tests, addRootProperties method removed

* [uiExports] "embeddableHandlers" -> "embeddableFactories"

* [pluginDiscovery] fix pluralization of invalidVersionSpec$

* [pluginDiscover] add README

* [pluginDiscovery/reduceExportSpecs] don't ignore fasly spec values, just undefined

* [ui/exportTypes] use better reducer names

* [ui/uiExports] add README

* fix links

* [pluginDiscovery/readme] expand examples

* [pluginDiscovery/readme] clean up reduceExportSpecs() doc

* [ui/uiExports/readme] cleanup example

* [pluginDiscovery] remove needless use of lodash

* [pluginDiscovery/waitForComplete] use better name

* [pluginDiscovery/findPluginSpecs] use fixtures rather than core_plugins

* [pluginDiscovery/stubSchema] use deafult: false

* [plguinDiscovery/pluginConfig] add tests

* typo

* [uiExports/readme] fix link

* [pluginDiscovery/packAtPath] fail with InvalidPackError if path is not a string

* [pluginDiscovery/packAtPath] rely on error.code to detect missing package.json file

* [pluginDiscovery/packAtPath] only attempt to get pack when observable is subscribed

* [pluginDiscovery/packAtPath] add tests

* [pluginDiscovery/pluginPack] move absolute path checks into fs lib

* [pluginDiscovery/packsInDirectory] fix error type check

* [pluginDiscovery/pluginPack/tests] share some utils

* [pluginDiscovery/packsInDirectory] add tests

* [pluginDiscovery/pluginPack] only cast undefined to array

* [pluginDiscovery/pluginPack] add tests

* [pluginDiscovery/pluginSpec/isVersionCompatible] add tests

* [pluginDiscovery/InvalidPluginError] be less redundant

* [pluginDiscovery/pluginSpec] verify config service is passed to isEnabled()

* [pluginDiscovery/pluginSpec] add tests

* fix "existent" spelling
2017-12-05 18:11:50 -07:00

256 lines
6.7 KiB
JavaScript

import { resolve } from 'path';
import { writeFile } from 'fs';
import Boom from 'boom';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import webpack from 'webpack';
import CommonsChunkPlugin from 'webpack/lib/optimize/CommonsChunkPlugin';
import DefinePlugin from 'webpack/lib/DefinePlugin';
import UglifyJsPlugin from 'webpack/lib/optimize/UglifyJsPlugin';
import NoEmitOnErrorsPlugin from 'webpack/lib/NoEmitOnErrorsPlugin';
import Stats from 'webpack/lib/Stats';
import webpackMerge from 'webpack-merge';
import { defaults } from 'lodash';
import { fromRoot } from '../utils';
import { PUBLIC_PATH_PLACEHOLDER } from './public_path_placeholder';
const POSTCSS_CONFIG_PATH = require.resolve('./postcss.config');
const BABEL_PRESET_PATH = require.resolve('../babel-preset/webpack');
const BABEL_EXCLUDE_RE = [
/[\/\\](webpackShims|node_modules|bower_components)[\/\\]/,
];
export default class BaseOptimizer {
constructor(opts) {
this.uiBundles = opts.uiBundles;
this.profile = opts.profile || false;
switch (opts.sourceMaps) {
case true:
this.sourceMaps = 'source-map';
break;
case 'fast':
this.sourceMaps = 'cheap-module-eval-source-map';
break;
default:
this.sourceMaps = opts.sourceMaps || false;
break;
}
this.unsafeCache = opts.unsafeCache || false;
if (typeof this.unsafeCache === 'string') {
this.unsafeCache = [
new RegExp(this.unsafeCache.slice(1, -1))
];
}
}
async initCompiler() {
if (this.compiler) return this.compiler;
const compilerConfig = this.getConfig();
this.compiler = webpack(compilerConfig);
this.compiler.plugin('done', stats => {
if (!this.profile) return;
const path = this.uiBundles.resolvePath('stats.json');
const content = JSON.stringify(stats.toJson());
writeFile(path, content, function (err) {
if (err) throw err;
});
});
return this.compiler;
}
getConfig() {
const cacheDirectory = this.uiBundles.getCachePath();
function getStyleLoaders(preProcessors = [], postProcessors = []) {
return ExtractTextPlugin.extract({
fallback: {
loader: 'style-loader'
},
use: [
...postProcessors,
{
loader: 'css-loader',
options: {
// importLoaders needs to know the number of loaders that follow this one,
// so we add 1 (for the postcss-loader) to the length of the preProcessors
// array that we merge into this array
importLoaders: 1 + preProcessors.length,
},
},
{
loader: 'postcss-loader',
options: {
config: {
path: POSTCSS_CONFIG_PATH,
},
},
},
...preProcessors,
],
});
}
const commonConfig = {
node: { fs: 'empty' },
context: fromRoot('.'),
entry: this.uiBundles.toWebpackEntries(),
devtool: this.sourceMaps,
profile: this.profile || false,
output: {
path: this.uiBundles.getWorkingDir(),
filename: '[name].bundle.js',
sourceMapFilename: '[file].map',
publicPath: PUBLIC_PATH_PLACEHOLDER,
devtoolModuleFilenameTemplate: '[absolute-resource-path]'
},
plugins: [
new ExtractTextPlugin('[name].style.css', {
allChunks: true
}),
new CommonsChunkPlugin({
name: 'commons',
filename: 'commons.bundle.js'
}),
new NoEmitOnErrorsPlugin(),
],
module: {
rules: [
{
test: /\.less$/,
use: getStyleLoaders(
['less-loader'],
[{
loader: 'cache-loader',
options: {
cacheDirectory: resolve(cacheDirectory, 'less'),
}
}]
),
},
{
test: /\.css$/,
use: getStyleLoaders(),
},
{
// TODO: this doesn't seem to be used, remove?
test: /\.jade$/,
loader: 'jade-loader'
},
{
test: /\.(html|tmpl)$/,
loader: 'raw-loader'
},
{
test: /\.png$/,
loader: 'url-loader'
},
{
test: /\.(woff|woff2|ttf|eot|svg|ico)(\?|$)/,
loader: 'file-loader'
},
{
test: /\.js$/,
exclude: BABEL_EXCLUDE_RE.concat(this.uiBundles.getWebpackNoParseRules()),
use: [
{
loader: 'cache-loader',
options: {
cacheDirectory: resolve(cacheDirectory, 'babel'),
}
},
{
loader: 'babel-loader',
options: {
babelrc: false,
presets: [
BABEL_PRESET_PATH,
],
},
},
],
},
...this.uiBundles.getPostLoaders().map(loader => ({
enforce: 'post',
...loader
})),
],
noParse: this.uiBundles.getWebpackNoParseRules(),
},
resolve: {
extensions: ['.js', '.json'],
mainFields: ['browser', 'browserify', 'main'],
modules: [
'webpackShims',
fromRoot('webpackShims'),
'node_modules',
fromRoot('node_modules'),
],
alias: this.uiBundles.getAliases(),
unsafeCache: this.unsafeCache,
},
};
if (this.uiBundles.isDevMode()) {
return webpackMerge(commonConfig, {
// In the test env we need to add react-addons (and a few other bits) for the
// enzyme tests to work.
// https://github.com/airbnb/enzyme/blob/master/docs/guides/webpack.md
externals: {
'mocha': 'mocha',
'react/lib/ExecutionEnvironment': true,
'react/addons': true,
'react/lib/ReactContext': true,
}
});
}
return webpackMerge(commonConfig, {
plugins: [
new DefinePlugin({
'process.env': {
'NODE_ENV': '"production"'
}
}),
new UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: false,
mangle: false
}),
]
});
}
failedStatsToError(stats) {
const details = stats.toString(defaults(
{ colors: true },
Stats.presetToOptions('minimal')
));
return Boom.create(
500,
`Optimizations failure.\n${details.split('\n').join('\n ')}\n`,
stats.toJson(Stats.presetToOptions('detailed'))
);
}
}