[Lens] Reduce initial bundle size (#78142)

This commit is contained in:
Joe Reuter 2020-09-29 19:00:29 +02:00 committed by GitHub
parent 5cdd93285e
commit dbef60d3f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 192 additions and 116 deletions

View file

@ -1 +0,0 @@
@import 'app';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './app.scss';
import _ from 'lodash';
import React, { useState, useEffect, useCallback } from 'react';
import { i18n } from '@kbn/i18n';

View file

@ -0,0 +1,24 @@
/*
* 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.
*/
/**
* This file re-exports all parts of visualizations and datasources which can be loaded lazily
* (to reduce page load bundle size) when Lens is actually accessed via editor or embeddable.
*
* It's also possible for each visualization and datasource to resolve this locally, but this causes
* a burst of bundles being loaded on Lens startup at once (and in some scenarios cascading bundle loads).
* This file causes all of them to be served in a single request.
*/
export * from './datatable_visualization/datatable_visualization';
export * from './metric_visualization/metric_visualization';
export * from './pie_visualization/pie_visualization';
export * from './xy_visualization/xy_visualization';
export * from './indexpattern_datasource/indexpattern';
export * from './editor_frame_service/editor_frame';
export * from './app_plugin/mounter';

View file

@ -1 +0,0 @@
@import 'visualization';

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './expression';
export * from './visualization';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './expression.scss';
import React, { useMemo } from 'react';
import ReactDOM from 'react-dom';
import { i18n } from '@kbn/i18n';

View file

@ -5,9 +5,7 @@
*/
import { CoreSetup } from 'kibana/public';
import { datatableVisualization } from './visualization';
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
import { datatable, datatableColumns, getDatatableRenderer } from './expression';
import { EditorFrameSetup, FormatFactory } from '../types';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
@ -29,6 +27,13 @@ export class DatatableVisualization {
core: CoreSetup<DatatableVisualizationPluginStartPlugins, void>,
{ expressions, formatFactory, editorFrame }: DatatableVisualizationPluginSetupPlugins
) {
editorFrame.registerVisualization(async () => {
const {
datatable,
datatableColumns,
getDatatableRenderer,
datatableVisualization,
} = await import('../async_services');
expressions.registerFunction(() => datatableColumns);
expressions.registerFunction(() => datatable);
expressions.registerRenderer(() =>
@ -39,6 +44,7 @@ export class DatatableVisualization {
.then(([_, { data: dataStart }]) => dataStart.search.aggs.types.get),
})
);
editorFrame.registerVisualization(datatableVisualization);
return datatableVisualization;
});
}
}

View file

@ -1 +0,0 @@
@import 'drag_drop';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './drag_drop.scss';
import React, { useState, useContext } from 'react';
import classNames from 'classnames';
import { DragContext } from './providers';

View file

@ -1 +0,0 @@
@import 'editor_frame/index';

View file

@ -1,12 +0,0 @@
.lnsExpressionRenderer {
@include euiScrollBar;
position: relative;
width: 100%;
height: 100%;
display: flex;
overflow: auto;
.lnsExpressionRenderer__component {
position: static; // Let the progress indicator position itself against the outer parent
}
}

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './data_panel_wrapper.scss';
import React, { useMemo, memo, useContext, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiPopover, EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './frame_layout.scss';
import React from 'react';
import { EuiPage, EuiPageSideBar, EuiPageBody } from '@elastic/eui';

View file

@ -1,5 +0,0 @@
@import 'data_panel_wrapper';
@import 'expression_renderer';
@import 'frame_layout';
@import 'suggestion_panel';
@import 'workspace_panel_wrapper';

View file

@ -1,3 +1,6 @@
@import '../../mixins';
@import '../../variables';
.lnsSuggestionPanel__title {
margin-left: $euiSizeXS / 2;
}

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './suggestion_panel.scss';
import _, { camelCase } from 'lodash';
import React, { useState, useEffect, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './workspace_panel_wrapper.scss';
import React, { useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import classNames from 'classnames';

View file

@ -22,7 +22,6 @@ import {
EditorFrameStart,
} from '../types';
import { Document } from '../persistence/saved_object_store';
import { EditorFrame } from './editor_frame';
import { mergeTables } from './merge_tables';
import { formatColumn } from './format_column';
import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory';
@ -47,9 +46,11 @@ export interface EditorFrameStartPlugins {
}
async function collectAsyncDefinitions<T extends { id: string }>(
definitions: Array<T | Promise<T>>
definitions: Array<T | (() => Promise<T>)>
) {
const resolvedDefinitions = await Promise.all(definitions);
const resolvedDefinitions = await Promise.all(
definitions.map((definition) => (typeof definition === 'function' ? definition() : definition))
);
const definitionMap: Record<string, T> = {};
resolvedDefinitions.forEach((definition) => {
definitionMap[definition.id] = definition;
@ -61,8 +62,8 @@ async function collectAsyncDefinitions<T extends { id: string }>(
export class EditorFrameService {
constructor() {}
private readonly datasources: Array<Datasource | Promise<Datasource>> = [];
private readonly visualizations: Array<Visualization | Promise<Visualization>> = [];
private readonly datasources: Array<Datasource | (() => Promise<Datasource>)> = [];
private readonly visualizations: Array<Visualization | (() => Promise<Visualization>)> = [];
/**
* This method takes a Lens saved object as returned from the persistence helper,
@ -124,7 +125,7 @@ export class EditorFrameService {
]);
return {
mount: (
mount: async (
element,
{ doc, onError, dateRange, query, filters, savedQuery, onChange, showNoDataPopover }
) => {
@ -132,6 +133,8 @@ export class EditorFrameService {
const firstDatasourceId = Object.keys(resolvedDatasources)[0];
const firstVisualizationId = Object.keys(resolvedVisualizations)[0];
const { EditorFrame } = await import('../async_services');
render(
<I18nProvider>
<EditorFrame

View file

@ -1,14 +0,0 @@
// Import the EUI global scope so we can use EUI constants
@import '@elastic/eui/src/global_styling/variables/index';
@import '@elastic/eui/src/global_styling/mixins/index';
@import 'variables';
@import 'mixins';
@import 'app_plugin/index';
@import 'datatable_visualization/index';
@import 'drag_drop/index';
@import 'editor_frame_service/index';
@import 'indexpattern_datasource/index';
@import 'xy_visualization/index';
@import 'metric_visualization/index';

View file

@ -1 +0,0 @@
@import 'field_item';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './field_item.scss';
import React, { useState } from 'react';
import DateMath from '@elastic/datemath';
import {

View file

@ -6,8 +6,6 @@
import { CoreSetup } from 'kibana/public';
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
import { getIndexPatternDatasource } from './indexpattern';
import { renameColumns } from './rename_columns';
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
import {
@ -34,17 +32,17 @@ export class IndexPatternDatasource {
core: CoreSetup<IndexPatternDatasourceStartPlugins>,
{ expressions, editorFrame, charts }: IndexPatternDatasourceSetupPlugins
) {
editorFrame.registerDatasource(async () => {
const { getIndexPatternDatasource, renameColumns } = await import('../async_services');
expressions.registerFunction(renameColumns);
editorFrame.registerDatasource(
core.getStartServices().then(([coreStart, { data }]) =>
return core.getStartServices().then(([coreStart, { data }]) =>
getIndexPatternDatasource({
core: coreStart,
storage: new Storage(localStorage),
data,
charts,
})
) as Promise<Datasource>
);
) as Promise<Datasource>;
});
}
}

View file

@ -104,6 +104,8 @@ export function uniqueLabels(layers: Record<string, IndexPatternLayer>) {
return columnLabelMap;
}
export * from './rename_columns';
export function getIndexPatternDatasource({
core,
storage,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { metricChart, MetricChart } from './metric_expression';
import { metricChart, MetricChart } from './expression';
import { LensMultiTable } from '../types';
import React from 'react';
import { shallow } from 'enzyme';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './expression.scss';
import React from 'react';
import ReactDOM from 'react-dom';
import {

View file

@ -5,9 +5,7 @@
*/
import { CoreSetup } from 'kibana/public';
import { metricVisualization } from './metric_visualization';
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
import { metricChart, getMetricChartRenderer } from './metric_expression';
import { EditorFrameSetup, FormatFactory } from '../types';
export interface MetricVisualizationPluginSetupPlugins {
@ -23,10 +21,15 @@ export class MetricVisualization {
_core: CoreSetup | null,
{ expressions, formatFactory, editorFrame }: MetricVisualizationPluginSetupPlugins
) {
editorFrame.registerVisualization(async () => {
const { metricVisualization, metricChart, getMetricChartRenderer } = await import(
'../async_services'
);
expressions.registerFunction(() => metricChart);
expressions.registerRenderer(() => getMetricChartRenderer(formatFactory));
editorFrame.registerVisualization(metricVisualization);
return metricVisualization;
});
}
}

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './expression';
export * from './visualization';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { metricVisualization } from './metric_visualization';
import { metricVisualization } from './visualization';
import { State } from './types';
import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks';
import { generateId } from '../id_generator';

View file

@ -6,8 +6,6 @@
import { CoreSetup } from 'src/core/public';
import { ExpressionsSetup } from 'src/plugins/expressions/public';
import { pieVisualization } from './pie_visualization';
import { pie, getPieRenderer } from './register_expression';
import { EditorFrameSetup, FormatFactory } from '../types';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
@ -30,6 +28,9 @@ export class PieVisualization {
core: CoreSetup,
{ expressions, formatFactory, editorFrame, charts }: PieVisualizationPluginSetupPlugins
) {
editorFrame.registerVisualization(async () => {
const { pieVisualization, pie, getPieRenderer } = await import('../async_services');
expressions.registerFunction(() => pie);
expressions.registerRenderer(
@ -38,7 +39,7 @@ export class PieVisualization {
chartsThemeService: charts.theme,
})
);
editorFrame.registerVisualization(pieVisualization);
return pieVisualization;
});
}
}

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './expression';
export * from './visualization';

View file

@ -35,7 +35,6 @@ import { EditorFrameStart } from './types';
import { getLensAliasConfig } from './vis_type_alias';
import { getSearchProvider } from './search_provider';
import './index.scss';
import { getLensAttributeService, LensAttributeService } from './lens_attribute_service';
export interface LensPluginSetupDependencies {
@ -127,7 +126,7 @@ export class LensPlugin {
title: NOT_INTERNATIONALIZED_PRODUCT_NAME,
navLinkStatus: AppNavLinkStatus.hidden,
mount: async (params: AppMountParameters) => {
const { mountApp } = await import('./app_plugin/mounter');
const { mountApp } = await import('./async_services');
return mountApp(core, params, {
createEditorFrame: this.createEditorFrame!,
attributeService: this.attributeService!,

View file

@ -57,8 +57,12 @@ export interface EditorFrameInstance {
export interface EditorFrameSetup {
// generic type on the API functions to pull the "unknown vs. specific type" error into the implementation
registerDatasource: <T, P>(datasource: Datasource<T, P> | Promise<Datasource<T, P>>) => void;
registerVisualization: <T>(visualization: Visualization<T> | Promise<Visualization<T>>) => void;
registerDatasource: <T, P>(
datasource: Datasource<T, P> | (() => Promise<Datasource<T, P>>)
) => void;
registerVisualization: <T>(
visualization: Visualization<T> | (() => Promise<Visualization<T>>)
) => void;
}
export interface EditorFrameStart {

View file

@ -2,3 +2,16 @@
@include euiScrollBar;
overflow: auto;
}
.lnsExpressionRenderer {
@include euiScrollBar;
position: relative;
width: 100%;
height: 100%;
display: flex;
overflow: auto;
.lnsExpressionRenderer__component {
position: static; // Let the progress indicator position itself against the outer parent
}
}

View file

@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './visualization_container.scss';
import React from 'react';
import classNames from 'classnames';
import './visualization_container.scss';
interface Props extends React.HTMLAttributes<HTMLDivElement> {
isReady?: boolean;
reportTitle?: string;

View file

@ -1 +0,0 @@
@import 'xy_expression';

View file

@ -17,7 +17,7 @@ import {
SeriesNameFn,
Fit,
} from '@elastic/charts';
import { xyChart, XYChart } from './xy_expression';
import { xyChart, XYChart } from './expression';
import { LensMultiTable } from '../types';
import { KibanaDatatable, KibanaDatatableRow } from '../../../../../src/plugins/expressions/public';
import React from 'react';

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import './expression.scss';
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import moment from 'moment';

View file

@ -8,16 +8,6 @@ import { CoreSetup, IUiSettingsClient } from 'kibana/public';
import moment from 'moment-timezone';
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
import { UI_SETTINGS } from '../../../../../src/plugins/data/public';
import { xyVisualization } from './xy_visualization';
import { xyChart, getXyChartRenderer } from './xy_expression';
import {
legendConfig,
layerConfig,
yAxisConfig,
tickLabelsConfig,
gridlinesConfig,
axisTitlesVisibilityConfig,
} from './types';
import { EditorFrameSetup, FormatFactory } from '../types';
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
@ -44,6 +34,18 @@ export class XyVisualization {
core: CoreSetup,
{ expressions, formatFactory, editorFrame, charts }: XyVisualizationPluginSetupPlugins
) {
editorFrame.registerVisualization(async () => {
const {
legendConfig,
yAxisConfig,
tickLabelsConfig,
gridlinesConfig,
axisTitlesVisibilityConfig,
layerConfig,
xyChart,
getXyChartRenderer,
xyVisualization,
} = await import('../async_services');
expressions.registerFunction(() => legendConfig);
expressions.registerFunction(() => yAxisConfig);
expressions.registerFunction(() => tickLabelsConfig);
@ -60,7 +62,7 @@ export class XyVisualization {
histogramBarTarget: core.uiSettings.get<number>(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
})
);
editorFrame.registerVisualization(xyVisualization);
return xyVisualization;
});
}
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { xyVisualization } from './xy_visualization';
import { xyVisualization } from './visualization';
import { Position } from '@elastic/charts';
import { Operation } from '../types';
import { State, SeriesType } from './types';

View file

@ -0,0 +1,9 @@
/*
* 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.
*/
export * from './expression';
export * from './types';
export * from './visualization';