parent
2e1d9d83c8
commit
d39e547623
11 changed files with 3810 additions and 3406 deletions
3
.babelrc
3
.babelrc
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"presets": [
|
"presets": [
|
||||||
["@babel/preset-env", {
|
["@babel/preset-env", {
|
||||||
modules: false
|
modules: false,
|
||||||
|
corejs: 3
|
||||||
}],
|
}],
|
||||||
"@babel/preset-react",
|
"@babel/preset-react",
|
||||||
"@babel/preset-flow"
|
"@babel/preset-flow"
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ types/mqtt.js
|
||||||
[options]
|
[options]
|
||||||
esproposal.export_star_as=enable
|
esproposal.export_star_as=enable
|
||||||
module.system.node.resolve_dirname=node_modules
|
module.system.node.resolve_dirname=node_modules
|
||||||
module.system.node.resolve_dirname=./src
|
module.system.node.resolve_dirname=src
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
all=warn
|
all=warn
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import * as types from "config/types";
|
||||||
import { tasmota, esper } from "./utils";
|
import { tasmota, esper } from "./utils";
|
||||||
|
|
||||||
export const topics: Topics = {
|
export const topics: Topics = {
|
||||||
|
...tasmota.topics("8", "ledOlymp"),
|
||||||
|
...esper.topics("afba45", "alarm"),
|
||||||
videogames: {
|
videogames: {
|
||||||
state: {
|
state: {
|
||||||
name: "/service/openhab/out/pca301_videogames/state",
|
name: "/service/openhab/out/pca301_videogames/state",
|
||||||
|
|
@ -38,9 +40,7 @@ export const topics: Topics = {
|
||||||
type: types.option({ on: "ON", off: "OFF" })
|
type: types.option({ on: "ON", off: "OFF" })
|
||||||
},
|
},
|
||||||
defaultValue: "off"
|
defaultValue: "off"
|
||||||
},
|
}
|
||||||
...tasmota.topics("8", "ledOlymp"),
|
|
||||||
...esper.topics("afba45", "alarm")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const controls: Controls = {
|
export const controls: Controls = {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { ControlUI } from "config/flowtypes";
|
import type { ControlUI, Topics } from "config/flowtypes";
|
||||||
import { mdi } from "config/icon";
|
import { mdi } from "config/icon";
|
||||||
import { hex } from "config/colors";
|
import { hex, type Color } from "config/colors";
|
||||||
import * as types from "config/types";
|
import * as types from "config/types";
|
||||||
|
|
||||||
export const tasmota = {
|
export const tasmota = {
|
||||||
topics: (id: string, name: string) => ({
|
topics: (id: string, name: string): Topics => ({
|
||||||
[name]: {
|
[name]: {
|
||||||
state: {
|
state: {
|
||||||
name: `stat/sonoff${id}/POWER`,
|
name: `stat/sonoff${id}/POWER`,
|
||||||
|
|
@ -26,20 +26,20 @@ export const tasmota = {
|
||||||
defaultValue: "off"
|
defaultValue: "off"
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
iconColor: (name: string, onColor: Color = hex("#00FF00")) =>
|
iconColor: (name: string, onCol: Color = hex("#00FF00")): (State => Color) =>
|
||||||
(state: State) => {
|
(state: State): Color => {
|
||||||
if (state[`${name}_online`] === "off") {
|
if (state[`${name}_online`] === "off") {
|
||||||
return hex("#888888");
|
return hex("#888888");
|
||||||
} else if (state[name] === "on") {
|
} else if (state[name] === "on") {
|
||||||
return onColor;
|
return onCol;
|
||||||
}
|
}
|
||||||
return hex("#000000");
|
return hex("#000000");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const floalt = {
|
export const floalt = {
|
||||||
color: (lightId: string) => `floalt_${lightId}_color`,
|
color: (lightId: string): string => `floalt_${lightId}_color`,
|
||||||
brightness: (lightId: string) => `floalt_${lightId}_brightness`,
|
brightness: (lightId: string): string => `floalt_${lightId}_brightness`,
|
||||||
topics: (lightId: string) => ({
|
topics: (lightId: string): Topics => ({
|
||||||
[`floalt_${lightId}_color`]: {
|
[`floalt_${lightId}_color`]: {
|
||||||
state: {
|
state: {
|
||||||
name: `/service/openhab/out/tradfri_0220_gwb8d7af2b448f_${lightId}` +
|
name: `/service/openhab/out/tradfri_0220_gwb8d7af2b448f_${lightId}` +
|
||||||
|
|
@ -70,9 +70,9 @@ export const floalt = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const tradfriRemote = {
|
const tradfriRemote = {
|
||||||
level: (remoteId: string) => `tradfri_remote_${remoteId}_level`,
|
level: (remoteId: string): string => `tradfri_remote_${remoteId}_level`,
|
||||||
low: (remoteId: string) => `tradfri_remote_${remoteId}_low`,
|
low: (remoteId: string): string => `tradfri_remote_${remoteId}_low`,
|
||||||
topics: (remoteId: string) => ({
|
topics: (remoteId: string): Topics => ({
|
||||||
[`tradfri_remote_${remoteId}_level`]: {
|
[`tradfri_remote_${remoteId}_level`]: {
|
||||||
state: {
|
state: {
|
||||||
name: `/service/openhab/out/tradfri_0830_gwb8d7af2b448f_${remoteId}` +
|
name: `/service/openhab/out/tradfri_0830_gwb8d7af2b448f_${remoteId}` +
|
||||||
|
|
@ -135,7 +135,7 @@ const esperStatistics = (name: string,
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
const esperTopics = (chipId: string, name: string) => ({
|
const esperTopics = (chipId: string, name: string): Topics => ({
|
||||||
[`esper_${name}_version`]: {
|
[`esper_${name}_version`]: {
|
||||||
state: {
|
state: {
|
||||||
name: `/service/esper/${chipId}/info`,
|
name: `/service/esper/${chipId}/info`,
|
||||||
|
|
|
||||||
34
package.json
34
package.json
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "mqtt-control-map",
|
"name": "mqtt-control-map",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "uwap <me+mqttmap.package.json@uwap.name>",
|
"author": "uwap <me@uwap.name>",
|
||||||
"description": "control devices via mqtt on a beautiful map of your space",
|
"description": "Control Devices via mqtt, visualized on a Map",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --bail --config webpack.config.js -p --env",
|
"build": "webpack --bail --config webpack.config.js -p --env",
|
||||||
"dev": "webpack --bail --config webpack.config.js --mode development --env",
|
"dev": "webpack --bail --config webpack.config.js --mode development --env",
|
||||||
|
|
@ -12,12 +12,12 @@
|
||||||
"precommit": "yarn lint"
|
"precommit": "yarn lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "^3.0.1",
|
"@material-ui/core": "^4.11.0",
|
||||||
"@material-ui/lab": "^3.0.0-alpha.16",
|
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||||
"@mdi/font": "^4.0.96",
|
"@mdi/font": "^5.6.55",
|
||||||
"leaflet": "^1.5.1",
|
"leaflet": "^1.5.1",
|
||||||
"lodash-es": "^4.17.15",
|
"lodash-es": "^4.17.15",
|
||||||
"mqtt": "^3.0.0",
|
"mqtt": "^4.2.1",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-leaflet": "^2.4.0",
|
"react-leaflet": "^2.4.0",
|
||||||
|
|
@ -28,25 +28,25 @@
|
||||||
"@babel/core": "^7.5.5",
|
"@babel/core": "^7.5.5",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.5.5",
|
"@babel/plugin-proposal-class-properties": "^7.5.5",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
||||||
"@babel/polyfill": "^7.4.4",
|
|
||||||
"@babel/preset-env": "^7.5.5",
|
"@babel/preset-env": "^7.5.5",
|
||||||
"@babel/preset-flow": "^7.0.0-rc.1",
|
"@babel/preset-flow": "^7.0.0-rc.1",
|
||||||
"@babel/preset-react": "^7.0.0-rc.1",
|
"@babel/preset-react": "^7.0.0-rc.1",
|
||||||
"babel-eslint": "^10.0.2",
|
"babel-eslint": "^10.0.2",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
"clean-webpack-plugin": "^2.0.0",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
"css-loader": "^3.0.0",
|
"core-js": "3",
|
||||||
"eslint": "^6.1.0",
|
"css-loader": "^4.3.0",
|
||||||
"eslint-plugin-flowtype": "^4.0.0",
|
"eslint": "^7.10.0",
|
||||||
|
"eslint-plugin-flowtype": "^5.2.0",
|
||||||
"eslint-plugin-fp": "^2.3.0",
|
"eslint-plugin-fp": "^2.3.0",
|
||||||
"eslint-plugin-react": "^7.14.3",
|
"eslint-plugin-react": "^7.14.3",
|
||||||
"file-loader": "^5.0.0",
|
"file-loader": "^6.1.0",
|
||||||
"flow": "^0.2.3",
|
"flow": "^0.2.3",
|
||||||
"flow-bin": "^0.82.0",
|
"flow-bin": "^0.135.0",
|
||||||
"flow-typed": "^2.3.0",
|
"flow-typed": "^3.2.1",
|
||||||
"html-webpack-plugin": "^3.1.0",
|
"html-webpack-plugin": "^4.5.0",
|
||||||
"husky": "^3.0.0",
|
"husky": "^4.3.0",
|
||||||
"style-loader": "^0.23.0",
|
"style-loader": "^1.3.0",
|
||||||
"webpack": "^4.39.1",
|
"webpack": "^4.39.1",
|
||||||
"webpack-cli": "^3.3.6",
|
"webpack-cli": "^3.3.6",
|
||||||
"webpack-dev-server": "^3.7.2",
|
"webpack-dev-server": "^3.7.2",
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import throttle from "lodash/throttle";
|
||||||
|
|
||||||
import type { Config, Control, Topics } from "config/flowtypes";
|
import type { Config, Control, Topics } from "config/flowtypes";
|
||||||
|
|
||||||
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
|
import {
|
||||||
import createMuiTheme from "@material-ui/core/styles/createMuiTheme";
|
MuiThemeProvider, createMuiTheme, withStyles
|
||||||
import withStyles from "@material-ui/core/styles/withStyles";
|
} from "@material-ui/core/styles";
|
||||||
import * as Colors from "@material-ui/core/colors";
|
import * as Colors from "@material-ui/core/colors";
|
||||||
import Snackbar from "@material-ui/core/Snackbar";
|
import Snackbar from "@material-ui/core/Snackbar";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
|
@ -55,7 +55,8 @@ class App extends React.PureComponent<AppProps & Classes, AppState> {
|
||||||
onDisconnect: () => this.setState({ mqttConnected: false }),
|
onDisconnect: () => this.setState({ mqttConnected: false }),
|
||||||
subscribe: map(
|
subscribe: map(
|
||||||
filter(keys(this.topics), (x) => this.topics[x].state != null),
|
filter(keys(this.topics), (x) => this.topics[x].state != null),
|
||||||
(x) => this.topics[x].state.name)
|
(x) => (this.topics[x].state != null ? this.topics[x].state.name : "")
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
mqttConnected: false,
|
mqttConnected: false,
|
||||||
search: "",
|
search: "",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import withStyles from "@material-ui/core/styles/withStyles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import Drawer from "@material-ui/core/Drawer";
|
import Drawer from "@material-ui/core/Drawer";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
|
@ -21,13 +21,23 @@ export type SideBarProps = {
|
||||||
children?: React.Node
|
children?: React.Node
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = SideBarProps & Classes;
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
drawerPaper: {
|
||||||
|
width: 340
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
flex: 1,
|
||||||
|
marginLeft: theme.spacing(1)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
const SideBar = (props: Props) => (
|
const SideBar = (props: SideBarProps) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
return (
|
||||||
<Drawer open={props.open}
|
<Drawer open={props.open}
|
||||||
anchor="right"
|
anchor="right"
|
||||||
onClose={props.onCloseRequest}
|
onClose={props.onCloseRequest}
|
||||||
classes={{paper: props.classes.drawerPaper}}
|
classes={{paper: classes.drawerPaper}}
|
||||||
variant="persistent"
|
variant="persistent"
|
||||||
>
|
>
|
||||||
<AppBar position="static">
|
<AppBar position="static">
|
||||||
|
|
@ -35,7 +45,7 @@ const SideBar = (props: Props) => (
|
||||||
<span>
|
<span>
|
||||||
{props.icon == null || renderRawIcon(props.icon, "mdi-36px")}
|
{props.icon == null || renderRawIcon(props.icon, "mdi-36px")}
|
||||||
</span>
|
</span>
|
||||||
<Typography variant="title" className={props.classes.title}>
|
<Typography variant="subtitle1" className={classes.title}>
|
||||||
{props.control == null ? "" : props.control.name}
|
{props.control == null ? "" : props.control.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton onClick={props.onCloseRequest}>
|
<IconButton onClick={props.onCloseRequest}>
|
||||||
|
|
@ -47,16 +57,7 @@ const SideBar = (props: Props) => (
|
||||||
<React.Fragment>{props.children}</React.Fragment>
|
<React.Fragment>{props.children}</React.Fragment>
|
||||||
</List>
|
</List>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const styles = (theme) => ({
|
export default SideBar;
|
||||||
drawerPaper: {
|
|
||||||
width: 340
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
flex: 1,
|
|
||||||
marginLeft: theme.spacing.unit
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default withStyles(styles)(SideBar);
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import Toolbar from "@material-ui/core/Toolbar";
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
import InputBase from "@material-ui/core/InputBase";
|
import InputBase from "@material-ui/core/InputBase";
|
||||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||||
import { withStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import Tooltip from "@material-ui/core/Tooltip";
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ const renderConnectionIndicator = (connected: boolean) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchStyles = (theme) => ({
|
const useSearchStyles = makeStyles((theme) => ({
|
||||||
search: {
|
search: {
|
||||||
position: "relative",
|
position: "relative",
|
||||||
borderRadius: theme.shape.borderRadius,
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
|
@ -36,16 +36,16 @@ const searchStyles = (theme) => ({
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundColor: fade(theme.palette.common.white, 0.25)
|
backgroundColor: fade(theme.palette.common.white, 0.25)
|
||||||
},
|
},
|
||||||
marginRight: theme.spacing.unit * 2,
|
marginRight: theme.spacing(2),
|
||||||
marginLeft: 0,
|
marginLeft: 0,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
[theme.breakpoints.up("sm")]: {
|
[theme.breakpoints.up("sm")]: {
|
||||||
marginLeft: theme.spacing.unit * 3,
|
marginLeft: theme.spacing(3),
|
||||||
width: "auto"
|
width: "auto"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
searchIcon: {
|
searchIcon: {
|
||||||
width: theme.spacing.unit * 6,
|
width: theme.spacing(6),
|
||||||
height: "100%",
|
height: "100%",
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
pointerEvents: "none",
|
pointerEvents: "none",
|
||||||
|
|
@ -59,31 +59,32 @@ const searchStyles = (theme) => ({
|
||||||
width: "100%"
|
width: "100%"
|
||||||
},
|
},
|
||||||
inputInput: {
|
inputInput: {
|
||||||
paddingTop: theme.spacing.unit,
|
paddingTop: theme.spacing(1),
|
||||||
paddingRight: theme.spacing.unit,
|
paddingRight: theme.spacing(1),
|
||||||
paddingBottom: theme.spacing.unit,
|
paddingBottom: theme.spacing(1),
|
||||||
paddingLeft: theme.spacing.unit * 6,
|
paddingLeft: theme.spacing(6),
|
||||||
transition: theme.transitions.create("width"),
|
transition: theme.transitions.create("width"),
|
||||||
width: "100%",
|
width: "100%",
|
||||||
[theme.breakpoints.up("md")]: {
|
[theme.breakpoints.up("md")]: {
|
||||||
width: 200
|
width: 200
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
const RawSearch = (props: SearchBarProps & Classes) => (
|
const Search = (props: SearchBarProps) => {
|
||||||
<div className={props.classes.search}>
|
const classes = useSearchStyles();
|
||||||
<i className={`mdi mdi-magnify ${props.classes.searchIcon}`}></i>
|
return (
|
||||||
|
<div className={classes.search}>
|
||||||
|
<i className={`mdi mdi-magnify ${classes.searchIcon}`}></i>
|
||||||
<InputBase placeholder="Search…" type="search"
|
<InputBase placeholder="Search…" type="search"
|
||||||
onChange={(e) => props.onSearch(e.target.value)}
|
onChange={(e) => props.onSearch(e.target.value)}
|
||||||
classes={{
|
classes={{
|
||||||
root: props.classes.inputRoot,
|
root: classes.inputRoot,
|
||||||
input: props.classes.inputInput
|
input: classes.inputInput
|
||||||
}} />
|
}} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
const Search = withStyles(searchStyles)(RawSearch);
|
|
||||||
|
|
||||||
const openOnGithub = () => window.open(
|
const openOnGithub = () => window.open(
|
||||||
"https://github.com/uwap/mqtt-control-map", "_blank");
|
"https://github.com/uwap/mqtt-control-map", "_blank");
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { isDisabled, getValue } from "./utils";
|
||||||
|
|
||||||
import type { UISlider } from "config/flowtypes";
|
import type { UISlider } from "config/flowtypes";
|
||||||
|
|
||||||
import SliderComponent from "@material-ui/lab/Slider";
|
import SliderComponent from "@material-ui/core/Slider";
|
||||||
|
|
||||||
const changeSliderValue = (item: UISlider, changeState) => (_e, v) =>
|
const changeSliderValue = (item: UISlider, changeState) => (_e, v) =>
|
||||||
changeState(item, v.toString());
|
changeState(item, v.toString());
|
||||||
|
|
@ -36,5 +36,3 @@ export default createComponent({
|
||||||
},
|
},
|
||||||
baseComponent: BaseComponent
|
baseComponent: BaseComponent
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const WebpackShellPlugin = require('webpack-shell-plugin');
|
const WebpackShellPlugin = require('webpack-shell-plugin');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||||
const preBuildScripts = process.env.NO_FLOW == undefined ?
|
const preBuildScripts = process.env.NO_FLOW == undefined ?
|
||||||
process.env.FLOW_PATH != undefined ? [process.env.FLOW_PATH] : ['flow']
|
process.env.FLOW_PATH != undefined ? [process.env.FLOW_PATH] : ['flow']
|
||||||
: [];
|
: [];
|
||||||
|
|
@ -16,7 +16,7 @@ const configPath = env => {
|
||||||
|
|
||||||
module.exports = env => ({
|
module.exports = env => ({
|
||||||
entry: {
|
entry: {
|
||||||
main: ["@babel/polyfill", configPath(env),
|
main: ["core-js/stable", "regenerator-runtime/runtime", configPath(env),
|
||||||
path.resolve(__dirname, 'src/index.jsx')]
|
path.resolve(__dirname, 'src/index.jsx')]
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|
@ -34,7 +34,7 @@ module.exports = env => ({
|
||||||
rules: [
|
rules: [
|
||||||
// TODO: CSS follow imports and minify + sourcemap on production
|
// TODO: CSS follow imports and minify + sourcemap on production
|
||||||
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
|
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
|
||||||
{ test: /\.(woff2?|eot|ttf|svg)$/, loader: "file-loader" },
|
{ test: /\.(woff2?|eot|ttf|svg)$/, use: [ { loader: "file-loader", options: { esModule: false } } ] },
|
||||||
{ test: /\.js(x)?$/, loader: "babel-loader?cacheDirectory=true" }
|
{ test: /\.js(x)?$/, loader: "babel-loader?cacheDirectory=true" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue