Commit 7985aa1e by kay delaney Committed by GitHub

Performance/Webpack: Introduces more aggressive code-splitting and other perf improvements (#18544)

* Performance/Webpack: Introduces more aggressive code-splitting and other perf improvements
- Introduces dynamic imports for built-in plugins
- Uses dynamic imports for various packages (rst2html, brace)
- Introduces route-based dynamic imports
- Splits angular and moment into separate bundles
parent 409874b3
......@@ -3,8 +3,11 @@
[
"@babel/preset-env",
{
"targets": { "browsers": "last 3 versions" },
"useBuiltIns": "entry"
"targets": {
"browsers": "last 3 versions"
},
"useBuiltIns": "entry",
"modules": "false",
}
]
]
......
......@@ -94,3 +94,6 @@ theOutput/
# Ignore go local build dependencies
/scripts/go/bin/**
# Ignore compilation stats from `yarn stats`
compilation-stats.json
......@@ -147,6 +147,7 @@
"start:hot": "grafana-toolkit core:start --hot --watchTheme",
"start:ignoreTheme": "grafana-toolkit core:start --hot",
"start:noTsCheck": "grafana-toolkit core:start --noTsCheck",
"stats": "webpack --mode production --config scripts/webpack/webpack.prod.js --profile --json > compilation-stats.json",
"watch": "yarn start -d watch,start core:start --watchTheme ",
"build": "grunt build",
"test": "grunt test",
......
import difference from 'lodash/difference';
import { fieldReducers, ReducerID, reduceField } from './fieldReducer';
import _ from 'lodash';
import { Field, FieldType } from '../types/index';
import { MutableDataFrame } from './dataFrameHelper';
import { ArrayVector } from './vector';
......@@ -42,7 +43,7 @@ describe('Stats Calculators', () => {
expect(stats.length).toBe(2);
const found = stats.map(v => v.id);
const notFound = _.difference(names, found);
const notFound = difference(names, found);
expect(notFound.length).toBe(2);
expect(notFound[0]).toBe('not a stat');
......
// @ts-ignore
import _ from 'lodash';
import each from 'lodash/each';
import groupBy from 'lodash/groupBy';
import { RawTimeRange } from '../types/time';
......@@ -64,12 +64,12 @@ const rangeOptions = [
const absoluteFormat = 'YYYY-MM-DD HH:mm:ss';
const rangeIndex: any = {};
_.each(rangeOptions, (frame: any) => {
each(rangeOptions, (frame: any) => {
rangeIndex[frame.from + ' to ' + frame.to] = frame;
});
export function getRelativeTimesList(timepickerSettings: any, currentDisplay: any) {
const groups = _.groupBy(rangeOptions, (option: any) => {
const groups = groupBy(rangeOptions, (option: any) => {
option.active = option.display === currentDisplay;
return option.section;
});
......
......@@ -183,7 +183,7 @@ export const getWebpackConfig: WebpackConfigurationGetter = options => {
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
presets: ['@babel/preset-env', { modules: false }],
plugins: ['angularjs-annotate'],
},
},
......
// Libraries
import _ from 'lodash';
import React from 'react';
import { css } from 'emotion';
import { GraphSeriesValue, AbsoluteTimeRange } from '@grafana/data';
......
......@@ -16,22 +16,22 @@ export const Footer: FC<Props> = React.memo(
<div className="text-center">
<ul>
<li>
<a href="http://docs.grafana.org" target="_blank">
<a href="http://docs.grafana.org" target="_blank" rel="noopener">
<i className="fa fa-file-code-o" /> Docs
</a>
</li>
<li>
<a href="https://grafana.com/services/support" target="_blank">
<a href="https://grafana.com/services/support" target="_blank" rel="noopener">
<i className="fa fa-support" /> Support Plans
</a>
</li>
<li>
<a href="https://community.grafana.com/" target="_blank">
<a href="https://community.grafana.com/" target="_blank" rel="noopener">
<i className="fa fa-comments-o" /> Community
</a>
</li>
<li>
<a href="https://grafana.com" target="_blank">
<a href="https://grafana.com" target="_blank" rel="noopener">
{appName}
</a>{' '}
<span>
......@@ -41,7 +41,7 @@ export const Footer: FC<Props> = React.memo(
{newGrafanaVersionExists && (
<li>
<Tooltip placement="auto" content={newGrafanaVersion}>
<a href="https://grafana.com/get" target="_blank">
<a href="https://grafana.com/get" target="_blank" rel="noopener">
New version available!
</a>
</Tooltip>
......
declare module 'brace/*' {
let brace: any;
export default brace;
}
......@@ -30,20 +30,6 @@
import coreModule from 'app/core/core_module';
import config from 'app/core/config';
import ace from 'brace';
import './theme-grafana-dark';
import 'brace/ext/language_tools';
import 'brace/theme/textmate';
import 'brace/mode/text';
import 'brace/snippets/text';
import 'brace/mode/sql';
import 'brace/snippets/sql';
import 'brace/mode/sqlserver';
import 'brace/snippets/sqlserver';
import 'brace/mode/markdown';
import 'brace/snippets/markdown';
import 'brace/mode/json';
import 'brace/snippets/json';
const DEFAULT_THEME_DARK = 'ace/theme/grafana-dark';
const DEFAULT_THEME_LIGHT = 'ace/theme/textmate';
......@@ -55,7 +41,7 @@ const DEFAULT_SNIPPETS = true;
const editorTemplate = `<div></div>`;
function link(scope: any, elem: any, attrs: any) {
async function link(scope: any, elem: any, attrs: any) {
// Options
const langMode = attrs.mode || DEFAULT_MODE;
const maxLines = attrs.maxLines || DEFAULT_MAX_LINES;
......@@ -66,6 +52,23 @@ function link(scope: any, elem: any, attrs: any) {
// Initialize editor
const aceElem = elem.get(0);
const { default: ace } = await import(/* webpackChunkName: "brace" */ 'brace');
await import('brace/ext/language_tools');
await import('brace/theme/textmate');
await import('brace/mode/text');
await import('brace/snippets/text');
await import('brace/mode/sql');
await import('brace/snippets/sql');
await import('brace/mode/sqlserver');
await import('brace/snippets/sqlserver');
await import('brace/mode/markdown');
await import('brace/snippets/markdown');
await import('brace/mode/json');
await import('brace/snippets/json');
// @ts-ignore
await import('./theme-grafana-dark');
const codeEditor = ace.edit(aceElem);
const editorSession = codeEditor.getSession();
......
......@@ -160,6 +160,7 @@ exports[`ServerStats Should render table with stats 1`] = `
<li>
<a
href="http://docs.grafana.org"
rel="noopener"
target="_blank"
>
<i
......@@ -171,6 +172,7 @@ exports[`ServerStats Should render table with stats 1`] = `
<li>
<a
href="https://grafana.com/services/support"
rel="noopener"
target="_blank"
>
<i
......@@ -182,6 +184,7 @@ exports[`ServerStats Should render table with stats 1`] = `
<li>
<a
href="https://community.grafana.com/"
rel="noopener"
target="_blank"
>
<i
......@@ -193,6 +196,7 @@ exports[`ServerStats Should render table with stats 1`] = `
<li>
<a
href="https://grafana.com"
rel="noopener"
target="_blank"
>
Grafana
......
......@@ -131,6 +131,7 @@ class NewDataSourcePage extends PureComponent<Props> {
className="btn btn-inverse"
href="https://grafana.com/plugins?type=datasource&utm_source=new-data-source"
target="_blank"
rel="noopener"
>
Find more data source plugins on grafana.com
</a>
......@@ -198,6 +199,7 @@ const DataSourceTypeCard: FC<DataSourceTypeCardProps> = props => {
className="btn btn-inverse"
href={`${learnMoreLink}?utm_source=grafana_add_ds`}
target="_blank"
rel="noopener"
onClick={onLearnMoreClick}
>
Learn more <i className="fa fa-external-link add-datasource-item-actions__btn-icon" />
......
......@@ -14,6 +14,7 @@ export const NoDataSourceCallToAction = () => {
<a
href="http://docs.grafana.org/administration/provisioning/#datasources?utm_source=explore"
target="_blank"
rel="noopener"
className="text-link"
>
Learn more
......
// Libraries
import React, { PureComponent } from 'react';
import _ from 'lodash';
import debounce from 'lodash/debounce';
import has from 'lodash/has';
import { hot } from 'react-hot-loader';
// @ts-ignore
import { connect } from 'react-redux';
......@@ -97,7 +98,7 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
this.setState({ textEditModeEnabled: !this.state.textEditModeEnabled });
};
updateLogsHighlights = _.debounce((value: DataQuery) => {
updateLogsHighlights = debounce((value: DataQuery) => {
const { datasourceInstance } = this.props;
if (datasourceInstance.getHighlighterExpression) {
const { exploreId } = this.props;
......@@ -120,7 +121,7 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
mode,
} = this.props;
const canToggleEditorModes =
mode === ExploreMode.Metrics && _.has(datasourceInstance, 'components.QueryCtrl.prototype.toggleEditorMode');
mode === ExploreMode.Metrics && has(datasourceInstance, 'components.QueryCtrl.prototype.toggleEditorMode');
const queryErrors = queryResponse.error && queryResponse.error.refId === query.refId ? [queryResponse.error] : [];
let QueryField;
......
......@@ -278,7 +278,7 @@ class PluginPage extends PureComponent<Props, State> {
{info.links.map(link => {
return (
<li key={link.url}>
<a href={link.url} className="external-link" target="_blank">
<a href={link.url} className="external-link" target="_blank" rel="noopener">
{link.name}
</a>
</li>
......
import * as graphitePlugin from 'app/plugins/datasource/graphite/module';
import * as cloudwatchPlugin from 'app/plugins/datasource/cloudwatch/module';
import * as dashboardDSPlugin from 'app/plugins/datasource/dashboard/module';
import * as elasticsearchPlugin from 'app/plugins/datasource/elasticsearch/module';
import * as opentsdbPlugin from 'app/plugins/datasource/opentsdb/module';
import * as grafanaPlugin from 'app/plugins/datasource/grafana/module';
import * as influxdbPlugin from 'app/plugins/datasource/influxdb/module';
import * as lokiPlugin from 'app/plugins/datasource/loki/module';
import * as mixedPlugin from 'app/plugins/datasource/mixed/module';
import * as mysqlPlugin from 'app/plugins/datasource/mysql/module';
import * as postgresPlugin from 'app/plugins/datasource/postgres/module';
import * as prometheusPlugin from 'app/plugins/datasource/prometheus/module';
import * as mssqlPlugin from 'app/plugins/datasource/mssql/module';
import * as testDataDSPlugin from 'app/plugins/datasource/testdata/module';
import * as inputDatasourcePlugin from 'app/plugins/datasource/input/module';
import * as stackdriverPlugin from 'app/plugins/datasource/stackdriver/module';
import * as azureMonitorPlugin from 'app/plugins/datasource/grafana-azure-monitor-datasource/module';
const graphitePlugin = async () =>
await import(/* webpackChunkName: "graphitePlugin" */ 'app/plugins/datasource/graphite/module');
const cloudwatchPlugin = async () =>
await import(/* webpackChunkName: "cloudwatchPlugin" */ 'app/plugins/datasource/cloudwatch/module');
const dashboardDSPlugin = async () =>
await import(/* webpackChunkName "dashboardDSPlugin" */ 'app/plugins/datasource/dashboard/module');
const elasticsearchPlugin = async () =>
await import(/* webpackChunkName: "elasticsearchPlugin" */ 'app/plugins/datasource/elasticsearch/module');
const opentsdbPlugin = async () =>
await import(/* webpackChunkName: "opentsdbPlugin" */ 'app/plugins/datasource/opentsdb/module');
const grafanaPlugin = async () =>
await import(/* webpackChunkName: "grafanaPlugin" */ 'app/plugins/datasource/grafana/module');
const influxdbPlugin = async () =>
await import(/* webpackChunkName: "influxdbPlugin" */ 'app/plugins/datasource/influxdb/module');
const lokiPlugin = async () => await import(/* webpackChunkName: "lokiPlugin" */ 'app/plugins/datasource/loki/module');
const mixedPlugin = async () =>
await import(/* webpackChunkName: "mixedPlugin" */ 'app/plugins/datasource/mixed/module');
const mysqlPlugin = async () =>
await import(/* webpackChunkName: "mysqlPlugin" */ 'app/plugins/datasource/mysql/module');
const postgresPlugin = async () =>
await import(/* webpackChunkName: "postgresPlugin" */ 'app/plugins/datasource/postgres/module');
const prometheusPlugin = async () =>
await import(/* webpackChunkName: "prometheusPlugin" */ 'app/plugins/datasource/prometheus/module');
const mssqlPlugin = async () =>
await import(/* webpackChunkName: "mssqlPlugin" */ 'app/plugins/datasource/mssql/module');
const testDataDSPlugin = async () =>
await import(/* webpackChunkName: "testDataDSPlugin" */ 'app/plugins/datasource/testdata/module');
const inputDatasourcePlugin = async () =>
await import(/* webpackChunkName: "inputDatasourcePlugin" */ 'app/plugins/datasource/input/module');
const stackdriverPlugin = async () =>
await import(/* webpackChunkName: "stackdriverPlugin" */ 'app/plugins/datasource/stackdriver/module');
const azureMonitorPlugin = async () =>
await import(/* webpackChunkName: "azureMonitorPlugin" */ 'app/plugins/datasource/grafana-azure-monitor-datasource/module');
import * as textPanel from 'app/plugins/panel/text/module';
import * as text2Panel from 'app/plugins/panel/text2/module';
......@@ -35,7 +51,7 @@ import * as pieChartPanel from 'app/plugins/panel/piechart/module';
import * as barGaugePanel from 'app/plugins/panel/bargauge/module';
import * as logsPanel from 'app/plugins/panel/logs/module';
import * as exampleApp from 'app/plugins/app/example-app/module';
const exampleApp = async () => await import(/* webpackChunkName: "exampleApp" */ 'app/plugins/app/example-app/module');
const builtInPlugins: any = {
'app/plugins/datasource/graphite/module': graphitePlugin,
......
......@@ -162,11 +162,16 @@ for (const flotDep of flotDeps) {
exposeToPlugin(flotDep, { fakeDep: 1 });
}
export function importPluginModule(path: string): Promise<any> {
export async function importPluginModule(path: string): Promise<any> {
const builtIn = builtInPlugins[path];
if (builtIn) {
// for handling dynamic imports
if (typeof builtIn === 'function') {
return await builtIn();
} else {
return Promise.resolve(builtIn);
}
}
return grafanaRuntime.SystemJS.import(path);
}
......
......@@ -68,7 +68,7 @@ export class UsersActionBar extends PureComponent<Props> {
</a>
)}
{externalUserMngLinkUrl && (
<a className="btn btn-primary" href={externalUserMngLinkUrl} target="_blank">
<a className="btn btn-primary" href={externalUserMngLinkUrl} target="_blank" rel="noopener">
<i className="fa fa-external-link-square" /> {externalUserMngLinkName}
</a>
)}
......
......@@ -86,6 +86,7 @@ exports[`Render should show external user management button 1`] = `
<a
className="btn btn-primary"
href="some/url"
rel="noopener"
target="_blank"
>
<i
......
import React from 'react';
import React, { Suspense } from 'react';
import { PopoverController, Popover } from '@grafana/ui';
// @ts-ignore
import rst2html from 'rst2html';
import { FunctionDescriptor, FunctionEditorControlsProps, FunctionEditorControls } from './FunctionEditorControls';
import { FunctionDescriptor, FunctionEditorControls, FunctionEditorControlsProps } from './FunctionEditorControls';
interface FunctionEditorProps extends FunctionEditorControlsProps {
func: FunctionDescriptor;
......@@ -11,6 +9,15 @@ interface FunctionEditorProps extends FunctionEditorControlsProps {
interface FunctionEditorState {
showingDescription: boolean;
}
const FunctionDescription = React.lazy(async () => {
// @ts-ignore
const { default: rst2html } = await import(/* webpackChunkName: "rst2html" */ 'rst2html');
return {
default: (props: { description: string }) => (
<div dangerouslySetInnerHTML={{ __html: rst2html(props.description) }} />
),
};
});
class FunctionEditor extends React.PureComponent<FunctionEditorProps, FunctionEditorState> {
private triggerRef = React.createRef<HTMLSpanElement>();
......@@ -37,11 +44,9 @@ class FunctionEditor extends React.PureComponent<FunctionEditorProps, FunctionEd
return (
<div style={{ overflow: 'auto', maxHeight: '30rem', textAlign: 'left', fontWeight: 'normal' }}>
<h4 style={{ color: 'white' }}> {name} </h4>
<div
dangerouslySetInnerHTML={{
__html: rst2html(description),
}}
/>
<Suspense fallback={<span>Loading description...</span>}>
<FunctionDescription description={description} />
</Suspense>
</div>
);
}
......
import _ from 'lodash';
import $ from 'jquery';
// @ts-ignore
import rst2html from 'rst2html';
// @ts-ignore
import Drop from 'tether-drop';
import coreModule from 'app/core/core_module';
import { FuncDef } from './gfunc';
......@@ -93,7 +91,7 @@ export function graphiteAddFunc($compile: any) {
};
$(elem)
.on('mouseenter', 'ul.dropdown-menu li', () => {
.on('mouseenter', 'ul.dropdown-menu li', async () => {
cleanUpDrop();
let funcDef;
......@@ -110,6 +108,8 @@ export function graphiteAddFunc($compile: any) {
}
const contentElement = document.createElement('div');
// @ts-ignore
const { default: rst2html } = await import(/* webpackChunkName: "rst2html" */ 'rst2html');
contentElement.innerHTML = '<h4>' + funcDef.name + '</h4>' + rst2html(shortDesc);
drop = new Drop({
......
......@@ -60,7 +60,7 @@ export default class PromLink extends Component<Props, State> {
render() {
const { href } = this.state;
return (
<a href={href} target="_blank">
<a href={href} target="_blank" rel="noopener">
<i className="fa fa-share-square-o" /> Prometheus
</a>
);
......
import React, { FC } from 'react';
import _ from 'lodash';
import { MetricSelect } from 'app/core/components/Select/MetricSelect';
import { TemplateSrv } from 'app/features/templating/template_srv';
......
import _ from 'lodash';
import { QueryCtrl } from 'app/plugins/sdk';
import { StackdriverQuery } from './types';
import { TemplateSrv } from 'app/features/templating/template_srv';
......
......@@ -18,7 +18,12 @@ export class TestInfoTab extends PureComponent<Props> {
See github for more information about setting up a reproducable test environment.
<br />
<br />
<a className="btn btn-inverse" href="https://github.com/grafana/grafana/tree/master/devenv" target="_blank">
<a
className="btn btn-inverse"
href="https://github.com/grafana/grafana/tree/master/devenv"
target="_blank"
rel="noopener"
>
Github
</a>
<br />
......
......@@ -59,6 +59,7 @@
}
/* latin */
@font-face {
font-display: swap;
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
......@@ -121,6 +122,7 @@
}
/* latin */
@font-face {
font-display: swap;
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
......@@ -184,6 +186,7 @@
}
/* latin */
@font-face {
font-display: swap;
font-family: 'Roboto';
font-style: italic;
font-weight: 400;
......
import _ from 'lodash';
import each from 'lodash/each';
import template from 'lodash/template';
import config from 'app/core/config';
import { dateMath } from '@grafana/data';
import { angularMocks, sinon } from '../lib/common';
......@@ -37,7 +39,7 @@ export function ControllerTestContext(this: any) {
$provide.value('templateSrv', self.templateSrv);
$provide.value('$element', self.$element);
$provide.value('$sanitize', self.$sanitize);
_.each(mocks, (value: any, key: any) => {
each(mocks, (value: any, key: any) => {
$provide.value(key, value);
});
});
......@@ -118,7 +120,7 @@ export function ServiceTestContext(this: any) {
this.providePhase = (mocks: any) => {
return angularMocks.module(($provide: any) => {
_.each(mocks, (key: string) => {
each(mocks, (key: string) => {
$provide.value(key, self[key]);
});
});
......@@ -184,7 +186,7 @@ export function TemplateSrvStub(this: any) {
this.templateSettings = { interpolate: /\[\[([\s\S]+?)\]\]/g };
this.data = {};
this.replace = (text: string) => {
return _.template(text, this.templateSettings)(this.data);
return template(text, this.templateSettings)(this.data);
};
this.init = () => {};
this.getAdhocFilters = (): any => {
......
......@@ -11,6 +11,7 @@
<base href="[[.AppSubUrl]]/" />
<link rel="preload" href="public/fonts/roboto/RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2" as="font" crossorigin />
<link rel="icon" type="image/png" href="public/img/fav32.png">
<link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28">
<link rel="apple-touch-icon" sizes="180x180" href="public/img/apple-touch-icon.png">
......
'use strict';
const pkg = require('../../package.json');
const _ = require('lodash');
const pull = require('lodash/pull');
let dependencies = Object.keys(pkg.dependencies);
// remove jquery so we can add it first
// remove rxjs so we can only depend on parts of it in code
_.pull(dependencies, 'jquery', 'rxjs')
pull(dependencies, 'jquery', 'rxjs')
// add jquery first
dependencies.unshift('jquery');
......
const path = require('path');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
target: 'web',
......@@ -13,63 +14,88 @@ module.exports = {
},
resolve: {
extensions: ['.ts', '.tsx', '.es6', '.js', '.json', '.svg'],
alias: {
},
modules: [
path.resolve('public'),
path.resolve('node_modules')
],
alias: {},
modules: [path.resolve('public'), path.resolve('node_modules')],
},
stats: {
children: false,
warningsFilter: /export .* was not found in/
warningsFilter: /export .* was not found in/,
source: false
},
node: {
fs: 'empty',
},
module: {
rules: [
{
rules: [{
test: require.resolve('jquery'),
use: [
{
use: [{
loader: 'expose-loader',
query: 'jQuery'
query: 'jQuery',
},
{
loader: 'expose-loader',
query: '$'
}
]
query: '$',
},
],
},
{
test: /\.html$/,
exclude: /(index|error)\-template\.html/,
use: [
{ loader: 'ngtemplate-loader?relativeTo=' + (path.resolve(__dirname, '../../public')) + '&prefix=public' },
use: [{
loader: 'ngtemplate-loader?relativeTo=' + path.resolve(__dirname, '../../public') + '&prefix=public',
},
{
loader: 'html-loader',
options: {
attrs: [],
minimize: true,
removeComments: false,
collapseWhitespace: false
}
}
]
}
]
collapseWhitespace: false,
},
},
],
},
],
},
// https://webpack.js.org/plugins/split-chunks-plugin/#split-chunks-example-3
optimization: {
moduleIds: 'hashed',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
minChunks: 1,
cacheGroups: {
commons: {
moment: {
test: /[\\/]node_modules[\\/]moment[\\/].*[jt]sx?$/,
chunks: 'initial',
priority: 20,
enforce: true
},
angular: {
test: /[\\/]node_modules[\\/]angular[\\/].*[jt]sx?$/,
chunks: 'initial',
priority: 50,
enforce: true
},
vendors: {
test: /[\\/]node_modules[\\/].*[jt]sx?$/,
name: 'vendor',
chunks: 'all'
}
}
}
}
chunks: 'initial',
priority: -10,
reuseExistingChunk: true,
enforce: true
},
default: {
priority: -20,
chunks: 'all',
test: /.*[jt]sx?$/,
reuseExistingChunk: true
},
},
},
},
plugins: [
new ForkTsCheckerWebpackPlugin({
checkSyntacticErrors: true,
})
],
};
......@@ -27,8 +27,7 @@ module.exports = (env = {}) =>
},
module: {
rules: [
{
rules: [{
test: /\.tsx?$/,
enforce: 'pre',
exclude: /node_modules/,
......@@ -37,8 +36,8 @@ module.exports = (env = {}) =>
options: {
emitErrors: true,
typeCheck: false,
},
},
}
}
},
{
test: /\.tsx?$/,
......@@ -46,48 +45,55 @@ module.exports = (env = {}) =>
use: {
loader: 'ts-loader',
options: {
transpileOnly: true,
transpileOnly: true
},
},
},
require('./sass.rule.js')({ sourceMap: false, preserveUrl: false }),
require('./sass.rule.js')({
sourceMap: false,
preserveUrl: false
}),
{
test: /\.(png|jpg|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
loader: 'file-loader',
loader: 'file-loader'
},
],
]
},
plugins: [
new CleanWebpackPlugin(),
env.noTsCheck
? new webpack.DefinePlugin({}) // bogus plugin to satisfy webpack API
: new ForkTsCheckerWebpackPlugin({
env.noTsCheck ?
new webpack.DefinePlugin({}) // bogus plugin to satisfy webpack API
:
new ForkTsCheckerWebpackPlugin({
checkSyntacticErrors: true,
}),
new MiniCssExtractPlugin({
filename: 'grafana.[name].[hash].css',
filename: "grafana.[name].[hash].css"
}),
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, '../../public/views/error.html'),
template: path.resolve(__dirname, '../../public/views/error-template.html'),
inject: false,
chunksSortMode: 'none',
excludeChunks: ['dark', 'light']
}),
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, '../../public/views/index.html'),
template: path.resolve(__dirname, '../../public/views/index-template.html'),
inject: 'body',
chunks: ['manifest', 'vendor', 'app'],
chunksSortMode: 'none',
excludeChunks: ['dark', 'light']
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development'),
},
'NODE_ENV': JSON.stringify('development')
}
}),
// new BundleAnalyzerPlugin({
// analyzerPort: 8889
// })
],
]
});
......@@ -42,23 +42,24 @@ module.exports = merge(common, {
optimization: {
removeAvailableModules: false,
runtimeChunk: false,
removeEmptyChunks: false,
splitChunks: false,
splitChunks: false
},
module: {
rules: [
{
rules: [{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
use: [{
loader: 'babel-loader',
options: {
cacheDirectory: true,
babelrc: false,
plugins: [
[require('@rtsao/plugin-proposal-class-properties'), { loose: true }],
[require('@rtsao/plugin-proposal-class-properties'), {
loose: true
}],
'angularjs-annotate',
'@babel/plugin-syntax-dynamic-import', // needed for `() => import()` in routes.ts
'react-hot-loader/babel',
......@@ -67,16 +68,18 @@ module.exports = merge(common, {
[
'@babel/preset-env',
{
targets: { browsers: 'last 3 versions' },
targets: {
browsers: 'last 3 versions'
},
useBuiltIns: 'entry',
modules: false
},
],
'@babel/preset-typescript',
'@babel/preset-react',
],
},
},
],
}, ],
},
{
test: /\.scss$/,
......@@ -86,7 +89,9 @@ module.exports = merge(common, {
{
loader: 'postcss-loader',
options: {
config: { path: __dirname + '/postcss.config.js' },
config: {
path: __dirname + '/postcss.config.js'
},
},
},
{
......@@ -108,6 +113,7 @@ module.exports = merge(common, {
template: path.resolve(__dirname, '../../public/views/index-template.html'),
inject: 'body',
alwaysWriteToDisk: true,
chunksSortMode: 'none'
}),
new HtmlWebpackHarddiskPlugin(),
new webpack.NamedModulesPlugin(),
......
......@@ -20,8 +20,7 @@ module.exports = merge(common, {
},
module: {
rules: [
{
rules: [{
test: /\.tsx?$/,
enforce: 'pre',
exclude: /node_modules/,
......@@ -44,11 +43,13 @@ module.exports = merge(common, {
},
},
require('./sass.rule.js')({
sourceMap: false, preserveUrl: false
sourceMap: false,
preserveUrl: false
})
]
},
optimization: {
nodeEnv: 'production',
minimizer: [
new TerserPlugin({
cache: false,
......@@ -70,12 +71,15 @@ module.exports = merge(common, {
filename: path.resolve(__dirname, '../../public/views/error.html'),
template: path.resolve(__dirname, '../../public/views/error-template.html'),
inject: false,
excludeChunks: ['dark', 'light'],
chunksSortMode: 'none'
}),
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, '../../public/views/index.html'),
template: path.resolve(__dirname, '../../public/views/index-template.html'),
inject: 'body',
chunks: ['vendor', 'app'],
excludeChunks: ['manifest', 'dark', 'light'],
chunksSortMode: 'none'
}),
function () {
this.hooks.done.tap('Done', function (stats) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment