Merge branch 'develop' of https://github.com/vector-im/riot-web into forward_message

Conflicts:
	.gitignore
pull/3688/head
Michael Telatynski 2017-04-24 18:38:52 +01:00
commit 2eb715002f
24 changed files with 223 additions and 83 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ npm-debug.log
electron/dist
electron/pub
/.idea
config.json

View File

@ -201,9 +201,12 @@ electron.app.on('ready', () => {
brand: vectorConfig.brand || 'Riot'
});
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
if (!process.argv.includes('--hidden')) {
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
}
mainWindow.on('closed', () => {
mainWindow = null;
});

View File

@ -30,7 +30,7 @@
"build:res": "node scripts/copy-res.js",
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
"build:compile": "babel --source-maps -d lib src",
"build:bundle": "NODE_ENV=production webpack -p --progress",
"build:bundle": "cross-env NODE_ENV=production webpack -p --progress",
"build:bundle:dev": "webpack --optimize-occurence-order --progress",
"build:electron": "npm run clean && npm run build && build -wml --ia32 --x64",
"build": "node scripts/babelcheck.js && npm run build:res && npm run build:bundle",
@ -38,7 +38,7 @@
"dist": "scripts/package.sh",
"start:res": "node scripts/copy-res.js -w",
"start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-file=bundles/_dev_/[name].js -w --progress",
"start:js:prod": "NODE_ENV=production webpack-dev-server -w --progress",
"start:js:prod": "cross-env NODE_ENV=production webpack-dev-server -w --progress",
"start": "node scripts/babelcheck.js && parallelshell \"npm run start:res\" \"npm run start:js\"",
"start:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\"",
"lint": "eslint src/",
@ -93,6 +93,7 @@
"babel-preset-stage-2": "^6.17.0",
"chokidar": "^1.6.1",
"cpx": "^1.3.2",
"cross-env": "^4.0.0",
"css-raw-loader": "^0.1.1",
"electron-builder": "^11.2.4",
"electron-builder-squirrel-windows": "^11.2.1",

View File

@ -10,6 +10,7 @@ const COPY_LIST = [
["res/{media,vector-icons}/**", "webapp"],
["src/skins/vector/{fonts,img}/**", "webapp"],
["node_modules/emojione/assets/svg/*", "webapp/emojione/svg/"],
["node_modules/emojione/assets/png/*", "webapp/emojione/png/"],
["./config.json", "webapp", {directwatch: 1}],
];

View File

@ -23,7 +23,6 @@ var ContentRepo = require("matrix-js-sdk").ContentRepo;
var Modal = require('matrix-react-sdk/lib/Modal');
var sdk = require('matrix-react-sdk');
var dis = require('matrix-react-sdk/lib/dispatcher');
var GeminiScrollbar = require('react-gemini-scrollbar');
var linkify = require('linkifyjs');
var linkifyString = require('linkifyjs/string');
@ -162,7 +161,7 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Failed to get public room list",
description: "The server may be unavailable or overloaded",
description: ((err && err.message) ? err.message : "The server may be unavailable or overloaded"),
});
});
},
@ -210,8 +209,8 @@ module.exports = React.createClass({
this.refreshRoomList();
console.error("Failed to " + step + ": " + err);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Failed to " + step,
title: "Failed to " + step,
description: ((err && err.message) ? err.message : "The server may be unavailable or overloaded"),
});
});
}
@ -460,6 +459,17 @@ module.exports = React.createClass({
return fields;
},
/**
* called by the parent component when PageUp/Down/etc is pressed.
*
* We pass it down to the scroll panel.
*/
handleScrollKey: function(ev) {
if (this.scrollPanel) {
this.scrollPanel.handleScrollKey(ev);
}
},
render: function() {
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
const Loader = sdk.getComponent("elements.Spinner");

View File

@ -456,8 +456,8 @@ var RoomSubList = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to add tag " + self.props.tagName + " to room" + err);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Failed to add tag " + self.props.tagName + " to room",
title: "Failed to add tag " + self.props.tagName + " to room",
description: ((err && err.message) ? err.message : "Operation failed"),
});
});
break;
@ -515,6 +515,7 @@ var RoomSubList = React.createClass({
roomCount={ roomCount }
collapsed={ this.props.collapsed }
hidden={ this.state.hidden }
incomingCall={ this.props.incomingCall }
isIncomingCallRoom={ isIncomingCallRoom }
roomNotificationCount={ this.roomNotificationCount() }
onClick={ this.onClick }

View File

@ -36,6 +36,7 @@ module.exports = React.createClass({
React.PropTypes.number
]),
collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
incomingCall: React.PropTypes.object,
isIncomingCallRoom: React.PropTypes.bool,
roomNotificationCount: React.PropTypes.array,
hidden: React.PropTypes.bool,
@ -111,8 +112,8 @@ module.exports = React.createClass({
<div className="mx_RoomSubList_roomCount">{ roomCount }</div>
<div className={chevronClasses}></div>
{ badge }
{ incomingCall }
</AccessibleButton>
{ incomingCall }
</div>
);
},

View File

@ -71,7 +71,7 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Failed to remove tag " + tagNameOff + " from room",
description: err.toString()
description: ((err && err.message) ? err.message : "Operation failed"),
});
});
}
@ -88,7 +88,7 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Failed to add tag " + tagNameOn + " to room",
description: err.toString()
description: ((err && err.message) ? err.message : "Operation failed"),
});
});
}
@ -149,7 +149,7 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Failed to set Direct Message status of room",
description: err.toString()
description: ((err && err.message) ? err.message : "Operation failed"),
});
});
},
@ -187,8 +187,8 @@ module.exports = React.createClass({
var errCode = err.errcode || "unknown error code";
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Error",
description: `Failed to forget room (${errCode})`
title: `Failed to forget room (${errCode})`,
description: ((err && err.message) ? err.message : "Operation failed"),
});
});

View File

@ -176,7 +176,7 @@ module.exports = React.createClass({
{ this.getName() }
</div>
{ eventMeta }
<a className="mx_ImageView_link" href={ this.props.src } target="_blank" rel="noopener">
<a className="mx_ImageView_link" href={ this.props.src } download={ this.props.name } target="_blank" rel="noopener">
<div className="mx_ImageView_download">
Download this file<br/>
<span className="mx_ImageView_size">{ size_res }</span>

View File

@ -18,6 +18,9 @@ limitations under the License.
var React = require('react');
const i = [1, 2, 3, 4, 5][Math.floor(Math.random() * 5)];
const DEFAULT_LOGO_URI = "img/logos/riot-logo-" + i + ".svg";
module.exports = React.createClass({
displayName: 'VectorLoginHeader',
statics: {
@ -30,7 +33,7 @@ module.exports = React.createClass({
render: function() {
return (
<div className="mx_Login_logo">
<img src={this.props.icon || "img/logo.png"} alt="Riot"/>
<img src={this.props.icon || DEFAULT_LOGO_URI} alt="Riot"/>
</div>
);
}

View File

@ -90,8 +90,8 @@ var roomTileSource = {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to set direct chat tag " + err);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Failed to set direct chat tag",
title: "Failed to set direct chat tag",
description: ((err && err.message) ? err.message : "Operation failed"),
});
});
return;
@ -115,8 +115,8 @@ var roomTileSource = {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to remove tag " + prevTag + " from room: " + err);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Failed to remove tag " + prevTag + " from room",
title: "Failed to remove tag " + prevTag + " from room",
description: ((err && err.message) ? err.message : "Operation failed"),
});
});
}
@ -137,8 +137,8 @@ var roomTileSource = {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to add tag " + newTag + " to room: " + err);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Failed to add tag " + newTag + " to room",
title: "Failed to add tag " + newTag + " to room",
description: ((err && err.message) ? err.message : "Operation failed"),
});
});
}

View File

@ -240,8 +240,8 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to change settings: " + error);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Failed to change settings",
title: "Failed to change settings",
description: ((error && error.message) ? error.message : "Operation failed"),
onFinished: self._refreshFromServer
});
});
@ -310,8 +310,8 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Can't update user notification settings: " + error);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Can't update user notification settings",
title: "Can't update user notification settings",
description: ((error && error.message) ? error.message : "Operation failed"),
onFinished: self._refreshFromServer
});
});
@ -352,8 +352,8 @@ module.exports = React.createClass({
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to update keywords: " + error);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: "Failed to update keywords",
title: "Failed to update keywords",
description: ((error && error.message) ? error.message : "Operation failed"),
onFinished: self._refreshFromServer
});
}

View File

@ -65,7 +65,7 @@ input[type=text].error, input[type=password].error {
border: 1px solid $warning-color;
}
input[type=text]:focus, textarea:focus {
input[type=text]:focus, input[type=password]:focus, textarea:focus {
border: 1px solid $accent-color;
outline: none;
box-shadow: none;
@ -225,6 +225,10 @@ textarea {
vertical-align: middle;
}
.mx_Dialog button:focus, .mx_Dialog input[type="submit"]:focus {
filter: brightness($focus-brightness);
}
.mx_Dialog button.mx_Dialog_primary, .mx_Dialog input[type="submit"].mx_Dialog_primary {
color: $accent-fg-color;
background-color: $accent-color;

View File

@ -171,7 +171,7 @@ hr.mx_RoomView_myReadMarker {
max-height: 0px;
background-color: $primary-bg-color;
z-index: 1000;
z-index: 5;
overflow: hidden;
-webkit-transition: all .2s ease-out;
@ -259,4 +259,4 @@ hr.mx_RoomView_myReadMarker {
.mx_RoomView_ongoingConfCallNotification a {
color: $accent-fg-color ! important;
}
}

View File

@ -213,3 +213,9 @@ input.mx_UserSettings_phoneNumberField {
.mx_UserSettings_avatarPicker_edit > input {
display: none;
}
.mx_UserSettings_advanced_spoiler {
cursor: pointer;
color: $accent-color;
word-break: break-all;
}

View File

@ -42,7 +42,8 @@ limitations under the License.
.mx_Login_logo {
text-align: center;
height: 195px;
height: 150px;
margin-bottom: 45px;
}
.mx_Login_logo img {
@ -66,10 +67,6 @@ limitations under the License.
margin-bottom: 14px;
}
.mx_Login_username {
margin-bottom: 0px;
}
.mx_Login_fieldLabel {
margin-top: -10px;
margin-left: 8px;
@ -167,6 +164,66 @@ limitations under the License.
margin-bottom: 12px;
}
.mx_Login_type_container {
display: flex;
margin-bottom: 14px;
}
.mx_Login_type_label {
flex-grow: 1;
line-height: 35px;
}
.mx_Login_type_dropdown {
width: 125px;
align-self: flex-end;
}
.mx_Login_username_group {
display: flex;
}
.mx_Login_username_prefix {
height: 33px;
padding: 0px 5px;
line-height: 33px;
background-color: #eee;
border: 1px solid #c7c7c7;
border-right: 0px;
border-radius: 3px 0px 0px 3px;
text-align: center;
}
.mx_Login_username_suffix {
height: 33px;
padding: 0px 5px;
line-height: 33px;
background-color: #eee;
border: 1px solid #c7c7c7;
border-left: 0px;
border-radius: 0px 3px 3px 0px;
text-align: center;
flex-grow: 1;
}
.mx_Login_username {
flex-shrink: 1;
min-width: 0px;
border-radius: 3px;
/* The "@" is always prefixed, so no rounded corners */
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
}
.mx_Login_field_has_suffix {
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
}
.mx_Login_phoneSection {
display: table;
}
@ -176,6 +233,20 @@ limitations under the License.
width: 70px;
}
.mx_Login_phoneCountry .mx_Dropdown_option {
/*
To match height of mx_Login_field
33px + 2px border from mx_Dropdown_option = 35px
*/
height: 33px;
line-height: 33px;
}
.mx_Login_phoneCountry .mx_Dropdown_option img {
margin: 4px;
vertical-align: top;
}
.mx_Login_phoneNumberField {
width: 210px;
margin-left: 3px;

View File

@ -27,6 +27,10 @@ limitations under the License.
user-select: none;
}
.mx_Dropdown_input:focus {
border-color: $accent-color;
}
.mx_Dropdown_arrow {
border-color: $primary-fg-color transparent transparent;
border-style: solid;
@ -74,7 +78,7 @@ input.mx_Dropdown_option, input.mx_Dropdown_option:focus {
border: 1px solid $accent-color;
background-color: $primary-bg-color;
max-height: 200px;
overflow-y: scroll;
overflow-y: auto;
}
.mx_Dropdown_menu .mx_Dropdown_option_highlight {

View File

@ -17,20 +17,15 @@ limitations under the License.
.mx_TopUnreadMessagesBar {
margin: auto; /* centre horizontally */
max-width: 960px;
padding-top: 5px;
padding-bottom: 5px;
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid $primary-hairline-color;
/* in absence of img */
height: 24px;
}
.mx_TopUnreadMessagesBar_scrollUp {
display: inline;
cursor: pointer;
/* in absence of img */
padding-left: 65px;
text-decoration: underline;
}
.mx_TopUnreadMessagesBar_scrollUp img {

View File

@ -1,21 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="25px" height="25px" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: sketchtool 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
<title>E34C64ED-EBD7-49B6-BDD9-CB729162705A</title>
<desc>Created with sketchtool.</desc>
<defs>
<rect id="path-1" x="6" y="10" width="13" height="8" rx="1"></rect>
<mask id="mask-2" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="0" y="0" width="13" height="8" fill="white">
<use xlink:href="#path-1"></use>
</mask>
</defs>
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>icons_directory</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Room-list-Copy-3" transform="translate(-58.000000, -726.000000)">
<g id="icons_directory" transform="translate(58.000000, 726.000000)">
<path d="M12.5,25 C19.4035594,25 25,19.4035594 25,12.5 C25,5.59644063 19.4035594,0 12.5,0 C5.59644063,0 0,5.59644063 0,12.5 C0,19.4035594 5.59644063,25 12.5,25 Z" id="Oval-1-Copy-7" fill="#76CFA6"></path>
<use id="Rectangle-9" stroke="#FFFFFF" mask="url(#mask-2)" stroke-width="2" opacity="0.8" xlink:href="#path-1"></use>
<path d="M6,9 L6,6.99895656 C6,6.44724809 6.45097518,6 6.99077797,6 L11.009222,6 C11.5564136,6 12,6.44386482 12,7 L12,8 L17.9970707,8 C18.5509732,8 19,8.45318604 19,9 L19,9 L6,9 Z" id="Path-Copy" fill="#FFFFFF" opacity="0.6"></path>
<g id="Left-panel" transform="translate(-83.000000, -726.000000)">
<g id="icons_directory">
<g transform="translate(83.000000, 726.000000)">
<path d="M12.5,25 C19.4035594,25 25,19.4035594 25,12.5 C25,5.59644063 19.4035594,0 12.5,0 C5.59644063,0 0,5.59644063 0,12.5 C0,19.4035594 5.59644063,25 12.5,25 Z" id="Oval-1-Copy-7" fill="#76CFA6"></path>
<g id="Lines" transform="translate(6.000000, 7.000000)" stroke="#FFFFFF" stroke-linecap="round">
<path d="M4,5.5 L9,5.5" id="Line"></path>
<path d="M4,1.5 L13,1.5" id="Line-Copy-4"></path>
<path d="M0,1.5 L2,1.5" id="Line" opacity="0.6"></path>
<path d="M0,5.5 L2,5.5" id="Line" opacity="0.6"></path>
<path d="M4,9.5 L11,9.5" id="Line-Copy-6"></path>
<path d="M0,9.5 L2,9.5" id="Line-Copy-3" opacity="0.6"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
<title>Slice 1</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="scrollup">
<g id="02-Chat" transform="translate(12.000000, 12.000000) scale(-1, 1) rotate(-180.000000) translate(-12.000000, -12.000000) ">
<g id="02_7-Chat-new-messages">
<g id="icon_newmessages">
<circle id="Oval-1909" fill-opacity="0.5" fill="#454545" fill-rule="nonzero" cx="12" cy="12" r="12"></circle>
<circle id="Oval" stroke="#FFFFFF" cx="12" cy="12" r="7"></circle>
<circle id="Oval" stroke="#FFFFFF" cx="12" cy="12" r="4"></circle>
<circle id="Oval" fill="#FFFFFF" cx="12" cy="12" r="1"></circle>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -263,6 +263,11 @@ async function loadApp() {
configError = e;
}
if (window.localStorage && window.localStorage.getItem('mx_accepts_unsupported_browser')) {
console.log('User has previously accepted risks in using an unsupported browser');
validBrowser = true;
}
console.log("Vector starting at "+window.location);
if (configError) {
window.matrixChat = ReactDOM.render(<div className="error">
@ -294,6 +299,7 @@ async function loadApp() {
var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
window.matrixChat = ReactDOM.render(
<CompatibilityPage onAccept={function() {
if (window.localStorage) window.localStorage.setItem('mx_accepts_unsupported_browser', true);
validBrowser = true;
console.log("User accepts the compatibility risks.");
loadApp();

View File

@ -81,6 +81,17 @@ export default class ElectronPlatform extends VectorBasePlatform {
}
displayNotification(title: string, msg: string, avatarUrl: string, room: Object): Notification {
// GNOME notification spec parses HTML tags for styling...
// Electron Docs state all supported linux notification systems follow this markup spec
// https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#linux
// maybe we should pass basic styling (italics, bold, underline) through from MD
// we only have to strip out < and > as the spec doesn't include anything about things like &amp;
// so we shouldn't assume that all implementations will treat those properly. Very basic tag parsing is done.
if (window.process.platform === 'linux') {
msg = msg.replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
// Notifications in Electron use the HTML5 notification API
const notification = new global.Notification(
title,

View File

@ -1,7 +1,7 @@
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
@ -19,11 +19,11 @@ module.exports = {
// CSS themes
"theme-light": "./src/skins/vector/css/themes/light.scss",
"theme-dark": "./src/skins/vector/css/themes/dark.scss"
"theme-dark": "./src/skins/vector/css/themes/dark.scss",
},
module: {
preLoaders: [
{ test: /\.js$/, loader: "source-map-loader" }
{ test: /\.js$/, loader: "source-map-loader" },
],
loaders: [
{ test: /\.json$/, loader: "json" },
@ -38,9 +38,7 @@ module.exports = {
// would also drag in the imgs and fonts that our CSS refers to
// as webpack inputs.)
// 3. ExtractTextPlugin turns that string into a separate asset.
loader: ExtractTextPlugin.extract(
"css-raw-loader!postcss-loader?config=postcss.config.js"
),
loader: ExtractTextPlugin.extract("css-raw-loader!postcss-loader?config=postcss.config.js"),
},
{
// this works similarly to the scss case, without postcss.
@ -49,15 +47,18 @@ module.exports = {
},
],
noParse: [
// for cross platform compatibility use [\\\/] as the path separator
// this ensures that the regex trips on both Windows and *nix
// don't parse the languages within highlight.js. They cause stack
// overflows (https://github.com/webpack/webpack/issues/1721), and
// there is no need for webpack to parse them - they can just be
// included as-is.
/highlight\.js\/lib\/languages/,
/highlight\.js[\\\/]lib[\\\/]languages/,
// olm takes ages for webpack to process, and it's already heavily
// optimised, so there is little to gain by us uglifying it.
/olm\/(javascript\/)?olm\.js$/,
/olm[\\\/](javascript[\\\/])?olm\.js$/,
],
},
output: {
@ -83,7 +84,7 @@ module.exports = {
// various levels of '.' and '..'
// Also, sometimes the resource path is absolute.
return path.relative(process.cwd(), info.resourcePath).replace(/^[\/\.]*/, '');
}
},
},
resolve: {
alias: {
@ -106,16 +107,13 @@ module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
}),
new ExtractTextPlugin(
"bundles/[hash]/[name].css",
{
allChunks: true
}
),
new ExtractTextPlugin("bundles/[hash]/[name].css", {
allChunks: true,
}),
new HtmlWebpackPlugin({
template: './src/vector/index.html',