Build: Upgrade Webpack 5 (#36444)

* build(webpack): bump to v5 and successful yarn start compilation

* build(webpack): update postcss dependencies

* build(webpack): silence warnings about hash renamed to fullhash

* build(webpack): enable persistent cache to store generated webpack modules / chunks

* build(webpack): prefer eslintWebpackPlugin over tschecker so eslint doesn't block typechecking

* chore(yarn): run yarn-deduplicate to clean up dependencies

* chore(yarn): refresh lock file after clean install

* build(webpack): prefer output.clean over CleanWebpackPlugin

* build(webpack): prefer esbuild over babel-loader for dev config

* build(babel): turn off cache compression to improve build performance

* build(webpack): get production builds working

* build(webpack): remove phantomJS (removed from grafana in v7) specific loader

* build(webpack): put back babel for dev builds no performance gain in using esbuild in webpack

* build(webpack): prefer terser and optimise css plugins for prod. slower but smaller bundles

* build(webpack): clean up redundant code. inform postcss about node_modules

* build(webpack): remove deprecation warnings flag

* build(webpack): bump packages, dev performance optimisations, attempt to get hot working

* chore(storybook): use webpack 5 for dev and production builds

* build(storybook): speed up dev build

* chore(yarn): refresh lock file

* chore(webpack): bump webpack and related deps to latest

* refactor(webpack): put back inline-source-map, move start scripts out of grafana toolkit

* feat(webpack): prefer react-refresh over react-hot-loader

* build(webpack): update webpack.hot to use react-refresh

* chore: remove react-hot-loader from codebase

* refactor(queryeditorrow): fix circular dependency causing react-fast-refresh errors

* revert(webpack): remove stats.errorDetails from common config

* build(webpack): bump to v5 and successful yarn start compilation

* build(webpack): update postcss dependencies

* build(webpack): silence warnings about hash renamed to fullhash

* build(webpack): enable persistent cache to store generated webpack modules / chunks

* build(webpack): prefer eslintWebpackPlugin over tschecker so eslint doesn't block typechecking

* chore(yarn): run yarn-deduplicate to clean up dependencies

* chore(yarn): refresh lock file after clean install

* build(webpack): prefer output.clean over CleanWebpackPlugin

* build(webpack): prefer esbuild over babel-loader for dev config

* build(babel): turn off cache compression to improve build performance

* build(webpack): get production builds working

* build(webpack): remove phantomJS (removed from grafana in v7) specific loader

* build(webpack): put back babel for dev builds no performance gain in using esbuild in webpack

* build(webpack): prefer terser and optimise css plugins for prod. slower but smaller bundles

* build(webpack): clean up redundant code. inform postcss about node_modules

* build(webpack): remove deprecation warnings flag

* build(webpack): bump packages, dev performance optimisations, attempt to get hot working

* chore(storybook): use webpack 5 for dev and production builds

* build(storybook): speed up dev build

* chore(yarn): refresh lock file

* chore(webpack): bump webpack and related deps to latest

* refactor(webpack): put back inline-source-map, move start scripts out of grafana toolkit

* feat(webpack): prefer react-refresh over react-hot-loader

* build(webpack): update webpack.hot to use react-refresh

* chore: remove react-hot-loader from codebase

* refactor(queryeditorrow): fix circular dependency causing react-fast-refresh errors

* revert(webpack): remove stats.errorDetails from common config

* revert(webpack): remove include from babel-loader so symlinks (enterprise) work as before

* refactor(webpack): fix deprecation warnings in prod builds

* fix(storybook): fix failing builds due to replacing css-optimise webpack plugin

* fix(storybook): use raw-loader for svg icons

* build(webpack): fix dev script colors error

* chore(webpack): bump css-loader and react-refresh-webpack-plugin to latest versions

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Jack Westbrook 2021-08-31 12:55:05 +02:00 committed by GitHub
parent b40d48258d
commit 8d3b31ff23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1947 additions and 1147 deletions

View File

@ -5,6 +5,7 @@ Generally we follow the Airbnb [React Style Guide](https://github.com/airbnb/jav
## Table of Contents
- [Frontend Style Guide](#frontend-style-guide)
- [Table of Contents](#table-of-contents)
- [Basic rules](#basic-rules)
- [Naming conventions](#naming-conventions)
@ -28,12 +29,12 @@ Generally we follow the Airbnb [React Style Guide](https://github.com/airbnb/jav
- [Linting](#linting)
- [React](#react)
- [Props](#props)
- [Name callback props and handlers with an "on" prefix.](#name-callback-props-and-handlers-with-an-on-prefix)
- [React Component definitions](#react-component-definitions)
- [React Component constructor](#react-component-constructor)
- [React Component defaultProps](#react-component-defaultprops)
- [Name callback props and handlers with an "on" prefix.](#name-callback-props-and-handlers-with-an-on-prefix)
- [React Component definitions](#react-component-definitions)
- [React Component constructor](#react-component-constructor)
- [React Component defaultProps](#react-component-defaultprops)
- [State management](#state-management)
- [Proposal for removing or replacing Angular dependencies](https://github.com/grafana/grafana/pull/23048)
## Basic rules
@ -194,12 +195,12 @@ _SASS styles are deprecated. Please migrate to Emotion whenever you need to modi
### Typing
In general, you should let Typescript infer the types so that there's no need to explicitly define type for each variable.
In general, you should let Typescript infer the types so that there's no need to explicitly define type for each variable.
There are some exceptions to this:
```typescript
// Typescript needs to know type of arrays or objects otherwise it would infer it as array of any
// Typescript needs to know type of arrays or objects otherwise it would infer it as array of any
// bad
const stringArray = [];
@ -208,7 +209,7 @@ const stringArray = [];
const stringArray: string[] = [];
```
Specify function return types explicitly in new code. This improves readability by being able to tell what a function returns just by looking at the signature. It also prevents errors when a function's return type is broader than expected by the author.
Specify function return types explicitly in new code. This improves readability by being able to tell what a function returns just by looking at the signature. It also prevents errors when a function's return type is broader than expected by the author.
> **Note:** We don't have linting for this enabled because of lots of old code that needs to be fixed first.
@ -216,18 +217,18 @@ Specify function return types explicitly in new code. This improves readability
// bad
function transform(value?: string) {
if (!value) {
return undefined
return undefined;
}
return applyTransform(value)
};
return applyTransform(value);
}
// good
function transform(value?: string): TransformedValue | undefined {
if (!value) {
return undefined
return undefined;
}
return applyTransform(value)
};
return applyTransform(value);
}
```
### File and directory naming conventions

View File

@ -8,7 +8,7 @@
"scripts": {
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
"build": "node ./node_modules/webpack/bin/webpack.js --config scripts/webpack/webpack.prod.js",
"dev": "webpack --progress --colors --config scripts/webpack/webpack.dev.js",
"dev": "webpack --progress --color --config scripts/webpack/webpack.dev.js",
"e2e": "./e2e/start-and-run-suite",
"e2e:debug": "./e2e/start-and-run-suite debug",
"e2e:dev": "./e2e/start-and-run-suite dev",
@ -33,10 +33,12 @@
"precommit": "yarn run lint-staged",
"prettier:check": "prettier --list-different \"**/*.{ts,tsx,scss}\"",
"prettier:write": "prettier --list-different \"**/*.{ts,tsx,scss,js}\" --write",
"start": "grafana-toolkit core:start --watchTheme",
"start:hot": "grafana-toolkit core:start --hot --watchTheme",
"start:ignoreTheme": "grafana-toolkit core:start --hot",
"start:noTsCheck": "grafana-toolkit core:start --noTsCheck",
"prestart": "yarn themes:generate",
"prestart:hot": "yarn themes:generate",
"prestart:noTsCheck": "yarn themes:generate",
"start": "webpack --progress --color --watch --env noTsCheck=0 --config scripts/webpack/webpack.dev.js",
"start:hot": "webpack serve --progress --color --config scripts/webpack/webpack.hot.js",
"start:noTsCheck": "webpack --progress --color --watch --env noTsCheck=1 --config scripts/webpack/webpack.dev.js",
"stats": "webpack --mode production --config scripts/webpack/webpack.prod.js --profile --json > compilation-stats.json",
"storybook": "yarn workspace @grafana/ui storybook --ci",
"storybook:build": "yarn workspace @grafana/ui storybook:build",
@ -82,6 +84,7 @@
"@grafana/api-extractor": "7.10.1",
"@grafana/eslint-config": "2.5.0",
"@kusto/monaco-kusto": "3.2.7",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.0-rc.6",
"@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1",
"@testing-library/jest-dom": "5.11.5",
"@testing-library/react": "11.1.2",
@ -139,14 +142,14 @@
"@typescript-eslint/parser": "4.28.0",
"@wojtekmaj/enzyme-adapter-react-17": "0.6.2",
"angular-mocks": "1.6.6",
"autoprefixer": "9.7.4",
"autoprefixer": "10.2.6",
"axios": "0.21.1",
"babel-jest": "26.6.3",
"babel-loader": "8.2.2",
"babel-plugin-angularjs-annotate": "0.10.0",
"clean-webpack-plugin": "3.0.0",
"copy-webpack-plugin": "6.4.1",
"css-loader": "3.4.2",
"copy-webpack-plugin": "9.0.1",
"css-loader": "6.2.0",
"css-minimizer-webpack-plugin": "^3.0.2",
"enzyme": "3.11.0",
"enzyme-to-json": "3.4.4",
"es-abstract": "1.18.0-next.1",
@ -160,16 +163,17 @@
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"eslint-webpack-plugin": "3.0.1",
"expect.js": "0.3.1",
"expose-loader": "0.7.5",
"file-loader": "5.0.2",
"fork-ts-checker-webpack-plugin": "6.3.1",
"expose-loader": "3.0.0",
"file-loader": "6.2.0",
"fork-ts-checker-webpack-plugin": "6.3.2",
"fs-extra": "9.1.0",
"gaze": "1.1.3",
"glob": "7.1.6",
"html-loader": "0.5.5",
"html-webpack-harddisk-plugin": "1.0.1",
"html-webpack-plugin": "3.2.0",
"html-loader": "2.1.2",
"html-webpack-harddisk-plugin": "2.0.0",
"html-webpack-plugin": "5.3.2",
"husky": "4.2.1",
"iconscout-unicons-tarball": "https://github.com/grafana/icons/tarball/9728be621a4e7d891611149c9cd179e793f790a7",
"jest": "26.6.3",
@ -178,44 +182,44 @@
"jest-matcher-utils": "26.0.0",
"lerna": "^3.22.1",
"lint-staged": "10.0.7",
"mini-css-extract-plugin": "0.9.0",
"mini-css-extract-plugin": "2.2.0",
"mocha": "7.0.1",
"module-alias": "2.2.2",
"mutationobserver-shim": "0.3.3",
"ngtemplate-loader": "2.0.1",
"ngtemplate-loader": "2.1.0",
"nodemon": "2.0.2",
"optimize-css-assets-webpack-plugin": "5.0.5",
"pa11y-ci": "2.4.2",
"postcss": "8.3.6",
"postcss-browser-reporter": "0.6.0",
"postcss-loader": "3.0.0",
"postcss-reporter": "6.0.1",
"postcss-loader": "6.1.1",
"postcss-reporter": "7.0.2",
"prettier": "2.2.1",
"raw-loader": "4.0.2",
"react-hot-loader": "4.8.0",
"react-refresh": "^0.10.0",
"react-select-event": "^5.1.0",
"react-test-renderer": "17.0.1",
"redux-mock-store": "1.5.4",
"regexp-replace-loader": "1.0.1",
"rimraf": "3.0.1",
"rxjs-spy": "8.0.0",
"sass": "1.27.0",
"sass": "1.32.13",
"sass-lint": "1.12.1",
"sass-loader": "8.0.2",
"sass-loader": "12.1.0",
"sinon": "8.1.1",
"style-loader": "1.1.3",
"terser-webpack-plugin": "2.3.7",
"style-loader": "3.2.1",
"terser-webpack-plugin": "5.1.4",
"testing-library-selector": "^0.1.3",
"ts-jest": "26.4.4",
"ts-node": "9.0.0",
"tslib": "2.2.0",
"typescript": "4.3.4",
"wait-on": "6.0.0",
"webpack": "4.41.5",
"webpack-bundle-analyzer": "3.6.0",
"webpack": "5.51.1",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cleanup-plugin": "0.5.1",
"webpack-cli": "3.3.10",
"webpack-dev-server": "3.11.1",
"webpack-merge": "4.2.2",
"webpack-cli": "4.8.0",
"webpack-dev-server": "4.0.0",
"webpack-merge": "5.8.0",
"worker-loader": "^3.0.8",
"zone.js": "0.7.8"
},

View File

@ -17,11 +17,11 @@ const startTaskRunner: TaskRunner<StartTaskOptions> = async ({ watchThemes, noTs
},
hot
? {
command: 'webpack-dev-server --progress --colors --config scripts/webpack/webpack.hot.js',
command: 'webpack serve --progress --color --config scripts/webpack/webpack.hot.js',
name: 'Dev server',
}
: {
command: `webpack --progress --colors --watch --env.noTsCheck=${noTsCheckArg} --config scripts/webpack/webpack.dev.js`,
command: `webpack --progress --color --watch --env noTsCheck=${noTsCheckArg} --config scripts/webpack/webpack.dev.js`,
name: 'Webpack',
},
];

View File

@ -1,7 +1,8 @@
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const FilterWarningsPlugin = require('webpack-filter-warnings-plugin');
const getBabelConfig = require('../../../scripts/webpack/babel.config');
const stories = ['../src/**/*.story.{js,jsx,ts,tsx,mdx}'];
@ -23,8 +24,12 @@ module.exports = {
'@storybook/addon-storysource',
'storybook-dark-mode',
],
reactOptions: {
fastRefresh: true,
// currently broken in webpack 5 builder support
// reactOptions: {
// fastRefresh: true,
// },
core: {
builder: 'webpack5',
},
typescript: {
check: true,
@ -39,6 +44,22 @@ module.exports = {
},
webpackFinal: async (config: any, { configType }: any) => {
const isProductionBuild = configType === 'PRODUCTION';
// remove svg from default storybook webpack 5 config so we can use `raw-loader`
config.module.rules = config.module.rules.map((rule: any) => {
if (
String(rule.test) ===
String(/\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/)
) {
return {
...rule,
test: /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
};
}
return rule;
});
config.module.rules = [
...(config.module.rules || []),
{
@ -52,6 +73,8 @@ module.exports = {
},
},
],
exclude: /node_modules/,
include: [path.resolve(__dirname, '../../../public/'), path.resolve(__dirname, '../../../packages/')],
},
{
test: /\.scss$/,
@ -63,6 +86,7 @@ module.exports = {
{
loader: 'css-loader',
options: {
url: false,
importLoaders: 2,
},
},
@ -70,7 +94,9 @@ module.exports = {
loader: 'postcss-loader',
options: {
sourceMap: false,
config: { path: __dirname + '../../../../scripts/webpack/postcss.config.js' },
postcssOptions: {
config: path.resolve(__dirname + '../../../../scripts/webpack/postcss.config.js'),
},
},
},
{
@ -81,52 +107,50 @@ module.exports = {
},
],
},
// for pre-caching SVGs as part of the JS bundles
{
test: /\.svg$/,
use: 'raw-loader',
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
query: 'jQuery',
},
{
loader: 'expose-loader',
query: '$',
},
],
loader: 'expose-loader',
options: {
exposes: ['$', 'jQuery'],
},
},
];
config.optimization = {
nodeEnv: 'production',
moduleIds: 'hashed',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
minChunks: 1,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/].*[jt]sx?$/,
chunks: 'initial',
priority: -10,
reuseExistingChunk: true,
enforce: true,
},
default: {
priority: -20,
chunks: 'all',
test: /.*[jt]sx?$/,
reuseExistingChunk: true,
if (isProductionBuild) {
config.optimization = {
nodeEnv: 'production',
moduleIds: 'deterministic',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
minChunks: 1,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/].*[jt]sx?$/,
chunks: 'initial',
priority: -10,
reuseExistingChunk: true,
enforce: true,
},
default: {
priority: -20,
chunks: 'all',
test: /.*[jt]sx?$/,
reuseExistingChunk: true,
},
},
},
},
minimize: isProductionBuild,
minimizer: isProductionBuild
? [
new TerserPlugin({ cache: false, parallel: false, sourceMap: false, exclude: /monaco/ }),
new OptimizeCSSAssetsPlugin({}),
]
: [],
};
minimize: isProductionBuild,
minimizer: isProductionBuild
? [new TerserPlugin({ parallel: false, exclude: /monaco/ }), new CssMinimizerPlugin()]
: [],
};
}
config.resolve.alias['@grafana/ui'] = path.resolve(__dirname, '..');

View File

@ -25,6 +25,10 @@
"storybook:build": "build-storybook -o ./dist/storybook -c .storybook -s ../../public/img:public/img,../../public/lib:public/lib",
"typecheck": "tsc --noEmit"
},
"browserslist": [
"defaults",
"not IE 11"
],
"dependencies": {
"@emotion/css": "11.1.3",
"@emotion/react": "11.1.5",
@ -81,6 +85,8 @@
"@storybook/addon-storysource": "6.3.7",
"@storybook/react": "6.3.7",
"@storybook/theming": "6.3.7",
"@storybook/builder-webpack5": "6.3.7",
"@storybook/manager-webpack5": "6.3.7",
"@testing-library/jest-dom": "5.11.9",
"@types/classnames": "2.2.7",
"@types/common-tags": "^1.8.0",

View File

@ -1,7 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import { getBackendSrv } from '@grafana/runtime';
import { NavModel } from '@grafana/data';
@ -74,4 +72,4 @@ const mapStateToProps = (state: StoreState) => ({
navModel: getNavModel(state.navIndex, 'server-settings'),
});
export default hot(module)(connect(mapStateToProps)(AdminSettings));
export default connect(mapStateToProps)(AdminSettings);

View File

@ -1,6 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import { hot } from 'react-hot-loader';
import { css } from '@emotion/css';
import { LinkButton, useStyles2 } from '@grafana/ui';
import { GrafanaTheme2, NavModel } from '@grafana/data';
@ -231,4 +230,4 @@ const mapStateToProps = (state: StoreState) => ({
navModel: getNavModel(state.navIndex, 'upgrading'),
});
export default hot(module)(connect(mapStateToProps)(UpgradePage));
export default connect(mapStateToProps)(UpgradePage);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { NavModel } from '@grafana/data';
import { getNavModel } from 'app/core/selectors/navModel';
@ -177,4 +176,4 @@ const mapDispatchToProps = {
const connector = connect(mapStateToProps, mapDispatchToProps);
type Props = OwnProps & ConnectedProps<typeof connector>;
export default hot(module)(connector(UserAdminPage));
export default connector(UserAdminPage);

View File

@ -1,5 +1,4 @@
import React, { useCallback } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { Form, Button, Input, Field } from '@grafana/ui';
import { NavModel } from '@grafana/data';
@ -83,4 +82,4 @@ const mapStateToProps = (state: StoreState) => ({
navModel: getNavModel(state.navIndex, 'global-users'),
});
export default hot(module)(connect(mapStateToProps)(UserCreatePage));
export default connect(mapStateToProps)(UserCreatePage);

View File

@ -1,6 +1,5 @@
import React, { useEffect } from 'react';
import { css, cx } from '@emotion/css';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { Pagination, Tooltip, stylesFactory, LinkButton, Icon } from '@grafana/ui';
import { AccessControlAction, StoreState, UserDTO } from '../../types';
@ -142,4 +141,4 @@ const getStyles = stylesFactory(() => {
};
});
export default hot(module)(connector(UserListAdminPageUnConnected));
export default connector(UserListAdminPageUnConnected);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { NavModel } from '@grafana/data';
import { Alert, Button, LegacyForms } from '@grafana/ui';
@ -160,4 +159,4 @@ const mapDispatchToProps = {
const connector = connect(mapStateToProps, mapDispatchToProps);
type Props = OwnProps & ConnectedProps<typeof connector>;
export default hot(module)(connector(LdapPage));
export default connector(LdapPage);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import Page from 'app/core/components/Page/Page';
import AlertRuleItem from './AlertRuleItem';
@ -141,4 +140,4 @@ export class AlertRuleListUnconnected extends PureComponent<Props> {
}
}
export default hot(module)(connector(AlertRuleListUnconnected));
export default connector(AlertRuleListUnconnected);

View File

@ -1,6 +1,5 @@
import React, { PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { hot } from 'react-hot-loader';
// Utils
import { ApiKey, NewApiKey, StoreState } from 'app/types';
import { getNavModel } from 'app/core/selectors/navModel';
@ -168,4 +167,4 @@ export class ApiKeysPageUnconnected extends PureComponent<Props, State> {
}
const ApiKeysPage = connector(ApiKeysPageUnconnected);
export default hot(module)(ApiKeysPage);
export default ApiKeysPage;

View File

@ -1,6 +1,5 @@
import React, { PureComponent } from 'react';
import { css } from 'emotion';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { locationService } from '@grafana/runtime';
import { selectors } from '@grafana/e2e-selectors';
@ -417,4 +416,4 @@ export const getStyles = stylesFactory((theme: GrafanaTheme2, kioskMode) => {
export const DashboardPage = withTheme2(UnthemedDashboardPage);
DashboardPage.displayName = 'DashboardPage';
export default hot(module)(connector(DashboardPage));
export default connector(DashboardPage);

View File

@ -1,5 +1,4 @@
import React, { Component } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import AutoSizer from 'react-virtualized-auto-sizer';
import { DashboardPanel } from '../dashgrid/DashboardPanel';
@ -111,4 +110,4 @@ export class SoloPanelPage extends Component<Props, State> {
}
}
export default hot(module)(connector(SoloPanelPage));
export default connector(SoloPanelPage);

View File

@ -1,7 +1,6 @@
// Libraries
import React, { PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { hot } from 'react-hot-loader';
// Components
import Page from 'app/core/components/Page/Page';
import PageActionBar from 'app/core/components/PageActionBar/PageActionBar';
@ -97,4 +96,4 @@ export class DataSourcesListPage extends PureComponent<Props> {
}
}
export default hot(module)(connector(DataSourcesListPage));
export default connector(DataSourcesListPage);

View File

@ -1,6 +1,5 @@
import React, { FC, PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { hot } from 'react-hot-loader';
import { DataSourcePluginMeta, NavModel } from '@grafana/data';
import { Button, LinkButton, List, PluginSignatureBadge } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
@ -179,4 +178,4 @@ export function getNavModel(): NavModel {
};
}
export default hot(module)(connector(NewDataSourcePage));
export default connector(NewDataSourcePage);

View File

@ -1,5 +1,4 @@
import React from 'react';
import { hot } from 'react-hot-loader';
import { css, cx } from '@emotion/css';
import { compose } from 'redux';
import { connect, ConnectedProps } from 'react-redux';
@ -408,4 +407,4 @@ const mapDispatchToProps = {
const connector = connect(mapStateToProps, mapDispatchToProps);
export default compose(hot(module), connector, withTheme2)(Explore) as React.ComponentType<{ exploreId: ExploreId }>;
export default compose(connector, withTheme2)(Explore) as React.ComponentType<{ exploreId: ExploreId }>;

View File

@ -3,7 +3,6 @@ import { TabbedContainer, TabConfig } from '@grafana/ui';
import { TimeZone } from '@grafana/data';
import { runQueries } from './state/query';
import { StoreState, ExploreItemState, ExploreId } from 'app/types';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { ExploreDrawer } from 'app/features/explore/ExploreDrawer';
import { InspectJSONTab } from 'app/features/inspector/InspectJSONTab';
@ -93,4 +92,4 @@ const mapDispatchToProps = {
const connector = connect(mapStateToProps, mapDispatchToProps);
export default hot(module)(connector(ExploreQueryInspector));
export default connector(ExploreQueryInspector);

View File

@ -1,6 +1,5 @@
import React, { PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { hot } from 'react-hot-loader';
import classNames from 'classnames';
import { css } from '@emotion/css';
@ -251,4 +250,4 @@ const mapDispatchToProps = {
const connector = connect(mapStateToProps, mapDispatchToProps);
export const ExploreToolbar = hot(module)(connector(UnConnectedExploreToolbar));
export const ExploreToolbar = connector(UnConnectedExploreToolbar);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { css } from 'emotion';
import { Collapse } from '@grafana/ui';
@ -192,4 +191,4 @@ const mapDispatchToProps = {
const connector = connect(mapStateToProps, mapDispatchToProps);
type PropsFromRedux = ConnectedProps<typeof connector>;
export default hot(module)(connector(LogsContainer));
export default connector(LogsContainer);

View File

@ -1,7 +1,6 @@
// Libraries
import React, { PureComponent } from 'react';
import { debounce, has } from 'lodash';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import AngularQueryEditor from './QueryEditor';
import { QueryRowActions } from './QueryRowActions';
@ -201,4 +200,4 @@ const mapDispatchToProps = {
const connector = connect(mapStateToProps, mapDispatchToProps);
export default hot(module)(connector(QueryRow));
export default connector(QueryRow);

View File

@ -1,6 +1,5 @@
import React, { useState, useEffect } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { hot } from 'react-hot-loader';
import { css, cx } from '@emotion/css';
import { stylesFactory, useTheme, TextArea, Button, IconButton } from '@grafana/ui';
import { getDataSourceSrv } from '@grafana/runtime';
@ -318,4 +317,4 @@ export function RichHistoryCard(props: Props) {
);
}
export default hot(module)(connector(RichHistoryCard));
export default connector(RichHistoryCard);

View File

@ -1,7 +1,6 @@
// Libraries
import React, { useState } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { hot } from 'react-hot-loader';
// Services & Utils
import store from 'app/core/store';
@ -72,4 +71,4 @@ export function RichHistoryContainer(props: Props) {
);
}
export default hot(module)(connector(RichHistoryContainer));
export default connector(RichHistoryContainer);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { ValueLinkConfig } from '@grafana/data';
import { Collapse, Table } from '@grafana/ui';
@ -84,4 +83,4 @@ export class TableContainer extends PureComponent<Props> {
}
}
export default hot(module)(connector(TableContainer));
export default connector(TableContainer);

View File

@ -4,7 +4,6 @@ import Page from 'app/core/components/Page/Page';
import { Button, Input, Field, Form } from '@grafana/ui';
import { getConfig } from 'app/core/config';
import { StoreState } from 'app/types';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { NavModel } from '@grafana/data';
import { getNavModel } from '../../core/selectors/navModel';
@ -76,4 +75,4 @@ const mapStateToProps = (state: StoreState) => {
return { navModel: getNavModel(state.navIndex, 'global-orgs') };
};
export default hot(module)(connect(mapStateToProps)(NewOrgPage));
export default connect(mapStateToProps)(NewOrgPage);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { NavModel } from '@grafana/data';
@ -62,4 +61,4 @@ const mapDispatchToProps = {
updateOrganization,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(OrgDetailsPage));
export default connect(mapStateToProps, mapDispatchToProps)(OrgDetailsPage);

View File

@ -1,5 +1,4 @@
import React, { FC } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import UserInviteForm from './UserInviteForm';
import { contextSrv, NavModel } from 'app/core/core';
@ -30,4 +29,4 @@ const mapStateToProps = (state: StoreState) => ({
navModel: getNavModel(state.navIndex, 'users'),
});
export default hot(module)(connect(mapStateToProps)(UserInvitePage));
export default connect(mapStateToProps)(UserInvitePage);

View File

@ -1,5 +1,4 @@
import React from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import Page from 'app/core/components/Page/Page';
import PageActionBar from 'app/core/components/PageActionBar/PageActionBar';
@ -65,4 +64,4 @@ export const PluginListPage: React.FC<Props> = ({
);
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(PluginListPage));
export default connect(mapStateToProps, mapDispatchToProps)(PluginListPage);

View File

@ -6,7 +6,6 @@ import { getAllPluginsErrors } from './state/selectors';
import { loadPlugins, loadPluginsErrors } from './state/actions';
import useAsync from 'react-use/lib/useAsync';
import { connect, ConnectedProps } from 'react-redux';
import { hot } from 'react-hot-loader';
import { PluginErrorCode, PluginSignatureStatus } from '@grafana/data';
import { css } from '@emotion/css';
@ -80,9 +79,7 @@ export const PluginsErrorsInfoUnconnected: React.FC<PluginsErrorsInfoProps> = ({
);
};
export const PluginsErrorsInfo = hot(module)(
connect(mapStateToProps, mapDispatchToProps)(PluginsErrorsInfoUnconnected)
);
export const PluginsErrorsInfo = connect(mapStateToProps, mapDispatchToProps)(PluginsErrorsInfoUnconnected);
function mapPluginErrorCodeToSignatureStatus(code: PluginErrorCode) {
switch (code) {

View File

@ -1,6 +1,5 @@
import React from 'react';
import { useMount } from 'react-use';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { NavModel } from '@grafana/data';
@ -50,4 +49,4 @@ export function ChangePasswordPage({ navModel, loadUser, isUpdating, user, chang
);
}
export default hot(module)(connector(ChangePasswordPage));
export default connector(ChangePasswordPage);

View File

@ -1,7 +1,6 @@
import React from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { useMount } from 'react-use';
import { hot } from 'react-hot-loader';
import { NavModel } from '@grafana/data';
import { VerticalGroup } from '@grafana/ui';
@ -78,4 +77,4 @@ export function UserProfileEditPage({
);
}
export default hot(module)(connector(UserProfileEditPage));
export default connector(UserProfileEditPage);

View File

@ -25,9 +25,9 @@ import {
QueryOperationRowRenderProps,
} from 'app/core/components/QueryOperationRow/QueryOperationRow';
import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
import { DashboardModel } from '../../dashboard/state/DashboardModel';
import { selectors } from '@grafana/e2e-selectors';
import { PanelModel } from 'app/features/dashboard/state';
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
interface Props<TQuery extends DataQuery> {

View File

@ -1,6 +1,5 @@
import React, { PureComponent } from 'react';
import Page from 'app/core/components/Page/Page';
import { hot } from 'react-hot-loader';
import { Button, Form, Field, Input, FieldSet, Label, Tooltip, Icon } from '@grafana/ui';
import { NavModel } from '@grafana/data';
import { getBackendSrv, locationService } from '@grafana/runtime';
@ -68,4 +67,4 @@ function mapStateToProps(state: StoreState) {
};
}
export default hot(module)(connect(mapStateToProps)(CreateTeam));
export default connect(mapStateToProps)(CreateTeam);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import Page from 'app/core/components/Page/Page';
import { DeleteButton, LinkButton } from '@grafana/ui';
import { NavModel } from '@grafana/data';
@ -167,4 +166,4 @@ const mapDispatchToProps = {
setSearchQuery,
};
export default hot(module)(connectWithCleanUp(mapStateToProps, mapDispatchToProps, (state) => state.teams)(TeamList));
export default connectWithCleanUp(mapStateToProps, mapDispatchToProps, (state) => state.teams)(TeamList);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { renderMarkdown } from '@grafana/data';
import { HorizontalGroup, Pagination, VerticalGroup } from '@grafana/ui';
@ -138,4 +137,4 @@ export class UsersListPage extends PureComponent<Props, State> {
}
}
export default hot(module)(connector(UsersListPage));
export default connector(UsersListPage);

View File

@ -1,20 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#000">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width" />
<meta name="theme-color" content="#000" />
<title>Grafana - Error</title>
<base href="[[.AppSubUrl]]/" />
<link rel="stylesheet" href="public/build/grafana.[[ .Theme ]].<%= webpack.hash %>.css">
<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="stylesheet" href="public/build/grafana.[[ .Theme ]].<%= compilation.hash %>.css" />
<link rel="icon" type="image/png" href="public/img/fav32.png" />
<link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28" />
</head>
<body class="theme-[[ .Theme ]]">
@ -43,10 +42,10 @@
</div>
<br />
[[if .ErrorMsg]]
<h4 class="page-heading">Error details</h4>
<div class="alert-text">
<pre>[[.ErrorMsg]]</pre>
</div>
<h4 class="page-heading">Error details</h4>
<div class="alert-text">
<pre>[[.ErrorMsg]]</pre>
</div>
[[end]]
<div style="padding: 2rem 0 0">
<p>Check the Grafana server logs for the detailed error message.</p>

View File

@ -20,7 +20,10 @@
<link rel="icon" type="image/png" href="[[.FavIcon]]" />
<link rel="apple-touch-icon" sizes="180x180" href="[[.AppleTouchIcon]]" />
<link rel="mask-icon" href="[[.ContentDeliveryURL]]public/img/grafana_mask_icon.svg" color="#F05A28" />
<link rel="stylesheet" href="[[.ContentDeliveryURL]]public/build/grafana.[[ .Theme ]].<%= webpack.hash %>.css" />
<link
rel="stylesheet"
href="[[.ContentDeliveryURL]]public/build/grafana.[[ .Theme ]].<%= compilation.hash %>.css"
/>
<script nonce="[[.Nonce]]">
performance.mark('frontend_boot_css_time_seconds');
@ -248,8 +251,8 @@
settings: [[.Settings]],
navTree: [[.NavTree]],
themePaths: {
light: '[[.ContentDeliveryURL]]public/build/grafana.light.<%= webpack.hash %>.css',
dark: '[[.ContentDeliveryURL]]public/build/grafana.dark.<%= webpack.hash %>.css'
light: '[[.ContentDeliveryURL]]public/build/grafana.light.<%= compilation.hash %>.css',
dark: '[[.ContentDeliveryURL]]public/build/grafana.dark.<%= compilation.hash %>.css'
}
};
@ -303,18 +306,18 @@
})(window, document, 'script', 'dataLayer', '[[.GoogleTagManagerId]]');
</script>
<!-- End Google Tag Manager -->
[[end]] <% for (key in htmlWebpackPlugin.files.chunks) { %> <% if (htmlWebpackPlugin.files.jsIntegrity) { %>
[[end]] <% for (index in htmlWebpackPlugin.files.js) { %> <% if (htmlWebpackPlugin.files.jsIntegrity) { %>
<script
nonce="[[.Nonce]]"
src="[[.ContentDeliveryURL]]<%= htmlWebpackPlugin.files.chunks[key].entry %>"
src="[[.ContentDeliveryURL]]<%= htmlWebpackPlugin.files.js[index] %>"
type="text/javascript"
integrity="<%= htmlWebpackPlugin.files.jsIntegrity[htmlWebpackPlugin.files.js.indexOf(htmlWebpackPlugin.files.chunks[key].entry)] %>"
integrity="<%= htmlWebpackPlugin.files.jsIntegrity[index] %>"
crossorigin="<%= webpackConfig.output.crossOriginLoading %>"
></script>
<% } else { %>
<script
nonce="[[.Nonce]]"
src="[[.ContentDeliveryURL]]<%= htmlWebpackPlugin.files.chunks[key].entry %>"
src="[[.ContentDeliveryURL]]<%= htmlWebpackPlugin.files.js[index] %>"
type="text/javascript"
></script>
<% } %> <% } %>

View File

@ -1,6 +1,7 @@
module.exports = function getBabelConfig(options = {}) {
return {
const babelOptions = {
cacheDirectory: true,
cacheCompression: false,
babelrc: false,
// Note: order is bottom-to-top and/or right-to-left
presets: [
@ -43,4 +44,10 @@ module.exports = function getBabelConfig(options = {}) {
'angularjs-annotate',
],
};
if (options.REACT_REFRESH) {
babelOptions.plugins.push('react-refresh/babel');
}
return babelOptions;
};

View File

@ -83,7 +83,7 @@ module.exports.pitch = function pitch(remainingRequest) {
function getOutputFilename(options, { target }) {
if (!options) {
return { filename: `[hash].${target}.js`, options: undefined };
return { filename: `[fullhash].${target}.js`, options: undefined };
}
if (typeof options === 'string') {
return { filename: options, options: undefined };

View File

@ -1,9 +1,7 @@
module.exports = () => {
return {
plugins: {
autoprefixer: {},
'postcss-reporter': {},
'postcss-browser-reporter': {},
},
};
module.exports = {
plugins: {
autoprefixer: {},
'postcss-reporter': {},
'postcss-browser-reporter': {},
},
};

View File

@ -1,10 +1,12 @@
'use strict';
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = function (options) {
return {
test: /\.scss$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{
@ -19,7 +21,9 @@ module.exports = function (options) {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap,
config: { path: __dirname },
postcssOptions: {
config: path.resolve(__dirname),
},
},
},
{

View File

@ -2,7 +2,6 @@ const fs = require('fs-extra');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const getBabelConfig = require('./babel.config');
class CopyUniconsPlugin {
apply(compiler) {
@ -17,31 +16,15 @@ class CopyUniconsPlugin {
}
}
// https://github.com/visionmedia/debug/issues/701#issuecomment-505487361
function shouldExclude(filename) {
// There is external js code inside this which needs to be processed by babel.
if (filename.indexOf(`jaeger-ui-components`) > 0) {
return false;
}
const packagesToProcessbyBabel = ['debug', 'lru-cache', 'yallist', 'react-hook-form', 'rc-trigger'];
for (const package of packagesToProcessbyBabel) {
if (filename.indexOf(`node_modules/${package}`) > 0) {
return false;
}
}
return true;
}
console.log(path.resolve());
module.exports = {
target: 'web',
entry: {
app: './public/app/index.ts',
},
output: {
clean: true,
path: path.resolve(__dirname, '../../public/build'),
filename: '[name].[hash].js',
filename: '[name].[fullhash].js',
// Keep publicPath relative for host.com/grafana/ deployments
publicPath: 'public/build/',
},
@ -64,15 +47,16 @@ module.exports = {
// we need full path to root node_modules for grafana-enterprise symlink to work
path.resolve('node_modules'),
],
fallback: {
fs: false,
stream: false,
},
},
ignoreWarnings: [/export .* was not found in/],
stats: {
children: false,
warningsFilter: /export .* was not found in/,
source: false,
},
node: {
fs: 'empty',
},
plugins: [
new CopyUniconsPlugin(),
new CopyWebpackPlugin({
@ -97,33 +81,12 @@ module.exports = {
],
module: {
rules: [
/**
* Some npm packages are bundled with es2015 syntax, ie. debug
* To make them work with PhantomJS we need to transpile them
* to get rid of unsupported syntax.
*/
{
test: /\.js$/,
exclude: shouldExclude,
use: [
{
loader: 'babel-loader',
options: getBabelConfig(),
},
],
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
query: 'jQuery',
},
{
loader: 'expose-loader',
query: '$',
},
],
loader: 'expose-loader',
options: {
exposes: ['$', 'jQuery'],
},
},
{
test: /\.html$/,
@ -135,10 +98,11 @@ module.exports = {
{
loader: 'html-loader',
options: {
attrs: [],
minimize: true,
removeComments: false,
collapseWhitespace: false,
sources: false,
minimize: {
removeComments: false,
collapseWhitespace: false,
},
},
},
],
@ -170,7 +134,7 @@ module.exports = {
},
// https://webpack.js.org/plugins/split-chunks-plugin/#split-chunks-example-3
optimization: {
moduleIds: 'hashed',
moduleIds: 'named',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
@ -194,7 +158,7 @@ module.exports = {
priority: 50,
enforce: true,
},
vendors: {
defaultVendors: {
test: /[\\/]node_modules[\\/].*[jt]sx?$/,
chunks: 'initial',
priority: -10,

View File

@ -1,11 +1,11 @@
'use strict';
const merge = require('webpack-merge');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');
const webpack = require('webpack');
const { DefinePlugin } = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const ESLintPlugin = require('eslint-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const getBabelConfig = require('./babel.config');
@ -32,13 +32,11 @@ module.exports = (env = {}) =>
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'babel-loader',
options: getBabelConfig({ BABEL_ENV: 'dev' }),
},
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: getBabelConfig({ BABEL_ENV: 'dev' }),
},
],
},
require('./sass.rule.js')({
sourceMap: false,
@ -47,19 +45,25 @@ module.exports = (env = {}) =>
],
},
// https://webpack.js.org/guides/build-performance/#output-without-path-info
output: {
pathinfo: false,
filename: '[name].js',
},
// https://webpack.js.org/guides/build-performance/#avoid-extra-optimization-steps
optimization: {
runtimeChunk: true,
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
},
plugins: [
new CleanWebpackPlugin(),
env.noTsCheck
? new webpack.DefinePlugin({}) // bogus plugin to satisfy webpack API
parseInt(env.noTsCheck, 10)
? new DefinePlugin({}) // bogus plugin to satisfy webpack API
: new ForkTsCheckerWebpackPlugin({
eslint: {
enabled: true,
files: ['public/app/**/*.{ts,tsx}', 'packages/*/src/**/*.{ts,tsx}'],
options: {
cache: true,
},
memoryLimit: 4096,
},
async: true, // don't block webpack emit
typescript: {
mode: 'write-references',
memoryLimit: 4096,
@ -69,8 +73,13 @@ module.exports = (env = {}) =>
},
},
}),
// next major version of ForkTsChecker is dropping support for ESLint
new ESLintPlugin({
lintDirtyModulesOnly: true, // don't lint on start, only lint changed files
extensions: ['.ts', '.tsx'],
}),
new MiniCssExtractPlugin({
filename: 'grafana.[name].[hash].css',
filename: 'grafana.[name].[fullhash].css',
}),
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, '../../public/views/error.html'),
@ -82,13 +91,12 @@ module.exports = (env = {}) =>
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, '../../public/views/index.html'),
template: path.resolve(__dirname, '../../public/views/index-template.html'),
hash: true,
inject: false,
chunksSortMode: 'none',
excludeChunks: ['dark', 'light'],
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
new DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development'),
},

View File

@ -1,104 +1,91 @@
'use strict';
const merge = require('webpack-merge');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');
const webpack = require('webpack');
const { DefinePlugin } = require('webpack');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const getBabelConfig = require('./babel.config');
module.exports = merge(common, {
devtool: 'inline-source-map',
mode: 'development',
entry: {
app: ['webpack-dev-server/client?http://localhost:3333', './public/app/dev.ts'],
app: ['./public/app/index.ts'],
dark: './public/sass/grafana.dark.scss',
light: './public/sass/grafana.light.scss',
},
output: {
path: path.resolve(__dirname, '../../public/build'),
filename: '[name].[hash].js',
publicPath: '/public/build/',
pathinfo: false,
watchOptions: {
ignored: /node_modules/,
},
resolve: {
extensions: ['.scss', '.ts', '.tsx', '.es6', '.js', '.json', '.svg', '.woff2', '.png', '.html'],
},
devtool: 'eval-source-map',
devServer: {
publicPath: '/public/build/',
devMiddleware: {
writeToDisk: true,
},
historyApiFallback: true,
hot: true,
open: false,
port: 3333,
proxy: {
'!/public/build': 'http://localhost:3000',
},
watchOptions: {
ignored: /node_modules/,
static: {
publicPath: '/public/build/',
},
},
optimization: {
removeAvailableModules: false,
runtimeChunk: false,
removeEmptyChunks: false,
splitChunks: false,
},
module: {
// Note: order is bottom-to-top and/or right-to-left
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'babel-loader',
options: getBabelConfig({ BABEL_ENV: 'dev', REACT_REFRESH: true }),
},
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: getBabelConfig(),
},
],
},
{
test: /\.scss$/,
use: [
'style-loader', // creates style nodes from JS strings
'css-loader', // translates CSS into CommonJS
{
loader: 'postcss-loader',
options: {
config: {
path: __dirname + '/postcss.config.js',
},
},
},
{
loader: 'sass-loader',
},
],
},
{
test: /\.(png|jpg|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
loader: 'file-loader',
include: [path.resolve(__dirname, '../../public/'), path.resolve(__dirname, '../../packages/')],
},
require('./sass.rule.js')({
sourceMap: false,
preserveUrl: false,
}),
],
},
// https://webpack.js.org/guides/build-performance/#output-without-path-info
output: {
filename: '[name].js',
pathinfo: false,
},
// https://webpack.js.org/guides/build-performance/#avoid-extra-optimization-steps
optimization: {
runtimeChunk: true,
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'grafana.[name].[fullhash].css',
}),
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, '../../public/views/index.html'),
template: path.resolve(__dirname, '../../public/views/index-template.html'),
inject: 'body',
alwaysWriteToDisk: true,
hash: true,
inject: false,
chunksSortMode: 'none',
excludeChunks: ['dark', 'light'],
}),
new HtmlWebpackHarddiskPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
GRAFANA_THEME: JSON.stringify(process.env.GRAFANA_THEME || 'dark'),
new ReactRefreshWebpackPlugin(),
new DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development'),
},

View File

@ -1,12 +1,12 @@
'use strict';
const merge = require('webpack-merge');
const { merge } = require('webpack-merge');
const TerserPlugin = require('terser-webpack-plugin');
const common = require('./webpack.common.js');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const getBabelConfig = require('./babel.config');
module.exports = merge(common, {
@ -41,16 +41,14 @@ module.exports = merge(common, {
nodeEnv: 'production',
minimizer: [
new TerserPlugin({
cache: false,
parallel: false,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
new CssMinimizerPlugin(),
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'grafana.[name].[hash].css',
filename: 'grafana.[name].[fullhash].css',
}),
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, '../../public/views/error.html'),

2464
yarn.lock

File diff suppressed because it is too large Load Diff