[visualizations] Reduce page load bundle to under 100kB (#98302)

* [visualizations] Reduce page load bundle to under 100kB

* move scss to async chunks

* fix CI

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Alexey Antonov 2021-05-03 13:22:40 +03:00 committed by GitHub
parent ff3badd80d
commit 582e6362c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 164 additions and 91 deletions

View file

@ -96,7 +96,7 @@ pageLoadAssetSize:
visTypeVega: 153573
visTypeVislib: 242838
visTypeXy: 113478
visualizations: 295025
visualizations: 90000
visualize: 57431
watcher: 43598
runtimeFields: 41752

View file

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

View file

@ -6,14 +6,13 @@
* Side Public License, v 1.
*/
import React, { ReactNode, Suspense } from 'react';
import React, { ReactNode, Suspense, lazy } from 'react';
import { EuiLoadingChart } from '@elastic/eui';
import classNames from 'classnames';
import { VisualizationNoResults } from './visualization_noresults';
import { VisualizationError } from './visualization_error';
import { IInterpreterRenderHandlers } from '../../../expressions/common';
interface VisualizationContainerProps {
export interface VisualizationContainerProps {
'data-test-subj'?: string;
className?: string;
children: ReactNode;
@ -22,6 +21,9 @@ interface VisualizationContainerProps {
error?: string;
}
const VisualizationNoResults = lazy(() => import('./visualization_noresults'));
const VisualizationError = lazy(() => import('./visualization_error'));
export const VisualizationContainer = ({
'data-test-subj': dataTestSubj = '',
className,

View file

@ -44,3 +44,6 @@ export class VisualizationError extends React.Component<VisualizationNoResultsPr
}
}
}
// eslint-disable-next-line import/no-default-export
export default VisualizationError;

View file

@ -48,3 +48,6 @@ export class VisualizationNoResults extends React.Component<VisualizationNoResul
}
}
}
// eslint-disable-next-line import/no-default-export
export default VisualizationNoResults;

View file

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

View file

@ -7,7 +7,7 @@
*/
import { Vis } from '../types';
import {
import type {
VisualizeInput,
VisualizeEmbeddable,
VisualizeByValueInput,
@ -31,6 +31,7 @@ import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants';
import { SavedVisualizationsLoader } from '../saved_visualizations';
import { IndexPattern } from '../../../data/public';
import { createVisualizeEmbeddableAsync } from './visualize_embeddable_async';
export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDeps) => async (
vis: Vis,
@ -72,7 +73,7 @@ export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDe
dashboardSave: Boolean(getCapabilities().dashboard?.showWriteControls),
};
return new VisualizeEmbeddable(
return createVisualizeEmbeddableAsync(
getTimeFilter(),
{
vis,

View file

@ -7,8 +7,9 @@
*/
export { DisabledLabEmbeddable } from './disabled_lab_embeddable';
export { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable';
export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory';
export { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
export { VIS_EVENT_TO_TRIGGER } from './events';
export { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
export type { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable';

View file

@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { VisualizeEmbeddable as VisualizeEmbeddableType } from './visualize_embeddable';
export const createVisualizeEmbeddableAsync = async (
...args: ConstructorParameters<typeof VisualizeEmbeddableType>
) => {
// Build optimization. Move app styles from main bundle
// @ts-expect-error TS error, cannot find type declaration for scss
await import('./embeddables.scss');
const { VisualizeEmbeddable } = await import('./visualize_embeddable');
return new VisualizeEmbeddable(...args);
};

View file

@ -7,12 +7,12 @@
*/
import { i18n } from '@kbn/i18n';
import { SavedObjectMetaData, OnSaveProps } from 'src/plugins/saved_objects/public';
import { first } from 'rxjs/operators';
import { EmbeddableStateWithType } from 'src/plugins/embeddable/common';
import { SavedObjectAttributes } from '../../../../core/public';
import type { SavedObjectMetaData, OnSaveProps } from 'src/plugins/saved_objects/public';
import type { EmbeddableStateWithType } from 'src/plugins/embeddable/common';
import { extractSearchSourceReferences } from '../../../data/public';
import { SavedObjectReference } from '../../../../core/public';
import type { SavedObjectAttributes, SavedObjectReference } from '../../../../core/public';
import {
EmbeddableFactoryDefinition,
@ -21,8 +21,8 @@ import {
IContainer,
AttributeService,
} from '../../../embeddable/public';
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
import {
import type { DisabledLabEmbeddable } from './disabled_lab_embeddable';
import type {
VisualizeByReferenceInput,
VisualizeByValueInput,
VisualizeEmbeddable,
@ -31,7 +31,8 @@ import {
VisualizeSavedObjectAttributes,
} from './visualize_embeddable';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
import { SerializedVis, Vis } from '../vis';
import type { SerializedVis, Vis } from '../vis';
import { createVisAsync } from '../vis_async';
import {
getCapabilities,
getTypes,
@ -47,10 +48,10 @@ import {
injectControlsReferences,
} from '../saved_visualizations/saved_visualization_references';
import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
import { StartServicesGetter } from '../../../kibana_utils/public';
import { VisualizationsStartDeps } from '../plugin';
import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants';
import { checkForDuplicateTitle } from '../../../saved_objects/public';
import type { StartServicesGetter } from '../../../kibana_utils/public';
import type { VisualizationsStartDeps } from '../plugin';
interface VisualizationAttributes extends SavedObjectAttributes {
visState: string;
@ -147,8 +148,8 @@ export class VisualizeEmbeddableFactory
try {
const savedObject = await savedVisualizations.get(savedObjectId);
const visState = convertToSerializedVis(savedObject);
const vis = new Vis(savedObject.visState.type, visState);
await vis.setState(visState);
const vis = await createVisAsync(savedObject.visState.type, visState);
return createVisEmbeddableFromObject(this.deps)(
vis,
input,
@ -167,8 +168,7 @@ export class VisualizeEmbeddableFactory
// to allow for in place creation of visualizations without having to navigate away to a new URL.
if (input.savedVis) {
const visState = input.savedVis;
const vis = new Vis(visState.type, visState);
await vis.setState(visState);
const vis = await createVisAsync(visState.type, visState);
const savedVisualizations = getSavedVisualizationsLoader();
return createVisEmbeddableFromObject(this.deps)(
vis,

View file

@ -1,2 +0,0 @@
@import 'embeddable/index';
@import 'components/index';

View file

@ -9,14 +9,13 @@
import { PublicContract } from '@kbn/utility-types';
import { PluginInitializerContext } from 'src/core/public';
import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin';
import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable';
import type { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable';
export function plugin(initializerContext: PluginInitializerContext) {
return new VisualizationsPlugin(initializerContext);
}
/** @public static code */
export { Vis } from './vis';
export { TypesService } from './vis_types/types_service';
export { VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER } from './embeddable';
export { VisualizationContainer } from './components';
@ -24,15 +23,15 @@ export { getVisSchemas } from './vis_schemas';
/** @public types */
export { VisualizationsSetup, VisualizationsStart };
export { VisGroups } from './vis_types';
export { VisGroups } from './vis_types/vis_groups_enum';
export type { BaseVisType, VisTypeAlias, VisTypeDefinition, Schema, ISchemas } from './vis_types';
export { SerializedVis, SerializedVisData, VisData } from './vis';
export type { Vis, SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract<VisualizeEmbeddableFactory>;
export type VisualizeEmbeddableContract = PublicContract<VisualizeEmbeddable>;
export { VisualizeInput } from './embeddable';
export { SchemaConfig } from './vis_schemas';
export { updateOldState } from './legacy/vis_update_state';
export { PersistedState } from './persisted_state';
export type { PersistedState } from './persisted_state';
export {
ISavedVis,
VisSavedObject,

View file

@ -5,18 +5,6 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import './index.scss';
import {
PluginInitializerContext,
CoreSetup,
CoreStart,
Plugin,
ApplicationStart,
SavedObjectsClientContract,
} from '../../../core/public';
import { TypesService, TypesSetup, TypesStart } from './vis_types';
import {
setUISettings,
setTypes,
@ -42,27 +30,42 @@ import {
VisualizeEmbeddableFactory,
createVisEmbeddableFromObject,
} from './embeddable';
import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public';
import { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public';
import { TypesService } from './vis_types/types_service';
import { range as rangeExpressionFunction } from './expression_functions/range';
import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension';
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public';
import {
Setup as InspectorSetup,
Start as InspectorStart,
} from '../../../plugins/inspector/public';
import { UsageCollectionSetup } from '../../usage_collection/public';
import { createStartServicesGetter, StartServicesGetter } from '../../kibana_utils/public';
import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations';
import { SerializedVis, Vis } from './vis';
import type { SerializedVis, Vis } from './vis';
import { showNewVisModal } from './wizard';
import { UiActionsStart } from '../../ui_actions/public';
import {
convertFromSerializedVis,
convertToSerializedVis,
} from './saved_visualizations/_saved_vis';
import { createSavedSearchesLoader } from '../../discover/public';
import { SavedObjectsStart } from '../../saved_objects/public';
import type {
PluginInitializerContext,
CoreSetup,
CoreStart,
Plugin,
ApplicationStart,
SavedObjectsClientContract,
} from '../../../core/public';
import type { UsageCollectionSetup } from '../../usage_collection/public';
import type { UiActionsStart } from '../../ui_actions/public';
import type { SavedObjectsStart } from '../../saved_objects/public';
import type { TypesSetup, TypesStart } from './vis_types';
import type {
Setup as InspectorSetup,
Start as InspectorStart,
} from '../../../plugins/inspector/public';
import type { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public';
import type { ExpressionsSetup, ExpressionsStart } from '../../expressions/public';
import type { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public';
import { createVisAsync } from './vis_async';
/**
* Interface for this plugin's returned setup/start contracts.
@ -180,11 +183,8 @@ export class VisualizationsPlugin
* @param {IIndexPattern} indexPattern - index pattern to use
* @param {VisState} visState - visualization configuration
*/
createVis: async (visType: string, visState: SerializedVis) => {
const vis = new Vis(visType);
await vis.setState(visState);
return vis;
},
createVis: async (visType: string, visState: SerializedVis) =>
await createVisAsync(visType, visState),
convertToSerializedVis,
convertFromSerializedVis,
savedVisualizationsLoader,

View file

@ -13,14 +13,14 @@
*
* NOTE: It's a type of SavedObject, but specific to visualizations.
*/
import { SavedObjectsStart, SavedObject } from '../../../../plugins/saved_objects/public';
import type { SavedObjectsStart, SavedObject } from '../../../../plugins/saved_objects/public';
// @ts-ignore
import { updateOldState } from '../legacy/vis_update_state';
import { extractReferences, injectReferences } from './saved_visualization_references';
import { IIndexPattern, IndexPatternsContract } from '../../../../plugins/data/public';
import { ISavedVis, SerializedVis } from '../types';
import { createSavedSearchesLoader } from '../../../discover/public';
import { SavedObjectsClientContract } from '../../../../core/public';
import type { SavedObjectsClientContract } from '../../../../core/public';
import type { IIndexPattern, IndexPatternsContract } from '../../../../plugins/data/public';
import type { ISavedVis, SerializedVis } from '../types';
export interface SavedVisServices {
savedObjectsClient: SavedObjectsClientContract;

View file

@ -14,7 +14,7 @@ import {
SavedObjectsFindOptions,
} from '../../../../core/public';
import { SavedObjectLoader } from '../../../../plugins/saved_objects/public';
import { VisTypeAlias } from '../vis_types';
import type { VisTypeAlias } from '../vis_types';
import { VisualizationsAppExtension } from '../vis_types/vis_type_alias_registry';
/**

View file

@ -6,11 +6,11 @@
* Side Public License, v 1.
*/
import { SavedObjectReference, SavedObjectsFindOptionsReference } from 'kibana/public';
import type { SavedObjectReference, SavedObjectsFindOptionsReference } from 'kibana/public';
import { SavedObjectLoader } from '../../../../plugins/saved_objects/public';
import { findListItems } from './find_list_items';
import { createSavedVisClass, SavedVisServices } from './_saved_vis';
import { TypesStart } from '../vis_types';
import type { TypesStart } from '../vis_types';
export interface SavedVisServicesWithVisualizations extends SavedVisServices {
visualizationTypes: TypesStart;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import {
import type {
ApplicationStart,
Capabilities,
ChromeStart,
@ -16,7 +16,7 @@ import {
SavedObjectsStart,
DocLinksStart,
} from '../../../core/public';
import { TypesStart } from './vis_types';
import type { TypesStart } from './vis_types';
import { createGetterSetter } from '../../../plugins/kibana_utils/public';
import { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public';
import { UsageCollectionSetup } from '../../../plugins/usage_collection/public';

View file

@ -15,11 +15,11 @@ import {
} from '../../../plugins/data/public';
import { ExpressionAstExpression } from '../../expressions/public';
import { SerializedVis, Vis } from './vis';
import { PersistedState } from './persisted_state';
import { VisParams } from '../common';
import type { SerializedVis, Vis } from './vis';
import type { PersistedState } from './persisted_state';
import type { VisParams } from '../common';
export { Vis, SerializedVis, VisParams };
export type { Vis, SerializedVis, VisParams };
export interface SavedVisState {
title: string;
type: string;

View file

@ -213,3 +213,6 @@ export class Vis<TVisParams = VisParams> {
return newConfigs;
}
}
// eslint-disable-next-line import/no-default-export
export default Vis;

View file

@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SerializedVis } from './vis';
import type { VisParams } from '../common';
export const createVisAsync = async <TVisParams = VisParams>(
visType: string,
visState: SerializedVis<TVisParams> = {} as any
) => {
// Build optimization. Move app styles from main bundle
// @ts-expect-error TS error, cannot find type declaration for scss
await import('./vis.scss');
const { Vis } = await import('./vis');
const vis = new Vis(visType, visState);
await vis.setState(visState);
return vis;
};

View file

@ -8,8 +8,9 @@
import { defaultsDeep } from 'lodash';
import { VisParams } from '../types';
import { VisTypeDefinition, VisTypeOptions, VisGroups } from './types';
import type { VisParams } from '../types';
import type { VisTypeDefinition, VisTypeOptions } from './types';
import { VisGroups } from './vis_groups_enum';
import { Schemas } from './schemas';
const defaultOptions: VisTypeOptions = {

View file

@ -8,6 +8,6 @@
export * from './types_service';
export { Schemas } from './schemas';
export { VisGroups } from './types';
export { VisGroups } from './vis_groups_enum';
export { BaseVisType } from './base_vis_type';
export type { VisTypeDefinition, ISchemas, Schema } from './types';

View file

@ -6,11 +6,12 @@
* Side Public License, v 1.
*/
import { IconType } from '@elastic/eui';
import { ReactNode } from 'react';
import { Adapters } from 'src/plugins/inspector';
import { IndexPattern, AggGroupNames, AggParam, AggGroupName } from '../../../data/public';
import { Vis, VisEditorOptionsProps, VisParams, VisToExpressionAst } from '../types';
import type { IconType } from '@elastic/eui';
import type { ReactNode } from 'react';
import type { Adapters } from 'src/plugins/inspector';
import type { IndexPattern, AggGroupNames, AggParam, AggGroupName } from '../../../data/public';
import type { Vis, VisEditorOptionsProps, VisParams, VisToExpressionAst } from '../types';
import { VisGroups } from './vis_groups_enum';
export interface VisTypeOptions {
showTimePicker: boolean;
@ -20,12 +21,6 @@ export interface VisTypeOptions {
hierarchicalData: boolean;
}
export enum VisGroups {
PROMOTED = 'promoted',
TOOLS = 'tools',
AGGBASED = 'aggbased',
}
export interface ISchemas {
[AggGroupNames.Buckets]: Schema[];
[AggGroupNames.Metrics]: Schema[];

View file

@ -8,7 +8,8 @@
import { visTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry';
import { BaseVisType } from './base_vis_type';
import { VisTypeDefinition, VisGroups } from './types';
import { VisTypeDefinition } from './types';
import { VisGroups } from './vis_groups_enum';
/**
* Vis Types Service

View file

@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export enum VisGroups {
PROMOTED = 'promoted',
TOOLS = 'tools',
AGGBASED = 'aggbased',
}

View file

@ -25,8 +25,8 @@ import {
} from '@elastic/eui';
import { memoizeLast } from '../../legacy/memoize';
import { VisGroups } from '../../vis_types/vis_groups_enum';
import type { BaseVisType, TypesStart } from '../../vis_types';
import { VisGroups } from '../../vis_types';
import { DialogNavigation } from '../dialog_navigation';
import './agg_based_selection.scss';

View file

@ -29,9 +29,9 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DocLinksStart } from '../../../../../core/public';
import { VisTypeAlias } from '../../vis_types/vis_type_alias_registry';
import type { BaseVisType, TypesStart } from '../../vis_types';
import { VisGroups } from '../../vis_types';
import { VisGroups } from '../../vis_types/vis_groups_enum';
import type { VisTypeAlias } from '../../vis_types/vis_type_alias_registry';
import './group_selection.scss';
interface GroupSelectionProps {

View file

@ -8,11 +8,10 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import { TypesStart, VisGroups } from '../vis_types';
import { TypesStart, VisGroups, BaseVisType } from '../vis_types';
import NewVisModal from './new_vis_modal';
import { ApplicationStart, SavedObjectsStart, DocLinksStart } from '../../../../core/public';
import { embeddablePluginMock } from '../../../embeddable/public/mocks';
import { BaseVisType } from '../vis_types';
describe('NewVisModal', () => {
const defaultVisTypeParams = {

View file

@ -0,0 +1,9 @@
.visualization {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
overflow: auto;
position: relative;
flex: 1 1 100%;
}

View file

@ -5,6 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import './main.scss';
import React from 'react';
import { EuiPage, EuiPageBody, EuiPageContent, EuiPageContentHeader } from '@elastic/eui';