From 178bb1d3abbf45c62203cb1bff054bb474a9b4c2 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Thu, 5 Dec 2019 08:30:39 +0100 Subject: [PATCH] Echo: mechanism for collecting custom events lazily (#20365) * Introduce Echo for collecting frontend metrics * Update public/app/core/services/echo/Echo.ts Co-Authored-By: Peter Holmberg * Custom meta when adding event * Rename consumer to backend * Remove buffer from Echo * Minor tweaks * Update package.json * Update public/app/app.ts * Update public/app/app.ts * Collect paint metrics when collecting tti. Remove echoBackendFactory * Update yarn.lock * Move Echo interfaces to runtime * progress on meta and echo * Collect meta analytics events * Move MetaanalyticsBackend to enterprise repo * Fixed unit tests * Removed unused type from test * Fixed issues with chunk loading (reverted index-template changes) * Restored changes * Fixed webpack prod --- package.json | 1 + packages/grafana-runtime/src/index.ts | 2 + .../grafana-runtime/src/services/EchoSrv.ts | 57 ++ .../grafana-runtime/src/services/index.ts | 1 + .../grafana-runtime/src/types/analytics.ts | 18 + packages/grafana-runtime/src/types/index.ts | 1 + .../grafana-runtime/src/utils/analytics.ts | 9 + .../src/themes/_variables.dark.scss.tmpl.ts | 2 +- public/app/app.ts | 26 + .../sidemenu/BottomNavLinks.test.tsx | 1 + public/app/core/services/context_srv.ts | 1 + public/app/core/services/echo/Echo.ts | 89 +++ public/app/core/services/echo/EchoSrv.ts | 12 + .../echo/backends/PerformanceBackend.ts | 49 ++ .../dashboard/state/PanelQueryRunner.test.ts | 25 +- .../dashboard/state/analyticsProcessor.ts | 56 ++ .../dashboard/state/runRequest.test.ts | 16 + .../features/dashboard/state/runRequest.ts | 4 +- public/app/index.ts | 2 + public/app/routes/GrafanaCtrl.ts | 1 - public/views/index-template.html | 529 ++++++++++-------- scripts/webpack/webpack.dev.js | 2 +- scripts/webpack/webpack.prod.js | 2 +- yarn.lock | 5 + 24 files changed, 650 insertions(+), 261 deletions(-) create mode 100644 packages/grafana-runtime/src/services/EchoSrv.ts create mode 100644 packages/grafana-runtime/src/types/analytics.ts create mode 100644 packages/grafana-runtime/src/types/index.ts create mode 100644 packages/grafana-runtime/src/utils/analytics.ts create mode 100644 public/app/core/services/echo/Echo.ts create mode 100644 public/app/core/services/echo/EchoSrv.ts create mode 100644 public/app/core/services/echo/backends/PerformanceBackend.ts create mode 100644 public/app/features/dashboard/state/analyticsProcessor.ts diff --git a/package.json b/package.json index 770dba91c14..d221c052fad 100644 --- a/package.json +++ b/package.json @@ -256,6 +256,7 @@ "tether": "1.4.5", "tether-drop": "https://github.com/torkelo/drop/tarball/master", "tinycolor2": "1.4.1", + "tti-polyfill": "0.2.2", "xss": "1.0.3" }, "resolutions": { diff --git a/packages/grafana-runtime/src/index.ts b/packages/grafana-runtime/src/index.ts index 10acfb3744e..e752f49ebc5 100644 --- a/packages/grafana-runtime/src/index.ts +++ b/packages/grafana-runtime/src/index.ts @@ -1,3 +1,5 @@ export * from './services'; export * from './config'; +export * from './types'; export { loadPluginCss, SystemJS } from './utils/plugin'; +export { reportMetaAnalytics } from './utils/analytics'; diff --git a/packages/grafana-runtime/src/services/EchoSrv.ts b/packages/grafana-runtime/src/services/EchoSrv.ts new file mode 100644 index 00000000000..6c5b30a83d6 --- /dev/null +++ b/packages/grafana-runtime/src/services/EchoSrv.ts @@ -0,0 +1,57 @@ +interface SizeMeta { + width: number; + height: number; +} + +export interface EchoMeta { + screenSize: SizeMeta; + windowSize: SizeMeta; + userAgent: string; + url?: string; + /** + * A unique browser session + */ + sessionId: string; + userLogin: string; + userId: number; + userSignedIn: boolean; + ts: number; +} + +export interface EchoBackend { + options: O; + supportedEvents: EchoEventType[]; + flush: () => void; + addEvent: (event: T) => void; +} + +export interface EchoEvent { + type: EchoEventType; + payload: P; + meta: EchoMeta; +} + +export enum EchoEventType { + Performance = 'performance', + MetaAnalytics = 'meta-analytics', +} + +export interface EchoSrv { + flush(): void; + addBackend(backend: EchoBackend): void; + addEvent(event: Omit, meta?: {}): void; +} + +let singletonInstance: EchoSrv; + +export function setEchoSrv(instance: EchoSrv) { + singletonInstance = instance; +} + +export function getEchoSrv(): EchoSrv { + return singletonInstance; +} + +export const registerEchoBackend = (backend: EchoBackend) => { + getEchoSrv().addBackend(backend); +}; diff --git a/packages/grafana-runtime/src/services/index.ts b/packages/grafana-runtime/src/services/index.ts index c92ac500c6a..d8daad5865d 100644 --- a/packages/grafana-runtime/src/services/index.ts +++ b/packages/grafana-runtime/src/services/index.ts @@ -2,3 +2,4 @@ export * from './backendSrv'; export * from './AngularLoader'; export * from './dataSourceSrv'; export * from './LocationSrv'; +export * from './EchoSrv'; diff --git a/packages/grafana-runtime/src/types/analytics.ts b/packages/grafana-runtime/src/types/analytics.ts new file mode 100644 index 00000000000..804ab09d5a5 --- /dev/null +++ b/packages/grafana-runtime/src/types/analytics.ts @@ -0,0 +1,18 @@ +import { EchoEvent, EchoEventType } from '../services/EchoSrv'; + +export interface MetaAnalyticsEventPayload { + eventName: string; + dashboardId?: number; + dashboardUid?: string; + dashboardName?: string; + folderName?: string; + panelId?: number; + panelName?: string; + datasourceName: string; + datasourceId?: number; + error?: string; + duration: number; + dataSize?: number; +} + +export interface MetaAnalyticsEvent extends EchoEvent {} diff --git a/packages/grafana-runtime/src/types/index.ts b/packages/grafana-runtime/src/types/index.ts new file mode 100644 index 00000000000..77dc4f52a6e --- /dev/null +++ b/packages/grafana-runtime/src/types/index.ts @@ -0,0 +1 @@ +export * from './analytics'; diff --git a/packages/grafana-runtime/src/utils/analytics.ts b/packages/grafana-runtime/src/utils/analytics.ts new file mode 100644 index 00000000000..72da85390de --- /dev/null +++ b/packages/grafana-runtime/src/utils/analytics.ts @@ -0,0 +1,9 @@ +import { getEchoSrv, EchoEventType } from '../services/EchoSrv'; +import { MetaAnalyticsEvent, MetaAnalyticsEventPayload } from '../types/analytics'; + +export const reportMetaAnalytics = (payload: MetaAnalyticsEventPayload) => { + getEchoSrv().addEvent({ + type: EchoEventType.MetaAnalytics, + payload, + }); +}; diff --git a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts index bc7eb1fa0ea..9eeba2e2738 100644 --- a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts @@ -187,7 +187,7 @@ $btn-drag-image: '../img/grab_dark.svg'; $navbar-btn-gicon-brightness: brightness(0.5); -$btn-active-box-shadow: 0px 0px 4px rgba(255,120,10,0.5); +$btn-active-box-shadow: 0px 0px 4px rgba(255, 120, 10, 0.5); // Forms // ------------------------- diff --git a/public/app/app.ts b/public/app/app.ts index 3016f41167c..dbb31244744 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -16,6 +16,8 @@ import 'vendor/angular-other/angular-strap'; import $ from 'jquery'; import angular from 'angular'; import config from 'app/core/config'; +// @ts-ignore +import ttiPolyfill from 'tti-polyfill'; // @ts-ignore ignoring this for now, otherwise we would have to extend _ interface with move import _ from 'lodash'; import { AppEvents, setMarkdownOptions, setLocale } from '@grafana/data'; @@ -34,6 +36,10 @@ _.move = (array: [], fromIndex: number, toIndex: number) => { import { coreModule, angularModules } from 'app/core/core_module'; import { registerAngularDirectives } from 'app/core/core'; import { setupAngularRoutes } from 'app/routes/routes'; +import { setEchoSrv, registerEchoBackend } from '@grafana/runtime'; +import { Echo } from './core/services/echo/Echo'; +import { reportPerformance } from './core/services/echo/EchoSrv'; +import { PerformanceBackend } from './core/services/echo/backends/PerformanceBackend'; import 'app/routes/GrafanaCtrl'; import 'app/features/all'; @@ -163,6 +169,26 @@ export class GrafanaApp { importPluginModule(modulePath); } } + + initEchoSrv() { + setEchoSrv(new Echo({ debug: process.env.NODE_ENV === 'development' })); + + ttiPolyfill.getFirstConsistentlyInteractive().then((tti: any) => { + // Collecting paint metrics first + const paintMetrics = performance.getEntriesByType('paint'); + + for (const metric of paintMetrics) { + reportPerformance(metric.name, Math.round(metric.startTime + metric.duration)); + } + reportPerformance('tti', tti); + }); + + registerEchoBackend(new PerformanceBackend({})); + + window.addEventListener('DOMContentLoaded', () => { + reportPerformance('dcl', Math.round(performance.now())); + }); + } } export default new GrafanaApp(); diff --git a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx index e3a98bd9736..747ad8f0367 100644 --- a/public/app/core/components/sidemenu/BottomNavLinks.test.tsx +++ b/public/app/core/components/sidemenu/BottomNavLinks.test.tsx @@ -21,6 +21,7 @@ const setup = (propOverrides?: object) => { orgCount: 2, orgRole: '', orgId: 1, + login: 'hello', orgName: 'Grafana', timezone: 'UTC', helpFlags1: 1, diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index bbb44886386..99d96271ce5 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -9,6 +9,7 @@ export class User { orgRole: any; orgId: number; orgName: string; + login: string; orgCount: number; timezone: string; helpFlags1: number; diff --git a/public/app/core/services/echo/Echo.ts b/public/app/core/services/echo/Echo.ts new file mode 100644 index 00000000000..e844bb5d0be --- /dev/null +++ b/public/app/core/services/echo/Echo.ts @@ -0,0 +1,89 @@ +import { EchoBackend, EchoMeta, EchoEvent, EchoSrv } from '@grafana/runtime'; +import { contextSrv } from '../context_srv'; + +interface EchoConfig { + // How often should metrics be reported + flushInterval: number; + // Enables debug mode + debug: boolean; +} + +/** + * Echo is a service for collecting events from Grafana client-app + * It collects events, distributes them across registered backend and flushes once per configured interval + * It's up to the registered backend to decide what to do with a given type of metric + */ +export class Echo implements EchoSrv { + private config: EchoConfig = { + flushInterval: 10000, // By default Echo flushes every 10s + debug: false, + }; + + private backends: EchoBackend[] = []; + // meta data added to every event collected + + constructor(config?: Partial) { + this.config = { + ...this.config, + ...config, + }; + setInterval(this.flush, this.config.flushInterval); + } + + logDebug = (...msg: any) => { + if (this.config.debug) { + // tslint:disable-next-line + // console.debug('ECHO:', ...msg); + } + }; + + flush = () => { + for (const backend of this.backends) { + backend.flush(); + } + }; + + addBackend = (backend: EchoBackend) => { + this.logDebug('Adding backend', backend); + this.backends.push(backend); + }; + + addEvent = (event: Omit, _meta?: {}) => { + const meta = this.getMeta(); + const _event = { + ...event, + meta: { + ...meta, + ..._meta, + }, + }; + + for (const backend of this.backends) { + if (backend.supportedEvents.length === 0 || backend.supportedEvents.indexOf(_event.type) > -1) { + backend.addEvent(_event); + } + } + + this.logDebug('Adding event', _event); + }; + + getMeta = (): EchoMeta => { + return { + sessionId: '', + userId: contextSrv.user.id, + userLogin: contextSrv.user.login, + userSignedIn: contextSrv.user.isSignedIn, + screenSize: { + width: window.innerWidth, + height: window.innerHeight, + }, + windowSize: { + width: window.screen.width, + height: window.screen.height, + }, + userAgent: window.navigator.userAgent, + ts: performance.now(), + url: window.location.href, + }; + }; +} diff --git a/public/app/core/services/echo/EchoSrv.ts b/public/app/core/services/echo/EchoSrv.ts new file mode 100644 index 00000000000..e8eb01e9c61 --- /dev/null +++ b/public/app/core/services/echo/EchoSrv.ts @@ -0,0 +1,12 @@ +import { getEchoSrv, EchoEventType } from '@grafana/runtime'; +import { PerformanceEvent } from './backends/PerformanceBackend'; + +export const reportPerformance = (metric: string, value: number) => { + getEchoSrv().addEvent({ + type: EchoEventType.Performance, + payload: { + metricName: metric, + duration: value, + }, + }); +}; diff --git a/public/app/core/services/echo/backends/PerformanceBackend.ts b/public/app/core/services/echo/backends/PerformanceBackend.ts new file mode 100644 index 00000000000..575a5b57a38 --- /dev/null +++ b/public/app/core/services/echo/backends/PerformanceBackend.ts @@ -0,0 +1,49 @@ +import { EchoBackend, EchoEvent, EchoEventType } from '@grafana/runtime'; + +export interface PerformanceEventPayload { + metricName: string; + duration: number; +} + +export interface PerformanceEvent extends EchoEvent {} + +export interface PerformanceBackendOptions { + url?: string; +} + +/** + * Echo's performance metrics consumer + * Reports performance metrics to given url (TODO) + */ +export class PerformanceBackend implements EchoBackend { + private buffer: PerformanceEvent[] = []; + supportedEvents = [EchoEventType.Performance]; + + constructor(public options: PerformanceBackendOptions) {} + + addEvent = (e: EchoEvent) => { + this.buffer.push(e); + }; + + flush = () => { + if (this.buffer.length === 0) { + return; + } + + const result = { + metrics: this.buffer, + }; + + // Currently we don have API for sending the metrics hence loging to console in dev environment + if (process.env.NODE_ENV === 'development') { + console.log('PerformanceBackend flushing:', result); + } + + this.buffer = []; + + // TODO: Enable backend request when we have metrics API + // if (this.options.url) { + // getBackendSrv().post(this.options.url, result); + // } + }; +} diff --git a/public/app/features/dashboard/state/PanelQueryRunner.test.ts b/public/app/features/dashboard/state/PanelQueryRunner.test.ts index daaa6ac8313..ff6e9c72073 100644 --- a/public/app/features/dashboard/state/PanelQueryRunner.test.ts +++ b/public/app/features/dashboard/state/PanelQueryRunner.test.ts @@ -1,22 +1,19 @@ import { PanelQueryRunner } from './PanelQueryRunner'; import { PanelData, DataQueryRequest, dateTime, ScopedVars } from '@grafana/data'; -import { PanelModel } from './PanelModel'; +import { DashboardModel } from './index'; +import { setEchoSrv } from '@grafana/runtime'; +import { Echo } from '../../../core/services/echo/Echo'; jest.mock('app/core/services/backend_srv'); -// Defined within setup functions -const panelsForCurrentDashboardMock: { [key: number]: PanelModel } = {}; +const dashboardModel = new DashboardModel({ + panels: [{ id: 1, type: 'graph' }], +}); jest.mock('app/features/dashboard/services/DashboardSrv', () => ({ getDashboardSrv: () => { return { - getCurrent: () => { - return { - getPanelById: (id: number) => { - return panelsForCurrentDashboardMock[id]; - }, - }; - }, + getCurrent: () => dashboardModel, }; }, })); @@ -68,6 +65,7 @@ function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn }; beforeEach(async () => { + setEchoSrv(new Echo()); setupFn(); const datasource: any = { @@ -103,13 +101,6 @@ function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn }, }); - panelsForCurrentDashboardMock[1] = { - id: 1, - getQueryRunner: () => { - return ctx.runner; - }, - } as PanelModel; - ctx.events = []; ctx.runner.run(args); }); diff --git a/public/app/features/dashboard/state/analyticsProcessor.ts b/public/app/features/dashboard/state/analyticsProcessor.ts new file mode 100644 index 00000000000..8be85693964 --- /dev/null +++ b/public/app/features/dashboard/state/analyticsProcessor.ts @@ -0,0 +1,56 @@ +import { getDashboardSrv } from '../services/DashboardSrv'; + +import { PanelData, LoadingState, DataSourceApi } from '@grafana/data'; + +import { reportMetaAnalytics, MetaAnalyticsEventPayload } from '@grafana/runtime'; + +export function getAnalyticsProcessor(datasource: DataSourceApi) { + let done = false; + + return (data: PanelData) => { + if (!data.request || done) { + return; + } + + if (data.state !== LoadingState.Done && data.state !== LoadingState.Error) { + return; + } + + const eventData: MetaAnalyticsEventPayload = { + datasourceName: datasource.name, + datasourceId: datasource.id, + panelId: data.request.panelId, + dashboardId: data.request.dashboardId, + // app: 'dashboard', + // count: 1, + dataSize: 0, + duration: data.request.endTime - data.request.startTime, + eventName: 'data-request', + // sessionId: '', + }; + + // enrich with dashboard info + const dashboard = getDashboardSrv().getCurrent(); + if (dashboard) { + eventData.dashboardId = dashboard.id; + eventData.dashboardName = dashboard.title; + eventData.dashboardUid = dashboard.uid; + eventData.folderName = dashboard.meta.folderTitle; + } + + if (data.series.length > 0) { + // estimate size + eventData.dataSize = data.series.length * data.series[0].length; + } + + if (data.error) { + eventData.error = data.error.message; + } + + reportMetaAnalytics(eventData); + + // this done check is to make sure we do not double emit events in case + // there are multiple responses with done state + done = true; + }; +} diff --git a/public/app/features/dashboard/state/runRequest.test.ts b/public/app/features/dashboard/state/runRequest.test.ts index 6d08ee9fa85..0d9a2c8f290 100644 --- a/public/app/features/dashboard/state/runRequest.test.ts +++ b/public/app/features/dashboard/state/runRequest.test.ts @@ -10,9 +10,24 @@ import { import { Subscriber, Observable, Subscription } from 'rxjs'; import { runRequest } from './runRequest'; import { deepFreeze } from '../../../../test/core/redux/reducerTester'; +import { DashboardModel } from './DashboardModel'; +import { setEchoSrv } from '@grafana/runtime'; +import { Echo } from '../../../core/services/echo/Echo'; jest.mock('app/core/services/backend_srv'); +const dashboardModel = new DashboardModel({ + panels: [{ id: 1, type: 'graph' }], +}); + +jest.mock('app/features/dashboard/services/DashboardSrv', () => ({ + getDashboardSrv: () => { + return { + getCurrent: () => dashboardModel, + }; + }, +})); + class ScenarioCtx { ds: DataSourceApi; request: DataQueryRequest; @@ -84,6 +99,7 @@ function runRequestScenario(desc: string, fn: (ctx: ScenarioCtx) => void) { const ctx = new ScenarioCtx(); beforeEach(() => { + setEchoSrv(new Echo()); ctx.reset(); return ctx.setupFn(); }); diff --git a/public/app/features/dashboard/state/runRequest.ts b/public/app/features/dashboard/state/runRequest.ts index e6ae8a85bfb..e97260d76b0 100644 --- a/public/app/features/dashboard/state/runRequest.ts +++ b/public/app/features/dashboard/state/runRequest.ts @@ -1,7 +1,7 @@ // Libraries import { Observable, of, timer, merge, from } from 'rxjs'; import { flatten, map as lodashMap, isArray, isString } from 'lodash'; -import { map, catchError, takeUntil, mapTo, share, finalize } from 'rxjs/operators'; +import { map, catchError, takeUntil, mapTo, share, finalize, tap } from 'rxjs/operators'; // Utils & Services import { getBackendSrv } from 'app/core/services/backend_srv'; // Types @@ -18,6 +18,7 @@ import { DataFrame, guessFieldTypes, } from '@grafana/data'; +import { getAnalyticsProcessor } from './analyticsProcessor'; import { ExpressionDatasourceID, expressionDatasource } from 'app/features/expressions/ExpressionDatasource'; type MapOfResponsePackets = { [str: string]: DataQueryResponse }; @@ -119,6 +120,7 @@ export function runRequest(datasource: DataSourceApi, request: DataQueryRequest) error: processQueryError(err), }) ), + tap(getAnalyticsProcessor(datasource)), // finalize is triggered when subscriber unsubscribes // This makes sure any still running network requests are cancelled finalize(cancelNetworkRequestsOnUnsubscribe(request)), diff --git a/public/app/index.ts b/public/app/index.ts index c909848ef39..c55f2850464 100644 --- a/public/app/index.ts +++ b/public/app/index.ts @@ -1,2 +1,4 @@ import app from './app'; + +app.initEchoSrv(); app.init(); diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts index 997c9359915..2df8dab54e5 100644 --- a/public/app/routes/GrafanaCtrl.ts +++ b/public/app/routes/GrafanaCtrl.ts @@ -141,7 +141,6 @@ export function grafanaAppDirective( controller: GrafanaCtrl, link: (scope: IRootScopeService & AppEventEmitter, elem: JQuery) => { const body = $('body'); - // see https://github.com/zenorocha/clipboard.js/issues/155 $.fn.modal.Constructor.prototype.enforceFocus = () => {}; diff --git a/public/views/index-template.html b/public/views/index-template.html index c67d9b8c808..b618d63774e 100644 --- a/public/views/index-template.html +++ b/public/views/index-template.html @@ -1,277 +1,328 @@ + + + + + + - Grafana + Grafana - + - - - - + + + + - + - - - - - + + + + + + - + + + .preloader--done .preloader__text--fail { + display: block; + } -
-
-
- + [ng\:cloak], + [ng-cloak], + .ng-cloak { + display: none !important; + } + + +
+
+
+ +
+
+
Loading Grafana
+
+

+ If you're seeing this Grafana has failed to load its application files +
+
+

+

+ 1. This could be caused by your reverse proxy settings.

+ 2. If you host grafana under subpath make sure your grafana.ini root_url setting includes subpath
+
+ 3. If you have a local dev build make sure you build frontend using: yarn start, yarn start:hot, or yarn + build
+
+ 4. Sometimes restarting grafana-server can help
+

-
Loading Grafana
-
-

- If you're seeing this Grafana has failed to load its application files -
-
-

-

- 1. This could be caused by your reverse proxy settings.

- 2. If you host grafana under subpath make sure your grafana.ini root_url setting includes subpath

- 3. If you have a local dev build make sure you build frontend using: yarn start, yarn start:hot, or yarn build

- 4. Sometimes restarting grafana-server can help
-

-
-
- - - - + + + + -
-
-
+
+
+
- + +
-
- + - + // In case the js files fails to load the code below will show an info message. + window.onload = function() { + var preloader = document.getElementsByClassName("preloader"); + if (preloader.length) { + preloader[0].className = "preloader preloader--done"; + } + }; + - [[if .GoogleTagManagerId]] - - - - - - [[end]] - - + [[if .GoogleTagManagerId]] + + + + + + [[end]] + <% + for (key in htmlWebpackPlugin.files.chunks) { %><% + if (htmlWebpackPlugin.files.jsIntegrity) { %> + <% + } else { %> + <% + } %><% + } %> + + diff --git a/scripts/webpack/webpack.dev.js b/scripts/webpack/webpack.dev.js index a6949540f38..ca04b10e848 100644 --- a/scripts/webpack/webpack.dev.js +++ b/scripts/webpack/webpack.dev.js @@ -81,7 +81,7 @@ module.exports = (env = {}) => new HtmlWebpackPlugin({ filename: path.resolve(__dirname, '../../public/views/index.html'), template: path.resolve(__dirname, '../../public/views/index-template.html'), - inject: 'body', + inject: false, chunksSortMode: 'none', excludeChunks: ['dark', 'light'] }), diff --git a/scripts/webpack/webpack.prod.js b/scripts/webpack/webpack.prod.js index b19f459c4f3..b71d7b57eaf 100644 --- a/scripts/webpack/webpack.prod.js +++ b/scripts/webpack/webpack.prod.js @@ -77,7 +77,7 @@ module.exports = merge(common, { new HtmlWebpackPlugin({ filename: path.resolve(__dirname, '../../public/views/index.html'), template: path.resolve(__dirname, '../../public/views/index-template.html'), - inject: 'body', + inject: false, excludeChunks: ['manifest', 'dark', 'light'], chunksSortMode: 'none' }), diff --git a/yarn.lock b/yarn.lock index 990376f55d6..0b68b83c50a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20371,6 +20371,11 @@ tsutils@^3.9.1: dependencies: tslib "^1.8.1" +tti-polyfill@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/tti-polyfill/-/tti-polyfill-0.2.2.tgz#f7bbf71b13afa9edf60c8bb0d0c05f134e1513b9" + integrity sha512-URIoJxvsHThbQEJij29hIBUDHx9UNoBBCQVjy7L8PnzkqY8N6lsAI6h8JrT1Wt2lA0avus/DkuiJxd9qpfCpqw== + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"