mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 10:11:03 +00:00
Merge pull request #2256 from weaveworks/contrast-as-component
Load contrast stylesheet
This commit is contained in:
@@ -12,6 +12,7 @@ import { doControlRequest, getAllNodes, getNodesDelta, getNodeDetails,
|
||||
import { getActiveTopologyOptions,
|
||||
getCurrentTopologyUrl } from '../utils/topology-utils';
|
||||
import { storageSet } from '../utils/storage-utils';
|
||||
import { loadTheme } from '../utils/contrast-utils';
|
||||
|
||||
const log = debug('scope:app-actions');
|
||||
|
||||
@@ -647,6 +648,18 @@ export function receiveNotFound(nodeId) {
|
||||
};
|
||||
}
|
||||
|
||||
export function setContrastMode(enabled) {
|
||||
return (dispatch, getState) => {
|
||||
loadTheme(enabled ? 'contrast' : 'normal');
|
||||
dispatch({
|
||||
type: ActionTypes.TOGGLE_CONTRAST_MODE,
|
||||
enabled,
|
||||
});
|
||||
|
||||
updateRoute(getState);
|
||||
};
|
||||
}
|
||||
|
||||
export function route(urlState) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
@@ -668,6 +681,10 @@ export function route(urlState) {
|
||||
state.get('nodeDetails'),
|
||||
dispatch
|
||||
);
|
||||
|
||||
if (urlState.contrastMode) {
|
||||
dispatch(setContrastMode(true));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { connect } from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { enterEdge, leaveEdge } from '../actions/app-actions';
|
||||
import { isContrastMode } from '../utils/contrast-utils';
|
||||
import { NODE_BASE_SIZE } from '../constants/styles';
|
||||
|
||||
class Edge extends React.Component {
|
||||
@@ -15,9 +14,9 @@ class Edge extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id, path, highlighted, blurred, focused, scale } = this.props;
|
||||
const { id, path, highlighted, blurred, focused, scale, contrastMode } = this.props;
|
||||
const className = classNames('edge', { highlighted, blurred, focused });
|
||||
const thickness = scale * (isContrastMode() ? 0.02 : 0.01) * NODE_BASE_SIZE;
|
||||
const thickness = scale * (contrastMode ? 0.02 : 0.01) * NODE_BASE_SIZE;
|
||||
|
||||
// Draws the edge so that its thickness reflects the zoom scale.
|
||||
// Edge shadow is always made 10x thicker than the edge itself.
|
||||
@@ -41,7 +40,13 @@ class Edge extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapStateToProps,
|
||||
{ enterEdge, leaveEdge }
|
||||
)(Edge);
|
||||
|
||||
@@ -3,12 +3,9 @@ import { omit } from 'lodash';
|
||||
import { Motion, spring } from 'react-motion';
|
||||
|
||||
import { NODES_SPRING_ANIMATION_CONFIG } from '../constants/animation';
|
||||
import { isContrastMode } from '../utils/contrast-utils';
|
||||
import Node from './node';
|
||||
|
||||
|
||||
const nodeBlurOpacity = isContrastMode() ? 0.6 : 0.25;
|
||||
|
||||
const transformedNode = (otherProps, { x, y, k, opacity }) => (
|
||||
// NOTE: Controlling blurring and transform from here seems to re-render
|
||||
// faster than adding a CSS class and controlling it from there.
|
||||
@@ -19,7 +16,8 @@ const transformedNode = (otherProps, { x, y, k, opacity }) => (
|
||||
|
||||
export default class NodeContainer extends React.PureComponent {
|
||||
render() {
|
||||
const { dx, dy, isAnimated, scale, blurred } = this.props;
|
||||
const { dx, dy, isAnimated, scale, blurred, contrastMode } = this.props;
|
||||
const nodeBlurOpacity = contrastMode ? 0.6 : 0.25;
|
||||
const forwardedProps = omit(this.props, 'dx', 'dy', 'isAnimated', 'scale', 'blurred');
|
||||
const opacity = blurred ? nodeBlurOpacity : 1;
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { scaleBand } from 'd3-scale';
|
||||
import { List as makeList } from 'immutable';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getNetworkColor } from '../utils/color-utils';
|
||||
import { isContrastMode } from '../utils/contrast-utils';
|
||||
import { NODE_BASE_SIZE } from '../constants/styles';
|
||||
|
||||
// Min size is about a quarter of the width, feels about right.
|
||||
@@ -13,7 +14,7 @@ const borderRadius = 0.01;
|
||||
const offset = 0.67;
|
||||
const x = scaleBand();
|
||||
|
||||
function NodeNetworksOverlay({ stack, networks = makeList() }) {
|
||||
function NodeNetworksOverlay({ stack, networks = makeList(), contrastMode }) {
|
||||
const barWidth = Math.max(1, minBarWidth * networks.size);
|
||||
const yPosition = offset - (barHeight * 0.5);
|
||||
|
||||
@@ -37,7 +38,7 @@ function NodeNetworksOverlay({ stack, networks = makeList() }) {
|
||||
/>
|
||||
));
|
||||
|
||||
const translateY = stack && isContrastMode() ? 0.15 : 0;
|
||||
const translateY = stack && contrastMode ? 0.15 : 0;
|
||||
return (
|
||||
<g transform={`translate(0, ${translateY}) scale(${NODE_BASE_SIZE})`}>
|
||||
{bars.toJS()}
|
||||
@@ -45,4 +46,10 @@ function NodeNetworksOverlay({ stack, networks = makeList() }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default NodeNetworksOverlay;
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NodeNetworksOverlay);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import { NODE_BASE_SIZE } from '../constants/styles';
|
||||
import { isContrastMode } from '../utils/contrast-utils';
|
||||
|
||||
export default function NodeShapeStack(props) {
|
||||
const shift = isContrastMode() ? 0.15 : 0.1;
|
||||
const shift = props.contrastMode ? 0.15 : 0.1;
|
||||
const highlightScale = [1, 1 + shift];
|
||||
const dy = NODE_BASE_SIZE * shift;
|
||||
|
||||
|
||||
@@ -127,7 +127,13 @@ class Node extends React.Component {
|
||||
this.renderStandardLabels(labelClassName, labelMinorClassName, labelOffsetY, mouseEvents)}
|
||||
|
||||
<g {...mouseEvents} ref={this.saveShapeRef}>
|
||||
<NodeShapeType id={id} highlighted={highlighted} color={color} metric={metric} />
|
||||
<NodeShapeType
|
||||
id={id}
|
||||
highlighted={highlighted}
|
||||
color={color}
|
||||
metric={metric}
|
||||
contrastMode={this.props.contrastMode}
|
||||
/>
|
||||
</g>
|
||||
|
||||
{showingNetworks && <NodeNetworksOverlay networks={networks} stack={stack} />}
|
||||
@@ -159,6 +165,7 @@ function mapStateToProps(state) {
|
||||
return {
|
||||
exportingGraph: state.get('exportingGraph'),
|
||||
showingNetworks: state.get('showingNetworks'),
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ class NodesChartNodes extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { layoutNodes, isAnimated } = this.props;
|
||||
const { layoutNodes, isAnimated, contrastMode } = this.props;
|
||||
|
||||
const nodesToRender = layoutNodes.toIndexedSeq()
|
||||
.map(this.nodeHighlightedDecorator)
|
||||
@@ -111,6 +111,7 @@ class NodesChartNodes extends React.Component {
|
||||
dy={node.get('y')}
|
||||
scale={node.get('scale')}
|
||||
isAnimated={isAnimated}
|
||||
contrastMode={contrastMode}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
@@ -130,6 +131,7 @@ function mapStateToProps(state) {
|
||||
selectedNetwork: state.get('selectedNetwork'),
|
||||
selectedNodeId: state.get('selectedNodeId'),
|
||||
searchQuery: state.get('searchQuery'),
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,6 @@ function mapStateToProps(state) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
)(App);
|
||||
|
||||
@@ -4,19 +4,22 @@ import moment from 'moment';
|
||||
|
||||
import Plugins from './plugins';
|
||||
import { getUpdateBufferSize } from '../utils/update-buffer-utils';
|
||||
import { contrastModeUrl, isContrastMode } from '../utils/contrast-utils';
|
||||
import { clickDownloadGraph, clickForceRelayout, clickPauseUpdate,
|
||||
clickResumeUpdate, toggleHelp, toggleTroubleshootingMenu } from '../actions/app-actions';
|
||||
import { basePathSlash } from '../utils/web-api-utils';
|
||||
clickResumeUpdate, toggleHelp, toggleTroubleshootingMenu, setContrastMode } from '../actions/app-actions';
|
||||
|
||||
class Footer extends React.Component {
|
||||
render() {
|
||||
const { hostname, updatePausedAt, version, versionUpdate } = this.props;
|
||||
const contrastMode = isContrastMode();
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.handleContrastClick = this.handleContrastClick.bind(this);
|
||||
}
|
||||
handleContrastClick(e) {
|
||||
e.preventDefault();
|
||||
this.props.setContrastMode(!this.props.contrastMode);
|
||||
}
|
||||
render() {
|
||||
const { hostname, updatePausedAt, version, versionUpdate, contrastMode } = this.props;
|
||||
|
||||
// link url to switch contrast with current UI state
|
||||
const otherContrastModeUrl = contrastMode
|
||||
? basePathSlash(window.location.pathname) : contrastModeUrl;
|
||||
const otherContrastModeTitle = contrastMode
|
||||
? 'Switch to normal contrast' : 'Switch to high contrast';
|
||||
const forceRelayoutTitle = 'Force re-layout (might reduce edge crossings, '
|
||||
@@ -76,7 +79,7 @@ class Footer extends React.Component {
|
||||
title={forceRelayoutTitle}>
|
||||
<span className="fa fa-refresh" />
|
||||
</a>
|
||||
<a className="footer-icon" href={otherContrastModeUrl} title={otherContrastModeTitle}>
|
||||
<a onClick={this.handleContrastClick} className="footer-icon" title={otherContrastModeTitle}>
|
||||
<span className="fa fa-adjust" />
|
||||
</a>
|
||||
<a
|
||||
@@ -101,7 +104,8 @@ function mapStateToProps(state) {
|
||||
hostname: state.get('hostname'),
|
||||
updatePausedAt: state.get('updatePausedAt'),
|
||||
version: state.get('version'),
|
||||
versionUpdate: state.get('versionUpdate')
|
||||
versionUpdate: state.get('versionUpdate'),
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,6 +117,7 @@ export default connect(
|
||||
clickPauseUpdate,
|
||||
clickResumeUpdate,
|
||||
toggleHelp,
|
||||
toggleTroubleshootingMenu
|
||||
toggleTroubleshootingMenu,
|
||||
setContrastMode
|
||||
}
|
||||
)(Footer);
|
||||
|
||||
@@ -58,7 +58,8 @@ const ACTION_TYPES = [
|
||||
'SET_RECEIVED_NODES_DELTA',
|
||||
'SORT_ORDER_CHANGED',
|
||||
'SET_GRID_MODE',
|
||||
'CHANGE_INSTANCE'
|
||||
'CHANGE_INSTANCE',
|
||||
'TOGGLE_CONTRAST_MODE',
|
||||
];
|
||||
|
||||
export default zipObject(ACTION_TYPES, ACTION_TYPES);
|
||||
|
||||
46
client/app/scripts/contrast-compiler.js
Normal file
46
client/app/scripts/contrast-compiler.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
// Webpack plugin for creating contrast mode stylesheet
|
||||
const _ = require('lodash');
|
||||
|
||||
function findAsset(collection, name) {
|
||||
return _.find(collection, c => _.includes(c, name));
|
||||
}
|
||||
|
||||
module.exports = class ContrastStyleCompiler {
|
||||
apply(compiler) {
|
||||
let themeJsChunk;
|
||||
|
||||
compiler.plugin('compilation', (compilation) => {
|
||||
compilation.plugin('html-webpack-plugin-before-html-processing', (htmlPluginData, callback) => {
|
||||
themeJsChunk = findAsset(htmlPluginData.assets.js, 'contrast-theme');
|
||||
if (!themeJsChunk) {
|
||||
return callback(null, htmlPluginData);
|
||||
}
|
||||
// Find the name of the contrast stylesheet and save it to a window variable.
|
||||
const { css, publicPath } = htmlPluginData.assets;
|
||||
const contrast = findAsset(css, 'contrast-theme');
|
||||
const normal = findAsset(css, 'style-app');
|
||||
// Convert to JSON string so they can be parsed into a window variable
|
||||
const themes = JSON.stringify({ normal, contrast, publicPath });
|
||||
// Append a script to the end of <head /> to evaluate before the other scripts are loaded.
|
||||
const script = `<script>window.__WEAVE_SCOPE_THEMES = JSON.parse('${themes}')</script>`;
|
||||
const [head, end] = htmlPluginData.html.split('</head>');
|
||||
htmlPluginData.html = head.concat(script).concat('\n </head>').concat(end);
|
||||
// Remove the contrast assets so they don't get added to the HTML.
|
||||
_.remove(htmlPluginData.assets.css, i => i === contrast);
|
||||
_.remove(htmlPluginData.assets.js, i => i === themeJsChunk);
|
||||
|
||||
return callback(null, htmlPluginData);
|
||||
});
|
||||
});
|
||||
|
||||
compiler.plugin('emit', (compilation, callback) => {
|
||||
// Remove the contrast-theme.js file, since it doesn't do anything
|
||||
const filename = themeJsChunk && themeJsChunk.split('?')[0];
|
||||
if (filename) {
|
||||
delete compilation.assets[filename];
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
import 'babel-polyfill';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import '../styles/contrast.scss';
|
||||
import '../images/favicon.ico';
|
||||
import configureStore from './stores/configureStore';
|
||||
|
||||
const store = configureStore();
|
||||
|
||||
function renderApp() {
|
||||
const App = require('./components/app').default;
|
||||
ReactDOM.render((
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
), document.getElementById('app'));
|
||||
}
|
||||
|
||||
renderApp();
|
||||
if (module.hot) {
|
||||
module.hot.accept('./components/app', renderApp);
|
||||
}
|
||||
1
client/app/scripts/contrast-theme.js
Normal file
1
client/app/scripts/contrast-theme.js
Normal file
@@ -0,0 +1 @@
|
||||
import '../styles/contrast.scss';
|
||||
@@ -1,3 +1,4 @@
|
||||
exports.reducer = require('./reducers/root').default;
|
||||
exports.Scope = require('./components/app').default;
|
||||
exports.actions = require('./actions/app-actions');
|
||||
exports.ContrastStyleCompiler = require('./contrast-compiler');
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax, import/no-unresolved */
|
||||
import debug from 'debug';
|
||||
import { size, each, includes } from 'lodash';
|
||||
import { fromJS, is as isDeepEqual, List as makeList, Map as makeMap,
|
||||
@@ -29,6 +30,8 @@ const topologySorter = topology => topology.get('rank');
|
||||
|
||||
export const initialState = makeMap({
|
||||
availableCanvasMetrics: makeList(),
|
||||
availableNetworks: makeList(),
|
||||
contrastMode: false,
|
||||
controlPipes: makeOrderedMap(), // pipeId -> controlPipe
|
||||
controlStatus: makeMap(),
|
||||
currentTopology: null,
|
||||
@@ -715,6 +718,10 @@ export function rootReducer(state = initialState, action) {
|
||||
return state;
|
||||
}
|
||||
|
||||
case ActionTypes.TOGGLE_CONTRAST_MODE: {
|
||||
return state.set('contrastMode', action.enabled);
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,38 @@
|
||||
export const contrastModeUrl = 'contrast.html';
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
import last from 'lodash/last';
|
||||
/**
|
||||
* Change the Scope UI theme from normal to high-contrast.
|
||||
* This will inject a stylesheet into <head> and override the styles.
|
||||
*
|
||||
* A window-level variable is written to the .html page during the build process that contains
|
||||
* the filename (and content hash) needed to download the file.
|
||||
*/
|
||||
|
||||
const contrastMode = window.location.pathname.indexOf(contrastModeUrl) > -1;
|
||||
|
||||
export function isContrastMode() {
|
||||
return contrastMode;
|
||||
function getFilename(href) {
|
||||
return last(href.split('/'));
|
||||
}
|
||||
|
||||
export function loadTheme(theme = 'normal') {
|
||||
if (window.__WEAVE_SCOPE_THEMES) {
|
||||
// Load the pre-built stylesheet.
|
||||
const stylesheet = window.__WEAVE_SCOPE_THEMES[theme];
|
||||
const head = document.querySelector('head');
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = `${window.__WEAVE_SCOPE_THEMES.publicPath}${stylesheet}`;
|
||||
link.onload = () => {
|
||||
// Remove the old stylesheet to prevent weird overlapping styling issues
|
||||
const oldTheme = theme === 'normal' ? 'contrast' : 'normal';
|
||||
const links = document.querySelectorAll('head link');
|
||||
for (let i = 0; i < links.length; i += 1) {
|
||||
const l = links[i];
|
||||
if (getFilename(l.href) === getFilename(window.__WEAVE_SCOPE_THEMES[oldTheme])) {
|
||||
head.removeChild(l);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
head.appendChild(link);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,18 @@ function encodeURL(url) {
|
||||
.replace(new RegExp(SLASH, 'g'), SLASH_REPLACEMENT);
|
||||
}
|
||||
|
||||
function decodeURL(url) {
|
||||
export function decodeURL(url) {
|
||||
return decodeURIComponent(url.replace(new RegExp(SLASH_REPLACEMENT, 'g'), SLASH))
|
||||
.replace(new RegExp(PERCENT_REPLACEMENT, 'g'), PERCENT);
|
||||
}
|
||||
|
||||
export function parseHashState(hash = window.location.hash) {
|
||||
const urlStateString = hash
|
||||
.replace('#!/state/', '')
|
||||
.replace('#!/', '') || '{}';
|
||||
return JSON.parse(decodeURL(urlStateString));
|
||||
}
|
||||
|
||||
function shouldReplaceState(prevState, nextState) {
|
||||
// Opening a new terminal while an existing one is open.
|
||||
const terminalToTerminal = (prevState.controlPipe && nextState.controlPipe);
|
||||
@@ -50,7 +57,8 @@ export function getUrlState(state) {
|
||||
gridSortedBy: state.get('gridSortedBy'),
|
||||
gridSortedDesc: state.get('gridSortedDesc'),
|
||||
topologyId: state.get('currentTopologyId'),
|
||||
topologyOptions: state.get('topologyOptions').toJS() // all options
|
||||
topologyOptions: state.get('topologyOptions').toJS(), // all options,
|
||||
contrastMode: state.get('contrastMode')
|
||||
};
|
||||
|
||||
if (state.get('showingNetworks')) {
|
||||
@@ -67,10 +75,7 @@ export function updateRoute(getState) {
|
||||
const state = getUrlState(getState());
|
||||
const stateUrl = encodeURL(JSON.stringify(state));
|
||||
const dispatch = false;
|
||||
const urlStateString = window.location.hash
|
||||
.replace('#!/state/', '')
|
||||
.replace('#!/', '') || '{}';
|
||||
const prevState = JSON.parse(decodeURL(urlStateString));
|
||||
const prevState = parseHashState();
|
||||
|
||||
// back up state in storage as well
|
||||
storageSet(STORAGE_STATE_KEY, stateUrl);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const webpack = require('webpack');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
const ContrastStyleCompiler = require('./app/scripts/contrast-compiler');
|
||||
/**
|
||||
* This is the Webpack configuration file for local development.
|
||||
* It contains local-specific configuration which includes:
|
||||
@@ -24,12 +25,12 @@ module.exports = {
|
||||
'./app/scripts/main',
|
||||
'webpack-hot-middleware/client'
|
||||
],
|
||||
'dev-app': [
|
||||
'./app/scripts/main.dev',
|
||||
'contrast-theme': [
|
||||
'./app/scripts/contrast-theme',
|
||||
'webpack-hot-middleware/client'
|
||||
],
|
||||
'contrast-app': [
|
||||
'./app/scripts/contrast-main',
|
||||
'dev-app': [
|
||||
'./app/scripts/main.dev',
|
||||
'webpack-hot-middleware/client'
|
||||
],
|
||||
'terminal-app': [
|
||||
@@ -45,7 +46,7 @@ module.exports = {
|
||||
// Used by Webpack Dev Middleware
|
||||
output: {
|
||||
publicPath: '',
|
||||
path: '/',
|
||||
path: path.join(__dirname, 'build'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
|
||||
@@ -56,26 +57,23 @@ module.exports = {
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, [/moment$/]),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['vendors', 'contrast-app'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'contrast.html'
|
||||
}),
|
||||
new ExtractTextPlugin('style-[name]-[chunkhash].css'),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['vendors', 'terminal-app'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'terminal.html'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['vendors', 'dev-app'],
|
||||
chunks: ['vendors', 'dev-app', 'contrast-theme'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'dev.html'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
chunks: ['vendors', 'app'],
|
||||
chunks: ['vendors', 'app', 'contrast-theme'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'index.html'
|
||||
})
|
||||
}),
|
||||
new ContrastStyleCompiler()
|
||||
],
|
||||
|
||||
// Transform source code using Babel and React Hot Loader
|
||||
@@ -114,7 +112,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
test: /\.(scss|css)$/,
|
||||
loader: 'style-loader!css-loader!postcss-loader!sass-loader'
|
||||
loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss!sass-loader')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ const path = require('path');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const ContrastStyleCompiler = require('./app/scripts/contrast-compiler');
|
||||
|
||||
const GLOBALS = {
|
||||
'process.env': {NODE_ENV: '"production"'}
|
||||
@@ -31,7 +32,7 @@ module.exports = {
|
||||
|
||||
entry: {
|
||||
app: './app/scripts/main',
|
||||
'contrast-app': './app/scripts/contrast-main',
|
||||
'contrast-theme': ['./app/scripts/contrast-theme'],
|
||||
'terminal-app': './app/scripts/terminal-main',
|
||||
// keep only some in here, to make vendors and app bundles roughly same size
|
||||
vendors: ['babel-polyfill', 'classnames', 'immutable',
|
||||
@@ -112,12 +113,6 @@ module.exports = {
|
||||
}
|
||||
}),
|
||||
new ExtractTextPlugin('style-[name]-[chunkhash].css'),
|
||||
new HtmlWebpackPlugin({
|
||||
hash: true,
|
||||
chunks: ['vendors', 'contrast-app'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'contrast.html'
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
hash: true,
|
||||
chunks: ['vendors', 'terminal-app'],
|
||||
@@ -126,10 +121,11 @@ module.exports = {
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
hash: true,
|
||||
chunks: ['vendors', 'app'],
|
||||
chunks: ['vendors', 'app', 'contrast-theme'],
|
||||
template: 'app/html/index.html',
|
||||
filename: 'index.html'
|
||||
})
|
||||
}),
|
||||
new ContrastStyleCompiler()
|
||||
],
|
||||
sassLoader: {
|
||||
includePaths: [
|
||||
|
||||
Reference in New Issue
Block a user